Skip to content

Commit

Permalink
Represent cell size units as a Quantity
Browse files Browse the repository at this point in the history
This fixes a bug where the UI indicated that the cell size units were always in meters but in actuality the cell size units would correspond to the target CRS specified in the resampling parameters/GDAL warp.

Add a combo box selection for cell size units in the importer UI to reduce confusion about which cell size units are being applied.
  • Loading branch information
Tom Brauer committed Jun 27, 2024
1 parent 27770dc commit 44657ed
Show file tree
Hide file tree
Showing 12 changed files with 177 additions and 72 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package mil.army.usace.hec.vortex.geo;

import javax.measure.Unit;
import javax.measure.quantity.Length;

import static systems.uom.common.USCustomary.FOOT;
import static tech.units.indriya.unit.Units.METRE;

public enum CellSizeUnits {
METERS("Meters", METRE),
FEET("Feet", FOOT);

private final String displayString;
private final Unit<Length> units;

CellSizeUnits(String displayString, Unit<Length> units) {
this.displayString = displayString;
this.units = units;
}

public static CellSizeUnits of(String displayString) {
for (CellSizeUnits value : values()) {
if (value.displayString.equals(displayString)) {
return value;
}
}
throw new IllegalArgumentException("display string not recognized");
}

public static String[] getDisplayStrings() {
CellSizeUnits[] values = values();
String[] displayStrings = new String[values.length];
for (int i = 0; i < values.length; i++) {
displayStrings[i] = values[i].displayString;
}
return displayStrings;
}

public Unit<Length> getUnits() {
return units;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,25 @@

import mil.army.usace.hec.vortex.VortexGrid;
import org.locationtech.jts.geom.Envelope;
import tech.units.indriya.quantity.Quantities;

import javax.measure.Quantity;
import javax.measure.Unit;
import javax.measure.quantity.Length;
import java.io.File;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static tech.units.indriya.unit.Units.METRE;

public class GeographicProcessor {
private final boolean isEmpty;
private final Envelope env;
private final String envWkt;
private final String destWkt;
private final double cellSize;
private final Quantity<Length> cellSize;
private final ResamplingMethod resamplingMethod;

public GeographicProcessor(Map<String, String> geoOptions) {
Expand Down Expand Up @@ -51,9 +57,16 @@ public GeographicProcessor(Map<String, String> geoOptions) {
}

if (geoOptions.containsKey("targetCellSize")) {
cellSize = Double.parseDouble(geoOptions.get("targetCellSize"));
String cellSizeString = geoOptions.get("targetCellSize");
double cellSizeValue = Double.parseDouble(cellSizeString);

// Cell size units defaults to meters
String unitsString = geoOptions.get("targetCellSizeUnits");
Unit<Length> cellSizeUnits = unitsString != null ? CellSizeUnits.of(unitsString).getUnits() : METRE;

cellSize = Quantities.getQuantity(cellSizeValue, cellSizeUnits);
} else {
cellSize = Double.NaN;
cellSize = null;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,15 @@
import mil.army.usace.hec.vortex.VortexGrid;
import org.gdal.osr.SpatialReference;

import javax.measure.Unit;
import java.util.Objects;
import java.util.logging.Logger;

import static javax.measure.MetricPrefix.KILO;
import static systems.uom.common.USCustomary.DEGREE_ANGLE;
import static systems.uom.common.USCustomary.FOOT;
import static tech.units.indriya.unit.Units.METRE;

public class ReferenceUtils {
private static final Logger logger = Logger.getLogger(ReferenceUtils.class.getName());

Expand All @@ -18,17 +24,28 @@ public class ReferenceUtils {

private ReferenceUtils(){}

public static String getMapUnits(String crs) {
SpatialReference srs = new SpatialReference();
public static Unit<?> getLinearUnits(String wkt) {
if (wkt == null)
throw new IllegalArgumentException("wkt must not be null");

srs.ImportFromWkt(crs);
srs.MorphFromESRI();
SpatialReference srs = new SpatialReference(wkt);
try {
srs.MorphFromESRI();

String linearUnitsName = srs.GetLinearUnitsName();
if (srs.IsGeographic() == 1)
return DEGREE_ANGLE;

srs.delete();
String linearUnitsName = srs.GetLinearUnitsName().toLowerCase();

return linearUnitsName;
return switch (linearUnitsName) {
case "m", "meter", "meters", "metre" -> METRE;
case "km" -> KILO(METRE);
case "ft", "feet", "foot", "us foot", "us survey foot" -> FOOT;
default -> throw new IllegalArgumentException("Linear Units not recognized");
};
} finally {
srs.delete();
}
}

public static int getUlyDirection(String wkt, double llx, double lly){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@
import org.gdal.osr.SpatialReference;
import org.locationtech.jts.geom.Envelope;

import javax.measure.IncommensurableException;
import javax.measure.Quantity;
import javax.measure.Unit;
import javax.measure.UnitConverter;
import javax.measure.quantity.Length;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

import static org.gdal.gdalconst.gdalconstConstants.GDT_Float32;
Expand All @@ -19,7 +25,7 @@ public class Resampler {
private final Envelope env;
private final String envWkt;
private final String targetWkt;
private final Double cellSize;
private final Quantity<Length> cellSize;
private final ResamplingMethod method;

private static final Map<EnvelopeReprojection, Envelope> envelopeReprojections = new HashMap<>();
Expand All @@ -38,7 +44,7 @@ public static class ResamplerBuilder{
private Envelope env;
private String envWkt;
private String targetWkt;
private double cellSize = Double.NaN;
private Quantity<Length> cellSize;
private ResamplingMethod method = ResamplingMethod.BILINEAR;

public ResamplerBuilder grid(VortexGrid grid){
Expand All @@ -61,7 +67,7 @@ public ResamplerBuilder targetWkt(String wkt){
return this;
}

public ResamplerBuilder cellSize (Double cellSize){
public ResamplerBuilder cellSize (Quantity<Length> cellSize){
this.cellSize = cellSize;
return this;
}
Expand All @@ -80,14 +86,7 @@ public Resampler build(){
if(grid == null){
throw new IllegalArgumentException("Resampler requires input grid");
}
if (Double.isNaN(cellSize)) {
// If cell size is the same between dx and dy, as is the case with DSS, set cell size
double dx = Math.abs(grid.dx());
double dy = Math.abs(grid.dy());
if (dx == dy) {
cellSize = grid.dx();
}
}

if (envWkt == null) {
// If envelope WKT is not provided, assume it is the same as the grid WKT
envWkt = grid.wkt();
Expand Down Expand Up @@ -115,7 +114,9 @@ public VortexGrid resample(){
destSrs.ImportFromWkt(dataset.GetProjection());
}

Dataset resampled = resample(dataset, env, envSrs, destSrs, cellSize, method);
double targetCellSize = getTargetCellSize();

Dataset resampled = resample(dataset, env, envSrs, destSrs, targetCellSize, method);

double[] geoTransform = resampled.GetGeoTransform();
double dx = geoTransform[1];
Expand Down Expand Up @@ -246,4 +247,29 @@ private static double getNoDataValue(Dataset dataset) {
return value[0];
}

private double getTargetCellSize() {
// If no cell size is specified but dx/dy and linear units are the same, preserve cell size
if (cellSize == null && grid.dx() == grid.dy()
&& ReferenceUtils.getLinearUnits(grid.wkt()).equals(ReferenceUtils.getLinearUnits(targetWkt)))
return grid.dx();

if (cellSize == null)
return Double.NaN;

Unit<?> targetUnit = ReferenceUtils.getLinearUnits(targetWkt);

Unit<Length> cellSizeUnit = cellSize.getUnit();

if (!cellSizeUnit.isCompatible(targetUnit))
return Double.NaN;

try {
UnitConverter converter = cellSizeUnit.getConverterToAny(targetUnit);
double cellSizeValue = cellSize.getValue().doubleValue();
return converter.convert(cellSizeValue);
} catch (IncommensurableException e) {
logger.log(Level.SEVERE, e, e::getMessage);
return Double.NaN;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ public void process() {
.grid(grid)
.envelope(envelope)
.envelopeWkt(envelopeWkt)
.cellSize(Double.NaN)
.method(resamplingMethod)
.build();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -633,40 +633,20 @@ private static Grid getGrid(GridCoordSystem coordinateSystem) {

String xAxisUnits = Objects.requireNonNull(xAxis).getUnitsString();

Unit<?> cellUnits;
switch (xAxisUnits.toLowerCase()) {
case "m":
case "meter":
case "metre":
cellUnits = METRE;
break;
case "km":
cellUnits = KILO(METRE);
break;
case "degrees":
case "degrees_east":
case "degrees_north":
cellUnits = DEGREE_ANGLE;
break;
default:
cellUnits = ONE;
}
Unit<?> cellUnits = switch (xAxisUnits.toLowerCase()) {
case "m", "meter", "metre" -> METRE;
case "km" -> KILO(METRE);
case "degrees", "degrees_east", "degrees_north" -> DEGREE_ANGLE;
default -> ONE;
};

Unit<?> csUnits;
switch (ReferenceUtils.getMapUnits(wkt).toLowerCase()) {
case "m":
case "meter":
case "metre":
csUnits = METRE;
break;
case "km":
csUnits = KILO(METRE);
break;
default:
csUnits = ONE;
}
Unit<?> csUnits = ReferenceUtils.getLinearUnits(wkt);

if (cellUnits.isCompatible(csUnits) && !cellUnits.equals(csUnits)) {
// This will scale the grid if cellUnits and csUnits do not align
// e.g. cellUnits are in meters but csUnits are in kilometers
// isCompatible is simply checking if the units are of type length so that no scaling is attempted
// between DEGREE_ANGLE and ONE
if (cellUnits.isCompatible(METRE) && csUnits.isCompatible(METRE) && !cellUnits.equals(csUnits)) {
grid.set(scaleGrid(grid.get(), cellUnits, csUnits));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,11 +317,7 @@ private void processCellInfo() {
ulx = ulx - 360;
}

Unit<?> csUnits = switch (ReferenceUtils.getMapUnits(wkt).toLowerCase()) {
case "m", "meter", "metre" -> Units.METRE;
case "km" -> KILO(Units.METRE);
default -> AbstractUnit.ONE;
};
Unit<?> csUnits = ReferenceUtils.getLinearUnits(wkt);

if (cellUnits.isCompatible(csUnits)) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,6 @@ private VortexGrid getRaster() {
.envelope(envelope)
.envelopeWkt(input.wkt())
.targetWkt(input.wkt())
.cellSize(input.dx())
.method(resamplingMethod)
.build()
.resample();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.locationtech.jts.geom.Envelope;
import tech.units.indriya.quantity.Quantities;

import javax.measure.Quantity;
import javax.measure.quantity.Length;
import java.io.File;
import java.io.IOException;
import java.net.URL;
Expand All @@ -32,6 +35,7 @@

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static tech.units.indriya.unit.Units.METRE;

class NetcdfDataReaderTest {

Expand Down Expand Up @@ -391,12 +395,14 @@ void cordex() {

String wkt = WktFactory.create("UTM17N");

Quantity<Length> cellSize = Quantities.getQuantity(2000, METRE);

VortexGrid resampled = Resampler.builder()
.grid(grid)
.envelope(envelope)
.envelopeWkt(wkt)
.targetWkt(WktFactory.create("UTM17N"))
.cellSize(2000.0)
.cellSize(cellSize)
.method("Bilinear")
.build()
.resample();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package mil.army.usace.hec.vortex.ui;

import mil.army.usace.hec.vortex.geo.CellSizeUnits;

import javax.swing.*;

public class CellSizeUnitsComboBox extends JComboBox<String> {

public CellSizeUnitsComboBox() {
setModel(new DefaultComboBoxModel<>(CellSizeUnits.getDisplayStrings()));
}
}
Loading

0 comments on commit 44657ed

Please sign in to comment.