From 6ae4c7c1a2f4c4021719f658de4ecbe99eb6dc8a Mon Sep 17 00:00:00 2001 From: osana Date: Wed, 10 Oct 2018 22:26:22 -0400 Subject: [PATCH] First attempt at Shifter implementation --- .../main/java/com/mapbox/geojson/Point.java | 28 +++-- .../geojson/gson/CoordinateTypeAdapter.java | 63 +++++++++++ .../mapbox/geojson/gson/PointSerializer.java | 16 ++- .../geojson/shifter/CoordinateShifter.java | 56 +++++++++ .../shifter/CoordinateShifterManager.java | 61 ++++++++++ .../mapbox/geojson/shifter/package-info.java | 4 + .../geojson/gson/PointDeserializerTest.java | 4 +- .../mapbox/geojson/shifter/ShifterTest.java | 107 ++++++++++++++++++ 8 files changed, 322 insertions(+), 17 deletions(-) create mode 100644 services-geojson/src/main/java/com/mapbox/geojson/gson/CoordinateTypeAdapter.java create mode 100644 services-geojson/src/main/java/com/mapbox/geojson/shifter/CoordinateShifter.java create mode 100644 services-geojson/src/main/java/com/mapbox/geojson/shifter/CoordinateShifterManager.java create mode 100644 services-geojson/src/main/java/com/mapbox/geojson/shifter/package-info.java create mode 100644 services-geojson/src/test/java/com/mapbox/geojson/shifter/ShifterTest.java diff --git a/services-geojson/src/main/java/com/mapbox/geojson/Point.java b/services-geojson/src/main/java/com/mapbox/geojson/Point.java index 9750b7ac8..822172226 100644 --- a/services-geojson/src/main/java/com/mapbox/geojson/Point.java +++ b/services-geojson/src/main/java/com/mapbox/geojson/Point.java @@ -12,12 +12,14 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.TypeAdapter; +import com.google.gson.reflect.TypeToken; import com.mapbox.geojson.gson.BoundingBoxDeserializer; import com.mapbox.geojson.gson.BoundingBoxSerializer; +import com.mapbox.geojson.gson.CoordinateTypeAdapter; import com.mapbox.geojson.gson.GeoJsonAdapterFactory; +import com.mapbox.geojson.shifter.CoordinateShifterManager; import java.io.Serializable; -import java.util.Arrays; import java.util.List; /** @@ -72,6 +74,8 @@ public abstract class Point implements CoordinateContainer>, Serial public static Point fromJson(@NonNull String json) { GsonBuilder gson = new GsonBuilder(); gson.registerTypeAdapterFactory(GeoJsonAdapterFactory.create()); + gson.registerTypeAdapter(new TypeToken>(){}.getType(), + new CoordinateTypeAdapter()); gson.registerTypeAdapter(BoundingBox.class, new BoundingBoxDeserializer()); return gson.create().fromJson(json, Point.class); } @@ -92,7 +96,9 @@ public static Point fromJson(@NonNull String json) { public static Point fromLngLat( @FloatRange(from = MIN_LONGITUDE, to = MAX_LONGITUDE) double longitude, @FloatRange(from = MIN_LATITUDE, to = MAX_LATITUDE) double latitude) { - List coordinates = Arrays.asList(longitude, latitude); + + List coordinates = + CoordinateShifterManager.getCoordinateShifter().shiftLonLat(longitude, latitude); return new AutoValue_Point(TYPE, null, coordinates); } @@ -115,7 +121,9 @@ public static Point fromLngLat( @FloatRange(from = MIN_LONGITUDE, to = MAX_LONGITUDE) double longitude, @FloatRange(from = MIN_LATITUDE, to = MAX_LATITUDE) double latitude, @Nullable BoundingBox bbox) { - List coordinates = Arrays.asList(longitude, latitude); + + List coordinates = + CoordinateShifterManager.getCoordinateShifter().shiftLonLat(longitude, latitude); return new AutoValue_Point(TYPE, bbox, coordinates); } @@ -139,9 +147,9 @@ public static Point fromLngLat( @FloatRange(from = MIN_LONGITUDE, to = MAX_LONGITUDE) double longitude, @FloatRange(from = MIN_LATITUDE, to = MAX_LATITUDE) double latitude, double altitude) { - List coordinates = Double.isNaN(altitude) - ? Arrays.asList(longitude, latitude) : - Arrays.asList(longitude, latitude, altitude); + + List coordinates = + CoordinateShifterManager.getCoordinateShifter().shiftLonLatAlt(longitude, latitude, altitude); return new AutoValue_Point(TYPE, null, coordinates); } @@ -166,9 +174,9 @@ public static Point fromLngLat( @FloatRange(from = MIN_LONGITUDE, to = MAX_LONGITUDE) double longitude, @FloatRange(from = MIN_LATITUDE, to = MAX_LATITUDE) double latitude, double altitude, @Nullable BoundingBox bbox) { - List coordinates = Double.isNaN(altitude) - ? Arrays.asList(longitude, latitude) : - Arrays.asList(longitude, latitude, altitude); + + List coordinates = + CoordinateShifterManager.getCoordinateShifter().shiftLonLatAlt(longitude, latitude, altitude); return new AutoValue_Point(TYPE, bbox, coordinates); } @@ -284,6 +292,8 @@ public boolean hasAltitude() { @Override public String toJson() { GsonBuilder gson = new GsonBuilder(); + gson.registerTypeAdapter(new TypeToken>(){}.getType(), + new CoordinateTypeAdapter()); gson.registerTypeAdapter(BoundingBox.class, new BoundingBoxSerializer()); return gson.create().toJson(this); } diff --git a/services-geojson/src/main/java/com/mapbox/geojson/gson/CoordinateTypeAdapter.java b/services-geojson/src/main/java/com/mapbox/geojson/gson/CoordinateTypeAdapter.java new file mode 100644 index 000000000..d755d11a5 --- /dev/null +++ b/services-geojson/src/main/java/com/mapbox/geojson/gson/CoordinateTypeAdapter.java @@ -0,0 +1,63 @@ +package com.mapbox.geojson.gson; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import com.mapbox.geojson.shifter.CoordinateShifterManager; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.ArrayList; +import java.util.List; + +/** + * Adapter to read and write coordinates for Point class. + * + * @since 4.1.0 + */ +public class CoordinateTypeAdapter extends TypeAdapter> { + @Override + public void write(JsonWriter out, List value) throws IOException { + + out.beginArray(); + + // Unshift coordinates + List unshiftedCoordinates = + CoordinateShifterManager.getCoordinateShifter().unshiftPoint(value); + + BigDecimal lon = BigDecimal.valueOf(unshiftedCoordinates.get(0)); + String lonString = lon.setScale(7, RoundingMode.HALF_UP) + .stripTrailingZeros().toPlainString(); + + BigDecimal lat = BigDecimal.valueOf(unshiftedCoordinates.get(1)); + String latString = lat.setScale(7, RoundingMode.HALF_UP) + .stripTrailingZeros().toPlainString(); + + out.value(Double.valueOf(lonString)); + out.value(Double.valueOf(latString)); + + // Includes altitude + if (value.size() > 2) { + out.value(unshiftedCoordinates.get(2)); + } + out.endArray(); + } + + @Override + public List read(JsonReader in) throws IOException { + List coordinates = new ArrayList(); + in.beginArray(); + while (in.hasNext()) { + coordinates.add(in.nextDouble()); + } + in.endArray(); + + if (coordinates.size() > 2) { + return CoordinateShifterManager.getCoordinateShifter() + .shiftLonLatAlt(coordinates.get(0), coordinates.get(1), coordinates.get(2)); + } + return CoordinateShifterManager.getCoordinateShifter() + .shiftLonLat(coordinates.get(0), coordinates.get(1)); + } +} diff --git a/services-geojson/src/main/java/com/mapbox/geojson/gson/PointSerializer.java b/services-geojson/src/main/java/com/mapbox/geojson/gson/PointSerializer.java index 89f5e082b..4c6a8a77d 100644 --- a/services-geojson/src/main/java/com/mapbox/geojson/gson/PointSerializer.java +++ b/services-geojson/src/main/java/com/mapbox/geojson/gson/PointSerializer.java @@ -6,10 +6,12 @@ import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; import com.mapbox.geojson.Point; +import com.mapbox.geojson.shifter.CoordinateShifterManager; import java.lang.reflect.Type; import java.math.BigDecimal; import java.math.RoundingMode; +import java.util.List; /** * Required to handle the special case where the altitude might be a Double.NaN, which isn't a valid @@ -44,20 +46,24 @@ public PointSerializer() { public JsonElement serialize(Point src, Type typeOfSrc, JsonSerializationContext context) { JsonArray rawCoordinates = new JsonArray(); - BigDecimal lat = BigDecimal.valueOf(src.latitude()); - String latString = lat.setScale(7, RoundingMode.HALF_UP) - .stripTrailingZeros().toPlainString(); + // Unshift coordinates + List unshiftedCoordinates = + CoordinateShifterManager.getCoordinateShifter().unshiftPoint(src); - BigDecimal lon = BigDecimal.valueOf(src.longitude()); + BigDecimal lon = BigDecimal.valueOf(unshiftedCoordinates.get(0)); String lonString = lon.setScale(7, RoundingMode.HALF_UP) .stripTrailingZeros().toPlainString(); + BigDecimal lat = BigDecimal.valueOf(unshiftedCoordinates.get(1)); + String latString = lat.setScale(7, RoundingMode.HALF_UP) + .stripTrailingZeros().toPlainString(); + rawCoordinates.add(new JsonPrimitive(Double.valueOf(lonString))); rawCoordinates.add(new JsonPrimitive(Double.valueOf(latString))); // Includes altitude if (src.hasAltitude()) { - rawCoordinates.add(new JsonPrimitive(src.altitude())); + rawCoordinates.add(new JsonPrimitive(unshiftedCoordinates.get(3))); } return rawCoordinates; diff --git a/services-geojson/src/main/java/com/mapbox/geojson/shifter/CoordinateShifter.java b/services-geojson/src/main/java/com/mapbox/geojson/shifter/CoordinateShifter.java new file mode 100644 index 000000000..618649ea1 --- /dev/null +++ b/services-geojson/src/main/java/com/mapbox/geojson/shifter/CoordinateShifter.java @@ -0,0 +1,56 @@ +package com.mapbox.geojson.shifter; + +import com.mapbox.geojson.Point; + +import java.util.List; + +/** + * ShifterManager allows to move all points according to some pluggable algorithm. + * Once set it will be applied to all Point object created through this method. + * + * @since 4.1.0 + */ +public interface CoordinateShifter { + + /** + * Shifted coordinate values according to its algorithm. + * + * @param lon unshifted longitude + * @param lat unshifted latitude + * @return shifted longitude, shifted latitude in the form of List of Double + * @since 4.1.0 + */ + List shiftLonLat(double lon, double lat); + + /** + * Shifted coordinate values according to its algorithm. + * + * @param lon unshifted longitude + * @param lat unshifted latitude + * @param altitude unshifted altitude + * @return shifted longitude, shifted latitude, shifted altitude in the form of List of Double + * @since 4.1.0 + */ + List shiftLonLatAlt(double lon, double lat, double altitude); + + /** + * Unshifted coordinate values according to its algorithm. + * + * @param shiftedPoint shifted point + * @return unshifted longitude, shifted latitude, + * and altitude (if present) in the form of List of Double + * @since 4.1.0 + */ + List unshiftPoint(Point shiftedPoint); + + + /** + * Unshifted coordinate values according to its algorithm. + * + * @param shiftedCoordinates shifted point + * @return unshifted longitude, shifted latitude, + * and altitude (if present) in the form of List of Double + * @since 4.1.0 + */ + List unshiftPoint(List shiftedCoordinates); +} diff --git a/services-geojson/src/main/java/com/mapbox/geojson/shifter/CoordinateShifterManager.java b/services-geojson/src/main/java/com/mapbox/geojson/shifter/CoordinateShifterManager.java new file mode 100644 index 000000000..82bf04b4b --- /dev/null +++ b/services-geojson/src/main/java/com/mapbox/geojson/shifter/CoordinateShifterManager.java @@ -0,0 +1,61 @@ +package com.mapbox.geojson.shifter; + +import com.mapbox.geojson.Point; + +import java.util.Arrays; +import java.util.List; + +/** + * CoordinateShifterManager keeps track of currently set CoordinateShifter. + * + * @since 4.1.0 + */ +public final class CoordinateShifterManager { + + private static final CoordinateShifter DEFAULT = new CoordinateShifter() { + @Override + public List shiftLonLat(double lon, double lat) { + return Arrays.asList(lon, lat); + } + + @Override + public List shiftLonLatAlt(double lon, double lat, double alt) { + return Double.isNaN(alt) + ? Arrays.asList(lon, lat) : + Arrays.asList(lon, lat, alt); + } + + @Override + public List unshiftPoint(Point point) { + return point.coordinates(); + } + + @Override + public List unshiftPoint(List coordinates) { + return coordinates; + } + }; + + private static volatile CoordinateShifter coordinateShifter = DEFAULT; + + /** + * Currently set CoordinateShifterManager. + * + * @return Currently set CoordinateShifterManager + * @since 4.1.0 + */ + public static CoordinateShifter getCoordinateShifter() { + return coordinateShifter; + } + + /** + * Sets CoordinateShifterManager. + * + * @param coordinateShifter CoordinateShifterManager to be set + * @since 4.1.0 + */ + public static void setCoordinateShifter(CoordinateShifter coordinateShifter) { + CoordinateShifterManager.coordinateShifter = + coordinateShifter == null ? DEFAULT : coordinateShifter; + } +} diff --git a/services-geojson/src/main/java/com/mapbox/geojson/shifter/package-info.java b/services-geojson/src/main/java/com/mapbox/geojson/shifter/package-info.java new file mode 100644 index 000000000..448cb5895 --- /dev/null +++ b/services-geojson/src/main/java/com/mapbox/geojson/shifter/package-info.java @@ -0,0 +1,4 @@ +/** + * Contains Utility for univerally applying shiftign algorithm to all Geometry. + */ +package com.mapbox.geojson.shifter; diff --git a/services-geojson/src/test/java/com/mapbox/geojson/gson/PointDeserializerTest.java b/services-geojson/src/test/java/com/mapbox/geojson/gson/PointDeserializerTest.java index c1c1a610a..e1d3d92d8 100644 --- a/services-geojson/src/test/java/com/mapbox/geojson/gson/PointDeserializerTest.java +++ b/services-geojson/src/test/java/com/mapbox/geojson/gson/PointDeserializerTest.java @@ -4,7 +4,6 @@ import com.google.gson.JsonSyntaxException; import com.google.gson.reflect.TypeToken; import com.mapbox.core.TestUtils; -import com.mapbox.core.exceptions.ServicesException; import com.mapbox.geojson.Point; import org.junit.Rule; @@ -14,10 +13,9 @@ import java.lang.reflect.Type; import java.util.List; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.startsWith; import static org.junit.Assert.assertEquals; + public class PointDeserializerTest extends TestUtils { @Rule diff --git a/services-geojson/src/test/java/com/mapbox/geojson/shifter/ShifterTest.java b/services-geojson/src/test/java/com/mapbox/geojson/shifter/ShifterTest.java new file mode 100644 index 000000000..989740c7c --- /dev/null +++ b/services-geojson/src/test/java/com/mapbox/geojson/shifter/ShifterTest.java @@ -0,0 +1,107 @@ +package com.mapbox.geojson.shifter; + +import com.mapbox.geojson.BoundingBox; +import com.mapbox.geojson.Point; + +import static com.mapbox.core.TestUtils.DELTA; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.List; + +public class ShifterTest { + + static class TestCoordinateShifter implements CoordinateShifter { + @Override + public List shiftLonLat(double lon, double lat) { + return Arrays.asList(lon + 3, lat + 5); + } + + @Override + public List shiftLonLatAlt(double lon, double lat, double altitude) { + return Arrays.asList(lon + 3, lat + 5, altitude + 8); + } + + @Override + public List unshiftPoint(Point shiftedPoint) { + return Arrays.asList(shiftedPoint.longitude() - 3, + shiftedPoint.latitude() - 5, + shiftedPoint.altitude() - 8); + } + + @Override + public List unshiftPoint(List coordinates) { + if (coordinates.size() > 2) { + return Arrays.asList(coordinates.get(0) - 3, + coordinates.get(1) - 5, + coordinates.get(2) - 8); + } + return Arrays.asList(coordinates.get(0) - 3, + coordinates.get(1) - 5); + } + }; + + @Test + public void basic_shift() throws Exception { + + Point southwest = Point.fromLngLat(2.0, 2.0); + Point northeast = Point.fromLngLat(4.0, 4.0); + + CoordinateShifter shifter = new TestCoordinateShifter(); + + // Manually shifted + List shifted = shifter.shiftLonLat(southwest.longitude(), southwest.latitude()); + Point southwestManualShifted = Point.fromLngLat(shifted.get(0), shifted.get(1)); + shifted = shifter.shiftLonLat(northeast.longitude(), northeast.latitude()); + Point northeastManualShifted = Point.fromLngLat(shifted.get(0), shifted.get(1)); + + CoordinateShifterManager.setCoordinateShifter(shifter); + + // Autoshifted + Point southwestShifted = Point.fromLngLat(southwest.longitude(), southwest.latitude()); + Point northeastShifted = Point.fromLngLat(northeast.longitude(), northeast.latitude()); + + assertEquals(southwestManualShifted, southwestShifted); + assertEquals(northeastManualShifted, northeastShifted); + + BoundingBox boundingBoxFromDouble = BoundingBox.fromLngLats(2.0, 2.0, 4.0, 4.0); + BoundingBox boundingBoxFromPoints = BoundingBox.fromPoints(southwestShifted, northeastShifted); + + assertEquals(boundingBoxFromDouble, boundingBoxFromPoints); + + CoordinateShifterManager.setCoordinateShifter(null); + } + + @Test + public void toJson() throws Exception { + + // set shifter + CoordinateShifterManager.setCoordinateShifter(new TestCoordinateShifter()); + Point point1 = Point.fromLngLat(2.0, 3.0); + String point1JsonString = point1.toJson(); + Point point2 = Point.fromJson(point1JsonString); + + assertEquals(point1, point2); + + CoordinateShifterManager.setCoordinateShifter(null); + } + + @Test + public void fromJson() throws Exception { + + // set shifter + CoordinateShifterManager.setCoordinateShifter(new TestCoordinateShifter()); + + Point point1 = Point.fromLngLat(1.0, 2.0); + String jsonString = "{\"type\":\"Point\",\"coordinates\":[1.0, 2.0]}"; + Point point2 = Point.fromJson(jsonString); + + assertEquals(point1, point2); + + CoordinateShifterManager.setCoordinateShifter(null); + } +}