From 6249996c1103f8af1c702376d094ca2893f1a869 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Tue, 13 Aug 2024 10:42:56 -0700 Subject: [PATCH 01/53] Unit test for veteran processing --- .../java/ca/bc/gov/nrs/vdyp/vri/VriStart.java | 1 + .../bc/gov/nrs/vdyp/vri/model/VriLayer.java | 21 +- .../ca/bc/gov/nrs/vdyp/vri/VriStartTest.java | 198 ++++++++++++++++++ 3 files changed, 218 insertions(+), 2 deletions(-) diff --git a/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java b/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java index 3e8ee399d..66344928a 100644 --- a/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java +++ b/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java @@ -1106,6 +1106,7 @@ VriPolygon processYoung(VriPolygon poly) throws ProcessingException { ); }); + lBuilder.ageIncrease(inc.ageIncrease()); }); }); }); diff --git a/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/model/VriLayer.java b/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/model/VriLayer.java index 80f3ec4a8..0d1491bc5 100644 --- a/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/model/VriLayer.java +++ b/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/model/VriLayer.java @@ -19,11 +19,12 @@ public class VriLayer extends BaseVdypLayer implements Inpu private final Optional primaryGenus; // FIPL_1C/JPRIME_L1 ISPP private final Optional secondaryGenus; // FIPL_1C/JPRIME_L1 ISPS private final Optional empericalRelationshipParameterIndex; // INXL1/GRPBA1 + private final float ageIncrease; // YOUNG1/AGE_INCR public VriLayer( PolygonIdentifier polygonIdentifier, LayerType layer, float crownClosure, Optional baseArea, Optional treesPerHectare, float utilization, Optional primaryGenus, - Optional secondaryGenus, Optional empericalRelationshipParameterIndex + Optional secondaryGenus, Optional empericalRelationshipParameterIndex, float ageIncrease ) { super(polygonIdentifier, layer, Optional.empty()); this.crownClosure = crownClosure; @@ -33,6 +34,7 @@ public VriLayer( this.primaryGenus = primaryGenus; this.secondaryGenus = secondaryGenus; this.empericalRelationshipParameterIndex = empericalRelationshipParameterIndex; + this.ageIncrease = ageIncrease; } @Override @@ -84,6 +86,11 @@ public Optional getEmpericalRelationshipParameterIndex() { return empericalRelationshipParameterIndex; } + + public float getAgeIncrease() { + return this.ageIncrease; + }; + /** * Accepts a configuration function that accepts a builder to configure. * @@ -109,6 +116,9 @@ public static VriLayer build(VriPolygon polygon, Consumer config) { public static class Builder extends BaseVdypLayer.Builder { + + private static final float DEFAULT_AGE_INCREASE = 0; + protected Optional crownClosure = Optional.empty(); protected Optional baseArea = Optional.empty(); protected Optional treesPerHectare = Optional.empty(); @@ -117,6 +127,7 @@ public static class Builder protected Optional primaryGenus = Optional.empty(); protected Optional secondaryGenus = Optional.empty(); protected Optional empericalRelationshipParameterIndex = Optional.empty(); + protected Optional ageIncrease = Optional.empty(); public Builder empiricalRelationshipParameterIndex(Optional empiricalRelationshipParameterIndex) { this.empericalRelationshipParameterIndex = empiricalRelationshipParameterIndex; @@ -193,6 +204,11 @@ public Builder secondaryGenus(Optional secondaryGenus) { public Builder secondaryGenus(String secondaryGenus) { return secondaryGenus(Optional.of(secondaryGenus)); } + + public Builder ageIncrease(float ageIncrease) { + this.ageIncrease = Optional.of(ageIncrease); + return this; + } @Override protected void check(Collection errors) { @@ -213,7 +229,8 @@ protected VriLayer doBuild() { Math.max(utilization.get(), 7.5f), // primaryGenus, // secondaryGenus, // - empericalRelationshipParameterIndex + empericalRelationshipParameterIndex, + ageIncrease.orElse(DEFAULT_AGE_INCREASE) ); result.setInventoryTypeGroup(inventoryTypeGroup); return result; diff --git a/vdyp-lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriStartTest.java b/vdyp-lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriStartTest.java index a30b966ca..09129f351 100644 --- a/vdyp-lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriStartTest.java +++ b/vdyp-lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriStartTest.java @@ -1,5 +1,6 @@ package ca.bc.gov.nrs.vdyp.vri; +import static ca.bc.gov.nrs.vdyp.test.TestUtils.assertHasPrimaryLayer; import static ca.bc.gov.nrs.vdyp.test.TestUtils.assertOnlyPrimaryLayer; import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.closeTo; import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.coe; @@ -2187,6 +2188,203 @@ resultLayer, hasProperty("loreyHeightByUtilization", utilizationHeight(4.1406788 app.close(); } + @Test + void testProcessVeteran() throws Exception { + + controlMap = TestUtils.loadControlMap(); + + VriStart app = new VriStart(); + + MockFileResolver resolver = dummyInput(); + + var poly = VriPolygon.build(pb -> { + pb.polygonIdentifier("TestPoly", 2024); + pb.biogeoclimaticZone("IDF"); + pb.yieldFactor(1.0f); + pb.mode(PolygonMode.BATN); + pb.forestInventoryZone(""); + pb.percentAvailable(85); + pb.addLayer(lb -> { + lb.layerType(LayerType.PRIMARY); + lb.crownClosure(40.2f); + lb.utilization(7.5f); + + lb.inventoryTypeGroup(14); + lb.empiricalRelationshipParameterIndex(33); + + lb.primaryGenus("C"); + // 1 + lb.addSpecies(sb -> { + sb.genus("B"); + sb.percentGenus(10); + sb.addSpecies("BL", 100); + sb.addSite(ib -> { + ib.siteSpecies("BL"); + ib.siteCurveNumber(8); + }); + }); + + // 2 (Primary) + lb.addSpecies(sb -> { + sb.genus("C"); + sb.percentGenus(50); + sb.addSpecies("CW", 100); + sb.addSite(ib -> { + ib.siteCurveNumber(11); + ib.ageTotal(100); + ib.height(20f); + ib.siteIndex(12f); + ib.yearsToBreastHeight(10.9f); + ib.breastHeightAge(89.1f); + ib.ageTotal(100f); + ib.siteSpecies("CW"); + }); + }); + + // 3 + lb.addSpecies(sb -> { + sb.genus("H"); + sb.percentGenus(40); + sb.addSpecies("HW", 100); + sb.addSite(ib -> { + ib.siteCurveNumber(37); + ib.height(25f); + ib.siteIndex(12.6f); + ib.yearsToBreastHeight(9.7f); + ib.breastHeightAge(90.3f); + ib.ageTotal(100f); + ib.siteSpecies("HW"); + }); + }); + + }); + }); + + app.init(resolver, controlMap); + + var result = app.processPolygon(0, poly).get(); + + assertThat(result, hasProperty("polygonIdentifier", isPolyId("TestPoly", 2024))); + assertThat(result, hasProperty("biogeoclimaticZone", is("IDF"))); + assertThat(result, hasProperty("forestInventoryZone", blankString())); + assertThat(result, hasProperty("mode", present(is(PolygonMode.BATN)))); + assertThat(result, hasProperty("percentAvailable", is(85f))); + + var resultLayer = assertHasPrimaryLayer(result); + + assertThat(resultLayer, hasProperty("ageTotal", present(closeTo(100)))); + assertThat(resultLayer, hasProperty("breastHeightAge", present(closeTo(89.1f)))); + assertThat(resultLayer, hasProperty("yearsToBreastHeight", present(closeTo(10.9f)))); + + assertThat(resultLayer, hasProperty("siteGenus", present(is("C")))); + + assertThat(resultLayer, hasProperty("height", present(closeTo(20f)))); + assertThat(resultLayer, hasProperty("inventoryTypeGroup", present(is(14)))); + assertThat(resultLayer, hasProperty("empiricalRelationshipParameterIndex", present(is(33)))); + + assertThat( + resultLayer, hasProperty("loreyHeightByUtilization", utilizationHeight(4.14067888f, 6.61390257f)) + ); + assertThat( + resultLayer, + hasProperty( + "baseAreaByUtilization", + utilization( + 0.0679966733f, 6.34290648f, 4.24561071f, 1.01540196f, 0.571661115f, 0.510232806f + ) + ) + ); + assertThat( + resultLayer, + hasProperty( + "quadraticMeanDiameterByUtilization", + utilization(5.58983135f, 10.3879948f, 9.11466217f, 13.9179964f, 18.6690178f, 25.3685265f) + ) + ); + assertThat( + resultLayer, + hasProperty( + "treesPerHectareByUtilization", + utilization(27.707695f, 748.4021f, 650.682556f, 66.7413025f, 20.8836231f, 10.094574f) + ) + ); + + assertThat( + resultLayer, + hasProperty( + "closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", + utilization(0, 4.73118162f, 0.0503439531f, 1.59589052f, 1.62338901f, 1.46155834f) + ) + ); + assertThat( + resultLayer.getSpecies(), + allOf(aMapWithSize(5), hasKey("B"), hasKey("C"), hasKey("F"), hasKey("H"), hasKey("S")) + ); + + VdypSpecies resultSpecB = TestUtils.assertHasSpecies(resultLayer, "B", "C", "F", "H", "S"); + + assertThat( + resultSpecB, + hasProperty( + "baseAreaByUtilization", + utilization( + 0.0116237309f, 0.634290636f, 0.239887208f, 0.196762085f, 0.102481194f, 0.095160149f + ) + ) + ); + assertThat( + resultSpecB, + hasProperty( + "quadraticMeanDiameterByUtilization", + utilization(5.61674118f, 12.9407434f, 9.93954372f, 14.3500404f, 19.1790199f, 27.5482502f) + ) + ); + assertThat( + resultSpecB, + hasProperty( + "treesPerHectareByUtilization", + utilization(4.69123125f, 48.2258606f, 30.9160728f, 12.1659298f, 3.54732919f, 1.59653044f) + ) + ); + + assertThat( + resultSpecB, + hasProperty( + "wholeStemVolumeByUtilization", + utilization(0.0244281366f, 2.41518188f, 0.747900844f, 0.752810001f, 0.4540295f, 0.46044156f) + ) + ); + assertThat( + resultSpecB, + hasProperty( + "closeUtilizationVolumeByUtilization", + utilization(0, 1.28733742f, 0.0235678982f, 0.464995325f, 0.378819793f, 0.41995436f) + ) + ); + assertThat( + resultSpecB, + hasProperty( + "closeUtilizationVolumeNetOfDecayByUtilization", + utilization(0, 1.24826729f, 0.0230324566f, 0.454239398f, 0.369579285f, 0.401416153f) + ) + ); + assertThat( + resultSpecB, + hasProperty( + "closeUtilizationVolumeNetOfDecayAndWasteByUtilization", + utilization(0, 1.23482728f, 0.0228475146f, 0.450360179f, 0.366144955f, 0.395474672f) + ) + ); + assertThat( + resultSpecB, + hasProperty( + "closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", + utilization(0, 1.20897281f, 0.0223761573f, 0.441060275f, 0.358547896f, 0.386988521f) + ) + ); + + app.close(); + } } From d935f0a417003fbf6a4e577feadb4bf7dd48afa3 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Tue, 13 Aug 2024 17:03:04 -0700 Subject: [PATCH 02/53] Fixing incorrect computation of lorey height for non-primary species with a defined height. --- .../java/ca/bc/gov/nrs/vdyp/vri/VriStart.java | 14 +++++++++----- .../ca/bc/gov/nrs/vdyp/vri/VriStartTest.java | 16 ++++++++++------ 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java b/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java index 66344928a..e9d8ef3ac 100644 --- a/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java +++ b/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java @@ -456,6 +456,11 @@ void processPrimaryLayer(VriPolygon polygon, VdypLayer.Builder lBuilder) throws applyGroups(bec, vriSpec.getGenus(), sBuilder); + float fraction = primaryLayer.getSpecies().size() == 1 ? 1 : vriSpec.getFractionGenus(); + + float specBaseArea = primaryBaseArea * fraction; + sBuilder.baseArea(specBaseArea); + if (vriSite == primarySiteIn) { sBuilder.loreyHeight(primaryHeight); @@ -463,9 +468,9 @@ void processPrimaryLayer(VriPolygon polygon, VdypLayer.Builder lBuilder) throws sBuilder.adaptSite(vriSite, (iBuilder, vriSite2) -> iBuilder.height(vriSite2.getHeight().get())); } else { - float loreyHeight = vriSite.getHeight().filter((x) -> getDebugMode(2) == 1).map(height -> { + float loreyHeight = vriSite.getHeight().filter((x) -> getDebugMode(2) != 1).map(height -> { float speciesQuadMeanDiameter = Math.max(7.5f, height / leadHeight * layerQuadMeanDiameter); - float speciesDensity = treesPerHectare(primaryBaseArea, speciesQuadMeanDiameter); + float speciesDensity = treesPerHectare(specBaseArea, speciesQuadMeanDiameter); // EMP050 Method 1 return estimationMethods.primaryHeightFromLeadHeight( vriSite.getHeight().get(), vriSite.getSiteGenus(), bec.getRegion(), speciesDensity @@ -494,13 +499,12 @@ void processPrimaryLayer(VriPolygon polygon, VdypLayer.Builder lBuilder) throws var species = lBuilder.getSpecies(); float sumBaseAreaLoreyHeight = 0; - // Assign BA by species + // find aggregate lorey height if (species.size() == 1) { - species.get(0).getBaseAreaByUtilization().setAll(primaryBaseArea); sumBaseAreaLoreyHeight = primaryBaseArea; } else { for (var spec : species) { - float specBaseArea = primaryBaseArea * spec.getFractionGenus(); + float specBaseArea = spec.getBaseAreaByUtilization().getAll(); float specHeight = spec.getLoreyHeightByUtilization().getAll(); spec.getBaseAreaByUtilization().setAll(specBaseArea); sumBaseAreaLoreyHeight += specBaseArea * specHeight; diff --git a/vdyp-lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriStartTest.java b/vdyp-lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriStartTest.java index 09129f351..3e37b7392 100644 --- a/vdyp-lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriStartTest.java +++ b/vdyp-lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriStartTest.java @@ -2188,6 +2188,7 @@ resultLayer, hasProperty("loreyHeightByUtilization", utilizationHeight(4.1406788 app.close(); } + @Test void testProcessVeteran() throws Exception { @@ -2201,19 +2202,22 @@ void testProcessVeteran() throws Exception { pb.polygonIdentifier("TestPoly", 2024); pb.biogeoclimaticZone("IDF"); pb.yieldFactor(1.0f); - pb.mode(PolygonMode.BATN); pb.forestInventoryZone(""); pb.percentAvailable(85); pb.addLayer(lb -> { lb.layerType(LayerType.PRIMARY); lb.crownClosure(40.2f); lb.utilization(7.5f); + lb.baseArea(47.0588226f); + lb.treesPerHectare(764.705872f); + lb.utilization(7.5f); + lb.inventoryTypeGroup(14); lb.empiricalRelationshipParameterIndex(33); lb.primaryGenus("C"); - // 1 + // 1 3 lb.addSpecies(sb -> { sb.genus("B"); sb.percentGenus(10); @@ -2224,7 +2228,7 @@ void testProcessVeteran() throws Exception { }); }); - // 2 (Primary) + // 2 4 (Primary) lb.addSpecies(sb -> { sb.genus("C"); sb.percentGenus(50); @@ -2241,7 +2245,7 @@ void testProcessVeteran() throws Exception { }); }); - // 3 + // 3 8 lb.addSpecies(sb -> { sb.genus("H"); sb.percentGenus(40); @@ -2267,7 +2271,7 @@ void testProcessVeteran() throws Exception { assertThat(result, hasProperty("polygonIdentifier", isPolyId("TestPoly", 2024))); assertThat(result, hasProperty("biogeoclimaticZone", is("IDF"))); assertThat(result, hasProperty("forestInventoryZone", blankString())); - assertThat(result, hasProperty("mode", present(is(PolygonMode.BATN)))); + assertThat(result, hasProperty("mode", present(is(PolygonMode.START)))); assertThat(result, hasProperty("percentAvailable", is(85f))); var resultLayer = assertHasPrimaryLayer(result); @@ -2283,7 +2287,7 @@ void testProcessVeteran() throws Exception { assertThat(resultLayer, hasProperty("empiricalRelationshipParameterIndex", present(is(33)))); assertThat( - resultLayer, hasProperty("loreyHeightByUtilization", utilizationHeight(4.14067888f, 6.61390257f)) + resultLayer, hasProperty("loreyHeightByUtilization", utilizationHeight(5.45770216f, 21.0985336f)) ); assertThat( resultLayer, From 593b40ff50702b36a6a33935fa2b1340f51435f8 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Tue, 13 Aug 2024 18:20:01 -0700 Subject: [PATCH 03/53] Veteran layer processing test works for primary layer --- .../ca/bc/gov/nrs/vdyp/vri/VriStartTest.java | 72 ++----------------- 1 file changed, 5 insertions(+), 67 deletions(-) diff --git a/vdyp-lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriStartTest.java b/vdyp-lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriStartTest.java index 3e37b7392..a8f5e5e21 100644 --- a/vdyp-lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriStartTest.java +++ b/vdyp-lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriStartTest.java @@ -2294,7 +2294,7 @@ resultLayer, hasProperty("loreyHeightByUtilization", utilizationHeight(5.4577021 hasProperty( "baseAreaByUtilization", utilization( - 0.0679966733f, 6.34290648f, 4.24561071f, 1.01540196f, 0.571661115f, 0.510232806f + 0.0787888616f, 47.0588226f, 0.787343979f, 2.33701372f, 3.97268224f, 39.9617844f ) ) ); @@ -2302,14 +2302,14 @@ resultLayer, hasProperty("loreyHeightByUtilization", utilizationHeight(5.4577021 resultLayer, hasProperty( "quadraticMeanDiameterByUtilization", - utilization(5.58983135f, 10.3879948f, 9.11466217f, 13.9179964f, 18.6690178f, 25.3685265f) + utilization(5.89174175f, 27.9916744f, 9.26363468f, 14.1112642f, 18.8414402f, 37.8068199f) ) ); assertThat( resultLayer, hasProperty( "treesPerHectareByUtilization", - utilization(27.707695f, 748.4021f, 650.682556f, 66.7413025f, 20.8836231f, 10.094574f) + utilization(28.8993168f, 764.704102f, 116.818542f, 149.430603f, 142.483887f, 355.971069f) ) ); @@ -2317,74 +2317,12 @@ resultLayer, hasProperty("loreyHeightByUtilization", utilizationHeight(5.4577021 resultLayer, hasProperty( "closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", - utilization(0, 4.73118162f, 0.0503439531f, 1.59589052f, 1.62338901f, 1.46155834f) + utilization(0, 252.98407f, 0.0354338735f, 4.66429567f, 14.5271645f, 233.757172f) ) ); assertThat( resultLayer.getSpecies(), - allOf(aMapWithSize(5), hasKey("B"), hasKey("C"), hasKey("F"), hasKey("H"), hasKey("S")) - ); - - VdypSpecies resultSpecB = TestUtils.assertHasSpecies(resultLayer, "B", "C", "F", "H", "S"); - - assertThat( - resultSpecB, - hasProperty( - "baseAreaByUtilization", - utilization( - 0.0116237309f, 0.634290636f, 0.239887208f, 0.196762085f, 0.102481194f, 0.095160149f - ) - ) - ); - assertThat( - resultSpecB, - hasProperty( - "quadraticMeanDiameterByUtilization", - utilization(5.61674118f, 12.9407434f, 9.93954372f, 14.3500404f, 19.1790199f, 27.5482502f) - ) - ); - assertThat( - resultSpecB, - hasProperty( - "treesPerHectareByUtilization", - utilization(4.69123125f, 48.2258606f, 30.9160728f, 12.1659298f, 3.54732919f, 1.59653044f) - ) - ); - - assertThat( - resultSpecB, - hasProperty( - "wholeStemVolumeByUtilization", - utilization(0.0244281366f, 2.41518188f, 0.747900844f, 0.752810001f, 0.4540295f, 0.46044156f) - ) - ); - assertThat( - resultSpecB, - hasProperty( - "closeUtilizationVolumeByUtilization", - utilization(0, 1.28733742f, 0.0235678982f, 0.464995325f, 0.378819793f, 0.41995436f) - ) - ); - assertThat( - resultSpecB, - hasProperty( - "closeUtilizationVolumeNetOfDecayByUtilization", - utilization(0, 1.24826729f, 0.0230324566f, 0.454239398f, 0.369579285f, 0.401416153f) - ) - ); - assertThat( - resultSpecB, - hasProperty( - "closeUtilizationVolumeNetOfDecayAndWasteByUtilization", - utilization(0, 1.23482728f, 0.0228475146f, 0.450360179f, 0.366144955f, 0.395474672f) - ) - ); - assertThat( - resultSpecB, - hasProperty( - "closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", - utilization(0, 1.20897281f, 0.0223761573f, 0.441060275f, 0.358547896f, 0.386988521f) - ) + allOf(aMapWithSize(3), hasKey("B"), hasKey("C"), hasKey("H")) ); app.close(); From 71c0537c7b58e0b7b0e220a44f8fef6fba87d33d Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Wed, 14 Aug 2024 13:16:27 -0700 Subject: [PATCH 04/53] Remove .orig files that accidentally got commited --- .../ca/bc/gov/nrs/vdyp/fip/FipStart.java.orig | 2048 -------- .../gov/nrs/vdyp/fip/FipStartTest.java.orig | 4519 ----------------- 2 files changed, 6567 deletions(-) delete mode 100644 vdyp-lib/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipStart.java.orig delete mode 100644 vdyp-lib/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/FipStartTest.java.orig diff --git a/vdyp-lib/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipStart.java.orig b/vdyp-lib/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipStart.java.orig deleted file mode 100644 index 92d20c230..000000000 --- a/vdyp-lib/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipStart.java.orig +++ /dev/null @@ -1,2048 +0,0 @@ -package ca.bc.gov.nrs.vdyp.fip; - -import static ca.bc.gov.nrs.vdyp.math.FloatMath.abs; -import static ca.bc.gov.nrs.vdyp.math.FloatMath.clamp; -import static ca.bc.gov.nrs.vdyp.math.FloatMath.exp; -import static ca.bc.gov.nrs.vdyp.math.FloatMath.log; -import static ca.bc.gov.nrs.vdyp.math.FloatMath.pow; -<<<<<<< HEAD -======= -import static ca.bc.gov.nrs.vdyp.math.FloatMath.sqrt; ->>>>>>> origin/feature/VDYP-362-compatibility-variables -import static java.lang.Math.max; -import static java.lang.Math.min; - -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.util.Arrays; -import java.util.Collection; -import java.util.Comparator; -import java.util.EnumMap; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Objects; -import java.util.Optional; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.ToDoubleFunction; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.apache.commons.math3.analysis.MultivariateMatrixFunction; -import org.apache.commons.math3.analysis.MultivariateVectorFunction; -import org.apache.commons.math3.fitting.leastsquares.LeastSquaresFactory; -import org.apache.commons.math3.fitting.leastsquares.LeastSquaresProblem; -import org.apache.commons.math3.fitting.leastsquares.LevenbergMarquardtOptimizer; -import org.apache.commons.math3.linear.DiagonalMatrix; -import org.apache.commons.math3.linear.RealMatrix; -import org.apache.commons.math3.linear.RealVector; -import org.apache.commons.math3.util.FastMath; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import ca.bc.gov.nrs.vdyp.application.LowValueException; -import ca.bc.gov.nrs.vdyp.application.ProcessingException; -import ca.bc.gov.nrs.vdyp.application.StandProcessingException; -import ca.bc.gov.nrs.vdyp.application.VdypApplicationIdentifier; -import ca.bc.gov.nrs.vdyp.application.VdypStartApplication; -import ca.bc.gov.nrs.vdyp.common.ControlKey; -<<<<<<< HEAD -======= -import ca.bc.gov.nrs.vdyp.common.EstimationMethods; -import ca.bc.gov.nrs.vdyp.common.IndexedFloatBinaryOperator; -import ca.bc.gov.nrs.vdyp.common.ReconcilationMethods; ->>>>>>> origin/feature/VDYP-362-compatibility-variables -import ca.bc.gov.nrs.vdyp.common.Utils; -import ca.bc.gov.nrs.vdyp.common.ValueOrMarker; -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.FipPolygon; -import ca.bc.gov.nrs.vdyp.fip.model.FipSite; -import ca.bc.gov.nrs.vdyp.fip.model.FipSpecies; -import ca.bc.gov.nrs.vdyp.io.parse.coe.BecDefinitionParser; -<<<<<<< HEAD -======= -import ca.bc.gov.nrs.vdyp.io.parse.coe.GenusDefinitionParser; ->>>>>>> origin/feature/VDYP-362-compatibility-variables -import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; -import ca.bc.gov.nrs.vdyp.io.parse.control.BaseControlParser; -import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParser; -import ca.bc.gov.nrs.vdyp.model.BaseVdypLayer; -import ca.bc.gov.nrs.vdyp.model.BaseVdypPolygon; -import ca.bc.gov.nrs.vdyp.model.BaseVdypSite; -import ca.bc.gov.nrs.vdyp.model.BaseVdypSpecies; -import ca.bc.gov.nrs.vdyp.model.BecDefinition; -import ca.bc.gov.nrs.vdyp.model.Coefficients; -<<<<<<< HEAD -import ca.bc.gov.nrs.vdyp.model.CompatibilityVariableMode; -import ca.bc.gov.nrs.vdyp.model.PolygonMode; -import ca.bc.gov.nrs.vdyp.model.LayerType; -import ca.bc.gov.nrs.vdyp.model.MatrixMap; -import ca.bc.gov.nrs.vdyp.model.MatrixMap2; -======= -import ca.bc.gov.nrs.vdyp.model.LayerType; -import ca.bc.gov.nrs.vdyp.model.MatrixMap; -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.PolygonMode; ->>>>>>> origin/feature/VDYP-362-compatibility-variables -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; -import ca.bc.gov.nrs.vdyp.model.VdypUtilizationHolder; -import ca.bc.gov.nrs.vdyp.model.VolumeComputeMode; - -public class FipStart extends VdypStartApplication { - - public static final Comparator PERCENT_GENUS_DESCENDING = Utils - .compareUsing(FipSpecies::getPercentGenus).reversed(); - - public static final Logger log = LoggerFactory.getLogger(FipStart.class); -<<<<<<< HEAD -======= - - public static final int UTIL_ALL = UtilizationClass.ALL.index; - public static final int UTIL_LARGEST = UtilizationClass.OVER225.index; - public static final int UTIL_SMALL = UtilizationClass.SMALL.index; ->>>>>>> origin/feature/VDYP-362-compatibility-variables - - public static final float TOLERANCE = 2.0e-3f; - - public static void main(final String... args) throws IOException { - - try (var app = new FipStart();) { - doMain(app, args); - } - } - - // FIP_SUB - // TODO Fortran takes a vector of flags (FIPPASS) controlling which stages are - // implemented. FIPSTART always uses the same vector so far now that's not - // implemented. - @Override - public void process() throws ProcessingException { - int polygonsRead = 0; - int polygonsWritten = 0; - try ( - var polyStream = this.getStreamingParser(ControlKey.FIP_INPUT_YIELD_POLY); - var layerStream = this.>getStreamingParser(ControlKey.FIP_INPUT_YIELD_LAYER); - var speciesStream = this.>getStreamingParser(ControlKey.FIP_INPUT_YIELD_LX_SP0); - ) { - log.atDebug().setMessage("Start Stand processing").log(); - - while (polyStream.hasNext()) { - - // FIP_GET - log.atInfo().setMessage("Getting polygon {}").addArgument(polygonsRead + 1).log(); - var polygon = getPolygon(polyStream, layerStream, speciesStream); - try { - - var resultPoly = processPolygon(polygonsRead, polygon); - if (resultPoly.isPresent()) { - polygonsRead++; - - // Output - vriWriter.writePolygonWithSpeciesAndUtilization(resultPoly.get()); - - polygonsWritten++; - } - - log.atInfo().setMessage("Read {} polygons and wrote {}").addArgument(polygonsRead) - .addArgument(polygonsWritten); - - } catch (StandProcessingException ex) { - // TODO include some sort of hook for different forms of user output - // TODO Implement single stand mode that propagates the exception - - log.atWarn().setMessage("Polygon {} bypassed").addArgument(polygon.getPolygonIdentifier()) - .setCause(ex); - } - - } - } catch (IOException | ResourceParseException ex) { - throw new ProcessingException("Error while reading or writing data.", ex); - } - } - - static final EnumSet ACCEPTABLE_MODES = EnumSet.of(PolygonMode.START, PolygonMode.YOUNG); - - Optional processPolygon(int polygonsRead, FipPolygon polygon) - throws ProcessingException, LowValueException { - VdypPolygon resultPoly; - log.atInfo().setMessage("Read polygon {}, preparing to process").addArgument(polygon.getPolygonIdentifier()) - .log(); - - // if (MODE .eq. -1) go to 100 - - final var mode = polygon.getMode().orElse(PolygonMode.START); - - if (!ACCEPTABLE_MODES.contains(mode)) { - log.atInfo().setMessage("Skipping polygon with mode {}").addArgument(mode).log(); - return Optional.empty(); - } - - // IP_IN = IP_IN+1 - // if (IP_IN .gt. MAXPOLY) go to 200 - - // IPASS = 1 - // CALL FIP_CHK( IPASS, IER) - // if (ier .gt. 0) go to 1000 - // - // if (IPASS .le. 0) GO TO 120 - - log.atInfo().setMessage("Checking validity of polygon {}:{}").addArgument(polygonsRead) - .addArgument(polygon.getPolygonIdentifier()).log(); - checkPolygon(polygon); - - // CALL FIPCALCV( BAV, IER) - // CALL FIPCALC1( BAV, BA_TOTL1, IER) - - Map processedLayers = new EnumMap<>(LayerType.class); - - var fipLayers = polygon.getLayers(); - var fipVetLayer = Optional.ofNullable(fipLayers.get(LayerType.VETERAN)); - Optional resultVetLayer; - if (fipVetLayer.isPresent()) { - resultVetLayer = Optional.of(processLayerAsVeteran(polygon, fipVetLayer.get())); - } else { - resultVetLayer = Optional.empty(); - } - resultVetLayer.ifPresent(layer -> processedLayers.put(LayerType.VETERAN, layer)); - - FipLayerPrimary fipPrimeLayer = (FipLayerPrimary) fipLayers.get(LayerType.PRIMARY); - assert fipPrimeLayer != null; - var resultPrimeLayer = processLayerAsPrimary( - polygon, fipPrimeLayer, - resultVetLayer.map(VdypLayer::getBaseAreaByUtilization).map(coe -> coe.getCoe(UTIL_ALL)).orElse(0f) - ); - processedLayers.put(LayerType.PRIMARY, resultPrimeLayer); - - resultPoly = createVdypPolygon(polygon, processedLayers); - - float baseAreaTotalPrime = resultPrimeLayer.getBaseAreaByUtilization().getCoe(UTIL_ALL); // BA_TOTL1 - - // if (FIPPASS(6) .eq. 0 .or. FIPPASS(6) .eq. 2) then - if (true /* TODO */) { - var minima = Utils.>expectParsedControl(controlMap, ControlKey.MINIMA, Map.class); - - float minimumBaseArea = minima.get(BaseControlParser.MINIMUM_BASE_AREA); - float minimumPredictedBaseArea = minima.get(BaseControlParser.MINIMUM_FULLY_STOCKED_AREA); - if (baseAreaTotalPrime < minimumBaseArea) { - throw new LowValueException("Base area", baseAreaTotalPrime, minimumBaseArea); - } - float predictedBaseArea = baseAreaTotalPrime * (100f / resultPoly.getPercentAvailable()); - if (predictedBaseArea < minimumPredictedBaseArea) { - throw new LowValueException("Predicted base area", predictedBaseArea, minimumPredictedBaseArea); - } - } - BecDefinition bec = BecDefinitionParser.getBecs(controlMap).get(polygon.getBiogeoclimaticZone()) - .orElseThrow(() -> new ProcessingException("Missing Bec " + polygon.getBiogeoclimaticZone())); - // FIPSTK - adjustForStocking(resultPoly.getLayers().get(LayerType.PRIMARY), fipPrimeLayer, bec); - return Optional.of(resultPoly); - } - - // FIPSTK - void adjustForStocking(VdypLayer vdypLayer, FipLayerPrimary fipLayerPrimary, BecDefinition bec) { - - MatrixMap2> stockingClassMap = Utils - .expectParsedControl(controlMap, ControlKey.STOCKING_CLASS_FACTORS, MatrixMap2.class); - - Region region = bec.getRegion(); - - var factorEntry = fipLayerPrimary.getStockingClass() - .flatMap(stockingClass -> MatrixMap.safeGet(stockingClassMap, stockingClass, region)); - - if (!factorEntry.isPresent()) { - return; - } - - float factor = factorEntry.get().getFactor(); - - scaleAllSummableUtilization(vdypLayer, factor); - vdypLayer.getSpecies().values().forEach(spec -> scaleAllSummableUtilization(spec, factor)); - - log.atInfo().addArgument(fipLayerPrimary.getStockingClass()).addArgument(factor).setMessage( - "Foregoing Primary Layer has stocking class {} Yield values will be multiplied by {} before being written to output file." - ); - } - - VdypPolygon createVdypPolygon(FipPolygon fipPolygon, Map processedLayers) - throws ProcessingException { - Optional fipVetLayer = Utils.optSafe(fipPolygon.getLayers().get(LayerType.VETERAN)); - FipLayerPrimary fipPrimaryLayer = (FipLayerPrimary) fipPolygon.getLayers().get(LayerType.PRIMARY); - - float percentAvailable = estimatePercentForestLand(fipPolygon, fipVetLayer, fipPrimaryLayer); - - var vdypPolygon = VdypPolygon.build(builder -> builder.adapt(fipPolygon, x -> percentAvailable)); - vdypPolygon.setLayers(processedLayers); - return vdypPolygon; - } - - // FIPCALC1 - VdypLayer processLayerAsPrimary(FipPolygon fipPolygon, FipLayerPrimary fipLayer, float baseAreaOverstory) - throws ProcessingException { - - // PRIMFIND - var primarySpecies = findPrimarySpecies(fipLayer.getSpecies().values()); - - // There's always at least one entry and we want the first. - fipLayer.setPrimaryGenus(Optional.of(primarySpecies.iterator().next().getGenus())); - - // VDYP7 stores this in the common FIPL_1C/ITGL1 but only seems to use it - // locally - var itg = findItg(primarySpecies); - - BecDefinition bec = Utils.getBec(fipPolygon.getBiogeoclimaticZone(), controlMap); - - // GRPBA1FD - int empiricalRelationshipParameterIndex = findEmpiricalRelationshipParameterIndex( - primarySpecies.get(0).getGenus(), bec, itg - ); - - var result = VdypLayer.build(builder -> { - builder.adapt(fipLayer); - builder.inventoryTypeGroup(itg); - builder.empiricalRelationshipParameterIndex(empiricalRelationshipParameterIndex); - fipLayer.getSite().ifPresent(site -> { - builder.addSite(siteBuilder -> { - siteBuilder.adapt(site); - }); - }); - - }); - - // EMP040 - var baseArea = estimatePrimaryBaseArea( - fipLayer, bec, fipPolygon.getYieldFactor(), result.getBreastHeightAge().orElse(0f), baseAreaOverstory - ); // BA_TOT - - result.getBaseAreaByUtilization().setCoe(UTIL_ALL, baseArea); - - var quadMeanDiameter = estimatePrimaryQuadMeanDiameter( - fipLayer, bec, result.getBreastHeightAge().orElse(0f), baseAreaOverstory - ); - - result.getQuadraticMeanDiameterByUtilization().setCoe(UTIL_ALL, quadMeanDiameter); - - var tphTotal = BaseAreaTreeDensityDiameter.treesPerHectare(baseArea, quadMeanDiameter); - - result.getTreesPerHectareByUtilization().setCoe(UTIL_ALL, tphTotal); - - // Copy over Species entries. - // LVCOM/ISPL1=ISPV - // LVCOM4/SP0L1=FIPSA/SP0V - // LVCOM4/SP64DISTL1=FIPSA/VDISTRV - // LVCOM1/PCLT1=FIPS/PCTVOLV - var vdypSpecies = fipLayer.getSpecies().values().stream() // - .map(VdypSpecies::new) // - .collect(Collectors.toMap(VdypSpecies::getGenus, Function.identity())); - - var vdypPrimarySpecies = vdypSpecies.get(primarySpecies.get(0).getGenus()); - - Map targetPercentages = applyGroups(fipPolygon, vdypSpecies.values()); - - var maxPass = fipLayer.getSpecies().size() > 1 ? 2 : 1; - - result.setSpecies(vdypSpecies); - - float primaryHeight; - float leadHeight = fipLayer.getHeight().orElse(0f); - for (var iPass = 1; iPass <= maxPass; iPass++) { - if (iPass == 2) { - for (var vSpec : vdypSpecies.values()) { - vSpec.setPercentGenus(targetPercentages.get(vSpec.getGenus())); - } - } - // Estimate lorey height for primary species - if (iPass == 1 && vdypSpecies.size() == 1) { - primaryHeight = emp.primaryHeightFromLeadHeight( - leadHeight, vdypPrimarySpecies.getGenus(), bec.getRegion(), tphTotal - ); - } else if (iPass == 1) { - primaryHeight = emp - .primaryHeightFromLeadHeightInitial(leadHeight, vdypPrimarySpecies.getGenus(), bec.getRegion()); - } else { - primaryHeight = emp.primaryHeightFromLeadHeight( - leadHeight, vdypPrimarySpecies.getGenus(), bec.getRegion(), - vdypPrimarySpecies.getTreesPerHectareByUtilization().getCoe(UTIL_ALL) - ); - } - vdypPrimarySpecies.getLoreyHeightByUtilization().setCoe(UTIL_ALL, primaryHeight); - - // Estimate lorey height for non-primary species - for (var vspec : vdypSpecies.values()) { - if (vspec == vdypPrimarySpecies) - continue; - - // EMP053 - vspec.getLoreyHeightByUtilization().setCoe( - UTIL_ALL, - emp.estimateNonPrimaryLoreyHeight(vspec, vdypPrimarySpecies, bec, leadHeight, primaryHeight) - ); - } - - // ROOTF01 - findRootsForDiameterAndBaseArea(result, fipLayer, bec, iPass + 1); - } - - estimateSmallComponents(fipPolygon, result); - - // YUC1 - computeUtilizationComponentsPrimary(bec, result, VolumeComputeMode.BY_UTIL, CompatibilityVariableMode.NONE); - - return result; - } - - public static List utilizationArray(VdypLayer layer, Function accessor) { - return Stream.concat(Stream.of(layer), layer.getSpecies().values().stream()).map(accessor).toList(); - } - - // ROOTF01 - void findRootsForDiameterAndBaseArea(VdypLayer result, FipLayerPrimary fipLayer, BecDefinition bec, int source) - throws ProcessingException { - - var quadMeanDiameterTotal = result.getQuadraticMeanDiameterByUtilization().getCoe(UTIL_ALL); // DQ_TOT - var baseAreaTotal = result.getBaseAreaByUtilization().getCoe(UTIL_ALL); // BA_TOT - var treesPerHectareTotal = result.getTreesPerHectareByUtilization().getCoe(UTIL_ALL); // TPH_TOT - Map goal = new LinkedHashMap<>(); // GOAL - Map xMap = new LinkedHashMap<>(); // X - - float treesPerHectareSum; - - assert result.getSpecies().size() > 0; - - if (result.getSpecies().size() == 1) { - var spec = result.getSpecies().values().iterator().next(); - for (var accessors : NON_VOLUME_UTILIZATION_VECTOR_ACCESSORS) { - - try { - Coefficients specVector = (Coefficients) accessors.getReadMethod().invoke(spec); - Coefficients layerVector = (Coefficients) accessors.getReadMethod().invoke(result); - specVector.setCoe(UTIL_ALL, layerVector.getCoe(UTIL_ALL)); - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { - throw new IllegalStateException(e); - } - } - - result.getLoreyHeightByUtilization().setCoe(UTIL_ALL, spec.getLoreyHeightByUtilization().getCoe(UTIL_ALL)); - spec.setPercentGenus(100f); - treesPerHectareSum = treesPerHectareTotal; - } else { - // Multiple Species - for (var spec : result.getSpecies().values()) { - - var limits = emp.getLimitsForHeightAndDiameter(spec.getGenus(), bec.getRegion()); - - final float maxHeightMultiplier = fipLayer.getPrimaryGenus() - .orElseThrow(() -> new IllegalStateException("primaryGenus has not been set")) - .equals(spec.getGenus()) ? 1.5f : 1.0f; - final float heightMax = limits.maxLoreyHeight() * maxHeightMultiplier; - - spec.getLoreyHeightByUtilization().scalarInPlace(UTIL_ALL, x -> min(x, heightMax)); - } - ToDoubleFunction accessor; - - switch (source) { - case 1: - accessor = x -> x.getPercentGenus(); - break; - case 2: - accessor = x -> x.getPercentGenus() / x.getLoreyHeightByUtilization().getCoe(UTIL_ALL); - break; - case 3: - accessor = x -> x.getBaseAreaByUtilization().getCoe(UTIL_ALL); - break; - default: - throw new UnsupportedOperationException("Unknown source for root finding " + source); - } - - var sumSourceArea = result.getSpecies().values().stream().mapToDouble(accessor).sum(); - - // FRJ - var fractionMap = result.getSpecies().values().stream().collect( - Collectors.toMap( - VdypSpecies::getGenus, spec -> (float) (accessor.applyAsDouble(spec) / sumSourceArea) - ) - ); - - // HL_TOT - float loreyHeightTotal = (float) fractionMap.entrySet().stream().mapToDouble( - e -> e.getValue() * result.getSpecies().get(e.getKey()).getLoreyHeightByUtilization().getCoe(0) - ).sum(); - // FRJ(ISP) = FRJ(J) // We aren't using the remapping between global species - // index and index for the species within the layer, so we can probably assign - // directly to the fraction attribute on the species object. - fractionMap.entrySet().forEach(e -> result.getSpecies().get(e.getKey()).setFractionGenus(e.getValue())); - - double[] quadMeanDiameterBase = new double[result.getSpecies().size()]; // DQspbase - - { - int i = 0; - for (var spec : result.getSpecies().values()) { - - // EMP061 - var limits = emp.getLimitsForHeightAndDiameter(spec.getGenus(), bec.getRegion()); - - var dqMin = limits.minDiameterHeight() * spec.getLoreyHeightByUtilization().getCoe(UTIL_ALL); - var dqMax = max( - limits.maxQuadMeanDiameter(), - limits.maxDiameterHeight() * spec.getLoreyHeightByUtilization().getCoe(UTIL_ALL) - ); - - // EMP060 - float quadMeanDiameter = clamp( - emp.estimateQuadMeanDiameterForSpecies( - spec, result.getSpecies(), bec.getRegion(), quadMeanDiameterTotal, baseAreaTotal, - treesPerHectareTotal, loreyHeightTotal - ), // - dqMin, dqMax - ); - - quadMeanDiameterBase[i++] = quadMeanDiameter; - } - } - // VDYP7 checks the number of species here, but this is already inside a branch - // that must be more than 1 - // Fill in target and trial values - - Utils.eachButLast(result.getSpecies().values(), spec -> { - goal.put(spec.getGenus(), spec.getPercentGenus()); - xMap.put(spec.getGenus(), spec.getPercentGenus()); - }, spec -> { - goal.put(spec.getGenus(), quadMeanDiameterTotal); - xMap.put(spec.getGenus(), 0f); - }); - - var xVec = xMap.values().stream().mapToDouble(v -> (double) v).toArray(); - var goalVec = goal.values().stream().mapToDouble(v -> (double) v).toArray(); - - // SNQSOL - var rootVec = this.findRoot(quadMeanDiameterBase, goalVec, xVec, result, TOLERANCE); - - var rootMap = new LinkedHashMap(); - { - float percentSum = 0; - var it = result.getSpecies().values().iterator(); - for (int i = 0; it.hasNext(); i++) { - var spec = it.next(); - rootMap.put(spec.getGenus(), (float) rootVec.getEntry(i)); - if (it.hasNext()) { - spec.setPercentGenus((float) rootVec.getEntry(i)); - percentSum += rootVec.getEntry(i); - } else { - spec.setPercentGenus(100 - percentSum); - } - } - } - - float loreyHeightSum = 0; - treesPerHectareSum = 0; - - { - int i = 0; - for (var spec : result.getSpecies().values()) { - float dqBase = (float) quadMeanDiameterBase[i++]; - float dq = 7.5f + (dqBase - 7.5f) * exp((float) rootVec.getEntry(rootVec.getDimension() - 1) / 20f); - assert dq >= 0; - float ba = baseAreaTotal * spec.getPercentGenus() / 100f; - assert ba >= 0; - float tph = BaseAreaTreeDensityDiameter.treesPerHectare(ba, dq); - assert tph >= 0; - spec.getQuadraticMeanDiameterByUtilization().setCoe(UTIL_ALL, dq); - spec.getBaseAreaByUtilization().setCoe(UTIL_ALL, ba); - spec.getTreesPerHectareByUtilization().setCoe(UTIL_ALL, tph); - treesPerHectareSum += tph; - loreyHeightSum += spec.getLoreyHeightByUtilization().getCoe(UTIL_ALL) * ba; - } - } - result.getLoreyHeightByUtilization().setCoe(UTIL_ALL, loreyHeightSum / baseAreaTotal); - - } // end of Multiple Species branch - - var volumeSum = 0f; - - for (var spec : result.getSpecies().values()) { - // EMP090 - var wholeStemVolume = spec.getTreesPerHectareByUtilization().getCoe(UTIL_ALL) - * EstimationMethods.estimateWholeStemVolumePerTree( - controlMap, spec.getVolumeGroup(), spec.getLoreyHeightByUtilization().getCoe(UTIL_ALL), - spec.getQuadraticMeanDiameterByUtilization().getCoe(UTIL_ALL) - ); - spec.getWholeStemVolumeByUtilization().setCoe(UTIL_ALL, wholeStemVolume); - volumeSum += wholeStemVolume; - } - - result.getWholeStemVolumeByUtilization().setCoe(UTIL_ALL, volumeSum); - var treesPerHectareStart = result.getTreesPerHectareByUtilization().getCoe(UTIL_ALL); - result.getTreesPerHectareByUtilization().setCoe(UTIL_ALL, treesPerHectareSum); - result.getQuadraticMeanDiameterByUtilization().setCoe( - UTIL_ALL, - BaseAreaTreeDensityDiameter.quadMeanDiameter( - result.getBaseAreaByUtilization().getCoe(UTIL_ALL), - result.getTreesPerHectareByUtilization().getCoe(UTIL_ALL) - ) - ); - - if (abs(treesPerHectareStart / result.getTreesPerHectareByUtilization().getCoe(UTIL_ALL) - 1f) > 0.002) { - throw new ProcessingException("TODO"); - } - - if (result.getSpecies().size() > 1) { - for (var spec : result.getSpecies().values()) { - if (spec.getWholeStemVolumeByUtilization().getCoe(UTIL_ALL) / volumeSum - - goal.get(spec.getGenus()) > 0.1) { - throw new ProcessingException("TODO"); - } - } - } - } - -<<<<<<< HEAD - // EMP090 - private float estimateWholeStemVolumePerTree(int volumeGroup, float loreyHeight, float quadMeanDiameter) { - var coeMap = Utils.>expectParsedControl( - controlMap, ControlKey.TOTAL_STAND_WHOLE_STEM_VOL, Map.class - ); - var coe = coeMap.get(volumeGroup).reindex(0); - - var logMeanVolume = coe.getCoe(UTIL_ALL) + // - coe.getCoe(1) * log(quadMeanDiameter) + // - coe.getCoe(2) * log(loreyHeight) + // - coe.getCoe(3) * quadMeanDiameter + // - coe.getCoe(4) / quadMeanDiameter + // - coe.getCoe(5) * loreyHeight + // - coe.getCoe(6) * quadMeanDiameter * quadMeanDiameter + // - coe.getCoe(7) * loreyHeight * quadMeanDiameter // - + coe.getCoe(8) * loreyHeight / quadMeanDiameter; - - return exp(logMeanVolume); -======= - // EMP060 - /** - * Estimate DQ for a species (primary or not). Using eqn in jf125.doc. - * - * Enforces mins and maxes from EMP061. - * - * @param spec Species of insterest - * @param allSpecies Collection of all species on the layer - * @param region BEC Region of the stand - * @param standQuadMeanDiameter Quadratic mean diameter of the stand - * @param standBaseArea Base area of the stand - * @param standTreesPerHectare Density opf the stand - * @param standLoreyHeight Lorey height of the stand - * @return Quadratic mean diameter of the species of interest - * @throws ProcessingException - */ - float estimateQuadMeanDiameterForSpecies( - VdypSpecies spec, // ISP, HLsp, DQsp - Map allSpecies, // FR - Region region, // INDEX_IC - float standQuadMeanDiameter, // DQ_TOT - float standBaseArea, // BA_TOT - float standTreesPerHectare, // TPH_TOT - float standLoreyHeight // HL_TOT - ) throws ProcessingException { - String species = spec.getGenus(); - - float c = 0.00441786467f; - - float minQuadMeanDiameter = min(7.6f, standQuadMeanDiameter); - - // Quick solution - if (spec.getFractionGenus() >= 1f || standQuadMeanDiameter < minQuadMeanDiameter) { - return standQuadMeanDiameter; - } - - var coeMap = Utils - .>expectParsedControl(controlMap, ControlKey.BY_SPECIES_DQ, Map.class); - var specAliases = GenusDefinitionParser.getSpeciesAliases(controlMap); - - // TODO we can probably remove these as they seem to only be used for debugging - // in VDYP7 - Map adjust = new HashMap<>(coeMap.size()); - Map mult = new HashMap<>(coeMap.size()); - - var specIt = specAliases.iterator(); - - var spec1 = specIt.next(); - - float a2 = coeMap.get(spec1).getCoe(2); - - float fractionOther = 1f - spec.getFractionGenus(); // FR_REST - - mult.put(spec1, 1f); - float a0 = coeMap.get(spec1).getCoe(0); - float a1 = coeMap.get(spec1).getCoe(1); - - while (specIt.hasNext()) { - var specIAlias = specIt.next(); - var specI = allSpecies.get(specIAlias); - if (specIAlias.equals(spec.getGenus())) { - float multI = 1f; - mult.put(specIAlias, multI); - a0 += multI * coeMap.get(specIAlias).getCoe(0); - a1 += multI * coeMap.get(specIAlias).getCoe(1); - } else { - if (specI != null && specI.getFractionGenus() > 0f) { - float multI = -specI.getFractionGenus() / fractionOther; - mult.put(specIAlias, multI); - a0 += multI * coeMap.get(specIAlias).getCoe(0); - a1 -= multI * coeMap.get(specIAlias).getCoe(1); - } - } - } - - float loreyHeightSpec = spec.getLoreyHeightByUtilization().getCoe(UTIL_ALL); - float loreyHeight1 = max(4f, loreyHeightSpec); - float loreyHeight2 = (standLoreyHeight - loreyHeightSpec * spec.getFractionGenus()) / fractionOther; - float loreyHeightRatio = clamp( (loreyHeight1 - 3f) / (loreyHeight2 - 3f), 0.05f, 20f); - - float r = exp( - a0 + a1 * log(loreyHeightRatio) + a2 * log(standQuadMeanDiameter) + adjust.getOrDefault(species, 0f) - ); - - float baseArea1 = spec.getFractionGenus() * standBaseArea; - float baseArea2 = standBaseArea - baseArea1; - - float treesPerHectare1; - if (abs(r - 1f) < 0.0005) { - treesPerHectare1 = spec.getFractionGenus() * standTreesPerHectare; - } else { - float aa = (r - 1f) * c; - float bb = c * (1f - r) * standTreesPerHectare + baseArea1 + baseArea2 * r; - float cc = -baseArea1 * standTreesPerHectare; - float term = bb * bb - 4 * aa * cc; - if (term <= 0f) { - throw new ProcessingException( - "Term for trees per hectare calculation when estimating quadratic mean diameter for species " - + species + " was " + term + " but should be positive." - ); - } - treesPerHectare1 = (-bb + sqrt(term)) / (2f * aa); - if (treesPerHectare1 <= 0f || treesPerHectare1 > standTreesPerHectare) { - throw new ProcessingException( - "Trees per hectare 1 for species " + species + " was " + treesPerHectare1 - + " but should be positive and less than or equal to stand trees per hectare " - + standTreesPerHectare - ); - } - } - - float quadMeanDiameter1 = BaseAreaTreeDensityDiameter.quadMeanDiameter(baseArea1, treesPerHectare1); - float treesPerHectare2 = standTreesPerHectare - treesPerHectare1; - float quadMeanDiameter2 = BaseAreaTreeDensityDiameter.quadMeanDiameter(baseArea2, treesPerHectare2); - - if (quadMeanDiameter2 < minQuadMeanDiameter) { - // species 2 is too small. Make target species smaller. - quadMeanDiameter2 = minQuadMeanDiameter; - treesPerHectare2 = BaseAreaTreeDensityDiameter.treesPerHectare(baseArea2, quadMeanDiameter2); - treesPerHectare1 = standTreesPerHectare - treesPerHectare2; - quadMeanDiameter1 = BaseAreaTreeDensityDiameter.quadMeanDiameter(baseArea1, treesPerHectare1); - } - var limitCoe = getLimitsForHeightAndDiameter(species, region); - - final var dqMinSp = max(minQuadMeanDiameter, limitCoe.getCoe(3) * loreyHeightSpec); - final var dqMaxSp = max(7.6f, min(limitCoe.getCoe(2), limitCoe.getCoe(4) * loreyHeightSpec)); - if (quadMeanDiameter1 < dqMinSp) { - quadMeanDiameter1 = dqMinSp; - treesPerHectare1 = BaseAreaTreeDensityDiameter.treesPerHectare(baseArea1, quadMeanDiameter1); - treesPerHectare2 = standTreesPerHectare - treesPerHectare2; - quadMeanDiameter2 = BaseAreaTreeDensityDiameter.quadMeanDiameter(baseArea2, treesPerHectare2); - } - if (quadMeanDiameter1 > dqMaxSp) { - // target species is too big. Make target species smaller, DQ2 bigger. - - quadMeanDiameter1 = dqMaxSp; - treesPerHectare1 = BaseAreaTreeDensityDiameter.treesPerHectare(baseArea1, quadMeanDiameter1); - treesPerHectare2 = standTreesPerHectare - treesPerHectare2; - - if (treesPerHectare2 > 0f && baseArea2 > 0f) { - quadMeanDiameter2 = BaseAreaTreeDensityDiameter.quadMeanDiameter(baseArea2, treesPerHectare2); - } else { - quadMeanDiameter2 = 1000f; - } - - // under rare circumstances, let DQ1 exceed DQMAXsp - if (quadMeanDiameter2 < minQuadMeanDiameter) { - quadMeanDiameter2 = minQuadMeanDiameter; - treesPerHectare2 = BaseAreaTreeDensityDiameter.treesPerHectare(baseArea2, quadMeanDiameter2); - treesPerHectare1 = standTreesPerHectare - treesPerHectare2; - quadMeanDiameter1 = BaseAreaTreeDensityDiameter.quadMeanDiameter(baseArea1, treesPerHectare1); - } - - } - return quadMeanDiameter1; - } - - // EMP061 - private Coefficients getLimitsForHeightAndDiameter(String genus, Region region) { - var coeMap = Utils.>expectParsedControl( - controlMap, ControlKey.SPECIES_COMPONENT_SIZE_LIMIT, MatrixMap2.class - ); - - return coeMap.get(genus, region); - } - - // EMP053 Using eqns N1 and N2 from ipsjf124.doc - /** - * Estimate the lorey height of a non-primary species of a primary layer. - * - * @param vspec The species. - * @param vspecPrime The primary species. - * @param leadHeight lead height of the layer - * @param primaryHeight height of the primary species - * @throws ProcessingException - */ - float estimateNonPrimaryLoreyHeight( - VdypSpecies vspec, VdypSpecies vspecPrime, BecDefinition bec, float leadHeight, float primaryHeight - ) throws ProcessingException { - var coeMap = Utils.>>expectParsedControl( - controlMap, ControlKey.HL_NONPRIMARY, MatrixMap3.class - ); - - var coe = coeMap.get(vspec.getGenus(), vspecPrime.getGenus(), bec.getRegion()).orElseThrow( - () -> new ProcessingException( - String.format( - "Could not find Lorey Height Nonprimary Coefficients for %s %s %s", vspec.getGenus(), - vspecPrime.getGenus(), bec.getRegion() - ) - ) - ); - var heightToUse = coe.getEquationIndex() == 1 ? leadHeight : primaryHeight; - return 1.3f + coe.getCoe(1) * pow(heightToUse - 1.3f, coe.getCoe(2)); ->>>>>>> origin/feature/VDYP-362-compatibility-variables - } - - VdypLayer processLayerAsVeteran(FipPolygon fipPolygon, FipLayer fipLayer) throws ProcessingException { - - var polygonIdentifier = fipLayer.getPolygonIdentifier(); - - assert fipLayer.getLayerType().equals(LayerType.VETERAN) : "Layer must be VETERAN"; - assert fipPolygon.getPolygonIdentifier().equals(fipLayer.getPolygonIdentifier()) : String.format( - "Polygon polygonIdentifier '%s' doesn't match that of layer '%s'", fipPolygon.getPolygonIdentifier(), - fipLayer.getPolygonIdentifier() - ); - - var layer = LayerType.VETERAN; - - // find Primary genus (highest percentage) ISPPVET - - var primaryGenus = fipLayer.getSpecies().values().stream() // - .max(Utils.compareUsing(FipSpecies::getPercentGenus)) // - .orElseThrow(() -> new IllegalStateException("No primarty genus (SP0) found. This should not happen.")) - .getGenus(); - - // ageTotal copy, LVCOM3/AGETOTLV copied from FIPL_V/AGETOT_LV - 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().orElse(0f), 6.0f); - - // height? copy LVCOM3/HDLV = FIPL_V/HT_LV - var height = fipLayer.getHeight().orElse(0f); - - var crownClosure = fipLayer.getCrownClosure(); - - var becId = fipPolygon.getBiogeoclimaticZone(); - var bec = Utils.getBec(becId, controlMap); - var region = bec.getRegion(); - - // 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 = Utils.utilizationVector(estimatedBaseArea); - // Copy over Species entries. - // LVCOM/ISPLV=ISPV - // LVCOM4/SP0LV=FIPSA/SP0V - // LVCOM4/SP64DISTLV=FIPSA/VDISTRV - // LVCOM1/PCLTV=FIPS/PCTVOLV - // LVCOM1/HL=FIPL_V/HT_LV - var vdypSpecies = fipLayer.getSpecies().values().stream() // - .map(fipSpec -> { - var vs = new VdypSpecies(fipSpec); - vs.setLoreyHeightByUtilization(new Coefficients(new float[] { 0f, height }, -1)); - return vs; - }) // - .collect(Collectors.toMap(VdypSpecies::getGenus, Function.identity())); - - applyGroups(fipPolygon, vdypSpecies.values()); - - /* - * From VDYP7 - * - * At this point we SHOULD invoke a root finding procedure sets species percents and adjusts DQ by species. - * fills in main components, through whole-stem volume INSTEAD, I will assume %volumes apply to % BA's - */ - - for (var vSpec : vdypSpecies.values()) { - vSpec.getBaseAreaByUtilization() - .setCoe(UTIL_LARGEST, baseAreaByUtilization.getCoe(UTIL_LARGEST) * vSpec.getPercentGenus() / 100f); - } - - var vetDqMap = Utils.>expectParsedControl( - controlMap, ControlKey.VETERAN_LAYER_DQ, MatrixMap2.class - ); - - for (var vSpec : vdypSpecies.values()) { - var genus = vSpec.getGenus(); - var coe = vetDqMap.get(genus, region); - var a0 = coe.getCoe(1); - var a1 = coe.getCoe(2); - var a2 = coe.getCoe(3); - 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, - BaseAreaTreeDensityDiameter - .treesPerHectare(vSpec.getBaseAreaByUtilization().getCoe(UTIL_LARGEST), dq) - ); - } - - var vdypLayer = VdypLayer.build(builder -> { - builder.polygonIdentifier(polygonIdentifier); - builder.layerType(layer); - - builder.addSite(siteBuilder -> { - siteBuilder.ageTotal(ageTotal); - siteBuilder.yearsToBreastHeight(yearsToBreastHeight); - siteBuilder.height(height); - siteBuilder.siteGenus(fipLayer.getSiteGenus()); - siteBuilder.siteIndex(fipLayer.getSiteIndex()); - }); - - builder.addSpecies(vdypSpecies.values()); - }); - - vdypLayer.setBaseAreaByUtilization(baseAreaByUtilization); - - computeUtilizationComponentsVeteran(vdypLayer, bec); - - return vdypLayer; - } - -<<<<<<< HEAD -======= - enum VolumeComputeMode { - /** - * set volume components to zero - */ - ZERO, // 0 - /** - * compute volumes by utilization component - */ - BY_UTIL, // 1 - /** - * As BY_UTIL but also compute Whole Stem Volume for every species - */ - BY_UTIL_WITH_WHOLE_STEM_BY_SPEC // 2 - } - - enum CompatibilityVariableMode { - /** - * Don't apply compatibility variables - */ - NONE, // 0 - /** - * Apply compatibility variables to all but volume - */ - NO_VOLUME, // 1 - /** - * Apply compatibility variables to all components - */ - ALL // 2 - } - - // YUC1 - void computeUtilizationComponentsPrimary( - BecDefinition bec, VdypLayer vdypLayer, VolumeComputeMode volumeComputeMode, - CompatibilityVariableMode compatibilityVariableMode - ) throws ProcessingException { - log.atTrace().setMessage("computeUtilizationComponentsPrimary for {}, stand total age is {}") - .addArgument(vdypLayer.getPolygonIdentifier()).addArgument(vdypLayer.getAgeTotal()).log(); - log.atDebug().setMessage("Primary layer for {} has {} species/genera: {}") - .addArgument(vdypLayer::getPolygonIdentifier) // - .addArgument(() -> vdypLayer.getSpecies().size()) // - .addArgument(() -> vdypLayer.getSpecies().keySet().stream().collect(Collectors.joining(", "))) // - .log(); - - for (VdypSpecies spec : vdypLayer.getSpecies().values()) { - float loreyHeightSpec = spec.getLoreyHeightByUtilization().getCoe(UTIL_ALL); - float baseAreaSpec = spec.getBaseAreaByUtilization().getCoe(UTIL_ALL); - float quadMeanDiameterSpec = spec.getQuadraticMeanDiameterByUtilization().getCoe(UTIL_ALL); - float treesPerHectareSpec = spec.getTreesPerHectareByUtilization().getCoe(UTIL_ALL); - - log.atDebug().setMessage("Working with species {} LH: {} DQ: {} BA: {} TPH: {}") - .addArgument(spec.getClass()).addArgument(loreyHeightSpec).addArgument(quadMeanDiameterSpec) - .addArgument(baseAreaSpec).addArgument(treesPerHectareSpec); - - if (volumeComputeMode == VolumeComputeMode.BY_UTIL_WITH_WHOLE_STEM_BY_SPEC) { - log.atDebug().log("Estimating tree volume"); - - // EMP090 - throw new UnsupportedOperationException("TODO"); // Not used yet - - // log.atDebug().setMessage("Species WS stand volume {}") - // .addArgument(() -> spec.getWholeStemVolumeByUtilization().getCoe(UTIL_ALL)); - - } - float wholeStemVolumeSpec = spec.getWholeStemVolumeByUtilization().getCoe(UTIL_ALL); - - 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 = Utils.utilizationVector(); // ADJVCU - @SuppressWarnings("unused") - var adjustDecayUtil = Utils.utilizationVector(); // ADJVD - @SuppressWarnings("unused") - var adjustDecayWasteUtil = Utils.utilizationVector(); // ADJVDW - - // EMP071 - EstimationMethods - .estimateQuadMeanDiameterByUtilization(controlMap, bec, quadMeanDiameterUtil, spec.getGenus()); - - // EMP070 - EstimationMethods.estimateBaseAreaByUtilization( - controlMap, bec, quadMeanDiameterUtil, baseAreaUtil, spec.getGenus() - ); - - // Calculate tree density components - for (var uc : UtilizationClass.UTIL_CLASSES) { - treesPerHectareUtil.setCoe( - uc.index, - BaseAreaTreeDensityDiameter - .treesPerHectare(baseAreaUtil.getCoe(uc.index), quadMeanDiameterUtil.getCoe(uc.index)) - ); - } - - // reconcile components with totals - - // YUC1R - ReconcilationMethods.reconcileComponents(baseAreaUtil, treesPerHectareUtil, quadMeanDiameterUtil); - - if (compatibilityVariableMode != CompatibilityVariableMode.NONE) { - throw new UnsupportedOperationException("TODO"); - } - - // Recalculate TPH's - - for (var uc : UtilizationClass.UTIL_CLASSES) { - treesPerHectareUtil.setCoe( - uc.index, - BaseAreaTreeDensityDiameter - .treesPerHectare(baseAreaUtil.getCoe(uc.index), quadMeanDiameterUtil.getCoe(uc.index)) - ); - } - - // Since DQ's may have changed, MUST RECONCILE AGAIN - // Seems this might only be needed when compatibilityVariableMode is not NONE? - - // YUC1R - ReconcilationMethods.reconcileComponents(baseAreaUtil, treesPerHectareUtil, quadMeanDiameterUtil); - - if (volumeComputeMode == VolumeComputeMode.ZERO) { - throw new UnsupportedOperationException("TODO"); - } else { - - // EMP091 - EstimationMethods.estimateWholeStemVolume( - controlMap, UtilizationClass.ALL, adjustCloseUtil.getCoe(4), spec.getVolumeGroup(), - loreyHeightSpec, quadMeanDiameterUtil, baseAreaUtil, wholeStemVolumeUtil - ); - - if (compatibilityVariableMode == CompatibilityVariableMode.ALL) { - // apply compatibity variables to WS volume - - // Set the adjustment factors for next three volume types - - throw new UnsupportedOperationException("TODO"); - } else { - // Do nothing as the adjustment vectors are already set to 0 - } - - // EMP092 - EstimationMethods.estimateCloseUtilizationVolume( - controlMap, UtilizationClass.ALL, adjustCloseUtil, spec.getVolumeGroup(), loreyHeightSpec, - quadMeanDiameterUtil, wholeStemVolumeUtil, closeVolumeUtil - ); - - // EMP093 - EstimationMethods.estimateNetDecayVolume( - controlMap, spec.getGenus(), bec.getRegion(), UtilizationClass.ALL, adjustCloseUtil, - spec.getDecayGroup(), vdypLayer.getBreastHeightAge().orElse(0f), quadMeanDiameterUtil, - closeVolumeUtil, closeVolumeNetDecayUtil - ); - - // EMP094 - EstimationMethods.estimateNetDecayAndWasteVolume( - controlMap, bec.getRegion(), UtilizationClass.ALL, adjustCloseUtil, spec.getGenus(), - loreyHeightSpec, quadMeanDiameterUtil, closeVolumeUtil, closeVolumeNetDecayUtil, - closeVolumeNetDecayWasteUtil - ); - - if (getId().isStart()) { - // EMP095 - EstimationMethods.estimateNetDecayWasteAndBreakageVolume( - controlMap, UtilizationClass.ALL, spec.getBreakageGroup(), quadMeanDiameterUtil, - closeVolumeUtil, closeVolumeNetDecayWasteUtil, closeVolumeNetDecayWasteBreakUtil - ); - } - } - - spec.getBaseAreaByUtilization().pairwiseInPlace(baseAreaUtil, COPY_IF_BAND); - spec.getTreesPerHectareByUtilization().pairwiseInPlace(treesPerHectareUtil, COPY_IF_BAND); - spec.getQuadraticMeanDiameterByUtilization().pairwiseInPlace(quadMeanDiameterUtil, COPY_IF_BAND); - - spec.getWholeStemVolumeByUtilization().pairwiseInPlace(wholeStemVolumeUtil, COPY_IF_NOT_TOTAL); - spec.getCloseUtilizationVolumeByUtilization().pairwiseInPlace(closeVolumeUtil, COPY_IF_NOT_TOTAL); - spec.getCloseUtilizationVolumeNetOfDecayByUtilization() - .pairwiseInPlace(closeVolumeNetDecayUtil, COPY_IF_NOT_TOTAL); - spec.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization() - .pairwiseInPlace(closeVolumeNetDecayWasteUtil, COPY_IF_NOT_TOTAL); - spec.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization() - .pairwiseInPlace(closeVolumeNetDecayWasteBreakUtil, COPY_IF_NOT_TOTAL); - - } - computeLayerUtilizationComponentsFromSpecies(vdypLayer); - - for (VdypSpecies spec : vdypLayer.getSpecies().values()) { - if (vdypLayer.getBaseAreaByUtilization().getCoe(UTIL_ALL) > 0f) { - spec.setFractionGenus( - spec.getBaseAreaByUtilization().getCoe(UTIL_ALL) - / vdypLayer.getBaseAreaByUtilization().getCoe(UTIL_ALL) - ); - } - log.atDebug().addArgument(spec.getGenus()).addArgument(spec.getFractionGenus()) - .setMessage("Species {} base area {}%").log(); - } - - log.atDebug().setMessage("Calculating Stand Lorey Height").log(); - - vdypLayer.getLoreyHeightByUtilization().setCoe(UTIL_SMALL, 0f); - vdypLayer.getLoreyHeightByUtilization().setCoe(UTIL_ALL, 0f); - - for (VdypSpecies spec : vdypLayer.getSpecies().values()) { - log.atDebug() // - .addArgument(spec.getGenus()) // - .addArgument(() -> spec.getLoreyHeightByUtilization().getCoe(UTIL_ALL)) - .addArgument(() -> spec.getBaseAreaByUtilization().getCoe(UTIL_ALL)) - .addArgument( - () -> spec.getLoreyHeightByUtilization().getCoe(UTIL_ALL) - * spec.getBaseAreaByUtilization().getCoe(UTIL_ALL) - ) - .setMessage( - "For species {}, Species LH (7.5cm+): {}, Species BA (7.5cm+): {}, Weighted LH (7.5cm+): {}" - ).log(); - vdypLayer.getLoreyHeightByUtilization().scalarInPlace( - UTIL_SMALL, - x -> x + spec.getLoreyHeightByUtilization().getCoe(UTIL_SMALL) - * spec.getBaseAreaByUtilization().getCoe(UTIL_SMALL) - ); - vdypLayer.getLoreyHeightByUtilization().scalarInPlace( - UTIL_ALL, - x -> x + spec.getLoreyHeightByUtilization().getCoe(UTIL_ALL) - * spec.getBaseAreaByUtilization().getCoe(UTIL_ALL) - ); - } - { - float baSmall = vdypLayer.getBaseAreaByUtilization().getCoe(UTIL_SMALL); - float baAll = vdypLayer.getBaseAreaByUtilization().getCoe(UTIL_ALL); - - if (baSmall > 0) { - vdypLayer.getLoreyHeightByUtilization().scalarInPlace(UTIL_SMALL, x -> x / baSmall); - } - if (baAll > 0) { - vdypLayer.getLoreyHeightByUtilization().scalarInPlace(UTIL_ALL, x -> x / baAll); - } - - } - - } - - private static final IndexedFloatBinaryOperator COPY_IF_BAND = (oldX, newX, i) -> i <= UTIL_ALL ? oldX : newX; - private static final IndexedFloatBinaryOperator COPY_IF_NOT_TOTAL = (oldX, newX, i) -> i < UTIL_ALL ? oldX : newX; - ->>>>>>> origin/feature/VDYP-362-compatibility-variables - // YUCV - private void computeUtilizationComponentsVeteran(VdypLayer vdypLayer, BecDefinition bec) - throws ProcessingException { - log.trace( - "computeUtilizationComponentsVeterany for {}, stand total age is {}", vdypLayer.getPolygonIdentifier(), - vdypLayer.getAgeTotal() - ); - - var volumeAdjustMap = Utils.>expectParsedControl( - controlMap, ControlKey.VETERAN_LAYER_VOLUME_ADJUST, Map.class - ); - try { - for (var vdypSpecies : vdypLayer.getSpecies().values()) { - - var treesPerHectareUtil = Utils.utilizationVector(); - var quadMeanDiameterUtil = Utils.utilizationVector(); - var baseAreaUtil = Utils.utilizationVector(); - var wholeStemVolumeUtil = Utils.utilizationVector(); - - var closeUtilizationVolumeUtil = Utils.utilizationVector(); - var closeUtilizationNetOfDecayUtil = Utils.utilizationVector(); - var closeUtilizationNetOfDecayAndWasteUtil = Utils.utilizationVector(); - var closeUtilizationNetOfDecayWasteAndBreakageUtil = Utils.utilizationVector(); - - var hlSp = vdypSpecies.getLoreyHeightByUtilization().getCoe(UTIL_ALL); - { - var baSp = vdypSpecies.getBaseAreaByUtilization().getCoe(UTIL_LARGEST); - var tphSp = vdypSpecies.getTreesPerHectareByUtilization().getCoe(UTIL_LARGEST); - var dqSp = vdypSpecies.getQuadraticMeanDiameterByUtilization().getCoe(UTIL_LARGEST); - - treesPerHectareUtil.setCoe(UTIL_ALL, tphSp); - quadMeanDiameterUtil.setCoe(UTIL_ALL, dqSp); - baseAreaUtil.setCoe(UTIL_ALL, baSp); - wholeStemVolumeUtil.setCoe(UTIL_ALL, 0f); - - treesPerHectareUtil.setCoe(UTIL_LARGEST, tphSp); - quadMeanDiameterUtil.setCoe(UTIL_LARGEST, dqSp); - baseAreaUtil.setCoe(UTIL_LARGEST, baSp); - wholeStemVolumeUtil.setCoe(UTIL_LARGEST, 0f); - } - // AADJUSTV - var volumeAdjustCoe = volumeAdjustMap.get(vdypSpecies.getGenus()); - - var utilizationClass = UtilizationClass.OVER225; // IUC_VET - - // ADJ - var adjust = new Coefficients(new float[] { 0f, 0f, 0f, 0f }, 1); - - // EMP091 -<<<<<<< HEAD - emp.estimateWholeStemVolume( - utilizationClass, volumeAdjustCoe.getCoe(1), vdypSpecies.getVolumeGroup(), hlSp, -======= - EstimationMethods.estimateWholeStemVolume( - controlMap, utilizationClass, volumeAdjustCoe.getCoe(1), vdypSpecies.getVolumeGroup(), hlSp, ->>>>>>> origin/feature/VDYP-362-compatibility-variables - quadMeanDiameterUtil, baseAreaUtil, wholeStemVolumeUtil - ); - - adjust.setCoe(4, volumeAdjustCoe.getCoe(2)); - // EMP092 -<<<<<<< HEAD - emp.estimateCloseUtilizationVolume( - utilizationClass, adjust, vdypSpecies.getVolumeGroup(), hlSp, quadMeanDiameterUtil, -======= - EstimationMethods.estimateCloseUtilizationVolume( - controlMap, utilizationClass, adjust, vdypSpecies.getVolumeGroup(), hlSp, quadMeanDiameterUtil, ->>>>>>> origin/feature/VDYP-362-compatibility-variables - wholeStemVolumeUtil, closeUtilizationVolumeUtil - ); - - adjust.setCoe(4, volumeAdjustCoe.getCoe(3)); - // EMP093 -<<<<<<< HEAD - emp.estimateNetDecayVolume( - vdypSpecies.getGenus(), bec.getRegion(), utilizationClass, adjust, vdypSpecies.getDecayGroup(), - hlSp, vdypLayer.getBreastHeightAge().orElse(0f), quadMeanDiameterUtil, -======= - EstimationMethods.estimateNetDecayVolume( - controlMap, vdypSpecies.getGenus(), bec.getRegion(), utilizationClass, adjust, - vdypSpecies.getDecayGroup(), vdypLayer.getBreastHeightAge().orElse(0f), quadMeanDiameterUtil, ->>>>>>> origin/feature/VDYP-362-compatibility-variables - closeUtilizationVolumeUtil, closeUtilizationNetOfDecayUtil - ); - - adjust.setCoe(4, volumeAdjustCoe.getCoe(4)); - // EMP094 -<<<<<<< HEAD - emp.estimateNetDecayAndWasteVolume( - bec.getRegion(), utilizationClass, adjust, vdypSpecies.getGenus(), hlSp, - vdypLayer.getBreastHeightAge().orElse(0f), quadMeanDiameterUtil, closeUtilizationVolumeUtil, -======= - final var netDecayCoeMap = Utils.>expectParsedControl( - controlMap, ControlKey.VOLUME_NET_DECAY_WASTE, Map.class - ); - final var wasteModifierMap = Utils.>expectParsedControl( - controlMap, ControlKey.WASTE_MODIFIERS, MatrixMap2.class - ); - EstimationMethods.estimateNetDecayAndWasteVolume( - bec.getRegion(), utilizationClass, adjust, vdypSpecies.getGenus(), hlSp, netDecayCoeMap, - wasteModifierMap, quadMeanDiameterUtil, closeUtilizationVolumeUtil, ->>>>>>> origin/feature/VDYP-362-compatibility-variables - closeUtilizationNetOfDecayUtil, closeUtilizationNetOfDecayAndWasteUtil - ); - - if (getId().isStart()) { - // EMP095 -<<<<<<< HEAD - emp.estimateNetDecayWasteAndBreakageVolume( - utilizationClass, vdypSpecies.getBreakageGroup(), quadMeanDiameterUtil, -======= - EstimationMethods.estimateNetDecayWasteAndBreakageVolume( - controlMap, utilizationClass, vdypSpecies.getBreakageGroup(), quadMeanDiameterUtil, ->>>>>>> origin/feature/VDYP-362-compatibility-variables - closeUtilizationVolumeUtil, closeUtilizationNetOfDecayAndWasteUtil, - closeUtilizationNetOfDecayWasteAndBreakageUtil - ); - } - - vdypSpecies.setBaseAreaByUtilization(baseAreaUtil); - vdypSpecies.setTreesPerHectareByUtilization(treesPerHectareUtil); - vdypSpecies.setQuadraticMeanDiameterByUtilization(quadMeanDiameterUtil); - vdypSpecies.setWholeStemVolumeByUtilization(wholeStemVolumeUtil); - vdypSpecies.setCloseUtilizationVolumeByUtilization(closeUtilizationVolumeUtil); - vdypSpecies.setCloseUtilizationVolumeNetOfDecayByUtilization(closeUtilizationNetOfDecayUtil); - vdypSpecies.setCloseUtilizationVolumeNetOfDecayAndWasteByUtilization( - closeUtilizationNetOfDecayAndWasteUtil - ); - vdypSpecies.setCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( - closeUtilizationNetOfDecayWasteAndBreakageUtil - ); - - for (var accessors : UTILIZATION_VECTOR_ACCESSORS) { - Coefficients utilVector = (Coefficients) accessors.getReadMethod().invoke(vdypSpecies); - - // Set all components other than 4 to 0.0 - for (var i = -1; i < UTIL_LARGEST; i++) { - utilVector.setCoe(i, 0f); - } - - // Set component 0 to equal component 4. - utilVector.setCoe(UTIL_ALL, utilVector.getCoe(UTIL_LARGEST)); - - accessors.getWriteMethod().invoke(vdypSpecies, utilVector); - } - } - - computeLayerUtilizationComponentsFromSpecies(vdypLayer); - - } catch (IllegalAccessException | InvocationTargetException ex) { - throw new IllegalStateException(ex); - } - } - -<<<<<<< HEAD -======= - /** - * Sets the Layer's utilization components based on those of its species. - * - * @param vdypLayer - */ - private void computeLayerUtilizationComponentsFromSpecies(VdypLayer vdypLayer) { - // Layer utilization vectors other than quadratic mean diameter are the pairwise - // sums of those of their species - sumSpeciesUtilizationVectorsToLayer(vdypLayer); - - { - var hlVector = new Coefficients(new float[] { 0f, 0f }, -1); - vdypLayer.getSpecies().values().stream().forEach(spec -> { - var ba = spec.getBaseAreaByUtilization(); - hlVector.pairwiseInPlace(spec.getLoreyHeightByUtilization(), (x, y, i) -> x + y * ba.getCoe(i)); - }); - var ba = vdypLayer.getBaseAreaByUtilization(); - hlVector.scalarInPlace((x, i) -> ba.getCoe(i) > 0 ? x / ba.getCoe(i) : x); - vdypLayer.setLoreyHeightByUtilization(hlVector); - } - // 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(), BaseAreaTreeDensityDiameter::quadMeanDiameter - ); - vdypLayer.setQuadraticMeanDiameterByUtilization(utilVector); - } - } - - // TODO De-reflectify this when we want to make it work in GralVM - private void sumSpeciesUtilizationVectorsToLayer(VdypLayer vdypLayer) throws IllegalStateException { - try { - for (var accessors : SUMMABLE_UTILIZATION_VECTOR_ACCESSORS) { - var utilVector = Utils.utilizationVector(); - for (var vdypSpecies : vdypLayer.getSpecies().values()) { - var speciesVector = (Coefficients) accessors.getReadMethod().invoke(vdypSpecies); - utilVector.pairwiseInPlace(speciesVector, (x, y) -> x + y); - } - accessors.getWriteMethod().invoke(vdypLayer, utilVector); - } - } catch (IllegalAccessException | InvocationTargetException ex) { - throw new IllegalStateException(ex); - } - } - - // TODO De-reflectify this when we want to make it work in GralVM - private void scaleAllSummableUtilization(VdypUtilizationHolder holder, float factor) throws IllegalStateException { - try { - for (var accessors : SUMMABLE_UTILIZATION_VECTOR_ACCESSORS) { - ((Coefficients) accessors.getReadMethod().invoke(holder)).scalarInPlace(x -> x * factor); - } - } catch (IllegalAccessException | InvocationTargetException ex) { - throw new IllegalStateException(ex); - } - } - - private float heightMultiplier(String genus, Region region, float treesPerHectarePrimary) { - final var coeMap = Utils.>expectParsedControl( - controlMap, ControlKey.HL_PRIMARY_SP_EQN_P1, MatrixMap2.class - ); - var coe = coeMap.get(genus, region).reindex(0); - return coe.get(0) - coe.getCoe(1) + coe.getCoe(1) * exp(coe.getCoe(2) * (treesPerHectarePrimary - 100f)); - } - - // EMP050 Meth==1 - /** - * Return the lorey height of the primary species based on the dominant height of the lead species. - * - * @param leadHeight dominant height of the lead species - * @param genus Primary species - * @param region Region of the polygon - * @param treesPerHectarePrimary trees per hectare >7.5 cm of the primary species - * @return - */ - float primaryHeightFromLeadHeight(float leadHeight, String genus, Region region, float treesPerHectarePrimary) { - return 1.3f + (leadHeight - 1.3f) * heightMultiplier(genus, region, treesPerHectarePrimary); - } - - // EMP050 Meth==2 - /** - * Return the dominant height of the lead species based on the lorey height of the primary species. - * - * @param primaryHeight lorey height of the primary species - * @param genus Primary species - * @param region Region of the polygon - * @param treesPerHectarePrimary trees per hectare >7.5 cm of the primary species - * @return - */ - float leadHeightFromPrimaryHeight(float primaryHeight, String genus, Region region, float treesPerHectarePrimary) { - return 1.3f + (primaryHeight - 1.3f) / heightMultiplier(genus, region, treesPerHectarePrimary); - } - - // EMP051 - /** - * Return the lorey height of the primary species based on the dominant height of the lead species. - * - * @param leadHeight dominant height of the lead species - * @param genus Primary species - * @param region Region of the polygon - * @return - */ - private float primaryHeightFromLeadHeightInitial(float leadHeight, String genus, Region region) { - final var coeMap = Utils.>expectParsedControl( - controlMap, ControlKey.HL_PRIMARY_SP_EQN_P2, MatrixMap2.class - ); - var coe = coeMap.get(genus, region); - return 1.3f + coe.getCoe(1) * pow(leadHeight - 1.3f, coe.getCoe(2)); - } - - /** - * Accessor methods for utilization vectors, except for Lorey Height, on Layer and Species objects. - */ - static final Collection UTILIZATION_VECTOR_ACCESSORS; - - /** - * Accessor methods for utilization vectors, except for Lorey Height and Quadratic Mean Diameter, on Layer and - * Species objects. These are properties where the values for the layer are the sum of those for its species. - */ - static final Collection SUMMABLE_UTILIZATION_VECTOR_ACCESSORS; - - /** - * Accessor methods for utilization vectors, except for Lorey Height,and Volume on Layer and Species objects. - */ - static final Collection NON_VOLUME_UTILIZATION_VECTOR_ACCESSORS; - - static { - try { - var bean = Introspector.getBeanInfo(VdypUtilizationHolder.class); - UTILIZATION_VECTOR_ACCESSORS = Arrays.stream(bean.getPropertyDescriptors()) // - .filter(p -> p.getName().endsWith("ByUtilization")) // - .filter(p -> !p.getName().startsWith("loreyHeight")) // - .filter(p -> p.getPropertyType() == Coefficients.class) // - .toList(); - } catch (IntrospectionException e) { - throw new IllegalStateException(e); - } - - SUMMABLE_UTILIZATION_VECTOR_ACCESSORS = UTILIZATION_VECTOR_ACCESSORS.stream() - .filter(x -> !x.getName().startsWith("quadraticMeanDiameter")).toList(); - - NON_VOLUME_UTILIZATION_VECTOR_ACCESSORS = UTILIZATION_VECTOR_ACCESSORS.stream() - .filter(x -> !x.getName().contains("Volume")).toList(); - } - - int getGroup(FipPolygon fipPolygon, MatrixMap2 volumeGroupMap, String genus) { - return volumeGroupMap.get(genus, fipPolygon.getBiogeoclimaticZone()); - } - - MatrixMap2 getGroupMap(ControlKey key) { - return Utils.expectParsedControl(controlMap, key, MatrixMap2.class); - } - ->>>>>>> origin/feature/VDYP-362-compatibility-variables - // FIP_GET - protected FipPolygon getPolygon( - StreamingParser polyStream, StreamingParser> layerStream, - StreamingParser> speciesStream - ) throws ProcessingException, IOException, ResourceParseException { - - log.trace("Getting polygon"); - var polygon = polyStream.next(); - - log.trace("Getting layers for polygon {}", polygon.getPolygonIdentifier()); - Map layers; - try { - layers = layerStream.next(); - } catch (NoSuchElementException ex) { - throw validationError("Layers file has fewer records than polygon file.", ex); - } - - log.trace("Getting species for polygon {}", polygon.getPolygonIdentifier()); - Collection species; - try { - species = speciesStream.next(); - } catch (NoSuchElementException ex) { - throw validationError("Species file has fewer records than polygon file.", ex); - } - - // Validate that layers belong to the correct polygon - for (var layer : layers.values()) { - if (!layer.getPolygonIdentifier().equals(polygon.getPolygonIdentifier())) { - throw validationError( - "Record in layer file contains layer for polygon %s when expecting one for %s.", - layer.getPolygonIdentifier(), polygon.getPolygonIdentifier() - ); - } - layer.setSpecies(new HashMap<>()); - } - - for (var spec : species) { - var layer = layers.get(spec.getLayerType()); - // Validate that species belong to the correct polygon - if (!spec.getPolygonIdentifier().equals(polygon.getPolygonIdentifier())) { - throw validationError( - "Record in species file contains species for polygon %s when expecting one for %s.", - layer.getPolygonIdentifier(), polygon.getPolygonIdentifier() - ); - } - if (Objects.isNull(layer)) { - throw validationError( - "Species entry references layer %s of polygon %s but it is not present.", layer, - polygon.getPolygonIdentifier() - ); - } - layer.getSpecies().put(spec.getGenus(), spec); - } - - polygon.setLayers(layers); - - return polygon; - } - - private Optional heightMinimum(LayerType layer) { - var minima = Utils.>expectParsedControl(controlMap, ControlKey.MINIMA.name(), Map.class); - switch (layer) { - case PRIMARY: - return Optional.of(minima.get(BaseControlParser.MINIMUM_HEIGHT)); - case VETERAN: - return Optional.of(minima.get(BaseControlParser.MINIMUM_VETERAN_HEIGHT)); - default: - return Optional.empty(); - } - } - - // FIP_CHK - void checkPolygon(FipPolygon polygon) throws ProcessingException { - - // Fortran did debug logging when a polygon is found to be invalid. Attaching - // messages to exceptions fills that need. - - // TODO finding all the things that are wrong rather than failing on just the - // first would be a good idea. - - var primaryLayer = requireLayer(polygon, LayerType.PRIMARY); - - // FIXME VDYP7 actually tests if total age - YTBH is less than 0.5 but gives an - // error that total age is "less than" YTBH. Replicating that for now but - // consider changing it. - - 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 - ); - } - - // TODO This is the only validation step done to non-primary layers, VDYP7 had a - // less well defined idea of a layer being present or not and so it may have - // skipped validating other layers rather than validating them conditionally on - // being present. Consider extending validation of other properties to other - // layers. - - for (FipLayer layer : polygon.getLayers().values()) { - var height = layer.getHeight().orElse(0f); - - throwIfPresent( - heightMinimum(layer.getLayerType()).filter(minimum -> height < minimum).map( - minimum -> validationError( - "Polygon %s has %s layer where height %.1f is less than minimum %.1f.", - polygon.getPolygonIdentifier(), layer.getLayerType(), layer.getHeightSafe(), minimum - ) - ) - ); - } - - if (polygon.getMode().map(x -> x == PolygonMode.YOUNG).orElse(false)) { - throw validationError( - "Polygon %s is using unsupported mode %s.", polygon.getPolygonIdentifier(), PolygonMode.YOUNG - ); - } - - 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.getYearsToBreastHeightSafe(), 0.5f - ); - } - - if (primaryLayer.getSiteIndex().orElse(0f) < 0.5) { - throw validationError( - "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 - ); - } - - for (FipLayer layer : polygon.getLayers().values()) { - var percentTotal = getPercentTotal(layer); - // VDYP7 performs this step which should be negligible but might have a small - // impact due to the 0.01 percent variation and floating point errors. - if (layer.getLayerType() == LayerType.PRIMARY) { - layer.getSpecies().values() - .forEach(species -> species.setFractionGenus(species.getPercentGenus() / percentTotal)); - } - } - - } - - // EMP098 - float estimateVeteranBaseArea(float height, float crownClosure, String genus, Region region) { - var coefficients = Utils.>expectParsedControl( - controlMap, ControlKey.VETERAN_BQ, MatrixMap2.class - ).getM(genus, region); - - // mismatched index is copied from VDYP7 - float a0 = coefficients.getCoe(1); - float a1 = coefficients.getCoe(2); - float a2 = coefficients.getCoe(3); - - float baseArea = a0 * pow(max(height - a1, 0.0f), a2); - - baseArea *= crownClosure / 4.0f; - - baseArea = max(baseArea, 0.01f); - - return baseArea; - } - - /** - * estimate mean volume per tree For a species, for trees with dbh >= 7.5 CM Using eqn in jf117.doc - * - * @param volumeGroup - * @param loreyHeight - * @param quadMeanDiameter - * @return - */ - public float estimateMeanVolume(int volumeGroup, float loreyHeight, float quadMeanDiameter) { - var coeMap = Utils.>expectParsedControl( - controlMap, ControlKey.TOTAL_STAND_WHOLE_STEM_VOL, Map.class - ); - - var coe = coeMap.get(volumeGroup); - - if (coe == null) { - throw new IllegalArgumentException("Coefficients not found for volume group " + volumeGroup); - } - - float lvMean = // - coe.getCoe(0) + // - coe.getCoe(1) * log(quadMeanDiameter) + // - coe.getCoe(2) * log(loreyHeight) + // - coe.getCoe(3) * quadMeanDiameter + // - coe.getCoe(4) / quadMeanDiameter + // - coe.getCoe(5) * loreyHeight + // - coe.getCoe(6) * quadMeanDiameter * quadMeanDiameter + // - coe.getCoe(7) * quadMeanDiameter * loreyHeight + // - coe.getCoe(8) * loreyHeight / quadMeanDiameter; - - return exp(lvMean); - } - - double[] rootFinderFunction(double[] point, VdypLayer layer, double[] diameterBase) { - - var percentL1 = new double[point.length]; - double percentSum = 0; - if (point.length > 1) { - for (int i = 0; i < point.length - 1; i++) { - percentL1[i] = point[i]; - percentSum += point[i]; - } - } - percentL1[point.length - 1] = 100d - percentSum; - - double volumeSum = 0d; - double treesPerHectareSum = 0d; - - final var layerBa = layer.getBaseAreaByUtilization().getCoe(UTIL_ALL); - - // Iterate over the fixed order list with an index - { - var it = layer.getSpecies().entrySet().iterator(); - for (int j = 0; it.hasNext(); j++) { - var spec = it.next().getValue(); - - // These side effects are evil but that's how VDYP7 works. - - final float quadMeanDiameter = (float) (7.5 - + (diameterBase[j] - 7.5) * FastMath.exp(point[point.length - 1] / 20d)); - spec.getQuadraticMeanDiameterByUtilization().setCoe(UTIL_ALL, quadMeanDiameter); - - final float baseArea = (float) (layerBa * percentL1[j] / 100d); - spec.getBaseAreaByUtilization().setCoe(UTIL_ALL, baseArea); - - final float tph = BaseAreaTreeDensityDiameter.treesPerHectare(baseArea, quadMeanDiameter); - spec.getTreesPerHectareByUtilization().setCoe(UTIL_ALL, tph); - treesPerHectareSum += tph; - - final float loreyHeight = spec.getLoreyHeightByUtilization().getCoe(UTIL_ALL); - - final float meanVolume = estimateMeanVolume(spec.getVolumeGroup(), loreyHeight, quadMeanDiameter); - final float wholeStemVolume = tph * meanVolume; - - spec.getWholeStemVolumeByUtilization().setCoe(UTIL_ALL, wholeStemVolume); - volumeSum += wholeStemVolume; - } - } - - double dqFinal = BaseAreaTreeDensityDiameter - .quadMeanDiameter(layer.getBaseAreaByUtilization().getCoe(UTIL_ALL), (float) treesPerHectareSum); - - var y = new double[point.length]; - - if (layer.getSpecies().size() > 1) { - var it = layer.getSpecies().values().iterator(); - for (int i = 0; it.hasNext(); i++) { - var spec = it.next(); - - y[i] = 100d * spec.getWholeStemVolumeByUtilization().getCoe(UTIL_ALL) / volumeSum; - } - } - y[y.length - 1] = dqFinal; - return y; - } - -<<<<<<< HEAD -======= - // YSMALL - // TODO move to shared location as it's used elsewhere and implement - // compatibility variables - /** - * Estimate small components for primary layer - * - * @throws ProcessingException - */ - public , SP extends BaseVdypSpecies, SI extends BaseVdypSite> void - estimateSmallComponents(BaseVdypPolygon, SP, SI> polygon, VdypLayer layer) - throws ProcessingException { - float loreyHeightSum = 0f; - float baseAreaSum = 0f; - float treesPerHectareSum = 0f; - float volumeSum = 0f; - - Region region = Utils.getBec(polygon.getBiogeoclimaticZone(), controlMap).getRegion(); - - for (VdypSpecies spec : layer.getSpecies().values()) { - @SuppressWarnings("unused") - float loreyHeightSpec = spec.getLoreyHeightByUtilization().getCoe(UTIL_ALL); // HLsp - float baseAreaSpec = spec.getBaseAreaByUtilization().getCoe(UTIL_ALL); // BAsp - @SuppressWarnings("unused") - float quadMeanDiameterSpec = spec.getQuadraticMeanDiameterByUtilization().getCoe(UTIL_ALL); // DQsp - - // EMP080 - float smallComponentProbability = smallComponentProbability(layer, spec, region); // PROBsp - - // this WHOLE operation on Actual BA's, not 100% occupancy. - float fractionAvailable = polygon.getPercentAvailable().map(p -> p / 100f).orElse(1f); - baseAreaSpec *= fractionAvailable; - // EMP081 - float conditionalExpectedBaseArea = conditionalExpectedBaseArea(spec, baseAreaSpec, region); // BACONDsp - conditionalExpectedBaseArea /= fractionAvailable; - - float baseAreaSpecSmall = smallComponentProbability * conditionalExpectedBaseArea; - - // EMP082 - float quadMeanDiameterSpecSmall = smallComponentQuadMeanDiameter(spec); // DQSMsp - - // EMP085 - float loreyHeightSpecSmall = smallComponentLoreyHeight(spec, quadMeanDiameterSpecSmall); // HLSMsp - - // EMP086 - float meanVolumeSmall = meanVolumeSmall(spec, quadMeanDiameterSpecSmall, loreyHeightSpecSmall); // VMEANSMs - - // TODO Apply Compatibility Variables, not needed for FIPSTART - - spec.getLoreyHeightByUtilization().setCoe(UTIL_SMALL, loreyHeightSpecSmall); - 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); - float wholeStemVolumeSpecSmall = treesPerHectareSpecSmall * meanVolumeSmall; // VOLWS(I,-1) - spec.getWholeStemVolumeByUtilization().setCoe(UTIL_SMALL, wholeStemVolumeSpecSmall); - - loreyHeightSum += baseAreaSpecSmall * loreyHeightSpecSmall; - baseAreaSum += baseAreaSpecSmall; - treesPerHectareSum += treesPerHectareSpecSmall; - volumeSum += wholeStemVolumeSpecSmall; - } - - if (baseAreaSum > 0f) { - layer.getLoreyHeightByUtilization().setCoe(UTIL_SMALL, loreyHeightSum / baseAreaSum); - } else { - layer.getLoreyHeightByUtilization().setCoe(UTIL_SMALL, 0f); - } - layer.getBaseAreaByUtilization().setCoe(UTIL_SMALL, baseAreaSum); - layer.getTreesPerHectareByUtilization().setCoe(UTIL_SMALL, treesPerHectareSum); - layer.getQuadraticMeanDiameterByUtilization() - .setCoe(UTIL_SMALL, BaseAreaTreeDensityDiameter.quadMeanDiameter(baseAreaSum, treesPerHectareSum)); - layer.getWholeStemVolumeByUtilization().setCoe(UTIL_SMALL, volumeSum); - } - - // EMP086 - private float meanVolumeSmall(VdypSpecies spec, float quadMeanDiameterSpecSmall, float loreyHeightSpecSmall) { - Coefficients coe = getCoeForSpecies(spec, ControlKey.SMALL_COMP_WS_VOLUME); - - // EQN 1 in IPSJF119.doc - - float a0 = coe.getCoe(1); - float a1 = coe.getCoe(2); - float a2 = coe.getCoe(3); - float a3 = coe.getCoe(4); - - return exp( - a0 + a1 * log(quadMeanDiameterSpecSmall) + a2 * log(loreyHeightSpecSmall) - + a3 * quadMeanDiameterSpecSmall - ); - } - - // EMP085 - private float smallComponentLoreyHeight(VdypSpecies spec, float quadMeanDiameterSpecSmall) { - Coefficients coe = getCoeForSpecies(spec, ControlKey.SMALL_COMP_HL); - - // EQN 1 in IPSJF119.doc - - float a0 = coe.getCoe(1); - float a1 = coe.getCoe(2); - - return 1.3f + (spec.getLoreyHeightByUtilization().getCoe(FipStart.UTIL_ALL) - 1.3f) * exp( - a0 * (pow(quadMeanDiameterSpecSmall, a1) - - pow(spec.getQuadraticMeanDiameterByUtilization().getCoe(UTIL_ALL), a1)) - ); - } - - // EMP082 - private float smallComponentQuadMeanDiameter(VdypSpecies spec) { - Coefficients coe = getCoeForSpecies(spec, ControlKey.SMALL_COMP_DQ); - - // EQN 5 in IPSJF118.doc - - float a0 = coe.getCoe(1); - float a1 = coe.getCoe(2); - - float logit = // - a0 + a1 * spec.getLoreyHeightByUtilization().getCoe(FipStart.UTIL_ALL); - - return 4.0f + 3.5f * exp(logit) / (1.0f + exp(logit)); - } - - // EMP081 - private float conditionalExpectedBaseArea(VdypSpecies spec, float baseAreaSpec, Region region) { - Coefficients coe = getCoeForSpecies(spec, ControlKey.SMALL_COMP_BA); - - // EQN 3 in IPSJF118.doc - - float a0 = coe.getCoe(1); - float a1 = coe.getCoe(2); - float a2 = coe.getCoe(3); - float a3 = coe.getCoe(4); - - float coast = region == Region.COASTAL ? 1.0f : 0.0f; - - // FIXME due to a bug in VDYP7 it always treats this as interior. Replicating - // that for now. - coast = 0f; - - float arg = // - (a0 + // - a1 * coast + // - a2 * spec.getBaseAreaByUtilization().getCoe(FipStart.UTIL_ALL)// - ) * exp(a3 * spec.getLoreyHeightByUtilization().getCoe(FipStart.UTIL_ALL)); - arg = max(arg, 0f); - - return arg; - } - - // EMP080 - private float smallComponentProbability(VdypLayer layer, VdypSpecies spec, Region region) { - Coefficients coe = getCoeForSpecies(spec, ControlKey.SMALL_COMP_PROBABILITY); - - // EQN 1 in IPSJF118.doc - - float a0 = coe.getCoe(1); - float a1 = coe.getCoe(2); - float a2 = coe.getCoe(3); - float a3 = coe.getCoe(4); - - float coast = region == Region.COASTAL ? 1.0f : 0.0f; - - float logit = // - a0 + // - a1 * coast + // - a2 * layer.getBreastHeightAge().orElse(0f) + // - a3 * spec.getLoreyHeightByUtilization().getCoe(FipStart.UTIL_ALL); - - return exp(logit) / (1.0f + exp(logit)); - } - ->>>>>>> origin/feature/VDYP-362-compatibility-variables - @Override - protected ValueOrMarker - isVeteranForEstimatePercentForestLand(FipPolygon polygon, Optional vetLayer) { - if (polygon.getMode().map(mode -> mode == PolygonMode.YOUNG).orElse(false)) { - return FLOAT_OR_BOOL.value(100f); - } - return super.isVeteranForEstimatePercentForestLand(polygon, vetLayer); - } - - /** - * Estimate the Jacobian Matrix of a function using forward difference - * - * @param x - * @param func - * @return - */ - double[][] estimateJacobian(double[] x, MultivariateVectorFunction func) { - return estimateJacobian(x, func.value(x), func); - } - - /** - * Estimate the Jacobian Matrix of a function using forward difference - * - * @param x - * @param y - * @param func - * @return - */ - double[][] estimateJacobian(double[] x, double[] y, MultivariateVectorFunction func) { - // TODO - final double machineEpsilon = 2.22e-16; - final double functionEpsilon = 1.19e-07; - - double epsilon = FastMath.sqrt(FastMath.max(functionEpsilon, machineEpsilon)); - - double[] x2 = Arrays.copyOf(x, x.length); - - double[][] result = new double[x.length][x.length]; - - for (int j = 0; j < x.length; j++) { - double temp = x[j]; - double h = epsilon * FastMath.abs(temp); - if (h == 0) { - h = epsilon; - } - x2[j] = temp + h; - double[] y2 = func.value(x2); - x2[j] = temp; - for (int i = 0; i < x.length; i++) { - result[i][j] = (y2[i] - y[i]) / h; - } - } - return result; - } - - RealMatrix identityMatrix(int n) { - var diag = new double[n]; - Arrays.fill(diag, n); - return new DiagonalMatrix(diag); - - } - - RealVector findRoot(double[] diameterBase, double[] goal, double[] x, VdypLayer layer, double tolerance) { - MultivariateVectorFunction func = point -> rootFinderFunction(point, layer, diameterBase); - - MultivariateMatrixFunction jacFunc = point -> estimateJacobian(point, func); - - LevenbergMarquardtOptimizer optimizer = new LevenbergMarquardtOptimizer(); - - optimizer.withCostRelativeTolerance(tolerance); // Not sure if this is the right tolerance - - LeastSquaresProblem leastSquaresProblem = LeastSquaresFactory.create( - func, // - jacFunc, // - goal, // - x, // - identityMatrix(x.length), // - null, // - 200, // - 1000 // - ); - - var result = optimizer.optimize(leastSquaresProblem); - - return result.getPoint(); - } - - @Override - public VdypApplicationIdentifier getId() { - return VdypApplicationIdentifier.FIP_START; - } - - @Override - protected BaseControlParser getControlFileParser() { - return new FipControlParser(); - } - - @Override - protected FipSpecies copySpecies(FipSpecies toCopy, Consumer> config) { - return FipSpecies.build(builder -> { - builder.copy(toCopy); - }); - } - - @Override - protected Optional getPrimarySite(FipLayer layer) { - return layer.getSite(); - } - - @Override - protected float getYieldFactor(FipPolygon polygon) { - return polygon.getYieldFactor(); - // TODO Make an InputPolygon interface that has this. - } - -} diff --git a/vdyp-lib/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/FipStartTest.java.orig b/vdyp-lib/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/FipStartTest.java.orig deleted file mode 100644 index 8e8368327..000000000 --- a/vdyp-lib/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/FipStartTest.java.orig +++ /dev/null @@ -1,4519 +0,0 @@ -package ca.bc.gov.nrs.vdyp.fip; - -import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.closeTo; -import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.coe; -import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.present; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.aMapWithSize; -import static org.hamcrest.Matchers.allOf; -import static org.hamcrest.Matchers.anEmptyMap; -import static org.hamcrest.Matchers.any; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.hasEntry; -import static org.hamcrest.Matchers.hasProperty; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static ca.bc.gov.nrs.vdyp.test.TestUtils.polygonId; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.stream.Collectors; - -import org.easymock.EasyMock; -import org.easymock.IMocksControl; -import org.hamcrest.Matcher; -import org.hamcrest.Matchers; -import org.junit.jupiter.api.Test; - -import ca.bc.gov.nrs.vdyp.application.ApplicationTestUtils; -import ca.bc.gov.nrs.vdyp.application.ProcessingException; -import ca.bc.gov.nrs.vdyp.application.StandProcessingException; -import ca.bc.gov.nrs.vdyp.application.VdypStartApplication; -import ca.bc.gov.nrs.vdyp.common.ControlKey; -import ca.bc.gov.nrs.vdyp.common.EstimationMethods; -import ca.bc.gov.nrs.vdyp.common.ReconcilationMethods; -import ca.bc.gov.nrs.vdyp.common.Utils; -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.FipLayerPrimary.PrimaryBuilder; -import ca.bc.gov.nrs.vdyp.fip.model.FipPolygon; -import ca.bc.gov.nrs.vdyp.fip.model.FipSite; -import ca.bc.gov.nrs.vdyp.fip.model.FipSpecies; -import ca.bc.gov.nrs.vdyp.fip.test.FipTestUtils; -import ca.bc.gov.nrs.vdyp.io.parse.coe.BecDefinitionParser; -import ca.bc.gov.nrs.vdyp.io.parse.coe.GenusDefinitionParser; -import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; -import ca.bc.gov.nrs.vdyp.io.parse.control.BaseControlParser; -import ca.bc.gov.nrs.vdyp.io.parse.streaming.MockStreamingParser; -import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParserFactory; -import ca.bc.gov.nrs.vdyp.model.Coefficients; -import ca.bc.gov.nrs.vdyp.model.LayerType; -import ca.bc.gov.nrs.vdyp.model.MatrixMap2; -import ca.bc.gov.nrs.vdyp.model.PolygonIdentifier; -import ca.bc.gov.nrs.vdyp.model.PolygonMode; -import ca.bc.gov.nrs.vdyp.model.Region; -import ca.bc.gov.nrs.vdyp.model.StockingClassFactor; -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; -import ca.bc.gov.nrs.vdyp.test.TestUtils; -import ca.bc.gov.nrs.vdyp.test.VdypMatchers; - -class FipStartTest { - - @Test - void testProcessEmpty() throws Exception { - - testWith(Arrays.asList(), Arrays.asList(), Arrays.asList(), (app, controlMap) -> { - assertDoesNotThrow(app::process); - }); - } - - @Test - void testProcessSimple() throws Exception { - - var polygonId = polygonId("Test Polygon", 2023); - var layer = LayerType.PRIMARY; - - // One polygon with one primary layer with one species entry - testWith( - FipTestUtils.loadControlMap(), Arrays.asList(getTestPolygon(polygonId, TestUtils.valid())), // - Arrays.asList(layerMap(getTestPrimaryLayer(polygonId, TestUtils.valid(), TestUtils.valid()))), // - Arrays.asList(Collections.singletonList(getTestSpecies(polygonId, layer, TestUtils.valid()))), // - (app, controlMap) -> { - assertDoesNotThrow(app::process); - } - ); - - } - - @Test - void testPolygonWithNoLayersRecord() throws Exception { - - var polygonId = polygonId("Test Polygon", 2023); - - testWith( - Arrays.asList(getTestPolygon(polygonId, TestUtils.valid())), // - Collections.emptyList(), // - Collections.emptyList(), // - (app, controlMap) -> { - var ex = assertThrows(ProcessingException.class, () -> app.process()); - - assertThat(ex, hasProperty("message", is("Layers file has fewer records than polygon file."))); - - } - ); - } - - @Test - void testPolygonWithNoSpeciesRecord() throws Exception { - - var polygonId = polygonId("Test Polygon", 2023); - - testWith( - Arrays.asList(getTestPolygon(polygonId, TestUtils.valid())), // - Arrays.asList(layerMap(getTestPrimaryLayer(polygonId, TestUtils.valid(), TestUtils.valid()))), // - Collections.emptyList(), // - (app, controlMap) -> { - var ex = assertThrows(ProcessingException.class, () -> app.process()); - - assertThat(ex, hasProperty("message", is("Species file has fewer records than polygon file."))); - - } - ); - } - - @Test - void testPolygonWithNoPrimaryLayer() throws Exception { - - // One polygon with one layer with one species entry, and type is VETERAN - - var controlMap = FipTestUtils.loadControlMap(); - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var polygonId = polygonId("Test Polygon", 2023); - - var polygon = getTestPolygon(polygonId, TestUtils.valid()); - var layer2 = getTestVeteranLayer(polygonId, TestUtils.valid(), siteBuilder -> { - siteBuilder.height(9f); - }); - polygon.setLayers(List.of(layer2)); - - var ex = assertThrows(StandProcessingException.class, () -> app.checkPolygon(polygon)); - assertThat( - ex, - hasProperty( - "message", - is( - "Polygon \"" + polygonId + "\" has no " + LayerType.PRIMARY - + " layer, or that layer has non-positive height or crown closure." - ) - ) - ); - } - } - - @Test - void testPrimaryLayerHeightLessThanMinimum() throws Exception { - - var controlMap = FipTestUtils.loadControlMap(); - var polygonId = new PolygonIdentifier("TestPolygon", 2024); - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var polygon = getTestPolygon(polygonId, TestUtils.valid()); - FipLayer layer = this.getTestPrimaryLayer(polygonId, TestUtils.valid(), sBuilder -> { - sBuilder.height(4f); - }); - polygon.setLayers(Collections.singletonMap(LayerType.PRIMARY, layer)); - - var ex = assertThrows(StandProcessingException.class, () -> app.checkPolygon(polygon)); - assertThat( - ex, - hasProperty( - "message", - is( - "Polygon " + polygonId + " has " + LayerType.PRIMARY - + " layer where height 4.0 is less than minimum 5.0." - ) - ) - ); - } - - } - - @Test - void testVeteranLayerHeightLessThanMinimum() throws Exception { - var controlMap = FipTestUtils.loadControlMap(); - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var polygonId = polygonId("Test Polygon", 2023); - - var polygon = getTestPolygon(polygonId, TestUtils.valid()); - var layer1 = getTestPrimaryLayer(polygonId, TestUtils.valid(), TestUtils.valid()); - var layer2 = getTestVeteranLayer(polygonId, TestUtils.valid(), sBuilder -> { - sBuilder.height(9f); - }); - polygon.setLayers(List.of(layer1, layer2)); - - var ex = assertThrows(StandProcessingException.class, () -> app.checkPolygon(polygon)); - assertThat( - ex, - hasProperty( - "message", - is( - "Polygon " + polygonId + " has " + LayerType.VETERAN - + " layer where height 9.0 is less than minimum 10.0." - ) - ) - ); - } - - } - - @Test - void testPrimaryLayerYearsToBreastHeightLessThanMinimum() throws Exception { - - var controlMap = FipTestUtils.loadControlMap(); - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var polygonId = polygonId("Test Polygon", 2023); - - var polygon = getTestPolygon(polygonId, TestUtils.valid()); - var layer1 = getTestPrimaryLayer(polygonId, TestUtils.valid(), sBuilder -> { - sBuilder.yearsToBreastHeight(0.2f); - }); - polygon.setLayers(List.of(layer1)); - - var ex = assertThrows(StandProcessingException.class, () -> app.checkPolygon(polygon)); - assertThat( - ex, - hasProperty( - "message", - is( - "Polygon " + polygonId + " has " + LayerType.PRIMARY - + " layer where years to breast height 0.2 is less than minimum 0.5 years." - ) - ) - ); - } - } - - @Test - void testPrimaryLayerTotalAgeLessThanYearsToBreastHeight() throws Exception { - - // FIXME VDYP7 actually tests if total age - YTBH is less than 0.5 but gives an - // error that total age is "less than" YTBH. Replicating that for now but - // consider changing it. - - var controlMap = FipTestUtils.loadControlMap(); - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var polygonId = polygonId("Test Polygon", 2023); - - var polygon = getTestPolygon(polygonId, TestUtils.valid()); - var layer1 = getTestPrimaryLayer(polygonId, TestUtils.valid(), siteBuilder -> { - siteBuilder.ageTotal(7f); - siteBuilder.yearsToBreastHeight(8f); - }); - polygon.setLayers(List.of(layer1)); - - var ex = assertThrows(StandProcessingException.class, () -> app.checkPolygon(polygon)); - assertThat( - ex, - hasProperty( - "message", - is( - "Polygon " + polygonId + " has " + LayerType.PRIMARY - + " layer where total age is less than YTBH." - ) - ) - ); - } - } - - @Test - void testPrimaryLayerSiteIndexLessThanMinimum() throws Exception { - - var controlMap = FipTestUtils.loadControlMap(); - var polygonId = new PolygonIdentifier("TestPolygon", 2024); - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var polygon = getTestPolygon(polygonId, TestUtils.valid()); - var layer = this.getTestPrimaryLayer(polygonId, TestUtils.valid(), siteBuilder -> { - siteBuilder.siteIndex(0.2f); - }); - polygon.setLayers(Collections.singletonMap(LayerType.PRIMARY, layer)); - - var ex = assertThrows(StandProcessingException.class, () -> app.checkPolygon(polygon)); - assertThat( - ex, - hasProperty( - "message", - is( - "Polygon " + polygonId + " has " + LayerType.PRIMARY - + " layer where site index 0.2 is less than minimum 0.5 years." - ) - ) - ); - } - } - - @Test - void testPolygonWithModeFipYoung() throws Exception { - - var controlMap = FipTestUtils.loadControlMap(); - var polygonId = new PolygonIdentifier("TestPolygon", 2024); - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var polygon = getTestPolygon(polygonId, x -> { - x.setMode(Optional.of(PolygonMode.YOUNG)); - }); - var layer = this.getTestPrimaryLayer(polygonId, TestUtils.valid(), TestUtils.valid()); - polygon.setLayers(List.of(layer)); - - var ex = assertThrows(StandProcessingException.class, () -> app.checkPolygon(polygon)); - assertThat( - ex, - hasProperty( - "message", - is("Polygon " + polygonId + " is using unsupported mode " + PolygonMode.YOUNG + ".") - ) - ); - } - - } - - @Test - void testOneSpeciesLessThan100Percent() throws Exception { - - var controlMap = FipTestUtils.loadControlMap(); - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var polygonId = polygonId("Test Polygon", 2023); - - var polygon = getTestPolygon(polygonId, TestUtils.valid()); - var layer = this.getTestPrimaryLayer(polygonId, TestUtils.valid(), TestUtils.valid()); - var spec = getTestSpecies(polygonId, LayerType.PRIMARY, x -> { - x.setPercentGenus(99f); - }); - layer.setSpecies(List.of(spec)); - polygon.setLayers(List.of(layer)); - - var ex = assertThrows(StandProcessingException.class, () -> app.checkPolygon(polygon)); - assertThat( - ex, - hasProperty( - "message", - is( - "Polygon \"" + polygonId - + "\" has PRIMARY layer where species entries have a percentage total that does not sum to 100%." - ) - ) - ); - } - - } - - @Test - void testOneSpeciesMoreThan100Percent() throws Exception { - - var controlMap = FipTestUtils.loadControlMap(); - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var polygonId = polygonId("Test Polygon", 2023); - - var polygon = getTestPolygon(polygonId, TestUtils.valid()); - var layer = this.getTestPrimaryLayer(polygonId, TestUtils.valid(), TestUtils.valid()); - var spec = getTestSpecies(polygonId, LayerType.PRIMARY, x -> { - x.setPercentGenus(101f); - }); - layer.setSpecies(List.of(spec)); - polygon.setLayers(List.of(layer)); - - var ex = assertThrows(StandProcessingException.class, () -> app.checkPolygon(polygon)); - assertThat( - ex, - hasProperty( - "message", - is( - "Polygon \"" + polygonId - + "\" has PRIMARY layer where species entries have a percentage total that does not sum to 100%." - ) - ) - ); - } - - } - - @Test - void testTwoSpeciesSumTo100Percent() throws Exception { - - var controlMap = FipTestUtils.loadControlMap(); - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var polygonId = polygonId("Test Polygon", 2023); - - var polygon = getTestPolygon(polygonId, TestUtils.valid()); - var layer = this.getTestPrimaryLayer(polygonId, TestUtils.valid(), TestUtils.valid()); - var spec1 = getTestSpecies(polygonId, LayerType.PRIMARY, "B", x -> { - x.setPercentGenus(75f); - }); - var spec2 = getTestSpecies(polygonId, LayerType.PRIMARY, "C", x -> { - x.setPercentGenus(25f); - }); - layer.setSpecies(List.of(spec1, spec2)); - polygon.setLayers(List.of(layer)); - - assertDoesNotThrow(() -> app.checkPolygon(polygon)); - } - } - - @Test - void testTwoSpeciesSumToLessThan100Percent() throws Exception { - - var controlMap = FipTestUtils.loadControlMap(); - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var polygonId = polygonId("Test Polygon", 2023); - - var polygon = getTestPolygon(polygonId, TestUtils.valid()); - var layer = this.getTestPrimaryLayer(polygonId, TestUtils.valid(), TestUtils.valid()); - var spec1 = getTestSpecies(polygonId, LayerType.PRIMARY, "B", x -> { - x.setPercentGenus(75f - 1f); - }); - var spec2 = getTestSpecies(polygonId, LayerType.PRIMARY, "C", x -> { - x.setPercentGenus(25f); - }); - layer.setSpecies(List.of(spec1, spec2)); - polygon.setLayers(List.of(layer)); - - var ex = assertThrows(StandProcessingException.class, () -> app.checkPolygon(polygon)); - assertThat( - ex, - hasProperty( - "message", - is( - "Polygon \"" + polygonId - + "\" has PRIMARY layer where species entries have a percentage total that does not sum to 100%." - ) - ) - ); - } - - } - - @Test - void testTwoSpeciesSumToMoreThan100Percent() throws Exception { - - var controlMap = FipTestUtils.loadControlMap(); - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var polygonId = polygonId("Test Polygon", 2023); - - var polygon = getTestPolygon(polygonId, TestUtils.valid()); - var layer = this.getTestPrimaryLayer(polygonId, TestUtils.valid(), TestUtils.valid()); - var spec1 = getTestSpecies(polygonId, LayerType.PRIMARY, "B", x -> { - x.setPercentGenus(75f + 1f); - }); - var spec2 = getTestSpecies(polygonId, LayerType.PRIMARY, "C", x -> { - x.setPercentGenus(25f); - }); - layer.setSpecies(List.of(spec1, spec2)); - polygon.setLayers(List.of(layer)); - - var ex = assertThrows(StandProcessingException.class, () -> app.checkPolygon(polygon)); - assertThat( - ex, - hasProperty( - "message", - is( - "Polygon \"" + polygonId - + "\" has PRIMARY layer where species entries have a percentage total that does not sum to 100%." - ) - ) - ); - } - - } - - @Test - void testFractionGenusCalculation() throws Exception { - - var polygonId = polygonId("Test Polygon", 2023); - var layer = LayerType.PRIMARY; - - final var speciesList = Arrays.asList( - // - getTestSpecies(polygonId, layer, "B", x -> { - x.setPercentGenus(75f); - }), getTestSpecies(polygonId, layer, "C", x -> { - x.setPercentGenus(25f); - }) - ); - testWith( - FipTestUtils.loadControlMap(), Arrays.asList(getTestPolygon(polygonId, TestUtils.valid())), // - Arrays.asList(layerMap(getTestPrimaryLayer(polygonId, TestUtils.valid(), TestUtils.valid()))), // - Arrays.asList(speciesList), // - (app, controlMap) -> { - - app.process(); - - // Testing exact floating point equality is intentional - assertThat( - speciesList, contains( - // - allOf(hasProperty("genus", is("B")), hasProperty("fractionGenus", is(0.75f))), // - allOf(hasProperty("genus", is("C")), hasProperty("fractionGenus", is(0.25f)))// - ) - ); - } - ); - - } - - @Test - void testFractionGenusCalculationWithSlightError() throws Exception { - - var polygonId = polygonId("Test Polygon", 2023); - var layer = LayerType.PRIMARY; - - final var speciesList = Arrays.asList( - // - getTestSpecies(polygonId, layer, "B", x -> { - x.setPercentGenus(75 + 0.009f); - }), getTestSpecies(polygonId, layer, "C", x -> { - x.setPercentGenus(25f); - }) - ); - testWith( - FipTestUtils.loadControlMap(), Arrays.asList(getTestPolygon(polygonId, TestUtils.valid())), // - Arrays.asList(layerMap(getTestPrimaryLayer(polygonId, TestUtils.valid(), TestUtils.valid()))), // - Arrays.asList(speciesList), // - (app, controlMap) -> { - - app.process(); - - // Testing exact floating point equality is intentional - assertThat( - speciesList, contains( - // - allOf(hasProperty("genus", is("B")), hasProperty("fractionGenus", is(0.75002253f))), // - allOf(hasProperty("genus", is("C")), hasProperty("fractionGenus", is(0.2499775f)))// - ) - ); - } - ); - - } - - @Test - void testProcessVeteran() throws Exception { - - var polygonId = polygonId("Test Polygon", 2023); - - var fipPolygon = getTestPolygon(polygonId, TestUtils.valid()); - var fipLayer = getTestVeteranLayer(polygonId, TestUtils.valid(), TestUtils.valid()); - var fipSpecies = getTestSpecies(polygonId, LayerType.VETERAN, x -> { - x.setSpeciesPercent(Collections.emptyMap()); - }); - fipPolygon.setLayers(Collections.singletonMap(LayerType.VETERAN, fipLayer)); - fipLayer.setSpecies(Collections.singletonMap(fipSpecies.getGenus(), fipSpecies)); - - var controlMap = new HashMap(); - TestUtils.populateControlMapBecReal(controlMap); - TestUtils.populateControlMapGenusReal(controlMap); - TestUtils.populateControlMapVeteranBq(controlMap); - TestUtils.populateControlMapEquationGroups(controlMap, (s, b) -> new int[] { 1, 1, 1 }); - TestUtils.populateControlMapVeteranDq(controlMap, (s, r) -> new float[] { 0f, 0f, 0f }); - TestUtils.populateControlMapVeteranVolAdjust(controlMap, s -> new float[] { 0f, 0f, 0f, 0f }); - TestUtils.populateControlMapWholeStemVolume(controlMap, TestUtils.wholeStemMap(1)); - TestUtils.populateControlMapCloseUtilization(controlMap, TestUtils.closeUtilMap(1)); - TestUtils.populateControlMapNetDecay(controlMap, TestUtils.closeUtilMap(1)); - TestUtils.populateControlMapDecayModifiers(controlMap, (s, r) -> 0f); - TestUtils.populateControlMapNetWaste( - controlMap, s -> new Coefficients(new float[] { 0f, 0f, 0f, 0f, 0f, 0f }, 0) - ); - TestUtils.populateControlMapWasteModifiers(controlMap, (s, r) -> 0f); - TestUtils - .populateControlMapNetBreakage(controlMap, bgrp -> new Coefficients(new float[] { 0f, 0f, 0f, 0f }, 1)); - - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var result = app.processLayerAsVeteran(fipPolygon, fipLayer); - - assertThat(result, notNullValue()); - - // Keys - assertThat(result, hasProperty("polygonIdentifier", is(polygonId))); - assertThat(result, hasProperty("layerType", is(LayerType.VETERAN))); - - // Direct Copy - 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", present(is(1f)))); - - // Remap species - assertThat( - result, hasProperty( - "species", allOf( - aMapWithSize(1), // - hasEntry(is("B"), instanceOf(VdypSpecies.class))// - ) - ) - ); - var speciesResult = result.getSpecies().get("B"); - - // Keys - assertThat(speciesResult, hasProperty("polygonIdentifier", is(polygonId))); - assertThat(speciesResult, hasProperty("layerType", is(LayerType.VETERAN))); - assertThat(speciesResult, hasProperty("genus", is("B"))); - - // Copied - assertThat(speciesResult, hasProperty("percentGenus", is(100f))); - - // Species distribution - assertThat(speciesResult, hasProperty("speciesPercent", anEmptyMap())); // Test map was empty - } - } - - @Test - void testProcessVeteranUtilization() throws Exception { - - var polygonId = polygonId("Test Polygon", 2023); - - var fipPolygon = getTestPolygon(polygonId, x -> { - x.setBiogeoclimaticZone("CWH"); - x.setForestInventoryZone("A"); - x.setYieldFactor(1f); - }); - var fipLayer = getTestVeteranLayer(polygonId, layerBuilder -> { - layerBuilder.crownClosure(4f); - }, siteBuilder -> { - siteBuilder.siteCurveNumber(Optional.of(34)); - siteBuilder.height(26.2f); - siteBuilder.siteIndex(16.7f); - siteBuilder.yearsToBreastHeight(7.1f); - siteBuilder.ageTotal(97.9f + 7.1f); - siteBuilder.siteSpecies("H"); - siteBuilder.siteGenus("H"); - }); - var fipSpecies1 = getTestSpecies(polygonId, LayerType.VETERAN, "B", x -> { - x.setPercentGenus(22f); - }); - var fipSpecies2 = getTestSpecies(polygonId, LayerType.VETERAN, "H", x -> { - x.setPercentGenus(60f); - }); - var fipSpecies3 = getTestSpecies(polygonId, LayerType.VETERAN, "S", x -> { - x.setPercentGenus(18f); - }); - fipPolygon.setLayers(Collections.singletonMap(LayerType.VETERAN, fipLayer)); - fipLayer.setSpecies(List.of(fipSpecies1, fipSpecies2, fipSpecies3)); - - var controlMap = FipTestUtils.loadControlMap(); - - VdypLayer result; - try (var app = new FipStart();) { - ApplicationTestUtils.setControlMap(app, controlMap); - - result = app.processLayerAsVeteran(fipPolygon, fipLayer); - } - - assertThat(result, notNullValue()); - - // Keys - assertThat(result, hasProperty("polygonIdentifier", is(polygonId))); - assertThat(result, hasProperty("layerType", is(LayerType.VETERAN))); - - // Direct Copy - assertThat(result, hasProperty("ageTotal", present(is(105f)))); - assertThat(result, hasProperty("height", present(is(26.2f)))); - assertThat(result, hasProperty("yearsToBreastHeight", present(is(7.1f)))); - - // Computed - assertThat(result, hasProperty("breastHeightAge", present(closeTo(97.9f)))); - - // Remap species - assertThat( - result, hasProperty( - "species", allOf( - aMapWithSize(3), // - hasEntry(is("B"), instanceOf(VdypSpecies.class)), // - hasEntry(is("H"), instanceOf(VdypSpecies.class)), // - hasEntry(is("S"), instanceOf(VdypSpecies.class))// - ) - ) - ); - - var speciesResult1 = result.getSpecies().get("B"); - - // Keys - assertThat(speciesResult1, hasProperty("polygonIdentifier", is(polygonId))); - assertThat(speciesResult1, hasProperty("layerType", is(LayerType.VETERAN))); - assertThat(speciesResult1, hasProperty("genus", is("B"))); - - // Copied - assertThat(speciesResult1, hasProperty("percentGenus", is(22f))); - - // Species distribution - assertThat(speciesResult1, hasProperty("speciesPercent", aMapWithSize(1))); - - var speciesResult2 = result.getSpecies().get("H"); - - // Keys - assertThat(speciesResult2, hasProperty("polygonIdentifier", is(polygonId))); - assertThat(speciesResult2, hasProperty("layerType", is(LayerType.VETERAN))); - assertThat(speciesResult2, hasProperty("genus", is("H"))); - - // Copied - assertThat(speciesResult2, hasProperty("percentGenus", is(60f))); - - // Species distribution - assertThat(speciesResult2, hasProperty("speciesPercent", aMapWithSize(1))); - - var speciesResult3 = result.getSpecies().get("S"); - - // Keys - assertThat(speciesResult3, hasProperty("polygonIdentifier", is(polygonId))); - assertThat(speciesResult3, hasProperty("layerType", is(LayerType.VETERAN))); - assertThat(speciesResult3, hasProperty("genus", is("S"))); - - // Copied - assertThat(speciesResult3, hasProperty("percentGenus", is(18f))); - - // Species distribution - assertThat(speciesResult3, hasProperty("speciesPercent", aMapWithSize(1))); - - // These Utilizations should differ between the layer and each genus - - { - var holder = speciesResult1; - String reason = "Genus " + holder.getGenus(); - assertThat( - reason, holder, - hasProperty("baseAreaByUtilization", VdypMatchers.utilizationAllAndBiggest(0.492921442f)) - ); - assertThat( - reason, holder, - hasProperty("treesPerHectareByUtilization", VdypMatchers.utilizationAllAndBiggest(2.3357718f)) - ); - assertThat( - reason, holder, - hasProperty("wholeStemVolumeByUtilization", VdypMatchers.utilizationAllAndBiggest(6.11904192f)) - ); - assertThat( - reason, holder, - hasProperty( - "closeUtilizationVolumeByUtilization", VdypMatchers.utilizationAllAndBiggest(5.86088896f) - ) - ); - assertThat( - reason, holder, - hasProperty( - "closeUtilizationVolumeNetOfDecayByUtilization", - VdypMatchers.utilizationAllAndBiggest(5.64048958f) - ) - ); - assertThat( - reason, holder, - hasProperty( - "closeUtilizationVolumeNetOfDecayAndWasteByUtilization", - VdypMatchers.utilizationAllAndBiggest(5.57935333f) - ) - ); - assertThat( - reason, holder, - hasProperty( - "closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", - VdypMatchers.utilizationAllAndBiggest(5.27515411f) - ) - ); - assertThat( - reason, holder, - hasProperty( - "quadraticMeanDiameterByUtilization", VdypMatchers.utilizationAllAndBiggest(51.8356705f) - ) - ); - } - { - var holder = speciesResult2; - String reason = "Genus " + holder.getGenus(); - assertThat( - reason, holder, - hasProperty("baseAreaByUtilization", VdypMatchers.utilizationAllAndBiggest(1.34433115f)) - ); - assertThat( - reason, holder, - hasProperty("treesPerHectareByUtilization", VdypMatchers.utilizationAllAndBiggest(5.95467329f)) - ); - assertThat( - reason, holder, - hasProperty("wholeStemVolumeByUtilization", VdypMatchers.utilizationAllAndBiggest(14.5863571f)) - ); - assertThat( - reason, holder, - hasProperty( - "closeUtilizationVolumeByUtilization", VdypMatchers.utilizationAllAndBiggest(13.9343023f) - ) - ); - assertThat( - reason, holder, - hasProperty( - "closeUtilizationVolumeNetOfDecayByUtilization", - VdypMatchers.utilizationAllAndBiggest(13.3831034f) - ) - ); - assertThat( - reason, holder, - hasProperty( - "closeUtilizationVolumeNetOfDecayAndWasteByUtilization", - VdypMatchers.utilizationAllAndBiggest(13.2065458f) - ) - ); - assertThat( - reason, holder, - hasProperty( - "closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", - VdypMatchers.utilizationAllAndBiggest(12.4877129f) - ) - ); - assertThat( - reason, holder, - hasProperty( - "quadraticMeanDiameterByUtilization", VdypMatchers.utilizationAllAndBiggest(53.6141243f) - ) - ); - } - { - var holder = speciesResult3; - String reason = "Genus " + holder.getGenus(); - assertThat( - reason, holder, - hasProperty("baseAreaByUtilization", VdypMatchers.utilizationAllAndBiggest(0.403299361f)) - ); - assertThat( - reason, holder, - hasProperty("treesPerHectareByUtilization", VdypMatchers.utilizationAllAndBiggest(2.38468361f)) - ); - assertThat( - reason, holder, - hasProperty("wholeStemVolumeByUtilization", VdypMatchers.utilizationAllAndBiggest(4.04864883f)) - ); - assertThat( - reason, holder, - hasProperty( - "closeUtilizationVolumeByUtilization", VdypMatchers.utilizationAllAndBiggest(3.81141663f) - ) - ); - assertThat( - reason, holder, - hasProperty( - "closeUtilizationVolumeNetOfDecayByUtilization", - VdypMatchers.utilizationAllAndBiggest(3.75043678f) - ) - ); - assertThat( - reason, holder, - hasProperty( - "closeUtilizationVolumeNetOfDecayAndWasteByUtilization", - VdypMatchers.utilizationAllAndBiggest(3.72647476f) - ) - ); - assertThat( - reason, holder, - hasProperty( - "closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", - VdypMatchers.utilizationAllAndBiggest(3.56433797f) - ) - ); - assertThat( - reason, holder, - hasProperty( - "quadraticMeanDiameterByUtilization", VdypMatchers.utilizationAllAndBiggest(46.4037895f) - ) - ); - } - { - var holder = result; - String reason = "Layer"; - assertThat( - reason, holder, - hasProperty("baseAreaByUtilization", VdypMatchers.utilizationAllAndBiggest(2.24055195f)) - ); - assertThat( - reason, holder, - hasProperty("treesPerHectareByUtilization", VdypMatchers.utilizationAllAndBiggest(10.6751289f)) - ); - assertThat( - reason, holder, - hasProperty("wholeStemVolumeByUtilization", VdypMatchers.utilizationAllAndBiggest(24.7540474f)) - ); - assertThat( - reason, holder, - hasProperty( - "closeUtilizationVolumeByUtilization", VdypMatchers.utilizationAllAndBiggest(23.6066074f) - ) - ); - assertThat( - reason, holder, - hasProperty( - "closeUtilizationVolumeNetOfDecayByUtilization", - VdypMatchers.utilizationAllAndBiggest(22.7740307f) - ) - ); - assertThat( - reason, holder, - hasProperty( - "closeUtilizationVolumeNetOfDecayAndWasteByUtilization", - VdypMatchers.utilizationAllAndBiggest(22.5123749f) - ) - ); - assertThat( - reason, holder, - hasProperty( - "closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", - VdypMatchers.utilizationAllAndBiggest(21.3272057f) - ) - ); - assertThat( - reason, holder, - hasProperty( - "quadraticMeanDiameterByUtilization", VdypMatchers.utilizationAllAndBiggest(51.6946983f) - ) - ); - } - // Lorey Height should be the same across layer and each species - for (var holder : List.of(speciesResult1, speciesResult2, speciesResult3, result)) { - String reason; - if (holder instanceof VdypLayer) { - reason = "Layer"; - } else { - reason = "Genus " + ((VdypSpecies) holder).getGenus(); - } - assertThat(reason, holder, hasProperty("loreyHeightByUtilization", coe(-1, 0f, 26.2f))); - } - } - - @Test - void testProcessVeteranYearsToBreastHeightLessThanMinimum() throws Exception { - - var polygonId = polygonId("Test Polygon", 2023); - - var fipPolygon = getTestPolygon(polygonId, TestUtils.valid()); - var fipLayer = getTestVeteranLayer(polygonId, TestUtils.valid(), siteBuilder -> { - siteBuilder.yearsToBreastHeight(5.0f); - }); - var fipSpecies = getTestSpecies(polygonId, LayerType.VETERAN, TestUtils.valid()); - fipPolygon.setLayers(Collections.singletonMap(LayerType.VETERAN, fipLayer)); - fipLayer.setSpecies(Collections.singletonMap(fipSpecies.getGenus(), fipSpecies)); - - var controlMap = new HashMap(); - TestUtils.populateControlMapBecReal(controlMap); - TestUtils.populateControlMapGenusReal(controlMap); - TestUtils.populateControlMapVeteranBq(controlMap); - TestUtils.populateControlMapEquationGroups(controlMap, (s, b) -> new int[] { 1, 1, 1 }); - TestUtils.populateControlMapVeteranDq(controlMap, (s, r) -> new float[] { 0f, 0f, 0f }); - TestUtils.populateControlMapVeteranVolAdjust(controlMap, s -> new float[] { 0f, 0f, 0f, 0f }); - TestUtils.populateControlMapWholeStemVolume(controlMap, (TestUtils.wholeStemMap(1))); - TestUtils.populateControlMapCloseUtilization(controlMap, TestUtils.closeUtilMap(1)); - TestUtils.populateControlMapNetDecay(controlMap, TestUtils.closeUtilMap(1)); - TestUtils.populateControlMapDecayModifiers(controlMap, (s, r) -> 0f); - TestUtils.populateControlMapNetWaste( - controlMap, s -> new Coefficients(new float[] { 0f, 0f, 0f, 0f, 0f, 0f }, 0) - ); - TestUtils.populateControlMapWasteModifiers(controlMap, (s, r) -> 0f); - TestUtils - .populateControlMapNetBreakage(controlMap, bgrp -> new Coefficients(new float[] { 0f, 0f, 0f, 0f }, 1)); - - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var result = app.processLayerAsVeteran(fipPolygon, fipLayer); - - assertThat(result, notNullValue()); - - // Set minimum - assertThat(result, hasProperty("yearsToBreastHeight", present(is(6f)))); - - // Computed based on minimum - assertThat(result, hasProperty("breastHeightAge", present(is(2f)))); - } - - } - - @Test - void testProcessVeteranWithSpeciesDistribution() throws Exception { - - var polygonId = polygonId("Test Polygon", 2023); - - var fipPolygon = getTestPolygon(polygonId, TestUtils.valid()); - var fipLayer = getTestVeteranLayer(polygonId, TestUtils.valid(), TestUtils.valid()); - var fipSpecies = getTestSpecies(polygonId, LayerType.VETERAN, x -> { - var map = new LinkedHashMap(); - map.put("S1", 75f); - map.put("S2", 25f); - x.setSpeciesPercent(map); - }); - fipPolygon.setLayers(Collections.singletonMap(LayerType.VETERAN, fipLayer)); - fipLayer.setSpecies(Collections.singletonMap(fipSpecies.getGenus(), fipSpecies)); - - var controlMap = new HashMap(); - TestUtils.populateControlMapBecReal(controlMap); - TestUtils.populateControlMapGenusReal(controlMap); - TestUtils.populateControlMapVeteranBq(controlMap); - TestUtils.populateControlMapEquationGroups(controlMap, (s, b) -> new int[] { 1, 1, 1 }); - TestUtils.populateControlMapVeteranDq(controlMap, (s, r) -> new float[] { 0f, 0f, 0f }); - TestUtils.populateControlMapVeteranVolAdjust(controlMap, s -> new float[] { 0f, 0f, 0f, 0f }); - TestUtils.populateControlMapWholeStemVolume(controlMap, (TestUtils.wholeStemMap(1))); - TestUtils.populateControlMapCloseUtilization(controlMap, TestUtils.closeUtilMap(1)); - TestUtils.populateControlMapNetDecay(controlMap, TestUtils.closeUtilMap(1)); - TestUtils.populateControlMapDecayModifiers(controlMap, (s, r) -> 0f); - TestUtils.populateControlMapNetWaste( - controlMap, s -> new Coefficients(new float[] { 0f, 0f, 0f, 0f, 0f, 0f }, 0) - ); - TestUtils.populateControlMapWasteModifiers(controlMap, (s, r) -> 0f); - TestUtils - .populateControlMapNetBreakage(controlMap, bgrp -> new Coefficients(new float[] { 0f, 0f, 0f, 0f }, 1)); - - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var result = app.processLayerAsVeteran(fipPolygon, fipLayer); - - assertThat(result, notNullValue()); - - // Remap species - assertThat( - result, hasProperty( - "species", allOf( - aMapWithSize(1), // - hasEntry(is("B"), instanceOf(VdypSpecies.class))// - ) - ) - ); - var speciesResult = result.getSpecies().get("B"); - - // Keys - assertThat(speciesResult, hasProperty("polygonIdentifier", is(polygonId))); - assertThat(speciesResult, hasProperty("layerType", is(LayerType.VETERAN))); - assertThat(speciesResult, hasProperty("genus", is("B"))); - - // Copied - assertThat(speciesResult, hasProperty("percentGenus", is(100f))); - - // Species distribution - assertThat(speciesResult, hasProperty("speciesPercent", aMapWithSize(2))); - - var distributionResult = speciesResult.getSpeciesPercent(); - - assertThat(distributionResult, hasEntry("S1", 75f)); - assertThat(distributionResult, hasEntry("S2", 25f)); - } - - } - - @Test - void testProcessPrimary() throws Exception { - - var polygonId = polygonId("Test Polygon", 2023); - - var fipPolygon = getTestPolygon(polygonId, x -> { - x.setBiogeoclimaticZone("CWH"); - x.setForestInventoryZone("A"); - }); - var fipLayer = getTestPrimaryLayer(polygonId, layerBuilder -> { - ((FipLayerPrimary.Builder) layerBuilder).crownClosure(87.4f); - ((FipLayerPrimary.PrimaryBuilder) layerBuilder).primaryGenus("H"); - layerBuilder.inventoryTypeGroup(13); - }, siteBuilder -> { - siteBuilder.ageTotal(55f); - siteBuilder.yearsToBreastHeight(1f); - siteBuilder.height(35.3f); - siteBuilder.siteIndex(5f); - siteBuilder.siteGenus("D"); - siteBuilder.siteSpecies("D"); - }); - var fipSpecies1 = getTestSpecies(polygonId, LayerType.PRIMARY, "B", x -> { - x.setPercentGenus(1f); - }); - var fipSpecies2 = getTestSpecies(polygonId, LayerType.PRIMARY, "C", x -> { - x.setPercentGenus(7f); - }); - var fipSpecies3 = getTestSpecies(polygonId, LayerType.PRIMARY, "D", x -> { - x.setPercentGenus(74f); - }); - var fipSpecies4 = getTestSpecies(polygonId, LayerType.PRIMARY, "H", x -> { - x.setPercentGenus(9f); - }); - var fipSpecies5 = getTestSpecies(polygonId, LayerType.PRIMARY, "S", x -> { - x.setPercentGenus(9f); - }); - fipPolygon.setLayers(List.of(fipLayer)); - fipLayer.setSpecies(List.of(fipSpecies1, fipSpecies2, fipSpecies3, fipSpecies4, fipSpecies5)); - - var controlMap = FipTestUtils.loadControlMap(); - - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var result = app.processLayerAsPrimary(fipPolygon, fipLayer, 0f); - - assertThat(result, notNullValue()); - - assertThat(result, hasProperty("polygonIdentifier", is(polygonId))); - assertThat(result, hasProperty("layerType", is(LayerType.PRIMARY))); - - 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", present(is(54f)))); - - assertThat( - result, - allOf( - hasProperty("loreyHeightByUtilization", coe(-1, 7.14446497f, 31.3307228f)), - hasProperty( - "baseAreaByUtilization", - VdypMatchers.utilization( - 0.0153773092f, 44.6249809f, 0.513127923f, 1.26773751f, 2.5276401f, - 40.3164787f - ) - ), - hasProperty( - "quadraticMeanDiameterByUtilization", - VdypMatchers.utilization( - 6.05058956f, 30.2606678f, 10.208025f, 15.0549212f, 20.11759f, 35.5117531f - ) - ), - hasProperty( - "treesPerHectareByUtilization", - VdypMatchers.utilization( - 5.34804535f, 620.484802f, 62.6977997f, 71.2168045f, 79.5194702f, 407.05072f - ) - ), - hasProperty( - "wholeStemVolumeByUtilization", - VdypMatchers.utilization( - 0.0666879341f, 635.659668f, 2.66822577f, 9.68201256f, 26.5469246f, - 596.762512f - ) - ), - - // Ignore intermediate close volumes, if they are wrong, - // closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization should also be - // wrong - - hasProperty( - "closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", - VdypMatchers.utilization( - 0f, 563.218933f, 0.414062887f, 7.01947737f, 22.6179276f, 533.16748f - ) - ) - - ) - ); - - assertThat( - result, hasProperty( - "species", allOf( - aMapWithSize(5), // - hasEntry(is("B"), instanceOf(VdypSpecies.class)), // - hasEntry(is("C"), instanceOf(VdypSpecies.class)), // - hasEntry(is("D"), instanceOf(VdypSpecies.class)), // - hasEntry(is("H"), instanceOf(VdypSpecies.class)), // - hasEntry(is("S"), instanceOf(VdypSpecies.class))// - ) - ) - ); - - // Setting the primaryGenus on the FIP layer is a necessary side effect - assertThat(fipLayer, hasProperty("primaryGenus", present(equalTo("D")))); - - var speciesResult = result.getSpecies().get("B"); - - assertThat(speciesResult, hasProperty("polygonIdentifier", is(polygonId))); - assertThat(speciesResult, hasProperty("layerType", is(LayerType.PRIMARY))); - assertThat(speciesResult, hasProperty("genus", is("B"))); - - assertThat(speciesResult, hasProperty("fractionGenus", closeTo(0.00890319888f))); - - assertThat(speciesResult, hasProperty("speciesPercent", aMapWithSize(1))); - - assertThat( - speciesResult, - allOf( - hasProperty("loreyHeightByUtilization", coe(-1, 8.39441967f, 38.6004372f)), - hasProperty( - "baseAreaByUtilization", - VdypMatchers.utilization( - 0f, 0.397305071f, 0.00485289097f, 0.0131751001f, 0.0221586525f, 0.357118428f - ) - ), - hasProperty( - "quadraticMeanDiameterByUtilization", - VdypMatchers.utilization( - 6.13586617f, 31.6622887f, 9.17939758f, 13.6573782f, 18.2005272f, 42.1307297f - ) - ), - hasProperty( - "treesPerHectareByUtilization", - VdypMatchers.utilization( - 0f, 5.04602766f, 0.733301044f, 0.899351299f, 0.851697803f, 2.56167722f - ) - ), - hasProperty( - "wholeStemVolumeByUtilization", - VdypMatchers.utilization( - 0f, 6.35662031f, 0.0182443243f, 0.0747248605f, 0.172960356f, 6.09069061f - ) - ), - - // Ignore intermediate close volumes, if they are wrong, - // closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization should also be - // wrong - - hasProperty( - "closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", - VdypMatchers.utilization( - 0f, 5.65764236f, 0.000855736958f, 0.046797853f, 0.143031254f, 5.46695757f - ) - ) - - ) - ); - - speciesResult = result.getSpecies().get("C"); - - assertThat(speciesResult, hasProperty("polygonIdentifier", is(polygonId))); - assertThat(speciesResult, hasProperty("layerType", is(LayerType.PRIMARY))); - assertThat(speciesResult, hasProperty("genus", is("C"))); - - assertThat(speciesResult, hasProperty("fractionGenus", closeTo(0.114011094f))); - - assertThat(speciesResult, hasProperty("speciesPercent", aMapWithSize(1))); - - assertThat( - speciesResult, - allOf( - hasProperty("loreyHeightByUtilization", coe(-1, 6.61517191f, 22.8001652f)), - hasProperty( - "baseAreaByUtilization", - VdypMatchers.utilization( - 0.0131671466f, 5.08774281f, 0.157695293f, 0.365746498f, 0.565057278f, - 3.99924374f - ) - ), - hasProperty( - "quadraticMeanDiameterByUtilization", - VdypMatchers.utilization( - 5.99067688f, 26.4735165f, 10.1137667f, 14.9345293f, 19.964777f, 38.7725677f - ) - ), - hasProperty( - "treesPerHectareByUtilization", - VdypMatchers.utilization( - 4.67143154f, 92.4298019f, 19.6292171f, 20.8788815f, 18.0498524f, 33.8718452f - ) - ), - hasProperty( - "wholeStemVolumeByUtilization", - VdypMatchers.utilization( - 0.0556972362f, 44.496151f, 0.78884691f, 2.40446854f, 4.43335152f, - 36.8694839f - ) - ), - - // Ignore intermediate close volumes, if they are wrong, - // closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization should also be - // wrong - - hasProperty( - "closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", - VdypMatchers.utilization( - 0f, 33.6030083f, 0.138336331f, 1.6231581f, 3.49037051f, 28.3511429f - ) - ) - - ) - ); - - speciesResult = result.getSpecies().get("D"); - - assertThat(speciesResult, hasProperty("polygonIdentifier", is(polygonId))); - assertThat(speciesResult, hasProperty("layerType", is(LayerType.PRIMARY))); - assertThat(speciesResult, hasProperty("genus", is("D"))); - - assertThat(speciesResult, hasProperty("fractionGenus", closeTo(0.661987007f))); - - assertThat(speciesResult, hasProperty("speciesPercent", aMapWithSize(1))); - - assertThat( - speciesResult, - allOf( - hasProperty("loreyHeightByUtilization", coe(-1, 10.8831682f, 33.5375252f)), - hasProperty( - "baseAreaByUtilization", - VdypMatchers.utilization( - 0.00163476227f, 29.5411568f, 0.0225830078f, 0.0963115692f, 0.748186111f, - 28.6740761f - ) - ), - hasProperty( - "quadraticMeanDiameterByUtilization", - VdypMatchers.utilization( - 6.46009731f, 33.9255791f, 10.4784775f, 15.5708427f, 20.4805717f, 35.0954628f - ) - ), - hasProperty( - "treesPerHectareByUtilization", - VdypMatchers.utilization( - 0.498754263f, 326.800781f, 2.61875916f, 5.05783129f, 22.7109661f, - 296.413239f - ) - ), - hasProperty( - "wholeStemVolumeByUtilization", - VdypMatchers.utilization( - 0.0085867513f, 470.388489f, 0.182312608f, 1.08978188f, 10.1118069f, - 459.004578f - ) - ), - - // Ignore intermediate close volumes, if they are wrong, - // closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization should also be - // wrong - - hasProperty( - "closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", - VdypMatchers.utilization( - 0f, 424.163849f, 0.0895428956f, 0.929004371f, 8.9712553f, 414.174042f - ) - ) - - ) - ); - - speciesResult = result.getSpecies().get("H"); - - assertThat(speciesResult, hasProperty("polygonIdentifier", is(polygonId))); - assertThat(speciesResult, hasProperty("layerType", is(LayerType.PRIMARY))); - assertThat(speciesResult, hasProperty("genus", is("H"))); - - assertThat(speciesResult, hasProperty("fractionGenus", closeTo(0.123297341f))); - - assertThat(speciesResult, hasProperty("speciesPercent", aMapWithSize(1))); - - assertThat( - speciesResult, - allOf( - hasProperty("loreyHeightByUtilization", coe(-1, 7.93716192f, 24.3451157f)), - hasProperty( - "baseAreaByUtilization", - VdypMatchers.utilization( - 0f, 5.50214148f, 0.311808586f, 0.736046314f, 0.988982677f, 3.4653039f - ) - ), - hasProperty( - "quadraticMeanDiameterByUtilization", - VdypMatchers.utilization( - 6.03505516f, 21.4343796f, 10.260808f, 15.0888424f, 20.0664616f, 32.2813988f - ) - ), - hasProperty( - "treesPerHectareByUtilization", - VdypMatchers.utilization( - 0f, 152.482513f, 37.7081375f, 41.1626587f, 31.2721119f, 42.3395996f - ) - ), - hasProperty( - "wholeStemVolumeByUtilization", - VdypMatchers.utilization( - 0f, 57.2091446f, 1.57991886f, 5.59581661f, 9.53606987f, 40.4973412f - ) - ), - - // Ignore intermediate close volumes, if they are wrong, - // closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization should also be - // wrong - - hasProperty( - "closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", - VdypMatchers.utilization( - 0f, 48.1333618f, 0.168331802f, 4.01862335f, 8.05745506f, 35.8889503f - ) - ) - - ) - ); - - speciesResult = result.getSpecies().get("S"); - - assertThat(speciesResult, hasProperty("polygonIdentifier", is(polygonId))); - assertThat(speciesResult, hasProperty("layerType", is(LayerType.PRIMARY))); - assertThat(speciesResult, hasProperty("genus", is("S"))); - - assertThat(speciesResult, hasProperty("fractionGenus", closeTo(0.0918014571f))); - - assertThat(speciesResult, hasProperty("speciesPercent", aMapWithSize(1))); - - assertThat( - speciesResult, - allOf( - hasProperty("loreyHeightByUtilization", coe(-1, 8.63455391f, 34.6888771f)), - hasProperty( - "baseAreaByUtilization", - VdypMatchers.utilization( - 0.000575399841f, 4.0966382f, 0.0161881447f, 0.0564579964f, 0.203255415f, - 3.82073665f - ) - ), - hasProperty( - "quadraticMeanDiameterByUtilization", - VdypMatchers.utilization( - 6.41802597f, 34.5382729f, 10.1304808f, 14.9457884f, 19.7497196f, 39.0729332f - ) - ), - hasProperty( - "treesPerHectareByUtilization", - VdypMatchers.utilization( - 0.17785944f, 43.7256737f, 2.00838566f, 3.21808815f, 6.63483906f, 31.8643608f - ) - ), - hasProperty( - "wholeStemVolumeByUtilization", - VdypMatchers.utilization( - 0.00240394124f, 57.2092552f, 0.0989032984f, 0.517220974f, 2.29273605f, - 54.300396f - ) - ), - - // Ignore intermediate close volumes, if they are wrong, - // closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization should also be - // wrong - - hasProperty( - "closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", - VdypMatchers.utilization( - 0f, 51.6610985f, 0.0169961192f, 0.401893795f, 1.95581412f, 49.286396f - ) - ) - - ) - ); - } - } - - @Test - void testProcessPrimaryWithOverstory() throws Exception { - - var polygonId = polygonId("01002 S000002 00", 1970); - - var fipPolygon = getTestPolygon(polygonId, x -> { - x.setBiogeoclimaticZone("CWH"); - x.setForestInventoryZone("A"); - }); - var fipLayer = getTestPrimaryLayer(polygonId, x -> { - ((FipLayer.Builder) x).crownClosure(82.8f); - ((PrimaryBuilder) x).primaryGenus(Optional.empty()); - x.inventoryTypeGroup(Optional.empty()); - }, x -> { - x.ageTotal(45f); - x.height(24.3f); - x.siteIndex(28.7f); - x.siteGenus("H"); - x.siteSpecies("H"); - x.yearsToBreastHeight(5.4f); - x.siteCurveNumber(34); - }); - var fipSpecies1 = getTestSpecies(polygonId, LayerType.PRIMARY, "B", x -> { - x.setPercentGenus(15f); - }); - var fipSpecies2 = getTestSpecies(polygonId, LayerType.PRIMARY, "D", x -> { - x.setPercentGenus(7f); - }); - var fipSpecies3 = getTestSpecies(polygonId, LayerType.PRIMARY, "H", x -> { - x.setPercentGenus(77f); - }); - var fipSpecies4 = getTestSpecies(polygonId, LayerType.PRIMARY, "S", x -> { - x.setPercentGenus(1f); - }); - fipPolygon.setLayers(List.of(fipLayer)); - fipLayer.setSpecies(List.of(fipSpecies1, fipSpecies2, fipSpecies3, fipSpecies4)); - - var controlMap = FipTestUtils.loadControlMap(); - - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var result = app.processLayerAsPrimary(fipPolygon, fipLayer, 2.24055195f); - - assertThat(result, notNullValue()); - - assertThat(result, hasProperty("polygonIdentifier", is(polygonId))); - assertThat(result, hasProperty("layerType", is(LayerType.PRIMARY))); - - assertThat(result, hasProperty("ageTotal", present(is(45f)))); - assertThat(result, hasProperty("height", present(is(24.3f)))); - assertThat(result, hasProperty("yearsToBreastHeight", present(is(5.4f)))); - - assertThat(result, hasProperty("breastHeightAge", present(is(45f - 5.4f)))); - - assertThat( - result, hasProperty( - "species", allOf( - aMapWithSize(4), // - hasEntry(is("B"), instanceOf(VdypSpecies.class)), // - hasEntry(is("D"), instanceOf(VdypSpecies.class)), // - hasEntry(is("H"), instanceOf(VdypSpecies.class)), // - hasEntry(is("S"), instanceOf(VdypSpecies.class)) - ) - ) - ); - - // Setting the primaryGenus on the FIP layer is a necessary side effect - assertThat(fipLayer, hasProperty("primaryGenus", present(equalTo("H")))); - - var speciesResult = result.getSpecies().get("H"); - - assertThat(speciesResult, hasProperty("polygonIdentifier", is(polygonId))); - assertThat(speciesResult, hasProperty("layerType", is(LayerType.PRIMARY))); - assertThat(speciesResult, hasProperty("genus", is("H"))); - - assertThat(speciesResult, hasProperty("fractionGenus", closeTo(0.787526369f))); - - assertThat(speciesResult, hasProperty("speciesPercent", aMapWithSize(1))); - - assertThat( - speciesResult, - allOf( - hasProperty("loreyHeightByUtilization", coe(-1, 7.00809479f, 20.9070625f)), - hasProperty( - "baseAreaByUtilization", - VdypMatchers.utilization( - 0.512469947f, 35.401783f, 2.32033157f, 5.18892097f, 6.6573391f, 21.2351913f - ) - ), - hasProperty( - "quadraticMeanDiameterByUtilization", - VdypMatchers.utilization( - 5.94023561f, 20.7426338f, 10.2836504f, 15.1184902f, 20.1040707f, 31.6741638f - ) - ), - hasProperty( - "treesPerHectareByUtilization", - VdypMatchers.utilization( - 184.914597f, 1047.62891f, 279.36087f, 289.048248f, 209.72142f, 269.49826f - ) - ) - - ) - ); - - assertThat( - result, - allOf( - hasProperty("loreyHeightByUtilization", coe(-1, 7.01034021f, 21.1241722f)), - hasProperty( - "baseAreaByUtilization", - VdypMatchers.utilization( - 0.553745031f, 44.9531403f, 2.83213019f, 6.17823505f, 8.11753464f, - 27.8252392f - ) - ), - hasProperty( - "quadraticMeanDiameterByUtilization", - VdypMatchers.utilization( - 5.9399271f, 21.0548763f, 10.235322f, 15.0843554f, 20.0680523f, 32.0662689f - ) - ), - hasProperty( - "treesPerHectareByUtilization", - VdypMatchers.utilization( - 199.828629f, 1291.1145f, 344.207489f, 345.717224f, 256.639709f, 344.549957f - ) - ) - - ) - ); - } - } - - @Test - void testEstimateVeteranLayerBaseArea() throws Exception { - - var controlMap = FipTestUtils.loadControlMap(); - - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var result = app.estimateVeteranBaseArea(26.2000008f, 4f, "H", Region.COASTAL); - - assertThat(result, closeTo(2.24055195f)); - } - } - - void populateControlMapVeteranVolumeAdjust(HashMap controlMap, Function mapper) { - var map = GenusDefinitionParser.getSpeciesAliases(controlMap).stream() - .collect(Collectors.toMap(x -> x, mapper.andThen(x -> new Coefficients(x, 1)))); - - controlMap.put(ControlKey.VETERAN_LAYER_VOLUME_ADJUST.name(), map); - } - - @Test - void testVeteranLayerLoreyHeight() throws Exception { - - var polygonId = polygonId("Test Polygon", 2023); - - var fipPolygon = getTestPolygon(polygonId, TestUtils.valid()); - var fipLayer = getTestVeteranLayer(polygonId, TestUtils.valid(), TestUtils.valid()); - var fipSpecies = getTestSpecies(polygonId, LayerType.VETERAN, x -> { - var map = new LinkedHashMap(); - map.put("B", 100f); - x.setSpeciesPercent(map); - }); - fipPolygon.setLayers(Collections.singletonMap(LayerType.VETERAN, fipLayer)); - fipLayer.setSpecies(Collections.singletonMap(fipSpecies.getGenus(), fipSpecies)); - - var controlMap = new HashMap(); - TestUtils.populateControlMapBecReal(controlMap); - TestUtils.populateControlMapGenusReal(controlMap); - TestUtils.populateControlMapVeteranBq(controlMap); - TestUtils.populateControlMapEquationGroups(controlMap, (s, b) -> new int[] { 1, 1, 1 }); - TestUtils.populateControlMapVeteranDq(controlMap, (s, r) -> new float[] { 0f, 0f, 0f }); - TestUtils.populateControlMapVeteranVolAdjust(controlMap, s -> new float[] { 0f, 0f, 0f, 0f }); - TestUtils.populateControlMapWholeStemVolume(controlMap, (TestUtils.wholeStemMap(1))); - TestUtils.populateControlMapCloseUtilization(controlMap, TestUtils.closeUtilMap(1)); - TestUtils.populateControlMapNetDecay(controlMap, TestUtils.closeUtilMap(1)); - TestUtils.populateControlMapDecayModifiers(controlMap, (s, r) -> 0f); - TestUtils.populateControlMapNetWaste( - controlMap, s -> new Coefficients(new float[] { 0f, 0f, 0f, 0f, 0f, 0f }, 0) - ); - TestUtils.populateControlMapWasteModifiers(controlMap, (s, r) -> 0f); - TestUtils - .populateControlMapNetBreakage(controlMap, bgrp -> new Coefficients(new float[] { 0f, 0f, 0f, 0f }, 1)); - - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var result = app.processLayerAsVeteran(fipPolygon, fipLayer); - - Matcher heightMatcher = closeTo(6f); - Matcher zeroMatcher = is(0.0f); - // Expect the estimated HL in 0 (-1 to 0) - assertThat( - result, - hasProperty( - "species", - hasEntry( - is("B"), - hasProperty("loreyHeightByUtilization", contains(zeroMatcher, heightMatcher)) - ) - ) - ); - } - - } - - @Test - void testVeteranLayerEquationGroups() throws Exception { - - var polygonId = polygonId("Test Polygon", 2023); - - var fipPolygon = getTestPolygon(polygonId, TestUtils.valid()); - var fipLayer = getTestVeteranLayer(polygonId, TestUtils.valid(), TestUtils.valid()); - var fipSpecies = getTestSpecies(polygonId, LayerType.VETERAN, x -> { - var map = new LinkedHashMap(); - map.put("B", 100f); - x.setSpeciesPercent(map); - }); - fipPolygon.setLayers(Collections.singletonMap(LayerType.VETERAN, fipLayer)); - fipLayer.setSpecies(Collections.singletonMap(fipSpecies.getGenus(), fipSpecies)); - - var controlMap = new HashMap(); - TestUtils.populateControlMapBecReal(controlMap); - TestUtils.populateControlMapGenusReal(controlMap); - TestUtils.populateControlMapVeteranBq(controlMap); - TestUtils.populateControlMapEquationGroups(controlMap, (s, b) -> { - if (s.equals("B") && b.equals("BG")) - return new int[] { 1, 2, 3 }; - if (s.equals("B") && b.equals("ESSF")) - return new int[] { 4, 5, 6 }; - return new int[] { 0, 0, 0 }; - }); - TestUtils.populateControlMapVeteranDq(controlMap, (s, r) -> new float[] { 0f, 0f, 0f }); - TestUtils.populateControlMapVeteranVolAdjust(controlMap, s -> new float[] { 0f, 0f, 0f, 0f }); - TestUtils.populateControlMapWholeStemVolume(controlMap, TestUtils.wholeStemMap(4)); - TestUtils.populateControlMapCloseUtilization(controlMap, TestUtils.closeUtilMap(4)); - TestUtils.populateControlMapNetDecay(controlMap, TestUtils.closeUtilMap(2)); - TestUtils.populateControlMapDecayModifiers(controlMap, (s, r) -> 0f); - TestUtils.populateControlMapNetWaste( - controlMap, s -> new Coefficients(new float[] { 0f, 0f, 0f, 0f, 0f, 0f }, 0) - ); - TestUtils.populateControlMapWasteModifiers(controlMap, (s, r) -> 0f); - TestUtils - .populateControlMapNetBreakage(controlMap, bgrp -> new Coefficients(new float[] { 0f, 0f, 0f, 0f }, 1)); - - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var result = app.processLayerAsVeteran(fipPolygon, fipLayer).getSpecies().get("B"); - - assertThat(result, hasProperty("volumeGroup", is(4))); // Remapped BEC to ESSF - assertThat(result, hasProperty("decayGroup", is(2))); - assertThat(result, hasProperty("breakageGroup", is(3))); - } - - } - - @Test - void testEstimateVeteranLayerDQ() throws Exception { - - var polygonId = polygonId("Test Polygon", 2023); - - var fipPolygon = getTestPolygon(polygonId, TestUtils.valid()); - var fipLayer = getTestVeteranLayer(polygonId, TestUtils.valid(), x -> { - x.height(10f); - }); - var fipSpecies1 = getTestSpecies(polygonId, LayerType.VETERAN, "B", x -> { - var map = new LinkedHashMap(); - map.put("S1", 75f); - map.put("S2", 25f); - x.setSpeciesPercent(map); - x.setPercentGenus(60f); - }); - var fipSpecies2 = getTestSpecies(polygonId, LayerType.VETERAN, "C", x -> { - var map = new LinkedHashMap(); - map.put("S3", 75f); - map.put("S4", 25f); - x.setSpeciesPercent(map); - x.setPercentGenus(40f); - }); - fipPolygon.setLayers(Collections.singletonMap(LayerType.VETERAN, fipLayer)); - var speciesMap = new HashMap(); - speciesMap.put("B", fipSpecies1); - speciesMap.put("C", fipSpecies2); - fipLayer.setSpecies(speciesMap); - - var controlMap = new HashMap(); - TestUtils.populateControlMapBecReal(controlMap); - TestUtils.populateControlMapGenusReal(controlMap); - TestUtils.populateControlMapEquationGroups(controlMap, (s, b) -> new int[] { 1, 1, 1 }); - TestUtils.populateControlMapVeteranBq(controlMap); - TestUtils.populateControlMapVeteranDq(controlMap, (s, r) -> { - if (s.equals("B") && r == Region.INTERIOR) - return new float[] { 19.417f, 0.04354f, 1.96395f }; - else if (s.equals("C") && r == Region.INTERIOR) - return new float[] { 22.500f, 0.00157f, 2.96382f }; - return new float[] { 0f, 0f, 0f }; - }); - TestUtils.populateControlMapVeteranVolAdjust(controlMap, s -> new float[] { 0f, 0f, 0f, 0f }); - TestUtils.populateControlMapWholeStemVolume(controlMap, (TestUtils.wholeStemMap(1))); - TestUtils.populateControlMapCloseUtilization(controlMap, TestUtils.closeUtilMap(1)); - TestUtils.populateControlMapNetDecay(controlMap, TestUtils.closeUtilMap(1)); - TestUtils.populateControlMapDecayModifiers(controlMap, (s, r) -> 0f); - TestUtils.populateControlMapNetWaste( - controlMap, s -> new Coefficients(new float[] { 0f, 0f, 0f, 0f, 0f, 0f }, 0) - ); - TestUtils.populateControlMapWasteModifiers(controlMap, (s, r) -> 0f); - TestUtils - .populateControlMapNetBreakage(controlMap, bgrp -> new Coefficients(new float[] { 0f, 0f, 0f, 0f }, 1)); - - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var result = app.processLayerAsVeteran(fipPolygon, fipLayer); - - Matcher zeroMatcher = is(0.0f); - // Expect the estimated DQ in 4 (-1 to 4) - - var expectedDqB = 19.417f + 0.04354f * (float) Math.pow(10f, 1.96395f); - var expectedDqC = 22.500f + 0.00157f * (float) Math.pow(10f, 2.96382); - - var resultB = result.getSpecies().get("B"); - - assertThat( - resultB, - hasProperty( - "quadraticMeanDiameterByUtilization", - contains( - zeroMatcher, closeTo(expectedDqB), zeroMatcher, zeroMatcher, zeroMatcher, - closeTo(expectedDqB) - ) - ) - ); - assertThat( - resultB, - hasProperty( - "treesPerHectareByUtilization", - contains( - zeroMatcher, closeTo(3.8092144f), zeroMatcher, zeroMatcher, zeroMatcher, - closeTo(3.8092144f) - ) - ) - ); - var resultC = result.getSpecies().get("C"); - assertThat( - resultC, - hasProperty( - "quadraticMeanDiameterByUtilization", - contains( - zeroMatcher, closeTo(expectedDqC), zeroMatcher, zeroMatcher, zeroMatcher, - closeTo(expectedDqC) - ) - ) - ); - assertThat( - resultC, - hasProperty( - "treesPerHectareByUtilization", - contains( - zeroMatcher, closeTo(2.430306f), zeroMatcher, zeroMatcher, zeroMatcher, - closeTo(2.430306f) - ) - ) - ); - } - } - -<<<<<<< HEAD -======= - static BiFunction> wholeStemMap(int group) { - return (u, g) -> { - if (g == group) { - switch (u) { - case 1: - return Optional.of( - new Coefficients(new float[] { -1.20775998f, 0.670000017f, 1.43023002f, -0.886789978f }, 0) - ); - case 2: - return Optional.of( - new Coefficients(new float[] { -1.58211005f, 0.677200019f, 1.36449003f, -0.781769991f }, 0) - ); - case 3: - return Optional.of( - new Coefficients(new float[] { -1.61995006f, 0.651030004f, 1.17782998f, -0.607379973f }, 0) - ); - case 4: - return Optional - .of( - new Coefficients( - new float[] { -0.172529995f, 0.932619989f, -0.0697899982f, - -0.00362000009f }, - 0 - ) - ); - default: - ; - } - } - return Optional.empty(); - }; - } - - static BiFunction> closeUtilMap(int group) { - return (u, g) -> { - if (g == group) { - switch (u) { - case 1: - return Optional.of(new Coefficients(new float[] { -10.6339998f, 0.835500002f, 0f }, 1)); - case 2: - return Optional.of(new Coefficients(new float[] { -4.44999981f, 0.373400003f, 0f }, 1)); - case 3: - return Optional.of(new Coefficients(new float[] { -0.796000004f, 0.141299993f, 0.0033499999f }, 1)); - case 4: - return Optional.of(new Coefficients(new float[] { 2.35400009f, 0.00419999985f, 0.0247699991f }, 1)); - default: - ; - } - } - return Optional.empty(); - }; - } - - static BiFunction> netDecayMap(int group) { - return (u, g) -> { - if (g == group) { - switch (u) { - case 1: - return Optional.of(new Coefficients(new float[] { 9.84819984f, -0.224209994f, -0.814949989f }, 1)); - case 2: - return Optional.of(new Coefficients(new float[] { 9.61330032f, -0.224209994f, -0.814949989f }, 1)); - case 3: - return Optional.of(new Coefficients(new float[] { 9.40579987f, -0.224209994f, -0.814949989f }, 1)); - case 4: - return Optional.of(new Coefficients(new float[] { 10.7090998f, -0.952880025f, -0.808309972f }, 1)); - default: - ; - } - } - return Optional.empty(); - }; - } - - @Test - void testEstimateVeteranWholeStemVolume() throws Exception { - var polygonId = polygonId("Test Polygon", 2023); - - var fipPolygon = getTestPolygon(polygonId, valid()); - var fipLayer = getTestVeteranLayer(polygonId, valid(), valid()); - var fipSpecies = getTestSpecies(polygonId, LayerType.VETERAN, valid()); - fipPolygon.setLayers(Collections.singletonMap(LayerType.VETERAN, fipLayer)); - fipLayer.setSpecies(Collections.singletonMap(fipSpecies.getGenus(), fipSpecies)); - - var controlMap = new HashMap(); - TestUtils.populateControlMapWholeStemVolume(controlMap, wholeStemMap(12)); - - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var utilizationClass = UtilizationClass.OVER225; - var aAdjust = 0.10881f; - var volumeGroup = 12; - var lorieHeight = 26.2000008f; - var quadMeanDiameterUtil = new Coefficients(new float[] { 51.8356705f, 0f, 0f, 0f, 51.8356705f }, 0); - var baseAreaUtil = new Coefficients(new float[] { 0.492921442f, 0f, 0f, 0f, 0.492921442f }, 0); - var wholeStemVolumeUtil = new Coefficients(new float[] { 0f, 0f, 0f, 0f, 0f }, 0); - - EstimationMethods.estimateWholeStemVolume( - controlMap, utilizationClass, aAdjust, volumeGroup, lorieHeight, quadMeanDiameterUtil, baseAreaUtil, - wholeStemVolumeUtil - ); - - assertThat(wholeStemVolumeUtil, coe(0, contains(is(0f), is(0f), is(0f), is(0f), closeTo(6.11904192f)))); - } - - } - - @Test - void testEstimateVeteranCloseUtilization() throws Exception { - var polygonId = polygonId("Test Polygon", 2023); - - var fipPolygon = getTestPolygon(polygonId, valid()); - var fipLayer = getTestVeteranLayer(polygonId, valid(), valid()); - var fipSpecies = getTestSpecies(polygonId, LayerType.VETERAN, valid()); - fipPolygon.setLayers(Collections.singletonMap(LayerType.VETERAN, fipLayer)); - fipLayer.setSpecies(Collections.singletonMap(fipSpecies.getGenus(), fipSpecies)); - - var controlMap = new HashMap(); - TestUtils.populateControlMapCloseUtilization(controlMap, closeUtilMap(12)); - - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var utilizationClass = UtilizationClass.OVER225; - var aAdjust = new Coefficients(new float[] { 0f, 0f, 0f, -0.0981800035f }, 1); - var volumeGroup = 12; - var lorieHeight = 26.2000008f; - var quadMeanDiameterUtil = new Coefficients(new float[] { 51.8356705f, 0f, 0f, 0f, 51.8356705f }, 0); - var wholeStemVolumeUtil = new Coefficients(new float[] { 0f, 0f, 0f, 0f, 6.11904192f }, 0); - - var closeUtilizationUtil = new Coefficients(new float[] { 0f, 0f, 0f, 0f, 0f }, 0); - - EstimationMethods.estimateCloseUtilizationVolume( - controlMap, utilizationClass, aAdjust, volumeGroup, lorieHeight, quadMeanDiameterUtil, - wholeStemVolumeUtil, closeUtilizationUtil - ); - - assertThat(closeUtilizationUtil, coe(0, contains(is(0f), is(0f), is(0f), is(0f), closeTo(5.86088896f)))); - } - - } - - @Test - void testEstimateVeteranNetDecay() throws Exception { - var polygonId = polygonId("Test Polygon", 2023); - - var fipPolygon = getTestPolygon(polygonId, valid()); - var fipLayer = getTestVeteranLayer(polygonId, valid(), valid()); - var fipSpecies = getTestSpecies(polygonId, LayerType.VETERAN, s -> { - }); - fipPolygon.setLayers(Collections.singletonMap(LayerType.VETERAN, fipLayer)); - fipLayer.setSpecies(Collections.singletonMap(fipSpecies.getGenus(), fipSpecies)); - - var controlMap = new HashMap(); - TestUtils.populateControlMapNetDecay(controlMap, netDecayMap(7)); - FipTestUtils.populateControlMapDecayModifiers( - controlMap, (s, r) -> s.equals("B") && r == Region.INTERIOR ? 0f : 0f - ); - - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var utilizationClass = UtilizationClass.OVER225; - var aAdjust = new Coefficients(new float[] { 0f, 0f, 0f, 0.000479999988f }, 1); - var decayGroup = 7; - var breastHeightAge = 97.9000015f; - var quadMeanDiameterUtil = new Coefficients(new float[] { 51.8356705f, 0f, 0f, 0f, 51.8356705f }, 0); - var closeUtilizationUtil = new Coefficients(new float[] { 0f, 0f, 0f, 0f, 5.86088896f }, 0); - - var closeUtilizationNetOfDecayUtil = new Coefficients(new float[] { 0f, 0f, 0f, 0f, 0f }, 0); - - EstimationMethods.estimateNetDecayVolume( - controlMap, fipSpecies.getGenus(), Region.INTERIOR, utilizationClass, aAdjust, decayGroup, - breastHeightAge, quadMeanDiameterUtil, closeUtilizationUtil, closeUtilizationNetOfDecayUtil - ); - - assertThat( - closeUtilizationNetOfDecayUtil, - coe(0, contains(is(0f), is(0f), is(0f), is(0f), closeTo(5.64048958f))) - ); - } - - } - - @Test - void testEstimateVeteranNetWaste() throws Exception { - var polygonId = polygonId("Test Polygon", 2023); - - var fipPolygon = getTestPolygon(polygonId, valid()); - var fipLayer = getTestVeteranLayer(polygonId, valid(), valid()); - var fipSpecies = getTestSpecies(polygonId, LayerType.VETERAN, s -> { - }); - fipPolygon.setLayers(Collections.singletonMap(LayerType.VETERAN, fipLayer)); - fipLayer.setSpecies(Collections.singletonMap(fipSpecies.getGenus(), fipSpecies)); - - var controlMap = new HashMap(); - TestUtils.populateControlMapNetWaste(controlMap, s -> s.equals("B") ? // - new Coefficients( - new float[] { -4.20249987f, 11.2235003f, -33.0270004f, 0.124600001f, -0.231800005f, -0.1259f }, - 0 - ) : // - new Coefficients(new float[] { 0f, 0f, 0f, 0f, 0f, 0f }, 0) - ); - FipTestUtils.populateControlMapWasteModifiers( - controlMap, (s, r) -> s.equals("B") && r == Region.INTERIOR ? 0f : 0f - ); - - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var utilizationClass = UtilizationClass.OVER225; - var aAdjust = new Coefficients(new float[] { 0f, 0f, 0f, -0.00295000011f }, 1); - var lorieHeight = 26.2000008f; - var quadMeanDiameterUtil = new Coefficients(new float[] { 51.8356705f, 0f, 0f, 0f, 51.8356705f }, 0); - var closeUtilizationUtil = new Coefficients(new float[] { 0f, 0f, 0f, 0f, 5.86088896f }, 0); - var closeUtilizationNetOfDecayUtil = new Coefficients(new float[] { 0f, 0f, 0f, 0f, 5.64048958f }, 0); - - var closeUtilizationNetOfDecayAndWasteUtil = new Coefficients(new float[] { 0f, 0f, 0f, 0f, 0f }, 0); - - final var netDecayCoeMap = Utils.>expectParsedControl( - controlMap, ControlKey.VOLUME_NET_DECAY_WASTE, Map.class - ); - final var wasteModifierMap = Utils.>expectParsedControl( - controlMap, ControlKey.WASTE_MODIFIERS, MatrixMap2.class - ); - - EstimationMethods.estimateNetDecayAndWasteVolume( - Region.INTERIOR, utilizationClass, aAdjust, fipSpecies.getGenus(), lorieHeight, netDecayCoeMap, - wasteModifierMap, quadMeanDiameterUtil, closeUtilizationUtil, closeUtilizationNetOfDecayUtil, - closeUtilizationNetOfDecayAndWasteUtil - ); - - assertThat( - closeUtilizationNetOfDecayAndWasteUtil, - coe(0, contains(is(0f), is(0f), is(0f), is(0f), closeTo(5.57935333f))) - ); - } - - } - - @Test - void testEstimateVeteranNetBreakage() throws Exception { - var polygonId = polygonId("Test Polygon", 2023); - - var fipPolygon = getTestPolygon(polygonId, valid()); - var fipLayer = getTestVeteranLayer(polygonId, valid(), valid()); - var fipSpecies = getTestSpecies(polygonId, LayerType.VETERAN, s -> { - }); - fipPolygon.setLayers(Collections.singletonMap(LayerType.VETERAN, fipLayer)); - fipLayer.setSpecies(Collections.singletonMap(fipSpecies.getGenus(), fipSpecies)); - - var controlMap = new HashMap(); - TestUtils.populateControlMapNetBreakage(controlMap, bgrp -> bgrp == 5 ? // - new Coefficients(new float[] { 2.2269001f, 0.75059998f, 4f, 6f }, 1) : // - new Coefficients(new float[] { 0f, 0f, 0f, 0f }, 1) - ); - - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var utilizationClass = UtilizationClass.OVER225; - var breakageGroup = 5; - var quadMeanDiameterUtil = new Coefficients(new float[] { 51.8356705f, 0f, 0f, 0f, 51.8356705f }, 0); - var closeUtilizationUtil = new Coefficients(new float[] { 0f, 0f, 0f, 0f, 5.86088896f }, 0); - var closeUtilizationNetOfDecayAndWasteUtil = new Coefficients( - new float[] { 0f, 0f, 0f, 0f, 5.57935333f }, 0 - ); - - var closeUtilizationNetOfDecayWasteAndBreakageUtil = new Coefficients( - new float[] { 0f, 0f, 0f, 0f, 0f }, 0 - ); - - EstimationMethods.estimateNetDecayWasteAndBreakageVolume( - controlMap, utilizationClass, breakageGroup, quadMeanDiameterUtil, closeUtilizationUtil, - closeUtilizationNetOfDecayAndWasteUtil, closeUtilizationNetOfDecayWasteAndBreakageUtil - ); - - assertThat( - closeUtilizationNetOfDecayWasteAndBreakageUtil, - coe(0, contains(is(0f), is(0f), is(0f), is(0f), closeTo(5.27515411f))) - ); - } - - } - - @Test - void testEstimatePrimaryNetBreakage() throws Exception { - var controlMap = FipTestUtils.loadControlMap(); - - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var utilizationClass = UtilizationClass.ALL; - var breakageGroup = 20; - 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 = Utils.utilizationVector(); - - EstimationMethods.estimateNetDecayWasteAndBreakageVolume( - controlMap, utilizationClass, breakageGroup, quadMeanDiameterUtil, closeUtilizationUtil, - closeUtilizationNetOfDecayAndWasteUtil, closeUtilizationNetOfDecayWasteAndBreakageUtil - ); - - assertThat( - closeUtilizationNetOfDecayWasteAndBreakageUtil, - utilization(0f, 5.989573f, 0.0337106399f, 2.84590816f, 2.10230994f, 1.00764418f) - ); - } - - } - ->>>>>>> origin/feature/VDYP-362-compatibility-variables - @Test - void testProcessAsVeteranLayer() throws Exception { - - var polygonId = PolygonIdentifier.split("01002 S000002 00 1970"); - - var fipPolygon = getTestPolygon(polygonId, x -> { - x.setBiogeoclimaticZone("CWH"); - x.setForestInventoryZone("A"); - x.setYieldFactor(1f); - }); - - var fipLayer = getTestVeteranLayer(polygonId, x -> { - ((FipLayer.Builder) x).crownClosure(4.0f); - }, x -> { - x.ageTotal(105f); - x.height(26.2f); - x.siteIndex(16.7f); - x.siteGenus("H"); - x.siteSpecies("H"); - x.yearsToBreastHeight(7.1f); - }); - var fipSpecies1 = getTestSpecies(polygonId, LayerType.VETERAN, "B", x -> { - var map = new LinkedHashMap(); - x.setSpeciesPercent(map); - x.setPercentGenus(22f); - }); - var fipSpecies2 = getTestSpecies(polygonId, LayerType.VETERAN, "H", x -> { - var map = new LinkedHashMap(); - x.setSpeciesPercent(map); - x.setPercentGenus(60f); - }); - var fipSpecies3 = getTestSpecies(polygonId, LayerType.VETERAN, "S", x -> { - var map = new LinkedHashMap(); - x.setSpeciesPercent(map); - x.setPercentGenus(18f); - }); - fipPolygon.setLayers(Collections.singletonMap(LayerType.VETERAN, fipLayer)); - var speciesMap = new HashMap(); - speciesMap.put("B", fipSpecies1); - speciesMap.put("H", fipSpecies2); - speciesMap.put("S", fipSpecies3); - fipLayer.setSpecies(speciesMap); - - var controlMap = FipTestUtils.loadControlMap(); - - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var result = app.processLayerAsVeteran(fipPolygon, fipLayer); - - assertThat(result, hasProperty("polygonIdentifier", is(polygonId))); - assertThat(result, hasProperty("layerType", is(LayerType.VETERAN))); - - 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(); - - assertThat(resultSpeciesMap, Matchers.hasKey("B")); - assertThat(resultSpeciesMap, Matchers.hasKey("H")); - assertThat(resultSpeciesMap, Matchers.hasKey("S")); - - var resultSpeciesB = resultSpeciesMap.get("B"); - var resultSpeciesH = resultSpeciesMap.get("H"); - var resultSpeciesS = resultSpeciesMap.get("S"); - - assertThat(resultSpeciesB, hasProperty("genus", is("B"))); - assertThat(resultSpeciesH, hasProperty("genus", is("H"))); - assertThat(resultSpeciesS, hasProperty("genus", is("S"))); - - vetUtilization("baseAreaByUtilization", matchGenerator -> { - assertThat(result, matchGenerator.apply(2.24055195f)); - assertThat(result.getSpecies().get("B"), matchGenerator.apply(0.492921442f)); - assertThat(result.getSpecies().get("H"), matchGenerator.apply(1.34433115f)); - assertThat(result.getSpecies().get("S"), matchGenerator.apply(0.403299361f)); - }); - vetUtilization("quadraticMeanDiameterByUtilization", matchGenerator -> { - assertThat(result, matchGenerator.apply(51.6946983f)); - assertThat(result.getSpecies().get("B"), matchGenerator.apply(51.8356705f)); - assertThat(result.getSpecies().get("H"), matchGenerator.apply(53.6141243f)); - assertThat(result.getSpecies().get("S"), matchGenerator.apply(46.4037895f)); - }); - vetUtilization("treesPerHectareByUtilization", matchGenerator -> { - assertThat(result, matchGenerator.apply(10.6751289f)); - assertThat(result.getSpecies().get("B"), matchGenerator.apply(2.3357718f)); - assertThat(result.getSpecies().get("H"), matchGenerator.apply(5.95467329f)); - assertThat(result.getSpecies().get("S"), matchGenerator.apply(2.38468361f)); - }); - vetUtilization("wholeStemVolumeByUtilization", matchGenerator -> { - assertThat(result.getSpecies().get("B"), matchGenerator.apply(6.11904192f)); - assertThat(result.getSpecies().get("H"), matchGenerator.apply(14.5863571f)); - assertThat(result.getSpecies().get("S"), matchGenerator.apply(4.04864883f)); - assertThat(result, matchGenerator.apply(24.7540474f)); - }); - vetUtilization("closeUtilizationVolumeByUtilization", matchGenerator -> { - assertThat(result, matchGenerator.apply(23.6066074f)); - assertThat(result.getSpecies().get("B"), matchGenerator.apply(5.86088896f)); - assertThat(result.getSpecies().get("H"), matchGenerator.apply(13.9343023f)); - assertThat(result.getSpecies().get("S"), matchGenerator.apply(3.81141663f)); - }); - vetUtilization("closeUtilizationVolumeNetOfDecayByUtilization", matchGenerator -> { - assertThat(result, matchGenerator.apply(22.7740307f)); - assertThat(result.getSpecies().get("B"), matchGenerator.apply(5.64048958f)); - assertThat(result.getSpecies().get("H"), matchGenerator.apply(13.3831034f)); - assertThat(result.getSpecies().get("S"), matchGenerator.apply(3.75043678f)); - }); - vetUtilization("closeUtilizationVolumeNetOfDecayAndWasteByUtilization", matchGenerator -> { - assertThat(result, matchGenerator.apply(22.5123749f)); - assertThat(result.getSpecies().get("B"), matchGenerator.apply(5.57935333f)); - assertThat(result.getSpecies().get("H"), matchGenerator.apply(13.2065458f)); - assertThat(result.getSpecies().get("S"), matchGenerator.apply(3.72647476f)); - }); - vetUtilization("closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", matchGenerator -> { - assertThat(result, matchGenerator.apply(21.3272057f)); - assertThat(result.getSpecies().get("B"), matchGenerator.apply(5.27515411f)); - assertThat(result.getSpecies().get("H"), matchGenerator.apply(12.4877129f)); - assertThat(result.getSpecies().get("S"), matchGenerator.apply(3.56433797f)); - }); - } - - } - - void vetUtilization(String property, Consumer>> body) { - Function> generator = v -> hasProperty( - property, coe(-1, contains(is(0f), closeTo(v), is(0f), is(0f), is(0f), closeTo(v))) - ); - body.accept(generator); - } - - @Test - void testFindRootsForPrimaryLayerDiameterAndAreaOneSpecies() throws Exception { - var controlMap = FipTestUtils.loadControlMap(); - var polygonId = new PolygonIdentifier("TestPolygon", 2024); - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var becLookup = BecDefinitionParser.getBecs(controlMap); - var bec = becLookup.get("CWH").get(); - - var layer = VdypLayer.build(builder -> { - builder.polygonIdentifier(polygonId); - builder.layerType(LayerType.PRIMARY); - builder.addSite(siteBuilder -> { - siteBuilder.ageTotal(285f); - siteBuilder.yearsToBreastHeight(11.3999996f); - siteBuilder.height(24.3999996f); - siteBuilder.siteGenus("Y"); - }); - }); - layer.getBaseAreaByUtilization().setCoe(0, 76.5122147f); - layer.getTreesPerHectareByUtilization().setCoe(0, 845.805969f); - layer.getQuadraticMeanDiameterByUtilization().setCoe(0, 33.9379082f); - - var spec = VdypSpecies.build(layer, builder -> { - builder.genus("Y"); - builder.percentGenus(100f); - builder.volumeGroup(-1); - builder.decayGroup(-1); - builder.breakageGroup(-1); - }); - spec.setVolumeGroup(74); - spec.setDecayGroup(63); - spec.setBreakageGroup(31); - spec.getLoreyHeightByUtilization().setCoe(0, 19.9850883f); - - var fipLayer = this.getTestPrimaryLayer(polygonId, l -> { - l.inventoryTypeGroup(Optional.of(9)); - ((PrimaryBuilder) l).primaryGenus(Optional.of("Y")); - }, TestUtils.valid()); - - app.findRootsForDiameterAndBaseArea(layer, fipLayer, bec, 2); - - assertThat( - layer, hasProperty( - "loreyHeightByUtilization", // - coe(-1, 0f, 19.9850883f) - ) - ); - assertThat( - spec, hasProperty( - "baseAreaByUtilization", // - coe(-1, 0f, 76.5122147f, 0f, 0f, 0f, 0f) - ) - ); - assertThat( - spec, hasProperty( - "treesPerHectareByUtilization", // - coe(-1, 0f, 845.805969f, 0f, 0f, 0f, 0f) - ) - ); - assertThat( - spec, hasProperty( - "quadraticMeanDiameterByUtilization", // - coe(-1, 0f, 33.9379082f, 0f, 0f, 0f, 0f) - ) - ); - - assertThat( - layer, hasProperty( - "wholeStemVolumeByUtilization", // - coe(-1, 0f, 571.22583f, 0f, 0f, 0f, 0f) - ) - ); - assertThat( - spec, hasProperty( - "wholeStemVolumeByUtilization", // - coe(-1, 0f, 571.22583f, 0f, 0f, 0f, 0f) - ) - ); - } - - } - - @Test - void testFindRootsForPrimaryLayerDiameterAndAreaMultipleSpeciesPass1() throws Exception { - var controlMap = FipTestUtils.loadControlMap(); - var polygonId = new PolygonIdentifier("TestPolygon", 2024); - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var becLookup = BecDefinitionParser.getBecs(controlMap); - var bec = becLookup.get("CWH").get(); - - var layer = VdypLayer.build(builder -> { - builder.polygonIdentifier(polygonId); - builder.layerType(LayerType.PRIMARY); - builder.addSite(siteBuilder -> { - siteBuilder.ageTotal(55f); - siteBuilder.yearsToBreastHeight(1f); - siteBuilder.height(35.2999992f); - siteBuilder.siteGenus("B"); - }); - }); - layer.getBaseAreaByUtilization().setCoe(0, 44.6249847f); - layer.getTreesPerHectareByUtilization().setCoe(0, 620.504883f); - layer.getQuadraticMeanDiameterByUtilization().setCoe(0, 30.2601795f); - - /* - * HL[*, -1] 0 HL[0, 0] 0 BA[*, -1] BA[1, 0] VOLWS VOLCU VOL_D VOL_DW VOLDWB dqspbase,goal - * - * HL[1, 0] spec BA[0, 0] layer TPH[0, 0] layer DQ[0,0] layer INL1VGRP, INL1DGRP, INL1BGRP spec VGRPL, - * DGRPL, BGRPL spec Same as above AGETOTL1 layer AGEBHL1 layer YTBH hdl1 - * - */ - // sp 3, 4, 5, 8, 15 - // sp B, C, D, H, S - var spec1 = VdypSpecies.build(layer, builder -> { - builder.genus("B"); - builder.percentGenus(1f); - builder.volumeGroup(12); - builder.decayGroup(7); - builder.breakageGroup(5); - }); - spec1.getLoreyHeightByUtilization().setCoe(0, 38.7456512f); - var spec2 = VdypSpecies.build(layer, builder -> { - builder.genus("C"); - builder.percentGenus(7f); - builder.volumeGroup(20); - builder.decayGroup(14); - builder.breakageGroup(6); - }); - - spec2.getLoreyHeightByUtilization().setCoe(0, 22.8001652f); - var spec3 = VdypSpecies.build(layer, builder -> { - builder.genus("D"); - builder.percentGenus(74f); - builder.volumeGroup(25); - builder.decayGroup(19); - builder.breakageGroup(12); - }); - spec3.getLoreyHeightByUtilization().setCoe(0, 33.6889763f); - var spec4 = VdypSpecies.build(layer, builder -> { - builder.genus("H"); - builder.percentGenus(9f); - builder.volumeGroup(37); - builder.decayGroup(31); - builder.breakageGroup(17); - }); - spec4.getLoreyHeightByUtilization().setCoe(0, 24.3451157f); - var spec5 = VdypSpecies.build(layer, builder -> { - builder.genus("S"); - builder.percentGenus(9f); - builder.volumeGroup(66); - builder.decayGroup(54); - builder.breakageGroup(28); - }); - spec5.getLoreyHeightByUtilization().setCoe(0, 34.6888771f); - - Collection specs = new ArrayList<>(5); - specs.add(spec1); - specs.add(spec2); - specs.add(spec3); - specs.add(spec4); - specs.add(spec5); - - layer.setSpecies(specs); - - var fipLayer = this.getTestPrimaryLayer(polygonId, l -> { - l.inventoryTypeGroup(Optional.of(9)); - ((PrimaryBuilder) l).primaryGenus(Optional.of("H")); - }, TestUtils.valid()); - - app.findRootsForDiameterAndBaseArea(layer, fipLayer, bec, 2); - - assertThat( - layer, hasProperty( - "loreyHeightByUtilization", // - coe(-1, 0f, 31.4222546f) - ) - ); - assertThat( - spec1, hasProperty( - "loreyHeightByUtilization", // - coe(-1, 0f, 38.7456512f) - ) - ); - assertThat( - spec2, hasProperty( - "loreyHeightByUtilization", // - coe(-1, 0f, 22.8001652f) - ) - ); - assertThat( - spec3, hasProperty( - "loreyHeightByUtilization", // - coe(-1, 0f, 33.6889763f) - ) - ); - assertThat( - spec4, hasProperty( - "loreyHeightByUtilization", // - coe(-1, 0f, 24.3451157f) - ) - ); - assertThat( - spec5, hasProperty( - "loreyHeightByUtilization", // - coe(-1, 0f, 34.6888771f) - ) - ); - - assertThat( - layer, hasProperty( - "baseAreaByUtilization", // - coe(-1, 0f, 44.6249847f, 0f, 0f, 0f, 0f) - ) - ); - assertThat( - spec1, hasProperty( - "baseAreaByUtilization", // - coe(-1, 0f, 0.398000091f, 0f, 0f, 0f, 0f) - ) - ); - assertThat( - spec2, hasProperty( - "baseAreaByUtilization", // - coe(-1, 0f, 5.10918713f, 0f, 0f, 0f, 0f) - ) - ); - assertThat( - spec3, hasProperty( - "baseAreaByUtilization", // - coe(-1, 0f, 29.478117f, 0f, 0f, 0f, 0f) - ) - ); - assertThat( - spec4, hasProperty( - "baseAreaByUtilization", // - coe(-1, 0f, 5.52707148f, 0f, 0f, 0f, 0f) - ) - ); - assertThat( - spec5, hasProperty( - "baseAreaByUtilization", // - coe(-1, 0f, 4.11260939f, 0f, 0f, 0f, 0f) - ) - ); - - assertThat( - layer, hasProperty( - "treesPerHectareByUtilization", // - coe(-1, 0f, 620.497803f, 0f, 0f, 0f, 0f) - ) - ); - assertThat( - spec1, hasProperty( - "treesPerHectareByUtilization", // - coe(-1, 0f, 5.04042435f, 0f, 0f, 0f, 0f) - ) - ); - assertThat( - spec2, hasProperty( - "treesPerHectareByUtilization", // - coe(-1, 0f, 92.9547882f, 0f, 0f, 0f, 0f) - ) - ); - assertThat( - spec3, hasProperty( - "treesPerHectareByUtilization", // - coe(-1, 0f, 325.183502f, 0f, 0f, 0f, 0f) - ) - ); - assertThat( - spec4, hasProperty( - "treesPerHectareByUtilization", // - coe(-1, 0f, 153.230591f, 0f, 0f, 0f, 0f) - ) - ); - assertThat( - spec5, hasProperty( - "treesPerHectareByUtilization", // - coe(-1, 0f, 44.0884819f, 0f, 0f, 0f, 0f) - ) - ); - - assertThat( - layer, hasProperty( - "quadraticMeanDiameterByUtilization", // - coe(-1, 0f, 30.2603531f, 0f, 0f, 0f, 0f) - ) - ); - assertThat( - spec1, hasProperty( - "quadraticMeanDiameterByUtilization", // - coe(-1, 0f, 31.7075806f, 0f, 0f, 0f, 0f) - ) - ); - assertThat( - spec2, hasProperty( - "quadraticMeanDiameterByUtilization", // - coe(-1, 0f, 26.4542274f, 0f, 0f, 0f, 0f) - ) - ); - assertThat( - spec3, hasProperty( - "quadraticMeanDiameterByUtilization", // - coe(-1, 0f, 33.9735298f, 0f, 0f, 0f, 0f) - ) - ); - assertThat( - spec4, hasProperty( - "quadraticMeanDiameterByUtilization", // - coe(-1, 0f, 21.4303799f, 0f, 0f, 0f, 0f) - ) - ); - assertThat( - spec5, hasProperty( - "quadraticMeanDiameterByUtilization", // - coe(-1, 0f, 34.4628525f, 0f, 0f, 0f, 0f) - ) - ); - - assertThat( - layer, hasProperty( - "wholeStemVolumeByUtilization", // - coe(-1, 0f, 638.572754f, 0f, 0f, 0f, 0f) - ) - ); - assertThat( - spec1, hasProperty( - "wholeStemVolumeByUtilization", // - coe(-1, 0f, 6.38573837f, 0f, 0f, 0f, 0f) - ) - ); - assertThat( - spec2, hasProperty( - "wholeStemVolumeByUtilization", // - coe(-1, 0f, 44.7000046f, 0f, 0f, 0f, 0f) - ) - ); - assertThat( - spec3, hasProperty( - "wholeStemVolumeByUtilization", // - coe(-1, 0f, 472.54422f, 0f, 0f, 0f, 0f) - ) - ); - assertThat( - spec4, hasProperty( - "wholeStemVolumeByUtilization", // - coe(-1, 0f, 57.471405f, 0f, 0f, 0f, 0f) - ) - ); - assertThat( - spec5, hasProperty( - "wholeStemVolumeByUtilization", // - coe(-1, 0f, 57.4714355f, 0f, 0f, 0f, 0f) - ) - ); - } - - } - - @Test - void testFindRootsForPrimaryLayerDiameterAndAreaMultipleSpeciesPass1Test2() throws Exception { - var controlMap = FipTestUtils.loadControlMap(); - var polygonId = new PolygonIdentifier("TestPolygon", 2024); - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var becLookup = BecDefinitionParser.getBecs(controlMap); - var bec = becLookup.get("CWH").get(); - - var layer = VdypLayer.build(builder -> { - builder.polygonIdentifier(polygonId); - builder.layerType(LayerType.PRIMARY); - builder.addSite(siteBuilder -> { - siteBuilder.ageTotal(45f); - siteBuilder.yearsToBreastHeight(5.4000001f); - siteBuilder.height(24.2999992f); - siteBuilder.siteGenus("H"); - }); - }); - - layer.getBaseAreaByUtilization().setCoe(0, 44.9531403f); - layer.getTreesPerHectareByUtilization().setCoe(0, 1291.11597f); - layer.getQuadraticMeanDiameterByUtilization().setCoe(0, 21.0548649f); - - /* - * HL[*, -1] 0 HL[0, 0] 0 BA[*, -1] BA[1, 0] VOLWS VOLCU VOL_D VOL_DW VOLDWB dqspbase,goal - * - * HL[1, 0] spec BA[0, 0] layer TPH[0, 0] layer DQ[0,0] layer INL1VGRP, INL1DGRP, INL1BGRP spec VGRPL, - * DGRPL, BGRPL spec Same as above AGETOTL1 layer AGEBHL1 layer YTBH hdl1 - * - */ - // sp 3, 4, 5, 8, 15 - // sp B, C, D, H, S - var spec1 = VdypSpecies.build(layer, builder -> { - builder.genus("B"); - builder.percentGenus(15f); - builder.volumeGroup(12); - builder.decayGroup(7); - builder.breakageGroup(5); - }); - spec1.getLoreyHeightByUtilization().setCoe(0, 21.5356998f); - var spec2 = VdypSpecies.build(layer, builder -> { - builder.genus("D"); - builder.percentGenus(7f); - builder.volumeGroup(25); - builder.decayGroup(19); - builder.breakageGroup(12); - }); - spec2.getLoreyHeightByUtilization().setCoe(0, 22.4329224f); - var spec3 = VdypSpecies.build(layer, builder -> { - builder.genus("H"); - builder.percentGenus(77f); - builder.volumeGroup(37); - builder.decayGroup(54); - builder.breakageGroup(28); - }); - spec3.getLoreyHeightByUtilization().setCoe(0, 20.5984688f); - var spec4 = VdypSpecies.build(layer, builder -> { - builder.genus("S"); - builder.percentGenus(1f); - builder.volumeGroup(66); - builder.decayGroup(54); - builder.breakageGroup(28); - }); - spec4.getLoreyHeightByUtilization().setCoe(0, 24.0494442f); - - var fipLayer = this.getTestPrimaryLayer(polygonId, l -> { - l.inventoryTypeGroup(Optional.of(15)); - ((PrimaryBuilder) l).primaryGenus(Optional.of("H")); - }, TestUtils.valid()); - - app.findRootsForDiameterAndBaseArea(layer, fipLayer, bec, 2); - - // This is L1COM1/PCTL1 not FIPS/PCTVOLV - assertThat( - spec1, hasProperty( - "percentGenus", // - closeTo(13.4858189f) // Change - ) - ); - assertThat( - spec2, hasProperty( - "percentGenus", // - closeTo(6.56449223f) // Change - ) - ); - assertThat( - spec3, hasProperty( - "percentGenus", // - closeTo(79.0027771f) // Change - ) - ); - assertThat( - spec4, hasProperty( - "percentGenus", // - closeTo(0.946914673f) // Change - ) - ); - - assertThat( - layer, hasProperty( - "loreyHeightByUtilization", // - coe(-1, 0f, 20.8779621f) // Changed - ) - ); - assertThat( - spec1, hasProperty( - "loreyHeightByUtilization", // - coe(-1, 0f, 21.5356998f) // No Change - ) - ); - assertThat( - spec2, hasProperty( - "loreyHeightByUtilization", // - coe(-1, 0f, 22.4329224f) // No Change - ) - ); - assertThat( - spec3, hasProperty( - "loreyHeightByUtilization", // - coe(-1, 0f, 20.5984688f) // No Change - ) - ); - assertThat( - spec4, hasProperty( - "loreyHeightByUtilization", // - coe(-1, 0f, 24.0494442f) // No Change - ) - ); - - assertThat( - layer, hasProperty( - "baseAreaByUtilization", // - coe(-1, 0f, 44.9531403f, 0f, 0f, 0f, 0f) // No Change - ) - ); - assertThat( - spec1, hasProperty( - "baseAreaByUtilization", // - coe(-1, 0f, 6.06229925f, 0f, 0f, 0f, 0f) // Change - ) - ); - assertThat( - spec2, hasProperty( - "baseAreaByUtilization", // - coe(-1, 0f, 2.95094538f, 0f, 0f, 0f, 0f) // Change - ) - ); - assertThat( - spec3, hasProperty( - "baseAreaByUtilization", // - coe(-1, 0f, 35.5142288f, 0f, 0f, 0f, 0f) // Change - ) - ); - assertThat( - spec4, hasProperty( - "baseAreaByUtilization", // - coe(-1, 0f, 0.425667882f, 0f, 0f, 0f, 0f) // Change - ) - ); - - assertThat( - layer, hasProperty( - "treesPerHectareByUtilization", // - coe(-1, 0f, 1291.11621f, 0f, 0f, 0f, 0f) // Change - ) - ); - assertThat( - spec1, hasProperty( - "treesPerHectareByUtilization", // - coe(-1, 0f, 175.253494f, 0f, 0f, 0f, 0f) // Change - ) - ); - assertThat( - spec2, hasProperty( - "treesPerHectareByUtilization", // - coe(-1, 0f, 52.7488708f, 0f, 0f, 0f, 0f) // Change - ) - ); - assertThat( - spec3, hasProperty( - "treesPerHectareByUtilization", // - coe(-1, 0f, 1056.43982f, 0f, 0f, 0f, 0f) // Change - ) - ); - assertThat( - spec4, hasProperty( - "treesPerHectareByUtilization", // - coe(-1, 0f, 6.67403078f, 0f, 0f, 0f, 0f) // Change - ) - ); - - assertThat( - layer, hasProperty( - "quadraticMeanDiameterByUtilization", // - coe(-1, 0f, 21.054863f, 0f, 0f, 0f, 0f) // No Change - ) - ); - assertThat( - spec1, hasProperty( - "quadraticMeanDiameterByUtilization", // - coe(-1, 0f, 20.9865189f, 0f, 0f, 0f, 0f) // Change - ) - ); - assertThat( - spec2, hasProperty( - "quadraticMeanDiameterByUtilization", // - coe(-1, 0f, 26.6888008f, 0f, 0f, 0f, 0f) // Change - ) - ); - assertThat( - spec3, hasProperty( - "quadraticMeanDiameterByUtilization", // - coe(-1, 0f, 20.6887321f, 0f, 0f, 0f, 0f) // Change - ) - ); - assertThat( - spec4, hasProperty( - "quadraticMeanDiameterByUtilization", // - coe(-1, 0f, 28.4968204f, 0f, 0f, 0f, 0f) // Change - ) - ); - - assertThat( - layer, hasProperty( - "wholeStemVolumeByUtilization", // - coe(-1, 0f, 393.150146f, 0f, 0f, 0f, 0f) // No Change - ) - ); - assertThat( - spec1, hasProperty( - "wholeStemVolumeByUtilization", // - coe(-1, 0f, 58.9725418f, 0f, 0f, 0f, 0f) // No Change - ) - ); - assertThat( - spec2, hasProperty( - "wholeStemVolumeByUtilization", // - coe(-1, 0f, 27.5205307f, 0f, 0f, 0f, 0f) // No Change - ) - ); - assertThat( - spec3, hasProperty( - "wholeStemVolumeByUtilization", // - coe(-1, 0f, 302.725555f, 0f, 0f, 0f, 0f) // No Change - ) - ); - assertThat( - spec4, hasProperty( - "wholeStemVolumeByUtilization", // - coe(-1, 0f, 3.93152428f, 0f, 0f, 0f, 0f) // No Change - ) - ); - } - - } - - @Test -<<<<<<< HEAD -======= - void testEstimateQuadMeanDiameterForSpecies() throws Exception { - var controlMap = FipTestUtils.loadControlMap(); - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var layer = VdypLayer.build(builder -> { - builder.polygonIdentifier("Test", 2024); - builder.layerType(LayerType.PRIMARY); - builder.addSite(siteBuilder -> { - siteBuilder.ageTotal(55f); - siteBuilder.yearsToBreastHeight(1f); - siteBuilder.height(32.2999992f); - siteBuilder.siteGenus("H"); - }); - }); - - // sp 3, 4, 5, 8, 15 - // sp B, C, D, H, S - var spec1 = VdypSpecies.build(layer, builder -> { - builder.genus("B"); - builder.volumeGroup(12); - builder.decayGroup(7); - builder.breakageGroup(5); - builder.percentGenus(1f); - }); - spec1.getLoreyHeightByUtilization().setCoe(0, 38.7456512f); - spec1.setFractionGenus(0.00817133673f); - - var spec2 = VdypSpecies.build(layer, builder -> { - builder.genus("C"); - builder.volumeGroup(20); - builder.decayGroup(14); - builder.breakageGroup(6); - builder.percentGenus(7f); - }); - spec2.getLoreyHeightByUtilization().setCoe(0, 22.8001652f); - spec2.setFractionGenus(0.0972022042f); - - var spec3 = VdypSpecies.build(layer, builder -> { - builder.genus("D"); - builder.volumeGroup(25); - builder.decayGroup(19); - builder.breakageGroup(12); - builder.percentGenus(74f); - }); - spec3.getLoreyHeightByUtilization().setCoe(0, 33.6889763f); - spec3.setFractionGenus(0.695440531f); - - var spec4 = VdypSpecies.build(layer, builder -> { - builder.genus("H"); - builder.volumeGroup(37); - builder.decayGroup(31); - builder.breakageGroup(17); - builder.percentGenus(9f); - }); - spec4.getLoreyHeightByUtilization().setCoe(0, 24.3451157f); - spec4.setFractionGenus(0.117043354f); - - var spec5 = VdypSpecies.build(layer, builder -> { - builder.genus("S"); - builder.volumeGroup(66); - builder.decayGroup(54); - builder.breakageGroup(28); - builder.percentGenus(9f); - }); - spec5.getLoreyHeightByUtilization().setCoe(0, 34.6888771f); - spec5.setFractionGenus(0.082142584f); - - Map specs = new HashMap<>(); - specs.put(spec1.getGenus(), spec1); - specs.put(spec2.getGenus(), spec2); - specs.put(spec3.getGenus(), spec3); - specs.put(spec4.getGenus(), spec4); - specs.put(spec5.getGenus(), spec5); - - float dq = app.estimateQuadMeanDiameterForSpecies( - spec1, specs, Region.COASTAL, 30.2601795f, 44.6249847f, 620.504883f, 31.6603775f - ); - - assertThat(dq, closeTo(31.7022133f)); - } - } - - @Test - void testEstimateSmallComponents() throws ProcessingException { - var controlMap = FipTestUtils.loadControlMap(); - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var fPoly = FipPolygon.build(builder -> { - builder.polygonIdentifier("Test", 2024); - builder.forestInventoryZone("A"); - builder.biogeoclimaticZone("CWH"); - builder.yieldFactor(1f); - }); - VdypLayer layer = VdypLayer.build(builder -> { - builder.polygonIdentifier("Test", 2024); - builder.layerType(LayerType.PRIMARY); - builder.addSite(siteBuilder -> { - siteBuilder.ageTotal(55f); - siteBuilder.yearsToBreastHeight(1f); - siteBuilder.height(31f); - siteBuilder.siteGenus("H"); - }); - }); - - layer.getLoreyHeightByUtilization().setCoe(FipStart.UTIL_ALL, 31.3307209f); - layer.getBaseAreaByUtilization().setCoe(FipStart.UTIL_ALL, 44.6249847f); - layer.getTreesPerHectareByUtilization().setCoe(FipStart.UTIL_ALL, 620.484802f); - layer.getQuadraticMeanDiameterByUtilization().setCoe(FipStart.UTIL_ALL, 30.2606697f); - layer.getWholeStemVolumeByUtilization().setCoe(FipStart.UTIL_ALL, 635.659668f); - - var spec1 = VdypSpecies.build(layer, builder -> { - builder.genus("B"); - builder.percentGenus(20f); - builder.volumeGroup(-1); - builder.decayGroup(-1); - builder.breakageGroup(-1); - }); - spec1.getLoreyHeightByUtilization().setCoe(FipStart.UTIL_ALL, 38.6004372f); - spec1.getBaseAreaByUtilization().setCoe(FipStart.UTIL_ALL, 0.397305071f); - spec1.getTreesPerHectareByUtilization().setCoe(FipStart.UTIL_ALL, 5.04602766f); - spec1.getQuadraticMeanDiameterByUtilization().setCoe(FipStart.UTIL_ALL, 31.6622887f); - spec1.getWholeStemVolumeByUtilization().setCoe(FipStart.UTIL_ALL, 635.659668f); - var spec2 = VdypSpecies.build(layer, builder -> { - builder.genus("C"); - builder.percentGenus(20f); - builder.volumeGroup(-1); - builder.decayGroup(-1); - builder.breakageGroup(-1); - }); - spec2.getLoreyHeightByUtilization().setCoe(FipStart.UTIL_ALL, 22.8001652f); - spec2.getBaseAreaByUtilization().setCoe(FipStart.UTIL_ALL, 5.08774281f); - spec2.getTreesPerHectareByUtilization().setCoe(FipStart.UTIL_ALL, 92.4298019f); - spec2.getQuadraticMeanDiameterByUtilization().setCoe(FipStart.UTIL_ALL, 26.4735165f); - spec2.getWholeStemVolumeByUtilization().setCoe(FipStart.UTIL_ALL, 6.35662031f); - var spec3 = VdypSpecies.build(layer, builder -> { - builder.genus("D"); - builder.percentGenus(20f); - builder.volumeGroup(-1); - builder.decayGroup(-1); - builder.breakageGroup(-1); - }); - spec3.getLoreyHeightByUtilization().setCoe(FipStart.UTIL_ALL, 33.5375252f); - spec3.getBaseAreaByUtilization().setCoe(FipStart.UTIL_ALL, 29.5411568f); - spec3.getTreesPerHectareByUtilization().setCoe(FipStart.UTIL_ALL, 326.800781f); - spec3.getQuadraticMeanDiameterByUtilization().setCoe(FipStart.UTIL_ALL, 33.9255791f); - spec3.getWholeStemVolumeByUtilization().setCoe(FipStart.UTIL_ALL, 44.496151f); - var spec4 = VdypSpecies.build(layer, builder -> { - builder.genus("H"); - builder.percentGenus(20f); - builder.volumeGroup(-1); - builder.decayGroup(-1); - builder.breakageGroup(-1); - }); - spec4.getLoreyHeightByUtilization().setCoe(FipStart.UTIL_ALL, 24.3451157f); - spec4.getBaseAreaByUtilization().setCoe(FipStart.UTIL_ALL, 5.50214148f); - spec4.getTreesPerHectareByUtilization().setCoe(FipStart.UTIL_ALL, 152.482513f); - spec4.getQuadraticMeanDiameterByUtilization().setCoe(FipStart.UTIL_ALL, 21.4343796f); - spec4.getWholeStemVolumeByUtilization().setCoe(FipStart.UTIL_ALL, 470.388489f); - var spec5 = VdypSpecies.build(layer, builder -> { - builder.genus("S"); - builder.percentGenus(20f); - builder.volumeGroup(-1); - builder.decayGroup(-1); - builder.breakageGroup(-1); - }); - spec5.getLoreyHeightByUtilization().setCoe(FipStart.UTIL_ALL, 34.6888771f); - spec5.getBaseAreaByUtilization().setCoe(FipStart.UTIL_ALL, 4.0966382f); - spec5.getTreesPerHectareByUtilization().setCoe(FipStart.UTIL_ALL, 43.7256737f); - spec5.getQuadraticMeanDiameterByUtilization().setCoe(FipStart.UTIL_ALL, 34.5382729f); - spec5.getWholeStemVolumeByUtilization().setCoe(FipStart.UTIL_ALL, 57.2091446f); - - layer.setSpecies(Arrays.asList(spec1, spec2, spec3, spec4, spec5)); - - app.estimateSmallComponents(fPoly, layer); - - assertThat(layer.getLoreyHeightByUtilization().getCoe(FipStart.UTIL_SMALL), closeTo(7.14446497f)); - assertThat(spec1.getLoreyHeightByUtilization().getCoe(FipStart.UTIL_SMALL), closeTo(8.39441967f)); - assertThat(spec2.getLoreyHeightByUtilization().getCoe(FipStart.UTIL_SMALL), closeTo(6.61517191f)); - assertThat(spec3.getLoreyHeightByUtilization().getCoe(FipStart.UTIL_SMALL), closeTo(10.8831682f)); - assertThat(spec4.getLoreyHeightByUtilization().getCoe(FipStart.UTIL_SMALL), closeTo(7.93716192f)); - assertThat(spec5.getLoreyHeightByUtilization().getCoe(FipStart.UTIL_SMALL), closeTo(8.63455391f)); - - assertThat(layer.getBaseAreaByUtilization().getCoe(FipStart.UTIL_SMALL), closeTo(0.0153773092f)); - assertThat(spec1.getBaseAreaByUtilization().getCoe(FipStart.UTIL_SMALL), closeTo(0f)); - assertThat(spec2.getBaseAreaByUtilization().getCoe(FipStart.UTIL_SMALL), closeTo(0.0131671466f)); - assertThat(spec3.getBaseAreaByUtilization().getCoe(FipStart.UTIL_SMALL), closeTo(0.00163476227f)); - assertThat(spec4.getBaseAreaByUtilization().getCoe(FipStart.UTIL_SMALL), closeTo(0f)); - assertThat(spec5.getBaseAreaByUtilization().getCoe(FipStart.UTIL_SMALL), closeTo(0.000575399841f)); - - assertThat(layer.getTreesPerHectareByUtilization().getCoe(FipStart.UTIL_SMALL), closeTo(5.34804487f)); - assertThat(spec1.getTreesPerHectareByUtilization().getCoe(FipStart.UTIL_SMALL), closeTo(0f)); - assertThat(spec2.getTreesPerHectareByUtilization().getCoe(FipStart.UTIL_SMALL), closeTo(4.67143154f)); - assertThat(spec3.getTreesPerHectareByUtilization().getCoe(FipStart.UTIL_SMALL), closeTo(0.498754263f)); - assertThat(spec4.getTreesPerHectareByUtilization().getCoe(FipStart.UTIL_SMALL), closeTo(0f)); - assertThat(spec5.getTreesPerHectareByUtilization().getCoe(FipStart.UTIL_SMALL), closeTo(0.17785944f)); - - assertThat(layer.getQuadraticMeanDiameterByUtilization().getCoe(FipStart.UTIL_SMALL), closeTo(6.05059004f)); - assertThat(spec1.getQuadraticMeanDiameterByUtilization().getCoe(FipStart.UTIL_SMALL), closeTo(6.13586617f)); - assertThat(spec2.getQuadraticMeanDiameterByUtilization().getCoe(FipStart.UTIL_SMALL), closeTo(5.99067688f)); - assertThat(spec3.getQuadraticMeanDiameterByUtilization().getCoe(FipStart.UTIL_SMALL), closeTo(6.46009731f)); - assertThat(spec4.getQuadraticMeanDiameterByUtilization().getCoe(FipStart.UTIL_SMALL), closeTo(6.03505516f)); - assertThat(spec5.getQuadraticMeanDiameterByUtilization().getCoe(FipStart.UTIL_SMALL), closeTo(6.41802597f)); - - assertThat(layer.getWholeStemVolumeByUtilization().getCoe(FipStart.UTIL_SMALL), closeTo(0.0666879341f)); - assertThat(spec1.getWholeStemVolumeByUtilization().getCoe(FipStart.UTIL_SMALL), closeTo(0f)); - assertThat(spec2.getWholeStemVolumeByUtilization().getCoe(FipStart.UTIL_SMALL), closeTo(0.0556972362f)); - assertThat(spec3.getWholeStemVolumeByUtilization().getCoe(FipStart.UTIL_SMALL), closeTo(0.0085867513f)); - assertThat(spec4.getWholeStemVolumeByUtilization().getCoe(FipStart.UTIL_SMALL), closeTo(0f)); - assertThat(spec5.getWholeStemVolumeByUtilization().getCoe(FipStart.UTIL_SMALL), closeTo(0.00240394124f)); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - @Test - void testEstimateQuadMeanDiameterByUtilization() throws ProcessingException { - var controlMap = FipTestUtils.loadControlMap(); - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var coe = Utils.utilizationVector(); - coe.setCoe(FipStart.UTIL_ALL, 31.6622887f); - - var bec = BecDefinitionParser.getBecs(controlMap).get("CWH").get(); - - var spec1 = VdypSpecies.build(builder -> { - builder.polygonIdentifier("Test", 2024); - builder.layerType(LayerType.PRIMARY); - builder.genus("B"); - builder.percentGenus(100f); - builder.volumeGroup(-1); - builder.decayGroup(-1); - builder.breakageGroup(-1); - }); - - EstimationMethods.estimateQuadMeanDiameterByUtilization(controlMap, bec, coe, spec1.getGenus()); - - assertThat(coe, utilization(0f, 31.6622887f, 10.0594692f, 14.966774f, 19.9454956f, 46.1699982f)); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - @Test - void testEstimateQuadMeanDiameterByUtilization2() throws ProcessingException { - var controlMap = FipTestUtils.loadControlMap(); - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var coe = Utils.utilizationVector(); - coe.setCoe(FipStart.UTIL_ALL, 13.4943399f); - - var bec = BecDefinitionParser.getBecs(controlMap).get("MH").get(); - - var spec1 = VdypSpecies.build(builder -> { - builder.polygonIdentifier("Test", 2024); - builder.layerType(LayerType.PRIMARY); - builder.genus("L"); - builder.percentGenus(100f); - builder.volumeGroup(-1); - builder.decayGroup(-1); - builder.breakageGroup(-1); - }); - - EstimationMethods.estimateQuadMeanDiameterByUtilization(controlMap, bec, coe, spec1.getGenus()); - - assertThat(coe, utilization(0f, 13.4943399f, 10.2766619f, 14.67033f, 19.4037666f, 25.719244f)); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - @Test - void testEstimateBaseAreaByUtilization() throws ProcessingException { - var controlMap = FipTestUtils.loadControlMap(); - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var dq = Utils.utilizationVector(); - var ba = Utils.utilizationVector(); - dq.setCoe(0, 31.6622887f); - dq.setCoe(1, 10.0594692f); - dq.setCoe(2, 14.966774f); - dq.setCoe(3, 19.9454956f); - dq.setCoe(4, 46.1699982f); - - ba.setCoe(0, 0.397305071f); - - var bec = BecDefinitionParser.getBecs(controlMap).get("CWH").get(); - - var spec1 = VdypSpecies.build(builder -> { - builder.polygonIdentifier("Test", 2024); - builder.layerType(LayerType.PRIMARY); - builder.genus("B"); - builder.percentGenus(100f); - builder.volumeGroup(-1); - builder.decayGroup(-1); - builder.breakageGroup(-1); - }); - - EstimationMethods.estimateBaseAreaByUtilization(controlMap, bec, dq, ba, spec1.getGenus()); - - assertThat(ba, utilization(0f, 0.397305071f, 0.00485289097f, 0.0131751001f, 0.0221586525f, 0.357118428f)); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - @Test - void testReconcileComponentsMode1() throws ProcessingException { - var controlMap = FipTestUtils.loadControlMap(); - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - 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. - - dq.setCoe(0, 13.4943399f); - dq.setCoe(1, 10.2766619f); - dq.setCoe(2, 14.67033f); - dq.setCoe(3, 19.4037666f); - dq.setCoe(4, 25.719244f); - - ba.setCoe(0, 2.20898318f); - ba.setCoe(1, 0.220842764f); - ba.setCoe(2, 0.433804274f); - ba.setCoe(3, 0.691931725f); - ba.setCoe(4, 0.862404406f); - - tph.setCoe(0, 154.454025f); - tph.setCoe(1, 83.4198151f); - tph.setCoe(2, 51.0201035f); - tph.setCoe(3, 14.6700592f); - tph.setCoe(4, 4.25086117f); - - ReconcilationMethods.reconcileComponents(ba, tph, dq); - - assertThat(ba, utilization(0f, 2.20898318f, 0.220842764f, 0.546404183f, 1.44173622f, 0f)); - assertThat(tph, utilization(0f, 154.454025f, 49.988575f, 44.5250206f, 59.9404259f, 0f)); - assertThat(dq, utilization(0f, 13.4943399f, 7.5f, 12.5f, 17.5f, 22.5f)); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - @Test - void testReconcileComponentsMode2() throws ProcessingException { - var controlMap = FipTestUtils.loadControlMap(); - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - 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); - dq.setCoe(3, 19.9454956f); - dq.setCoe(4, 46.1699982f); - - ba.setCoe(0, 0.397305071f); - ba.setCoe(1, 0.00485289097f); - ba.setCoe(2, 0.0131751001f); - ba.setCoe(3, 0.0221586525f); - ba.setCoe(4, 0.357118428f); - - tph.setCoe(0, 5.04602766f); - tph.setCoe(1, 0.61060524f); - tph.setCoe(2, 0.748872101f); - tph.setCoe(3, 0.709191978f); - tph.setCoe(4, 2.13305807f); - - ReconcilationMethods.reconcileComponents(ba, tph, dq); - - assertThat(ba, utilization(0f, 0.397305071f, 0.00485289097f, 0.0131751001f, 0.0221586525f, 0.357118428f)); - assertThat(tph, utilization(0f, 5.04602766f, 0.733301044f, 0.899351299f, 0.851697803f, 2.56167722f)); - assertThat(dq, utilization(0f, 31.6622887f, 9.17939758f, 13.6573782f, 18.2005272f, 42.1307297f)); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - @Test - void testReconcileComponentsMode3() throws ProcessingException { - var controlMap = FipTestUtils.loadControlMap(); - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - 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 - - dq.setCoe(0, 12.51f); - dq.setCoe(1, 12.4f); - dq.setCoe(2, 0f); - dq.setCoe(3, 0f); - dq.setCoe(4, 0f); - - ba.setCoe(0, 2.20898318f); - ba.setCoe(1, 2.20898318f); - ba.setCoe(2, 0f); - ba.setCoe(3, 0f); - ba.setCoe(4, 0f); - - tph.setCoe(0, 179.71648f); - tph.setCoe(1, 182.91916f); - tph.setCoe(2, 0f); - tph.setCoe(3, 0f); - tph.setCoe(4, 0f); - - ReconcilationMethods.reconcileComponents(ba, tph, dq); - - assertThat(ba, utilization(0f, 2.20898318f, 0f, 2.20898318f, 0f, 0f)); - assertThat(tph, utilization(0f, 179.71648f, 0f, 179.71648f, 0f, 0f)); - assertThat(dq, utilization(0f, 12.51f, 10, 12.51f, 20f, 25f)); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - @Test - void testEstimateWholeStemVolumeByUtilizationClass() throws ProcessingException { - var controlMap = FipTestUtils.loadControlMap(); - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var dq = Utils.utilizationVector(); - var ba = Utils.utilizationVector(); - var wsv = Utils.utilizationVector(); - - dq.setCoe(0, 13.4943399f); - dq.setCoe(1, 10.2402296f); - dq.setCoe(2, 14.6183214f); - dq.setCoe(3, 19.3349762f); - dq.setCoe(4, 25.6280651f); - - ba.setCoe(0, 2.20898318f); - ba.setCoe(1, 0.691931725f); - ba.setCoe(2, 0.862404406f); - ba.setCoe(3, 0.433804274f); - ba.setCoe(4, 0.220842764f); - - wsv.setCoe(FipStart.UTIL_ALL, 11.7993851f); - - // app.estimateWholeStemVolumeByUtilizationClass(46, 14.2597857f, dq, ba, wsv); - EstimationMethods - .estimateWholeStemVolume(controlMap, UtilizationClass.ALL, 0f, 46, 14.2597857f, dq, ba, wsv); - - assertThat(wsv, utilization(0f, 11.7993851f, 3.13278913f, 4.76524019f, 2.63645673f, 1.26489878f)); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - @Test - void testComputeUtilizationComponentsPrimaryByUtilNoCV() throws ProcessingException { - var controlMap = FipTestUtils.loadControlMap(); - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var bec = BecDefinitionParser.getBecs(controlMap).get("IDF").get(); - - var layer = VdypLayer.build(builder -> { - builder.polygonIdentifier("Test", 2024); - builder.layerType(LayerType.PRIMARY); - builder.addSite(siteBuilder -> { - siteBuilder.ageTotal(55f); - siteBuilder.yearsToBreastHeight(3.5f); - siteBuilder.height(20f); - siteBuilder.siteGenus("H"); - }); - - }); - - layer.getLoreyHeightByUtilization().setCoe(FipStart.UTIL_ALL, 13.0660105f); - layer.getBaseAreaByUtilization().setCoe(FipStart.UTIL_ALL, 19.9786701f); - layer.getTreesPerHectareByUtilization().setCoe(FipStart.UTIL_ALL, 1485.8208f); - layer.getQuadraticMeanDiameterByUtilization().setCoe(FipStart.UTIL_ALL, 13.0844402f); - layer.getWholeStemVolumeByUtilization().setCoe(FipStart.UTIL_ALL, 117.993797f); - - layer.getLoreyHeightByUtilization().setCoe(FipStart.UTIL_SMALL, 7.83768177f); - layer.getBaseAreaByUtilization().setCoe(FipStart.UTIL_SMALL, 0.0286490358f); - layer.getTreesPerHectareByUtilization().setCoe(FipStart.UTIL_SMALL, 9.29024601f); - layer.getQuadraticMeanDiameterByUtilization().setCoe(FipStart.UTIL_SMALL, 6.26608753f); - layer.getWholeStemVolumeByUtilization().setCoe(FipStart.UTIL_SMALL, 0.107688069f); - - var spec1 = VdypSpecies.build(layer, builder -> { - builder.genus("L"); - builder.percentGenus(11.0567074f); - builder.volumeGroup(46); - builder.decayGroup(38); - builder.breakageGroup(20); - }); - - spec1.getLoreyHeightByUtilization().setCoe(FipStart.UTIL_ALL, 14.2597857f); - spec1.getBaseAreaByUtilization().setCoe(FipStart.UTIL_ALL, 2.20898318f); - spec1.getTreesPerHectareByUtilization().setCoe(FipStart.UTIL_ALL, 154.454025f); - spec1.getQuadraticMeanDiameterByUtilization().setCoe(FipStart.UTIL_ALL, 13.4943399f); - spec1.getWholeStemVolumeByUtilization().setCoe(FipStart.UTIL_ALL, 11.7993851f); - - spec1.getLoreyHeightByUtilization().setCoe(FipStart.UTIL_SMALL, 7.86393309f); - spec1.getBaseAreaByUtilization().setCoe(FipStart.UTIL_SMALL, 0.012636207f); - spec1.getTreesPerHectareByUtilization().setCoe(FipStart.UTIL_SMALL, 3.68722916f); - spec1.getQuadraticMeanDiameterByUtilization().setCoe(FipStart.UTIL_SMALL, 6.60561657f); - spec1.getWholeStemVolumeByUtilization().setCoe(FipStart.UTIL_SMALL, 0.0411359742f); - - var spec2 = VdypSpecies.build(layer, builder -> { - builder.genus("PL"); - builder.percentGenus(88.9432907f); - builder.volumeGroup(54); - builder.decayGroup(42); - builder.breakageGroup(24); - }); - - spec2.getLoreyHeightByUtilization().setCoe(FipStart.UTIL_ALL, 12.9176102f); - spec2.getBaseAreaByUtilization().setCoe(FipStart.UTIL_ALL, 17.7696857f); - spec2.getTreesPerHectareByUtilization().setCoe(FipStart.UTIL_ALL, 1331.36682f); - spec2.getQuadraticMeanDiameterByUtilization().setCoe(FipStart.UTIL_ALL, 13.0360518f); - spec2.getWholeStemVolumeByUtilization().setCoe(FipStart.UTIL_ALL, 106.194412f); - - spec2.getLoreyHeightByUtilization().setCoe(FipStart.UTIL_SMALL, 7.81696558f); - spec2.getBaseAreaByUtilization().setCoe(FipStart.UTIL_SMALL, 0.0160128288f); - spec2.getTreesPerHectareByUtilization().setCoe(FipStart.UTIL_SMALL, 5.60301685f); - spec2.getQuadraticMeanDiameterByUtilization().setCoe(FipStart.UTIL_SMALL, 6.03223324f); - spec2.getWholeStemVolumeByUtilization().setCoe(FipStart.UTIL_SMALL, 0.0665520951f); - - layer.setSpecies(Arrays.asList(spec1, spec2)); - - app.computeUtilizationComponentsPrimary( - bec, layer, VolumeComputeMode.BY_UTIL, CompatibilityVariableMode.NONE - ); - - // TODO test percent for each species - - assertThat( - layer.getLoreyHeightByUtilization(), coe(-1, contains(closeTo(7.83768177f), closeTo(13.0660114f))) - ); - assertThat( - spec1.getLoreyHeightByUtilization(), coe(-1, contains(closeTo(7.86393309f), closeTo(14.2597857f))) - ); - assertThat( - spec2.getLoreyHeightByUtilization(), coe(-1, contains(closeTo(7.81696558f), closeTo(12.9176102f))) - ); - - assertThat( - spec1.getBaseAreaByUtilization(), - utilization(0.012636207f, 2.20898318f, 0.691931725f, 0.862404406f, 0.433804274f, 0.220842764f) - ); - assertThat( - spec2.getBaseAreaByUtilization(), - utilization(0.0160128288f, 17.7696857f, 6.10537529f, 7.68449211f, 3.20196891f, 0.777849257f) - ); - assertThat( - layer.getBaseAreaByUtilization(), - utilization(0.0286490358f, 19.9786682f, 6.79730701f, 8.54689693f, 3.63577318f, 0.998692036f) - ); - - assertThat( - spec1.getTreesPerHectareByUtilization(), - utilization(3.68722916f, 154.454025f, 84.0144501f, 51.3837852f, 14.7746315f, 4.28116179f) - ); - assertThat( - spec2.getTreesPerHectareByUtilization(), - utilization(5.60301685f, 1331.36682f, 750.238892f, 457.704498f, 108.785675f, 14.6378069f) - ); - assertThat( - layer.getTreesPerHectareByUtilization(), - utilization(9.29024601f, 1485.8208f, 834.253357f, 509.088287f, 123.560303f, 18.9189682f) - ); - - assertThat( - spec1.getQuadraticMeanDiameterByUtilization(), - utilization(6.60561657f, 13.4943399f, 10.2402296f, 14.6183214f, 19.3349762f, 25.6280651f) - ); - assertThat( - spec2.getQuadraticMeanDiameterByUtilization(), - utilization(6.03223324f, 13.0360518f, 10.1791487f, 14.6207638f, 19.3587704f, 26.0114632f) - ); - assertThat( - layer.getQuadraticMeanDiameterByUtilization(), - utilization(6.26608753f, 13.0844393f, 10.1853161f, 14.6205177f, 19.3559265f, 25.9252014f) - ); - - assertThat( - spec1.getWholeStemVolumeByUtilization(), - utilization(0.0411359742f, 11.7993851f, 3.13278913f, 4.76524019f, 2.63645673f, 1.26489878f) - ); - assertThat( - spec2.getWholeStemVolumeByUtilization(), - utilization(0.0665520951f, 106.194412f, 30.2351704f, 47.6655998f, 22.5931034f, 5.70053911f) - ); - assertThat( - layer.getWholeStemVolumeByUtilization(), - utilization(0.107688069f, 117.993797f, 33.3679581f, 52.4308395f, 25.2295609f, 6.96543789f) - ); - - assertThat( - spec1.getCloseUtilizationVolumeByUtilization(), - utilization(0f, 6.41845179f, 0.0353721268f, 2.99654913f, 2.23212862f, 1.1544019f) - ); - assertThat( - spec2.getCloseUtilizationVolumeByUtilization(), - utilization(0f, 61.335495f, 2.38199472f, 33.878521f, 19.783432f, 5.29154539f) - ); - assertThat( - layer.getCloseUtilizationVolumeByUtilization(), - utilization(0f, 67.7539444f, 2.41736674f, 36.8750687f, 22.0155602f, 6.44594717f) - ); - - assertThat( - spec1.getCloseUtilizationVolumeNetOfDecayByUtilization(), - utilization(0f, 6.26433992f, 0.0349677317f, 2.95546484f, 2.18952441f, 1.08438313f) - ); - assertThat( - spec2.getCloseUtilizationVolumeNetOfDecayByUtilization(), - utilization(0f, 60.8021164f, 2.36405492f, 33.6109734f, 19.6035042f, 5.2235837f) - ); - assertThat( - layer.getCloseUtilizationVolumeNetOfDecayByUtilization(), - utilization(0f, 67.0664597f, 2.39902258f, 36.5664368f, 21.7930279f, 6.30796671f) - ); - - assertThat( - spec1.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization(), - utilization(0f, 6.18276405f, 0.0347718038f, 2.93580461f, 2.16927385f, 1.04291379f) - ); - assertThat( - spec2.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization(), - utilization(0f, 60.6585732f, 2.36029577f, 33.544487f, 19.5525551f, 5.20123625f) - ); - assertThat( - layer.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization(), - utilization(0f, 66.8413391f, 2.39506769f, 36.4802933f, 21.7218285f, 6.24415016f) - ); - - assertThat( - spec1.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(), - utilization(0f, 5.989573f, 0.0337106399f, 2.84590816f, 2.10230994f, 1.00764418f) - ); - assertThat( - spec2.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(), - utilization(0f, 59.4318657f, 2.31265593f, 32.8669167f, 19.1568871f, 5.09540558f) - ); - assertThat( - layer.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(), - utilization(0f, 65.4214401f, 2.34636664f, 35.7128258f, 21.2591972f, 6.10304976f) - ); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - @Test ->>>>>>> origin/feature/VDYP-362-compatibility-variables - void testCreateVdypPolygon() throws ProcessingException { - var controlMap = FipTestUtils.loadControlMap(); - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var fipPolygon = FipPolygon.build(builder -> { - builder.polygonIdentifier("Test", 2024); - builder.forestInventoryZone("D"); - builder.biogeoclimaticZone("IDF"); - builder.mode(PolygonMode.START); - builder.yieldFactor(1f); - }); - - // var fipVeteranLayer = new FipLayer("Test", LayerType.VETERAN); - var fipPrimaryLayer = FipLayerPrimary.buildPrimary(fipPolygon, builder -> { - - builder.crownClosure(60f); - builder.addSite(siteBuilder -> { - siteBuilder.ageTotal(60f); - siteBuilder.yearsToBreastHeight(8.5f); - siteBuilder.height(15f); - siteBuilder.siteGenus("L"); - siteBuilder.siteSpecies("L"); - siteBuilder.siteIndex(5f); - }); - - }); - - var processedLayers = new HashMap(); - processedLayers.put(LayerType.PRIMARY, VdypLayer.build(builder -> { - builder.polygonIdentifier("Test", 2024); - builder.layerType(LayerType.PRIMARY); - builder.addSite(siteBuilder -> { - siteBuilder.ageTotal(60f); - siteBuilder.yearsToBreastHeight(8.5f); - siteBuilder.height(15f); - siteBuilder.siteGenus("H"); - }); - })); - - FipSpecies.build(fipPrimaryLayer, builder -> { - builder.genus("L"); - builder.percentGenus(10f); - }); - FipSpecies.build(fipPrimaryLayer, builder -> { - builder.genus("PL"); - builder.percentGenus(90f); - }); - - var vdypPolygon = app.createVdypPolygon(fipPolygon, processedLayers); - - assertThat(vdypPolygon, notNullValue()); - assertThat(vdypPolygon, hasProperty("layers", equalTo(processedLayers))); - assertThat(vdypPolygon, hasProperty("percentAvailable", closeTo(90f))); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - @Test - void testCreateVdypPolygonPercentForestLandGiven() throws ProcessingException { - var controlMap = FipTestUtils.loadControlMap(); - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var fipPolygon = FipPolygon.build(builder -> { - builder.polygonIdentifier("Test", 2024); - builder.forestInventoryZone("D"); - builder.biogeoclimaticZone("IDF"); - builder.mode(PolygonMode.START); - builder.yieldFactor(1f); - - builder.percentAvailable(42f); - }); - - // var fipVeteranLayer = new FipLayer("Test", LayerType.VETERAN); - var fipPrimaryLayer = FipLayerPrimary.buildPrimary(fipPolygon, builder -> { - builder.crownClosure(60f); - - builder.addSite(siteBuilder -> { - siteBuilder.ageTotal(60f); - siteBuilder.yearsToBreastHeight(8.5f); - siteBuilder.height(15f); - - siteBuilder.siteIndex(5f); - siteBuilder.siteGenus("L"); - siteBuilder.siteSpecies("L"); - }); - }); - - var processedLayers = new HashMap(); - processedLayers.put(LayerType.PRIMARY, VdypLayer.build(builder -> { - builder.polygonIdentifier("Test", 2024); - builder.layerType(LayerType.PRIMARY); - builder.addSite(siteBuilder -> { - siteBuilder.ageTotal(60f); - siteBuilder.yearsToBreastHeight(8.5f); - siteBuilder.height(15f); - siteBuilder.siteGenus("L"); - }); - })); - - FipSpecies.build(fipPrimaryLayer, builder -> { - builder.polygonIdentifier("Test", 2024); - builder.layerType(LayerType.PRIMARY); - builder.genus("L"); - builder.percentGenus(10f); - }); - FipSpecies.build(fipPrimaryLayer, builder -> { - builder.polygonIdentifier("Test", 2024); - builder.layerType(LayerType.PRIMARY); - builder.genus("PL"); - builder.percentGenus(90f); - }); - - var vdypPolygon = app.createVdypPolygon(fipPolygon, processedLayers); - - assertThat(vdypPolygon, notNullValue()); - assertThat(vdypPolygon, hasProperty("layers", equalTo(processedLayers))); - assertThat(vdypPolygon, hasProperty("percentAvailable", closeTo(42f))); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - @Test - void testCreateVdypPolygonFipYoung() throws ProcessingException { - var controlMap = FipTestUtils.loadControlMap(); - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - var fipPolygon = FipPolygon.build(builder -> { - builder.polygonIdentifier("Test", 2024); - builder.forestInventoryZone("D"); - builder.biogeoclimaticZone("IDF"); - builder.mode(PolygonMode.YOUNG); - builder.yieldFactor(1f); - }); - - // var fipVeteranLayer = new FipLayer("Test", LayerType.VETERAN); - var fipPrimaryLayer = FipLayerPrimary.buildPrimary(fipPolygon, builder -> { - builder.crownClosure(60f); - - builder.addSite(siteBuilder -> { - siteBuilder.ageTotal(60f); - siteBuilder.yearsToBreastHeight(8.5f); - siteBuilder.height(15f); - - siteBuilder.siteIndex(5f); - siteBuilder.siteGenus("L"); - siteBuilder.siteSpecies("L"); - }); - }); - - var processedLayers = new HashMap(); - processedLayers.put(LayerType.PRIMARY, VdypLayer.build(builder -> { - builder.polygonIdentifier("Test", 2024); - builder.layerType(LayerType.PRIMARY); - builder.addSite(siteBuilder -> { - siteBuilder.ageTotal(60f); - siteBuilder.yearsToBreastHeight(8.5f); - siteBuilder.height(15f); - siteBuilder.siteGenus("L"); - }); - })); - - FipSpecies.build(fipPrimaryLayer, builder -> { - builder.genus("L"); - builder.percentGenus(10f); - }); - FipSpecies.build(fipPrimaryLayer, builder -> { - builder.genus("PL"); - builder.percentGenus(90f); - }); - - var vdypPolygon = app.createVdypPolygon(fipPolygon, processedLayers); - - assertThat(vdypPolygon, notNullValue()); - assertThat(vdypPolygon, hasProperty("layers", equalTo(processedLayers))); - assertThat(vdypPolygon, hasProperty("percentAvailable", closeTo(100f))); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - @Test - void testApplyStockingFactor() throws ProcessingException { - var controlMap = FipTestUtils.loadControlMap(); - @SuppressWarnings("unchecked") - var stockingClassMap = (MatrixMap2>) controlMap - .get(ControlKey.STOCKING_CLASS_FACTORS.name()); - - stockingClassMap - .put('R', Region.INTERIOR, Optional.of(new StockingClassFactor('R', Region.INTERIOR, 0.42f, 100))); - - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - // var fipVeteranLayer = new FipLayer("Test", LayerType.VETERAN); - var fipPrimaryLayer = FipLayerPrimary.buildPrimary(builder -> { - builder.polygonIdentifier("Test", 2024); - builder.crownClosure(0.9f); - - builder.addSite(siteBuilder -> { - siteBuilder.ageTotal(60f); - siteBuilder.yearsToBreastHeight(8.5f); - siteBuilder.height(20f); - - siteBuilder.siteIndex(5f); - siteBuilder.siteGenus("L"); - siteBuilder.siteSpecies("L"); - }); - - builder.stockingClass('R'); - }); - - var processedLayers = new HashMap(); - processedLayers.put(LayerType.PRIMARY, VdypLayer.build(builder -> { - builder.polygonIdentifier("Test", 2024); - builder.layerType(LayerType.PRIMARY); - builder.addSite(siteBuilder -> { - siteBuilder.ageTotal(60f); - siteBuilder.yearsToBreastHeight(8.5f); - siteBuilder.height(20f); - - siteBuilder.siteIndex(5f); - siteBuilder.siteGenus("L"); - }); - })); - - var vdypLayer = VdypLayer.build(builder -> { - builder.polygonIdentifier("Test", 2024); - builder.layerType(LayerType.PRIMARY); - builder.addSite(siteBuilder -> { - siteBuilder.ageTotal(60f); - siteBuilder.yearsToBreastHeight(8.5f); - siteBuilder.height(20f); - - siteBuilder.siteIndex(5f); - siteBuilder.siteGenus("L"); - }); - }); - - vdypLayer.setLoreyHeightByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); - vdypLayer.setQuadraticMeanDiameterByUtilization(Utils.utilizationVector(1f, 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( - Utils.utilizationVector(1f, 1f, 1f, 1f, 1f) - ); - - var spec1 = VdypSpecies.build(vdypLayer, builder -> { - builder.genus("L"); - builder.percentGenus(50f); - builder.volumeGroup(-1); - builder.decayGroup(-1); - builder.breakageGroup(-1); - }); - - spec1.setLoreyHeightByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); - spec1.setQuadraticMeanDiameterByUtilization(Utils.utilizationVector(1f, 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( - Utils.utilizationVector(1f, 1f, 1f, 1f, 1f) - ); - - var spec2 = VdypSpecies.build(vdypLayer, builder -> { - builder.genus("PL"); - builder.percentGenus(50f); - builder.volumeGroup(-1); - builder.decayGroup(-1); - builder.breakageGroup(-1); - }); - - spec2.setLoreyHeightByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); - spec2.setQuadraticMeanDiameterByUtilization(Utils.utilizationVector(1f, 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( - Utils.utilizationVector(1f, 1f, 1f, 1f, 1f) - ); - - app.adjustForStocking(vdypLayer, fipPrimaryLayer, BecDefinitionParser.getBecs(controlMap).get("IDF").get()); - -<<<<<<< HEAD - final var MODIFIED = VdypMatchers.utilization(0.42f, 4 * 0.42f, 0.42f, 0.42f, 0.42f, 0.42f); - final var NEVER_MODIFIED = VdypMatchers.utilization(1f, 1f, 1f, 1f, 1f, 1f); -======= - final var modifiedValue = utilization(0.42f, 4 * 0.42f, 0.42f, 0.42f, 0.42f, 0.42f); - final var neverModifiedValue = utilization(1f, 1f, 1f, 1f, 1f, 1f); ->>>>>>> origin/feature/VDYP-362-compatibility-variables - - assertThat(vdypLayer, hasProperty("loreyHeightByUtilization", neverModifiedValue)); - assertThat(vdypLayer, hasProperty("quadraticMeanDiameterByUtilization", neverModifiedValue)); - - assertThat(vdypLayer, hasProperty("baseAreaByUtilization", modifiedValue)); - assertThat(vdypLayer, hasProperty("treesPerHectareByUtilization", modifiedValue)); - assertThat(vdypLayer, hasProperty("closeUtilizationVolumeNetOfDecayByUtilization", modifiedValue)); - assertThat(vdypLayer, hasProperty("closeUtilizationVolumeNetOfDecayAndWasteByUtilization", modifiedValue)); - assertThat( - vdypLayer, hasProperty("closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", modifiedValue) - ); - - assertThat(spec1, hasProperty("loreyHeightByUtilization", neverModifiedValue)); - assertThat(spec1, hasProperty("quadraticMeanDiameterByUtilization", neverModifiedValue)); - - assertThat(spec1, hasProperty("baseAreaByUtilization", modifiedValue)); - assertThat(spec1, hasProperty("treesPerHectareByUtilization", modifiedValue)); - assertThat(spec1, hasProperty("closeUtilizationVolumeNetOfDecayByUtilization", modifiedValue)); - assertThat(spec1, hasProperty("closeUtilizationVolumeNetOfDecayAndWasteByUtilization", modifiedValue)); - assertThat(spec1, hasProperty("closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", modifiedValue)); - - assertThat(spec2, hasProperty("loreyHeightByUtilization", neverModifiedValue)); - assertThat(spec2, hasProperty("quadraticMeanDiameterByUtilization", neverModifiedValue)); - - assertThat(spec2, hasProperty("baseAreaByUtilization", modifiedValue)); - assertThat(spec2, hasProperty("treesPerHectareByUtilization", modifiedValue)); - assertThat(spec2, hasProperty("closeUtilizationVolumeNetOfDecayByUtilization", modifiedValue)); - assertThat(spec2, hasProperty("closeUtilizationVolumeNetOfDecayAndWasteByUtilization", modifiedValue)); - assertThat(spec2, hasProperty("closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", modifiedValue)); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - } - - @Test - void testApplyStockingFactorNoFactorForLayer() throws ProcessingException { - var controlMap = FipTestUtils.loadControlMap(); - @SuppressWarnings("unchecked") - var stockingClassMap = (MatrixMap2) controlMap - .get(ControlKey.STOCKING_CLASS_FACTORS.name()); - - stockingClassMap.put('R', Region.INTERIOR, new StockingClassFactor('R', Region.INTERIOR, 0.42f, 100)); - - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - // var fipVeteranLayer = new FipLayer("Test", LayerType.VETERAN); - var fipPrimaryLayer = FipLayerPrimary.buildPrimary(builder -> { - builder.polygonIdentifier("Test", 2024); - builder.crownClosure(60f); - - builder.addSite(siteBuilder -> { - siteBuilder.ageTotal(60f); - siteBuilder.yearsToBreastHeight(8.5f); - siteBuilder.height(15f); - - siteBuilder.siteIndex(5f); - siteBuilder.siteGenus("L"); - siteBuilder.siteSpecies("L"); - }); - }); - - var processedLayers = new HashMap(); - processedLayers.put(LayerType.PRIMARY, VdypLayer.build(builder -> { - builder.polygonIdentifier("Test", 2024); - builder.layerType(LayerType.PRIMARY); - - builder.addSite(siteBuilder -> { - siteBuilder.ageTotal(60f); - siteBuilder.yearsToBreastHeight(8.5f); - siteBuilder.height(20f); - - siteBuilder.siteIndex(5f); - siteBuilder.siteGenus("L"); - }); - })); - - fipPrimaryLayer.setStockingClass(Optional.empty()); - - var vdypLayer = VdypLayer.build(builder -> { - builder.polygonIdentifier("Test", 2024); - builder.layerType(LayerType.PRIMARY); - - builder.addSite(siteBuilder -> { - siteBuilder.ageTotal(60f); - siteBuilder.yearsToBreastHeight(8.5f); - siteBuilder.height(20f); - - siteBuilder.siteIndex(5f); - siteBuilder.siteGenus("L"); - }); - }); - vdypLayer.setLoreyHeightByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); - vdypLayer.setQuadraticMeanDiameterByUtilization(Utils.utilizationVector(1f, 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( - Utils.utilizationVector(1f, 1f, 1f, 1f, 1f) - ); - - var spec1 = VdypSpecies.build(vdypLayer, builder -> { - builder.genus("L"); - builder.percentGenus(50f); - builder.volumeGroup(-1); - builder.decayGroup(-1); - builder.breakageGroup(-1); - }); - - spec1.setLoreyHeightByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); - spec1.setQuadraticMeanDiameterByUtilization(Utils.utilizationVector(1f, 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( - Utils.utilizationVector(1f, 1f, 1f, 1f, 1f) - ); - - var spec2 = VdypSpecies.build(vdypLayer, builder -> { - builder.genus("PL"); - builder.percentGenus(50f); - builder.volumeGroup(-1); - builder.decayGroup(-1); - builder.breakageGroup(-1); - }); - - spec2.setLoreyHeightByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); - spec2.setQuadraticMeanDiameterByUtilization(Utils.utilizationVector(1f, 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( - Utils.utilizationVector(1f, 1f, 1f, 1f, 1f) - ); - - app.adjustForStocking(vdypLayer, fipPrimaryLayer, BecDefinitionParser.getBecs(controlMap).get("IDF").get()); - -<<<<<<< HEAD - final var MOFIIABLE_NOT_MODIFIED = VdypMatchers.utilization(1f, 4f, 1f, 1f, 1f, 1f); - final var NEVER_MODIFIED = VdypMatchers.utilization(1f, 1f, 1f, 1f, 1f, 1f); -======= - final var modifiableNotModifiedValue = utilization(1f, 4f, 1f, 1f, 1f, 1f); - final var neverModifiedValue = utilization(1f, 1f, 1f, 1f, 1f, 1f); ->>>>>>> origin/feature/VDYP-362-compatibility-variables - - assertThat(vdypLayer, hasProperty("loreyHeightByUtilization", neverModifiedValue)); - assertThat(vdypLayer, hasProperty("quadraticMeanDiameterByUtilization", neverModifiedValue)); - - assertThat(vdypLayer, hasProperty("baseAreaByUtilization", modifiableNotModifiedValue)); - assertThat(vdypLayer, hasProperty("treesPerHectareByUtilization", modifiableNotModifiedValue)); - assertThat(vdypLayer, hasProperty("closeUtilizationVolumeNetOfDecayByUtilization", modifiableNotModifiedValue)); - assertThat( - vdypLayer, - hasProperty("closeUtilizationVolumeNetOfDecayAndWasteByUtilization", modifiableNotModifiedValue) - ); - assertThat( - vdypLayer, - hasProperty("closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", modifiableNotModifiedValue) - ); - - assertThat(spec1, hasProperty("loreyHeightByUtilization", neverModifiedValue)); - assertThat(spec1, hasProperty("quadraticMeanDiameterByUtilization", neverModifiedValue)); - - assertThat(spec1, hasProperty("baseAreaByUtilization", modifiableNotModifiedValue)); - assertThat(spec1, hasProperty("treesPerHectareByUtilization", modifiableNotModifiedValue)); - assertThat(spec1, hasProperty("closeUtilizationVolumeNetOfDecayByUtilization", modifiableNotModifiedValue)); - assertThat( - spec1, hasProperty("closeUtilizationVolumeNetOfDecayAndWasteByUtilization", modifiableNotModifiedValue) - ); - assertThat( - spec1, - hasProperty("closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", modifiableNotModifiedValue) - ); - - assertThat(spec2, hasProperty("loreyHeightByUtilization", neverModifiedValue)); - assertThat(spec2, hasProperty("quadraticMeanDiameterByUtilization", neverModifiedValue)); - - assertThat(spec2, hasProperty("baseAreaByUtilization", modifiableNotModifiedValue)); - assertThat(spec2, hasProperty("treesPerHectareByUtilization", modifiableNotModifiedValue)); - assertThat(spec2, hasProperty("closeUtilizationVolumeNetOfDecayByUtilization", modifiableNotModifiedValue)); - assertThat( - spec2, hasProperty("closeUtilizationVolumeNetOfDecayAndWasteByUtilization", modifiableNotModifiedValue) - ); - assertThat( - spec2, - hasProperty("closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", modifiableNotModifiedValue) - ); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - } - - @Test - void testApplyStockingFactorNoFactorForClass() throws ProcessingException { - var controlMap = FipTestUtils.loadControlMap(); - @SuppressWarnings("unchecked") - var stockingClassMap = (MatrixMap2) controlMap - .get(ControlKey.STOCKING_CLASS_FACTORS.name()); - - stockingClassMap.remove('R', Region.INTERIOR); - - try (var app = new FipStart()) { - ApplicationTestUtils.setControlMap(app, controlMap); - - // var fipVeteranLayer = new FipLayer("Test", LayerType.VETERAN); - var fipPrimaryLayer = FipLayerPrimary.buildPrimary(builder -> { - builder.polygonIdentifier("Test", 2024); - builder.crownClosure(60f); - - builder.addSite(siteBuilder -> { - siteBuilder.ageTotal(60f); - siteBuilder.yearsToBreastHeight(8.5f); - siteBuilder.height(15f); - - siteBuilder.siteIndex(5f); - siteBuilder.siteGenus("L"); - siteBuilder.siteSpecies("L"); - }); - }); - - var processedLayers = new HashMap(); - processedLayers.put(LayerType.PRIMARY, VdypLayer.build(builder -> { - builder.polygonIdentifier("Test", 2024); - builder.layerType(LayerType.PRIMARY); - - builder.addSite(siteBuilder -> { - siteBuilder.ageTotal(60f); - siteBuilder.yearsToBreastHeight(3.5f); - siteBuilder.height(20f); - - siteBuilder.siteIndex(5f); - siteBuilder.siteGenus("L"); - }); - })); - - fipPrimaryLayer.setStockingClass(Optional.of('R')); - - var vdypLayer = VdypLayer.build(builder -> { - builder.polygonIdentifier("Test", 2024); - builder.layerType(LayerType.PRIMARY); - - builder.addSite(siteBuilder -> { - siteBuilder.ageTotal(60f); - siteBuilder.yearsToBreastHeight(3.5f); - siteBuilder.height(20f); - - siteBuilder.siteIndex(5f); - siteBuilder.siteGenus("L"); - }); - }); - - vdypLayer.setLoreyHeightByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); - vdypLayer.setQuadraticMeanDiameterByUtilization(Utils.utilizationVector(1f, 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( - Utils.utilizationVector(1f, 1f, 1f, 1f, 1f) - ); - - var spec1 = VdypSpecies.build(vdypLayer, builder -> { - builder.genus("L"); - builder.percentGenus(50f); - builder.volumeGroup(-1); - builder.decayGroup(-1); - builder.breakageGroup(-1); - }); - - spec1.setLoreyHeightByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); - spec1.setQuadraticMeanDiameterByUtilization(Utils.utilizationVector(1f, 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( - Utils.utilizationVector(1f, 1f, 1f, 1f, 1f) - ); - - var spec2 = VdypSpecies.build(vdypLayer, builder -> { - builder.genus("PL"); - builder.percentGenus(50f); - builder.volumeGroup(-1); - builder.decayGroup(-1); - builder.breakageGroup(-1); - }); - - spec2.setLoreyHeightByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); - spec2.setQuadraticMeanDiameterByUtilization(Utils.utilizationVector(1f, 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( - Utils.utilizationVector(1f, 1f, 1f, 1f, 1f) - ); - - app.adjustForStocking(vdypLayer, fipPrimaryLayer, BecDefinitionParser.getBecs(controlMap).get("IDF").get()); - -<<<<<<< HEAD - final var MODIFIABLE_NOT_MODIFIED = VdypMatchers.utilization(1f, 4f, 1f, 1f, 1f, 1f); - final var NEVER_MODIFIED = VdypMatchers.utilization(1f, 1f, 1f, 1f, 1f, 1f); -======= - final var modifiableNotModifiedValue = utilization(1f, 4f, 1f, 1f, 1f, 1f); - final var neverModifiedValue = utilization(1f, 1f, 1f, 1f, 1f, 1f); ->>>>>>> origin/feature/VDYP-362-compatibility-variables - - assertThat(vdypLayer, hasProperty("loreyHeightByUtilization", neverModifiedValue)); - assertThat(vdypLayer, hasProperty("quadraticMeanDiameterByUtilization", neverModifiedValue)); - - assertThat(vdypLayer, hasProperty("baseAreaByUtilization", modifiableNotModifiedValue)); - assertThat(vdypLayer, hasProperty("treesPerHectareByUtilization", modifiableNotModifiedValue)); - assertThat( - vdypLayer, hasProperty("closeUtilizationVolumeNetOfDecayByUtilization", modifiableNotModifiedValue) - ); - assertThat( - vdypLayer, - hasProperty("closeUtilizationVolumeNetOfDecayAndWasteByUtilization", modifiableNotModifiedValue) - ); - assertThat( - vdypLayer, - hasProperty( - "closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", modifiableNotModifiedValue - ) - ); - - assertThat(spec1, hasProperty("loreyHeightByUtilization", neverModifiedValue)); - assertThat(spec1, hasProperty("quadraticMeanDiameterByUtilization", neverModifiedValue)); - - assertThat(spec1, hasProperty("baseAreaByUtilization", modifiableNotModifiedValue)); - assertThat(spec1, hasProperty("treesPerHectareByUtilization", modifiableNotModifiedValue)); - assertThat(spec1, hasProperty("closeUtilizationVolumeNetOfDecayByUtilization", modifiableNotModifiedValue)); - assertThat( - spec1, hasProperty("closeUtilizationVolumeNetOfDecayAndWasteByUtilization", modifiableNotModifiedValue) - ); - assertThat( - spec1, - hasProperty( - "closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", modifiableNotModifiedValue - ) - ); - - assertThat(spec2, hasProperty("loreyHeightByUtilization", neverModifiedValue)); - assertThat(spec2, hasProperty("quadraticMeanDiameterByUtilization", neverModifiedValue)); - - assertThat(spec2, hasProperty("baseAreaByUtilization", modifiableNotModifiedValue)); - assertThat(spec2, hasProperty("treesPerHectareByUtilization", modifiableNotModifiedValue)); - assertThat(spec2, hasProperty("closeUtilizationVolumeNetOfDecayByUtilization", modifiableNotModifiedValue)); - assertThat( - spec2, hasProperty("closeUtilizationVolumeNetOfDecayAndWasteByUtilization", modifiableNotModifiedValue) - ); - assertThat( - spec2, - hasProperty( - "closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", modifiableNotModifiedValue - ) - ); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - } - - @Test - void testProcessPolygon() throws ProcessingException, IOException { - var controlMap = FipTestUtils.loadControlMap(); - - var poly = FipPolygon.build(builder -> { - builder.polygonIdentifier("Test", 2024); - builder.biogeoclimaticZone("CWH"); - builder.yieldFactor(1f); - builder.forestInventoryZone("0"); - builder.mode(PolygonMode.START); - }); - - var layer = FipLayerPrimary.buildPrimary(poly, builder -> { - builder.crownClosure(0.9f); - - builder.addSite(siteBuilder -> { - siteBuilder.ageTotal(50f); - siteBuilder.yearsToBreastHeight(2f); - siteBuilder.height(20f); - - siteBuilder.siteIndex(1f); - siteBuilder.siteGenus("B"); - siteBuilder.siteSpecies("B"); - }); - - }); - - @SuppressWarnings("unused") - var spec = FipSpecies.build(layer, builder -> { - builder.genus("B"); - builder.percentGenus(100f); - }); - - var app = new FipStart(); - ApplicationTestUtils.setControlMap(app, controlMap); - - var result = app.processPolygon(0, poly); - - assertThat(result, present(any(VdypPolygon.class))); - - app.close(); - } - - private static MockStreamingParser - mockStream(IMocksControl control, Map controlMap, String key, String name) - throws IOException { - StreamingParserFactory streamFactory = control.mock(name + "Factory", StreamingParserFactory.class); - MockStreamingParser stream = new MockStreamingParser<>(); - - EasyMock.expect(streamFactory.get()).andReturn(stream); - - controlMap.put(key, streamFactory); - return stream; - } - - private static void expectAllClosed(MockStreamingParser... toClose) throws Exception { - for (var x : toClose) { - x.expectClosed(); - } - } - - private static void mockWith(MockStreamingParser stream, List results) - throws IOException, ResourceParseException { - stream.addValues(results); - } - - @SuppressWarnings("unused") - @SafeVarargs - private static void mockWith(MockStreamingParser stream, T... results) - throws IOException, ResourceParseException { - stream.addValues(results); - } - - private static final void testWith( - List polygons, List> layers, List> species, - TestConsumer test - ) throws Exception { - testWith(new HashMap<>(), polygons, layers, species, test); - } - - private static final void testWith( - Map myControlMap, List polygons, List> layers, - List> species, TestConsumer test - ) throws Exception { - - VdypStartApplication app = new FipStart(); - - Map controlMap = new HashMap<>(); - - Map minima = new HashMap<>(); - - minima.put(BaseControlParser.MINIMUM_HEIGHT, 5f); - minima.put(BaseControlParser.MINIMUM_BASE_AREA, 0f); - minima.put(BaseControlParser.MINIMUM_PREDICTED_BASE_AREA, 2f); - minima.put(BaseControlParser.MINIMUM_VETERAN_HEIGHT, 10f); - - controlMap.put(ControlKey.MINIMA.name(), minima); - - controlMap.putAll(myControlMap); - - var control = EasyMock.createControl(); - - MockStreamingParser polygonStream = mockStream( - control, controlMap, ControlKey.FIP_INPUT_YIELD_POLY.name(), "polygonStream" - ); - MockStreamingParser> layerStream = mockStream( - control, controlMap, ControlKey.FIP_INPUT_YIELD_LAYER.name(), "layerStream" - ); - MockStreamingParser> speciesStream = mockStream( - control, controlMap, ControlKey.FIP_INPUT_YIELD_LX_SP0.name(), "speciesStream" - ); - - mockWith(polygonStream, polygons); - mockWith(layerStream, layers); - mockWith(speciesStream, species); - - ApplicationTestUtils.setControlMap(app, controlMap); - - control.replay(); - - test.accept(app, controlMap); - - control.verify(); - - expectAllClosed(polygonStream, layerStream, speciesStream); - - } - -<<<<<<< HEAD -======= - /** - * Do nothing to mutate valid test data - */ - static final Consumer valid() { - return x -> { - }; - } - ->>>>>>> origin/feature/VDYP-362-compatibility-variables - static Map layerMap(FipLayer... layers) { - Map result = new HashMap<>(); - for (var layer : layers) { - result.put(layer.getLayerType(), layer); - } - return result; - } - - FipPolygon getTestPolygon(PolygonIdentifier polygonId, Consumer mutator) { - var result = FipPolygon.build(builder -> { - builder.polygonIdentifier(polygonId); - builder.forestInventoryZone("0"); - builder.biogeoclimaticZone("BG"); - builder.mode(PolygonMode.START); - builder.yieldFactor(1.0f); - }); - mutator.accept(result); - return result; - } - - FipLayerPrimary getTestPrimaryLayer( - PolygonIdentifier polygonId, Consumer mutator, - Consumer siteMutator - ) { - return FipLayerPrimary.buildPrimary(builder -> { - builder.polygonIdentifier(polygonId); - builder.addSite(siteBuilder -> { - siteBuilder.ageTotal(8f); - siteBuilder.yearsToBreastHeight(7f); - siteBuilder.height(6f); - siteBuilder.siteIndex(5f); - siteBuilder.siteGenus("B"); - siteBuilder.siteSpecies("B"); - siteMutator.accept(siteBuilder); - }); - - builder.crownClosure(0.9f); - mutator.accept(builder); - }); - } - - FipLayer getTestVeteranLayer( - PolygonIdentifier polygonId, Consumer mutator, Consumer siteMutator - ) { - return FipLayer.build(builder -> { - builder.polygonIdentifier(polygonId); - builder.layerType(LayerType.VETERAN); - - builder.addSite(siteBuilder -> { - siteBuilder.ageTotal(8f); - siteBuilder.yearsToBreastHeight(7f); - siteBuilder.height(6f); - siteBuilder.siteIndex(5f); - siteBuilder.siteGenus("B"); - siteBuilder.siteSpecies("B"); - siteMutator.accept(siteBuilder); - }); - - builder.crownClosure(0.9f); - mutator.accept(builder); - }); - } - - FipSpecies getTestSpecies(PolygonIdentifier polygonId, LayerType layer, Consumer mutator) { - return getTestSpecies(polygonId, layer, "B", mutator); - } - - FipSpecies - getTestSpecies(PolygonIdentifier polygonId, LayerType layer, String genusId, Consumer mutator) { - var result = FipSpecies.build(builder -> { - builder.polygonIdentifier(polygonId); - builder.layerType(layer); - builder.genus(genusId); - builder.percentGenus(100.0f); - builder.addSpecies(genusId, 100f); - }); - mutator.accept(result); - return result; - } - - @FunctionalInterface - private static interface TestConsumer { - public void accept( - VdypStartApplication unit, Map controlMap - ) throws Exception; - } -} From 795685a735723c9503f493efe4f4e0d7e55dd958 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Wed, 14 Aug 2024 13:16:58 -0700 Subject: [PATCH 05/53] Progress on veteran processing for VRI --- .../java/ca/bc/gov/nrs/vdyp/vri/VriStart.java | 19 +- .../ca/bc/gov/nrs/vdyp/vri/VriStartTest.java | 197 ++++++++++++++++-- 2 files changed, 190 insertions(+), 26 deletions(-) diff --git a/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java b/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java index e9d8ef3ac..cbc502948 100644 --- a/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java +++ b/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java @@ -457,10 +457,10 @@ void processPrimaryLayer(VriPolygon polygon, VdypLayer.Builder lBuilder) throws applyGroups(bec, vriSpec.getGenus(), sBuilder); float fraction = primaryLayer.getSpecies().size() == 1 ? 1 : vriSpec.getFractionGenus(); - + float specBaseArea = primaryBaseArea * fraction; sBuilder.baseArea(specBaseArea); - + if (vriSite == primarySiteIn) { sBuilder.loreyHeight(primaryHeight); @@ -641,7 +641,20 @@ float quadMeanDiameterSpeciesAdjust(float x, float initialDq, float min, float m private void processVeteranLayer(VriPolygon polygon, VdypLayer.Builder lBuilder) throws StandProcessingException { var veteranLayer = polygon.getLayers().get(LayerType.VETERAN); - + lBuilder.layerType(LayerType.VETERAN); + + lBuilder.adapt(veteranLayer); + + var lowDq=false; + + var primarySite = veteranLayer.getPrimarySite(); + var primarySpecies = veteranLayer.getPrimarySpeciesRecord(); + + float ageTotal = primarySite.flatMap(VriSite::getAgeTotal).map(at->at+veteranLayer.getAgeIncrease()).orElse(0f); + float yearsToBreastHeight = primarySite.flatMap(VriSite::getYearsToBreastHeight).orElse(0f); + float breastHeightAge = ageTotal - yearsToBreastHeight; + + // TODO } diff --git a/vdyp-lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriStartTest.java b/vdyp-lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriStartTest.java index a8f5e5e21..eb7d7fc70 100644 --- a/vdyp-lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriStartTest.java +++ b/vdyp-lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriStartTest.java @@ -1,6 +1,7 @@ package ca.bc.gov.nrs.vdyp.vri; import static ca.bc.gov.nrs.vdyp.test.TestUtils.assertHasPrimaryLayer; +import static ca.bc.gov.nrs.vdyp.test.TestUtils.assertHasVeteranLayer; import static ca.bc.gov.nrs.vdyp.test.TestUtils.assertOnlyPrimaryLayer; import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.closeTo; import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.coe; @@ -8,6 +9,7 @@ import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.notPresent; import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.present; import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.utilization; +import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.utilizationAllAndBiggest; import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.utilizationHeight; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.aMapWithSize; @@ -2188,7 +2190,7 @@ resultLayer, hasProperty("loreyHeightByUtilization", utilizationHeight(4.1406788 app.close(); } - + @Test void testProcessVeteran() throws Exception { @@ -2211,13 +2213,12 @@ void testProcessVeteran() throws Exception { lb.baseArea(47.0588226f); lb.treesPerHectare(764.705872f); lb.utilization(7.5f); - lb.inventoryTypeGroup(14); lb.empiricalRelationshipParameterIndex(33); lb.primaryGenus("C"); - // 1 3 + // 1 3 lb.addSpecies(sb -> { sb.genus("B"); sb.percentGenus(10); @@ -2228,7 +2229,7 @@ void testProcessVeteran() throws Exception { }); }); - // 2 4 (Primary) + // 2 4 (Primary) lb.addSpecies(sb -> { sb.genus("C"); sb.percentGenus(50); @@ -2245,7 +2246,7 @@ void testProcessVeteran() throws Exception { }); }); - // 3 8 + // 3 8 lb.addSpecies(sb -> { sb.genus("H"); sb.percentGenus(40); @@ -2262,6 +2263,63 @@ void testProcessVeteran() throws Exception { }); }); + pb.addLayer(lb -> { + lb.layerType(LayerType.VETERAN); + lb.crownClosure(50.8f); + lb.utilization(7.5f); + lb.baseArea(20f); + lb.treesPerHectare(123f); + lb.utilization(7.5f); + + lb.inventoryTypeGroup(14); + // lb.empiricalRelationshipParameterIndex(33); + + lb.primaryGenus("H"); // 3 + // 1 3 + lb.addSpecies(sb -> { + sb.genus("B"); + sb.percentGenus(20); + sb.addSpecies("BL", 100); + sb.addSite(ib -> { + ib.siteSpecies("BL"); + ib.siteCurveNumber(8); + }); + }); + + // 2 4 + lb.addSpecies(sb -> { + sb.genus("C"); + sb.percentGenus(30); + sb.addSpecies("CW", 100); + sb.addSite(ib -> { + ib.siteCurveNumber(11); + ib.ageTotal(100); + ib.height(30f); + ib.siteIndex(14.3f); + ib.yearsToBreastHeight(10.9f); + ib.breastHeightAge(189.1f); + ib.ageTotal(200f); + ib.siteSpecies("CW"); + }); + }); + + // 3 8 (Primary) + lb.addSpecies(sb -> { + sb.genus("H"); + sb.percentGenus(50); + sb.addSpecies("HW", 100); + sb.addSite(ib -> { + ib.siteCurveNumber(37); + ib.height(34f); + ib.siteIndex(14.6f); + ib.yearsToBreastHeight(9.7f); + ib.breastHeightAge(190.3f); + ib.ageTotal(200f); + ib.siteSpecies("HW"); + }); + }); + + }); }); app.init(resolver, controlMap); @@ -2274,39 +2332,37 @@ void testProcessVeteran() throws Exception { assertThat(result, hasProperty("mode", present(is(PolygonMode.START)))); assertThat(result, hasProperty("percentAvailable", is(85f))); - var resultLayer = assertHasPrimaryLayer(result); + var primaryLayer = assertHasPrimaryLayer(result); - assertThat(resultLayer, hasProperty("ageTotal", present(closeTo(100)))); - assertThat(resultLayer, hasProperty("breastHeightAge", present(closeTo(89.1f)))); - assertThat(resultLayer, hasProperty("yearsToBreastHeight", present(closeTo(10.9f)))); + assertThat(primaryLayer, hasProperty("ageTotal", present(closeTo(100)))); + assertThat(primaryLayer, hasProperty("breastHeightAge", present(closeTo(89.1f)))); + assertThat(primaryLayer, hasProperty("yearsToBreastHeight", present(closeTo(10.9f)))); - assertThat(resultLayer, hasProperty("siteGenus", present(is("C")))); + assertThat(primaryLayer, hasProperty("siteGenus", present(is("C")))); - assertThat(resultLayer, hasProperty("height", present(closeTo(20f)))); - assertThat(resultLayer, hasProperty("inventoryTypeGroup", present(is(14)))); - assertThat(resultLayer, hasProperty("empiricalRelationshipParameterIndex", present(is(33)))); + assertThat(primaryLayer, hasProperty("height", present(closeTo(20f)))); + assertThat(primaryLayer, hasProperty("inventoryTypeGroup", present(is(14)))); + assertThat(primaryLayer, hasProperty("empiricalRelationshipParameterIndex", present(is(33)))); assertThat( - resultLayer, hasProperty("loreyHeightByUtilization", utilizationHeight(5.45770216f, 21.0985336f)) + primaryLayer, hasProperty("loreyHeightByUtilization", utilizationHeight(5.45770216f, 21.0985336f)) ); assertThat( - resultLayer, + primaryLayer, hasProperty( "baseAreaByUtilization", - utilization( - 0.0787888616f, 47.0588226f, 0.787343979f, 2.33701372f, 3.97268224f, 39.9617844f - ) + utilization(0.0787888616f, 47.0588226f, 0.787343979f, 2.33701372f, 3.97268224f, 39.9617844f) ) ); assertThat( - resultLayer, + primaryLayer, hasProperty( "quadraticMeanDiameterByUtilization", utilization(5.89174175f, 27.9916744f, 9.26363468f, 14.1112642f, 18.8414402f, 37.8068199f) ) ); assertThat( - resultLayer, + primaryLayer, hasProperty( "treesPerHectareByUtilization", utilization(28.8993168f, 764.704102f, 116.818542f, 149.430603f, 142.483887f, 355.971069f) @@ -2314,15 +2370,110 @@ resultLayer, hasProperty("loreyHeightByUtilization", utilizationHeight(5.4577021 ); assertThat( - resultLayer, + primaryLayer, hasProperty( "closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", utilization(0, 252.98407f, 0.0354338735f, 4.66429567f, 14.5271645f, 233.757172f) ) ); + assertThat(primaryLayer.getSpecies(), allOf(aMapWithSize(3), hasKey("B"), hasKey("C"), hasKey("H"))); + + var veteranLayer = assertHasVeteranLayer(result); + + assertThat(veteranLayer, hasProperty("ageTotal", present(closeTo(200)))); + assertThat(veteranLayer, hasProperty("breastHeightAge", present(closeTo(190.3f)))); + assertThat(veteranLayer, hasProperty("yearsToBreastHeight", present(closeTo(9.7f)))); + + assertThat(veteranLayer, hasProperty("siteGenus", present(is("H")))); + + assertThat(veteranLayer, hasProperty("height", present(closeTo(34f)))); + assertThat(veteranLayer, hasProperty("inventoryTypeGroup", notPresent())); // ? + assertThat(veteranLayer, hasProperty("empiricalRelationshipParameterIndex", notPresent())); // ? + + assertThat(veteranLayer, hasProperty("loreyHeightByUtilization", utilizationHeight(0f, 32.8f))); + assertThat(veteranLayer, hasProperty("baseAreaByUtilization", utilizationAllAndBiggest(20f))); assertThat( - resultLayer.getSpecies(), - allOf(aMapWithSize(3), hasKey("B"), hasKey("C"), hasKey("H")) + veteranLayer, + hasProperty( + "quadraticMeanDiameterByUtilization", + utilizationAllAndBiggest(45.5006409f) + ) + ); + assertThat( + veteranLayer, + hasProperty( + "treesPerHectareByUtilization", + utilizationAllAndBiggest(123f) + ) + ); + + assertThat( + veteranLayer, + hasProperty( + "closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", + utilizationAllAndBiggest(167.61972f) + ) + ); + + VdypSpecies resultSpecB = TestUtils.assertHasSpecies(veteranLayer, "B", "C", "H"); + + assertThat(resultSpecB, hasProperty("loreyHeightByUtilization", utilizationHeight(0f, 34f))); + assertThat( + resultSpecB, + hasProperty( + "baseAreaByUtilization", + utilizationAllAndBiggest(4f) + ) + ); + assertThat( + resultSpecB, + hasProperty( + "quadraticMeanDiameterByUtilization", + utilizationAllAndBiggest(45.8757401f) + ) + ); + assertThat( + resultSpecB, + hasProperty( + "treesPerHectareByUtilization", + utilizationAllAndBiggest(24.1993656f) + ) + ); + + assertThat( + resultSpecB, + hasProperty( + "wholeStemVolumeByUtilization", + utilizationAllAndBiggest(47.5739288f) + ) + ); + assertThat( + resultSpecB, + hasProperty( + "closeUtilizationVolumeByUtilization", + utilizationAllAndBiggest(45.9957237f) + ) + ); + assertThat( + resultSpecB, + hasProperty( + "closeUtilizationVolumeNetOfDecayByUtilization", + utilizationAllAndBiggest(39.5351295f) + ) + ); + assertThat( + resultSpecB, + hasProperty( + "closeUtilizationVolumeNetOfDecayAndWasteByUtilization", + utilizationAllAndBiggest(37.830616f) + ) + ); + assertThat( + resultSpecB, + hasProperty( + "closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", + utilizationAllAndBiggest(36.8912659f) + ) ); app.close(); From 081f2ed4976eeab7bc9bf97d07daba22aed72286 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Wed, 14 Aug 2024 15:38:38 -0700 Subject: [PATCH 06/53] Progress on veteran processing for VRI --- .../java/ca/bc/gov/nrs/vdyp/vri/VriStart.java | 48 ++++++++++++++++--- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java b/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java index cbc502948..1b65c10a4 100644 --- a/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java +++ b/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java @@ -4,6 +4,7 @@ import java.io.Closeable; import java.io.IOException; +import java.text.MessageFormat; import java.util.Collection; import java.util.EnumSet; import java.util.HashMap; @@ -34,6 +35,7 @@ import ca.bc.gov.nrs.vdyp.common.EstimationMethods.Limits; import ca.bc.gov.nrs.vdyp.common.Utils; import ca.bc.gov.nrs.vdyp.common.ValueOrMarker; +import ca.bc.gov.nrs.vdyp.common_calculators.BaseAreaTreeDensityDiameter; import ca.bc.gov.nrs.vdyp.common_calculators.SiteIndex2Height; import ca.bc.gov.nrs.vdyp.common_calculators.custom_exceptions.CommonCalculatorException; import ca.bc.gov.nrs.vdyp.common_calculators.enumerations.SiteIndexAgeType; @@ -44,6 +46,7 @@ import ca.bc.gov.nrs.vdyp.math.FloatMath; import ca.bc.gov.nrs.vdyp.model.PolygonMode; import ca.bc.gov.nrs.vdyp.model.Region; +import ca.bc.gov.nrs.vdyp.model.UtilizationClass; import ca.bc.gov.nrs.vdyp.model.BaseVdypSite; import ca.bc.gov.nrs.vdyp.model.BaseVdypSpecies; import ca.bc.gov.nrs.vdyp.model.BaseVdypSpecies.Builder; @@ -74,6 +77,8 @@ public class VriStart extends VdypStartApplication at + veteranLayer.getAgeIncrease()) + .orElse(0f); // AGETOTLV + float yearsToBreastHeight = primarySite.flatMap(VriSite::getYearsToBreastHeight).orElse(0f); // YTBHLV + float breastHeightAge = ageTotal - yearsToBreastHeight; // AGEBHLV + float dominantHeight = primarySite.flatMap(VriSite::getHeight).orElse(0f); // HDLV + + var baseArea = veteranLayer.getBaseArea().orElseThrow(()->new StandProcessingException("Expected veteran layer to have a base area")); // BA_V1 + var treesPerHectare = veteranLayer.getTreesPerHectare().orElseThrow(()->new StandProcessingException("Expected veteran layer to have a trees per hectare")); // BA_V1 + + if (BaseAreaTreeDensityDiameter.quadMeanDiameter(baseArea, treesPerHectare) < VETERAN_MIN_DQ) { + lowDq = true; + treesPerHectare = BaseAreaTreeDensityDiameter.treesPerHectare(breastHeightAge, VETERAN_MIN_DQ); + } + float inputTreesPerHectare = treesPerHectare; // TPHInput - float ageTotal = primarySite.flatMap(VriSite::getAgeTotal).map(at->at+veteranLayer.getAgeIncrease()).orElse(0f); - float yearsToBreastHeight = primarySite.flatMap(VriSite::getYearsToBreastHeight).orElse(0f); - float breastHeightAge = ageTotal - yearsToBreastHeight; - + if(baseArea<=0) { + throw new StandProcessingException(MessageFormat.format("Veteran layer base area ({0}) was not positive", baseArea)); + } + if(treesPerHectare<=0) { + throw new StandProcessingException(MessageFormat.format("Veteran layer trees per hectare ({0}) was not positive", baseArea)); + } + var quadMeanDiameter = BaseAreaTreeDensityDiameter.quadMeanDiameter(baseArea, treesPerHectare); // DQ(0,0) + lBuilder.adaptSpecies(veteranLayer, (sBuilder,spec)->{ + applyGroups(null, AVERSION, sBuilder); + sBuilder.volumeGroup(CONFIG_LOAD_ERROR); + }); + // TODO } From 9fd007b12c1180363867283740d176756d7df468 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Fri, 16 Aug 2024 13:39:07 -0700 Subject: [PATCH 07/53] VRI veteran processing --- .../application/VdypStartApplication.java | 126 ++++++++++++ .../ca/bc/gov/nrs/vdyp/test/VdypMatchers.java | 4 + .../java/ca/bc/gov/nrs/vdyp/fip/FipStart.java | 127 +----------- .../java/ca/bc/gov/nrs/vdyp/vri/VriStart.java | 181 ++++++++++++++---- .../bc/gov/nrs/vdyp/vri/model/VriLayer.java | 2 +- .../bc/gov/nrs/vdyp/vri/model/VriSpecies.java | 3 +- .../gov/nrs/vdyp/vri/ParsersTogetherTest.java | 5 +- .../ca/bc/gov/nrs/vdyp/vri/VriStartTest.java | 57 ++---- 8 files changed, 287 insertions(+), 218 deletions(-) diff --git a/vdyp-lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplication.java b/vdyp-lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplication.java index 00a1d9340..d5c90e2fe 100644 --- a/vdyp-lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplication.java +++ b/vdyp-lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplication.java @@ -1400,4 +1400,130 @@ protected void scaleAllSummableUtilization(VdypUtilizationHolder holder, float f } } + // YUCV + protected void computeUtilizationComponentsVeteran(VdypLayer vdypLayer, BecDefinition bec) throws ProcessingException { + log.trace( + "computeUtilizationComponentsVeteran for {}, stand total age is {}", vdypLayer.getPolygonIdentifier(), + vdypLayer.getAgeTotal() + ); + + var volumeAdjustMap = Utils.>expectParsedControl( + controlMap, ControlKey.VETERAN_LAYER_VOLUME_ADJUST, java.util.Map.class + ); + try { + for (var vdypSpecies : vdypLayer.getSpecies().values()) { + + var treesPerHectareUtil = Utils.utilizationVector(); + var quadMeanDiameterUtil = Utils.utilizationVector(); + var baseAreaUtil = Utils.utilizationVector(); + var wholeStemVolumeUtil = Utils.utilizationVector(); + + var closeUtilizationVolumeUtil = Utils.utilizationVector(); + var closeUtilizationNetOfDecayUtil = Utils.utilizationVector(); + var closeUtilizationNetOfDecayAndWasteUtil = Utils.utilizationVector(); + var closeUtilizationNetOfDecayWasteAndBreakageUtil = Utils.utilizationVector(); + + var hlSp = vdypSpecies.getLoreyHeightByUtilization().getAll(); + { + var baSp = vdypSpecies.getBaseAreaByUtilization().getLarge(); + var tphSp = vdypSpecies.getTreesPerHectareByUtilization().getLarge(); + var dqSp = vdypSpecies.getQuadraticMeanDiameterByUtilization().getLarge(); + + treesPerHectareUtil.setAll(tphSp); + quadMeanDiameterUtil.setAll(dqSp); + baseAreaUtil.setAll(baSp); + wholeStemVolumeUtil.setAll(0f); + + treesPerHectareUtil.setLarge(tphSp); + quadMeanDiameterUtil.setLarge(dqSp); + baseAreaUtil.setLarge(baSp); + wholeStemVolumeUtil.setLarge(0f); + } + // AADJUSTV + var volumeAdjustCoe = volumeAdjustMap.get(vdypSpecies.getGenus()); + + var utilizationClass = UtilizationClass.OVER225; // IUC_VET + + // ADJ + var adjust = new Coefficients(new float[] { 0f, 0f, 0f, 0f }, 1); + + // EMP091 + EstimationMethods.estimateWholeStemVolume( + controlMap, utilizationClass, volumeAdjustCoe.getCoe(1), vdypSpecies.getVolumeGroup(), hlSp, + quadMeanDiameterUtil, baseAreaUtil, wholeStemVolumeUtil + ); + + adjust.setCoe(4, volumeAdjustCoe.getCoe(2)); + // EMP092 + EstimationMethods.estimateCloseUtilizationVolume( + controlMap, utilizationClass, adjust, vdypSpecies.getVolumeGroup(), hlSp, quadMeanDiameterUtil, + wholeStemVolumeUtil, closeUtilizationVolumeUtil + ); + + adjust.setCoe(4, volumeAdjustCoe.getCoe(3)); + // EMP093 + EstimationMethods.estimateNetDecayVolume( + controlMap, vdypSpecies.getGenus(), bec.getRegion(), utilizationClass, adjust, + vdypSpecies.getDecayGroup(), vdypLayer.getBreastHeightAge().orElse(0f), quadMeanDiameterUtil, + closeUtilizationVolumeUtil, closeUtilizationNetOfDecayUtil + ); + + adjust.setCoe(4, volumeAdjustCoe.getCoe(4)); + // EMP094 + final var netDecayCoeMap = Utils.>expectParsedControl( + controlMap, ControlKey.VOLUME_NET_DECAY_WASTE, java.util.Map.class + ); + final var wasteModifierMap = Utils.>expectParsedControl( + controlMap, ControlKey.WASTE_MODIFIERS, ca.bc.gov.nrs.vdyp.model.MatrixMap2.class + ); + EstimationMethods.estimateNetDecayAndWasteVolume( + bec.getRegion(), utilizationClass, adjust, vdypSpecies.getGenus(), hlSp, netDecayCoeMap, + wasteModifierMap, quadMeanDiameterUtil, closeUtilizationVolumeUtil, + closeUtilizationNetOfDecayUtil, closeUtilizationNetOfDecayAndWasteUtil + ); + + if (getId().isStart()) { + // EMP095 + EstimationMethods.estimateNetDecayWasteAndBreakageVolume( + controlMap, utilizationClass, vdypSpecies.getBreakageGroup(), quadMeanDiameterUtil, + closeUtilizationVolumeUtil, closeUtilizationNetOfDecayAndWasteUtil, + closeUtilizationNetOfDecayWasteAndBreakageUtil + ); + } + + vdypSpecies.setBaseAreaByUtilization(baseAreaUtil); + vdypSpecies.setTreesPerHectareByUtilization(treesPerHectareUtil); + vdypSpecies.setQuadraticMeanDiameterByUtilization(quadMeanDiameterUtil); + vdypSpecies.setWholeStemVolumeByUtilization(wholeStemVolumeUtil); + vdypSpecies.setCloseUtilizationVolumeByUtilization(closeUtilizationVolumeUtil); + vdypSpecies.setCloseUtilizationVolumeNetOfDecayByUtilization(closeUtilizationNetOfDecayUtil); + vdypSpecies.setCloseUtilizationVolumeNetOfDecayAndWasteByUtilization( + closeUtilizationNetOfDecayAndWasteUtil + ); + vdypSpecies.setCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( + closeUtilizationNetOfDecayWasteAndBreakageUtil + ); + + for (var accessors : UTILIZATION_VECTOR_ACCESSORS) { + UtilizationVector utilVector = (UtilizationVector) accessors.getReadMethod().invoke(vdypSpecies); + + // Set all components other than 4 to 0.0 + for (var uc : UtilizationClass.ALL_BUT_LARGEST) { + utilVector.set(uc, 0f); + } + + // Set component 0 to equal component 4. + utilVector.setAll(utilVector.getLarge()); + + accessors.getWriteMethod().invoke(vdypSpecies, utilVector); + } + } + + computeLayerUtilizationComponentsFromSpecies(vdypLayer); + + } catch (IllegalAccessException | InvocationTargetException ex) { + throw new IllegalStateException(ex); + } + } + } diff --git a/vdyp-lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java b/vdyp-lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java index 58178ab2c..171737102 100644 --- a/vdyp-lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java +++ b/vdyp-lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java @@ -584,6 +584,10 @@ public static Matcher isPolyId(String base, int year) { return allOf(instanceOf(PolygonIdentifier.class), hasProperty("base", is(base)), hasProperty("year", is(year))); } + public static Matcher isBec(String alias) { + return allOf(instanceOf(BecDefinition.class), hasProperty("alias", is(alias))); + } + public static Matcher utilization(float small, float all, float util1, float util2, float util3, float util4) { return new TypeSafeDiagnosingMatcher() { diff --git a/vdyp-lib/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipStart.java b/vdyp-lib/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipStart.java index 1c35c9f83..3de6a5ee0 100644 --- a/vdyp-lib/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipStart.java +++ b/vdyp-lib/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipStart.java @@ -654,6 +654,7 @@ VdypLayer processLayerAsVeteran(FipPolygon fipPolygon, FipLayer fipLayer) throws ); for (var vSpec : vdypSpecies.values()) { + // TODO this should probably be using estimateVeteranQuadMeanDiameter var genus = vSpec.getGenus(); var coe = vetDqMap.get(genus, region); var a0 = coe.getCoe(1); @@ -681,132 +682,6 @@ VdypLayer processLayerAsVeteran(FipPolygon fipPolygon, FipLayer fipLayer) throws return vdypLayer; } - // YUCV - private void computeUtilizationComponentsVeteran(VdypLayer vdypLayer, BecDefinition bec) - throws ProcessingException { - log.trace( - "computeUtilizationComponentsVeterany for {}, stand total age is {}", vdypLayer - .getPolygonIdentifier(), vdypLayer.getAgeTotal() - ); - - var volumeAdjustMap = Utils.>expectParsedControl( - controlMap, ControlKey.VETERAN_LAYER_VOLUME_ADJUST, Map.class - ); - try { - for (var vdypSpecies : vdypLayer.getSpecies().values()) { - - var treesPerHectareUtil = Utils.utilizationVector(); - var quadMeanDiameterUtil = Utils.utilizationVector(); - var baseAreaUtil = Utils.utilizationVector(); - var wholeStemVolumeUtil = Utils.utilizationVector(); - - var closeUtilizationVolumeUtil = Utils.utilizationVector(); - var closeUtilizationNetOfDecayUtil = Utils.utilizationVector(); - var closeUtilizationNetOfDecayAndWasteUtil = Utils.utilizationVector(); - var closeUtilizationNetOfDecayWasteAndBreakageUtil = Utils.utilizationVector(); - - var hlSp = vdypSpecies.getLoreyHeightByUtilization().getAll(); - { - var baSp = vdypSpecies.getBaseAreaByUtilization().getLarge(); - var tphSp = vdypSpecies.getTreesPerHectareByUtilization().getLarge(); - var dqSp = vdypSpecies.getQuadraticMeanDiameterByUtilization().getLarge(); - - treesPerHectareUtil.setAll(tphSp); - quadMeanDiameterUtil.setAll(dqSp); - baseAreaUtil.setAll(baSp); - wholeStemVolumeUtil.setAll(0f); - - treesPerHectareUtil.setLarge(tphSp); - quadMeanDiameterUtil.setLarge(dqSp); - baseAreaUtil.setLarge(baSp); - wholeStemVolumeUtil.setLarge(0f); - } - // AADJUSTV - var volumeAdjustCoe = volumeAdjustMap.get(vdypSpecies.getGenus()); - - var utilizationClass = UtilizationClass.OVER225; // IUC_VET - - // ADJ - var adjust = new Coefficients(new float[] { 0f, 0f, 0f, 0f }, 1); - - // EMP091 - EstimationMethods.estimateWholeStemVolume( - controlMap, utilizationClass, volumeAdjustCoe.getCoe(1), vdypSpecies - .getVolumeGroup(), hlSp, quadMeanDiameterUtil, baseAreaUtil, wholeStemVolumeUtil - ); - - adjust.setCoe(4, volumeAdjustCoe.getCoe(2)); - // EMP092 - EstimationMethods.estimateCloseUtilizationVolume( - controlMap, utilizationClass, adjust, vdypSpecies - .getVolumeGroup(), hlSp, quadMeanDiameterUtil, wholeStemVolumeUtil, closeUtilizationVolumeUtil - ); - - adjust.setCoe(4, volumeAdjustCoe.getCoe(3)); - // EMP093 - EstimationMethods.estimateNetDecayVolume( - controlMap, vdypSpecies.getGenus(), bec.getRegion(), utilizationClass, adjust, vdypSpecies - .getDecayGroup(), vdypLayer.getBreastHeightAge().orElse( - 0f - ), quadMeanDiameterUtil, closeUtilizationVolumeUtil, closeUtilizationNetOfDecayUtil - ); - - adjust.setCoe(4, volumeAdjustCoe.getCoe(4)); - // EMP094 - final var netDecayCoeMap = Utils.>expectParsedControl( - controlMap, ControlKey.VOLUME_NET_DECAY_WASTE, Map.class - ); - final var wasteModifierMap = Utils.>expectParsedControl( - controlMap, ControlKey.WASTE_MODIFIERS, MatrixMap2.class - ); - EstimationMethods.estimateNetDecayAndWasteVolume( - bec.getRegion(), utilizationClass, adjust, vdypSpecies - .getGenus(), hlSp, netDecayCoeMap, wasteModifierMap, quadMeanDiameterUtil, closeUtilizationVolumeUtil, closeUtilizationNetOfDecayUtil, closeUtilizationNetOfDecayAndWasteUtil - ); - - if (getId().isStart()) { - // EMP095 - EstimationMethods.estimateNetDecayWasteAndBreakageVolume( - controlMap, utilizationClass, vdypSpecies - .getBreakageGroup(), quadMeanDiameterUtil, closeUtilizationVolumeUtil, closeUtilizationNetOfDecayAndWasteUtil, closeUtilizationNetOfDecayWasteAndBreakageUtil - ); - } - - vdypSpecies.setBaseAreaByUtilization(baseAreaUtil); - vdypSpecies.setTreesPerHectareByUtilization(treesPerHectareUtil); - vdypSpecies.setQuadraticMeanDiameterByUtilization(quadMeanDiameterUtil); - vdypSpecies.setWholeStemVolumeByUtilization(wholeStemVolumeUtil); - vdypSpecies.setCloseUtilizationVolumeByUtilization(closeUtilizationVolumeUtil); - vdypSpecies.setCloseUtilizationVolumeNetOfDecayByUtilization(closeUtilizationNetOfDecayUtil); - vdypSpecies.setCloseUtilizationVolumeNetOfDecayAndWasteByUtilization( - closeUtilizationNetOfDecayAndWasteUtil - ); - vdypSpecies.setCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( - closeUtilizationNetOfDecayWasteAndBreakageUtil - ); - - for (var accessors : UTILIZATION_VECTOR_ACCESSORS) { - UtilizationVector utilVector = (UtilizationVector) accessors.getReadMethod().invoke(vdypSpecies); - - // Set all components other than 4 to 0.0 - for (var uc : UtilizationClass.ALL_BUT_LARGEST) { - utilVector.set(uc, 0f); - } - - // Set component 0 to equal component 4. - utilVector.setAll(utilVector.getLarge()); - - accessors.getWriteMethod().invoke(vdypSpecies, utilVector); - } - } - - computeLayerUtilizationComponentsFromSpecies(vdypLayer); - - } catch (IllegalAccessException | InvocationTargetException ex) { - throw new IllegalStateException(ex); - } - } - // FIP_GET protected FipPolygon getPolygon( StreamingParser polyStream, StreamingParser> layerStream, diff --git a/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java b/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java index 1b301fbc7..a8bf4ec6d 100644 --- a/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java +++ b/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java @@ -1,6 +1,8 @@ package ca.bc.gov.nrs.vdyp.vri; import static ca.bc.gov.nrs.vdyp.common_calculators.BaseAreaTreeDensityDiameter.*; +import static ca.bc.gov.nrs.vdyp.math.FloatMath.pow; +import static java.lang.Math.max; import java.io.Closeable; import java.io.IOException; @@ -78,6 +80,7 @@ public class VriStart extends VdypStartApplication site.getSiteGenus()).collect(Collectors.joining(", ")); var layerType = sites.iterator().next().getLayerType(); throw validationError( - "Site entries reference species %s of layer %s of polygon %s but they are not present.", specNames, - layerType, polygon.getPolygonIdentifier() + "Site entries reference species %s of layer %s of polygon %s but they are not present.", specNames, layerType, polygon + .getPolygonIdentifier() ); } @@ -224,8 +227,8 @@ VriPolygon getPolygon( for (var layer : layers.values()) { if (!layer.getPolygonIdentifier().equals(polygon.getPolygonIdentifier())) { throw validationError( - "Record in layer file contains layer for polygon %s when expecting one for %s.", - layer.getPolygonIdentifier(), polygon.getPolygonIdentifier() + "Record in layer file contains layer for polygon %s when expecting one for %s.", layer + .getPolygonIdentifier(), polygon.getPolygonIdentifier() ); } layer.setSpecies(new HashMap<>()); @@ -297,8 +300,8 @@ private void modifyVeteranLayerBuild( float crownClosure = builder.getCrownClosure().filter(x -> x > 0f).orElseThrow( () -> new RuntimeStandProcessingException( validationError( - "Expected a positive crown closure for veteran layer but was %s", - Utils.optNa(builder.getCrownClosure()) + "Expected a positive crown closure for veteran layer but was %s", Utils + .optNa(builder.getCrownClosure()) ) ) ); @@ -397,6 +400,7 @@ Optional processPolygon(int polygonsRead, VriPolygon polygon) throw })); result.ifPresent(resultPoly -> { var resultPrimaryLayer = resultPoly.getLayers().get(LayerType.PRIMARY); + var resultVeteranLayer = resultPoly.getLayers().get(LayerType.VETERAN); try { getDqBySpecies(resultPrimaryLayer, bec.getRegion()); @@ -404,10 +408,16 @@ Optional processPolygon(int polygonsRead, VriPolygon polygon) throw estimateSmallComponents(polygon, resultPrimaryLayer); computeUtilizationComponentsPrimary( - bec, resultPrimaryLayer, VolumeComputeMode.BY_UTIL_WITH_WHOLE_STEM_BY_SPEC, - CompatibilityVariableMode.NONE + bec, resultPrimaryLayer, VolumeComputeMode.BY_UTIL_WITH_WHOLE_STEM_BY_SPEC, CompatibilityVariableMode.NONE ); + if (resultVeteranLayer != null) { + + // YUCV + computeUtilizationComponentsVeteran(resultVeteranLayer, bec); + + + } } catch (ProcessingException e) { throw new RuntimeProcessingException(e); } @@ -554,8 +564,7 @@ void getDqBySpecies(VdypLayer layer, Region region) throws ProcessingException { } findRootForQuadMeanDiameterFractionalError( - -0.6f, 0.5f, resultsPerSpecies, initialDqEstimate, baseAreaPerSpecies, minPerSpecies, maxPerSpecies, - treeDensityTotal + -0.6f, 0.5f, resultsPerSpecies, initialDqEstimate, baseAreaPerSpecies, minPerSpecies, maxPerSpecies, treeDensityTotal ); applyDqBySpecies(layer, baseAreaTotal, baseAreaPerSpecies, resultsPerSpecies); @@ -589,8 +598,8 @@ void getDqBySpeciesInitial( for (var spec : layer.getSpecies().values()) { // EMP060 float specDq = estimationMethods.estimateQuadMeanDiameterForSpecies( - spec, layer.getSpecies(), region, quadMeanDiameterTotal, baseAreaTotal, treeDensityTotal, - loreyHeightTotal + spec, layer + .getSpecies(), region, quadMeanDiameterTotal, baseAreaTotal, treeDensityTotal, loreyHeightTotal ); var limits = getLimitsForSpecies(spec, region); @@ -641,7 +650,7 @@ float quadMeanDiameterSpeciesAdjust(float x, float initialDq, float min, float m return FloatMath.clamp(7.5f + (initialDq - 7.5f) * FloatMath.exp(x), min, max); } - private void processVeteranLayer(VriPolygon polygon, VdypLayer.Builder lBuilder) throws StandProcessingException { + private float processVeteranLayer(VriPolygon polygon, VdypLayer.Builder lBuilder) throws StandProcessingException { @SuppressWarnings("unused") var veteranLayer = polygon.getLayers().get(LayerType.VETERAN); lBuilder.layerType(LayerType.VETERAN); @@ -664,12 +673,10 @@ private void processVeteranLayer(VriPolygon polygon, VdypLayer.Builder lBuilder) var baseArea = veteranLayer.getBaseArea() .orElseThrow(() -> new StandProcessingException("Expected veteran layer to have a base area")); // BA_V1 var treesPerHectare = veteranLayer.getTreesPerHectare() - .orElseThrow(() -> new StandProcessingException("Expected veteran layer to have a trees per hectare")); // BA_V1 + .orElseThrow(() -> new StandProcessingException("Expected veteran layer to have a trees per hectare")); // TPH_V1 + + treesPerHectare = enforceMinimumDiameter(baseArea, treesPerHectare); - if (BaseAreaTreeDensityDiameter.quadMeanDiameter(baseArea, treesPerHectare) < VETERAN_MIN_DQ) { - lowDq = true; - treesPerHectare = BaseAreaTreeDensityDiameter.treesPerHectare(breastHeightAge, VETERAN_MIN_DQ); - } float inputTreesPerHectare = treesPerHectare; // TPHInput if (baseArea <= 0) { @@ -685,11 +692,106 @@ private void processVeteranLayer(VriPolygon polygon, VdypLayer.Builder lBuilder) var quadMeanDiameter = BaseAreaTreeDensityDiameter.quadMeanDiameter(baseArea, treesPerHectare); // DQ(0,0) lBuilder.adaptSpecies(veteranLayer, (sBuilder, spec) -> { - applyGroups(null, AVERSION, sBuilder); - sBuilder.volumeGroup(CONFIG_LOAD_ERROR); + applyGroups(bec, spec.getGenus(), sBuilder); + if (primarySpecies.map(spec::equals).orElse(false)) { + sBuilder.addSite(iBuilder -> { + iBuilder.adapt(primarySite.get()); + iBuilder.ageTotal(ageTotal); + iBuilder.yearsToBreastHeight(yearsToBreastHeight); + }); + } + float specHeight = spec.getSite().flatMap(VriSite::getHeight).filter(x -> x > 0).orElse(dominantHeight); + float specBaseArea = spec.getFractionGenus() * baseArea; + float specQuadMeanDiameter = max( + estimateVeteranQuadMeanDiameter(spec.getGenus(), bec, specHeight), VETERAN_MIN_HL + ); + float specTreeDensity = BaseAreaTreeDensityDiameter.treesPerHectare(specBaseArea, specQuadMeanDiameter); + sBuilder.loreyHeight(specHeight); + sBuilder.baseArea(specBaseArea); + sBuilder.quadMeanDiameter(specQuadMeanDiameter); }); - // TODO + lBuilder.buildChildren(); + var specList = lBuilder.getSpecies(); + + // Really crude reconciliation to the specified TPH (if any) + // Could improve with code similar to ROOTV01 + if (treesPerHectare > 0) { + float tphSum = 0; + for (var spec : specList) { + tphSum += spec.getTreesPerHectareByUtilization().getAll(); + } + float k = treesPerHectare / tphSum; + for (var spec : specList) { + float specBaseArea = spec.getBaseAreaByUtilization().getLarge(); + float specTreesPerHectare = spec.getTreesPerHectareByUtilization().getLarge() * k; + float specQuadMeanDiameter = BaseAreaTreeDensityDiameter + .quadMeanDiameter(specBaseArea, specTreesPerHectare); + if (specQuadMeanDiameter < VETERAN_MIN_DQ) { + specQuadMeanDiameter = VETERAN_MIN_DQ; + specTreesPerHectare = BaseAreaTreeDensityDiameter + .treesPerHectare(specBaseArea, specQuadMeanDiameter); + } + spec.setTreesPerHectareByUtilization(Utils.utilizationVector(specTreesPerHectare)); + spec.setQuadraticMeanDiameterByUtilization(Utils.utilizationVector(specQuadMeanDiameter)); + } + + } + + // Sum BA and TPH + float tphSum = 0; // TPH(0,4) + float baSum = 0; // BA(0,4) + for (var spec : specList) { + tphSum += spec.getTreesPerHectareByUtilization().getAll(); + baSum += spec.getBaseAreaByUtilization().getAll(); + } + + if (polygon.getMode().filter(mode -> mode == PolygonMode.BATC).isPresent()) { + // for mode 4 (with CC) input TPH was NOT available. + inputTreesPerHectare = tphSum; + treesPerHectare = tphSum; + } + + return inputTreesPerHectare; + } + + static final String MINIMUM_DIAMETER_BASE_MESSAGE = "Quadratic mean diameter {0} cm was lower than {1} cm"; + static final String MINIMUM_DIAMETER_WARN_MESSAGE = MINIMUM_DIAMETER_BASE_MESSAGE+", raising tree density to {2} trees/ha"; + static final String MINIMUM_DIAMETER_FAIL_MESSAGE = MINIMUM_DIAMETER_BASE_MESSAGE+"."; + + + private Float enforceMinimumDiameter(Float baseArea, Float treesPerHectare) throws StandProcessingException { + final float quadMeanDiameter = BaseAreaTreeDensityDiameter.quadMeanDiameter(baseArea, treesPerHectare); + if (quadMeanDiameter < VETERAN_MIN_DQ) { + if (this.getId() == VdypApplicationIdentifier.VRI_START && this.getDebugMode(1) == 2) { + throw new StandProcessingException( + MessageFormat.format( + MINIMUM_DIAMETER_FAIL_MESSAGE, + quadMeanDiameter, VETERAN_MIN_DQ + ) + ); + } + treesPerHectare = BaseAreaTreeDensityDiameter.treesPerHectare(baseArea, VETERAN_MIN_DQ); + log.atWarn() + .setMessage( + MINIMUM_DIAMETER_WARN_MESSAGE + ).addArgument(treesPerHectare); + } + return treesPerHectare; + } + + // EMP097 TODO move to EstimationMethods and this should probably be used in FipStart + + float estimateVeteranQuadMeanDiameter(String sp0, BecDefinition bec, float loreyHeight) { + var vetDqMap = Utils.>expectParsedControl( + controlMap, ControlKey.VETERAN_LAYER_DQ, MatrixMap2.class + ); + var coe = vetDqMap.get(sp0, bec.getRegion()); + var a0 = coe.getCoe(1); + var a1 = coe.getCoe(2); + var a2 = coe.getCoe(3); + + return a0 + a1 * pow(loreyHeight, a2); } // VRI_CHK @@ -771,9 +873,8 @@ protected PolygonMode checkPolygonForMode(VriPolygon polygon, BecDefinition bec) PolygonMode mode = polygon.getMode().orElseGet(() -> { try { return findDefaultPolygonMode( - ageTotal, yearsToBreastHeight, height, baseArea, treesPerHectare, percentForest, - primaryLayer.getSpecies().values(), bec, - primaryLayer.getEmpericalRelationshipParameterIndex() + ageTotal, yearsToBreastHeight, height, baseArea, treesPerHectare, percentForest, primaryLayer + .getSpecies().values(), bec, primaryLayer.getEmpericalRelationshipParameterIndex() ); } catch (StandProcessingException e) { throw new RuntimeStandProcessingException(e); @@ -781,8 +882,8 @@ protected PolygonMode checkPolygonForMode(VriPolygon polygon, BecDefinition bec) }); polygon.setMode(Optional.of(mode)); Optional primaryBreastHeightAge = Utils.mapBoth( - primaryLayer.getPrimarySite().flatMap(VriSite::getAgeTotal), - primaryLayer.getPrimarySite().flatMap(VriSite::getYearsToBreastHeight), (at, ytbh) -> at - ytbh + primaryLayer.getPrimarySite().flatMap(VriSite::getAgeTotal), primaryLayer.getPrimarySite() + .flatMap(VriSite::getYearsToBreastHeight), (at, ytbh) -> at - ytbh ); log.atDebug().setMessage("Polygon mode {} checks").addArgument(mode).log(); switch (mode) { @@ -832,8 +933,8 @@ void validateMinimum(String fieldName, float value, float minimum, boolean inclu throws StandProcessingException { if (value < minimum || (value == minimum && !inclusive)) throw validationError( - "%s %s should be %s %s", fieldName, value, inclusive ? "greater than or equal to" : "greater than", - minimum + "%s %s should be %s %s", fieldName, value, inclusive ? "greater than or equal to" + : "greater than", minimum ); } @@ -1127,8 +1228,7 @@ VriPolygon processYoung(VriPolygon poly) throws ProcessingException { float primaryHeight = primarySite.getHeight().orElseThrow(); // HT_L1 final Increase inc = findIncreaseForYoungMode( - bec, primaryLayer, siteCurve, primaryBreastHeightAge0, siteIndex, yeastToBreastHeight, - baseAreaTarget, heightTarget, ageTarget, dominantHeight0, moreYears, primaryHeight + bec, primaryLayer, siteCurve, primaryBreastHeightAge0, siteIndex, yeastToBreastHeight, baseAreaTarget, heightTarget, ageTarget, dominantHeight0, moreYears, primaryHeight ); return VriPolygon.build(pBuilder -> { @@ -1199,9 +1299,8 @@ private Increase findIncreaseForYoungMode( // check empirical BA assuming BAV = 0 float predictedBaseArea = estimateBaseAreaYield( - dominantHeight, primaryBreastHeightAge, Optional.empty(), false, - primaryLayer.getSpecies().values(), bec, - primaryLayer.getEmpericalRelationshipParameterIndex().orElseThrow() + dominantHeight, primaryBreastHeightAge, Optional.empty(), false, primaryLayer.getSpecies() + .values(), bec, primaryLayer.getEmpericalRelationshipParameterIndex().orElseThrow() ); // BAP // Calculate the full occupancy BA Hence the BA we will test is the Full @@ -1310,14 +1409,14 @@ VriPolygon processBatn(VriPolygon poly) throws ProcessingException { ); float primaryBaseAreaEstimated = estimateBaseAreaYield( - primaryHeight, primaryBreastHeightAge, veteranBaseArea, false, primaryLayer.getSpecies().values(), bec, - primaryEmpericalRelationshipParameterIndex + primaryHeight, primaryBreastHeightAge, veteranBaseArea, false, primaryLayer.getSpecies() + .values(), bec, primaryEmpericalRelationshipParameterIndex ); // EMP107 float normativeQuadMeanDiameter = estimateQuadMeanDiameterYield( - primaryHeight, primaryBreastHeightAge, veteranBaseArea, primaryLayer.getSpecies().values(), bec, - primaryEmpericalRelationshipParameterIndex + primaryHeight, primaryBreastHeightAge, veteranBaseArea, primaryLayer.getSpecies() + .values(), bec, primaryEmpericalRelationshipParameterIndex ); final float normativePercentAvailable = 85f; diff --git a/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/model/VriLayer.java b/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/model/VriLayer.java index 0d1491bc5..e9e7a8f32 100644 --- a/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/model/VriLayer.java +++ b/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/model/VriLayer.java @@ -229,7 +229,7 @@ protected VriLayer doBuild() { Math.max(utilization.get(), 7.5f), // primaryGenus, // secondaryGenus, // - empericalRelationshipParameterIndex, + empericalRelationshipParameterIndex, // ageIncrease.orElse(DEFAULT_AGE_INCREASE) ); result.setInventoryTypeGroup(inventoryTypeGroup); diff --git a/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/model/VriSpecies.java b/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/model/VriSpecies.java index 22f35fb46..0627f88c7 100644 --- a/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/model/VriSpecies.java +++ b/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/model/VriSpecies.java @@ -73,7 +73,8 @@ public String toString() { this.getPolygonIdentifier(), // this.getLayerType(), // this.getGenus(), // - this.getPercentGenus(), this.getSp64DistributionSet().getSp64DistributionList().stream() + this.getPercentGenus(), // + this.getSp64DistributionSet().getSp64DistributionList().stream() .map(e -> String.format("%s: %s%%", e.getGenusAlias(), e.getPercentage())) .collect(Collectors.joining(", ")) ); diff --git a/vdyp-lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/ParsersTogetherTest.java b/vdyp-lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/ParsersTogetherTest.java index 16868e69f..b1cd0d895 100644 --- a/vdyp-lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/ParsersTogetherTest.java +++ b/vdyp-lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/ParsersTogetherTest.java @@ -626,9 +626,8 @@ veteranResult, allOf( hasProperty("layerType", is(LayerType.VETERAN)), // hasProperty("crownClosure", is(88f)), // hasProperty("utilization", is(8f)), // - hasProperty("baseArea", present(closeTo(expectedBaseArea))), hasProperty( - "treesPerHectare", present(closeTo(expectedTreesPerHectare)) - ) + hasProperty("baseArea", present(closeTo(expectedBaseArea))), // + hasProperty("treesPerHectare", present(closeTo(expectedTreesPerHectare))) ) ); diff --git a/vdyp-lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriStartTest.java b/vdyp-lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriStartTest.java index beae52587..18b9af0b1 100644 --- a/vdyp-lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriStartTest.java +++ b/vdyp-lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriStartTest.java @@ -5,6 +5,7 @@ import static ca.bc.gov.nrs.vdyp.test.TestUtils.assertOnlyPrimaryLayer; import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.closeTo; import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.coe; +import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.isBec; import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.isPolyId; import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.notPresent; import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.present; @@ -2316,7 +2317,7 @@ void testProcessVeteran() throws Exception { var result = app.processPolygon(0, poly).get(); assertThat(result, hasProperty("polygonIdentifier", isPolyId("TestPoly", 2024))); - assertThat(result, hasProperty("biogeoclimaticZone", is("IDF"))); + assertThat(result, hasProperty("biogeoclimaticZone", isBec("IDF"))); assertThat(result, hasProperty("forestInventoryZone", blankString())); assertThat(result, hasProperty("mode", present(is(PolygonMode.START)))); assertThat(result, hasProperty("percentAvailable", is(85f))); @@ -2376,25 +2377,16 @@ primaryLayer, hasProperty("loreyHeightByUtilization", utilizationHeight(5.457702 assertThat(veteranLayer, hasProperty("siteGenus", present(is("H")))); assertThat(veteranLayer, hasProperty("height", present(closeTo(34f)))); - assertThat(veteranLayer, hasProperty("inventoryTypeGroup", notPresent())); // ? + assertThat(veteranLayer, hasProperty("inventoryTypeGroup", present(is(14)))); // ? assertThat(veteranLayer, hasProperty("empiricalRelationshipParameterIndex", notPresent())); // ? assertThat(veteranLayer, hasProperty("loreyHeightByUtilization", utilizationHeight(0f, 32.8f))); assertThat(veteranLayer, hasProperty("baseAreaByUtilization", utilizationAllAndBiggest(20f))); assertThat( veteranLayer, - hasProperty( - "quadraticMeanDiameterByUtilization", - utilizationAllAndBiggest(45.5006409f) - ) - ); - assertThat( - veteranLayer, - hasProperty( - "treesPerHectareByUtilization", - utilizationAllAndBiggest(123f) - ) + hasProperty("quadraticMeanDiameterByUtilization", utilizationAllAndBiggest(45.5006409f)) ); + assertThat(veteranLayer, hasProperty("treesPerHectareByUtilization", utilizationAllAndBiggest(123f))); assertThat( veteranLayer, @@ -2407,48 +2399,21 @@ primaryLayer, hasProperty("loreyHeightByUtilization", utilizationHeight(5.457702 VdypSpecies resultSpecB = TestUtils.assertHasSpecies(veteranLayer, "B", "C", "H"); assertThat(resultSpecB, hasProperty("loreyHeightByUtilization", utilizationHeight(0f, 34f))); + assertThat(resultSpecB, hasProperty("baseAreaByUtilization", utilizationAllAndBiggest(4f))); assertThat( resultSpecB, - hasProperty( - "baseAreaByUtilization", - utilizationAllAndBiggest(4f) - ) - ); - assertThat( - resultSpecB, - hasProperty( - "quadraticMeanDiameterByUtilization", - utilizationAllAndBiggest(45.8757401f) - ) - ); - assertThat( - resultSpecB, - hasProperty( - "treesPerHectareByUtilization", - utilizationAllAndBiggest(24.1993656f) - ) + hasProperty("quadraticMeanDiameterByUtilization", utilizationAllAndBiggest(45.8757401f)) ); + assertThat(resultSpecB, hasProperty("treesPerHectareByUtilization", utilizationAllAndBiggest(24.1993656f))); + assertThat(resultSpecB, hasProperty("wholeStemVolumeByUtilization", utilizationAllAndBiggest(47.5739288f))); assertThat( resultSpecB, - hasProperty( - "wholeStemVolumeByUtilization", - utilizationAllAndBiggest(47.5739288f) - ) - ); - assertThat( - resultSpecB, - hasProperty( - "closeUtilizationVolumeByUtilization", - utilizationAllAndBiggest(45.9957237f) - ) + hasProperty("closeUtilizationVolumeByUtilization", utilizationAllAndBiggest(45.9957237f)) ); assertThat( resultSpecB, - hasProperty( - "closeUtilizationVolumeNetOfDecayByUtilization", - utilizationAllAndBiggest(39.5351295f) - ) + hasProperty("closeUtilizationVolumeNetOfDecayByUtilization", utilizationAllAndBiggest(39.5351295f)) ); assertThat( resultSpecB, From 50ef65c00897b41aec5870b0074dc543574d87c2 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Thu, 22 Aug 2024 13:21:46 -0700 Subject: [PATCH 08/53] Veteran VRI processing works on standard code path --- .../application/VdypStartApplication.java | 43 +++++++-------- .../java/ca/bc/gov/nrs/vdyp/vri/VriStart.java | 24 ++++----- .../ca/bc/gov/nrs/vdyp/vri/VriStartTest.java | 52 +++++++++---------- 3 files changed, 57 insertions(+), 62 deletions(-) diff --git a/vdyp-lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplication.java b/vdyp-lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplication.java index d5c90e2fe..f0f2454bf 100644 --- a/vdyp-lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplication.java +++ b/vdyp-lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplication.java @@ -1401,39 +1401,40 @@ protected void scaleAllSummableUtilization(VdypUtilizationHolder holder, float f } // YUCV - protected void computeUtilizationComponentsVeteran(VdypLayer vdypLayer, BecDefinition bec) throws ProcessingException { + protected void computeUtilizationComponentsVeteran(VdypLayer vdypLayer, BecDefinition bec) + throws ProcessingException { log.trace( "computeUtilizationComponentsVeteran for {}, stand total age is {}", vdypLayer.getPolygonIdentifier(), vdypLayer.getAgeTotal() ); - + var volumeAdjustMap = Utils.>expectParsedControl( controlMap, ControlKey.VETERAN_LAYER_VOLUME_ADJUST, java.util.Map.class ); try { for (var vdypSpecies : vdypLayer.getSpecies().values()) { - + var treesPerHectareUtil = Utils.utilizationVector(); var quadMeanDiameterUtil = Utils.utilizationVector(); var baseAreaUtil = Utils.utilizationVector(); var wholeStemVolumeUtil = Utils.utilizationVector(); - + var closeUtilizationVolumeUtil = Utils.utilizationVector(); var closeUtilizationNetOfDecayUtil = Utils.utilizationVector(); var closeUtilizationNetOfDecayAndWasteUtil = Utils.utilizationVector(); var closeUtilizationNetOfDecayWasteAndBreakageUtil = Utils.utilizationVector(); - + var hlSp = vdypSpecies.getLoreyHeightByUtilization().getAll(); { var baSp = vdypSpecies.getBaseAreaByUtilization().getLarge(); var tphSp = vdypSpecies.getTreesPerHectareByUtilization().getLarge(); var dqSp = vdypSpecies.getQuadraticMeanDiameterByUtilization().getLarge(); - + treesPerHectareUtil.setAll(tphSp); quadMeanDiameterUtil.setAll(dqSp); baseAreaUtil.setAll(baSp); wholeStemVolumeUtil.setAll(0f); - + treesPerHectareUtil.setLarge(tphSp); quadMeanDiameterUtil.setLarge(dqSp); baseAreaUtil.setLarge(baSp); @@ -1441,25 +1442,25 @@ protected void computeUtilizationComponentsVeteran(VdypLayer vdypLayer, BecDefin } // AADJUSTV var volumeAdjustCoe = volumeAdjustMap.get(vdypSpecies.getGenus()); - + var utilizationClass = UtilizationClass.OVER225; // IUC_VET - + // ADJ var adjust = new Coefficients(new float[] { 0f, 0f, 0f, 0f }, 1); - + // EMP091 EstimationMethods.estimateWholeStemVolume( controlMap, utilizationClass, volumeAdjustCoe.getCoe(1), vdypSpecies.getVolumeGroup(), hlSp, quadMeanDiameterUtil, baseAreaUtil, wholeStemVolumeUtil ); - + adjust.setCoe(4, volumeAdjustCoe.getCoe(2)); // EMP092 EstimationMethods.estimateCloseUtilizationVolume( controlMap, utilizationClass, adjust, vdypSpecies.getVolumeGroup(), hlSp, quadMeanDiameterUtil, wholeStemVolumeUtil, closeUtilizationVolumeUtil ); - + adjust.setCoe(4, volumeAdjustCoe.getCoe(3)); // EMP093 EstimationMethods.estimateNetDecayVolume( @@ -1467,7 +1468,7 @@ protected void computeUtilizationComponentsVeteran(VdypLayer vdypLayer, BecDefin vdypSpecies.getDecayGroup(), vdypLayer.getBreastHeightAge().orElse(0f), quadMeanDiameterUtil, closeUtilizationVolumeUtil, closeUtilizationNetOfDecayUtil ); - + adjust.setCoe(4, volumeAdjustCoe.getCoe(4)); // EMP094 final var netDecayCoeMap = Utils.>expectParsedControl( @@ -1481,7 +1482,7 @@ protected void computeUtilizationComponentsVeteran(VdypLayer vdypLayer, BecDefin wasteModifierMap, quadMeanDiameterUtil, closeUtilizationVolumeUtil, closeUtilizationNetOfDecayUtil, closeUtilizationNetOfDecayAndWasteUtil ); - + if (getId().isStart()) { // EMP095 EstimationMethods.estimateNetDecayWasteAndBreakageVolume( @@ -1490,7 +1491,7 @@ protected void computeUtilizationComponentsVeteran(VdypLayer vdypLayer, BecDefin closeUtilizationNetOfDecayWasteAndBreakageUtil ); } - + vdypSpecies.setBaseAreaByUtilization(baseAreaUtil); vdypSpecies.setTreesPerHectareByUtilization(treesPerHectareUtil); vdypSpecies.setQuadraticMeanDiameterByUtilization(quadMeanDiameterUtil); @@ -1503,24 +1504,24 @@ protected void computeUtilizationComponentsVeteran(VdypLayer vdypLayer, BecDefin vdypSpecies.setCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( closeUtilizationNetOfDecayWasteAndBreakageUtil ); - + for (var accessors : UTILIZATION_VECTOR_ACCESSORS) { UtilizationVector utilVector = (UtilizationVector) accessors.getReadMethod().invoke(vdypSpecies); - + // Set all components other than 4 to 0.0 for (var uc : UtilizationClass.ALL_BUT_LARGEST) { utilVector.set(uc, 0f); } - + // Set component 0 to equal component 4. utilVector.setAll(utilVector.getLarge()); - + accessors.getWriteMethod().invoke(vdypSpecies, utilVector); } } - + computeLayerUtilizationComponentsFromSpecies(vdypLayer); - + } catch (IllegalAccessException | InvocationTargetException ex) { throw new IllegalStateException(ex); } diff --git a/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java b/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java index a8bf4ec6d..9ee56bd23 100644 --- a/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java +++ b/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java @@ -415,8 +415,7 @@ Optional processPolygon(int polygonsRead, VriPolygon polygon) throw // YUCV computeUtilizationComponentsVeteran(resultVeteranLayer, bec); - - + } } catch (ProcessingException e) { throw new RuntimeProcessingException(e); @@ -703,12 +702,13 @@ private float processVeteranLayer(VriPolygon polygon, VdypLayer.Builder lBuilder float specHeight = spec.getSite().flatMap(VriSite::getHeight).filter(x -> x > 0).orElse(dominantHeight); float specBaseArea = spec.getFractionGenus() * baseArea; float specQuadMeanDiameter = max( - estimateVeteranQuadMeanDiameter(spec.getGenus(), bec, specHeight), VETERAN_MIN_HL + estimateVeteranQuadMeanDiameter(spec.getGenus(), bec, specHeight), VETERAN_MIN_DQ ); float specTreeDensity = BaseAreaTreeDensityDiameter.treesPerHectare(specBaseArea, specQuadMeanDiameter); sBuilder.loreyHeight(specHeight); sBuilder.baseArea(specBaseArea); sBuilder.quadMeanDiameter(specQuadMeanDiameter); + sBuilder.treesPerHectare(specTreeDensity); }); lBuilder.buildChildren(); @@ -754,28 +754,22 @@ private float processVeteranLayer(VriPolygon polygon, VdypLayer.Builder lBuilder return inputTreesPerHectare; } - - static final String MINIMUM_DIAMETER_BASE_MESSAGE = "Quadratic mean diameter {0} cm was lower than {1} cm"; - static final String MINIMUM_DIAMETER_WARN_MESSAGE = MINIMUM_DIAMETER_BASE_MESSAGE+", raising tree density to {2} trees/ha"; - static final String MINIMUM_DIAMETER_FAIL_MESSAGE = MINIMUM_DIAMETER_BASE_MESSAGE+"."; + static final String MINIMUM_DIAMETER_BASE_MESSAGE = "Quadratic mean diameter {0} cm was lower than {1} cm"; + static final String MINIMUM_DIAMETER_WARN_MESSAGE = MINIMUM_DIAMETER_BASE_MESSAGE + + ", raising tree density to {2} trees/ha"; + static final String MINIMUM_DIAMETER_FAIL_MESSAGE = MINIMUM_DIAMETER_BASE_MESSAGE + "."; private Float enforceMinimumDiameter(Float baseArea, Float treesPerHectare) throws StandProcessingException { final float quadMeanDiameter = BaseAreaTreeDensityDiameter.quadMeanDiameter(baseArea, treesPerHectare); if (quadMeanDiameter < VETERAN_MIN_DQ) { if (this.getId() == VdypApplicationIdentifier.VRI_START && this.getDebugMode(1) == 2) { throw new StandProcessingException( - MessageFormat.format( - MINIMUM_DIAMETER_FAIL_MESSAGE, - quadMeanDiameter, VETERAN_MIN_DQ - ) + MessageFormat.format(MINIMUM_DIAMETER_FAIL_MESSAGE, quadMeanDiameter, VETERAN_MIN_DQ) ); } treesPerHectare = BaseAreaTreeDensityDiameter.treesPerHectare(baseArea, VETERAN_MIN_DQ); - log.atWarn() - .setMessage( - MINIMUM_DIAMETER_WARN_MESSAGE - ).addArgument(treesPerHectare); + log.atWarn().setMessage(MINIMUM_DIAMETER_WARN_MESSAGE).addArgument(treesPerHectare); } return treesPerHectare; } diff --git a/vdyp-lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriStartTest.java b/vdyp-lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriStartTest.java index 18b9af0b1..ed36b420c 100644 --- a/vdyp-lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriStartTest.java +++ b/vdyp-lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriStartTest.java @@ -2370,32 +2370,6 @@ primaryLayer, hasProperty("loreyHeightByUtilization", utilizationHeight(5.457702 var veteranLayer = assertHasVeteranLayer(result); - assertThat(veteranLayer, hasProperty("ageTotal", present(closeTo(200)))); - assertThat(veteranLayer, hasProperty("breastHeightAge", present(closeTo(190.3f)))); - assertThat(veteranLayer, hasProperty("yearsToBreastHeight", present(closeTo(9.7f)))); - - assertThat(veteranLayer, hasProperty("siteGenus", present(is("H")))); - - assertThat(veteranLayer, hasProperty("height", present(closeTo(34f)))); - assertThat(veteranLayer, hasProperty("inventoryTypeGroup", present(is(14)))); // ? - assertThat(veteranLayer, hasProperty("empiricalRelationshipParameterIndex", notPresent())); // ? - - assertThat(veteranLayer, hasProperty("loreyHeightByUtilization", utilizationHeight(0f, 32.8f))); - assertThat(veteranLayer, hasProperty("baseAreaByUtilization", utilizationAllAndBiggest(20f))); - assertThat( - veteranLayer, - hasProperty("quadraticMeanDiameterByUtilization", utilizationAllAndBiggest(45.5006409f)) - ); - assertThat(veteranLayer, hasProperty("treesPerHectareByUtilization", utilizationAllAndBiggest(123f))); - - assertThat( - veteranLayer, - hasProperty( - "closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", - utilizationAllAndBiggest(167.61972f) - ) - ); - VdypSpecies resultSpecB = TestUtils.assertHasSpecies(veteranLayer, "B", "C", "H"); assertThat(resultSpecB, hasProperty("loreyHeightByUtilization", utilizationHeight(0f, 34f))); @@ -2430,6 +2404,32 @@ primaryLayer, hasProperty("loreyHeightByUtilization", utilizationHeight(5.457702 ) ); + assertThat(veteranLayer, hasProperty("ageTotal", present(closeTo(200)))); + assertThat(veteranLayer, hasProperty("breastHeightAge", present(closeTo(190.3f)))); + assertThat(veteranLayer, hasProperty("yearsToBreastHeight", present(closeTo(9.7f)))); + + assertThat(veteranLayer, hasProperty("siteGenus", present(is("H")))); + + assertThat(veteranLayer, hasProperty("height", present(closeTo(34f)))); + assertThat(veteranLayer, hasProperty("inventoryTypeGroup", present(is(14)))); // ? + assertThat(veteranLayer, hasProperty("empiricalRelationshipParameterIndex", notPresent())); // ? + + assertThat(veteranLayer, hasProperty("loreyHeightByUtilization", utilizationHeight(0f, 32.8f))); + assertThat(veteranLayer, hasProperty("baseAreaByUtilization", utilizationAllAndBiggest(20f))); + assertThat( + veteranLayer, + hasProperty("quadraticMeanDiameterByUtilization", utilizationAllAndBiggest(45.5006409f)) + ); + assertThat(veteranLayer, hasProperty("treesPerHectareByUtilization", utilizationAllAndBiggest(123f))); + + assertThat( + veteranLayer, + hasProperty( + "closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", + utilizationAllAndBiggest(167.61972f) + ) + ); + app.close(); } From d359c68791ad1fecce256125c2d692b14f45f7fd Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Fri, 23 Aug 2024 12:55:57 -0700 Subject: [PATCH 09/53] Unit test for computeUtilizationComponentsVeteran --- .../application/VdypStartApplicationTest.java | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/vdyp-lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplicationTest.java b/vdyp-lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplicationTest.java index 92f0ba02a..e49adc7f8 100644 --- a/vdyp-lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplicationTest.java +++ b/vdyp-lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplicationTest.java @@ -3,7 +3,10 @@ import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.causedBy; import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.closeTo; import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.coe; +import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.notPresent; +import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.present; import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.utilization; +import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.utilizationAllAndBiggest; import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.utilizationHeight; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; @@ -1327,6 +1330,145 @@ void testEstimate() throws ProcessingException, IOException { } } + @Nested + class VeteranUtilization { + @Test + void testCompute() throws ProcessingException, IOException { + controlMap = TestUtils.loadControlMap(); + var bec = Utils.getBec("IDF", controlMap); + try (var app = new TestStartApplication(controlMap, false)) { + ApplicationTestUtils.setControlMap(app, controlMap); + + var layer = VdypLayer.build(lb -> { + lb.polygonIdentifier("Test", 2024); + lb.layerType(LayerType.VETERAN); + lb.inventoryTypeGroup(14); + lb.addSpecies(sb -> { + sb.genus("B", controlMap); + sb.percentGenus(20f); + + sb.volumeGroup(15); + sb.decayGroup(11); + sb.breakageGroup(4); + + sb.loreyHeight(34f); + sb.baseArea(4f); + sb.treesPerHectare(24.199366f); + sb.quadMeanDiameter(45.87574f); + + sb.addSp64Distribution("BL", 100); + }); + lb.addSpecies(sb -> { + sb.genus("C", controlMap); + sb.percentGenus(30f); + + sb.volumeGroup(23); + sb.decayGroup(15); + sb.breakageGroup(10); + + sb.loreyHeight(30f); + sb.baseArea(6f); + sb.treesPerHectare(40.991108f); + sb.quadMeanDiameter(43.17038f); + + sb.addSp64Distribution("CW", 100); + }); + lb.addSpecies(sb -> { + sb.genus("H", controlMap); + sb.percentGenus(50f); + + sb.volumeGroup(40); + sb.decayGroup(33); + sb.breakageGroup(19); + + sb.loreyHeight(34f); + sb.baseArea(10f); + sb.treesPerHectare(57.809525f); + sb.quadMeanDiameter(46.93052f); + + sb.addSite(siteBuilder -> { + siteBuilder.ageTotal(200f); + siteBuilder.yearsToBreastHeight(9.7f); + siteBuilder.height(34f); + siteBuilder.siteCurveNumber(37); + siteBuilder.siteIndex(14.6f); + }); + + }); + + }); + + app.computeUtilizationComponentsVeteran(layer, bec); + + VdypSpecies resultSpecB = TestUtils.assertHasSpecies(layer, "B", "C", "H"); + + assertThat(resultSpecB, hasProperty("loreyHeightByUtilization", utilizationHeight(0f, 34f))); + assertThat(resultSpecB, hasProperty("baseAreaByUtilization", utilizationAllAndBiggest(4f))); + assertThat( + resultSpecB, + hasProperty("quadraticMeanDiameterByUtilization", utilizationAllAndBiggest(45.8757401f)) + ); + assertThat( + resultSpecB, hasProperty("treesPerHectareByUtilization", utilizationAllAndBiggest(24.1993656f)) + ); + + assertThat( + resultSpecB, hasProperty("wholeStemVolumeByUtilization", utilizationAllAndBiggest(47.5739288f)) + ); + assertThat( + resultSpecB, + hasProperty("closeUtilizationVolumeByUtilization", utilizationAllAndBiggest(45.9957237f)) + ); + assertThat( + resultSpecB, + hasProperty( + "closeUtilizationVolumeNetOfDecayByUtilization", utilizationAllAndBiggest(39.5351295f) + ) + ); + assertThat( + resultSpecB, + hasProperty( + "closeUtilizationVolumeNetOfDecayAndWasteByUtilization", + utilizationAllAndBiggest(37.830616f) + ) + ); + assertThat( + resultSpecB, + hasProperty( + "closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", + utilizationAllAndBiggest(36.8912659f) + ) + ); + + assertThat(layer, hasProperty("ageTotal", present(closeTo(200)))); + assertThat(layer, hasProperty("breastHeightAge", present(closeTo(190.3f)))); + assertThat(layer, hasProperty("yearsToBreastHeight", present(closeTo(9.7f)))); + + assertThat(layer, hasProperty("siteGenus", present(is("H")))); + + assertThat(layer, hasProperty("height", present(closeTo(34f)))); + assertThat(layer, hasProperty("inventoryTypeGroup", present(is(14)))); // ? + assertThat(layer, hasProperty("empiricalRelationshipParameterIndex", notPresent())); // ? + + assertThat(layer, hasProperty("loreyHeightByUtilization", utilizationHeight(0f, 32.8f))); + assertThat(layer, hasProperty("baseAreaByUtilization", utilizationAllAndBiggest(20f))); + assertThat( + layer, hasProperty("quadraticMeanDiameterByUtilization", utilizationAllAndBiggest(45.5006409f)) + ); + assertThat(layer, hasProperty("treesPerHectareByUtilization", utilizationAllAndBiggest(123f))); + + assertThat( + layer, + hasProperty( + "closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", + utilizationAllAndBiggest(167.61972f) + ) + ); + + } + } + } + @Nested class ApplyGroups { From 0c0a71e7e0c5a5835a30623dca455e1159744238 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Wed, 28 Aug 2024 14:52:59 -0700 Subject: [PATCH 10/53] Update species percent based on computed base areas --- .../bc/gov/nrs/vdyp/application/VdypStartApplication.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/vdyp-lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplication.java b/vdyp-lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplication.java index f0f2454bf..504319af6 100644 --- a/vdyp-lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplication.java +++ b/vdyp-lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplication.java @@ -1360,6 +1360,12 @@ protected void computeLayerUtilizationComponentsFromSpecies(VdypLayer vdypLayer) }); var ba = vdypLayer.getBaseAreaByUtilization(); hlVector.scalarInPlace((float x, UtilizationClass uc) -> ba.get(uc) > 0 ? x / ba.get(uc) : x); + + // Update percent based on updated areas + vdypLayer.getSpecies().values().stream().forEach(spec -> { + spec.setPercentGenus(100 * spec.getBaseAreaByUtilization().getAll() / ba.getAll()); + }); + vdypLayer.setLoreyHeightByUtilization(hlVector); } // Quadratic mean diameter for the layer is computed from the BA and TPH after @@ -1370,6 +1376,7 @@ protected void computeLayerUtilizationComponentsFromSpecies(VdypLayer vdypLayer) ); vdypLayer.setQuadraticMeanDiameterByUtilization(utilVector); } + } // TODO De-reflectify this when we want to make it work in GralVM From d7debb037f6a3801c4ebe858bc0da16b75e92eae Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Wed, 28 Aug 2024 15:23:09 -0700 Subject: [PATCH 11/53] Remove incoreect supress warmings annotation for unused, remove unused variables --- .../bc/gov/nrs/vdyp/application/VdypStartApplicationTest.java | 2 ++ .../src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java | 4 ---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/vdyp-lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplicationTest.java b/vdyp-lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplicationTest.java index e49adc7f8..e62f4ad65 100644 --- a/vdyp-lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplicationTest.java +++ b/vdyp-lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplicationTest.java @@ -1402,6 +1402,8 @@ void testCompute() throws ProcessingException, IOException { VdypSpecies resultSpecB = TestUtils.assertHasSpecies(layer, "B", "C", "H"); + assertThat(resultSpecB, hasProperty("percentGenus", closeTo(20f))); + assertThat(resultSpecB, hasProperty("loreyHeightByUtilization", utilizationHeight(0f, 34f))); assertThat(resultSpecB, hasProperty("baseAreaByUtilization", utilizationAllAndBiggest(4f))); assertThat( diff --git a/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java b/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java index 9ee56bd23..80a8012ad 100644 --- a/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java +++ b/vdyp-lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java @@ -650,7 +650,6 @@ float quadMeanDiameterSpeciesAdjust(float x, float initialDq, float min, float m } private float processVeteranLayer(VriPolygon polygon, VdypLayer.Builder lBuilder) throws StandProcessingException { - @SuppressWarnings("unused") var veteranLayer = polygon.getLayers().get(LayerType.VETERAN); lBuilder.layerType(LayerType.VETERAN); @@ -658,15 +657,12 @@ private float processVeteranLayer(VriPolygon polygon, VdypLayer.Builder lBuilder var bec = polygon.getBiogeoclimaticZone(); - var lowDq = false; - var primarySite = veteranLayer.getPrimarySite(); var primarySpecies = veteranLayer.getPrimarySpeciesRecord(); float ageTotal = primarySite.flatMap(VriSite::getAgeTotal).map(at -> at + veteranLayer.getAgeIncrease()) .orElse(0f); // AGETOTLV float yearsToBreastHeight = primarySite.flatMap(VriSite::getYearsToBreastHeight).orElse(0f); // YTBHLV - float breastHeightAge = ageTotal - yearsToBreastHeight; // AGEBHLV float dominantHeight = primarySite.flatMap(VriSite::getHeight).orElse(0f); // HDLV var baseArea = veteranLayer.getBaseArea() From 1088480819d4c7061c02d11956c93974418738f9 Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Thu, 29 Aug 2024 16:52:43 -0700 Subject: [PATCH 12/53] initial commit --- vdyp-web-ui/.editorconfig | 18 + vdyp-web-ui/.eslintignore | 7 + vdyp-web-ui/.eslintrc.cjs | 13 + vdyp-web-ui/.gitignore | 33 + vdyp-web-ui/.prettierrc.yml | 22 + vdyp-web-ui/env.d.ts | 1 + vdyp-web-ui/index.html | 22 + vdyp-web-ui/mdi-icons.md | 17 + vdyp-web-ui/package-lock.json | 3571 +++++++++++++++++ vdyp-web-ui/package.json | 52 + vdyp-web-ui/public/favicon.ico | Bin 0 -> 22486 bytes vdyp-web-ui/silent-check-sso.html | 12 + vdyp-web-ui/src/App.vue | 29 + vdyp-web-ui/src/assets/BCID_H_rgb_pos.png | Bin 0 -> 16262 bytes vdyp-web-ui/src/assets/gov-bc-logo-horiz.png | Bin 0 -> 3603 bytes .../src/components/JobTypeSelection.vue | 44 + .../components/common/AppConfirmDialog.vue | 71 + .../src/components/common/AppDatePicker.vue | 32 + .../components/common/AppDateRangePicker.vue | 97 + .../components/common/AppDialogDragger.vue | 77 + .../components/common/AppDialogTemplate.vue | 45 + .../src/components/common/AppMessage.vue | 40 + .../components/common/AppProgressCircular.vue | 44 + .../src/components/layout/TheHeader.vue | 125 + .../ModelStep1.vue | 252 ++ .../ModelStep2.vue | 184 + .../ModelStep3.vue | 72 + vdyp-web-ui/src/constants/constants.ts | 15 + vdyp-web-ui/src/interfaces/JobSearchParams.ts | 10 + .../src/interfaces/ProgressCircularState.ts | 4 + vdyp-web-ui/src/interfaces/TableOptions.ts | 6 + vdyp-web-ui/src/layouts/DefaultLayout.vue | 80 + vdyp-web-ui/src/main.ts | 33 + vdyp-web-ui/src/plugins/index.ts | 8 + vdyp-web-ui/src/plugins/vuetify.ts | 41 + vdyp-web-ui/src/router/index.ts | 35 + vdyp-web-ui/src/services/keycloak.ts | 250 ++ vdyp-web-ui/src/shims-vue.d.ts | 5 + vdyp-web-ui/src/stores/common/authStore.ts | 69 + .../src/stores/common/confirmDialogStore.ts | 64 + vdyp-web-ui/src/stores/common/messageStore.ts | 46 + .../stores/common/progressCircularStore.ts | 22 + vdyp-web-ui/src/stores/index.ts | 3 + .../src/stores/jobTypeSelectionStore.ts | 13 + vdyp-web-ui/src/stores/speciesStore.ts | 29 + vdyp-web-ui/src/stores/stepStore.ts | 18 + vdyp-web-ui/src/styles/style.scss | 113 + vdyp-web-ui/src/types/types.ts | 1 + vdyp-web-ui/src/utils/util.ts | 200 + vdyp-web-ui/src/views/JobList.vue | 372 ++ vdyp-web-ui/src/views/PageNotFound.vue | 24 + .../ModelParameterInput.vue | 53 + .../ModelParameterSelection.vue | 61 + .../input-model-parameters/SpeciesResults.vue | 5 + .../ViewErrorMessages.vue | 5 + .../input-model-parameters/ViewLogFile.vue | 5 + vdyp-web-ui/tsconfig.app.json | 14 + vdyp-web-ui/tsconfig.json | 11 + vdyp-web-ui/tsconfig.node.json | 13 + vdyp-web-ui/vite.config.ts | 21 + 60 files changed, 6529 insertions(+) create mode 100644 vdyp-web-ui/.editorconfig create mode 100644 vdyp-web-ui/.eslintignore create mode 100644 vdyp-web-ui/.eslintrc.cjs create mode 100644 vdyp-web-ui/.gitignore create mode 100644 vdyp-web-ui/.prettierrc.yml create mode 100644 vdyp-web-ui/env.d.ts create mode 100644 vdyp-web-ui/index.html create mode 100644 vdyp-web-ui/mdi-icons.md create mode 100644 vdyp-web-ui/package-lock.json create mode 100644 vdyp-web-ui/package.json create mode 100644 vdyp-web-ui/public/favicon.ico create mode 100644 vdyp-web-ui/silent-check-sso.html create mode 100644 vdyp-web-ui/src/App.vue create mode 100644 vdyp-web-ui/src/assets/BCID_H_rgb_pos.png create mode 100644 vdyp-web-ui/src/assets/gov-bc-logo-horiz.png create mode 100644 vdyp-web-ui/src/components/JobTypeSelection.vue create mode 100644 vdyp-web-ui/src/components/common/AppConfirmDialog.vue create mode 100644 vdyp-web-ui/src/components/common/AppDatePicker.vue create mode 100644 vdyp-web-ui/src/components/common/AppDateRangePicker.vue create mode 100644 vdyp-web-ui/src/components/common/AppDialogDragger.vue create mode 100644 vdyp-web-ui/src/components/common/AppDialogTemplate.vue create mode 100644 vdyp-web-ui/src/components/common/AppMessage.vue create mode 100644 vdyp-web-ui/src/components/common/AppProgressCircular.vue create mode 100644 vdyp-web-ui/src/components/layout/TheHeader.vue create mode 100644 vdyp-web-ui/src/components/model-param-selection-steps/ModelStep1.vue create mode 100644 vdyp-web-ui/src/components/model-param-selection-steps/ModelStep2.vue create mode 100644 vdyp-web-ui/src/components/model-param-selection-steps/ModelStep3.vue create mode 100644 vdyp-web-ui/src/constants/constants.ts create mode 100644 vdyp-web-ui/src/interfaces/JobSearchParams.ts create mode 100644 vdyp-web-ui/src/interfaces/ProgressCircularState.ts create mode 100644 vdyp-web-ui/src/interfaces/TableOptions.ts create mode 100644 vdyp-web-ui/src/layouts/DefaultLayout.vue create mode 100644 vdyp-web-ui/src/main.ts create mode 100644 vdyp-web-ui/src/plugins/index.ts create mode 100644 vdyp-web-ui/src/plugins/vuetify.ts create mode 100644 vdyp-web-ui/src/router/index.ts create mode 100644 vdyp-web-ui/src/services/keycloak.ts create mode 100644 vdyp-web-ui/src/shims-vue.d.ts create mode 100644 vdyp-web-ui/src/stores/common/authStore.ts create mode 100644 vdyp-web-ui/src/stores/common/confirmDialogStore.ts create mode 100644 vdyp-web-ui/src/stores/common/messageStore.ts create mode 100644 vdyp-web-ui/src/stores/common/progressCircularStore.ts create mode 100644 vdyp-web-ui/src/stores/index.ts create mode 100644 vdyp-web-ui/src/stores/jobTypeSelectionStore.ts create mode 100644 vdyp-web-ui/src/stores/speciesStore.ts create mode 100644 vdyp-web-ui/src/stores/stepStore.ts create mode 100644 vdyp-web-ui/src/styles/style.scss create mode 100644 vdyp-web-ui/src/types/types.ts create mode 100644 vdyp-web-ui/src/utils/util.ts create mode 100644 vdyp-web-ui/src/views/JobList.vue create mode 100644 vdyp-web-ui/src/views/PageNotFound.vue create mode 100644 vdyp-web-ui/src/views/input-model-parameters/ModelParameterInput.vue create mode 100644 vdyp-web-ui/src/views/input-model-parameters/ModelParameterSelection.vue create mode 100644 vdyp-web-ui/src/views/input-model-parameters/SpeciesResults.vue create mode 100644 vdyp-web-ui/src/views/input-model-parameters/ViewErrorMessages.vue create mode 100644 vdyp-web-ui/src/views/input-model-parameters/ViewLogFile.vue create mode 100644 vdyp-web-ui/tsconfig.app.json create mode 100644 vdyp-web-ui/tsconfig.json create mode 100644 vdyp-web-ui/tsconfig.node.json create mode 100644 vdyp-web-ui/vite.config.ts diff --git a/vdyp-web-ui/.editorconfig b/vdyp-web-ui/.editorconfig new file mode 100644 index 000000000..d7a2085c6 --- /dev/null +++ b/vdyp-web-ui/.editorconfig @@ -0,0 +1,18 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 2 + +[Makefile] +indent_style = tab + +[*.md] +indent_size = 4 diff --git a/vdyp-web-ui/.eslintignore b/vdyp-web-ui/.eslintignore new file mode 100644 index 000000000..7d455ee93 --- /dev/null +++ b/vdyp-web-ui/.eslintignore @@ -0,0 +1,7 @@ +.husky/ +.vscode/ +.yarn/ +coverage/ +dist/ +public/assets/ +tsconfig.*.json diff --git a/vdyp-web-ui/.eslintrc.cjs b/vdyp-web-ui/.eslintrc.cjs new file mode 100644 index 000000000..f99fd2d92 --- /dev/null +++ b/vdyp-web-ui/.eslintrc.cjs @@ -0,0 +1,13 @@ +/* eslint-env node */ +require('@rushstack/eslint-patch/modern-module-resolution') + +module.exports = { + root: true, + extends: ['plugin:vue/vue3-essential', 'eslint:recommended', '@vue/eslint-config-typescript', '@vue/eslint-config-prettier/skip-formatting'], + parserOptions: { + ecmaVersion: 'latest', + }, + rules: { + 'spaced-comment': ['error', 'always', { markers: ['/'] }], + }, +} diff --git a/vdyp-web-ui/.gitignore b/vdyp-web-ui/.gitignore new file mode 100644 index 000000000..f594da980 --- /dev/null +++ b/vdyp-web-ui/.gitignore @@ -0,0 +1,33 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +.DS_Store +dist +dist-ssr +coverage +*.local + +/cypress/videos/ +/cypress/screenshots/ + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +*.tsbuildinfo + +# env +.env diff --git a/vdyp-web-ui/.prettierrc.yml b/vdyp-web-ui/.prettierrc.yml new file mode 100644 index 000000000..bb30df991 --- /dev/null +++ b/vdyp-web-ui/.prettierrc.yml @@ -0,0 +1,22 @@ +# .prettierrc.yml + +# Use single quotes instead of double quotes +singleQuote: true + +# Use 2 spaces for indentation +tabWidth: 2 + +# Use spaces instead of tabs +useTabs: false + +# Add a trailing comma to the last item in an object or array +trailingComma: 'all' + +# Print semicolons at the ends of statements +semi: false + +# Wrap prose-like comments as-is +proseWrap: 'always' + +# Format files with Unix-style line endings +endOfLine: 'lf' diff --git a/vdyp-web-ui/env.d.ts b/vdyp-web-ui/env.d.ts new file mode 100644 index 000000000..11f02fe2a --- /dev/null +++ b/vdyp-web-ui/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/vdyp-web-ui/index.html b/vdyp-web-ui/index.html new file mode 100644 index 000000000..21ed4b05b --- /dev/null +++ b/vdyp-web-ui/index.html @@ -0,0 +1,22 @@ + + + + VDYP - Variable Density Yield Projection + + + + + + + + + + + + +
+ + + diff --git a/vdyp-web-ui/mdi-icons.md b/vdyp-web-ui/mdi-icons.md new file mode 100644 index 000000000..b38bd8904 --- /dev/null +++ b/vdyp-web-ui/mdi-icons.md @@ -0,0 +1,17 @@ +1. mdi-dots-horizontal or mdi-dots-horizontal-circle +2. mdi-dots-vertical or mdi-dots-vertical-circle +3. mdi-star +4. mdi-star-outline +5. mdi-help-circle-outline +6. mdi-pause +7. mdi-play +8. mdi-close +9. mdi-magnify +10. mdi-comment-multiple-outline or mdi-forum or mdi-wechat +11. mdi-filter +12. mdi-comment-outline +13. mdi-comment-check-outline +14. mdi-printer +15. mdi-download +16. mdi-delete +17. mdi-calendar or mdi-calendar-range diff --git a/vdyp-web-ui/package-lock.json b/vdyp-web-ui/package-lock.json new file mode 100644 index 000000000..cc6e0aea6 --- /dev/null +++ b/vdyp-web-ui/package-lock.json @@ -0,0 +1,3571 @@ +{ + "name": "vdyp", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "vdyp", + "version": "0.0.1", + "license": "(MIT OR Apache-2.0)", + "dependencies": { + "@bcgov/bc-sans": "^2.1.0", + "@vuepic/vue-datepicker": "^8.8.0", + "axios": "^1.7.2", + "http-status-codes": "^2.3.0", + "keycloak-js": "^25.0.2", + "pinia": "^2.2.2", + "vdyp": "file:", + "vite-plugin-package-version": "^1.1.0", + "vue": "^3.4.29", + "vue-router": "^4.3.3", + "vuetify": "^3.6.14" + }, + "devDependencies": { + "@mdi/font": "^7.4.47", + "@rushstack/eslint-patch": "^1.8.0", + "@tsconfig/node20": "^20.1.4", + "@types/node": "^20.14.5", + "@vitejs/plugin-vue": "^5.0.5", + "@vue/eslint-config-prettier": "^9.0.0", + "@vue/eslint-config-typescript": "^13.0.0", + "@vue/tsconfig": "^0.5.1", + "eslint": "^8.57.0", + "npm-run-all2": "^6.2.0", + "prettier": "^3.3.3", + "sass": "^1.77.6", + "sass-loader": "^14.2.1", + "typescript": "~5.4.0", + "vite": "^5.3.1", + "vite-plugin-vuetify": "^2.0.3", + "vue-tsc": "^2.0.21" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", + "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", + "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", + "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", + "dependencies": { + "@babel/types": "^7.25.6" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", + "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", + "dependencies": { + "@babel/helper-string-parser": "^7.24.8", + "@babel/helper-validator-identifier": "^7.24.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcgov/bc-sans": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@bcgov/bc-sans/-/bc-sans-2.1.0.tgz", + "integrity": "sha512-1MesF4NAVpM5dywoJ68wNcBylHbPqg1dDV/FNuQm0BbspETGlPmfX8LG8rtrCjCAPhWuL2qRT/lBYDUMvFTUnw==" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", + "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" + }, + "node_modules/@mdi/font": { + "version": "7.4.47", + "resolved": "https://registry.npmjs.org/@mdi/font/-/font-7.4.47.tgz", + "integrity": "sha512-43MtGpd585SNzHZPcYowu/84Vz2a2g31TvPMTm9uTiCSWzaheQySUcSyUH/46fPnuPQWof2yd0pGBtzee/IQWw==", + "dev": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.1.tgz", + "integrity": "sha512-2thheikVEuU7ZxFXubPDOtspKn1x0yqaYQwvALVtEcvFhMifPADBrgRPyHV0TF3b+9BgvgjgagVyvA/UqPZHmg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.1.tgz", + "integrity": "sha512-t1lLYn4V9WgnIFHXy1d2Di/7gyzBWS8G5pQSXdZqfrdCGTwi1VasRMSS81DTYb+avDs/Zz4A6dzERki5oRYz1g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.1.tgz", + "integrity": "sha512-AH/wNWSEEHvs6t4iJ3RANxW5ZCK3fUnmf0gyMxWCesY1AlUj8jY7GC+rQE4wd3gwmZ9XDOpL0kcFnCjtN7FXlA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.1.tgz", + "integrity": "sha512-dO0BIz/+5ZdkLZrVgQrDdW7m2RkrLwYTh2YMFG9IpBtlC1x1NPNSXkfczhZieOlOLEqgXOFH3wYHB7PmBtf+Bg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.1.tgz", + "integrity": "sha512-sWWgdQ1fq+XKrlda8PsMCfut8caFwZBmhYeoehJ05FdI0YZXk6ZyUjWLrIgbR/VgiGycrFKMMgp7eJ69HOF2pQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.1.tgz", + "integrity": "sha512-9OIiSuj5EsYQlmwhmFRA0LRO0dRRjdCVZA3hnmZe1rEwRk11Jy3ECGGq3a7RrVEZ0/pCsYWx8jG3IvcrJ6RCew==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.1.tgz", + "integrity": "sha512-0kuAkRK4MeIUbzQYu63NrJmfoUVicajoRAL1bpwdYIYRcs57iyIV9NLcuyDyDXE2GiZCL4uhKSYAnyWpjZkWow==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.1.tgz", + "integrity": "sha512-/6dYC9fZtfEY0vozpc5bx1RP4VrtEOhNQGb0HwvYNwXD1BBbwQ5cKIbUVVU7G2d5WRE90NfB922elN8ASXAJEA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.1.tgz", + "integrity": "sha512-ltUWy+sHeAh3YZ91NUsV4Xg3uBXAlscQe8ZOXRCVAKLsivGuJsrkawYPUEyCV3DYa9urgJugMLn8Z3Z/6CeyRQ==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.1.tgz", + "integrity": "sha512-BggMndzI7Tlv4/abrgLwa/dxNEMn2gC61DCLrTzw8LkpSKel4o+O+gtjbnkevZ18SKkeN3ihRGPuBxjaetWzWg==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.1.tgz", + "integrity": "sha512-z/9rtlGd/OMv+gb1mNSjElasMf9yXusAxnRDrBaYB+eS1shFm6/4/xDH1SAISO5729fFKUkJ88TkGPRUh8WSAA==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.1.tgz", + "integrity": "sha512-kXQVcWqDcDKw0S2E0TmhlTLlUgAmMVqPrJZR+KpH/1ZaZhLSl23GZpQVmawBQGVhyP5WXIsIQ/zqbDBBYmxm5w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.1.tgz", + "integrity": "sha512-CbFv/WMQsSdl+bpX6rVbzR4kAjSSBuDgCqb1l4J68UYsQNalz5wOqLGYj4ZI0thGpyX5kc+LLZ9CL+kpqDovZA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.1.tgz", + "integrity": "sha512-3Q3brDgA86gHXWHklrwdREKIrIbxC0ZgU8lwpj0eEKGBQH+31uPqr0P2v11pn0tSIxHvcdOWxa4j+YvLNx1i6g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.1.tgz", + "integrity": "sha512-tNg+jJcKR3Uwe4L0/wY3Ro0H+u3nrb04+tcq1GSYzBEmKLeOQF2emk1whxlzNqb6MMrQ2JOcQEpuuiPLyRcSIw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.1.tgz", + "integrity": "sha512-xGiIH95H1zU7naUyTKEyOA/I0aexNMUdO9qRv0bLKN3qu25bBdrxZHqA3PTJ24YNN/GdMzG4xkDcd/GvjuhfLg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.4.tgz", + "integrity": "sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==", + "dev": true + }, + "node_modules/@tsconfig/node20": { + "version": "20.1.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node20/-/node20-20.1.4.tgz", + "integrity": "sha512-sqgsT69YFeLWf5NtJ4Xq/xAF8p4ZQHlmGW74Nu2tD4+g5fAsposc4ZfaaPixVu4y01BEiDCWLRDCvDM5JOsRxg==", + "dev": true + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + }, + "node_modules/@types/node": { + "version": "20.16.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.2.tgz", + "integrity": "sha512-91s/n4qUPV/wg8eE9KHYW1kouTfDk2FPGjXbBMfRWP/2vg1rCXNQL1OCabwGs0XSdukuK+MwCDXE30QpSeMUhQ==", + "devOptional": true, + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", + "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", + "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", + "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", + "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.1.2.tgz", + "integrity": "sha512-nY9IwH12qeiJqumTCLJLE7IiNx7HZ39cbHaysEUd+Myvbz9KAqd2yq+U01Kab1R/H1BmiyM2ShTYlNH32Fzo3A==", + "dev": true, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.1.tgz", + "integrity": "sha512-9AKhC7Qn2mQYxj7Dz3bVxeOk7gGJladhWixUYKef/o0o7Bm4an+A3XvmcTHVqZ8stE6lBVH++g050tBtJ4TZPQ==", + "dev": true, + "dependencies": { + "@volar/source-map": "2.4.1" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.1.tgz", + "integrity": "sha512-Xq6ep3OZg9xUqN90jEgB9ztX5SsTz1yiV8wiQbcYNjWkek+Ie3dc8l7AVt3EhDm9mSIR58oWczHkzM2H6HIsmQ==", + "dev": true + }, + "node_modules/@volar/typescript": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.1.tgz", + "integrity": "sha512-UoRzC0PXcwajFQTu8XxKSYNsWNBtVja6Y9gC8eLv7kYm+UEKJCcZ8g7dialsOYA0HKs3Vpg57MeCsawFLC6m9Q==", + "dev": true, + "dependencies": { + "@volar/language-core": "2.4.1", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.38.tgz", + "integrity": "sha512-8IQOTCWnLFqfHzOGm9+P8OPSEDukgg3Huc92qSG49if/xI2SAwLHQO2qaPQbjCWPBcQoO1WYfXfTACUrWV3c5A==", + "dependencies": { + "@babel/parser": "^7.24.7", + "@vue/shared": "3.4.38", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.38.tgz", + "integrity": "sha512-Osc/c7ABsHXTsETLgykcOwIxFktHfGSUDkb05V61rocEfsFDcjDLH/IHJSNJP+/Sv9KeN2Lx1V6McZzlSb9EhQ==", + "dependencies": { + "@vue/compiler-core": "3.4.38", + "@vue/shared": "3.4.38" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.38.tgz", + "integrity": "sha512-s5QfZ+9PzPh3T5H4hsQDJtI8x7zdJaew/dCGgqZ2630XdzaZ3AD8xGZfBqpT8oaD/p2eedd+pL8tD5vvt5ZYJQ==", + "dependencies": { + "@babel/parser": "^7.24.7", + "@vue/compiler-core": "3.4.38", + "@vue/compiler-dom": "3.4.38", + "@vue/compiler-ssr": "3.4.38", + "@vue/shared": "3.4.38", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.10", + "postcss": "^8.4.40", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.38.tgz", + "integrity": "sha512-YXznKFQ8dxYpAz9zLuVvfcXhc31FSPFDcqr0kyujbOwNhlmaNvL2QfIy+RZeJgSn5Fk54CWoEUeW+NVBAogGaw==", + "dependencies": { + "@vue/compiler-dom": "3.4.38", + "@vue/shared": "3.4.38" + } + }, + "node_modules/@vue/compiler-vue2": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz", + "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==", + "dev": true, + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.3.tgz", + "integrity": "sha512-0MiMsFma/HqA6g3KLKn+AGpL1kgKhFWszC9U29NfpWK5LE7bjeXxySWJrOJ77hBz+TBrBQ7o4QJqbPbqbs8rJw==" + }, + "node_modules/@vue/eslint-config-prettier": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@vue/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", + "integrity": "sha512-z1ZIAAUS9pKzo/ANEfd2sO+v2IUalz7cM/cTLOZ7vRFOPk5/xuRKQteOu1DErFLAh/lYGXMVZ0IfYKlyInuDVg==", + "dev": true, + "dependencies": { + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.0" + }, + "peerDependencies": { + "eslint": ">= 8.0.0", + "prettier": ">= 3.0.0" + } + }, + "node_modules/@vue/eslint-config-typescript": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-13.0.0.tgz", + "integrity": "sha512-MHh9SncG/sfqjVqjcuFLOLD6Ed4dRAis4HNt0dXASeAuLqIAx4YMB1/m2o4pUKK1vCt8fUvYG8KKX2Ot3BVZTg==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "^7.1.1", + "@typescript-eslint/parser": "^7.1.1", + "vue-eslint-parser": "^9.3.1" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "peerDependencies": { + "eslint": "^8.56.0", + "eslint-plugin-vue": "^9.0.0", + "typescript": ">=4.7.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/eslint-config-typescript/node_modules/@typescript-eslint/eslint-plugin": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", + "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/type-utils": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/eslint-config-typescript/node_modules/@typescript-eslint/parser": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", + "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/eslint-config-typescript/node_modules/@typescript-eslint/type-utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", + "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/eslint-config-typescript/node_modules/@typescript-eslint/utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", + "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/@vue/language-core": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.1.2.tgz", + "integrity": "sha512-tt2J7C+l0J/T5PaLhJ0jvCCi0JNwu3e8azWTYxW3jmAW5B/dac0g5UxmI7l59CQgCGFotqUqI3tXjfZgoWNtog==", + "dev": true, + "dependencies": { + "@volar/language-core": "~2.4.1", + "@vue/compiler-dom": "^3.4.0", + "@vue/compiler-vue2": "^2.7.16", + "@vue/shared": "^3.4.0", + "computeds": "^0.0.1", + "minimatch": "^9.0.3", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/reactivity": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.38.tgz", + "integrity": "sha512-4vl4wMMVniLsSYYeldAKzbk72+D3hUnkw9z8lDeJacTxAkXeDAP1uE9xr2+aKIN0ipOL8EG2GPouVTH6yF7Gnw==", + "dependencies": { + "@vue/shared": "3.4.38" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.38.tgz", + "integrity": "sha512-21z3wA99EABtuf+O3IhdxP0iHgkBs1vuoCAsCKLVJPEjpVqvblwBnTj42vzHRlWDCyxu9ptDm7sI2ZMcWrQqlA==", + "dependencies": { + "@vue/reactivity": "3.4.38", + "@vue/shared": "3.4.38" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.38.tgz", + "integrity": "sha512-afZzmUreU7vKwKsV17H1NDThEEmdYI+GCAK/KY1U957Ig2NATPVjCROv61R19fjZNzMmiU03n79OMnXyJVN0UA==", + "dependencies": { + "@vue/reactivity": "3.4.38", + "@vue/runtime-core": "3.4.38", + "@vue/shared": "3.4.38", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.38.tgz", + "integrity": "sha512-NggOTr82FbPEkkUvBm4fTGcwUY8UuTsnWC/L2YZBmvaQ4C4Jl/Ao4HHTB+l7WnFCt5M/dN3l0XLuyjzswGYVCA==", + "dependencies": { + "@vue/compiler-ssr": "3.4.38", + "@vue/shared": "3.4.38" + }, + "peerDependencies": { + "vue": "3.4.38" + } + }, + "node_modules/@vue/shared": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.38.tgz", + "integrity": "sha512-q0xCiLkuWWQLzVrecPb0RMsNWyxICOjPrcrwxTUEHb1fsnvni4dcuyG7RT/Ie7VPTvnjzIaWzRMUBsrqNj/hhw==" + }, + "node_modules/@vue/tsconfig": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.5.1.tgz", + "integrity": "sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==", + "dev": true + }, + "node_modules/@vuepic/vue-datepicker": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@vuepic/vue-datepicker/-/vue-datepicker-8.8.1.tgz", + "integrity": "sha512-8ehfUz1m69Vuc16Pm4ukgb3Mg1VT14x4EsG1ag4O/qbSNRWztTo+pUV4JnFt0FGLl5gGb6NXlxIvR7EjLgD7Gg==", + "dependencies": { + "date-fns": "^3.6.0" + }, + "peerDependencies": { + "vue": ">=3.2.0" + } + }, + "node_modules/@vuetify/loader-shared": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@vuetify/loader-shared/-/loader-shared-2.0.3.tgz", + "integrity": "sha512-Ss3GC7eJYkp2SF6xVzsT7FAruEmdihmn4OCk2+UocREerlXKWgOKKzTN5PN3ZVN5q05jHHrsNhTuWbhN61Bpdg==", + "devOptional": true, + "dependencies": { + "upath": "^2.0.1" + }, + "peerDependencies": { + "vue": "^3.0.0", + "vuetify": "^3.0.0" + } + }, + "node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "devOptional": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz", + "integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "devOptional": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "peer": true + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "devOptional": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "devOptional": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "devOptional": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/computeds": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/computeds/-/computeds-0.0.1.tgz", + "integrity": "sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "peer": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, + "node_modules/date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", + "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", + "devOptional": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", + "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.9.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-vue": { + "version": "9.27.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.27.0.tgz", + "integrity": "sha512-5Dw3yxEyuBSXTzT5/Ge1X5kIkRTQ3nvBn/VwPwInNiZBSJOO/timWMUaflONnFBzU6NhB68lxnCda7ULV5N7LA==", + "dev": true, + "peer": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "globals": "^13.24.0", + "natural-compare": "^1.4.0", + "nth-check": "^2.1.1", + "postcss-selector-parser": "^6.0.15", + "semver": "^7.6.0", + "vue-eslint-parser": "^9.4.3", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "devOptional": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/http-status-codes": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.3.0.tgz", + "integrity": "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/immutable": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", + "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", + "devOptional": true + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "devOptional": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "devOptional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "devOptional": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "devOptional": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/js-sha256": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.11.0.tgz", + "integrity": "sha512-6xNlKayMZvds9h1Y1VWc0fQHQ82BxTXizWPEtEeGvmOUYpBRy4gbWroHLpzowe6xiQhHpelCQiE7HEdznyBL9Q==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz", + "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/jwt-decode": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", + "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", + "engines": { + "node": ">=18" + } + }, + "node_modules/keycloak-js": { + "version": "25.0.4", + "resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-25.0.4.tgz", + "integrity": "sha512-LW7dVgqcBxMnnJTdmh7Zgd0NpStJnX2sCMrJGqcGtm4zmk4Rwlqk2o2uOvY7PaRHHYePXfbIwrqVhlN3GAnRCg==", + "dependencies": { + "js-sha256": "^0.11.0", + "jwt-decode": "^4.0.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/magic-string": { + "version": "0.30.11", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", + "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "devOptional": true + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "devOptional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", + "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-run-all2": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/npm-run-all2/-/npm-run-all2-6.2.2.tgz", + "integrity": "sha512-Q+alQAGIW7ZhKcxLt8GcSi3h3ryheD6xnmXahkMRVM5LYmajcUrSITm8h+OPC9RYWMV2GR0Q1ntTUCfxaNoOJw==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "cross-spawn": "^7.0.3", + "memorystream": "^0.3.1", + "minimatch": "^9.0.0", + "pidtree": "^0.6.0", + "read-package-json-fast": "^3.0.2", + "shell-quote": "^1.7.3" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "npm-run-all2": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": "^14.18.0 || ^16.13.0 || >=18.0.0", + "npm": ">= 8" + } + }, + "node_modules/npm-run-all2/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "peer": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "devOptional": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pinia": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.2.2.tgz", + "integrity": "sha512-ja2XqFWZC36mupU4z1ZzxeTApV7DOw44cV4dhQ9sGwun+N89v/XP7+j7q6TanS1u1tdbK4r+1BUx7heMaIdagA==", + "dependencies": { + "@vue/devtools-api": "^6.6.3", + "vue-demi": "^0.14.10" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "@vue/composition-api": "^1.4.0", + "typescript": ">=4.4.4", + "vue": "^2.6.14 || ^3.3.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/pinia/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/postcss": { + "version": "8.4.41", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", + "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.1", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "peer": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/read-package-json-fast": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz", + "integrity": "sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==", + "dev": true, + "dependencies": { + "json-parse-even-better-errors": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "devOptional": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.1.tgz", + "integrity": "sha512-ZnYyKvscThhgd3M5+Qt3pmhO4jIRR5RGzaSovB6Q7rGNrK5cUncrtLmcTTJVSdcKXyZjW8X8MB0JMSuH9bcAJg==", + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.21.1", + "@rollup/rollup-android-arm64": "4.21.1", + "@rollup/rollup-darwin-arm64": "4.21.1", + "@rollup/rollup-darwin-x64": "4.21.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.21.1", + "@rollup/rollup-linux-arm-musleabihf": "4.21.1", + "@rollup/rollup-linux-arm64-gnu": "4.21.1", + "@rollup/rollup-linux-arm64-musl": "4.21.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.21.1", + "@rollup/rollup-linux-riscv64-gnu": "4.21.1", + "@rollup/rollup-linux-s390x-gnu": "4.21.1", + "@rollup/rollup-linux-x64-gnu": "4.21.1", + "@rollup/rollup-linux-x64-musl": "4.21.1", + "@rollup/rollup-win32-arm64-msvc": "4.21.1", + "@rollup/rollup-win32-ia32-msvc": "4.21.1", + "@rollup/rollup-win32-x64-msvc": "4.21.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/sass": { + "version": "1.77.8", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.8.tgz", + "integrity": "sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==", + "devOptional": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-loader": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-14.2.1.tgz", + "integrity": "sha512-G0VcnMYU18a4N7VoNDegg2OuMjYtxnqzQWARVWCIVSZwJeiL9kg8QMsuIZOplsJgTzZLF6jGxI3AClj8I9nRdQ==", + "dev": true, + "dependencies": { + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/synckit": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz", + "integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "devOptional": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "devOptional": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "devOptional": true + }, + "node_modules/upath": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz", + "integrity": "sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==", + "devOptional": true, + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "peer": true + }, + "node_modules/vdyp": { + "resolved": "", + "link": true + }, + "node_modules/vite": { + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.2.tgz", + "integrity": "sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.41", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-plugin-package-version": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vite-plugin-package-version/-/vite-plugin-package-version-1.1.0.tgz", + "integrity": "sha512-TPoFZXNanzcaKCIrC3e2L/TVRkkRLB6l4RPN/S7KbG7rWfyLcCEGsnXvxn6qR7fyZwXalnnSN/I9d6pSFjHpEA==", + "peerDependencies": { + "vite": ">=2.0.0-beta.69" + } + }, + "node_modules/vite-plugin-vuetify": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vite-plugin-vuetify/-/vite-plugin-vuetify-2.0.4.tgz", + "integrity": "sha512-A4cliYUoP/u4AWSRVRvAPKgpgR987Pss7LpFa7s1GvOe8WjgDq92Rt3eVXrvgxGCWvZsPKziVqfHHdCMqeDhfw==", + "devOptional": true, + "dependencies": { + "@vuetify/loader-shared": "^2.0.3", + "debug": "^4.3.3", + "upath": "^2.0.1" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": ">=5", + "vue": "^3.0.0", + "vuetify": "^3.0.0" + } + }, + "node_modules/vscode-uri": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", + "dev": true + }, + "node_modules/vue": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.38.tgz", + "integrity": "sha512-f0ZgN+mZ5KFgVv9wz0f4OgVKukoXtS3nwET4c2vLBGQR50aI8G0cqbFtLlX9Yiyg3LFGBitruPHt2PxwTduJEw==", + "dependencies": { + "@vue/compiler-dom": "3.4.38", + "@vue/compiler-sfc": "3.4.38", + "@vue/runtime-dom": "3.4.38", + "@vue/server-renderer": "3.4.38", + "@vue/shared": "3.4.38" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-eslint-parser": { + "version": "9.4.3", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz", + "integrity": "sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==", + "dev": true, + "dependencies": { + "debug": "^4.3.4", + "eslint-scope": "^7.1.1", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", + "esquery": "^1.4.0", + "lodash": "^4.17.21", + "semver": "^7.3.6" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/vue-router": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.4.3.tgz", + "integrity": "sha512-sv6wmNKx2j3aqJQDMxLFzs/u/mjA9Z5LCgy6BE0f7yFWMjrPLnS/sPNn8ARY/FXw6byV18EFutn5lTO6+UsV5A==", + "dependencies": { + "@vue/devtools-api": "^6.6.3" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/vue-tsc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.1.2.tgz", + "integrity": "sha512-PH1BDxWT3eaPhl73elyZj6DV0nR3K4IFoUM1sGzMXXQneovVUwHQytdSyAHiED5MtEINGSHpL/Hs9ch+c/tDTw==", + "dev": true, + "dependencies": { + "@volar/typescript": "~2.4.1", + "@vue/language-core": "2.1.2", + "semver": "^7.5.4" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + }, + "node_modules/vuetify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.7.1.tgz", + "integrity": "sha512-N1XlczbgeGt/O+JUk72QPrqcDaRIXUdptUciJqGyTvZ9cfMoSlEWs6TZO+dOOfXbKvmIMFMycYg4dgSHDpCPhg==", + "engines": { + "node": "^12.20 || >=14.13" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/johnleider" + }, + "peerDependencies": { + "typescript": ">=4.7", + "vite-plugin-vuetify": ">=1.0.0", + "vue": "^3.3.0", + "webpack-plugin-vuetify": ">=2.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vite-plugin-vuetify": { + "optional": true + }, + "webpack-plugin-vuetify": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/vdyp-web-ui/package.json b/vdyp-web-ui/package.json new file mode 100644 index 000000000..dccba0dbe --- /dev/null +++ b/vdyp-web-ui/package.json @@ -0,0 +1,52 @@ +{ + "name": "vdyp", + "version": "0.0.1", + "private": true, + "type": "module", + "description": "Variable Density Yield Projection", + "scripts": { + "dev": "vite", + "build": "run-p type-check \"build-only {@}\" --", + "preview": "vite preview", + "build-only": "vite build", + "type-check": "vue-tsc --build --force", + "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", + "format": "prettier --write src/" + }, + "keywords": [], + "author": "Vivid Solutions Inc.", + "license": "(MIT OR Apache-2.0)", + "repository": {}, + "dependencies": { + "@bcgov/bc-sans": "^2.1.0", + "@vuepic/vue-datepicker": "^8.8.0", + "axios": "^1.7.2", + "http-status-codes": "^2.3.0", + "keycloak-js": "^25.0.2", + "pinia": "^2.2.2", + "vdyp": "file:", + "vite-plugin-package-version": "^1.1.0", + "vue": "^3.4.29", + "vue-router": "^4.3.3", + "vuetify": "^3.6.14" + }, + "devDependencies": { + "@mdi/font": "^7.4.47", + "@rushstack/eslint-patch": "^1.8.0", + "@tsconfig/node20": "^20.1.4", + "@types/node": "^20.14.5", + "@vitejs/plugin-vue": "^5.0.5", + "@vue/eslint-config-prettier": "^9.0.0", + "@vue/eslint-config-typescript": "^13.0.0", + "@vue/tsconfig": "^0.5.1", + "eslint": "^8.57.0", + "npm-run-all2": "^6.2.0", + "prettier": "^3.3.3", + "sass": "^1.77.6", + "sass-loader": "^14.2.1", + "typescript": "~5.4.0", + "vite": "^5.3.1", + "vite-plugin-vuetify": "^2.0.3", + "vue-tsc": "^2.0.21" + } +} diff --git a/vdyp-web-ui/public/favicon.ico b/vdyp-web-ui/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..1841396d21ce974cdec16f7414c88683cd36af16 GIT binary patch literal 22486 zcmeHP3w+Mi|Nm%0{Y#rtDHTchOGGO*gp8KyX)AlyYBgz1S(xaC=tUx`Q^)mPji~xHM83ZEcaO`7=G{Q9@^vV)KGChWnDY2Wx#jIm`8w32 zGEuK5h#I0zfS@RNfur3OcA$SN;TL{TbE3f={b)eYP4uecPI|*}2fg`XJL=!BD-8`G z8rGU|G8riNI(yzkzl{6-( zI*oe751}uO>T(l}ee6aW4?m*ojr3mVwUpHLdYTYgi{5+kCQ1ygjqh71{^=$N>(B>J z-9gFE)Ti;0O)2HMMwIew6Z$Z!38lW!gc4qQfKt0PqmN!{PM`L?hbG6~hwwp4kA0A) z^lC}zz1mQGTrhpyKai3JKTT8ncBENC=ofJolKk848_zCpV#AJ%im`Qg{UP%v5UP7&>uc2$c zvd9m<=6s84EwHHeLW^$s+M*V7w^QA3EUN#lMU56&^va^m)OfK)P2lfYVv&EYMJ<5O#Bp8g4~$oK~> zn>v(MWDcY6vqsR0Y4No5({TvL(#oun^!@Zv^j+py%Ab))`7__AA7;Hrg#TLDs2qW6DNl>D=zdFy|nZ%Ve&q+cvb{YBBITNGt%RWxT$m13!U{%^T$>_EAc!DRTDd0;pbPwb|_clJY^-@cYG&z`%>0bvx30SuWDi^Ag#0I z7d3<&9w6Rv0q#3~gsXkKb>C`(;+t3Xb;0rVZPxY*^HBqdXn%ZFuhxBUO;}s<#h|+A zlb_!eRsA~7%^!6es*95b{e0)n)$QxneXHL6xs2fY*#Ri)+iG@x5x!=%*6=h)zpDB9 zmtCuSfV$N>xTn?Ib?Xjt5B~TDwQjz@l5YO)RfXMxDhsqG4QsVuUhFP2YnD4&TsU*W zf{f00)bR7IJmAOTG)I>zH`i;KIr+^`b8tFQKqNnezexyY0%_l@qG9_?^A;#TWM(nB5Hlef&bq2(goZdH zoX*!`BKzlL#APPOF3d?z&WcINN{ft1c7{eeLn1QVtxF^g)N(|{L`0@%^p8r5i^<4| z>z)2WY)X3f@ZK>XjZI3gDj7~kXm@8wbl;TdsMlg+Qqp6hlVhWj`^Gw)Q6Y|~Os}GL zG$#hb;E0Nibw>A&ib_d~L61UWVID~NRhgd-vX)gr&oyaXwR7X5!b4HY5sr$Xp`nr8D{9+-E6hrc zjfw6a6&V#B8=Jh)B%e1dT)k-F!bJt=!Q;Hu|JIoo{|5Hoje)#V>%g~#cfV8dhIKpF zJ2mc^R{9;P-=Kl6H)sd@?Kvv28jTIQlE!tuh7!75jW=d>dN;TRjpK}21e6j>i{%!YVl-bEk3(3C!oZcZt0IB81n zb~NqP4)odUL6rSwXUZNFLOBCNXvW~KG;_$4G-pI4jUCa8=8o(}^TxbL3nsiw3li~m zoY;@PO@57XKOR7fQwP!0Lx)iK1Yq@}x$Q{LXwv>0Xen-A|kdGop7c}MWZ zJ5s9OdYkdyJF*B!!p?l_g6IYEcd*l)VO z>wC_y4t#XE1k=}I4;O8--%cD-)#pL&%^>-k*6 zT0TBLRp9I&AD?=|rgp!n;d4>l?y7%nb!6#uDd2KG)uS7uigqTgOz6|JZuvT8^PAN1 zXPH*+xRwXtPHNO>l8(gJy`_O)l`7TWO?;r4-wn?^9hop$r%y#I;u1!Uc{{e|b@zNc zHK=9lS6=HgAZfgvVgDv3z8=;m?x8l0sZ$5~w+{^LH=s{alBf__uG>K0J8f>;3u+ z7}%#@#6Yy&HqbxJAoO8~zkg7x;MYRK9DQDQ^a+X!Z57(WKP1(zj^D}QEnD6j6w)dz zFsyfASj&)LfB%+o?p%os3GxqU)griMr{I>s{w)LigUgTu1yTn-65!vmc>w zpuHPO9MC7s5gZmbAjLcNFOFaA?M&_|V72SJiu;}-cN61W_mn~5chcyNl{AlB_~K(8 zsZ3)Zt%f_umAHdkMH3#shDJSpExq@|b>MN|Ldj3x4*qt1`mkF=nh<>tebS>j_}TYU z`YR8BmmNSK4+^Ku*8*|(=mK7KIL#Z~opRF#Q`4^wQ0w`7-8|;I!CP(y-m=^e9s)19 z9eBx)f{*Ng?+QM$+y$O0u;}GghpF3Yi((2rykp@Lzn_{w)Ohc(!t8eKn>AzHI3U31-|PG`GzPG^^U?!KbgLpL>ipaYmmPG@Lf z%LX?GI_uruDBz(WhvTsh0gZ2O;B*GnuGi?kfPmHk_cf}2+hZu#@wQtUH)!0re&btj z>!cBKG`Y2Q?b^58cAsv>ZmLb2K%IBikD>YxApYud#btt{KSdLVG*%ogtr(>rc@Q$Edn zOTNU7GwCMuPB(E0&(4=Sg#7d5>9|#jG~6w$GYowZ-uH76LobG*H^b1UVd&Q|bZ!_r zHw+ychAs_5cZPQquVCn#aH*~@EqJ%>fhD+@d&^m{-L)$2qtq8?wm@Zfo9E8-FDh(`rh<)pA1bUt*jc>2I6bUo-6 zF?4_!`Y^8c4HO%8`SG*yF*w;}%&%S3;Ap6VvX~n}U+5b#^qLrYTnt?=PJO-^kL`R7 zLszYghu#|SCC0m)#|9z9_h@{N?pU^*)EgUgHACNwbKVYR=t?nkh|mu&*e>Lhg>f!D zzT$k{ zFSHZi;WCE)9YcSP^Jl!r&{G07%!l{wVCdK}^xzmeU%VmrGv2fL2j2TrAw!Rfp*zJp z*Dqt}gfVpB7E6uyCoiAf0?L!9< z^tpxVJl!_YTl=qK_p%)@`i$3JXnW#F378>V;sugQnLB||5Tp-0EV+Zde6hu*Nu z=Xv9JuDriCUvL)MLs#-V_*aD2#ZNc*L$~uh`2YRs|7Y=+J$y9Q)kxXH^?v6)HWl|j z9`&To@9jSo3kaJV(FQa3_Vp|mNAr}1FhSWv&ih1 z5*K*lp-+u9*9&`eAs_m6J3|*&w{jAeyr;*$6R{) z6zY7>GX_7Q_wj#re-`|qm&*rnenDrQp>wX!26Ltx+L?1h+J#O!LnhBhe%qnXVRNrA zp}9lJ_p)h28M^4&OI)0N?#mf&JMYlK`M+^#HbY09q3h1jb7$zCGj!_pxn<}p_zcJV zgU&ibkC^wKDrM+>Gjz)tI`=4hxgJ9q^KqRkGj!Y;y6+5~d4_H}Lm!zBox#1&1>744 zi5oBDBKJt~_FY8oiuUcp^KHs2K2~Z07sb$}X6SJ<^rU(6R>jbDXXxT%-c{CjlK6)& zW7r2UbjTU{&3wF+Iq@gO(1B;@!!zs!7<&EuzVlXw&OJ}trWiWi4Bc~vem6sBo7Wvy zs0Unttb6`=;cjCo)VA|YpUQ^vC3H}H8u!2LClt@wW&1_@6zBe`7&3pJxl=Lp&lz?A z3>yQ6O#?%BpTF9x_zda^4CsGzt%Vj}zrbSX>@#%y8G7;z{dCUSp%}6b?X$Kk-gwwz z=!f%$BdCwcHWr4C8YLH%O^zq8EdBynG04DMk1H-ld(tLkDm-AlV(82BdoH-pnP=Dz zFm(7C_67{S5MFagsARXj`IzF)D{cJ$cag=9f3FyJ0Q~xoik~S^`~<$CU(etFrg-*F zi?g=d^$q@p-X^p$v)h(md*0jM2Bw53F$qgcOF?^P$bC2)v=SJZpi|IyiUXG`e(nd` zJ69=&y@RIlTg4VbzN7IMSSK)t(lFOxH^H!>(B+ygwiq@O+-8~LkG3d=zCRD$XwwGz zA9}6|y=1J<{^!Bf?u!SO_Zon%KgP0@p`Xvt3*ph5Y#6i9moESVIyHPVaJuzti|+u< z1vkMF_6K_X8CswVdQQV$L$8DDP|g?Yp&{^31@65t7BVJ63t3Y#cCa_#bi|7g-;VKL z0r=8hc_;Xr@v!5HVVA(az(YSpw;^rGn)(tr3#{Okiu+^ig>D^J*f`1h6#okHVXtr& zESU>}E9_QuIjJY~78=~Y)W#pOK8D^6FU7igyIAq~62*s3GeiET`yu<%TJ#6f#RJ_!EXFa|Q8q<@f0 z@@FV_5cr9og8WTKnPF?fEB4!EC13i#`=nyn-|)+86bpT(qO9x}T`&eW0DcYN(sZ%n z#+VC3(U*#xJ%_O_F_y6X(ep=em-+V?+LAp$d~A{8hp;AKH>2l%B-XdAJJ~;=8>Qni zr=Uv(dfT|BA`W{OUI6@MKa{z$9N#YiXV}&-ZGs&NVHk&T7`*0V=OvclA=&Z2=-!hKRVm(N@j5}n;44WWLcbUhqWx^U^ zhJ6lCaHY>i9oRN$e5DR_)%5(7KFD}Bz&JzaO^=7{c>-J3;eCK9ZC2zY$M$9?VV;a} ztc^JcyDr_X%)NS;k1{7ACui7X@x8f<2V?FE?jM21kfm$-Lq^UK`HJ&EW66_s^bdHU z{ga^a81x6WOgs_uMA{H~z&46+N82}Hyjx(Ol)Xb>!(NPK&Kexg)d!R3!JZj5Mx6YM zjjxQKoNqFg&m9;159c&b_e|hqyjMTNyd8wm$O8?l)VRg zOnaP>vghtSbvBI+96{8?CHy1K)Hio8X+zS)L${Vc$2yb!QN{&!uKYOGitJO+ZPp&P zvD^joJ{rH*rMWaY?&SXWEh7z^^x~v0-?AO^$)>-G)h% zGA3?QM1E~&((QiOwOv^k9GJGGqG`W;+`CclGA7-|?Cc!Vws+{MWBT{!@Hg#U3jcK! zD6bE6>HFVH0nyW{1K$?Dl8zC*BMP$R+=YzzU$x3$bFUZv#R@Nk27g7_i`8!f_pj*F zg|un#xLDzZ;95~xb8?w;s-kk`({*`Q$0exfTk6^QScUF#!es@2|M+1g^6#xhixq6U z7`9#twqCrYXo-UT6~lfCHNaDK*FyyLy-VILp^+V2NGSD0 zo(Eefk6>8c2dHN;tl=JnwqI{v-;?%(kHj`Sv!j{Vz*_H`CGNylZ zzKjR#FBx{50>8p>Gvg|`NK;sJf2ed~UkgIxUM+2lBGX!*svS9w*(ditlha=15O< z`s8uFK17}c+fD_WSdpvg`2yQmwPM<6z5Yel<>awL3U;l~ExAGoUgx5hq_3L!fr9-f z!**B8=xkgyJ+({`cDdL~rprzt%TYVCH^3f~=Z%fgUgGAxV)CU9>^>RxxC}d8h7B&m z=2z20P6I%16%Vjlr{Dx%LUze7fl( z@XY=p`&=pZJMdN&>`fW=rV4hd44YR4n_`A-v4X8H!*&#~z& z-^E*22E@KFFueTTQN&`UajzmcSAAp9@kSqQ%2;KvEyROA4{(-j`CjFs@4FdmwJ zCNvQql!PL0giWsIcMGpxKS-~ql8{y%tZ3bxabSy)Q=zuNf#6~30F8y=c>{{;t`=fbZApH8&^?+Rn3@b}1g zP_d;tgO8fI-BQ9gP1vl|+rSAh)hzI6Ft?QOeBaq%DUshivC2{+V?d=S2&&EvFXpxwYX?6c11jS7EP@QDPiq@TgSQDhXF z=a2rvj!pCVh40t{G&lgBlKs#0+z!3*5gcGsrrreK4m>d>@+R2iqyHA>qostW4cmAn z@~{5uEG2w?*x9RBAYa1Rs%YTe59J4!jBpA&qs4+ zeD4R%rsMjCzaJp{WF>P*=puN?x8ULXRjFzSn6iF^KCmTJB2&`)roe+U`-2I+;Tb$k zsA1#7!;3;3iC8z-OB9dTWbx~m<04lOkH1l%`NJZQ6M3D~*KiP;w#tVFmV|;|MWLQA zO7=VabCtuF-rpgIP-g%L(?#dF0)4MD?U{NSzFo}Sj_Xs3p3`ZhkW)T1g-yEWyKIVl z882l_uHfYD3?3IFEXNM`w|@$s!eh5vN5gOH{r5#&7!QQnmSLBRH&O}1?pEe0W{Rhb_qXV-!5xLNK>uy1DAYis$(^ElUJuM-)v+&|>5^((yS3&}l1 z?gBk=9>LCA?~nHWZ{M9{e}>JsJ{#rC6uktIBg;KUwl6eGm5Bx?kQ)L)sC1MOH3%L6K34H|Lwc9F8;G zKC3-@mTAM_AuVfKL7pNy1EOanvQ3=bZd^@SQ`dxcx~FaV7H8KqeKnz->)HnXaPa>* C+3a-y literal 0 HcmV?d00001 diff --git a/vdyp-web-ui/silent-check-sso.html b/vdyp-web-ui/silent-check-sso.html new file mode 100644 index 000000000..11db1fbf9 --- /dev/null +++ b/vdyp-web-ui/silent-check-sso.html @@ -0,0 +1,12 @@ + + + + + VDYP - Variable Density Yield Projection + + + + + diff --git a/vdyp-web-ui/src/App.vue b/vdyp-web-ui/src/App.vue new file mode 100644 index 000000000..72925f98f --- /dev/null +++ b/vdyp-web-ui/src/App.vue @@ -0,0 +1,29 @@ + + + diff --git a/vdyp-web-ui/src/assets/BCID_H_rgb_pos.png b/vdyp-web-ui/src/assets/BCID_H_rgb_pos.png new file mode 100644 index 0000000000000000000000000000000000000000..ebde8bbbdb5f6d1b0c7c670df357970b5b525794 GIT binary patch literal 16262 zcmeIZ^;eYN7eD&U44p#>Qo=|KDJ|_#(lC^Sq=-mMw+tm6(#?P%B`J+`gM`wdAT8a9 z2;AZQ`QH04-21~_Yu2;odDb~+@BP~Q?6dbi=R_bi6p0CF2><{fR#uYN1^@^W06>a( zIG8V$`sES;@I+l%UPjkze)mVU?Mo{foaNS#){s4iUHgV=YNw7xRQ$ZH_=3=R=g?E* zs%j+EzDE{(@5MuqzLu8AHmnAAJOZwFnMnb*eRzX&Qjo3;hqEQ&pO z(hdQDj&+zUhMpUg3=9D1;BYABg+V?9^W0*BVF3UFmy8+nA|8$h0)TNDc{~6>k^KK% z{y%{j(}?FmtK04UvfE#W=?6zHLnjab5g6n!^vy1DYIkC8Zp}||l=t7_)b3kl?`!}- zq8k-9WL@~gnhoMJL|c&+B$QkLiinJPxzyHoaqW$Zq?k7u>qZ!7Gc=i-%_2-nh;Xnr)O1d57`rwe^UY|a4)e?Zmg$d(#LD6Voo(X!pTRutxZ~i zf3J?Z{~8T-QYsGJwNkL8W>;rsU%oD?@FAVZ0P8(?%z>;+(nh}%u*!UJ*8nmRbM<5J z*BVO!GD&?%)8Y3*GeYX-JUD#wbJfU!xy$4A0|>yRCy(bCcX|3{mAHOvYPvxjELnlj zs}BX&ozAwFjDXZnET&1@K_!mZ?`f1%CwR8@vD2Y>vf$tKmSvteeAWUpG82?jKdSX(!yWW!e=FR zIXAm<3?4^9-GAow4O{Iw>uOp_F(|IkMop}Uv{u8DEm|G-i^}Sc>Ufad?>Ae7+G3xl zv^e|b%CQ5e1zs4|<8Id8636A8S8Z$$2>XK`scD>}*(_Z_w$ITyf`ess^Yi-calhQn zGE_LUfewdzWI;buA}2(TXrdIU%9^LYC|VmV)eSYbA!5Q*zs@w{Pa~B)3f}H_*|nX! zb#UrYvj;`~rxRi7q72(f^}k&ntLla-x#=v^(2b|cpV-&Sb9Wgt?4z#NK zf4e18-s1NEe25J!-Xmk)vXa(wav!X(<|G{sT|1_``0MBOPnyKMcrlf_*xP~o-PK~7 zK1Ja=$HbpMdEDw_P18Fcy4e8Ka~>F$|JU1p=k`(MPTiCrDmd@iP4D2mwJDpc(7P!F z3$GdLl)eLhCS0|9oYIvc8#Cta_fJxpE^5lBDE7&w+4*SUR61W-R{--9=H&;Xt_lMB zETUxAAlx7dQd!cT=jgm_Wm+=a$XEGzdrbMTQ}{fSEGZE@(uzT%)TWl`P5$EF*>-LGj1KY#nDC&M+p~E(e5+$uFXc zM&r2Q5#af%9A3Bfu9!z*A6bhhz_ux>)5P3S7T5G?)YGBHxIN)+1Z6PX=Bu+tk@@lH zk26tbMcCLVo&=MmDLXG<5*l?vjsPKTL7d;ae$eSZy$YVmx;|YjDkkCFNxbW9qoG0; zlBA>2WZDyYobeROw0xcNi6WfFAch3?%!mengM0ZcH%8y>s{Ar@!N%8K-A{ZhZJW8? zobn%Oa)h#f@I+>^xjz<_9c?Evq+k7j9ZFbg%gFW|j}w{*K~ADz#a=rn=O5`s-(gh< z-F!{wD%G~GlS=)M`t}`lMGenZQxo`-g8`lOA;tnT|srz3Rb9ZuF3L=-Otk6;Ci&;{ul;=CqI-ucxU zN+_R87;PbYX`*n+$neDFUzYYz_*Fr*)_){Olf5L6it~(VtQMDb6!;5;?{mqB&PsQSGlNpsztkpAc z+%H*eDN9TWP+~o=-m`4&hETS%m+Grhft1`9B^AdOpOCsKCex}Z-7%eHg5kN3YtD5( zF>X(`sVZc~p>s(iIAS!&72A3H_T%S=w2&lFB+nyIW;r|4cCq4E zXQ!`deYiy8mF{*vum6AGS?NYH6?FcxQC!U?Z$#p#veGbD7r6)pGxBOhX{ymVS7tdI z@gk0_S1jvb8tM8gult8{EImp4VVPH_vxQEH>%RYKF@XhTgyQ?jp)8;l(JTW_PO58U z*PW+EPPVRi=lY?#+INk5O$zXOpdl-tv7@Q3)cT*4U7Z%2q#XPwjk5L~7t6oXSU~R< z6Qoqn@qvN#nu~8CRUlFC&Q@D&I4YrcG**{>dgZE*XOLKB&ZdjMYF4mCGfw=~rRjfa zsI|vvlD5FGr%*|HqKRNP=*?gb2wO~N(hbH0#0YDI7h}DLB0#`ufA2IM?jw4M%1i86 zhUC-xudUp3RkA0IW&e)>Pd=6`r(4Fy7c^&8h5g%-eINCLgy=~>!Ci8yHaG z6yMYOjJIu?3X@@@E(JiYN?T%FVILwaf)_pkbYQrBE)dDs{*03b%0SwqT%mnr>~n#C zWr;2$@fS?#{MOI@+h^wf8`k2x(Xfz&l%0vBAb)Xe;apbhu$scF10qyuj7Q_D&Ki8lZfu;BO-C%uGHBe`*y~oYin$-r0cboh#N-tGM$s}GPnLKY zPuABhw~F!Lq^>YLsQCV{$~K8-w)zZp=Oz}7;2wH~-?Rm5#`~Vq9Sv}CIss2kW;T(= zPh!bJkpVx}vG;nTO_9BN& zNc%-^k1p$J_^O?Piqi>~!Nmlq(b82PeVk{TKw3#?+npt%G7=e=BbZ7CZECPGvtoyO zsL>NegrV)K<}&#j%fU0Izul~($|dMOY>NKkSJ$P3cSnKMSL*>L592K<9JfCWq!$q= zHkP(mVvHVhYw|qu&onN+USh-6Z-!TDqJ$jUM7M;;ZDWS*HToxlrgwnJ8SvK zg%c>^+a@QdG$i-?dL&AT2;Imd7MPap8PzW;%-;%0Wn)L1Q8n)=U(u%6hGtQ&4> z1ArjLw;A_MS%qQ7#zz;-EZaVx-SK2zMxgNV#o#&r-=;112$~k-377m=pU6PAKfZ+;X_~(+%Vh0nVMcY6 z+o|M3@-?FN7FPCHCoHoXNO-QCgcnep8;>5&7M*Cb_p}g3D;Bux8f#_-zV+p$;@|uH z@1qL1xHn>%14l~<&0*+HK9`{_2??{Mj$Gjl95WW&o}~ur5E8DoJxEgIZWq?@jaLOb zMDE}74szkNV&~kdQSsSVZyILta%}tMP4-p%8;ph^$UyD33xu{;Fem$8Dbd)864mbu z7mLs25KEtXqzC_*S8mZarr6c~24y)ie8ibunQv$(Ww==XUZ*BF%x@FsnL_)GTOYH6 z_%V}x*-=q`!65HF=oPcK(KkaDU(F@q}{NF1(+V4b~wG7q|u(SlH27tQ4v(FmIuBNS?A*KFRyW6O<*sW;? zH3>*99-MJc)Lw0;p$otWvY_+BkdD|f}suzOnRc)(`kVh|G zc^y|eXm<;Hjvkw&e1}YOE^`yjnH(CC-IhS$pKxd;Tz5W`;R4;x>s`tviGUfW`5*E& z-peeLzFn}*xGUk9_q`|jZ7=P1zxNhSFFFrvMKZBaqct>sKLhgNfpudPUqma^ zxaUMVN}Tf!y>~SYBcD1F#$4k^TXUj=*ML{}$o0R6o>;m7nFVUa(RX*~mzl&nH+9{t zL_W#}(-uFI7fW4-FLMK)4V4^6MA)jKg1~_A+S_bfiZ#ukV*i2Uh*Ts4ndcdQ<&z>J z9U_(%t-xkRw+4TdVeXP$Omc` zO4v&=W%Px$11a_#K+EoZt>#fZltQhL1l)*!aHL7*k+y%(ANPW(m~PWpfZGM1!z_-- z>8EhSW^?V@jHEQhBs(za&4QXIUCg-Vb2%Oy0Sp$rbVy?#(;<%9`ul&?HhP9JCf?d@ z85OthYe0UE&$9hf*Zv%ieK&94mQ2$VR5^$A-4u`O zP|b~&3*1Xin1!j!pm4Yh-r6bNW1di zC$D5MK~WFGMHb7rYX%sq06FJyE_Y*iZI28k)T!1f6gvFGEG>o~RAzH;bWB4v#avd!gTQwCW5Npq$rE3(!Dk(Bv=ggtO< z`@d9Ok$L6~*l;keo+0(6Buq#DsqW8ysyU4XB8R}&mK1w<2C-Byc9OWLy$OCIO_$*c zEy|GLW10C|K+@TMWWP-OWJC!+Q^~#vjD)g|e3~)2OwUk%>vhvd8+lmHxi=;Cq_@J_73~Ybcp$|sR@T;=YOF@ds zBfFs1JGQQ8Ub#)_SSw{1&*kXRyRwq9PozZVB@oYc7`dIae#Kh|hA&lD4{EXFAp7fU znhSus&$BZW;9|XR9EUvj5eztl}b$1ILP zg8}6~sjO_O4zQB!VLvYL!INzcxiz544v!b?i7F2=&mjaYrIHoud&T^F0 z*bMDueXeB6Ve>e=Y8@6$|ufK3ejHNg0WvtWIoK=Zc#}=#=ixxd44unL8 zR-$AZSBLasM&!6bUa+uarZ3um$9S9G;llU-Zm`Z#U==&0@V|_xEM$dD@(g>L0Icfj z96<)nva2~Op0^zLS;K8LUvLe591x4BS1NK=2;B+kx2jnZ9%S3fTP1@O7iU^IfB%m2 z*eYw?8O=sFdz%*AV;)9h#?4;HZ!+p0_D9mIOT0L#mi>niBWFWCAUs}>?d2e>g__4h zV<8Q0DcRprA(YdTVc~^b()%2yH?Lxe*gq4|DGhL>3tPj+ITr(Qz}4}Ddg70)3?EEu z#<0l(>j!l8dJrTL%@;QwX6``BvDD?@v()Nd=2T{OqZ`EqHZNFz)N(`?cVy86o0aY! zS^!Ox^Uooxtz$wSWc#>|9|?wkf3vc5^7_b7AqDcL5>Tz;e*I+*dQV^SRLCv%m z5zLum45Os2PmtgBxzuYU_(MUQ`=iF$1;@nr}cl$3-? z&kaiXa4Z`O;@Rnh8#suxHtuPiK>X^vLJ5Ltr0tv&RG%U4mrEdHXPV9Gq1*}^t~J-; zDc9em=d0m)@6B1a4wK`J7KDO2h$?*#b=#hG<+q9y3q8@*zy>%4rhm56^kanN!Y@w|GGcHxiKGQ{a1_+w_eIfochH%c|YBQhYW7Fo+!w8_mj_8T9Z6v*XN8DhD6z_p(kNYqP4W=7#Ra(TEGd_~jz^3ow* z7I3eAil51LpL@?iO5e#O&f#+r36ddUqa3ik4=7mq-CS^tj9`-M|HRKPc60L5qI*M~ zPxEDH42eHS=n7HDR2i34Jb!${vr;tZ`~e8K-gk}}@Gpf^ zxL43o=M>8f5FQpxZwOMbBD=R=^3~_yBN!GI*+G-}KqP9O!%Y3?IVR`2n@*|}$@?*^ zj7uQ}b%gR#lA0Pa<%CoFB5$I@+wDsc8u3!Mr=sIs6$bQZKRlVQjBW~9k2(yR+Q`-N z`xBUX_w8Xh`oHk|GkjIl5d%GM(Pd+Sl}`pcAPLGI*2U5rK1{~2`h(#Pcf4I&n@&AwK2QZ5lDAFpfCA?9|<5cm`472Q!t_W=^* zANy2GRum?S4rPk0%vH}{t-(}jz{R0>g}%zFJ7;vL4e*|OM_@gv5ZFcRS!>}+~G^-<$tLsNriu;(;Pj^KmT*&HiR_r zo6)KL2=ZLjp92UApP4+bA;fqLEG_4FBX&__$I*tH zVg5K52LjiKBzY)H38AbE*b6lKW^uR%EMp&Bl9vH0z?2Xhrw1gkPh19V;C>;@V-`WARydM2jBvBX$U0G}E3e7(o_YmYE z1+evtyZm8?C2w+RTXvEUL)xXhWc^NmGKTsi(&({~ z;NnP^=~mzCE5}KX!__X<(oB9`A#@Wyngui|dUz^QWruY}to6pUxCIj+GdmZCq&Hvb zH6rV;ZK(;WlNaQoy?7^rEF&9<3aXas_9=4uclZ%!3k|FE6i~6lkeLrihHQPJtCDx* zg1R*5zT8Kfq5$HD&ugHH8i9|tMU>_b_Ba--9-k|3ZpZ&N##rwhLGU+4c&l8^4^Q$z z5{MinP|D%V`-4HU!X=NW$JPXH|ByjQ42gxk$UY-n>YR%gPKRjriWNV>HWw$-upFUt z|9b3OBNT&+{1m&c*1$X;bKzz`4Tgg#b>~WsPYL907~wYi7)XtUUoE(BmE>1TQ^BaQ zfMagMcU4IaB_NS=nh2kc&`9?UIZ0KM0e=|a5hyUf(Z(eyqf0QqT1Qg+!JXt&19v-@ z>UVa?WQAo-=)OgIM5S7i5fKJAzl*kEOw;pPmy-zD2}&Nk1swQB=n2$9@i z%E#}HHUqnS1&M8`5hE1nNKw!C8p)Vy@F+$OdUwr64~65%f?7hhLJ!q58EX@+9(0P`2J^BF$BTj0TWa&HmrKHxD$AR z_^uiou_}w57|>H?fJ<-{bNy&p?B`LOf@Lbb*T)TJtmd$Sxv&dZ?X=WJD;nZsCUdTk#+!$zgpFcKnXl?^a8n)2Wyv-8 zRuM@vP}uc9HP`1^cB6LLjf1=S<9g2iqqC_b*9cD z#bj9EF(DTORAB|LS!g$5@Mu|jU6w_hy_tT-z-Cb zE)e5XfVzI~o-L{md9RWiKTa&hJ49~Exl`uWFce|VanyIBAy8T2W647#CfQcrWSpQP zP;MMQ{T%<#wMHY|?S9EaGXZrh^tm!%`(q>N4iZRj02HCVdN%=+n%SQ%C^jx1X}ldw zV2va3I}?8wanRhaU}b{ygcVZ{XfiH#zL!OPka4&ik>fhV{}7g%FtnK;+0q`K$MaO_ zM0Q=3JI_BT@HmqQgSu3GPcs#Q2ZAQLX|QBDn|*iZLWM9cl(#GUci0h&)rA0<${LVp zZMW9argyaOtbP*865M57Y1!hE_s$USCB$+R5NXtqWP(UerHqnA$LRJ&_B$LrZnO&- zvrJja7PE6hMM4IN4^**`;otyN>j2#UCvpQyAtQXh9IK;I-vtxjRx@$)x zU!P-X{g=e?k;o=h3^C0>?P>j+#}WZuCOBLI2~Y5& zz*V&>oyVgQ7LEg>K8x!KON8d09fMPQ6?!R4d19pygbD8Ad0|vyA~9Ngbj+0!F~W#G zugw|b`1PFB%^2e%F)6Y0kHq5Z?msXDc|sMS)bu7~li|WPeuI1paz*wb<&qnSB8YeH zYttL>bof;z9W(Ni4pj8=Spq6b7N@qfKsRaqN{V3wMDj>*rS! z14mA;@Q`8l{1{ToLq@5tUA_C-sTF2k4+1HtH`PSvefy<94I4@psqqY2*Bh)xzz4jn zGKe(Kvd_FElb5iV*&SWUtdl+@n_vSQiyCt?!d)!|26@( zR8eaR_x@7UUuDI8%JE=*(U-Z45Y+$X#<@lrt6@HIBLosRtyabQ12OVd#NGd`@v}f={ zEzCw7IO!bR+>M+J-zIJ_L1GbCEURJOMsen`Pkl6qwQJtEzLA} zGtRj5WlYcrfb{L&NlsK-%JDOMX8E;K1pMb%x}m=yG9(m^VP{Ip*(X44ardBt!cBnY zQu(1AVGE#Z`xp_{hUk|zeQ<7~`m@(L@{8T~O!=m`Q6D!!Q2bg7v_6=^^u$ryOItAF zZ6G*eFp&I*xQT;*VMX9rF5J0%p+9DCwfw^ej}B`%b6JM*jONE5A(Ob@?3S~>thzo7 z6gB@#k2)AJsL+9n3{-@bx*p^vy6eMTSANF%89N4>XwpyctEy>}VBAq@7jP_)SKBhJ zyT8hkCp4R5%<sLJ={6=LDz z^!mTIpBjeML)iwztu9S6GTK;FmECHvob?FRAbT^|M|#msk@lO*S(VS@uH5zswS%jB z<7E6#0`10Tx}NtR_u83f7qy%hZf!iRQyoS=^ZY5A!k^U%Ab;P6-N!h49ZH;7304GI zT9;g5Or$SotH3ZoJejsp}Fin5+p|CiltGp5YnFeGppkyJUef&z1qTO?D$HE6(mxu zTQ2_o6)|~$WUPDDKidhko&$%4Vyj+-rTQ4Lm*XF=ss3qp1I7CLqJ3qN1|B%Po3SV5 z*@T|9f?u5w*UrD&ck89ul21l-Fw1RzWK-@uDx4hojzfdVCU32rs-TL|J{adpUoDmn zV2(8z{+Ajlvc4~D`I6+!T%t`Tt#Tms`DutuQoacH^h1Z5jx&g5%al2Tp=_+CK+bNbE z$B%Y%(q%;4F>$8%d%N?|+0yMt|CudcT>Vm2Iimj!Sn!|wND%aPRI*C=(;slO7x&;c zeF=@lOQOB2!ms39bGdYTRvDhhVL>HtQxaBm?wz4hd|V-ixYwk9E^Qn})X(!EBJ-!&$?)3LfK3M$tL#4u_g|u*`qCiX3oQW}bu~TLNR|oSde`5{b{&#c(v!7)J6sPw28d!$U z&Hr5WyaeK`Wx%-26DFf7|dV8VD^H_d2~&LhUxM1e2Mp1eM` zAn=^gvegyWdDLVIcjin{>X!yhex=R|s7#`$Y#~eyuUY6>y`;&QZ4DBXKPmauayYg0 zFHxq=wf)ep{p?6H;7_-D|BHKo1%#5HZ^w?<@biOYYLtI z%{9D6{T%=|PBoeW)%EhU_&hQ0-vn+8^Fm@l#SG}kWAWyCK#o!9s9cRI^iqMTuLvdVw{)>(M4HVO}4*8{xS;v5({F(WRJp1dxQPsasSpHz5O?;AFv{! zV{c8OIDr0rZ+0^Z6hG)({9hl5e8c=~Dr~nl5`XgeJqqvw? zIL0@Gl9`}6_1)We1B3`LM6UfXl2R#&`oUV5tYo6-bs;UKmLEgD2mc)pAEU$mTu^WH z`{RZeONKl$qG~>~gpH$2Gv?;)aqmpPzvJt$fV^Fk=V?BLoxi>~(pudFtRJ=}U$$=( z=W}u#DdRC0NHRq$1^S+8wzpnwon<<;2Sdg9%@H!aKaw0^OVee6A^*_^j=A? zBuqY3Qpg3$&$;xlPW@tgjA@_HjhU&$OQOEi8n0OqMqu>=k2wTNP)tGU0}Uez`YxX& zx^Ml;d(e7aLPea#g%V9ySnyjJbDE)Dx7p+waHZdFvGl1af3FC}K37&bPKup>u~_HS~pqA72R>U%p16E-;Aeu}S2-i`jd#j)DM12u)&sjo)R zu74T-wJ1pdf4$a;zY;BzPlxrAl>kf_xclm9pht&X917h})tV ziK3=1|9D=DOGJv4SY-68@OgI1Shs42kDv*RUF=~RrQtSu!?NVgEBn{bL-0e#1~0RK zv_*UKN6})Bb%QdX?pbRv4S93&gct)74>P`dc?8jbDI6uzSS>$ ztk`DklQK!kJdEwzwu3693-9GxK9+M&0@GB5YhDH;O-<%~D_T2*vFldP&q|N1!~174 zEv2E`Fe0LyuS=L+&7G<6s=1c<8XbbNG*1{B%_DkRUg`^Ja-2k|DmCw z=hL1P7yXv~@px*VLD{S*nP}l%+xG)rCy`M)BETszso|Z??Xv_g{+=V_O%p) zf@j<@czbkS4esmBfk8Lgo|j3(K>M{8V73`NP4K*kG*UE>N+(*}qR4a6HBH;MgZKN! zd}8cLweJ8QPlXV_)}Xk96FLA zBER=ub?BkesSYTH>9_wSPVwjEloziwl@vZvKhVe-qi?R)v6t5vG@g%ew7fG7B8fxq zh)*2_r#3RFwa{?xQWh2V0E(4GijR;qy~iJROPpsqeptlDf1mp6cQ0@1NjMroaX|Y0 zk1pzjA--pIC}&=el8F%7@0~R=w?z5d)6d*8CPT!C7W$M)4wS68i{7H}30YBI`F*b`u%EJ&M5*Fo0k8Q*mZtob?A#06Mbpr1yunj>+?J&% z(~mGTy`7{JHeNA_utTj7MyohIpkZ*tzk%q&@UZau-5rmw%>vsY!!+V1xP>HypBT+u zRB|J8M?LdUL$ZkLM?=BnfzPM>r_%9@hku&tqfi;WIh3|_F1Q%OMbKA$im&jzWlaC- zCM#1}mi7391}elOO+|fafsC7ayie{Swyti=yK(RkbDeE8qrbqnv@EP7%i7jKJwyal z(+bD%4nuVmjU9sp>4}VOcm(D(}kmhigJ`462@@I45!6fEldgru|8-R2<4r_ zlXy|)B)-I^TRfAL$fN90V2h-K*%SN=ppe~|>9D6cb%8FOmfest*A@V&RJp3eC zcx5-&9{S!tdmLG+-O1mhfi}!3Z9))BDHp#vF{;+Ep>H^EfsR%=o$*- z51%&Ul5|}7omke0U!qr}I4wh)OuLG`@|QI3KGHKZPBZUq17)KqPEXupWVH~X4O}%S zTT9RGq}m?$R=qxO=|W~patSJ9W{jdbf@g#S&6J0Vj4W7E)@8|*X;j)edojt;ZPDyO z*@t8Gu=RptjVh|>p-wdp#Y@Us)HX}HvwE)z3*^Q z=Y?NI`c-vPTpzSvwON-=P~6%4_TF3D2nGWVbjL5~k0NHCfm9BEfz5C*sz417G?g?P zqddxyeBK%hYrt5M24c#2Us>XBQWHwscPTd$)vU9lTVAP9FI0pI3I=cg=4nz8t(L(W z3|vUWnncy){&iu~F1x77AeeQdL=NH?PrH1Iv#K(dQ^<9pLP`iOJyplxvt5pNRW0>O z{%$AjKjq^w=g`2>|AnI*czqxd!-os9 z%IDQrZxG(uyOv69LK!{%F`xK;pTJ7<>z%2AoWI~P|E6FGt*qtsD~VJ{eM*a3k$))%GreQ-w`L>jG*iz#wRaXKy4TAoWtkPd3!ut)J z{2uZlHsw2$w$QG@&dP1K(?R(fax$cJnsByZFsCKRN;vM#rJ6E>AqN`ue@J6aYsEJ+ zZ)R4Z=3Tj|uyyOfr9a6bjQN`G7GT>E^@%kW^rwOKr;{=Nnd1LC35I0x>&oJ?5x@!H zOdubm~{6zSeKJIS3VgQHo>9@%{IEb8>*+2_?#tV+tYZ zgnnIqkoKaK`zYdjhXnQ5%JY#gE8k%`-Jwwyk$jf(T7( zbCSI~kw`olqn1Se3f)H`SS+{OAX;y={j~D;U7(*cp!< zSIb)eCQ$K{W02x6Qz6m~bHV>Tc<_-Tu2cFbsNXA(6hfSI!9BXgN>dzsv1yq`#2GYK zC264*gF`?`3zx-f4xFzvhDFX!Qk8;h;YaUkNMciPI1gIhe?z!O1Qe~i{c8*;3T$?# z&LMw+-k99z%cz>Co_dW&aZ5f=dG@PPo>IAf$1- z-*@;1_6e7^ctKqK16?uX;q1o@UzU;N?(DD1pOGZPz13lESk?m)yeRb*EknC?Z{3L#Mjo+Pj~Jmwm$LIDQQ zvr`8;IkLg9Dq=o(v01iQyhRA>;|wH--VC(uDL` z|31=jgu6bM*LTGA;>XD-H_yFR*D*zS6kQ#FncdHfQQ?nd!_9%RcA0dzUCO9hQEj=O z;jSf;ynxrU#lDk3&Is&SOSrIl?Lx$<$p3K zOe->8apUy5*Qjp3xcOpZ$^jc&1o<;f+1R61fgkyfckeSvu0I&_DPKO~Yh%xfY%u~W zkm%6_YlxBvl8)?)Y51AL>$lgI`!C{O<{MwBzVo=H>tfs?)~O0n(*>Oruo`=9 zJBX>-jW=uKaGs}Id(DIq>9G`@KKN-l#=m~k7KKUJipV$g`mkDH6(hGQ{XZi0l$O*ZVTegDjtecq@ zTfV+RT8&L#7R$dYzmj=yK~gfWJir!+zu*BDXeA{ZCe3rnR&F1p~6e>4!p+g*cgkUCJl)qaf><&3)8QH2D5k zv0(3Q&A(`3|567Vj(R`GoXyRreN7P#T!(LQH!0;>7*1OFFG-?r*M~n%>`dn;WIhu3 zGOV+~(-+fTMQIu>bNPxHv+xMu9|Rc@&*#y%=5c+wt#gg$eV6_;D*hner@e3ELz{f~ z0|tYEOL=bky7YUUr@iOwW?!ye@TSJsI{=T~sw)urN=D_WT76-#lP8+zm#3{`_K1E? z&hxVkLd|A(aLqfmz$Z@|A=XTKLoZ5~@xfErowNqtR!I2oslwPetnEq|BbGRB;h5Xc432kId3p|uU&qA=$kw2Hh zLz6oB*eR`jK1-Ehrwu=S9LMuswqiJmM~9J@D}`~!G>v~k5J|>$8fD>8{a9=z^55Q6 zY~Gqyu)O6{hu0PL)HAV~B|pU2_n!7L{E7MU7MtdRzxVvnqN6Cuy<~Kt3cnb!Ccn!2 z2U)+&=F>c_$&QyIpGm?jJeu3#%@R>b{->EY-b^``Wn>pTzt5ExM`Z9y=)#$l{6+b{ z(^0BZdDCP(XT%gR;(Y?~f+T~>OIEOZ3uH@&#mQtbUvVN))pIwGPt>T$zGfP-V?M3j zM_|bev`vaP+>kN*c}c&+R8G9_L1myW)i0)rOgB;`!+QD9UmjwL6?mBCh9#+Ro0y{E znvC|YbI3S?jNDm_F-iye|GXcsE2K**DzqUcME?&-NfLco|79^wm-p$t%y+dFS%?DO znAZDTL!uAALIpJNnb$OZJ?$q9pb8d^X!Uw56*N}rGP!Y(D%0X&@-jKxqR_!y0wr^- zgvX2}dvEjWRnQ}afMwK9lOWEqygXt6xqf+ieR)ue5AY!ZHzzJd+cUO_i+m@CF+d@#thuq^^-(z`c}F1T=f!N1yVXRov(RYuIs zOKo|1AeZ(N$w`lG-ht{r?^QUqDF`!m$V5pqj5Kz-R(cR?v{IkTnnbzW_uR Bx&iQw1kp``mdiiZ~pv0@1Hq$-*whrXYI4k%$>P0hWZ-xG+Z*O)O9rdb)5&Z%+w(gtvo}M6jn1kOzU}m4bcj9o?N!Utxa&gr}`#Bk-^-Ubn?vAnuekDa3`CvF;;OT_22M2q4c=^MF z75M+{3kUEY8p03$w*=*`z_0wLAlO3B5UlF$=LD9KfQdUwUXub}mz9u&L1m?`iGih{ zlGh>7>kvt4ai}C53WH0_g8v%)iZoz(KZG;fNKO5(V!%p)-vx#8fkPlcK|vBh(h}Z& zNQk7YtSkg71(A{x2NdG|Azmo^U~w;hfj<^!9%@ehj()B_C|7SU@E=cm2k!us0zW_G zPZ7vpRe<&&fA1(2uzGq$Q({)hWVqAttuA9Mel*skaP=j182MezS*ffTk0d?whgVLbx| zULXEqYB|ope?>#@0`s5}vw&i3tDsF-Ikrt05d3GSxHb`h!F56Y-e?w30@wo6GN|M( zy6g-C(b%^T&;Sm&PVqA_w)OHKrjh4tY3x^cCU%OdHbE7iVJmc*ZFt2_x8#2ob$jy3 zJ*M`I*{05|{`m;iW?@N%*03a(QE+bRTDouz=d(=D|>;jmrLMVNiGKaJX|VZ+aa z?~PmIzX$D&817GbA8z&Te%1sw5`M<)^vmOaM4p};5$02YlMsJpoE#H&2UYgQOtG!P z+qLY(U$20=$NO^!tL22bq@%r=eT+MCx!{6HaeOPEVJ#EuY`-eQ}04}RBK zp+C+nY@lDX3}cq|F|PRU_Xuzr8R6yLI3*JUSY%d32|d*kKp>GX0#>NA)<15 z@?(AH*c_&13NuQ;&3AV-9UdK*mlscreU>V zZ#9Sh%vz+{{c>($s%L{Nh`9ATO(cwTr0bg+VW;Ec8&8Db(DHdkaxC=?r*GWy*L>D=g_CLo*0`GKTg>2~xpM06 z7fkTwNeDk@ZF)tu5o|Z|eBV(wvcG!#{bQR)G9uEFd5y&CW2t_eI;vgPgwU#0F2 z7tQUDNRFqnyauCJxqg0ay|Gvdnrm?6#x%S+P-GXVF@Ai^_6VBYH8K0t+c&>uX1IsW z%qch?BNKJkOANnWB)3&xB(zdQbyvXbyTzfJkkokY2+T^(t%@(eUr)}=N{1O%R?gS< zY^s+V&)Tne8qzAr`KHJ3n+xR97f{qsC7Z>Ws6K?r z7@6Dmj1{6ysk5)KGs+U`X=oy#JukX+Xf6oRl58`k&^jCiR(2{I=`(%OWjW7zmHoLV z*-upZwaXYQs?V`x^CoYDvLl!m>({1{@l6?`s(Z2??S6SVTx%-*UM3@MFmNT4%qM#L z4u@~%by)Qxvs*VU zoDTMMx8zdp;_xWx?={nHZB=|8gXv4(v@uSKJ$l5Z7Nf_Hf#Z}(a=A-~nh3r?*iXB2 zfi)r)Z0+B1ZxS_XcvHvtg5S-+U@kYs157_uBQK0s^HW_NGF13FmfLKUuh!j`p-gZ5 zxqsH@i#mBXndeuEN1#=F|1><6Y*RbD8=N z&~}xqKPuYa!#}Jxc&hgo)RZo1%a_EkoU-zG-qCH7d8Eg!rZpOYgn`A9lw*{Jx>K0# z8!9#gGT&%7dK&M1HHoh1nkw;%3K*6!3pQ4uP8EN)+p^cqhiVARCnfSRF zXn-P&n_)G{S_LyIk3pHMs0zz^ipbp17B4ALSiWrci4{KJ>EgBKvQaz!=xe>G%EW7U z<$!>dAVS(BQ^c&d(E}u?d=h$6V^B~s`qVKqTYBMW#1gqNtjb=%S#c*58-f5WJDa>Q z&Oqbq639pME~vFr@Y_{LTQy2sQO7tZQSb_SO!8oG@Qd+mO^Z_dbv4rqE9#d}5DLUP z%#j>lV_Vv6;neTrq!Y>VLomAwC(OyQ9J>OFwYZA;oJM0Vd5NU+AWu_jAjOG!apN7Y zMyo<4d#GcJjclRuIHq(y#VK-zo(u2tZB5ROJ#Zi+gR4Z#{C3MJ(tm`94q0v%tJe4= z6#Pru;sL2{2ZPNb#Hq52G1-?|E>yfcgduaNkL;oa?)42DNf0F|#_#Pgm2FL@`$TMu zs$1B%Sw*fg`iyCGazBki97Q!|(7Gl(VeKRD%80>MwQp@9y zwnfF;@y>?VXJMJbQ>6@z=0&x;J&oxyD*KO}H$rfmyb0llUv_BjxM63X1dOIH;RU4f z8=k@meZ8~Mq9^jYUry)n>pBQZ3NkDs*=>^*Yv)6~l6Fh=tPw?mA$C(vADjq%{gA6TxR;ot5fdpOS;oXPn-Be7ZD}dN2mE=caXpzdm;Z&+eddt zNUV`-bKbF2l?ex|2PE6&TiiU7lLOP; zv*i}Er)YnWZWbPK*N!iD6KbJt(?h(qk`MUI3dmbxnp6zZP&a0mkX5SRrRJ91 zlJb=cIhEvCM?@L3Fry7rY(b=Nj$LcBT;rao#&)rku<7$#)3;xNuQ~)-s8`4^@l>a* zdYIPs-{YnK5@IB`?mT7u>lo63%FhDDtvB7ct8I<(>s^{xg_2%>6S_$^Z6!$5uO!CJ zK1fG>G{c_K8lN9<#;D9qt!{C7$g;yEhA7%C)jWxTuypp8CrwO)WsC0*paGmBn@I_b zi_#uUd2lITlDf@F?fx=y=QfQx5eK-!f`nJ$)q2`lZ+7DR=|b^x?J|2JK5+ zx`7VQpd~DsYr9v@tMri|nSCl&-Ppvkop*4Hez&0Cp0$V@)=d50f9zUStS41-{L(ri zv4R1n%rS;ukZ$dM*vZJi0z$&J-9t?(Dr2_CT-Z{#VEXK0)12SlArd96km!dG1EZ~m zo(lPL=^aq!(xz7?Dp9wHgtA;o?bAk9vi@YI>R^L7WeK!fhg;RkyDL=_`zKPglWxs< z8nhvfgq~fFX**a*`f@Yr0$nJkb22W>Rwz%4aDiDz;@4F^czj+Bi?+7DsbERhNEC$k z@f>5-3!Cp1#HifgiD3)rjv6_$jf*4E?4S2j1mqXY&~buq1s%RL87bFaqsZwRt0k?= zV(v{|fLzRZomy-sdE1*Wu}7RF`CY5h?ar9V6=snh-ig(^XdkKhzL2BE76a{EW0c*5LB5R5q%dA1D`IT$~LO@%-vujL>wO%8F&A5w4rb3#0torpJ8k2Fxa zK0xlC7#g-Sat}1fmsqTHw2NKnX~DJ6?d@3Nifn8BiA$rW25AT_MROdl^jx`^r2fk^ R;4cfLbxU8ZRK@PWzW^>bft&yU literal 0 HcmV?d00001 diff --git a/vdyp-web-ui/src/components/JobTypeSelection.vue b/vdyp-web-ui/src/components/JobTypeSelection.vue new file mode 100644 index 000000000..6076d3d22 --- /dev/null +++ b/vdyp-web-ui/src/components/JobTypeSelection.vue @@ -0,0 +1,44 @@ + + + + diff --git a/vdyp-web-ui/src/components/common/AppConfirmDialog.vue b/vdyp-web-ui/src/components/common/AppConfirmDialog.vue new file mode 100644 index 000000000..aa5da9acf --- /dev/null +++ b/vdyp-web-ui/src/components/common/AppConfirmDialog.vue @@ -0,0 +1,71 @@ + + + + + diff --git a/vdyp-web-ui/src/components/common/AppDatePicker.vue b/vdyp-web-ui/src/components/common/AppDatePicker.vue new file mode 100644 index 000000000..233d55bc6 --- /dev/null +++ b/vdyp-web-ui/src/components/common/AppDatePicker.vue @@ -0,0 +1,32 @@ + + + + + diff --git a/vdyp-web-ui/src/components/common/AppDateRangePicker.vue b/vdyp-web-ui/src/components/common/AppDateRangePicker.vue new file mode 100644 index 000000000..45a2274b3 --- /dev/null +++ b/vdyp-web-ui/src/components/common/AppDateRangePicker.vue @@ -0,0 +1,97 @@ + + + + + diff --git a/vdyp-web-ui/src/components/common/AppDialogDragger.vue b/vdyp-web-ui/src/components/common/AppDialogDragger.vue new file mode 100644 index 000000000..149bb7b13 --- /dev/null +++ b/vdyp-web-ui/src/components/common/AppDialogDragger.vue @@ -0,0 +1,77 @@ + + + + + diff --git a/vdyp-web-ui/src/components/common/AppDialogTemplate.vue b/vdyp-web-ui/src/components/common/AppDialogTemplate.vue new file mode 100644 index 000000000..9d17f4304 --- /dev/null +++ b/vdyp-web-ui/src/components/common/AppDialogTemplate.vue @@ -0,0 +1,45 @@ + + + diff --git a/vdyp-web-ui/src/components/common/AppMessage.vue b/vdyp-web-ui/src/components/common/AppMessage.vue new file mode 100644 index 000000000..7c9dcf148 --- /dev/null +++ b/vdyp-web-ui/src/components/common/AppMessage.vue @@ -0,0 +1,40 @@ + + diff --git a/vdyp-web-ui/src/components/common/AppProgressCircular.vue b/vdyp-web-ui/src/components/common/AppProgressCircular.vue new file mode 100644 index 000000000..a4d04db43 --- /dev/null +++ b/vdyp-web-ui/src/components/common/AppProgressCircular.vue @@ -0,0 +1,44 @@ + + + diff --git a/vdyp-web-ui/src/components/layout/TheHeader.vue b/vdyp-web-ui/src/components/layout/TheHeader.vue new file mode 100644 index 000000000..7ec1f9628 --- /dev/null +++ b/vdyp-web-ui/src/components/layout/TheHeader.vue @@ -0,0 +1,125 @@ + + + diff --git a/vdyp-web-ui/src/components/model-param-selection-steps/ModelStep1.vue b/vdyp-web-ui/src/components/model-param-selection-steps/ModelStep1.vue new file mode 100644 index 000000000..aa93c7878 --- /dev/null +++ b/vdyp-web-ui/src/components/model-param-selection-steps/ModelStep1.vue @@ -0,0 +1,252 @@ + + + + + diff --git a/vdyp-web-ui/src/components/model-param-selection-steps/ModelStep2.vue b/vdyp-web-ui/src/components/model-param-selection-steps/ModelStep2.vue new file mode 100644 index 000000000..9168709c2 --- /dev/null +++ b/vdyp-web-ui/src/components/model-param-selection-steps/ModelStep2.vue @@ -0,0 +1,184 @@ + + + + + diff --git a/vdyp-web-ui/src/components/model-param-selection-steps/ModelStep3.vue b/vdyp-web-ui/src/components/model-param-selection-steps/ModelStep3.vue new file mode 100644 index 000000000..e8aaa1b1a --- /dev/null +++ b/vdyp-web-ui/src/components/model-param-selection-steps/ModelStep3.vue @@ -0,0 +1,72 @@ + + + + + diff --git a/vdyp-web-ui/src/constants/constants.ts b/vdyp-web-ui/src/constants/constants.ts new file mode 100644 index 000000000..12f6d236e --- /dev/null +++ b/vdyp-web-ui/src/constants/constants.ts @@ -0,0 +1,15 @@ +export const KEYCLOAK = Object.freeze({ + PKCE_METHOD: 'S256', + ONLOAD: 'check-sso', + CHECK_LOGIN_IFRAME: false, + SILENT_CHECK_SSO_REDIRECT_PAGE: '/silent-check-sso.html', + IDP_AZUR_IDIR: 'azureidir', // Identity Provider: IDIR with MFA + MAX_SESSION_DURATION: 8 * 60 * 60 * 1000, // 8 hours (in milliseconds) + UPDATE_TOKEN_MIN_VALIDITY: 5, // if -1, forcibly refreshed (in minutes) + ENABLE_LOGGING: true, +}) + +export const SORT_ORDER = Object.freeze({ + ASC: 'ASC', + DESC: 'DESC', +}) diff --git a/vdyp-web-ui/src/interfaces/JobSearchParams.ts b/vdyp-web-ui/src/interfaces/JobSearchParams.ts new file mode 100644 index 000000000..fdcc80617 --- /dev/null +++ b/vdyp-web-ui/src/interfaces/JobSearchParams.ts @@ -0,0 +1,10 @@ +export default interface JobSearchParams { + pageNumber: number + pageSize: number + sortBy?: string + sortOrder?: 'asc' | 'desc' + searchJobName?: string + startDate?: string | null + endDate?: string | null + status?: string +} diff --git a/vdyp-web-ui/src/interfaces/ProgressCircularState.ts b/vdyp-web-ui/src/interfaces/ProgressCircularState.ts new file mode 100644 index 000000000..777dc0bf9 --- /dev/null +++ b/vdyp-web-ui/src/interfaces/ProgressCircularState.ts @@ -0,0 +1,4 @@ +export default interface ProgressCircularState { + isShow: boolean + message: string | undefined +} diff --git a/vdyp-web-ui/src/interfaces/TableOptions.ts b/vdyp-web-ui/src/interfaces/TableOptions.ts new file mode 100644 index 000000000..9d3fc9c8d --- /dev/null +++ b/vdyp-web-ui/src/interfaces/TableOptions.ts @@ -0,0 +1,6 @@ +export default interface TableOptions { + page: number + itemsPerPage: number + sortBy: string + sortDesc: string +} diff --git a/vdyp-web-ui/src/layouts/DefaultLayout.vue b/vdyp-web-ui/src/layouts/DefaultLayout.vue new file mode 100644 index 000000000..5502264e7 --- /dev/null +++ b/vdyp-web-ui/src/layouts/DefaultLayout.vue @@ -0,0 +1,80 @@ + + + + + diff --git a/vdyp-web-ui/src/main.ts b/vdyp-web-ui/src/main.ts new file mode 100644 index 000000000..a50453c06 --- /dev/null +++ b/vdyp-web-ui/src/main.ts @@ -0,0 +1,33 @@ +import { createApp } from 'vue' +import { registerPlugins } from '@/plugins' +import App from './App.vue' +// import { initializeKeycloak } from '@/services/keycloak' + +import '@bcgov/bc-sans/css/BCSans.css' +import '@/styles/style.scss' + +// To ensure that unauthenticated users cannot see any part of the application, even momentarily +// delay the app initialization until Keycloak is fully initialized and the user's authentication state is confirmed. +const bootstrap = async () => { + const app = createApp(App) + + registerPlugins(app) + + /** + * NOTE: Disabled user authentication for deploy testing. + */ + + // try { + // const keycloak = await initializeKeycloak() + + // if (keycloak?.authenticated) { + app.mount('#app') + // } else { + // keycloak?.login() + // } + // } catch (error) { + // console.error('Failed to initialize Keycloak:', error) + // } +} + +bootstrap() diff --git a/vdyp-web-ui/src/plugins/index.ts b/vdyp-web-ui/src/plugins/index.ts new file mode 100644 index 000000000..2167077b4 --- /dev/null +++ b/vdyp-web-ui/src/plugins/index.ts @@ -0,0 +1,8 @@ +import type { App } from 'vue' +import pinia from '../stores' +import router from '../router' +import vuetify from './vuetify' + +export const registerPlugins = (app: App) => { + app.use(pinia).use(router).use(vuetify) +} diff --git a/vdyp-web-ui/src/plugins/vuetify.ts b/vdyp-web-ui/src/plugins/vuetify.ts new file mode 100644 index 000000000..e1f51d622 --- /dev/null +++ b/vdyp-web-ui/src/plugins/vuetify.ts @@ -0,0 +1,41 @@ +import { createVuetify, type ThemeDefinition } from 'vuetify' +import '@mdi/font/css/materialdesignicons.css' +import 'vuetify/styles' + +const defaultTheme: ThemeDefinition = { + dark: false, + colors: { + primary: '#003366', + secondary: '#fcba19', + accent: '#606060', + error: '#d8292f', + warning: '#f9f1c6', + info: '#d9eaf7', + success: '#2e8540', + anchor: '#1A5A96', + background: '#ffffff', + }, +} + +export default createVuetify({ + icons: { + defaultSet: 'mdi', + }, + theme: { + defaultTheme: 'defaultTheme', + themes: { + defaultTheme, + }, + }, + defaults: { + VTabs: { + color: 'white darken-1', + sliderColor: '#fcc219', + centerActive: true, + showArrows: true, + alignWithTitle: true, + fixedTabs: false, + bgColor: '#38598a', + }, + }, +}) diff --git a/vdyp-web-ui/src/router/index.ts b/vdyp-web-ui/src/router/index.ts new file mode 100644 index 000000000..5d7030d7c --- /dev/null +++ b/vdyp-web-ui/src/router/index.ts @@ -0,0 +1,35 @@ +import { createRouter, createWebHistory } from 'vue-router' +import type { RouteRecordRaw } from 'vue-router' + +import JobList from '@/views/JobList.vue' +import ModelParameterInput from '@/views/input-model-parameters/ModelParameterInput.vue' +import ModelParameterSelection from '@/views/input-model-parameters/ModelParameterSelection.vue' +import PageNotFound from '@/views/PageNotFound.vue' + +const routes: Array = [ + { + path: '/', + name: 'Home', + component: JobList, + }, + { + path: '/input-model-parameters', + name: 'ModelParameterInput', + component: ModelParameterInput, + children: [ + { + path: 'model-parameter-selection', + name: 'ModelParameterSelection', + component: ModelParameterSelection, + }, + ], + }, + { path: '/:pathMatch(.*)*', name: 'NotFound', component: PageNotFound }, +] + +const router = createRouter({ + history: createWebHistory(import.meta.env.BASE_URL), + routes, +}) + +export default router diff --git a/vdyp-web-ui/src/services/keycloak.ts b/vdyp-web-ui/src/services/keycloak.ts new file mode 100644 index 000000000..c1b377dce --- /dev/null +++ b/vdyp-web-ui/src/services/keycloak.ts @@ -0,0 +1,250 @@ +import Keycloak from 'keycloak-js' +import { useAuthStore } from '@/stores/common/authStore' +import type { KeycloakInitOptions } from 'keycloak-js' +import { KEYCLOAK } from '@/constants/constants' +import { Util } from '@/utils/util' + +let keycloakInstance: Keycloak | null = null + +const createKeycloakInstance = (): Keycloak => { + if (!keycloakInstance) { + keycloakInstance = new Keycloak({ + url: `${import.meta.env.VITE_SSO_AUTH_SERVER_URL}` as string, + realm: `${import.meta.env.VITE_SSO_REALM}` as string, + clientId: `${import.meta.env.VITE_SSO_CLIENT_ID}` as string, + }) + } + return keycloakInstance +} + +const initOptions: KeycloakInitOptions = { + pkceMethod: KEYCLOAK.PKCE_METHOD, + checkLoginIframe: KEYCLOAK.CHECK_LOGIN_IFRAME, + onLoad: KEYCLOAK.ONLOAD, + silentCheckSsoRedirectUri: `${location.origin}${KEYCLOAK.SILENT_CHECK_SSO_REDIRECT_PAGE}`, + enableLogging: KEYCLOAK.ENABLE_LOGGING, +} + +const loginOptions = { + redirectUri: import.meta.env.VITE_SSO_REDIRECT_URI as string, +} + +export const initializeKeycloak = async (): Promise => { + try { + keycloakInstance = createKeycloakInstance() + + const authStore = useAuthStore() + + // to avoid making a KeyCloak API on every request + authStore.loadUserFromStorage() + if ( + authStore.authenticated && + authStore.user?.accessToken && + authStore.user.refToken && + authStore.user.idToken + ) { + keycloakInstance.token = authStore.user.accessToken + keycloakInstance.refreshToken = authStore.user.refToken + keycloakInstance.idToken = authStore.user.idToken + keycloakInstance.authenticated = true + + // Perform token validation + if (!validateAccessToken(keycloakInstance.token)) { + console.error('Token validation failed.') + logout() + return undefined + } + + return keycloakInstance + } + + const auth = await keycloakInstance.init(initOptions) + + if ( + auth && + keycloakInstance.token && + keycloakInstance.refreshToken && + keycloakInstance.idToken + ) { + const tokenParsed = JSON.parse(atob(keycloakInstance.token.split('.')[1])) + // do validate the IDP in the JWT + if (tokenParsed.identity_provider !== KEYCLOAK.IDP_AZUR_IDIR) { + console.error('Authentication failed: Invalid identity provider.') + logout() + return undefined + } + + // Perform token validation + if (!validateAccessToken(keycloakInstance.token)) { + console.error('Token validation failed.') + logout() + return undefined + } + + authStore.setUser({ + accessToken: keycloakInstance.token, + refToken: keycloakInstance.refreshToken, + idToken: keycloakInstance.idToken, + }) + + return keycloakInstance + } else { + console.error('Keycloak initialization failed.') + keycloakInstance.login(loginOptions) + } + } catch (err) { + console.error('Keycloak initialization failed:', err) + keycloakInstance = null // Reset the instance on failure + throw err + } +} + +const validateAccessToken = (accessToken: string): boolean => { + try { + const tokenParsed = JSON.parse(atob(accessToken.split('.')[1])) + + // Validate issuer + if ( + tokenParsed.iss !== + `${import.meta.env.VITE_SSO_AUTH_SERVER_URL}/realms/${import.meta.env.VITE_SSO_REALM}` + ) { + console.error('Invalid token issuer.') + return false + } + + // validate subject + if (!tokenParsed.sub) { + console.error('Token subject is missing.') + return false + } + + return true + } catch (error) { + console.error('Failed to validate token:', error) + return false + } +} + +/** + * checks if the access token is valid, refreshes it if not, and stores it afterwards. + * @returns null if valid, or null if the token has been refreshed. If the token fails to refresh or it is not available, return an error message. + */ +export const handleTokenValidation = async (): Promise => { + try { + if (!keycloakInstance) { + keycloakInstance = createKeycloakInstance() + } + + const authStore = useAuthStore() + + // not initialized, the token not be refreshed + if (!keycloakInstance.clientId) { + if ( + !authStore.user?.accessToken || + !authStore.user?.refToken || + !authStore.user?.idToken + ) { + console.error('Auth load failed') + logout() // force to logout + return + } + + const auth = await keycloakInstance.init({ + token: authStore.user.accessToken, + refreshToken: authStore.user.refToken, + idToken: authStore.user.idToken, + }) + + if (!auth || !keycloakInstance.token) { + console.error('Keycloak initialization failed') + logout() + return + } + } + + const currentTime = Date.now() + const authTimeInUnixTime = getAuthTimeInUnixTime( + authStore.user!.accessToken, + ) + const sessionDuration = currentTime - authTimeInUnixTime + + // force session logout when the maximum session time is exceeded + if (sessionDuration > KEYCLOAK.MAX_SESSION_DURATION) { + console.warn('Session has exceeded the maximum duration, logging out.') + logout() + return + } + + console.log( + `access token expired date: ${getTokenExpirationDate(authStore.user!.accessToken)}`, + ) + console.log(`access token expired!: ${keycloakInstance.isTokenExpired()}`) + + if (keycloakInstance.isTokenExpired()) { + try { + const refreshed = await keycloakInstance.updateToken( + KEYCLOAK.UPDATE_TOKEN_MIN_VALIDITY, + ) + console.log(refreshed ? 'Token was refreshed' : 'Token is still valid') + if (refreshed) { + authStore.setUser({ + accessToken: keycloakInstance.token!, + refToken: keycloakInstance.refreshToken!, + idToken: keycloakInstance.idToken!, + }) + } else { + console.error('Token refresh failed') + logout() + return + } + } catch (error) { + console.error('Error refreshing token:', error) + logout() + return + } + } + } catch (error) { + console.error('Error during token validation:', error) + logout() + return + } +} + +export const logout = (): void => { + const authStore = useAuthStore() + authStore.clearUser() + window.location.href = `https://logon7.gov.bc.ca/clp-cgi/logoff.cgi?retnow=1&returl=${encodeURIComponent( + `${import.meta.env.VITE_SSO_AUTH_SERVER_URL}/realms/${import.meta.env.VITE_SSO_REALM}/protocol/openid-connect/logout?post_logout_redirect_uri=` + + import.meta.env.VITE_SSO_REDIRECT_URI + + '&client_id=' + + import.meta.env.VITE_SSO_CLIENT_ID, + )}` +} + +const getAuthTimeInUnixTime = (accessToken: string): number => { + try { + const accessTokenParsed = JSON.parse(atob(accessToken.split('.')[1])) + console.log( + `auth_time: ${accessTokenParsed.auth_time} (${Util.formatUnixTimestampToDate(accessTokenParsed.auth_time)})`, + ) + return accessTokenParsed.auth_time * 1000 // convert to milliseconds + } catch (error) { + console.error('Failed to parse auth_time from ID Token:', error) + return Date.now() // the current time on failure + } +} + +const getTokenExpirationDate = (token: string): Date | null => { + try { + const tokenParsed = JSON.parse(atob(token.split('.')[1])) + if (!tokenParsed.exp) { + return null + } + + const expirationDate = Util.formatUnixTimestampToDate(tokenParsed.exp) + return expirationDate + } catch (error) { + console.error('Failed to parse token expiration:', error) + return null + } +} diff --git a/vdyp-web-ui/src/shims-vue.d.ts b/vdyp-web-ui/src/shims-vue.d.ts new file mode 100644 index 000000000..d7a41a762 --- /dev/null +++ b/vdyp-web-ui/src/shims-vue.d.ts @@ -0,0 +1,5 @@ +declare module '*.vue' { + import { DefineComponent } from 'vue' + const component: DefineComponent<{}, {}, any> + export default component +} \ No newline at end of file diff --git a/vdyp-web-ui/src/stores/common/authStore.ts b/vdyp-web-ui/src/stores/common/authStore.ts new file mode 100644 index 000000000..576b7d8b0 --- /dev/null +++ b/vdyp-web-ui/src/stores/common/authStore.ts @@ -0,0 +1,69 @@ +import { defineStore } from 'pinia' +import { logout as keycloakLogout } from '@/services/keycloak' + +interface User { + accessToken: string + refToken: string + idToken: string +} + +export const useAuthStore = defineStore('auth', { + state: () => ({ + authenticated: false, + user: null as User | null, + }), + actions: { + setUser(user: User) { + this.user = user + this.authenticated = true + sessionStorage.setItem('authUser', JSON.stringify(user)) + }, + clearUser() { + this.user = null + this.authenticated = false + sessionStorage.removeItem('authUser') + }, + loadUserFromStorage() { + const user = sessionStorage.getItem('authUser') + if (user) { + this.user = JSON.parse(user) + this.authenticated = true + } + }, + parseIdToken() { + if (!this.user?.idToken) return null + + try { + const idTokenParsed = JSON.parse(atob(this.user.idToken.split('.')[1])) + return { + auth_time: idTokenParsed.auth_time, + client_roles: idTokenParsed.client_roles, + display_name: idTokenParsed.display_name, + email: idTokenParsed.email, + exp: idTokenParsed.exp, + family_name: idTokenParsed.family_name, + given_name: idTokenParsed.given_name, + idir_username: idTokenParsed.idir_username, + name: idTokenParsed.name, + preferred_username: idTokenParsed.preferred_username, + user_principal_name: idTokenParsed.user_principal_name, + } + } catch (error) { + console.error('Failed to parse ID Token:', error) + return null + } + }, + getAllRoles(): string[] { + const parsedToken = this.parseIdToken() + return parsedToken?.client_roles || [] + }, + hasRole(role: string): boolean { + const roles = this.getAllRoles() + return roles.includes(role) + }, + async logout() { + this.clearUser() + keycloakLogout() + }, + }, +}) diff --git a/vdyp-web-ui/src/stores/common/confirmDialogStore.ts b/vdyp-web-ui/src/stores/common/confirmDialogStore.ts new file mode 100644 index 000000000..fea5d7ed6 --- /dev/null +++ b/vdyp-web-ui/src/stores/common/confirmDialogStore.ts @@ -0,0 +1,64 @@ +import { defineStore } from 'pinia' + +interface DialogOptions { + color: string + width: number + zIndex: number + noconfirm: boolean + titleStyle: string +} + +interface ConfirmDialogState { + dialog: boolean + resolve: ((value: boolean) => void) | null + title: string | null + message: string | null + options: DialogOptions +} + +export const useConfirmDialogStore = defineStore('confirmDialog', { + state: (): ConfirmDialogState => ({ + dialog: false, + resolve: null, + title: null, + message: null, + options: { + color: 'grey lighten-3', + width: 400, + zIndex: 200, + noconfirm: false, + titleStyle: 'color: rgba(0,0,0,.87) !important;', + }, + }), + + getters: { + isOpen: (state) => state.dialog, + dialogTitle: (state) => state.title, + dialogMessage: (state) => state.message, + dialogOptions: (state) => state.options, + }, + + actions: { + openDialog(newTitle: string, newMessage: string, newOptions?: Partial): Promise { + this.dialog = true + this.title = newTitle + this.message = newMessage + if (newOptions) { + this.options = { ...this.options, ...newOptions } + } + return new Promise((resolve) => { + this.resolve = resolve + }) + }, + + agree() { + if (this.resolve) this.resolve(true) + this.dialog = false + }, + + cancel() { + if (this.resolve) this.resolve(false) + this.dialog = false + }, + }, +}) diff --git a/vdyp-web-ui/src/stores/common/messageStore.ts b/vdyp-web-ui/src/stores/common/messageStore.ts new file mode 100644 index 000000000..47de9b0ec --- /dev/null +++ b/vdyp-web-ui/src/stores/common/messageStore.ts @@ -0,0 +1,46 @@ +import { defineStore } from 'pinia' +import type { MessageType } from '@/types/types' + +interface MessageState { + isShow: boolean + message: string + type: MessageType +} + +export const useMessageStore = defineStore({ + id: 'messageStore', + state: (): MessageState => ({ + isShow: false, + message: '', + type: '', + }), + getters: {}, + actions: { + showMessage(message: string) { + this.isShow = true + this.message = message + this.type = '' + }, + + showErrorMessage(message: string) { + this.isShow = true + this.message = message + this.type = 'error' + }, + showSuccessMessage(message: string) { + this.isShow = true + this.message = message + this.type = 'success' + }, + showInfoMessage(message: string) { + this.isShow = true + this.message = message + this.type = 'info' + }, + showWarningMessage(message: string) { + this.isShow = true + this.message = message + this.type = 'warning' + }, + }, +}) diff --git a/vdyp-web-ui/src/stores/common/progressCircularStore.ts b/vdyp-web-ui/src/stores/common/progressCircularStore.ts new file mode 100644 index 000000000..7fe71e223 --- /dev/null +++ b/vdyp-web-ui/src/stores/common/progressCircularStore.ts @@ -0,0 +1,22 @@ +import { defineStore } from 'pinia' +import type ProgressCircularState from '@/interfaces/ProgressCircularState' + +export const useProgressCircularStore = defineStore({ + id: 'progressCircularStore', + state: (): ProgressCircularState => ({ + isShow: false, + message: '', + }), + getters: { + showMessage: (state) => !!state.message, + }, + actions: { + showProgress(message?: string) { + this.isShow = true + this.message = message + }, + hideProgress() { + this.isShow = false + }, + }, +}) diff --git a/vdyp-web-ui/src/stores/index.ts b/vdyp-web-ui/src/stores/index.ts new file mode 100644 index 000000000..f00b209da --- /dev/null +++ b/vdyp-web-ui/src/stores/index.ts @@ -0,0 +1,3 @@ +import { createPinia } from 'pinia' + +export default createPinia() diff --git a/vdyp-web-ui/src/stores/jobTypeSelectionStore.ts b/vdyp-web-ui/src/stores/jobTypeSelectionStore.ts new file mode 100644 index 000000000..325d010eb --- /dev/null +++ b/vdyp-web-ui/src/stores/jobTypeSelectionStore.ts @@ -0,0 +1,13 @@ +import { defineStore } from 'pinia' +import { ref } from 'vue' + +export const useJobTypeSelectionStore = defineStore( + 'jobTypeSelectionStore', + () => { + const modelType = ref('Input Model Parameters') + const modelName = ref('VDYP 8') + const jobName = ref('Job_Name_DD/MM/YYYY') + + return { modelType, modelName, jobName } + }, +) diff --git a/vdyp-web-ui/src/stores/speciesStore.ts b/vdyp-web-ui/src/stores/speciesStore.ts new file mode 100644 index 000000000..9ef7871d4 --- /dev/null +++ b/vdyp-web-ui/src/stores/speciesStore.ts @@ -0,0 +1,29 @@ +import { defineStore } from 'pinia' + +export const useSpeciesStore = defineStore('speciesStore', { + state: () => ({ + speciesList: [ + { name: 'PL - Lodgepole', percent: 30, group: 'S', groupPercent: 30, siteSpecies: 'PL' }, + { name: 'AC - Popular', percent: 30, group: 'AC', groupPercent: 30, siteSpecies: 'AC' }, + { name: 'H - Hemlock', percent: 30, group: 'H', groupPercent: 30, siteSpecies: 'H' }, + { name: 'S - Spruce', percent: 10, group: 'S', groupPercent: 10, siteSpecies: 'S' }, + { name: 'S - Spruce', percent: 0, group: 'S', groupPercent: 10, siteSpecies: 'S' }, + { name: 'S - Spruce', percent: 0, group: 'S', groupPercent: 10, siteSpecies: 'S' }, + ], + speciesOptions: [ + { value: 'PL - Lodgepole', label: 'PL - Lodgepole' }, + { value: 'AC - Popular', label: 'AC - Popular' }, + { value: 'H - Hemlock', label: 'H - Hemlock' }, + { value: 'S - Spruce', label: 'S - Spruce' }, + ], + speciesGroups: ['S', 'AC', 'H'], + }), + getters: { + totalSpeciesPercent: (state) => state.speciesList.reduce((sum, species) => sum + species.percent, 0), + }, + actions: { + addMoreSpecies() { + this.speciesList.push({ name: '', percent: 0, group: '', groupPercent: 0, siteSpecies: '' }) + }, + }, +}) diff --git a/vdyp-web-ui/src/stores/stepStore.ts b/vdyp-web-ui/src/stores/stepStore.ts new file mode 100644 index 000000000..9a3b0ba24 --- /dev/null +++ b/vdyp-web-ui/src/stores/stepStore.ts @@ -0,0 +1,18 @@ +import { defineStore } from 'pinia' + +export const useStepStore = defineStore('stepStore', { + state: () => ({ + currentStep: 1, + }), + actions: { + nextStep() { + if (this.currentStep < 5) this.currentStep++ + }, + previousStep() { + if (this.currentStep > 1) this.currentStep-- + }, + setStep(step: number) { + if (step >= 1 && step <= 5) this.currentStep = step + }, + }, +}) diff --git a/vdyp-web-ui/src/styles/style.scss b/vdyp-web-ui/src/styles/style.scss new file mode 100644 index 000000000..628e2032a --- /dev/null +++ b/vdyp-web-ui/src/styles/style.scss @@ -0,0 +1,113 @@ +$font-family: 'BCSans', 'Noto Sans', Verdana, Arial, sans-serif; + +// Apply the font-family globally +html, +body { + font-family: $font-family; +} + +/* V-LIST */ +.v-list-item__prepend { + width: 32px !important; // Reduced the space between the prepend icon and text in the v-list +} + +/* Button */ +.v-btn { + text-transform: none; // Disabled V-BTN text from being capitalized +} + +/* Data Table */ +.v-data-table-footer { + padding-top: 20px; // Increased the spacing between table and pagination + justify-content: center !important; // Centered the data table pagination +} + +.v-table__wrapper { + // Added table border lines + border-top: 1px solid #ddd; + border-left: 1px solid #ddd; + border-right: 1px solid #ddd; +} + +.data-table-even-row { + background-color: #f5f5f5; + border-left: 2px solid black; +} + +.data-table-odd-row { + background-color: #ffffff; +} + +/* LINK */ +.link-no-decoration { + // no decoration on link + text-decoration: none; + color: inherit; +} + +.link-no-decoration:focus, +.link-no-decoration:hover, +.link-no-decoration:visited, +.link-no-decoration:link, +.link-no-decoration:active { + text-decoration: none; + color: inherit; +} + +.v-card-title { + color: white; + background: #44608d; + border-bottom: 2px solid orange; + padding: 1rem !important; +} + +// only for color='primary' +.v-card-actions { + .text-primary.v-btn--variant-text { + background: #003366; + color: white !important; + } +} + +/* etc */ + +.fieldset { + border: 2px solid #39598b; + border-radius: 8px; + padding: 16px; + margin-bottom: 16px; +} + +.legend { + padding: 0 8px; + font-weight: bold; + color: #39598b; +} + +.readonly__text { + color: black; + font-size: 18px; +} + +.readonly__label { + font-size: 13px; + color: #747474; +} + +.search-label { + font-size: 14px; +} + +.rounded-chip { + border-radius: 5px !important; +} + +.dotted-divider { + border-top: 2px dotted #000; +} + +.species-row { + display: flex; + gap: 10px; + margin-bottom: 10px; +} diff --git a/vdyp-web-ui/src/types/types.ts b/vdyp-web-ui/src/types/types.ts new file mode 100644 index 000000000..26a63846c --- /dev/null +++ b/vdyp-web-ui/src/types/types.ts @@ -0,0 +1 @@ +export type MessageType = '' | 'info' | 'success' | 'error' | 'warning' diff --git a/vdyp-web-ui/src/utils/util.ts b/vdyp-web-ui/src/utils/util.ts new file mode 100644 index 000000000..eb0a49eae --- /dev/null +++ b/vdyp-web-ui/src/utils/util.ts @@ -0,0 +1,200 @@ +export class Util { + /** + * VALIDATATING + */ + static isValidVal(item: any): any { + return !Util.isBlank(item) ? item : '' + } + + // undefined, null, NaN, 0, "" (empty string), and false + // isBlank(" ") = false + static isBlank(item: any): boolean { + if (!item) { + return true + } else if (Array.isArray(item)) { + return item.length === 0 + } else if (typeof item === 'string') { + return item.trim().length === 0 + } + return false + } + + static areEqual(array1: Array, array2: Array): boolean { + if (array1.length === array2.length) { + return array1.every((element, index) => { + if (element === array2[index]) { + return true + } + + return false + }) + } + + return false + } + + static isJson(str: any) { + try { + JSON.parse(str) + } catch (e) { + return false + } + return true + } + + static isURL(input: string): boolean { + // Regular expression for URL validation + const urlRegex = /^(https?|ftp):\/\/[^\s/$.?#].[^\s]*$/ + return urlRegex.test(input) + } + + /** + * FORMATTING + */ + + /** + * Returns the parameter of the given date type as a string with a certain pattern. + * @param date + * @returns YYYY-MM-DD hh:mm:ss + */ + static formatDateTime(date: Date | null): string | null { + if (!date) { + return null + } + + const year = new Intl.DateTimeFormat('en', { year: 'numeric' }).format(date) + const month = new Intl.DateTimeFormat('en', { month: '2-digit' }).format(date) + const day = new Intl.DateTimeFormat('en', { day: '2-digit' }).format(date) + + const hour = date.getHours().toString().padStart(2, '0') + const minute = date.getMinutes().toString().padStart(2, '0') + const second = date.getSeconds().toString().padStart(2, '0') + + return `${year}-${month}-${day} ${hour}:${minute}:${second}` + } + + static formatLargeNumber(number: number, decimalPlaces = 10): string | null { + if (number) { + return number.toFixed(decimalPlaces).replace(/\.?0+$/, '') // Remove trailing zeros + } else { + return null + } + } + + static formatNumberFractionGroup(minimumFractionDigits: number, maximumFractionDigits: number): any { + return new Intl.NumberFormat('en-US', { + minimumFractionDigits: minimumFractionDigits, + maximumFractionDigits: maximumFractionDigits, + useGrouping: true, + }) + } + + static formatNumberGroup(): any { + return new Intl.NumberFormat('en-US', { + useGrouping: true, + }) + } + + /** + * converts a given Unix timestamp to a Date object. + * @param {number} unixTimestamp - Unix timestamps in seconds + * @returns {Date | null} the converted Date object or null if the conversion failed + */ + static formatUnixTimestampToDate(timestamp: number): Date | null { + try { + const date = new Date(timestamp * 1000) // Convert seconds to milliseconds + return date + } catch (error) { + console.error('Failed to convert timestamp to date:', error) + return null + } + } + + /** + * INSERTING + */ + static insertComma(item: string): string { + if (!Util.isBlank(item)) { + return ', ' + } else { + return '' + } + } + + static insertBR(item: string): string { + if (!Util.isBlank(item)) { + return '
' + } else { + return '' + } + } + + /** + * CONVERTING + */ + static arrayToString(array: Array, separator: string): string { + if (array) { + return array.join(separator) + } else { + return '' + } + } + + /** + * EXTRACTING + */ + static translateCode(code: string, codeList: Array): string { + if (code && codeList && codeList.length > 0) { + return codeList.find((c) => c.codeName === code).description + } else { + return '' + } + } + static userIdConversion(item: string): string { + if (item && item.indexOf('\\') !== -1) { + return item.split('\\')[1] + } else { + return item + } + } + + /** + * SPLITING + */ + static splitStringByCharCamelCase(inString: string): string { + return !Util.isBlank(inString) ? inString.replace(/([a-z0-9])([A-Z])/g, '$1 $2') : '' + } + + /** + * MISCELLANEOUS + */ + static flattenArray(inputArray: any[]): any[] { + const result: any[] = [] + + const flattenHelper = function (arr: any[]) { + for (const element of arr) { + if (Array.isArray(element) && Array.isArray(element[0])) { + flattenHelper(element) + } else { + result.push(element) + } + } + } + + flattenHelper(inputArray) + return result + } + + static openRequestedPopup(url: string, windowsName: string, windowFeatures?: string): void { + if (Util.isBlank(windowFeatures)) windowFeatures = 'toolbar=0,location=0,menubar=0' + window.open(url, windowsName, windowFeatures) + } + + static async delay(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)) + } + + static deepCopy(obj: any): any { + return JSON.parse(JSON.stringify(obj)) as typeof obj + } +} diff --git a/vdyp-web-ui/src/views/JobList.vue b/vdyp-web-ui/src/views/JobList.vue new file mode 100644 index 000000000..171b2a7de --- /dev/null +++ b/vdyp-web-ui/src/views/JobList.vue @@ -0,0 +1,372 @@ + + + + + diff --git a/vdyp-web-ui/src/views/PageNotFound.vue b/vdyp-web-ui/src/views/PageNotFound.vue new file mode 100644 index 000000000..49cf1ec20 --- /dev/null +++ b/vdyp-web-ui/src/views/PageNotFound.vue @@ -0,0 +1,24 @@ + + + diff --git a/vdyp-web-ui/src/views/input-model-parameters/ModelParameterInput.vue b/vdyp-web-ui/src/views/input-model-parameters/ModelParameterInput.vue new file mode 100644 index 000000000..0dbed909c --- /dev/null +++ b/vdyp-web-ui/src/views/input-model-parameters/ModelParameterInput.vue @@ -0,0 +1,53 @@ + + + + + diff --git a/vdyp-web-ui/src/views/input-model-parameters/ModelParameterSelection.vue b/vdyp-web-ui/src/views/input-model-parameters/ModelParameterSelection.vue new file mode 100644 index 000000000..4ce5c69ec --- /dev/null +++ b/vdyp-web-ui/src/views/input-model-parameters/ModelParameterSelection.vue @@ -0,0 +1,61 @@ + + + + + diff --git a/vdyp-web-ui/src/views/input-model-parameters/SpeciesResults.vue b/vdyp-web-ui/src/views/input-model-parameters/SpeciesResults.vue new file mode 100644 index 000000000..9aff3e215 --- /dev/null +++ b/vdyp-web-ui/src/views/input-model-parameters/SpeciesResults.vue @@ -0,0 +1,5 @@ + + + diff --git a/vdyp-web-ui/src/views/input-model-parameters/ViewErrorMessages.vue b/vdyp-web-ui/src/views/input-model-parameters/ViewErrorMessages.vue new file mode 100644 index 000000000..9aff3e215 --- /dev/null +++ b/vdyp-web-ui/src/views/input-model-parameters/ViewErrorMessages.vue @@ -0,0 +1,5 @@ + + + diff --git a/vdyp-web-ui/src/views/input-model-parameters/ViewLogFile.vue b/vdyp-web-ui/src/views/input-model-parameters/ViewLogFile.vue new file mode 100644 index 000000000..9aff3e215 --- /dev/null +++ b/vdyp-web-ui/src/views/input-model-parameters/ViewLogFile.vue @@ -0,0 +1,5 @@ + + + diff --git a/vdyp-web-ui/tsconfig.app.json b/vdyp-web-ui/tsconfig.app.json new file mode 100644 index 000000000..e14c754d3 --- /dev/null +++ b/vdyp-web-ui/tsconfig.app.json @@ -0,0 +1,14 @@ +{ + "extends": "@vue/tsconfig/tsconfig.dom.json", + "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], + "exclude": ["src/**/__tests__/*"], + "compilerOptions": { + "composite": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + } +} diff --git a/vdyp-web-ui/tsconfig.json b/vdyp-web-ui/tsconfig.json new file mode 100644 index 000000000..66b5e5703 --- /dev/null +++ b/vdyp-web-ui/tsconfig.json @@ -0,0 +1,11 @@ +{ + "files": [], + "references": [ + { + "path": "./tsconfig.node.json" + }, + { + "path": "./tsconfig.app.json" + } + ] +} diff --git a/vdyp-web-ui/tsconfig.node.json b/vdyp-web-ui/tsconfig.node.json new file mode 100644 index 000000000..2c669eeb8 --- /dev/null +++ b/vdyp-web-ui/tsconfig.node.json @@ -0,0 +1,13 @@ +{ + "extends": "@tsconfig/node20/tsconfig.json", + "include": ["vite.config.*", "vitest.config.*", "cypress.config.*", "nightwatch.conf.*", "playwright.config.*"], + "compilerOptions": { + "composite": true, + "noEmit": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + + "module": "ESNext", + "moduleResolution": "Bundler", + "types": ["node"] + } +} diff --git a/vdyp-web-ui/vite.config.ts b/vdyp-web-ui/vite.config.ts new file mode 100644 index 000000000..c398de8e7 --- /dev/null +++ b/vdyp-web-ui/vite.config.ts @@ -0,0 +1,21 @@ +import { fileURLToPath, URL } from 'node:url' +import { defineConfig } from 'vite' +import Vue from '@vitejs/plugin-vue' +import Vuetify from 'vite-plugin-vuetify' +import packageVersion from 'vite-plugin-package-version' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [ + Vue(), + packageVersion(), + Vuetify({ + autoImport: true, + }), + ], + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)), + }, + }, +}) From c1460bbd16e13c659f63b72b195475c89e182d62 Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Fri, 30 Aug 2024 12:51:49 -0700 Subject: [PATCH 13/53] changed the packages needed for build from devDependencies to dependencies --- vdyp-web-ui/package-lock.json | 32 ++++++++++++++------------------ vdyp-web-ui/package.json | 8 ++++---- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/vdyp-web-ui/package-lock.json b/vdyp-web-ui/package-lock.json index cc6e0aea6..118f02e59 100644 --- a/vdyp-web-ui/package-lock.json +++ b/vdyp-web-ui/package-lock.json @@ -10,6 +10,10 @@ "license": "(MIT OR Apache-2.0)", "dependencies": { "@bcgov/bc-sans": "^2.1.0", + "@mdi/font": "^7.4.47", + "@tsconfig/node20": "^20.1.4", + "@vitejs/plugin-vue": "^5.0.5", + "@vue/tsconfig": "^0.5.1", "@vuepic/vue-datepicker": "^8.8.0", "axios": "^1.7.2", "http-status-codes": "^2.3.0", @@ -22,14 +26,10 @@ "vuetify": "^3.6.14" }, "devDependencies": { - "@mdi/font": "^7.4.47", "@rushstack/eslint-patch": "^1.8.0", - "@tsconfig/node20": "^20.1.4", "@types/node": "^20.14.5", - "@vitejs/plugin-vue": "^5.0.5", "@vue/eslint-config-prettier": "^9.0.0", "@vue/eslint-config-typescript": "^13.0.0", - "@vue/tsconfig": "^0.5.1", "eslint": "^8.57.0", "npm-run-all2": "^6.2.0", "prettier": "^3.3.3", @@ -577,8 +577,7 @@ "node_modules/@mdi/font": { "version": "7.4.47", "resolved": "https://registry.npmjs.org/@mdi/font/-/font-7.4.47.tgz", - "integrity": "sha512-43MtGpd585SNzHZPcYowu/84Vz2a2g31TvPMTm9uTiCSWzaheQySUcSyUH/46fPnuPQWof2yd0pGBtzee/IQWw==", - "dev": true + "integrity": "sha512-43MtGpd585SNzHZPcYowu/84Vz2a2g31TvPMTm9uTiCSWzaheQySUcSyUH/46fPnuPQWof2yd0pGBtzee/IQWw==" }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", @@ -828,8 +827,7 @@ "node_modules/@tsconfig/node20": { "version": "20.1.4", "resolved": "https://registry.npmjs.org/@tsconfig/node20/-/node20-20.1.4.tgz", - "integrity": "sha512-sqgsT69YFeLWf5NtJ4Xq/xAF8p4ZQHlmGW74Nu2tD4+g5fAsposc4ZfaaPixVu4y01BEiDCWLRDCvDM5JOsRxg==", - "dev": true + "integrity": "sha512-sqgsT69YFeLWf5NtJ4Xq/xAF8p4ZQHlmGW74Nu2tD4+g5fAsposc4ZfaaPixVu4y01BEiDCWLRDCvDM5JOsRxg==" }, "node_modules/@types/estree": { "version": "1.0.5", @@ -927,10 +925,9 @@ "dev": true }, "node_modules/@vitejs/plugin-vue": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.1.2.tgz", - "integrity": "sha512-nY9IwH12qeiJqumTCLJLE7IiNx7HZ39cbHaysEUd+Myvbz9KAqd2yq+U01Kab1R/H1BmiyM2ShTYlNH32Fzo3A==", - "dev": true, + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.5.tgz", + "integrity": "sha512-LOjm7XeIimLBZyzinBQ6OSm3UBCNVCpLkxGC0oWmm2YPzVZoxMsdvNVimLTBzpAnR9hl/yn1SHGuRfe6/Td9rQ==", "engines": { "node": "^18.0.0 || >=20.0.0" }, @@ -1246,8 +1243,7 @@ "node_modules/@vue/tsconfig": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.5.1.tgz", - "integrity": "sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==", - "dev": true + "integrity": "sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==" }, "node_modules/@vuepic/vue-datepicker": { "version": "8.8.1", @@ -2569,9 +2565,9 @@ } }, "node_modules/npm-run-all2": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/npm-run-all2/-/npm-run-all2-6.2.2.tgz", - "integrity": "sha512-Q+alQAGIW7ZhKcxLt8GcSi3h3ryheD6xnmXahkMRVM5LYmajcUrSITm8h+OPC9RYWMV2GR0Q1ntTUCfxaNoOJw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/npm-run-all2/-/npm-run-all2-6.2.0.tgz", + "integrity": "sha512-wA7yVIkthe6qJBfiJ2g6aweaaRlw72itsFGF6HuwCHKwtwAx/4BY1vVpk6bw6lS8RLMsexoasOkd0aYOmsFG7Q==", "dev": true, "dependencies": { "ansi-styles": "^6.2.1", @@ -2589,7 +2585,7 @@ "run-s": "bin/run-s/index.js" }, "engines": { - "node": "^14.18.0 || ^16.13.0 || >=18.0.0", + "node": "^14.18.0 || >=16.0.0", "npm": ">= 8" } }, diff --git a/vdyp-web-ui/package.json b/vdyp-web-ui/package.json index dccba0dbe..1975cafe1 100644 --- a/vdyp-web-ui/package.json +++ b/vdyp-web-ui/package.json @@ -19,6 +19,10 @@ "repository": {}, "dependencies": { "@bcgov/bc-sans": "^2.1.0", + "@mdi/font": "^7.4.47", + "@tsconfig/node20": "^20.1.4", + "@vitejs/plugin-vue": "^5.0.5", + "@vue/tsconfig": "^0.5.1", "@vuepic/vue-datepicker": "^8.8.0", "axios": "^1.7.2", "http-status-codes": "^2.3.0", @@ -31,14 +35,10 @@ "vuetify": "^3.6.14" }, "devDependencies": { - "@mdi/font": "^7.4.47", "@rushstack/eslint-patch": "^1.8.0", - "@tsconfig/node20": "^20.1.4", "@types/node": "^20.14.5", - "@vitejs/plugin-vue": "^5.0.5", "@vue/eslint-config-prettier": "^9.0.0", "@vue/eslint-config-typescript": "^13.0.0", - "@vue/tsconfig": "^0.5.1", "eslint": "^8.57.0", "npm-run-all2": "^6.2.0", "prettier": "^3.3.3", From 51e4f1985ab6f935f420743b741042de4d57b26e Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Fri, 30 Aug 2024 12:52:25 -0700 Subject: [PATCH 14/53] added Dockerfile and Caddyfile --- vdyp-web-ui/.dockerignore | 6 +++++ vdyp-web-ui/Caddyfile | 48 +++++++++++++++++++++++++++++++++++++++ vdyp-web-ui/Dockerfile | 30 ++++++++++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 vdyp-web-ui/.dockerignore create mode 100644 vdyp-web-ui/Caddyfile create mode 100644 vdyp-web-ui/Dockerfile diff --git a/vdyp-web-ui/.dockerignore b/vdyp-web-ui/.dockerignore new file mode 100644 index 000000000..4f05d3192 --- /dev/null +++ b/vdyp-web-ui/.dockerignore @@ -0,0 +1,6 @@ +.idea +.vscode +coverage +cypress +dist +node_modules diff --git a/vdyp-web-ui/Caddyfile b/vdyp-web-ui/Caddyfile new file mode 100644 index 000000000..4b04c9044 --- /dev/null +++ b/vdyp-web-ui/Caddyfile @@ -0,0 +1,48 @@ +{ + auto_https off + admin 0.0.0.0:3003 + servers { + metrics + } +} +:3000 { + log { + output stdout + format console { + time_format iso8601 + level_format color + } + level {$LOG_LEVEL} + } + root * /srv + encode zstd gzip + file_server + @spa_router { + not path /api/* + file { + try_files {path} /index.html + } + } + rewrite @spa_router {http.matchers.file.relative} + # Proxy requests to API service + reverse_proxy /api/* {$BACKEND_URL} { + header_up Host {http.reverse_proxy.upstream.hostport} + header_up X-Real-IP {remote_host} + header_up X-Forwarded-For {remote_host} + } + header { + X-Frame-Options "SAMEORIGIN" + X-XSS-Protection "1;mode=block" + Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate" + X-Content-Type-Options "nosniff" + Strict-Transport-Security "max-age=31536000" + Content-Security-Policy "default-src 'self' https://spt.apps.gov.bc.ca data:; script-src 'self' 'unsafe-eval' https://www2.gov.bc.ca ;style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://use.fontawesome.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https://fonts.googleapis.com http://www.w3.org https://*.gov.bc.ca" + Referrer-Policy "same-origin" + Feature-Policy "fullscreen 'self'; camera 'none'; microphone 'none'" + } +} +:3001 { + handle /health { + respond "OK" + } +} diff --git a/vdyp-web-ui/Dockerfile b/vdyp-web-ui/Dockerfile new file mode 100644 index 000000000..2798799ca --- /dev/null +++ b/vdyp-web-ui/Dockerfile @@ -0,0 +1,30 @@ +# Build static files +# Node Bullseye has npm +FROM node:20.16.0-bullseye-slim AS build + +# Install packages, build and keep only prod packages +WORKDIR /app +COPY *.json *.ts index.html ./ +COPY public ./public +COPY ./src ./src +RUN npm ci --ignore-scripts --no-update-notifier --omit=dev && \ + npm run build-only + +# Deploy container +# Caddy serves static files +FROM caddy:2.8.4-alpine +RUN apk add --no-cache ca-certificates + +# Ensure Caddy has write permissions to necessary directories +RUN mkdir -p /config/caddy /data/caddy && \ + chown -R 1001:1001 /config/caddy /data/caddy + +# Copy static files and run formatting +COPY --from=build /app/dist /srv +COPY Caddyfile /etc/caddy/Caddyfile +RUN caddy fmt --overwrite /etc/caddy/Caddyfile + +# Ports, health check and non-root user +EXPOSE 3000 3001 +HEALTHCHECK --interval=30s --timeout=3s CMD curl -f http://localhost/:3001/health || exit 1 +USER 1001 From 84dea26b060d95e63a0f0a4d9528d0816ea72b09 Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Fri, 30 Aug 2024 14:55:32 -0700 Subject: [PATCH 15/53] Fix invalid Health Check URL --- vdyp-web-ui/Dockerfile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vdyp-web-ui/Dockerfile b/vdyp-web-ui/Dockerfile index 2798799ca..a0352a454 100644 --- a/vdyp-web-ui/Dockerfile +++ b/vdyp-web-ui/Dockerfile @@ -13,10 +13,10 @@ RUN npm ci --ignore-scripts --no-update-notifier --omit=dev && \ # Deploy container # Caddy serves static files FROM caddy:2.8.4-alpine -RUN apk add --no-cache ca-certificates -# Ensure Caddy has write permissions to necessary directories -RUN mkdir -p /config/caddy /data/caddy && \ +# Install packages and ensure permissions in one RUN command +RUN apk add --no-cache ca-certificates && \ + mkdir -p /config/caddy /data/caddy && \ chown -R 1001:1001 /config/caddy /data/caddy # Copy static files and run formatting @@ -26,5 +26,5 @@ RUN caddy fmt --overwrite /etc/caddy/Caddyfile # Ports, health check and non-root user EXPOSE 3000 3001 -HEALTHCHECK --interval=30s --timeout=3s CMD curl -f http://localhost/:3001/health || exit 1 +HEALTHCHECK --interval=30s --timeout=3s CMD ["curl", "-f", "http://localhost:3001/health"] USER 1001 From b3676505781971b8a9ac44141608aedf06292d8a Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Tue, 3 Sep 2024 15:52:14 -0700 Subject: [PATCH 16/53] Add github workflow to deploy web-ui to dev --- .../workflows/openshift-deploy-web-ui-dev.yml | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 .github/workflows/openshift-deploy-web-ui-dev.yml diff --git a/.github/workflows/openshift-deploy-web-ui-dev.yml b/.github/workflows/openshift-deploy-web-ui-dev.yml new file mode 100644 index 000000000..dc7a5252f --- /dev/null +++ b/.github/workflows/openshift-deploy-web-ui-dev.yml @@ -0,0 +1,49 @@ +name: Deploy web-ui to Openshift Dev +on: + workflow_dispatch: + inputs: + TAG: + required: false + description: The docker image to deploy e.g. 1.0.0, 1.1.2, etc + type: string + default: latest +jobs: + build: + runs-on: ubuntu-latest + environment: + name: dev # read secrets from test environment in github. + permissions: + contents: read + packages: write + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Copy openshift yaml files + run: mkdir staging && cp openshift/vdyp-web-ui*.yaml staging/ + + - name: Fill yaml files + uses: cschleiden/replace-tokens@v1.2 + with: + files: staging/**.yaml + env: + #Common variables + ENV: dev + NAMESPACE: ${{ vars.OC_NAMESPACE }} + TAG: ${{ inputs.TAG }} + + #Frontend variables + ACTIVE_PORT: ${{ vars.TARGET_PORT }} + - name: Authenticate and set context + uses: redhat-actions/oc-login@v1 + with: + openshift_server_url: ${{secrets.OPENSHIFT_SERVER_URL}} + openshift_token: ${{secrets.OC_TOKEN}} + namespace: ${{ vars.OC_NAMESPACE }} + + - name: Apply .yaml files to openshift + run: | + for file in staging/* + do + oc apply -f "$file" + done From 6591f1508d4f7a918bc6082cc8b978e36141e3ec Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Tue, 3 Sep 2024 16:08:55 -0700 Subject: [PATCH 17/53] Add openshift object definition files --- openshift/vdyp-web-ui-configmap.yaml | 14 +++++++++ openshift/vdyp-web-ui-deployment.yaml | 41 +++++++++++++++++++++++++++ openshift/vdyp-web-ui-egress.yaml | 13 +++++++++ openshift/vdyp-web-ui-route.yaml | 16 +++++++++++ openshift/vdyp-web-ui-service.yaml | 12 ++++++++ 5 files changed, 96 insertions(+) create mode 100644 openshift/vdyp-web-ui-configmap.yaml create mode 100644 openshift/vdyp-web-ui-deployment.yaml create mode 100644 openshift/vdyp-web-ui-egress.yaml create mode 100644 openshift/vdyp-web-ui-route.yaml create mode 100644 openshift/vdyp-web-ui-service.yaml diff --git a/openshift/vdyp-web-ui-configmap.yaml b/openshift/vdyp-web-ui-configmap.yaml new file mode 100644 index 000000000..c1a771a0b --- /dev/null +++ b/openshift/vdyp-web-ui-configmap.yaml @@ -0,0 +1,14 @@ +kind: ConfigMap +apiVersion: v1 +metadata: + name: vdyp-web-ui-config-#{ENV}# + namespace: "#{NAMESPACE}#" + managedFields: + - manager: Mozilla + operation: Update + apiVersion: v1 + time: "2024-04-15T22:11:17Z" + fieldsType: FieldsV1 +immutable: false +data: + ACTIVE_PORT: "#{ACTIVE_PORT}#" diff --git a/openshift/vdyp-web-ui-deployment.yaml b/openshift/vdyp-web-ui-deployment.yaml new file mode 100644 index 000000000..f13103826 --- /dev/null +++ b/openshift/vdyp-web-ui-deployment.yaml @@ -0,0 +1,41 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: "#{NAMESPACE}#" + name: "vdyp-web-ui-deployment-#{ENV}#" +spec: + selector: + matchLabels: + app: vdyp-web-ui-#{ENV}# + replicas: 2 + template: + metadata: + labels: + app: vdyp-web-ui-#{ENV}# + project: vdyp-#{ENV}# + spec: + containers: + - name: container + image: >- + ghcr.io/bcgov/vdyp-web-ui:#{TAG}# + ports: + - containerPort: #{TARGET_PORT}# + protocol: TCP + env: + - name: ACTIVE_PORT + valueFrom: + configMapKeyRef: + name: vdyp-web-ui-config-#{ENV}# + key: ACTIVE_PORT + resources: + requests: + cpu: 100m + memory: 200Mi + limits: + cpu: 200m + memory: 400Mi + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 50% + maxUnavailable: 50% diff --git a/openshift/vdyp-web-ui-egress.yaml b/openshift/vdyp-web-ui-egress.yaml new file mode 100644 index 000000000..06aed1564 --- /dev/null +++ b/openshift/vdyp-web-ui-egress.yaml @@ -0,0 +1,13 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: -egress-#{ENV}# + namespace: "#{NAMESPACE}#" +spec: + podSelector: + matchLabels: + app: vdyp-web-ui-#{ENV}# + policyTypes: + - Egress + egress: + - {} diff --git a/openshift/vdyp-web-ui-route.yaml b/openshift/vdyp-web-ui-route.yaml new file mode 100644 index 000000000..365bf8caa --- /dev/null +++ b/openshift/vdyp-web-ui-route.yaml @@ -0,0 +1,16 @@ +kind: Route +apiVersion: route.openshift.io/v1 +metadata: + name: vdyp-web-ui-route-#{ENV}# + namespace: "#{NAMESPACE}#" + labels: {} +spec: + to: + kind: Service + name: vdyp-web-ui-service-#{ENV}# + tls: + termination: edge + insecureEdgeTerminationPolicy: Redirect + destinationCACertificate: "" + port: + targetPort: #{ACTIVE_PORT}# diff --git a/openshift/vdyp-web-ui-service.yaml b/openshift/vdyp-web-ui-service.yaml new file mode 100644 index 000000000..61e7fe4cb --- /dev/null +++ b/openshift/vdyp-web-ui-service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: vdyp-web-ui-service-#{ENV}# + namespace: "#{NAMESPACE}#" +spec: + selector: + app: vdyp-web-ui-#{ENV}# + ports: + - protocol: TCP + port: #{TARGET_PORT}# + targetPort: #{ACTIVE_PORT}# From 5f916a7e77cabb6f9e1662eba6c57c2bc46e6996 Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Tue, 3 Sep 2024 17:05:43 -0700 Subject: [PATCH 18/53] Add host to specify vdyp dev url --- openshift/vdyp-web-ui-route.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/openshift/vdyp-web-ui-route.yaml b/openshift/vdyp-web-ui-route.yaml index 365bf8caa..2c64a5a6c 100644 --- a/openshift/vdyp-web-ui-route.yaml +++ b/openshift/vdyp-web-ui-route.yaml @@ -5,6 +5,7 @@ metadata: namespace: "#{NAMESPACE}#" labels: {} spec: + host: vdyp-ui-dev.apps.silver.devops.gov.bc.ca to: kind: Service name: vdyp-web-ui-service-#{ENV}# From 6e7069e9651807dc7ab457a4fd2692b34a36919c Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Wed, 4 Sep 2024 10:47:01 -0700 Subject: [PATCH 19/53] Replace the target port with the activity port in its place --- openshift/vdyp-web-ui-deployment.yaml | 2 +- openshift/vdyp-web-ui-service.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openshift/vdyp-web-ui-deployment.yaml b/openshift/vdyp-web-ui-deployment.yaml index f13103826..647bbf6bc 100644 --- a/openshift/vdyp-web-ui-deployment.yaml +++ b/openshift/vdyp-web-ui-deployment.yaml @@ -19,7 +19,7 @@ spec: image: >- ghcr.io/bcgov/vdyp-web-ui:#{TAG}# ports: - - containerPort: #{TARGET_PORT}# + - containerPort: #{ACTIVE_PORT}# protocol: TCP env: - name: ACTIVE_PORT diff --git a/openshift/vdyp-web-ui-service.yaml b/openshift/vdyp-web-ui-service.yaml index 61e7fe4cb..e16bf658e 100644 --- a/openshift/vdyp-web-ui-service.yaml +++ b/openshift/vdyp-web-ui-service.yaml @@ -8,5 +8,5 @@ spec: app: vdyp-web-ui-#{ENV}# ports: - protocol: TCP - port: #{TARGET_PORT}# + port: #{ACTIVE_PORT}# targetPort: #{ACTIVE_PORT}# From a39a3e8fcfb76d5b650b573ecf0c574afff56518 Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Wed, 4 Sep 2024 10:56:29 -0700 Subject: [PATCH 20/53] Fix metadata name in egress config --- openshift/vdyp-web-ui-egress.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openshift/vdyp-web-ui-egress.yaml b/openshift/vdyp-web-ui-egress.yaml index 06aed1564..73c85332e 100644 --- a/openshift/vdyp-web-ui-egress.yaml +++ b/openshift/vdyp-web-ui-egress.yaml @@ -1,7 +1,7 @@ apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: - name: -egress-#{ENV}# + name: vdyp-web-ui-egress-#{ENV}# namespace: "#{NAMESPACE}#" spec: podSelector: From 43484cc4b0639d4faa23a0ca6721a002a42256e4 Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Wed, 4 Sep 2024 13:37:44 -0700 Subject: [PATCH 21/53] Disable Sonar analysis to temporarily run a maven build from a forked repository Disable Sonar analysis to temporarily run a maven build from the forked repository --- .github/workflows/maven.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 06902b1cb..3ae2a911b 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -44,11 +44,11 @@ jobs: restore-keys: ${{ runner.os }}-m2 - name: Build with Maven run: mvn -Pcoverage package verify - - name: Run Sonar Analysis - run: mvn sonar:sonar - -Dsonar.host.url=https://sonarcloud.io - -Dsonar.organization=bcgov-sonarcloud - -Dsonar.projectKey=bcgov_VDYP +# - name: Run Sonar Analysis +# run: mvn sonar:sonar +# -Dsonar.host.url=https://sonarcloud.io +# -Dsonar.organization=bcgov-sonarcloud +# -Dsonar.projectKey=bcgov_VDYP env: GITHUB_TOKEN: ${{ github.token }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} From 045b1000f44a06f7157aad35ad6fd2e91067244d Mon Sep 17 00:00:00 2001 From: vivid-cpreston <97257824+vivid-cpreston@users.noreply.github.com> Date: Wed, 4 Sep 2024 17:09:27 -0700 Subject: [PATCH 22/53] Create docker.yml --- .github/workflows/docker.yml | 45 ++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 .github/workflows/docker.yml diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 000000000..5815ba0a4 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,45 @@ +permissions: + contents: read + packages: write + +on: + workflow_dispatch: + inputs: + BUILD_TARGET: + type: choice + description: 'Name of project to build' + options: + - vdyp-web-ui + required: true + TAG: + required: false + type: string + default: "latest" + description: 'Additional tag to add to docker image' + + +jobs: + docker: + runs-on: ubuntu-latest + steps: + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Github Container Repository + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: ${{inputs.BUILD_TARGET}} + push: true + tags: ${{inputs.BUILD_TARGET}}:${{inputs.TAG}} + + From 42ca7b24b8217793b5e083402fee996eeca83f19 Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Wed, 4 Sep 2024 17:16:50 -0700 Subject: [PATCH 23/53] Fix build context --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 5815ba0a4..0656087c1 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -38,7 +38,7 @@ jobs: - name: Build and push uses: docker/build-push-action@v6 with: - context: ${{inputs.BUILD_TARGET}} + context: ./${{inputs.BUILD_TARGET}} push: true tags: ${{inputs.BUILD_TARGET}}:${{inputs.TAG}} From ba4e111c765e03af1ae917eeced6c4a9288f8c3a Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Wed, 4 Sep 2024 17:21:11 -0700 Subject: [PATCH 24/53] Add missing Checkout --- .github/workflows/docker.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 0656087c1..58ccb9167 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -22,6 +22,9 @@ jobs: docker: runs-on: ubuntu-latest steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Set up QEMU uses: docker/setup-qemu-action@v3 From 8d2753da01c32c483caaed03392736260eb0e045 Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Wed, 4 Sep 2024 17:26:59 -0700 Subject: [PATCH 25/53] Add permissions --- .github/workflows/docker.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 58ccb9167..190d81266 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -21,6 +21,9 @@ on: jobs: docker: runs-on: ubuntu-latest + permissions: + contents: read + packages: write steps: - name: Checkout uses: actions/checkout@v4 From e4a823d9e0f72021af8792e647f0ac17d965cc49 Mon Sep 17 00:00:00 2001 From: vivid-cpreston <97257824+vivid-cpreston@users.noreply.github.com> Date: Wed, 4 Sep 2024 17:32:03 -0700 Subject: [PATCH 26/53] Add metadata step --- .github/workflows/docker.yml | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 190d81266..d1d357f22 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -40,12 +40,31 @@ jobs: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + + - id: trimBranchName + name: trim branch name if necessary + run: | + export branchTag="${{ github.ref_name }}" + trimTag="${branchTag##*/}" + echo "BRANCH_TAG=$trimTag" >> $GITHUB_OUTPUT + + - name: Extract metadata (tags, labels) for Docker + id: meta_pr + uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 + with: + images: ghcr.io/${{ github.repository }}-${{ inputs.BUILD_TARGET }} + tags: | + type=schedule + type=raw,value=${{ steps.trimBranchName.outputs.BRANCH_TAG }} + type=ref,event=tag + type=raw,value=${{ inputs.TAG }} - name: Build and push uses: docker/build-push-action@v6 with: context: ./${{inputs.BUILD_TARGET}} push: true - tags: ${{inputs.BUILD_TARGET}}:${{inputs.TAG}} + tags: ${{ steps.meta_pr.outputs.tags }} + labels: ${{ steps.meta_pr.outputs.labels }} From ea8ed66300ee5a1b5cccdc7ee64d298d66c1fa73 Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Wed, 4 Sep 2024 17:38:57 -0700 Subject: [PATCH 27/53] Update actual docker image path --- openshift/vdyp-web-ui-deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openshift/vdyp-web-ui-deployment.yaml b/openshift/vdyp-web-ui-deployment.yaml index 647bbf6bc..1fdb42693 100644 --- a/openshift/vdyp-web-ui-deployment.yaml +++ b/openshift/vdyp-web-ui-deployment.yaml @@ -17,7 +17,7 @@ spec: containers: - name: container image: >- - ghcr.io/bcgov/vdyp-web-ui:#{TAG}# + ghcr.io/vividroyjeong/nr-vdyp-vdyp-web-ui:#{TAG}# ports: - containerPort: #{ACTIVE_PORT}# protocol: TCP From 7bc4a14a911884eb33fc259c8f3bdd24c7e33619 Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Wed, 4 Sep 2024 18:19:05 -0700 Subject: [PATCH 28/53] Update caddy run "exec /usr/bin/caddy: operation not permitted" --- vdyp-web-ui/Dockerfile | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/vdyp-web-ui/Dockerfile b/vdyp-web-ui/Dockerfile index a0352a454..622956d82 100644 --- a/vdyp-web-ui/Dockerfile +++ b/vdyp-web-ui/Dockerfile @@ -1,6 +1,6 @@ # Build static files # Node Bullseye has npm -FROM node:20.16.0-bullseye-slim AS build +FROM node:20.17.0-bullseye-slim AS build # Install packages, build and keep only prod packages WORKDIR /app @@ -13,11 +13,7 @@ RUN npm ci --ignore-scripts --no-update-notifier --omit=dev && \ # Deploy container # Caddy serves static files FROM caddy:2.8.4-alpine - -# Install packages and ensure permissions in one RUN command -RUN apk add --no-cache ca-certificates && \ - mkdir -p /config/caddy /data/caddy && \ - chown -R 1001:1001 /config/caddy /data/caddy +RUN apk add --no-cache ca-certificates # Copy static files and run formatting COPY --from=build /app/dist /srv From e01ffe27dd187b6fb9b1b9e1018fafd9c9ddc5e6 Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Thu, 5 Sep 2024 09:14:56 -0700 Subject: [PATCH 29/53] Modify the execute permission of Caddy --- vdyp-web-ui/Dockerfile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/vdyp-web-ui/Dockerfile b/vdyp-web-ui/Dockerfile index 622956d82..812f1d8da 100644 --- a/vdyp-web-ui/Dockerfile +++ b/vdyp-web-ui/Dockerfile @@ -13,7 +13,12 @@ RUN npm ci --ignore-scripts --no-update-notifier --omit=dev && \ # Deploy container # Caddy serves static files FROM caddy:2.8.4-alpine -RUN apk add --no-cache ca-certificates + +# Install packages and ensure permissions in one RUN command +RUN apk add --no-cache ca-certificates && \ + mkdir -p /config/caddy /data/caddy && \ + chown -R 1001:1001 /config/caddy /data/caddy /usr/bin/caddy && \ + chmod +x /usr/bin/caddy # Copy static files and run formatting COPY --from=build /app/dist /srv From 56484d0e3ffe7d9b2d9f8ef974490d66e9a7d3a8 Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Thu, 5 Sep 2024 10:16:36 -0700 Subject: [PATCH 30/53] Define variables used by Caddy --- .github/workflows/openshift-deploy-web-ui-dev.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/openshift-deploy-web-ui-dev.yml b/.github/workflows/openshift-deploy-web-ui-dev.yml index dc7a5252f..caa8d5f02 100644 --- a/.github/workflows/openshift-deploy-web-ui-dev.yml +++ b/.github/workflows/openshift-deploy-web-ui-dev.yml @@ -19,6 +19,11 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Set up environment variables + env: + LOG_LEVEL: ${{ vars.LOG_LEVEL }} + BACKEND_URL: ${{ vars.BACKEND_URL }} + - name: Copy openshift yaml files run: mkdir staging && cp openshift/vdyp-web-ui*.yaml staging/ @@ -34,6 +39,9 @@ jobs: #Frontend variables ACTIVE_PORT: ${{ vars.TARGET_PORT }} + LOG_LEVEL: ${{ env.LOG_LEVEL }} + BACKEND_URL: ${{ env.BACKEND_URL }} + - name: Authenticate and set context uses: redhat-actions/oc-login@v1 with: From c12a78872d23bc2dbc9d854085dcda15e2ddf317 Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Thu, 5 Sep 2024 10:20:36 -0700 Subject: [PATCH 31/53] Remove 'set up environment variables' step --- .github/workflows/openshift-deploy-web-ui-dev.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/openshift-deploy-web-ui-dev.yml b/.github/workflows/openshift-deploy-web-ui-dev.yml index caa8d5f02..243f48a2b 100644 --- a/.github/workflows/openshift-deploy-web-ui-dev.yml +++ b/.github/workflows/openshift-deploy-web-ui-dev.yml @@ -19,11 +19,6 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Set up environment variables - env: - LOG_LEVEL: ${{ vars.LOG_LEVEL }} - BACKEND_URL: ${{ vars.BACKEND_URL }} - - name: Copy openshift yaml files run: mkdir staging && cp openshift/vdyp-web-ui*.yaml staging/ @@ -39,8 +34,8 @@ jobs: #Frontend variables ACTIVE_PORT: ${{ vars.TARGET_PORT }} - LOG_LEVEL: ${{ env.LOG_LEVEL }} - BACKEND_URL: ${{ env.BACKEND_URL }} + LOG_LEVEL: ${{ vars.LOG_LEVEL }} + BACKEND_URL: ${{ vars.BACKEND_URL }} - name: Authenticate and set context uses: redhat-actions/oc-login@v1 From bc4015e36feb214a2ebdde2ca05339813535fa0b Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Thu, 5 Sep 2024 11:14:00 -0700 Subject: [PATCH 32/53] Add env variable definitions on deploy --- openshift/vdyp-web-ui-deployment.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openshift/vdyp-web-ui-deployment.yaml b/openshift/vdyp-web-ui-deployment.yaml index 1fdb42693..8181d0c6e 100644 --- a/openshift/vdyp-web-ui-deployment.yaml +++ b/openshift/vdyp-web-ui-deployment.yaml @@ -27,6 +27,10 @@ spec: configMapKeyRef: name: vdyp-web-ui-config-#{ENV}# key: ACTIVE_PORT + - name: LOG_LEVEL + value: "#{LOG_LEVEL}#" + - name: BACKEND_URL + value: "#{BACKEND_URL}#" resources: requests: cpu: 100m From 74cabc8f8d0d9380d71145337b6a95838b0411d1 Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Thu, 5 Sep 2024 11:47:24 -0700 Subject: [PATCH 33/53] Add permissions to access the directory --- vdyp-web-ui/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/vdyp-web-ui/Dockerfile b/vdyp-web-ui/Dockerfile index 812f1d8da..0c317b92d 100644 --- a/vdyp-web-ui/Dockerfile +++ b/vdyp-web-ui/Dockerfile @@ -18,6 +18,7 @@ FROM caddy:2.8.4-alpine RUN apk add --no-cache ca-certificates && \ mkdir -p /config/caddy /data/caddy && \ chown -R 1001:1001 /config/caddy /data/caddy /usr/bin/caddy && \ + chmod -R 755 /config/caddy /data/caddy && \ chmod +x /usr/bin/caddy # Copy static files and run formatting From e244bfb28287c5a376761b38dbc6d4e43ad5aa9a Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Thu, 5 Sep 2024 12:30:49 -0700 Subject: [PATCH 34/53] Add permissions for Caddy running --- vdyp-web-ui/Dockerfile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/vdyp-web-ui/Dockerfile b/vdyp-web-ui/Dockerfile index 0c317b92d..e62272fa4 100644 --- a/vdyp-web-ui/Dockerfile +++ b/vdyp-web-ui/Dockerfile @@ -16,9 +16,10 @@ FROM caddy:2.8.4-alpine # Install packages and ensure permissions in one RUN command RUN apk add --no-cache ca-certificates && \ - mkdir -p /config/caddy /data/caddy && \ - chown -R 1001:1001 /config/caddy /data/caddy /usr/bin/caddy && \ - chmod -R 755 /config/caddy /data/caddy && \ + mkdir -p /config/caddy /data/caddy /data/caddy/locks && \ + touch /config/caddy/autosave.json /data/caddy/instance.uuid && \ + chown -R 1001:1001 /config/caddy /data/caddy && \ + chmod -R 775 /config/caddy /data/caddy && \ chmod +x /usr/bin/caddy # Copy static files and run formatting From a8948012e94078f6e54829eca834a312d7b1323f Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Thu, 5 Sep 2024 12:46:39 -0700 Subject: [PATCH 35/53] Modify to run as root temporary --- vdyp-web-ui/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vdyp-web-ui/Dockerfile b/vdyp-web-ui/Dockerfile index e62272fa4..261857952 100644 --- a/vdyp-web-ui/Dockerfile +++ b/vdyp-web-ui/Dockerfile @@ -30,4 +30,5 @@ RUN caddy fmt --overwrite /etc/caddy/Caddyfile # Ports, health check and non-root user EXPOSE 3000 3001 HEALTHCHECK --interval=30s --timeout=3s CMD ["curl", "-f", "http://localhost:3001/health"] -USER 1001 +USER root +CMD ["/usr/bin/caddy", "run", "--config", "/etc/caddy/Caddyfile"] From e0d37be9f4d11f0a321a8ad27cac8cc157e0f821 Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Thu, 5 Sep 2024 13:09:56 -0700 Subject: [PATCH 36/53] Remove the unnecessary capability --- vdyp-web-ui/Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vdyp-web-ui/Dockerfile b/vdyp-web-ui/Dockerfile index 261857952..5d7cee103 100644 --- a/vdyp-web-ui/Dockerfile +++ b/vdyp-web-ui/Dockerfile @@ -20,7 +20,8 @@ RUN apk add --no-cache ca-certificates && \ touch /config/caddy/autosave.json /data/caddy/instance.uuid && \ chown -R 1001:1001 /config/caddy /data/caddy && \ chmod -R 775 /config/caddy /data/caddy && \ - chmod +x /usr/bin/caddy + chmod +x /usr/bin/caddy && \ + setcap -r /usr/bin/caddy # This line removes the unnecessary capability # Copy static files and run formatting COPY --from=build /app/dist /srv @@ -30,5 +31,4 @@ RUN caddy fmt --overwrite /etc/caddy/Caddyfile # Ports, health check and non-root user EXPOSE 3000 3001 HEALTHCHECK --interval=30s --timeout=3s CMD ["curl", "-f", "http://localhost:3001/health"] -USER root -CMD ["/usr/bin/caddy", "run", "--config", "/etc/caddy/Caddyfile"] +USER 1001 From 4362859a1153d263795a05805ed4583e7ef856ee Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Thu, 5 Sep 2024 13:16:37 -0700 Subject: [PATCH 37/53] Remove comment --- vdyp-web-ui/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vdyp-web-ui/Dockerfile b/vdyp-web-ui/Dockerfile index 5d7cee103..700e81ece 100644 --- a/vdyp-web-ui/Dockerfile +++ b/vdyp-web-ui/Dockerfile @@ -21,7 +21,7 @@ RUN apk add --no-cache ca-certificates && \ chown -R 1001:1001 /config/caddy /data/caddy && \ chmod -R 775 /config/caddy /data/caddy && \ chmod +x /usr/bin/caddy && \ - setcap -r /usr/bin/caddy # This line removes the unnecessary capability + setcap -r /usr/bin/caddy # Copy static files and run formatting COPY --from=build /app/dist /srv From 84a1790e224545b37a4cfd775c234f32579df545 Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Thu, 5 Sep 2024 13:27:22 -0700 Subject: [PATCH 38/53] Revert changes into quickstart-openshift Dockerfile caddy config part --- vdyp-web-ui/Dockerfile | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/vdyp-web-ui/Dockerfile b/vdyp-web-ui/Dockerfile index 700e81ece..5965768ba 100644 --- a/vdyp-web-ui/Dockerfile +++ b/vdyp-web-ui/Dockerfile @@ -10,23 +10,14 @@ COPY ./src ./src RUN npm ci --ignore-scripts --no-update-notifier --omit=dev && \ npm run build-only -# Deploy container -# Caddy serves static files +# Deploy using Caddy to host static files FROM caddy:2.8.4-alpine +RUN apk add --no-cache ca-certificates -# Install packages and ensure permissions in one RUN command -RUN apk add --no-cache ca-certificates && \ - mkdir -p /config/caddy /data/caddy /data/caddy/locks && \ - touch /config/caddy/autosave.json /data/caddy/instance.uuid && \ - chown -R 1001:1001 /config/caddy /data/caddy && \ - chmod -R 775 /config/caddy /data/caddy && \ - chmod +x /usr/bin/caddy && \ - setcap -r /usr/bin/caddy - -# Copy static files and run formatting +# Copy static files, verify Caddyfile formatting COPY --from=build /app/dist /srv COPY Caddyfile /etc/caddy/Caddyfile -RUN caddy fmt --overwrite /etc/caddy/Caddyfile +RUN caddy fmt /etc/caddy/Caddyfile # Ports, health check and non-root user EXPOSE 3000 3001 From 6d46e83ca33a1c3cfa7e34cf7e437579a5fa60cd Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Thu, 5 Sep 2024 13:34:32 -0700 Subject: [PATCH 39/53] Add overwrite option --- vdyp-web-ui/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vdyp-web-ui/Dockerfile b/vdyp-web-ui/Dockerfile index 5965768ba..7ca51b03f 100644 --- a/vdyp-web-ui/Dockerfile +++ b/vdyp-web-ui/Dockerfile @@ -17,7 +17,7 @@ RUN apk add --no-cache ca-certificates # Copy static files, verify Caddyfile formatting COPY --from=build /app/dist /srv COPY Caddyfile /etc/caddy/Caddyfile -RUN caddy fmt /etc/caddy/Caddyfile +RUN caddy fmt --overwrite /etc/caddy/Caddyfile # Ports, health check and non-root user EXPOSE 3000 3001 From 172e982bac18650f467de6e43731fb7500b6fee5 Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Thu, 5 Sep 2024 14:47:18 -0700 Subject: [PATCH 40/53] Grant exec permissions on Caddy to anonymous user --- vdyp-web-ui/Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vdyp-web-ui/Dockerfile b/vdyp-web-ui/Dockerfile index 7ca51b03f..5aa2a5d9f 100644 --- a/vdyp-web-ui/Dockerfile +++ b/vdyp-web-ui/Dockerfile @@ -19,6 +19,8 @@ COPY --from=build /app/dist /srv COPY Caddyfile /etc/caddy/Caddyfile RUN caddy fmt --overwrite /etc/caddy/Caddyfile +RUN chmod a+x /usr/bin/caddy + # Ports, health check and non-root user EXPOSE 3000 3001 HEALTHCHECK --interval=30s --timeout=3s CMD ["curl", "-f", "http://localhost:3001/health"] From 4faa41add77d25abef9985b296e7f159da00e422 Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Thu, 5 Sep 2024 15:35:33 -0700 Subject: [PATCH 41/53] add NET_BIND_SERVICE capability --- openshift/vdyp-web-ui-deployment.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openshift/vdyp-web-ui-deployment.yaml b/openshift/vdyp-web-ui-deployment.yaml index 8181d0c6e..4f8f5d42f 100644 --- a/openshift/vdyp-web-ui-deployment.yaml +++ b/openshift/vdyp-web-ui-deployment.yaml @@ -30,7 +30,11 @@ spec: - name: LOG_LEVEL value: "#{LOG_LEVEL}#" - name: BACKEND_URL - value: "#{BACKEND_URL}#" + value: "#{BACKEND_URL}#" + securityContext: + capabilities: + add: + - NET_BIND_SERVICE resources: requests: cpu: 100m From bae4eebff94eab8d35457cd13d22d568ccd51423 Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Thu, 5 Sep 2024 16:18:10 -0700 Subject: [PATCH 42/53] Add exe permission to non-root user --- vdyp-web-ui/Dockerfile | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/vdyp-web-ui/Dockerfile b/vdyp-web-ui/Dockerfile index 5aa2a5d9f..1bd0a1dd5 100644 --- a/vdyp-web-ui/Dockerfile +++ b/vdyp-web-ui/Dockerfile @@ -12,9 +12,13 @@ RUN npm ci --ignore-scripts --no-update-notifier --omit=dev && \ # Deploy using Caddy to host static files FROM caddy:2.8.4-alpine -RUN apk add --no-cache ca-certificates -# Copy static files, verify Caddyfile formatting +# Install packages and ensure permissions in one RUN command +RUN apk add --no-cache ca-certificates && \ + mkdir -p /config/caddy /data/caddy && \ + chown -R 1001:1001 /config/caddy /data/caddy + +# Copy static files and run formatting COPY --from=build /app/dist /srv COPY Caddyfile /etc/caddy/Caddyfile RUN caddy fmt --overwrite /etc/caddy/Caddyfile From ff6ba3917cf6297a4f6a6acce03e52448b4bff3c Mon Sep 17 00:00:00 2001 From: vivid-cpreston <97257824+vivid-cpreston@users.noreply.github.com> Date: Thu, 5 Sep 2024 16:29:48 -0700 Subject: [PATCH 43/53] Update Dockerfile --- vdyp-web-ui/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vdyp-web-ui/Dockerfile b/vdyp-web-ui/Dockerfile index 1bd0a1dd5..d25b7367c 100644 --- a/vdyp-web-ui/Dockerfile +++ b/vdyp-web-ui/Dockerfile @@ -23,7 +23,7 @@ COPY --from=build /app/dist /srv COPY Caddyfile /etc/caddy/Caddyfile RUN caddy fmt --overwrite /etc/caddy/Caddyfile -RUN chmod a+x /usr/bin/caddy +RUN chmod 755 /usr/bin/caddy # Ports, health check and non-root user EXPOSE 3000 3001 From 0b0fc1672bdea237c18021beee387e2ebf8b4800 Mon Sep 17 00:00:00 2001 From: vivid-cpreston <97257824+vivid-cpreston@users.noreply.github.com> Date: Thu, 5 Sep 2024 16:32:56 -0700 Subject: [PATCH 44/53] Update vdyp-web-ui-deployment.yaml --- openshift/vdyp-web-ui-deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openshift/vdyp-web-ui-deployment.yaml b/openshift/vdyp-web-ui-deployment.yaml index 4f8f5d42f..e26528c83 100644 --- a/openshift/vdyp-web-ui-deployment.yaml +++ b/openshift/vdyp-web-ui-deployment.yaml @@ -17,7 +17,7 @@ spec: containers: - name: container image: >- - ghcr.io/vividroyjeong/nr-vdyp-vdyp-web-ui:#{TAG}# + ghcr.io/vivid-cpreston/nr-vdyp-vdyp-web-ui:latest ports: - containerPort: #{ACTIVE_PORT}# protocol: TCP From 42bbb87fe080561e4fe8ad563f3a6db6048b4836 Mon Sep 17 00:00:00 2001 From: vivid-cpreston <97257824+vivid-cpreston@users.noreply.github.com> Date: Fri, 6 Sep 2024 10:47:51 -0700 Subject: [PATCH 45/53] Update demo-openshift.yml --- .github/workflows/demo-openshift.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/demo-openshift.yml b/.github/workflows/demo-openshift.yml index 6f6e055ea..40c7ab77b 100644 --- a/.github/workflows/demo-openshift.yml +++ b/.github/workflows/demo-openshift.yml @@ -9,8 +9,8 @@ on: - 'vdyp-buildtools/migrations' concurrency: - # Cancel in progress for PR open and close - group: ${{ github.event.number }} + # Cancel if re-attempted + group: ${{ github.event_name }} cancel-in-progress: true jobs: From 5c0e13abf4aa78d55a04d1fc05b8a298016f40f5 Mon Sep 17 00:00:00 2001 From: vivid-cpreston <97257824+vivid-cpreston@users.noreply.github.com> Date: Fri, 6 Sep 2024 11:06:03 -0700 Subject: [PATCH 46/53] Use branchname for tag --- .github/workflows/demo-openshift.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/demo-openshift.yml b/.github/workflows/demo-openshift.yml index 40c7ab77b..5204378ff 100644 --- a/.github/workflows/demo-openshift.yml +++ b/.github/workflows/demo-openshift.yml @@ -23,12 +23,19 @@ jobs: package: [vdyp-api/backend, vdyp-web-ui/frontend, vdyp-buildtools/migrations] timeout-minutes: 10 steps: + - id: trimBranchName + name: trim branch name if necessary + run: | + export branchTag="${{ github.ref_name }}" + trimTag="${branchTag##*/}" + echo "BRANCH_TAG=$trimTag" >> $GITHUB_OUTPUT + - uses: bcgov-nr/action-builder-ghcr@v2.0.2 with: build_context: ${{ matrix.build_context }} keep_versions: 10 package: ${{ matrix.package }} - tag: ${{ github.event.number }} + tag: ${{ steps.trimBranchName.outputs.BRANCH_TAG }} tag_fallback: latest triggers: ('${{ matrix.package }}/') From 3b5cd79e78bcba76b1a1a44a2e2825c30093aed7 Mon Sep 17 00:00:00 2001 From: vivid-cpreston <97257824+vivid-cpreston@users.noreply.github.com> Date: Fri, 6 Sep 2024 11:21:12 -0700 Subject: [PATCH 47/53] Temporarily disable triggers --- .github/workflows/demo-openshift.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/demo-openshift.yml b/.github/workflows/demo-openshift.yml index 6f6e055ea..64fa69470 100644 --- a/.github/workflows/demo-openshift.yml +++ b/.github/workflows/demo-openshift.yml @@ -41,7 +41,7 @@ jobs: oc_namespace: ${{ secrets.OC_NAMESPACE }} oc_token: ${{ secrets.OC_TOKEN }} with: - triggers: ('vdyp-api/backend/' 'vdyp-web-ui/frontend/' 'vdyp-tools/migrations/') + # triggers: ('vdyp-api/backend/' 'vdyp-web-ui/frontend/' 'vdyp-tools/migrations/') params: --set global.secrets.persist=false From c747b49dd07513b347f87a907e5eaaa748daea55 Mon Sep 17 00:00:00 2001 From: Chris Preston Date: Fri, 6 Sep 2024 12:28:19 -0700 Subject: [PATCH 48/53] Rearrange directories --- .github/workflows/demo-openshift.yml | 10 +++++----- {vdyp-api/backend => backend}/.dockerignore | 0 {vdyp-api/backend => backend}/.gitignore | 0 .../backend => backend}/.mvn/wrapper/.gitignore | 0 .../.mvn/wrapper/MavenWrapperDownloader.java | 0 .../.mvn/wrapper/maven-wrapper.properties | 0 {vdyp-api/backend => backend}/Dockerfile | 0 {vdyp-api/backend => backend}/README.md | 0 {vdyp-api/backend => backend}/mvnw | 0 {vdyp-api/backend => backend}/mvnw.cmd | 0 {vdyp-api/backend => backend}/openshift.deploy.yml | 0 {vdyp-api/backend => backend}/pom.xml | 0 .../bc/gov/nrs/api/v1/endpoints/UserEndpoint.java | 0 .../bc/gov/nrs/api/v1/entity/UserAddressEntity.java | 0 .../ca/bc/gov/nrs/api/v1/entity/UserEntity.java | 0 .../api/v1/repository/UserAddressRepository.java | 0 .../gov/nrs/api/v1/repository/UserRepository.java | 0 .../ca/bc/gov/nrs/api/v1/service/UserService.java | 0 .../java/ca/bc/gov/nrs/api/v1/structs/User.java | 0 .../ca/bc/gov/nrs/api/v1/structs/UserAddress.java | 0 .../main/resources/META-INF/resources/index.html | 0 .../src/main/resources/application.properties | 0 .../main/resources/db/migration/V1.0.0__init.sql | 0 .../java/ca/bc/gov/nrs/api/helpers/TestHelper.java | 0 .../gov/nrs/api/v1/endpoints/UserEndpointTest.java | 0 .../src/test/resources/application.properties | 0 .../migrations => frontend}/.dockerignore | 0 {vdyp-web-ui/frontend => frontend}/.eslintignore | 0 {vdyp-web-ui/frontend => frontend}/.eslintrc.yml | 0 {vdyp-web-ui/frontend => frontend}/.prettierrc.yml | 0 .../frontend => frontend}/.vscode/extensions.json | 0 {vdyp-web-ui/frontend => frontend}/Caddyfile | 0 {vdyp-web-ui/frontend => frontend}/Dockerfile | 0 .../frontend => frontend}/cypress.config.ts | 0 .../cypress/e2e/home-page.cy.ts | 0 .../cypress/e2e/user-table.cy.ts | 0 .../frontend => frontend}/cypress/support/e2e.ts | 0 .../frontend => frontend}/cypress/support/index.ts | 0 {vdyp-web-ui/frontend => frontend}/index.html | 0 .../frontend => frontend}/package-lock.json | 0 {vdyp-web-ui/frontend => frontend}/package.json | 0 .../frontend => frontend}/public/favicon.ico | Bin {vdyp-web-ui/frontend => frontend}/src/App.tsx | 0 .../src/__tests__/App.test.tsx | 0 .../src/assets/BCID_H_rgb_pos.png | Bin .../src/assets/gov-bc-logo-horiz.png | Bin .../src/components/Dashboard.tsx | 0 .../frontend => frontend}/src/components/Footer.tsx | 0 .../frontend => frontend}/src/components/Header.tsx | 0 .../src/components/NotFound.tsx | 0 .../src/components/__tests__/NotFound.test.tsx | 0 {vdyp-web-ui/frontend => frontend}/src/index.css | 0 .../frontend => frontend}/src/interfaces/UserDto.ts | 0 {vdyp-web-ui/frontend => frontend}/src/main.tsx | 0 .../frontend => frontend}/src/routes/index.tsx | 0 .../src/service/api-service.ts | 0 .../frontend => frontend}/src/test-setup.ts | 0 .../frontend => frontend}/src/test-utils.tsx | 0 {vdyp-web-ui/frontend => frontend}/src/theme.tsx | 0 {vdyp-web-ui/frontend => frontend}/tsconfig.json | 0 .../frontend => frontend}/tsconfig.node.json | 0 {vdyp-web-ui/frontend => frontend}/vite.config.ts | 0 {vdyp-web-ui/frontend => frontend}/vitest.config.ts | 0 {vdyp-web-ui/frontend => migrations}/.dockerignore | 0 .../migrations => migrations}/Dockerfile | 0 .../migrations => migrations}/sql/V1.0.0__init.sql | 0 .../sql/V1.0.1__alter_user_seq.sql | 0 67 files changed, 5 insertions(+), 5 deletions(-) rename {vdyp-api/backend => backend}/.dockerignore (100%) rename {vdyp-api/backend => backend}/.gitignore (100%) rename {vdyp-api/backend => backend}/.mvn/wrapper/.gitignore (100%) rename {vdyp-api/backend => backend}/.mvn/wrapper/MavenWrapperDownloader.java (100%) rename {vdyp-api/backend => backend}/.mvn/wrapper/maven-wrapper.properties (100%) rename {vdyp-api/backend => backend}/Dockerfile (100%) rename {vdyp-api/backend => backend}/README.md (100%) rename {vdyp-api/backend => backend}/mvnw (100%) rename {vdyp-api/backend => backend}/mvnw.cmd (100%) rename {vdyp-api/backend => backend}/openshift.deploy.yml (100%) rename {vdyp-api/backend => backend}/pom.xml (100%) rename {vdyp-api/backend => backend}/src/main/java/ca/bc/gov/nrs/api/v1/endpoints/UserEndpoint.java (100%) rename {vdyp-api/backend => backend}/src/main/java/ca/bc/gov/nrs/api/v1/entity/UserAddressEntity.java (100%) rename {vdyp-api/backend => backend}/src/main/java/ca/bc/gov/nrs/api/v1/entity/UserEntity.java (100%) rename {vdyp-api/backend => backend}/src/main/java/ca/bc/gov/nrs/api/v1/repository/UserAddressRepository.java (100%) rename {vdyp-api/backend => backend}/src/main/java/ca/bc/gov/nrs/api/v1/repository/UserRepository.java (100%) rename {vdyp-api/backend => backend}/src/main/java/ca/bc/gov/nrs/api/v1/service/UserService.java (100%) rename {vdyp-api/backend => backend}/src/main/java/ca/bc/gov/nrs/api/v1/structs/User.java (100%) rename {vdyp-api/backend => backend}/src/main/java/ca/bc/gov/nrs/api/v1/structs/UserAddress.java (100%) rename {vdyp-api/backend => backend}/src/main/resources/META-INF/resources/index.html (100%) rename {vdyp-api/backend => backend}/src/main/resources/application.properties (100%) rename {vdyp-api/backend => backend}/src/main/resources/db/migration/V1.0.0__init.sql (100%) rename {vdyp-api/backend => backend}/src/test/java/ca/bc/gov/nrs/api/helpers/TestHelper.java (100%) rename {vdyp-api/backend => backend}/src/test/java/ca/bc/gov/nrs/api/v1/endpoints/UserEndpointTest.java (100%) rename {vdyp-api/backend => backend}/src/test/resources/application.properties (100%) rename {vdyp-buildtools/migrations => frontend}/.dockerignore (100%) rename {vdyp-web-ui/frontend => frontend}/.eslintignore (100%) rename {vdyp-web-ui/frontend => frontend}/.eslintrc.yml (100%) rename {vdyp-web-ui/frontend => frontend}/.prettierrc.yml (100%) rename {vdyp-web-ui/frontend => frontend}/.vscode/extensions.json (100%) rename {vdyp-web-ui/frontend => frontend}/Caddyfile (100%) rename {vdyp-web-ui/frontend => frontend}/Dockerfile (100%) rename {vdyp-web-ui/frontend => frontend}/cypress.config.ts (100%) rename {vdyp-web-ui/frontend => frontend}/cypress/e2e/home-page.cy.ts (100%) rename {vdyp-web-ui/frontend => frontend}/cypress/e2e/user-table.cy.ts (100%) rename {vdyp-web-ui/frontend => frontend}/cypress/support/e2e.ts (100%) rename {vdyp-web-ui/frontend => frontend}/cypress/support/index.ts (100%) rename {vdyp-web-ui/frontend => frontend}/index.html (100%) rename {vdyp-web-ui/frontend => frontend}/package-lock.json (100%) rename {vdyp-web-ui/frontend => frontend}/package.json (100%) rename {vdyp-web-ui/frontend => frontend}/public/favicon.ico (100%) rename {vdyp-web-ui/frontend => frontend}/src/App.tsx (100%) rename {vdyp-web-ui/frontend => frontend}/src/__tests__/App.test.tsx (100%) rename {vdyp-web-ui/frontend => frontend}/src/assets/BCID_H_rgb_pos.png (100%) rename {vdyp-web-ui/frontend => frontend}/src/assets/gov-bc-logo-horiz.png (100%) rename {vdyp-web-ui/frontend => frontend}/src/components/Dashboard.tsx (100%) rename {vdyp-web-ui/frontend => frontend}/src/components/Footer.tsx (100%) rename {vdyp-web-ui/frontend => frontend}/src/components/Header.tsx (100%) rename {vdyp-web-ui/frontend => frontend}/src/components/NotFound.tsx (100%) rename {vdyp-web-ui/frontend => frontend}/src/components/__tests__/NotFound.test.tsx (100%) rename {vdyp-web-ui/frontend => frontend}/src/index.css (100%) rename {vdyp-web-ui/frontend => frontend}/src/interfaces/UserDto.ts (100%) rename {vdyp-web-ui/frontend => frontend}/src/main.tsx (100%) rename {vdyp-web-ui/frontend => frontend}/src/routes/index.tsx (100%) rename {vdyp-web-ui/frontend => frontend}/src/service/api-service.ts (100%) rename {vdyp-web-ui/frontend => frontend}/src/test-setup.ts (100%) rename {vdyp-web-ui/frontend => frontend}/src/test-utils.tsx (100%) rename {vdyp-web-ui/frontend => frontend}/src/theme.tsx (100%) rename {vdyp-web-ui/frontend => frontend}/tsconfig.json (100%) rename {vdyp-web-ui/frontend => frontend}/tsconfig.node.json (100%) rename {vdyp-web-ui/frontend => frontend}/vite.config.ts (100%) rename {vdyp-web-ui/frontend => frontend}/vitest.config.ts (100%) rename {vdyp-web-ui/frontend => migrations}/.dockerignore (100%) rename {vdyp-buildtools/migrations => migrations}/Dockerfile (100%) rename {vdyp-buildtools/migrations => migrations}/sql/V1.0.0__init.sql (100%) rename {vdyp-buildtools/migrations => migrations}/sql/V1.0.1__alter_user_seq.sql (100%) diff --git a/.github/workflows/demo-openshift.yml b/.github/workflows/demo-openshift.yml index 6ff3c1443..bff782561 100644 --- a/.github/workflows/demo-openshift.yml +++ b/.github/workflows/demo-openshift.yml @@ -4,9 +4,9 @@ on: workflow_dispatch: pull_request: paths: - - 'vdyp-web-ui/**' - - 'vdyp-api/**' - - 'vdyp-buildtools/migrations' + - 'frontend/**' + - 'backend/**' + - 'migrations/**' concurrency: # Cancel if re-attempted @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-22.04 strategy: matrix: - package: [vdyp-api/backend, vdyp-web-ui/frontend, vdyp-buildtools/migrations] + package: [backend, frontend, migrations] timeout-minutes: 10 steps: - id: trimBranchName @@ -48,7 +48,7 @@ jobs: oc_namespace: ${{ secrets.OC_NAMESPACE }} oc_token: ${{ secrets.OC_TOKEN }} with: - # triggers: ('vdyp-api/backend/' 'vdyp-web-ui/frontend/' 'vdyp-tools/migrations/') + triggers: ('backend/' 'frontend/' 'migrations/') params: --set global.secrets.persist=false diff --git a/vdyp-api/backend/.dockerignore b/backend/.dockerignore similarity index 100% rename from vdyp-api/backend/.dockerignore rename to backend/.dockerignore diff --git a/vdyp-api/backend/.gitignore b/backend/.gitignore similarity index 100% rename from vdyp-api/backend/.gitignore rename to backend/.gitignore diff --git a/vdyp-api/backend/.mvn/wrapper/.gitignore b/backend/.mvn/wrapper/.gitignore similarity index 100% rename from vdyp-api/backend/.mvn/wrapper/.gitignore rename to backend/.mvn/wrapper/.gitignore diff --git a/vdyp-api/backend/.mvn/wrapper/MavenWrapperDownloader.java b/backend/.mvn/wrapper/MavenWrapperDownloader.java similarity index 100% rename from vdyp-api/backend/.mvn/wrapper/MavenWrapperDownloader.java rename to backend/.mvn/wrapper/MavenWrapperDownloader.java diff --git a/vdyp-api/backend/.mvn/wrapper/maven-wrapper.properties b/backend/.mvn/wrapper/maven-wrapper.properties similarity index 100% rename from vdyp-api/backend/.mvn/wrapper/maven-wrapper.properties rename to backend/.mvn/wrapper/maven-wrapper.properties diff --git a/vdyp-api/backend/Dockerfile b/backend/Dockerfile similarity index 100% rename from vdyp-api/backend/Dockerfile rename to backend/Dockerfile diff --git a/vdyp-api/backend/README.md b/backend/README.md similarity index 100% rename from vdyp-api/backend/README.md rename to backend/README.md diff --git a/vdyp-api/backend/mvnw b/backend/mvnw similarity index 100% rename from vdyp-api/backend/mvnw rename to backend/mvnw diff --git a/vdyp-api/backend/mvnw.cmd b/backend/mvnw.cmd similarity index 100% rename from vdyp-api/backend/mvnw.cmd rename to backend/mvnw.cmd diff --git a/vdyp-api/backend/openshift.deploy.yml b/backend/openshift.deploy.yml similarity index 100% rename from vdyp-api/backend/openshift.deploy.yml rename to backend/openshift.deploy.yml diff --git a/vdyp-api/backend/pom.xml b/backend/pom.xml similarity index 100% rename from vdyp-api/backend/pom.xml rename to backend/pom.xml diff --git a/vdyp-api/backend/src/main/java/ca/bc/gov/nrs/api/v1/endpoints/UserEndpoint.java b/backend/src/main/java/ca/bc/gov/nrs/api/v1/endpoints/UserEndpoint.java similarity index 100% rename from vdyp-api/backend/src/main/java/ca/bc/gov/nrs/api/v1/endpoints/UserEndpoint.java rename to backend/src/main/java/ca/bc/gov/nrs/api/v1/endpoints/UserEndpoint.java diff --git a/vdyp-api/backend/src/main/java/ca/bc/gov/nrs/api/v1/entity/UserAddressEntity.java b/backend/src/main/java/ca/bc/gov/nrs/api/v1/entity/UserAddressEntity.java similarity index 100% rename from vdyp-api/backend/src/main/java/ca/bc/gov/nrs/api/v1/entity/UserAddressEntity.java rename to backend/src/main/java/ca/bc/gov/nrs/api/v1/entity/UserAddressEntity.java diff --git a/vdyp-api/backend/src/main/java/ca/bc/gov/nrs/api/v1/entity/UserEntity.java b/backend/src/main/java/ca/bc/gov/nrs/api/v1/entity/UserEntity.java similarity index 100% rename from vdyp-api/backend/src/main/java/ca/bc/gov/nrs/api/v1/entity/UserEntity.java rename to backend/src/main/java/ca/bc/gov/nrs/api/v1/entity/UserEntity.java diff --git a/vdyp-api/backend/src/main/java/ca/bc/gov/nrs/api/v1/repository/UserAddressRepository.java b/backend/src/main/java/ca/bc/gov/nrs/api/v1/repository/UserAddressRepository.java similarity index 100% rename from vdyp-api/backend/src/main/java/ca/bc/gov/nrs/api/v1/repository/UserAddressRepository.java rename to backend/src/main/java/ca/bc/gov/nrs/api/v1/repository/UserAddressRepository.java diff --git a/vdyp-api/backend/src/main/java/ca/bc/gov/nrs/api/v1/repository/UserRepository.java b/backend/src/main/java/ca/bc/gov/nrs/api/v1/repository/UserRepository.java similarity index 100% rename from vdyp-api/backend/src/main/java/ca/bc/gov/nrs/api/v1/repository/UserRepository.java rename to backend/src/main/java/ca/bc/gov/nrs/api/v1/repository/UserRepository.java diff --git a/vdyp-api/backend/src/main/java/ca/bc/gov/nrs/api/v1/service/UserService.java b/backend/src/main/java/ca/bc/gov/nrs/api/v1/service/UserService.java similarity index 100% rename from vdyp-api/backend/src/main/java/ca/bc/gov/nrs/api/v1/service/UserService.java rename to backend/src/main/java/ca/bc/gov/nrs/api/v1/service/UserService.java diff --git a/vdyp-api/backend/src/main/java/ca/bc/gov/nrs/api/v1/structs/User.java b/backend/src/main/java/ca/bc/gov/nrs/api/v1/structs/User.java similarity index 100% rename from vdyp-api/backend/src/main/java/ca/bc/gov/nrs/api/v1/structs/User.java rename to backend/src/main/java/ca/bc/gov/nrs/api/v1/structs/User.java diff --git a/vdyp-api/backend/src/main/java/ca/bc/gov/nrs/api/v1/structs/UserAddress.java b/backend/src/main/java/ca/bc/gov/nrs/api/v1/structs/UserAddress.java similarity index 100% rename from vdyp-api/backend/src/main/java/ca/bc/gov/nrs/api/v1/structs/UserAddress.java rename to backend/src/main/java/ca/bc/gov/nrs/api/v1/structs/UserAddress.java diff --git a/vdyp-api/backend/src/main/resources/META-INF/resources/index.html b/backend/src/main/resources/META-INF/resources/index.html similarity index 100% rename from vdyp-api/backend/src/main/resources/META-INF/resources/index.html rename to backend/src/main/resources/META-INF/resources/index.html diff --git a/vdyp-api/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties similarity index 100% rename from vdyp-api/backend/src/main/resources/application.properties rename to backend/src/main/resources/application.properties diff --git a/vdyp-api/backend/src/main/resources/db/migration/V1.0.0__init.sql b/backend/src/main/resources/db/migration/V1.0.0__init.sql similarity index 100% rename from vdyp-api/backend/src/main/resources/db/migration/V1.0.0__init.sql rename to backend/src/main/resources/db/migration/V1.0.0__init.sql diff --git a/vdyp-api/backend/src/test/java/ca/bc/gov/nrs/api/helpers/TestHelper.java b/backend/src/test/java/ca/bc/gov/nrs/api/helpers/TestHelper.java similarity index 100% rename from vdyp-api/backend/src/test/java/ca/bc/gov/nrs/api/helpers/TestHelper.java rename to backend/src/test/java/ca/bc/gov/nrs/api/helpers/TestHelper.java diff --git a/vdyp-api/backend/src/test/java/ca/bc/gov/nrs/api/v1/endpoints/UserEndpointTest.java b/backend/src/test/java/ca/bc/gov/nrs/api/v1/endpoints/UserEndpointTest.java similarity index 100% rename from vdyp-api/backend/src/test/java/ca/bc/gov/nrs/api/v1/endpoints/UserEndpointTest.java rename to backend/src/test/java/ca/bc/gov/nrs/api/v1/endpoints/UserEndpointTest.java diff --git a/vdyp-api/backend/src/test/resources/application.properties b/backend/src/test/resources/application.properties similarity index 100% rename from vdyp-api/backend/src/test/resources/application.properties rename to backend/src/test/resources/application.properties diff --git a/vdyp-buildtools/migrations/.dockerignore b/frontend/.dockerignore similarity index 100% rename from vdyp-buildtools/migrations/.dockerignore rename to frontend/.dockerignore diff --git a/vdyp-web-ui/frontend/.eslintignore b/frontend/.eslintignore similarity index 100% rename from vdyp-web-ui/frontend/.eslintignore rename to frontend/.eslintignore diff --git a/vdyp-web-ui/frontend/.eslintrc.yml b/frontend/.eslintrc.yml similarity index 100% rename from vdyp-web-ui/frontend/.eslintrc.yml rename to frontend/.eslintrc.yml diff --git a/vdyp-web-ui/frontend/.prettierrc.yml b/frontend/.prettierrc.yml similarity index 100% rename from vdyp-web-ui/frontend/.prettierrc.yml rename to frontend/.prettierrc.yml diff --git a/vdyp-web-ui/frontend/.vscode/extensions.json b/frontend/.vscode/extensions.json similarity index 100% rename from vdyp-web-ui/frontend/.vscode/extensions.json rename to frontend/.vscode/extensions.json diff --git a/vdyp-web-ui/frontend/Caddyfile b/frontend/Caddyfile similarity index 100% rename from vdyp-web-ui/frontend/Caddyfile rename to frontend/Caddyfile diff --git a/vdyp-web-ui/frontend/Dockerfile b/frontend/Dockerfile similarity index 100% rename from vdyp-web-ui/frontend/Dockerfile rename to frontend/Dockerfile diff --git a/vdyp-web-ui/frontend/cypress.config.ts b/frontend/cypress.config.ts similarity index 100% rename from vdyp-web-ui/frontend/cypress.config.ts rename to frontend/cypress.config.ts diff --git a/vdyp-web-ui/frontend/cypress/e2e/home-page.cy.ts b/frontend/cypress/e2e/home-page.cy.ts similarity index 100% rename from vdyp-web-ui/frontend/cypress/e2e/home-page.cy.ts rename to frontend/cypress/e2e/home-page.cy.ts diff --git a/vdyp-web-ui/frontend/cypress/e2e/user-table.cy.ts b/frontend/cypress/e2e/user-table.cy.ts similarity index 100% rename from vdyp-web-ui/frontend/cypress/e2e/user-table.cy.ts rename to frontend/cypress/e2e/user-table.cy.ts diff --git a/vdyp-web-ui/frontend/cypress/support/e2e.ts b/frontend/cypress/support/e2e.ts similarity index 100% rename from vdyp-web-ui/frontend/cypress/support/e2e.ts rename to frontend/cypress/support/e2e.ts diff --git a/vdyp-web-ui/frontend/cypress/support/index.ts b/frontend/cypress/support/index.ts similarity index 100% rename from vdyp-web-ui/frontend/cypress/support/index.ts rename to frontend/cypress/support/index.ts diff --git a/vdyp-web-ui/frontend/index.html b/frontend/index.html similarity index 100% rename from vdyp-web-ui/frontend/index.html rename to frontend/index.html diff --git a/vdyp-web-ui/frontend/package-lock.json b/frontend/package-lock.json similarity index 100% rename from vdyp-web-ui/frontend/package-lock.json rename to frontend/package-lock.json diff --git a/vdyp-web-ui/frontend/package.json b/frontend/package.json similarity index 100% rename from vdyp-web-ui/frontend/package.json rename to frontend/package.json diff --git a/vdyp-web-ui/frontend/public/favicon.ico b/frontend/public/favicon.ico similarity index 100% rename from vdyp-web-ui/frontend/public/favicon.ico rename to frontend/public/favicon.ico diff --git a/vdyp-web-ui/frontend/src/App.tsx b/frontend/src/App.tsx similarity index 100% rename from vdyp-web-ui/frontend/src/App.tsx rename to frontend/src/App.tsx diff --git a/vdyp-web-ui/frontend/src/__tests__/App.test.tsx b/frontend/src/__tests__/App.test.tsx similarity index 100% rename from vdyp-web-ui/frontend/src/__tests__/App.test.tsx rename to frontend/src/__tests__/App.test.tsx diff --git a/vdyp-web-ui/frontend/src/assets/BCID_H_rgb_pos.png b/frontend/src/assets/BCID_H_rgb_pos.png similarity index 100% rename from vdyp-web-ui/frontend/src/assets/BCID_H_rgb_pos.png rename to frontend/src/assets/BCID_H_rgb_pos.png diff --git a/vdyp-web-ui/frontend/src/assets/gov-bc-logo-horiz.png b/frontend/src/assets/gov-bc-logo-horiz.png similarity index 100% rename from vdyp-web-ui/frontend/src/assets/gov-bc-logo-horiz.png rename to frontend/src/assets/gov-bc-logo-horiz.png diff --git a/vdyp-web-ui/frontend/src/components/Dashboard.tsx b/frontend/src/components/Dashboard.tsx similarity index 100% rename from vdyp-web-ui/frontend/src/components/Dashboard.tsx rename to frontend/src/components/Dashboard.tsx diff --git a/vdyp-web-ui/frontend/src/components/Footer.tsx b/frontend/src/components/Footer.tsx similarity index 100% rename from vdyp-web-ui/frontend/src/components/Footer.tsx rename to frontend/src/components/Footer.tsx diff --git a/vdyp-web-ui/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx similarity index 100% rename from vdyp-web-ui/frontend/src/components/Header.tsx rename to frontend/src/components/Header.tsx diff --git a/vdyp-web-ui/frontend/src/components/NotFound.tsx b/frontend/src/components/NotFound.tsx similarity index 100% rename from vdyp-web-ui/frontend/src/components/NotFound.tsx rename to frontend/src/components/NotFound.tsx diff --git a/vdyp-web-ui/frontend/src/components/__tests__/NotFound.test.tsx b/frontend/src/components/__tests__/NotFound.test.tsx similarity index 100% rename from vdyp-web-ui/frontend/src/components/__tests__/NotFound.test.tsx rename to frontend/src/components/__tests__/NotFound.test.tsx diff --git a/vdyp-web-ui/frontend/src/index.css b/frontend/src/index.css similarity index 100% rename from vdyp-web-ui/frontend/src/index.css rename to frontend/src/index.css diff --git a/vdyp-web-ui/frontend/src/interfaces/UserDto.ts b/frontend/src/interfaces/UserDto.ts similarity index 100% rename from vdyp-web-ui/frontend/src/interfaces/UserDto.ts rename to frontend/src/interfaces/UserDto.ts diff --git a/vdyp-web-ui/frontend/src/main.tsx b/frontend/src/main.tsx similarity index 100% rename from vdyp-web-ui/frontend/src/main.tsx rename to frontend/src/main.tsx diff --git a/vdyp-web-ui/frontend/src/routes/index.tsx b/frontend/src/routes/index.tsx similarity index 100% rename from vdyp-web-ui/frontend/src/routes/index.tsx rename to frontend/src/routes/index.tsx diff --git a/vdyp-web-ui/frontend/src/service/api-service.ts b/frontend/src/service/api-service.ts similarity index 100% rename from vdyp-web-ui/frontend/src/service/api-service.ts rename to frontend/src/service/api-service.ts diff --git a/vdyp-web-ui/frontend/src/test-setup.ts b/frontend/src/test-setup.ts similarity index 100% rename from vdyp-web-ui/frontend/src/test-setup.ts rename to frontend/src/test-setup.ts diff --git a/vdyp-web-ui/frontend/src/test-utils.tsx b/frontend/src/test-utils.tsx similarity index 100% rename from vdyp-web-ui/frontend/src/test-utils.tsx rename to frontend/src/test-utils.tsx diff --git a/vdyp-web-ui/frontend/src/theme.tsx b/frontend/src/theme.tsx similarity index 100% rename from vdyp-web-ui/frontend/src/theme.tsx rename to frontend/src/theme.tsx diff --git a/vdyp-web-ui/frontend/tsconfig.json b/frontend/tsconfig.json similarity index 100% rename from vdyp-web-ui/frontend/tsconfig.json rename to frontend/tsconfig.json diff --git a/vdyp-web-ui/frontend/tsconfig.node.json b/frontend/tsconfig.node.json similarity index 100% rename from vdyp-web-ui/frontend/tsconfig.node.json rename to frontend/tsconfig.node.json diff --git a/vdyp-web-ui/frontend/vite.config.ts b/frontend/vite.config.ts similarity index 100% rename from vdyp-web-ui/frontend/vite.config.ts rename to frontend/vite.config.ts diff --git a/vdyp-web-ui/frontend/vitest.config.ts b/frontend/vitest.config.ts similarity index 100% rename from vdyp-web-ui/frontend/vitest.config.ts rename to frontend/vitest.config.ts diff --git a/vdyp-web-ui/frontend/.dockerignore b/migrations/.dockerignore similarity index 100% rename from vdyp-web-ui/frontend/.dockerignore rename to migrations/.dockerignore diff --git a/vdyp-buildtools/migrations/Dockerfile b/migrations/Dockerfile similarity index 100% rename from vdyp-buildtools/migrations/Dockerfile rename to migrations/Dockerfile diff --git a/vdyp-buildtools/migrations/sql/V1.0.0__init.sql b/migrations/sql/V1.0.0__init.sql similarity index 100% rename from vdyp-buildtools/migrations/sql/V1.0.0__init.sql rename to migrations/sql/V1.0.0__init.sql diff --git a/vdyp-buildtools/migrations/sql/V1.0.1__alter_user_seq.sql b/migrations/sql/V1.0.1__alter_user_seq.sql similarity index 100% rename from vdyp-buildtools/migrations/sql/V1.0.1__alter_user_seq.sql rename to migrations/sql/V1.0.1__alter_user_seq.sql From 292601e3939aa0ba6f849364954ae537754e902d Mon Sep 17 00:00:00 2001 From: vivid-cpreston <97257824+vivid-cpreston@users.noreply.github.com> Date: Fri, 6 Sep 2024 12:35:53 -0700 Subject: [PATCH 49/53] Update demo-openshift.yml --- .github/workflows/demo-openshift.yml | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/.github/workflows/demo-openshift.yml b/.github/workflows/demo-openshift.yml index bff782561..9e706f8a1 100644 --- a/.github/workflows/demo-openshift.yml +++ b/.github/workflows/demo-openshift.yml @@ -2,11 +2,6 @@ name: PR on: workflow_dispatch: - pull_request: - paths: - - 'frontend/**' - - 'backend/**' - - 'migrations/**' concurrency: # Cancel if re-attempted @@ -23,27 +18,20 @@ jobs: package: [backend, frontend, migrations] timeout-minutes: 10 steps: - - id: trimBranchName - name: trim branch name if necessary - run: | - export branchTag="${{ github.ref_name }}" - trimTag="${branchTag##*/}" - echo "BRANCH_TAG=$trimTag" >> $GITHUB_OUTPUT - - - uses: bcgov-nr/action-builder-ghcr@v2.0.2 + - uses: bcgov-nr/action-builder-ghcr@v2.2.0 with: - build_context: ${{ matrix.build_context }} - keep_versions: 10 + keep_versions: 50 package: ${{ matrix.package }} - tag: ${{ steps.trimBranchName.outputs.BRANCH_TAG }} + tag: ${{ github.event.number }} tag_fallback: latest triggers: ('${{ matrix.package }}/') + # https://github.com/bcgov/quickstart-openshift-helpers deploys: name: Deploys needs: [builds] - uses: bcgov/quickstart-openshift-helpers/.github/workflows/.deployer.yml@v0.4.1 + uses: bcgov/quickstart-openshift-helpers/.github/workflows/.deployer.yml@v0.6.1 secrets: oc_namespace: ${{ secrets.OC_NAMESPACE }} oc_token: ${{ secrets.OC_TOKEN }} From 49a133512641c4620aecb6de22e81397af826906 Mon Sep 17 00:00:00 2001 From: Chris Preston Date: Fri, 6 Sep 2024 13:19:18 -0700 Subject: [PATCH 50/53] Re-add PR triggers --- .github/workflows/demo-openshift.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/demo-openshift.yml b/.github/workflows/demo-openshift.yml index 9e706f8a1..e8f3861c5 100644 --- a/.github/workflows/demo-openshift.yml +++ b/.github/workflows/demo-openshift.yml @@ -2,7 +2,11 @@ name: PR on: workflow_dispatch: - + pull_request: + paths: + - 'frontend/**' + - 'backend/**' + - 'migrations/**' concurrency: # Cancel if re-attempted group: ${{ github.event_name }} @@ -26,7 +30,6 @@ jobs: tag_fallback: latest triggers: ('${{ matrix.package }}/') - # https://github.com/bcgov/quickstart-openshift-helpers deploys: name: Deploys @@ -39,7 +42,7 @@ jobs: triggers: ('backend/' 'frontend/' 'migrations/') params: --set global.secrets.persist=false - + # tests: # name: Tests # if: needs.deploys.outputs.triggered == 'true' From 433c983e28b2df20bbc7333db5c0a1d2422c1077 Mon Sep 17 00:00:00 2001 From: vivid-cpreston <97257824+vivid-cpreston@users.noreply.github.com> Date: Fri, 6 Sep 2024 14:50:16 -0700 Subject: [PATCH 51/53] Uncomment sonar analysis --- .github/workflows/maven.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 3ae2a911b..06902b1cb 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -44,11 +44,11 @@ jobs: restore-keys: ${{ runner.os }}-m2 - name: Build with Maven run: mvn -Pcoverage package verify -# - name: Run Sonar Analysis -# run: mvn sonar:sonar -# -Dsonar.host.url=https://sonarcloud.io -# -Dsonar.organization=bcgov-sonarcloud -# -Dsonar.projectKey=bcgov_VDYP + - name: Run Sonar Analysis + run: mvn sonar:sonar + -Dsonar.host.url=https://sonarcloud.io + -Dsonar.organization=bcgov-sonarcloud + -Dsonar.projectKey=bcgov_VDYP env: GITHUB_TOKEN: ${{ github.token }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} From f650f7dd19bbdb1d97d56e824d413daac3576e4a Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Fri, 6 Sep 2024 15:24:44 -0700 Subject: [PATCH 52/53] test input --- vdyp-web-ui/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vdyp-web-ui/README.md b/vdyp-web-ui/README.md index 30e8211f9..7fe3e2aaa 100644 --- a/vdyp-web-ui/README.md +++ b/vdyp-web-ui/README.md @@ -1,2 +1,2 @@ # VDYP -Web based user interface for the Variable Density Yield Projection (VDYP) +Web based user interface for the Variable Density Yield Projection (VDYP). From d35086de2af5236a348dfdb01acfcc75667b7032 Mon Sep 17 00:00:00 2001 From: Roy Jeong Date: Fri, 6 Sep 2024 15:40:21 -0700 Subject: [PATCH 53/53] Add vdyp-web-ui into PR path --- .github/workflows/demo-openshift.yml | 16 ++++++++-------- frontend/package-lock.json | 5 +++++ frontend/package.json | 3 ++- .../tests/integration/package-lock.json | 5 +++++ vdyp-buildtools/tests/integration/package.json | 1 + vdyp-web-ui/README.md | 2 +- 6 files changed, 22 insertions(+), 10 deletions(-) diff --git a/.github/workflows/demo-openshift.yml b/.github/workflows/demo-openshift.yml index e8f3861c5..976825a49 100644 --- a/.github/workflows/demo-openshift.yml +++ b/.github/workflows/demo-openshift.yml @@ -4,9 +4,10 @@ on: workflow_dispatch: pull_request: paths: - - 'frontend/**' - - 'backend/**' - - 'migrations/**' + - "frontend/**" + - "backend/**" + - "migrations/**" + - "vdyp-web-ui/**" concurrency: # Cancel if re-attempted group: ${{ github.event_name }} @@ -19,7 +20,7 @@ jobs: runs-on: ubuntu-22.04 strategy: matrix: - package: [backend, frontend, migrations] + package: [backend, frontend, migrations, vdyp-web-ui] timeout-minutes: 10 steps: - uses: bcgov-nr/action-builder-ghcr@v2.2.0 @@ -39,10 +40,9 @@ jobs: oc_namespace: ${{ secrets.OC_NAMESPACE }} oc_token: ${{ secrets.OC_TOKEN }} with: - triggers: ('backend/' 'frontend/' 'migrations/') - params: - --set global.secrets.persist=false - + triggers: ('backend/' 'frontend/' 'migrations/' 'vdyp-web-ui/') + params: --set global.secrets.persist=false + # tests: # name: Tests # if: needs.deploys.outputs.triggered == 'true' diff --git a/frontend/package-lock.json b/frontend/package-lock.json index dde5d8dff..0dcca04e0 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -18,6 +18,7 @@ "@vitejs/plugin-react": "^4.0.0", "axios": "^1.6.8", "mui-datatables": "^4.3.0", + "quickstart-openshift-react-frontend": "file:", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router": "^6.12.1", @@ -9243,6 +9244,10 @@ ], "license": "MIT" }, + "node_modules/quickstart-openshift-react-frontend": { + "resolved": "", + "link": true + }, "node_modules/raf": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 12599fd79..024add5e3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -24,6 +24,7 @@ "@vitejs/plugin-react": "^4.0.0", "axios": "^1.6.8", "mui-datatables": "^4.3.0", + "quickstart-openshift-react-frontend": "file:", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router": "^6.12.1", @@ -46,8 +47,8 @@ "cypress-file-upload": "^5.0.8", "cypress-plugin-api": "^2.11.1", "eslint": "^8.57.0", - "eslint-config-prettier": "^9.0.0", "eslint-config-love": "^48.0.0", + "eslint-config-prettier": "^9.0.0", "eslint-import-resolver-alias": "^1.1.2", "eslint-import-resolver-typescript": "^3.5.5", "eslint-plugin-cypress": "^2.13.3", diff --git a/vdyp-buildtools/tests/integration/package-lock.json b/vdyp-buildtools/tests/integration/package-lock.json index 4ff39047c..3993db43d 100644 --- a/vdyp-buildtools/tests/integration/package-lock.json +++ b/vdyp-buildtools/tests/integration/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "axios": "^1.6.8", "dotenv": "^16.3.1", + "integration-tests": "file:", "js-yaml": "^4.1.0", "lodash": "^4.17.21" } @@ -96,6 +97,10 @@ "node": ">= 6" } }, + "node_modules/integration-tests": { + "resolved": "", + "link": true + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", diff --git a/vdyp-buildtools/tests/integration/package.json b/vdyp-buildtools/tests/integration/package.json index 32cd584ef..bbedd0084 100644 --- a/vdyp-buildtools/tests/integration/package.json +++ b/vdyp-buildtools/tests/integration/package.json @@ -9,6 +9,7 @@ "dependencies": { "axios": "^1.6.8", "dotenv": "^16.3.1", + "integration-tests": "file:", "js-yaml": "^4.1.0", "lodash": "^4.17.21" } diff --git a/vdyp-web-ui/README.md b/vdyp-web-ui/README.md index 7fe3e2aaa..30e8211f9 100644 --- a/vdyp-web-ui/README.md +++ b/vdyp-web-ui/README.md @@ -1,2 +1,2 @@ # VDYP -Web based user interface for the Variable Density Yield Projection (VDYP). +Web based user interface for the Variable Density Yield Projection (VDYP)