diff --git a/src/main/java/org/mitre/caasd/commons/PathPair.java b/src/main/java/org/mitre/caasd/commons/PathPair.java index 96028e0..36abbc2 100644 --- a/src/main/java/org/mitre/caasd/commons/PathPair.java +++ b/src/main/java/org/mitre/caasd/commons/PathPair.java @@ -1,8 +1,13 @@ package org.mitre.caasd.commons; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.Base64; + /** * A PathPair combines two VehiclePaths that have the same size. *
@@ -16,17 +21,68 @@ */ public record PathPair(VehiclePath path0, VehiclePath path1) { + private static final Base64.Encoder BASE_64_ENCODER = Base64.getUrlEncoder().withoutPadding(); + public PathPair { requireNonNull(path0); requireNonNull(path1); checkArgument(path0.size() == path1.size()); } + /** + * Create a new PathPair from an array of bytes. + */ + public static PathPair fromBytes(byte[] bytes) { + requireNonNull(bytes); + + int n = bytes.length / 2; + + byte[] frontHalf = Arrays.copyOfRange(bytes, 0, n); + byte[] backHalf = Arrays.copyOfRange(bytes, n, 2 * n); + + VehiclePath path0 = VehiclePath.fromBytes(frontHalf); + VehiclePath path1 = VehiclePath.fromBytes(backHalf); + + return new PathPair(path0, path1); + } + + /** + * Create a new PathPair object. + * + * @param base64Encoding The Base64 safe and URL safe (no padding) encoding of a PathPair's + * byte[] + * + * @return A new PathPair object. + */ + public static PathPair fromBase64Str(String base64Encoding) { + return PathPair.fromBytes(Base64.getUrlDecoder().decode(base64Encoding)); + } + /** @return The number of "locations" in each path (which must be the same). */ public int size() { return path0.size(); } + /** @return This PathPair as a byte[]. */ + public byte[] toBytes() { + byte[] p0Bytes = path0().toBytes(); + byte[] p1Bytes = path1().toBytes(); + + checkState(p0Bytes.length == p1Bytes.length, "byte count mismatch"); + + ByteBuffer buffer = ByteBuffer.allocate(p0Bytes.length + p1Bytes.length); + + buffer.put(p0Bytes); + buffer.put(p1Bytes); + + return buffer.array(); + } + + /** @return The Base64 file and url safe encoding of this PathPair's byte[] . */ + public String toBase64() { + return BASE_64_ENCODER.encodeToString(toBytes()); + } + /** Compute the distance between these two paths (use the full paths) */ public static double distanceBtw(PathPair a, PathPair b) { requireNonNull(a); diff --git a/src/main/java/org/mitre/caasd/commons/VehiclePath.java b/src/main/java/org/mitre/caasd/commons/VehiclePath.java index aee1c83..122d5b4 100644 --- a/src/main/java/org/mitre/caasd/commons/VehiclePath.java +++ b/src/main/java/org/mitre/caasd/commons/VehiclePath.java @@ -66,7 +66,7 @@ public byte[] toBytes() { return buffer.array(); } - /** @return The Base64 file and url safe encoding of this AltitudePath's byte[] . */ + /** @return The Base64 file and url safe encoding of this VehiclePath's byte[] . */ public String toBase64() { return BASE_64_ENCODER.encodeToString(toBytes()); } diff --git a/src/test/java/org/mitre/caasd/commons/PathPairTest.java b/src/test/java/org/mitre/caasd/commons/PathPairTest.java index 6d99765..04bfed1 100644 --- a/src/test/java/org/mitre/caasd/commons/PathPairTest.java +++ b/src/test/java/org/mitre/caasd/commons/PathPairTest.java @@ -4,6 +4,7 @@ import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.is; +import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; class PathPairTest { @@ -60,4 +61,38 @@ void distanceReflectsAltitude() { assertThat(PathPair.distanceBtw(a, b), is(0.0)); assertThat(PathPair.distanceBtw(b, a), is(0.0)); } + + @Test + void toAndFromBytes() { + + // Data to make 2 VehiclePath, i.e. 1 PathPair + LatLong64Path lat_path_1 = LatLong64Path.from(LatLong.of(1.0, 2.0), LatLong.of(3.0, 4.0)); + LatLong64Path lat_path_2 = LatLong64Path.from(LatLong.of(1.0, 2.0), LatLong.of(3.0, 4.0)); + AltitudePath vert_path_1 = AltitudePath.from(Distance.ofFeet(100), Distance.ofFeet(200)); + AltitudePath vert_path_2 = AltitudePath.from(Distance.ofFeet(200), Distance.ofFeet(400)); + VehiclePath vp_1 = new VehiclePath(lat_path_1, vert_path_1); + VehiclePath vp_2 = new VehiclePath(lat_path_2, vert_path_2); + + PathPair path = new PathPair(vp_1, vp_2); + PathPair path2 = PathPair.fromBytes(path.toBytes()); + + assertThat(path, (CoreMatchers.is(path2))); + } + + @Test + void toBase64AndBack() { + + // Data to make 2 VehiclePath, i.e. 1 PathPair + LatLong64Path lat_path_1 = LatLong64Path.from(LatLong.of(1.0, 2.0), LatLong.of(3.0, 4.0)); + LatLong64Path lat_path_2 = LatLong64Path.from(LatLong.of(1.0, 2.0), LatLong.of(3.0, 4.0)); + AltitudePath vert_path_1 = AltitudePath.from(Distance.ofFeet(100), Distance.ofFeet(200)); + AltitudePath vert_path_2 = AltitudePath.from(Distance.ofFeet(200), Distance.ofFeet(400)); + VehiclePath vp_1 = new VehiclePath(lat_path_1, vert_path_1); + VehiclePath vp_2 = new VehiclePath(lat_path_2, vert_path_2); + + PathPair path = new PathPair(vp_1, vp_2); + PathPair path2 = PathPair.fromBase64Str(path.toBase64()); + + assertThat(path, (CoreMatchers.is(path2))); + } }