object)
throws IOException {
if (object == null) {
diff --git a/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPoints.java b/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPoints.java
index 5cd842b28..25972599b 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPoints.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPoints.java
@@ -90,7 +90,7 @@ public double[] getFlattenLatLngArray() {
/**
* @return an array of all the altitudes (or null if no altitudes are present at all). If a
- * coordinate does not contain altitude it's represented as {@link Double#NaN}
+ * coordinate does not contain altitude it's represented as {@link Double#NaN}
*/
@Nullable
public double[] getAltitudes() {
@@ -100,7 +100,8 @@ public double[] getAltitudes() {
/**
* Creates a list of {@link Point}s and returns it.
*
- * If possible consider using {@link #getFlattenLatLngArray()} and {@link #getAltitudes()} instead.
+ * If possible consider using {@link #getFlattenLatLngArray()} and {@link #getAltitudes()}
+ * instead.
*
* @return a list of {@link Point}s
*/
@@ -113,7 +114,11 @@ public List points() {
for (int i = 0; i < flattenLatLngPoints.length / 2; i++) {
double[] coordinates;
if (altitudes != null && !Double.isNaN(altitudes[i])) {
- coordinates = new double[]{flattenLatLngPoints[i * 2], flattenLatLngPoints[(i * 2) + 1], altitudes[i]};
+ coordinates = new double[]{
+ flattenLatLngPoints[i * 2],
+ flattenLatLngPoints[(i * 2) + 1],
+ altitudes[i]
+ };
} else {
coordinates = new double[]{flattenLatLngPoints[i * 2], flattenLatLngPoints[(i * 2) + 1]};
}
@@ -131,13 +136,21 @@ public List points() {
@Override
public boolean equals(Object o) {
- if (!(o instanceof FlattenListOfPoints)) return false;
+ if (!(o instanceof FlattenListOfPoints)) {
+ return false;
+ }
FlattenListOfPoints that = (FlattenListOfPoints) o;
- return Objects.deepEquals(flattenLatLngPoints, that.flattenLatLngPoints) && Objects.deepEquals(altitudes, that.altitudes) && Objects.deepEquals(boundingBoxes, that.boundingBoxes);
+ return Objects.deepEquals(flattenLatLngPoints, that.flattenLatLngPoints)
+ && Objects.deepEquals(altitudes, that.altitudes)
+ && Objects.deepEquals(boundingBoxes, that.boundingBoxes);
}
@Override
public int hashCode() {
- return Objects.hash(Arrays.hashCode(flattenLatLngPoints), Arrays.hashCode(altitudes), Arrays.hashCode(boundingBoxes));
+ return Objects.hash(
+ Arrays.hashCode(flattenLatLngPoints),
+ Arrays.hashCode(altitudes),
+ Arrays.hashCode(boundingBoxes)
+ );
}
}
diff --git a/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPointsTypeAdapter.java b/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPointsTypeAdapter.java
index e22d898ed..6a7a5cb12 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPointsTypeAdapter.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPointsTypeAdapter.java
@@ -34,9 +34,16 @@ public void write(JsonWriter out, FlattenListOfPoints flattenListOfPoints) throw
for (int i = 0; i < flattenLatLngCoordinates.length / 2; i++) {
double[] value;
if (altitudes != null && !Double.isNaN(altitudes[i])) {
- value = new double[]{flattenLatLngCoordinates[i * 2], flattenLatLngCoordinates[(i * 2) + 1], altitudes[i]};
+ value = new double[]{
+ flattenLatLngCoordinates[i * 2],
+ flattenLatLngCoordinates[(i * 2) + 1],
+ altitudes[i]
+ };
} else {
- value = new double[]{flattenLatLngCoordinates[i * 2], flattenLatLngCoordinates[(i * 2) + 1]};
+ value = new double[]{
+ flattenLatLngCoordinates[i * 2],
+ flattenLatLngCoordinates[(i * 2) + 1]
+ };
}
writePointList(out, value);
diff --git a/services-geojson/src/main/java/com/mapbox/geojson/LineString.java b/services-geojson/src/main/java/com/mapbox/geojson/LineString.java
index 57d278ed7..c6415e3cc 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/LineString.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/LineString.java
@@ -50,7 +50,8 @@
* @since 1.0.0
*/
@Keep
-public final class LineString implements FlattenedCoordinateContainer, FlattenListOfPoints> {
+public final class LineString implements
+ FlattenedCoordinateContainer, FlattenListOfPoints> {
private static final String TYPE = "LineString";
@@ -278,9 +279,13 @@ public String toString() {
@Override
public boolean equals(Object o) {
- if (!(o instanceof LineString)) return false;
+ if (!(o instanceof LineString)) {
+ return false;
+ }
LineString that = (LineString) o;
- return Objects.equals(type, that.type) && Objects.equals(bbox, that.bbox) && Objects.equals(flattenListOfPoints, that.flattenListOfPoints);
+ return Objects.equals(type, that.type)
+ && Objects.equals(bbox, that.bbox)
+ && Objects.equals(flattenListOfPoints, that.flattenListOfPoints);
}
@Override
@@ -298,7 +303,8 @@ public FlattenListOfPoints flattenCoordinates() {
*
* @since 4.6.0
*/
- static final class GsonTypeAdapter extends BaseGeometryTypeAdapter, FlattenListOfPoints> {
+ static final class GsonTypeAdapter extends
+ BaseGeometryTypeAdapter, FlattenListOfPoints> {
GsonTypeAdapter(Gson gson) {
super(gson, new FlattenListOfPointsTypeAdapter());
@@ -315,9 +321,10 @@ public LineString read(JsonReader jsonReader) throws IOException {
}
@Override
- CoordinateContainer> createCoordinateContainer(String type,
- BoundingBox bbox,
- FlattenListOfPoints flattenListOfPoints) {
+ CoordinateContainer> createCoordinateContainer(
+ String type,
+ BoundingBox bbox,
+ FlattenListOfPoints flattenListOfPoints) {
return new LineString(type == null ? "LineString" : type, bbox, flattenListOfPoints);
}
}
diff --git a/services-geojson/src/main/java/com/mapbox/geojson/MultiPoint.java b/services-geojson/src/main/java/com/mapbox/geojson/MultiPoint.java
index c71b9310e..bfa3a7caa 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/MultiPoint.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/MultiPoint.java
@@ -36,7 +36,8 @@
* @since 1.0.0
*/
@Keep
-public final class MultiPoint implements FlattenedCoordinateContainer, FlattenListOfPoints> {
+public final class MultiPoint implements
+ FlattenedCoordinateContainer, FlattenListOfPoints> {
private static final String TYPE = "MultiPoint";
@@ -107,7 +108,8 @@ static MultiPoint fromLngLats(@NonNull double[][] coordinates) {
MultiPoint(String type, @Nullable BoundingBox bbox, List coordinates) {
this(type, bbox, new FlattenListOfPoints(coordinates));
}
- MultiPoint(String type, @Nullable BoundingBox bbox, FlattenListOfPoints flattenListOfPoints) {
+
+ MultiPoint(String type, @Nullable BoundingBox bbox, FlattenListOfPoints flattenListOfPoints) {
if (type == null) {
throw new NullPointerException("Null type");
}
@@ -197,9 +199,13 @@ public String toString() {
@Override
public boolean equals(Object o) {
- if (!(o instanceof MultiPoint)) return false;
+ if (!(o instanceof MultiPoint)) {
+ return false;
+ }
MultiPoint that = (MultiPoint) o;
- return Objects.equals(type, that.type) && Objects.equals(bbox, that.bbox) && Objects.equals(flattenListOfPoints, that.flattenListOfPoints);
+ return Objects.equals(type, that.type)
+ && Objects.equals(bbox, that.bbox)
+ && Objects.equals(flattenListOfPoints, that.flattenListOfPoints);
}
@Override
@@ -217,7 +223,8 @@ public FlattenListOfPoints flattenCoordinates() {
*
* @since 4.6.0
*/
- static final class GsonTypeAdapter extends BaseGeometryTypeAdapter, FlattenListOfPoints> {
+ static final class GsonTypeAdapter extends
+ BaseGeometryTypeAdapter, FlattenListOfPoints> {
GsonTypeAdapter(Gson gson) {
super(gson, new FlattenListOfPointsTypeAdapter());
@@ -234,9 +241,10 @@ public MultiPoint read(JsonReader jsonReader) throws IOException {
}
@Override
- CoordinateContainer> createCoordinateContainer(String type,
- BoundingBox bbox,
- FlattenListOfPoints flattenListOfPoints) {
+ CoordinateContainer> createCoordinateContainer(
+ String type,
+ BoundingBox bbox,
+ FlattenListOfPoints flattenListOfPoints) {
return new MultiPoint(type == null ? "MultiPoint" : type, bbox, flattenListOfPoints);
}
}
diff --git a/services-geojson/src/main/java/com/mapbox/geojson/MultiPolygon.java b/services-geojson/src/main/java/com/mapbox/geojson/MultiPolygon.java
index e9f6ef053..0688a7be1 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/MultiPolygon.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/MultiPolygon.java
@@ -344,8 +344,8 @@ public int hashCode() {
*
* @since 4.6.0
*/
- static final class GsonTypeAdapter
- extends BaseGeometryTypeAdapter>>, List>>> {
+ static final class GsonTypeAdapter extends
+ BaseGeometryTypeAdapter>>, List>>> {
GsonTypeAdapter(Gson gson) {
super(gson, new ListofListofListOfPointCoordinatesTypeAdapter());
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 38a7f890f..24a57c78f 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/Point.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/Point.java
@@ -331,10 +331,15 @@ public static TypeAdapter typeAdapter(Gson gson) {
@Override
public String toString() {
String coordinatesStr;
- if (coordinates.length > 2)
- coordinatesStr = "[" + this.coordinates[0] + ", " + this.coordinates[1] + ", " + this.coordinates[2] + "]";
- else
+ if (coordinates.length > 2) {
+ coordinatesStr = "["
+ + this.coordinates[0] + ", "
+ + this.coordinates[1] + ", "
+ + this.coordinates[2]
+ + "]";
+ } else {
coordinatesStr = "[" + this.coordinates[0] + ", " + this.coordinates[1] + "]";
+ }
return "Point{"
+ "type=" + type + ", "
+ "bbox=" + bbox + ", "
@@ -344,9 +349,13 @@ public String toString() {
@Override
public boolean equals(Object o) {
- if (!(o instanceof Point)) return false;
+ if (!(o instanceof Point)) {
+ return false;
+ }
Point point = (Point) o;
- return Objects.equals(type, point.type) && Objects.equals(bbox, point.bbox) && Objects.deepEquals(coordinates, point.coordinates);
+ return Objects.equals(type, point.type)
+ && Objects.equals(bbox, point.bbox)
+ && Objects.deepEquals(coordinates, point.coordinates);
}
@Override
@@ -366,7 +375,8 @@ public int hashCode() {
*
* @since 4.6.0
*/
- static final class GsonTypeAdapter extends BaseGeometryTypeAdapter, double[]> {
+ static final class GsonTypeAdapter extends
+ BaseGeometryTypeAdapter, double[]> {
GsonTypeAdapter(Gson gson) {
super(gson, new ListOfDoublesCoordinatesTypeAdapter());
diff --git a/services-geojson/src/main/java/com/mapbox/geojson/Polygon.java b/services-geojson/src/main/java/com/mapbox/geojson/Polygon.java
index df9d02974..e8970c064 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/Polygon.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/Polygon.java
@@ -432,7 +432,8 @@ public int hashCode() {
*
* @since 4.6.0
*/
- static final class GsonTypeAdapter extends BaseGeometryTypeAdapter>, List>> {
+ static final class GsonTypeAdapter extends
+ BaseGeometryTypeAdapter>, List>> {
GsonTypeAdapter(Gson gson) {
super(gson, new ListOfListOfPointCoordinatesTypeAdapter());
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
index 75d7541bf..fc3571d84 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/shifter/CoordinateShifter.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/shifter/CoordinateShifter.java
@@ -55,7 +55,30 @@ public interface CoordinateShifter {
*/
List unshiftPoint(List shiftedCoordinates);
+ /**
+ * 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
+ */
double[] shift(double lon, double lat, double altitude);
+
+ /**
+ * Shifted coordinate values according to its algorithm.
+ *
+ * @param lon unshifted longitude
+ * @param lat unshifted latitude
+ * @return shifted longitude, shifted latitude
+ */
double[] shift(double lon, double lat);
+
+ /**
+ * Unshifted coordinate values according to its algorithm.
+ *
+ * @param shiftedCoordinates shifted point
+ * @return unshifted longitude, shifted latitude, and altitude (if present)
+ */
double[] unshiftPointArray(double[] 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
index 40ac56e0d..b5cc12d44 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/shifter/CoordinateShifterManager.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/shifter/CoordinateShifterManager.java
@@ -42,7 +42,7 @@ public double[] shift(double lon, double lat) {
@Override
public double[] shift(double lon, double lat, double altitude) {
- if (Double.isNaN(altitude)){
+ if (Double.isNaN(altitude)) {
return shift(lon, lat);
} else {
return new double[]{lon, lat, altitude};
From cfa95b9731e6f420da241c9986c1567348ff3de2 Mon Sep 17 00:00:00 2001
From: Ramon
Date: Mon, 26 Jan 2026 15:10:21 +0200
Subject: [PATCH 12/31] Better names when reading point from JSON
---
.../geojson/BaseCoordinatesTypeAdapter.java | 18 ++++++++----------
1 file changed, 8 insertions(+), 10 deletions(-)
diff --git a/services-geojson/src/main/java/com/mapbox/geojson/BaseCoordinatesTypeAdapter.java b/services-geojson/src/main/java/com/mapbox/geojson/BaseCoordinatesTypeAdapter.java
index fc9c9ecb6..38c165f2e 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/BaseCoordinatesTypeAdapter.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/BaseCoordinatesTypeAdapter.java
@@ -63,33 +63,31 @@ protected double[] readPointList(JsonReader in) throws IOException {
throw new NullPointerException();
}
- double coordinate0;
- double coordinate1;
- double coordinate2;
+ double lon;
+ double lat;
+ double altitude;
in.beginArray();
if (in.hasNext()) {
- coordinate0 = in.nextDouble();
+ lon = in.nextDouble();
} else {
throw new IndexOutOfBoundsException("Point coordinates should contain at least two values");
}
if (in.hasNext()) {
- coordinate1 = in.nextDouble();
+ lat = in.nextDouble();
} else {
throw new IndexOutOfBoundsException("Point coordinates should contain at least two values");
}
if (in.hasNext()) {
- coordinate2 = in.nextDouble();
+ altitude = in.nextDouble();
// Consume any extra value but don't store it
while (in.hasNext()) {
in.skipValue();
}
in.endArray();
- return CoordinateShifterManager.getCoordinateShifter()
- .shift(coordinate0, coordinate1, coordinate2);
+ return CoordinateShifterManager.getCoordinateShifter().shift(lon, lat, altitude);
} else {
in.endArray();
- return CoordinateShifterManager.getCoordinateShifter()
- .shift(coordinate0, coordinate1);
+ return CoordinateShifterManager.getCoordinateShifter().shift(lon, lat);
}
}
From 8db98e82ced842b2f032c56572081eb9779050db Mon Sep 17 00:00:00 2001
From: Ramon
Date: Mon, 26 Jan 2026 15:11:21 +0200
Subject: [PATCH 13/31] Fix order of lng,lat in various names and improved
tests
---
.../mapbox/geojson/FlattenListOfPoints.java | 46 +++++++++++--------
.../main/java/com/mapbox/geojson/Point.java | 4 +-
.../com/mapbox/geojson/LineStringTest.java | 4 +-
.../com/mapbox/geojson/MultiPointTest.java | 16 ++++---
.../java/com/mapbox/geojson/PointTest.java | 25 ++++++++++
5 files changed, 64 insertions(+), 31 deletions(-)
diff --git a/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPoints.java b/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPoints.java
index 25972599b..2a27d9fe9 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPoints.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPoints.java
@@ -16,14 +16,14 @@
@Keep
public class FlattenListOfPoints implements Serializable {
/**
- * A one-dimensional array to store the flattened coordinates: [lat1, lng1, lat2, lng2, ...].
+ * A one-dimensional array to store the flattened coordinates: [lng1, lat1, lng2, lat2, ...].
*
* Note: we use one-dimensional array for performance reasons related to JNI access (
* Android JNI Tips
* - Primitive arrays)
*/
@NonNull
- private final double[] flattenLatLngPoints;
+ private final double[] flattenLngLatPoints;
/**
* An array to store the altitudes of each coordinate or {@link Double#NaN} if the coordinate
* does not have altitude.
@@ -37,19 +37,24 @@ public class FlattenListOfPoints implements Serializable {
@Nullable
private BoundingBox[] boundingBoxes;
- FlattenListOfPoints(List points) {
+ FlattenListOfPoints(@NonNull double[] flattenLngLatPoints, @Nullable double[] altitudes) {
+ this.flattenLngLatPoints = flattenLngLatPoints;
+ this.altitudes = altitudes;
+ }
+
+ FlattenListOfPoints(@NonNull List points) {
if (points.isEmpty()) {
- this.flattenLatLngPoints = new double[0];
+ this.flattenLngLatPoints = new double[0];
this.altitudes = null;
this.boundingBoxes = null;
return;
}
- double[] flattenLatLngCoordinates = new double[points.size() * 2];
+ double[] flattenLngLatCoordinates = new double[points.size() * 2];
double[] altitudes = null;
for (int i = 0; i < points.size(); i++) {
Point point = points.get(i);
- flattenLatLngCoordinates[i * 2] = point.longitude();
- flattenLatLngCoordinates[(i * 2) + 1] = point.latitude();
+ flattenLngLatCoordinates[i * 2] = point.longitude();
+ flattenLngLatCoordinates[(i * 2) + 1] = point.latitude();
// It is quite common to not have altitude in Point. Therefore only if we have points
// with altitude then we create an array to store those.
@@ -76,16 +81,17 @@ public class FlattenListOfPoints implements Serializable {
boundingBoxes[i] = point.bbox();
}
}
- this.flattenLatLngPoints = flattenLatLngCoordinates;
+ this.flattenLngLatPoints = flattenLngLatCoordinates;
this.altitudes = altitudes;
}
/**
- * @return a flatten array of all the coordinates (lat, lng): [lat1, lng1, lat2, lng2, ...].
+ * @return a flatten array of all the coordinates (longitude, latitude):
+ * [lng1, lat1, lng2, lat2, ...].
*/
@NonNull
- public double[] getFlattenLatLngArray() {
- return flattenLatLngPoints;
+ public double[] getFlattenLngLatArray() {
+ return flattenLngLatPoints;
}
/**
@@ -100,27 +106,27 @@ public double[] getAltitudes() {
/**
* Creates a list of {@link Point}s and returns it.
*
- * If possible consider using {@link #getFlattenLatLngArray()} and {@link #getAltitudes()}
+ * If possible consider using {@link #getFlattenLngLatArray()} and {@link #getAltitudes()}
* instead.
*
* @return a list of {@link Point}s
*/
@NonNull
public List points() {
- if (flattenLatLngPoints.length == 0) {
+ if (flattenLngLatPoints.length == 0) {
return new ArrayList<>();
}
- ArrayList points = new ArrayList<>(flattenLatLngPoints.length / 2);
- for (int i = 0; i < flattenLatLngPoints.length / 2; i++) {
+ ArrayList points = new ArrayList<>(flattenLngLatPoints.length / 2);
+ for (int i = 0; i < flattenLngLatPoints.length / 2; i++) {
double[] coordinates;
if (altitudes != null && !Double.isNaN(altitudes[i])) {
coordinates = new double[]{
- flattenLatLngPoints[i * 2],
- flattenLatLngPoints[(i * 2) + 1],
+ flattenLngLatPoints[i * 2],
+ flattenLngLatPoints[(i * 2) + 1],
altitudes[i]
};
} else {
- coordinates = new double[]{flattenLatLngPoints[i * 2], flattenLatLngPoints[(i * 2) + 1]};
+ coordinates = new double[]{flattenLngLatPoints[i * 2], flattenLngLatPoints[(i * 2) + 1]};
}
BoundingBox pointBbox = null;
if (boundingBoxes != null) {
@@ -140,7 +146,7 @@ public boolean equals(Object o) {
return false;
}
FlattenListOfPoints that = (FlattenListOfPoints) o;
- return Objects.deepEquals(flattenLatLngPoints, that.flattenLatLngPoints)
+ return Objects.deepEquals(flattenLngLatPoints, that.flattenLngLatPoints)
&& Objects.deepEquals(altitudes, that.altitudes)
&& Objects.deepEquals(boundingBoxes, that.boundingBoxes);
}
@@ -148,7 +154,7 @@ public boolean equals(Object o) {
@Override
public int hashCode() {
return Objects.hash(
- Arrays.hashCode(flattenLatLngPoints),
+ Arrays.hashCode(flattenLngLatPoints),
Arrays.hashCode(altitudes),
Arrays.hashCode(boundingBoxes)
);
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 24a57c78f..1ee3b9a18 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/Point.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/Point.java
@@ -271,7 +271,7 @@ public BoundingBox bbox() {
}
/**
- * Provide a single double array containing the longitude, latitude, and optionally an
+ * Provide a list of Doubles containing the longitude, latitude, and optionally an
* altitude/elevation. {@link #longitude()}, {@link #latitude()}, and {@link #altitude()} are all
* available which make getting specific coordinates more direct.
*
@@ -292,7 +292,7 @@ public List coordinates() {
/**
* Provide a single double array containing the longitude, latitude, and optionally an
- * altitude/elevation. {@link #longitude()}, {@link #latitude()}, and {@link #altitude()} are all
+ * altitude. {@link #longitude()}, {@link #latitude()}, and {@link #altitude()} are all
* available which make getting specific coordinates more direct.
*
* @return a double array which holds this points coordinates
diff --git a/services-geojson/src/test/java/com/mapbox/geojson/LineStringTest.java b/services-geojson/src/test/java/com/mapbox/geojson/LineStringTest.java
index dad5aa4d3..efbc0298f 100644
--- a/services-geojson/src/test/java/com/mapbox/geojson/LineStringTest.java
+++ b/services-geojson/src/test/java/com/mapbox/geojson/LineStringTest.java
@@ -114,7 +114,7 @@ public void bbox_doesDeserializeWhenPresent() throws Exception {
assertEquals(3, coordinates.get(2).longitude(), DELTA);
assertEquals(4, coordinates.get(2).latitude(), DELTA);
- double[] coordinatesPrimitive = lineString.flattenCoordinates().getFlattenLatLngArray();
+ double[] coordinatesPrimitive = lineString.flattenCoordinates().getFlattenLngLatArray();
assertEquals(1, coordinatesPrimitive[0], DELTA);
assertEquals(2, coordinatesPrimitive[1], DELTA);
assertEquals(2, coordinatesPrimitive[2], DELTA);
@@ -154,7 +154,7 @@ public void fromJson() throws IOException {
assertEquals(1.0, secondPoint.latitude(), 0.0);
assertFalse(secondPoint.hasAltitude());
- double[] coordinates = geo.flattenCoordinates().getFlattenLatLngArray();
+ double[] coordinates = geo.flattenCoordinates().getFlattenLngLatArray();
double[] altitudes = geo.flattenCoordinates().getAltitudes();
assertEquals(100.0, coordinates[0], 0.0);
assertEquals(0.0, coordinates[1], 0.0);
diff --git a/services-geojson/src/test/java/com/mapbox/geojson/MultiPointTest.java b/services-geojson/src/test/java/com/mapbox/geojson/MultiPointTest.java
index 75318cae9..dee5add5b 100644
--- a/services-geojson/src/test/java/com/mapbox/geojson/MultiPointTest.java
+++ b/services-geojson/src/test/java/com/mapbox/geojson/MultiPointTest.java
@@ -94,19 +94,21 @@ public void testSerializable() throws Exception {
@Test
public void fromJson() throws IOException {
final String json = "{ \"type\": \"MultiPoint\"," +
- "\"coordinates\": [ [100, 0, 1000], [101, 1] ] } ";
+ "\"coordinates\": [ [100, 90, 1000], [101, 1] ] } ";
MultiPoint geo = MultiPoint.fromJson(json);
assertEquals("MultiPoint", geo.type());
List coordinates = geo.coordinates();
Point firstPoint = coordinates.get(0);
assertEquals(100.0, firstPoint.longitude(), DELTA);
- assertEquals(0.0, firstPoint.latitude(), DELTA);
+ assertEquals(90.0, firstPoint.latitude(), DELTA);
assertTrue(firstPoint.hasAltitude());
assertEquals(1000.0, firstPoint.altitude(), DELTA);
- double[] flattenLatLngArray = geo.flattenCoordinates().getFlattenLatLngArray();
- assertEquals(100.0, flattenLatLngArray[0], DELTA);
- assertEquals(0.0, flattenLatLngArray[1], DELTA);
+ double[] flattenLngLatArray = geo.flattenCoordinates().getFlattenLngLatArray();
+ assertEquals(100.0, flattenLngLatArray[0], DELTA);
+ assertEquals(firstPoint.longitude(), flattenLngLatArray[0], DELTA);
+ assertEquals(90.0, flattenLngLatArray[1], DELTA);
+ assertEquals(firstPoint.latitude(), flattenLngLatArray[1], DELTA);
Point secondPoint = coordinates.get(1);
@@ -115,8 +117,8 @@ public void fromJson() throws IOException {
assertFalse(secondPoint.hasAltitude());
assertEquals(Double.NaN, secondPoint.altitude(), DELTA);
- assertEquals(101.0, flattenLatLngArray[2], DELTA);
- assertEquals(1.0, flattenLatLngArray[3], DELTA);
+ assertEquals(101.0, flattenLngLatArray[2], DELTA);
+ assertEquals(1.0, flattenLngLatArray[3], DELTA);
}
@Test
diff --git a/services-geojson/src/test/java/com/mapbox/geojson/PointTest.java b/services-geojson/src/test/java/com/mapbox/geojson/PointTest.java
index 1b9335fe7..66de019b1 100644
--- a/services-geojson/src/test/java/com/mapbox/geojson/PointTest.java
+++ b/services-geojson/src/test/java/com/mapbox/geojson/PointTest.java
@@ -25,6 +25,18 @@ public class PointTest extends TestUtils {
public void sanity() throws Exception {
Point point = Point.fromLngLat(1.0, 2.0);
assertNotNull(point);
+ assertEquals("Point", point.type());
+ assertEquals(1.0, point.longitude(), DELTA);
+ assertEquals(2.0, point.latitude(), DELTA);
+ assertEquals(Double.NaN, point.altitude(), DELTA);
+ List coordinates = point.coordinates();
+ assertEquals(2, coordinates.size());
+ assertEquals(1.0, coordinates.get(0), DELTA);
+ assertEquals(2.0, coordinates.get(1), DELTA);
+ double[] doubles = point.flattenCoordinates();
+ assertEquals(2, doubles.length);
+ assertEquals(1.0, doubles[0], DELTA);
+ assertEquals(2.0, doubles[1], DELTA);
}
@Test
@@ -37,6 +49,19 @@ public void hasAltitude_returnsFalseWhenAltitudeNotPresent() throws Exception {
public void hasAltitude_returnsTrueWhenAltitudeIsPresent() throws Exception {
Point point = Point.fromLngLat(1.0, 2.0, 5.0);
assertTrue(point.hasAltitude());
+ assertEquals(1.0, point.longitude(), DELTA);
+ assertEquals(2.0, point.latitude(), DELTA);
+ assertEquals(5.0, point.altitude(), DELTA);
+ List coordinates = point.coordinates();
+ assertEquals(3, coordinates.size());
+ assertEquals(1.0, coordinates.get(0), DELTA);
+ assertEquals(2.0, coordinates.get(1), DELTA);
+ assertEquals(5.0, coordinates.get(2), DELTA);
+ double[] doubles = point.flattenCoordinates();
+ assertEquals(3, doubles.length);
+ assertEquals(1.0, doubles[0], DELTA);
+ assertEquals(2.0, doubles[1], DELTA);
+ assertEquals(5.0, doubles[2], DELTA);
}
@Test
From eb9672100121436982cd8fb56dbb6f692a992fbd Mon Sep 17 00:00:00 2001
From: Ramon
Date: Mon, 26 Jan 2026 15:11:38 +0200
Subject: [PATCH 14/31] Improve reading list of points from JSON
---
.../FlattenListOfPointsTypeAdapter.java | 86 ++++++++++++++++---
1 file changed, 74 insertions(+), 12 deletions(-)
diff --git a/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPointsTypeAdapter.java b/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPointsTypeAdapter.java
index 6a7a5cb12..e12333789 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPointsTypeAdapter.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPointsTypeAdapter.java
@@ -8,8 +8,6 @@
import com.mapbox.geojson.exception.GeoJsonException;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
/**
* Type Adapter to serialize/deserialize List<Point> into/from two dimentional double array.
@@ -19,6 +17,8 @@
@Keep
class FlattenListOfPointsTypeAdapter extends BaseCoordinatesTypeAdapter {
+ private static final int INITIAL_CAPACITY = 100;
+
@Override
public void write(JsonWriter out, FlattenListOfPoints flattenListOfPoints) throws IOException {
@@ -28,21 +28,21 @@ public void write(JsonWriter out, FlattenListOfPoints flattenListOfPoints) throw
}
out.beginArray();
- double[] flattenLatLngCoordinates = flattenListOfPoints.getFlattenLatLngArray();
+ double[] flattenLngLatCoordinates = flattenListOfPoints.getFlattenLngLatArray();
double[] altitudes = flattenListOfPoints.getAltitudes();
- for (int i = 0; i < flattenLatLngCoordinates.length / 2; i++) {
+ for (int i = 0; i < flattenLngLatCoordinates.length / 2; i++) {
double[] value;
if (altitudes != null && !Double.isNaN(altitudes[i])) {
value = new double[]{
- flattenLatLngCoordinates[i * 2],
- flattenLatLngCoordinates[(i * 2) + 1],
+ flattenLngLatCoordinates[i * 2],
+ flattenLngLatCoordinates[(i * 2) + 1],
altitudes[i]
};
} else {
value = new double[]{
- flattenLatLngCoordinates[i * 2],
- flattenLatLngCoordinates[(i * 2) + 1]
+ flattenLngLatCoordinates[i * 2],
+ flattenLngLatCoordinates[(i * 2) + 1]
};
}
@@ -54,21 +54,83 @@ public void write(JsonWriter out, FlattenListOfPoints flattenListOfPoints) throw
@Override
public FlattenListOfPoints read(JsonReader in) throws IOException {
-
if (in.peek() == JsonToken.NULL) {
throw new NullPointerException();
}
if (in.peek() == JsonToken.BEGIN_ARRAY) {
- List points = new ArrayList<>();
in.beginArray();
+ double[] flattenLngLats = new double[INITIAL_CAPACITY * 2];
+ double[] altitudes = null;
+ int currentIdx = 0;
while (in.peek() == JsonToken.BEGIN_ARRAY) {
- points.add(readPoint(in));
+ in.beginArray();
+ // Read longitude
+ if (in.hasNext()) {
+ flattenLngLats[currentIdx * 2] = in.nextDouble();
+ } else {
+ throw new IndexOutOfBoundsException(
+ "Point coordinates should contain at least two values"
+ );
+ }
+
+ // Read latitude
+ if (in.hasNext()) {
+ flattenLngLats[currentIdx * 2 + 1] = in.nextDouble();
+ } else {
+ throw new IndexOutOfBoundsException(
+ "Point coordinates should contain at least two values"
+ );
+ }
+
+ // Finally altitude if present
+ if (in.hasNext()) {
+ if (altitudes == null) {
+ altitudes = new double[flattenLngLats.length / 2];
+ // Fill in any previous altitude as NaN
+ for (int j = 0; j < currentIdx; j++) {
+ altitudes[j] = Double.NaN;
+ }
+ }
+ altitudes[currentIdx] = in.nextDouble();
+ // Consume any extra value but don't store it
+ while (in.hasNext()) {
+ in.skipValue();
+ }
+ in.endArray();
+ } else {
+ in.endArray();
+ if (altitudes != null) {
+ // If we are storing altitudes but this point doesn't have it then set it to NaN
+ altitudes[currentIdx] = Double.NaN;
+ }
+ }
+ currentIdx++;
+ // If we run out of space we grow the the arrays
+ if (currentIdx * 2 >= flattenLngLats.length) {
+ double[] newFlattenLngLats = new double[flattenLngLats.length * 2];
+ System.arraycopy(flattenLngLats, 0, newFlattenLngLats, 0, flattenLngLats.length);
+ flattenLngLats = newFlattenLngLats;
+ if (altitudes != null) {
+ double[] newAltitudes = new double[altitudes.length * 2];
+ System.arraycopy(altitudes, 0, newAltitudes, 0, altitudes.length);
+ altitudes = newAltitudes;
+ }
+ }
}
in.endArray();
- return new FlattenListOfPoints(points);
+ int totalPoints = currentIdx;
+ double[] trimmedFlattenLngLats = new double[totalPoints * 2];
+ System.arraycopy(flattenLngLats, 0, trimmedFlattenLngLats, 0, totalPoints * 2);
+ double[] trimmedAltitudes = null;
+ if (altitudes != null) {
+ trimmedAltitudes = new double[totalPoints];
+ System.arraycopy(altitudes, 0, trimmedAltitudes, 0, totalPoints);
+ }
+
+ return new FlattenListOfPoints(trimmedFlattenLngLats, trimmedAltitudes);
}
throw new GeoJsonException("coordinates should be non-null array of array of double");
From 63fc1577dbe69c1a321c293228515502e4f926b7 Mon Sep 17 00:00:00 2001
From: Ramon
Date: Mon, 26 Jan 2026 15:42:37 +0200
Subject: [PATCH 15/31] Expose LineString constructor with FlattenListOfPoints
---
.../mapbox/geojson/FlattenListOfPoints.java | 10 ++++-
.../java/com/mapbox/geojson/LineString.java | 38 +++++++++++++++++++
.../com/mapbox/geojson/LineStringTest.java | 8 ++++
.../mapbox/geojson/shifter/ShifterTest.java | 9 +++++
4 files changed, 64 insertions(+), 1 deletion(-)
diff --git a/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPoints.java b/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPoints.java
index 2a27d9fe9..aafcb4c98 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPoints.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPoints.java
@@ -37,7 +37,15 @@ public class FlattenListOfPoints implements Serializable {
@Nullable
private BoundingBox[] boundingBoxes;
- FlattenListOfPoints(@NonNull double[] flattenLngLatPoints, @Nullable double[] altitudes) {
+ /**
+ *
+ * @param flattenLngLatPoints A one-dimensional array coordinates: [lng1, lat1, lng2, lat2, ...].
+ * It is stored as is, no copy or shifting is done.
+ * @param altitudes An array of altitudes of each coordinate or {@link Double#NaN} if the
+ * coordinate does not have altitude. It is stored as is, no copy or shifting is
+ * done.
+ */
+ public FlattenListOfPoints(@NonNull double[] flattenLngLatPoints, @Nullable double[] altitudes) {
this.flattenLngLatPoints = flattenLngLatPoints;
this.altitudes = altitudes;
}
diff --git a/services-geojson/src/main/java/com/mapbox/geojson/LineString.java b/services-geojson/src/main/java/com/mapbox/geojson/LineString.java
index c6415e3cc..be480c598 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/LineString.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/LineString.java
@@ -9,6 +9,8 @@
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.mapbox.geojson.gson.GeoJsonAdapterFactory;
+import com.mapbox.geojson.shifter.CoordinateShifter;
+import com.mapbox.geojson.shifter.CoordinateShifterManager;
import com.mapbox.geojson.utils.PolylineUtils;
import java.io.IOException;
@@ -144,6 +146,42 @@ public static LineString fromLngLats(@NonNull MultiPoint multiPoint, @Nullable B
return new LineString(TYPE, bbox, multiPoint.coordinates());
}
+ /**
+ * Create a new instance of this class by defining a {@link FlattenListOfPoints} object.
+ * The multipoint object should comply with the GeoJson specifications described in the
+ * documentation.
+ *
+ * @param flattenListOfPoints which will make up the LineString geometry. The points will be
+ * shifted according to the current
+ * {@link CoordinateShifterManager#getCoordinateShifter()}
+ * @param bbox optionally include a bbox definition
+ * @return a new instance of this class defined by the values passed inside this static factory
+ * method
+ */
+ public static LineString fromFlattenListOfPoints(
+ FlattenListOfPoints flattenListOfPoints,
+ @Nullable BoundingBox bbox
+ ) {
+ double[] flattenLngLatArray = flattenListOfPoints.getFlattenLngLatArray();
+ double[] altitudes = flattenListOfPoints.getAltitudes();
+ CoordinateShifter coordinateShifter = CoordinateShifterManager.getCoordinateShifter();
+ // Iterate all the points and shift them
+ for (int i = 0; i < flattenLngLatArray.length / 2; i++) {
+ if (altitudes != null && !Double.isNaN(altitudes[i])) {
+ double[] shifted = coordinateShifter.shift(flattenLngLatArray[i * 2], flattenLngLatArray[(i * 2) + 1], altitudes[i]);
+ flattenLngLatArray[i * 2] = shifted[0];
+ flattenLngLatArray[(i * 2) + 1] = shifted[1];
+ altitudes[i] = shifted[2];
+ } else {
+ double[] shifted = coordinateShifter.shift(flattenLngLatArray[i * 2], flattenLngLatArray[(i * 2) + 1]);
+ flattenLngLatArray[i * 2] = shifted[0];
+ flattenLngLatArray[(i * 2) + 1] = shifted[1];
+ }
+ }
+
+ return new LineString(TYPE, bbox, flattenListOfPoints);
+ }
+
LineString(String type, @Nullable BoundingBox bbox, List coordinates) {
this(type, bbox, new FlattenListOfPoints(coordinates));
}
diff --git a/services-geojson/src/test/java/com/mapbox/geojson/LineStringTest.java b/services-geojson/src/test/java/com/mapbox/geojson/LineStringTest.java
index efbc0298f..8717509c0 100644
--- a/services-geojson/src/test/java/com/mapbox/geojson/LineStringTest.java
+++ b/services-geojson/src/test/java/com/mapbox/geojson/LineStringTest.java
@@ -44,6 +44,14 @@ public void fromLngLats_generatedFromMultipoint() throws Exception {
assertEquals("_gayB_c`|@_wemJ_kbvD", lineString.toPolyline(PRECISION_6));
}
+ @Test
+ public void fromFlattenListOfPoints() throws Exception {
+ double[] flattenLngLatPoints= new double[]{1.0, 2.0, 4.0, 8.0};
+ FlattenListOfPoints flattenListOfPoints = new FlattenListOfPoints(flattenLngLatPoints, null);
+ LineString lineString = LineString.fromFlattenListOfPoints(flattenListOfPoints, null);
+ assertEquals("_gayB_c`|@_wemJ_kbvD", lineString.toPolyline(PRECISION_6));
+ }
+
@Test
public void bbox_nullWhenNotSet() throws Exception {
List points = new ArrayList<>();
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
index 6475e592f..67353d3cd 100644
--- a/services-geojson/src/test/java/com/mapbox/geojson/shifter/ShifterTest.java
+++ b/services-geojson/src/test/java/com/mapbox/geojson/shifter/ShifterTest.java
@@ -2,6 +2,7 @@
import com.google.gson.JsonParser;
import com.mapbox.geojson.BoundingBox;
+import com.mapbox.geojson.FlattenListOfPoints;
import com.mapbox.geojson.LineString;
import com.mapbox.geojson.Point;
@@ -222,6 +223,14 @@ public void linestring_basic_shift_with_bbox() {
+ "\"type\":\"LineString\",\"bbox\":[1.0,2.0,3.0,4.0]}",
jsonString);
+ double[] flattenLngLatPoints= new double[]{1.0, 1.0, 2.0, 2.0, 3.0, 3.0};
+ FlattenListOfPoints flattenListOfPoints = new FlattenListOfPoints(flattenLngLatPoints, null);
+ LineString lineString2 = LineString.fromFlattenListOfPoints(flattenListOfPoints, bbox);
+ String jsonString2 = lineString2.toJson();
+ compareJson("{\"coordinates\":[[1,1],[2,2],[3,3]],"
+ + "\"type\":\"LineString\",\"bbox\":[1.0,2.0,3.0,4.0]}",
+ jsonString2);
+
CoordinateShifterManager.setCoordinateShifter(null);
}
From 6d564fbe0fc30d092b8fed7a7b63d4316f05edfe Mon Sep 17 00:00:00 2001
From: Ramon
Date: Mon, 26 Jan 2026 16:29:15 +0200
Subject: [PATCH 16/31] Added encode/decode to PolylineUtils
---
.../java/com/mapbox/geojson/LineString.java | 6 +-
.../mapbox/geojson/utils/PolylineUtils.java | 99 +++++++++++++++++++
2 files changed, 103 insertions(+), 2 deletions(-)
diff --git a/services-geojson/src/main/java/com/mapbox/geojson/LineString.java b/services-geojson/src/main/java/com/mapbox/geojson/LineString.java
index be480c598..b7e19e4dd 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/LineString.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/LineString.java
@@ -221,7 +221,8 @@ static LineString fromLngLats(double[][] coordinates) {
* @since 1.0.0
*/
public static LineString fromPolyline(@NonNull String polyline, int precision) {
- return LineString.fromLngLats(PolylineUtils.decode(polyline, precision), null);
+ FlattenListOfPoints points = PolylineUtils.decodeToFlattenListOfPoints(polyline, precision);
+ return LineString.fromFlattenListOfPoints(points, null);
}
/**
@@ -264,6 +265,7 @@ public BoundingBox bbox() {
*/
@NonNull
@Override
+ @Deprecated
public List coordinates() {
return flattenListOfPoints.points();
}
@@ -292,7 +294,7 @@ public String toJson() {
* @since 1.0.0
*/
public String toPolyline(int precision) {
- return PolylineUtils.encode(coordinates(), precision);
+ return PolylineUtils.encode(flattenListOfPoints, precision);
}
/**
diff --git a/services-geojson/src/main/java/com/mapbox/geojson/utils/PolylineUtils.java b/services-geojson/src/main/java/com/mapbox/geojson/utils/PolylineUtils.java
index 90a9b3b3a..f811453cb 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/utils/PolylineUtils.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/utils/PolylineUtils.java
@@ -1,6 +1,8 @@
package com.mapbox.geojson.utils;
import androidx.annotation.NonNull;
+
+import com.mapbox.geojson.FlattenListOfPoints;
import com.mapbox.geojson.Point;
import java.util.ArrayList;
@@ -79,6 +81,67 @@ public static List decode(@NonNull final String encodedPath, int precisio
return path.subList(0, itemsCount);
}
+ /**
+ * Decodes an encoded path string into a {@link FlattenListOfPoints}.
+ *
+ * @param encodedPath a String representing an encoded path string
+ * @param precision OSRMv4 uses 6, OSRMv5 and Google uses 5
+ * @return a {@link FlattenListOfPoints} making up the line
+ * @see Part of algorithm came from this source
+ * @see Part of algorithm came from this source.
+ */
+ @NonNull
+ public static FlattenListOfPoints decodeToFlattenListOfPoints(
+ @NonNull
+ final String encodedPath,
+ int precision
+ ) {
+ int len = encodedPath.length();
+
+ // OSRM uses precision=6, the default Polyline spec divides by 1E5, capping at precision=5
+ double factor = Math.pow(10, precision);
+
+ // For speed we preallocate to an upper bound on the final length, then
+ // truncate the array before returning.
+ double[] flattenLngLatCoordinates = new double[len];
+ int index = 0;
+ int lat = 0;
+ int lng = 0;
+ int itemsCount = 0;
+
+ while (index < len) {
+ int result = 1;
+ int shift = 0;
+ int temp;
+ do {
+ temp = encodedPath.charAt(index++) - 63 - 1;
+ result += temp << shift;
+ shift += 5;
+ }
+ while (temp >= 0x1f);
+ lat += (result & 1) != 0 ? ~(result >> 1) : (result >> 1);
+
+ result = 1;
+ shift = 0;
+ do {
+ temp = encodedPath.charAt(index++) - 63 - 1;
+ result += temp << shift;
+ shift += 5;
+ }
+ while (temp >= 0x1f);
+ lng += (result & 1) != 0 ? ~(result >> 1) : (result >> 1);
+
+ flattenLngLatCoordinates[itemsCount*2] = lng / factor;
+ flattenLngLatCoordinates[itemsCount*2+1] = lat / factor;
+
+ itemsCount++;
+ }
+
+ double[] trimmedFlattenLngLatCoordinates = new double[itemsCount * 2];
+ System.arraycopy(flattenLngLatCoordinates, 0, trimmedFlattenLngLatCoordinates, 0, itemsCount * 2);
+ return new FlattenListOfPoints(trimmedFlattenLngLatCoordinates, null);
+ }
+
/**
* Encodes a sequence of Points into an encoded path string.
*
@@ -113,6 +176,42 @@ public static String encode(@NonNull final List path, int precision) {
return result.toString();
}
+ /**
+ * Encodes a {@link FlattenListOfPoints} into an encoded path string.
+ *
+ * @param flattenListOfPoints a {@link FlattenListOfPoints} making up the line
+ * @param precision OSRMv4 uses 6, OSRMv5 and Google uses 5
+ * @return a String representing a path string
+ */
+ @NonNull
+ public static String encode(@NonNull final FlattenListOfPoints flattenListOfPoints, int precision) {
+ long lastLat = 0;
+ long lastLng = 0;
+
+ final StringBuilder result = new StringBuilder();
+
+ // OSRM uses precision=6, the default Polyline spec divides by 1E5, capping at precision=5
+ double factor = Math.pow(10, precision);
+
+ double[] flattenLngLatArray = flattenListOfPoints.getFlattenLngLatArray();
+ for (int i = 0; i < flattenLngLatArray.length / 2; i++) {
+ double longitude = flattenLngLatArray[i * 2];
+ double latitude = flattenLngLatArray[i * 2 + 1];
+ long lat = Math.round(latitude * factor);
+ long lng = Math.round(longitude * factor);
+
+ long varLat = lat - lastLat;
+ long varLng = lng - lastLng;
+
+ encode(varLat, result);
+ encode(varLng, result);
+
+ lastLat = lat;
+ lastLng = lng;
+ }
+ return result.toString();
+ }
+
private static void encode(long variable, StringBuilder result) {
variable = variable < 0 ? ~(variable << 1) : variable << 1;
while (variable >= 0x20) {
From b0fb8f77377a3f2517f34ed1d7e153ff5a2878d7 Mon Sep 17 00:00:00 2001
From: Ramon
Date: Mon, 26 Jan 2026 16:56:14 +0200
Subject: [PATCH 17/31] Build LineString.toString using the FlattenListOfPoints
---
.../mapbox/geojson/FlattenListOfPoints.java | 40 +++++++++++++++++++
.../java/com/mapbox/geojson/LineString.java | 2 +-
2 files changed, 41 insertions(+), 1 deletion(-)
diff --git a/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPoints.java b/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPoints.java
index aafcb4c98..34b4e267b 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPoints.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPoints.java
@@ -33,6 +33,7 @@ public class FlattenListOfPoints implements Serializable {
/**
* An array to store the {@link BoundingBox} of each coordinate or null if the coordinate does
* not have bounding box.
+ * In practice is very unlikely that the points have bounding box when inside a list of points.
*/
@Nullable
private BoundingBox[] boundingBoxes;
@@ -167,4 +168,43 @@ public int hashCode() {
Arrays.hashCode(boundingBoxes)
);
}
+
+ @Override
+ public String toString() {
+ int totalPoints = flattenLngLatPoints.length / 2;
+
+ int iMax = totalPoints - 1;
+ if (iMax == -1) {
+ return "[]";
+ }
+
+ StringBuilder b = new StringBuilder();
+ b.append("[");
+
+ for (int i = 0; ; i++) {
+ b.append("Point{type=Point, bbox=");
+ if (boundingBoxes != null) {
+ BoundingBox boundingBox = boundingBoxes[i];
+ b.append(boundingBox);
+ } else {
+ b.append("null");
+ }
+ b.append(", coordinates=[");
+ b.append(flattenLngLatPoints[i * 2]);
+ b.append(", ");
+ b.append(flattenLngLatPoints[i * 2 + 1]);
+ if (altitudes != null && !Double.isNaN(altitudes[i])) {
+ b.append(", ");
+ b.append(altitudes[i]);
+ }
+ b.append("]}");
+ if (i == iMax) {
+ b.append("]");
+ break;
+ }
+ b.append(", ");
+ }
+
+ return b.toString();
+ }
}
diff --git a/services-geojson/src/main/java/com/mapbox/geojson/LineString.java b/services-geojson/src/main/java/com/mapbox/geojson/LineString.java
index b7e19e4dd..80901f1b9 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/LineString.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/LineString.java
@@ -313,7 +313,7 @@ public String toString() {
return "LineString{"
+ "type=" + type + ", "
+ "bbox=" + bbox + ", "
- + "coordinates=" + coordinates()
+ + "coordinates=" + flattenCoordinates()
+ "}";
}
From c3d7bab37e470ec48a167c69c958ebddfe92223c Mon Sep 17 00:00:00 2001
From: Ramon
Date: Tue, 27 Jan 2026 16:47:00 +0200
Subject: [PATCH 18/31] Code style fixes
---
.../mapbox/geojson/FlattenListOfPoints.java | 6 +-
.../java/com/mapbox/geojson/LineString.java | 80 ++++++++++---------
.../mapbox/geojson/utils/PolylineUtils.java | 15 +++-
3 files changed, 55 insertions(+), 46 deletions(-)
diff --git a/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPoints.java b/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPoints.java
index 34b4e267b..0913d609e 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPoints.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPoints.java
@@ -181,7 +181,7 @@ public String toString() {
StringBuilder b = new StringBuilder();
b.append("[");
- for (int i = 0; ; i++) {
+ for (int i = 0;; i++) {
b.append("Point{type=Point, bbox=");
if (boundingBoxes != null) {
BoundingBox boundingBox = boundingBoxes[i];
@@ -199,8 +199,8 @@ public String toString() {
}
b.append("]}");
if (i == iMax) {
- b.append("]");
- break;
+ b.append("]");
+ break;
}
b.append(", ");
}
diff --git a/services-geojson/src/main/java/com/mapbox/geojson/LineString.java b/services-geojson/src/main/java/com/mapbox/geojson/LineString.java
index 80901f1b9..1908b5c69 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/LineString.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/LineString.java
@@ -95,6 +95,20 @@ public static LineString fromLngLats(@NonNull MultiPoint multiPoint) {
return new LineString(TYPE, null, multiPoint.coordinates());
}
+ /**
+ * Create a new instance of this class by defining a {@link MultiPoint} object and passing. The
+ * multipoint object should comply with the GeoJson specifications described in the documentation.
+ *
+ * @param multiPoint which will make up the LineString geometry
+ * @param bbox optionally include a bbox definition as a double array
+ * @return a new instance of this class defined by the values passed inside this static factory
+ * method
+ * @since 3.0.0
+ */
+ public static LineString fromLngLats(@NonNull MultiPoint multiPoint, @Nullable BoundingBox bbox) {
+ return new LineString(TYPE, bbox, multiPoint.coordinates());
+ }
+
/**
* Create a new instance of this class by defining a list of {@link Point}s which follow the
* correct specifications described in the Point documentation. Note that there should not be any
@@ -132,18 +146,12 @@ public static LineString fromLngLats(@NonNull List points, @Nullable Boun
return new LineString(TYPE, bbox, points);
}
- /**
- * Create a new instance of this class by defining a {@link MultiPoint} object and passing. The
- * multipoint object should comply with the GeoJson specifications described in the documentation.
- *
- * @param multiPoint which will make up the LineString geometry
- * @param bbox optionally include a bbox definition as a double array
- * @return a new instance of this class defined by the values passed inside this static factory
- * method
- * @since 3.0.0
- */
- public static LineString fromLngLats(@NonNull MultiPoint multiPoint, @Nullable BoundingBox bbox) {
- return new LineString(TYPE, bbox, multiPoint.coordinates());
+ static LineString fromLngLats(double[][] coordinates) {
+ ArrayList converted = new ArrayList<>(coordinates.length);
+ for (int i = 0; i < coordinates.length; i++) {
+ converted.add(Point.fromLngLat(coordinates[i]));
+ }
+ return LineString.fromLngLats(converted);
}
/**
@@ -167,13 +175,15 @@ public static LineString fromFlattenListOfPoints(
CoordinateShifter coordinateShifter = CoordinateShifterManager.getCoordinateShifter();
// Iterate all the points and shift them
for (int i = 0; i < flattenLngLatArray.length / 2; i++) {
+ double lon = flattenLngLatArray[i * 2];
+ double lat = flattenLngLatArray[(i * 2) + 1];
if (altitudes != null && !Double.isNaN(altitudes[i])) {
- double[] shifted = coordinateShifter.shift(flattenLngLatArray[i * 2], flattenLngLatArray[(i * 2) + 1], altitudes[i]);
+ double[] shifted = coordinateShifter.shift(lon, lat, altitudes[i]);
flattenLngLatArray[i * 2] = shifted[0];
flattenLngLatArray[(i * 2) + 1] = shifted[1];
altitudes[i] = shifted[2];
} else {
- double[] shifted = coordinateShifter.shift(flattenLngLatArray[i * 2], flattenLngLatArray[(i * 2) + 1]);
+ double[] shifted = coordinateShifter.shift(lon, lat);
flattenLngLatArray[i * 2] = shifted[0];
flattenLngLatArray[(i * 2) + 1] = shifted[1];
}
@@ -182,30 +192,6 @@ public static LineString fromFlattenListOfPoints(
return new LineString(TYPE, bbox, flattenListOfPoints);
}
- LineString(String type, @Nullable BoundingBox bbox, List coordinates) {
- this(type, bbox, new FlattenListOfPoints(coordinates));
- }
-
- LineString(String type, @Nullable BoundingBox bbox, FlattenListOfPoints flattenListOfPoints) {
- if (type == null) {
- throw new NullPointerException("Null type");
- }
- if (flattenListOfPoints == null) {
- throw new NullPointerException("Null coordinates");
- }
- this.flattenListOfPoints = flattenListOfPoints;
- this.type = type;
- this.bbox = bbox;
- }
-
- static LineString fromLngLats(double[][] coordinates) {
- ArrayList converted = new ArrayList<>(coordinates.length);
- for (int i = 0; i < coordinates.length; i++) {
- converted.add(Point.fromLngLat(coordinates[i]));
- }
- return LineString.fromLngLats(converted);
- }
-
/**
* Create a new instance of this class by convert a polyline string into a lineString. This is
* handy when an API provides you with an encoded string representing the line geometry and you'd
@@ -225,6 +211,22 @@ public static LineString fromPolyline(@NonNull String polyline, int precision) {
return LineString.fromFlattenListOfPoints(points, null);
}
+ LineString(String type, @Nullable BoundingBox bbox, List coordinates) {
+ this(type, bbox, new FlattenListOfPoints(coordinates));
+ }
+
+ LineString(String type, @Nullable BoundingBox bbox, FlattenListOfPoints flattenListOfPoints) {
+ if (type == null) {
+ throw new NullPointerException("Null type");
+ }
+ if (flattenListOfPoints == null) {
+ throw new NullPointerException("Null coordinates");
+ }
+ this.flattenListOfPoints = flattenListOfPoints;
+ this.type = type;
+ this.bbox = bbox;
+ }
+
/**
* This describes the TYPE of GeoJson geometry this object is, thus this will always return
* {@link LineString}.
@@ -258,10 +260,10 @@ public BoundingBox bbox() {
/**
* Provides the list of {@link Point}s that make up the LineString geometry.
*
- * Please consider using {@link #flattenCoordinates()} instead for better performance.
*
* @return a list of points
* @since 3.0.0
+ * @deprecated Please consider using {@link #flattenCoordinates()} instead for better performance.
*/
@NonNull
@Override
diff --git a/services-geojson/src/main/java/com/mapbox/geojson/utils/PolylineUtils.java b/services-geojson/src/main/java/com/mapbox/geojson/utils/PolylineUtils.java
index f811453cb..763e6ed6b 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/utils/PolylineUtils.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/utils/PolylineUtils.java
@@ -131,14 +131,17 @@ public static FlattenListOfPoints decodeToFlattenListOfPoints(
while (temp >= 0x1f);
lng += (result & 1) != 0 ? ~(result >> 1) : (result >> 1);
- flattenLngLatCoordinates[itemsCount*2] = lng / factor;
- flattenLngLatCoordinates[itemsCount*2+1] = lat / factor;
+ flattenLngLatCoordinates[itemsCount * 2] = lng / factor;
+ flattenLngLatCoordinates[itemsCount * 2 + 1] = lat / factor;
itemsCount++;
}
double[] trimmedFlattenLngLatCoordinates = new double[itemsCount * 2];
- System.arraycopy(flattenLngLatCoordinates, 0, trimmedFlattenLngLatCoordinates, 0, itemsCount * 2);
+ System.arraycopy(flattenLngLatCoordinates, 0,
+ trimmedFlattenLngLatCoordinates, 0,
+ itemsCount * 2
+ );
return new FlattenListOfPoints(trimmedFlattenLngLatCoordinates, null);
}
@@ -184,7 +187,11 @@ public static String encode(@NonNull final List path, int precision) {
* @return a String representing a path string
*/
@NonNull
- public static String encode(@NonNull final FlattenListOfPoints flattenListOfPoints, int precision) {
+ public static String encode(
+ @NonNull
+ final FlattenListOfPoints flattenListOfPoints,
+ int precision
+ ) {
long lastLat = 0;
long lastLng = 0;
From e264d6f500a16b5367252474ea5ca57eaa356705 Mon Sep 17 00:00:00 2001
From: Ramon
Date: Tue, 27 Jan 2026 17:03:59 +0200
Subject: [PATCH 19/31] Use flattenListOfPoints for the `toString`
---
.../src/main/java/com/mapbox/geojson/LineString.java | 2 +-
.../src/main/java/com/mapbox/geojson/MultiPoint.java | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/services-geojson/src/main/java/com/mapbox/geojson/LineString.java b/services-geojson/src/main/java/com/mapbox/geojson/LineString.java
index 1908b5c69..91b4679b7 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/LineString.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/LineString.java
@@ -315,7 +315,7 @@ public String toString() {
return "LineString{"
+ "type=" + type + ", "
+ "bbox=" + bbox + ", "
- + "coordinates=" + flattenCoordinates()
+ + "coordinates=" + flattenListOfPoints
+ "}";
}
diff --git a/services-geojson/src/main/java/com/mapbox/geojson/MultiPoint.java b/services-geojson/src/main/java/com/mapbox/geojson/MultiPoint.java
index bfa3a7caa..5bd16bb7c 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/MultiPoint.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/MultiPoint.java
@@ -193,7 +193,7 @@ public String toString() {
return "MultiPoint{"
+ "type=" + type + ", "
+ "bbox=" + bbox + ", "
- + "coordinates=" + flattenListOfPoints.points()
+ + "coordinates=" + flattenListOfPoints
+ "}";
}
From e375321f56c5e0a420c3ad55761f81ac278d64b5 Mon Sep 17 00:00:00 2001
From: Ramon
Date: Tue, 27 Jan 2026 17:04:34 +0200
Subject: [PATCH 20/31] Update
services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPoints.java
---
.../src/main/java/com/mapbox/geojson/FlattenListOfPoints.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPoints.java b/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPoints.java
index 0913d609e..51f4d34b7 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPoints.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPoints.java
@@ -82,7 +82,7 @@ public FlattenListOfPoints(@NonNull double[] flattenLngLatPoints, @Nullable doub
altitudes[i] = Double.NaN;
}
- // Similarly to altitudes, if one point has bound we create an array to store those.
+ // Similarly to altitudes, if one point has bounding box we create an array to store those.
if (point.bbox() != null) {
if (boundingBoxes == null) {
boundingBoxes = new BoundingBox[points.size()];
From aa51c393134dc045cd3ddfe7a61b1e0d4f91a23d Mon Sep 17 00:00:00 2001
From: Ramon
Date: Thu, 29 Jan 2026 17:11:43 +0200
Subject: [PATCH 21/31] PolylineUtils#decodeToFlattenListOfPoints returns
double[]
---
.../java/com/mapbox/geojson/LineString.java | 35 +++++++------------
.../mapbox/geojson/utils/PolylineUtils.java | 7 ++--
.../com/mapbox/geojson/LineStringTest.java | 3 +-
.../mapbox/geojson/shifter/ShifterTest.java | 3 +-
4 files changed, 19 insertions(+), 29 deletions(-)
diff --git a/services-geojson/src/main/java/com/mapbox/geojson/LineString.java b/services-geojson/src/main/java/com/mapbox/geojson/LineString.java
index 91b4679b7..f81a4bd49 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/LineString.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/LineString.java
@@ -155,41 +155,32 @@ static LineString fromLngLats(double[][] coordinates) {
}
/**
- * Create a new instance of this class by defining a {@link FlattenListOfPoints} object.
- * The multipoint object should comply with the GeoJson specifications described in the
+ * Create a new instance by providing a flatten array of [lng1, lat1, lng2, lat2, ...].
+ * The flatten array object should comply with the GeoJson specifications described in the
* documentation.
*
- * @param flattenListOfPoints which will make up the LineString geometry. The points will be
- * shifted according to the current
- * {@link CoordinateShifterManager#getCoordinateShifter()}
+ * @param flattenLngLatArray which will make up the LineString geometry. WARNING: The points will
+ * be shifted according to the current
+ * {@link CoordinateShifterManager#getCoordinateShifter()} in place!
* @param bbox optionally include a bbox definition
* @return a new instance of this class defined by the values passed inside this static factory
* method
*/
- public static LineString fromFlattenListOfPoints(
- FlattenListOfPoints flattenListOfPoints,
+ public static LineString fromFlattenArrayOfPoints(
+ double[] flattenLngLatArray,
@Nullable BoundingBox bbox
) {
- double[] flattenLngLatArray = flattenListOfPoints.getFlattenLngLatArray();
- double[] altitudes = flattenListOfPoints.getAltitudes();
CoordinateShifter coordinateShifter = CoordinateShifterManager.getCoordinateShifter();
// Iterate all the points and shift them
for (int i = 0; i < flattenLngLatArray.length / 2; i++) {
double lon = flattenLngLatArray[i * 2];
double lat = flattenLngLatArray[(i * 2) + 1];
- if (altitudes != null && !Double.isNaN(altitudes[i])) {
- double[] shifted = coordinateShifter.shift(lon, lat, altitudes[i]);
- flattenLngLatArray[i * 2] = shifted[0];
- flattenLngLatArray[(i * 2) + 1] = shifted[1];
- altitudes[i] = shifted[2];
- } else {
- double[] shifted = coordinateShifter.shift(lon, lat);
- flattenLngLatArray[i * 2] = shifted[0];
- flattenLngLatArray[(i * 2) + 1] = shifted[1];
- }
+ double[] shifted = coordinateShifter.shift(lon, lat);
+ flattenLngLatArray[i * 2] = shifted[0];
+ flattenLngLatArray[(i * 2) + 1] = shifted[1];
}
- return new LineString(TYPE, bbox, flattenListOfPoints);
+ return new LineString(TYPE, bbox, new FlattenListOfPoints(flattenLngLatArray, null));
}
/**
@@ -207,8 +198,8 @@ public static LineString fromFlattenListOfPoints(
* @since 1.0.0
*/
public static LineString fromPolyline(@NonNull String polyline, int precision) {
- FlattenListOfPoints points = PolylineUtils.decodeToFlattenListOfPoints(polyline, precision);
- return LineString.fromFlattenListOfPoints(points, null);
+ double[] points = PolylineUtils.decodeToFlattenListOfPoints(polyline, precision);
+ return LineString.fromFlattenArrayOfPoints(points, null);
}
LineString(String type, @Nullable BoundingBox bbox, List coordinates) {
diff --git a/services-geojson/src/main/java/com/mapbox/geojson/utils/PolylineUtils.java b/services-geojson/src/main/java/com/mapbox/geojson/utils/PolylineUtils.java
index 763e6ed6b..ecdb35c14 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/utils/PolylineUtils.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/utils/PolylineUtils.java
@@ -86,12 +86,13 @@ public static List decode(@NonNull final String encodedPath, int precisio
*
* @param encodedPath a String representing an encoded path string
* @param precision OSRMv4 uses 6, OSRMv5 and Google uses 5
- * @return a {@link FlattenListOfPoints} making up the line
+ * @return an array of doubles representing a line geometry with flattened points
+ * in the form: [lng1, lat1, lng2, lat2, ...]
* @see Part of algorithm came from this source
* @see Part of algorithm came from this source.
*/
@NonNull
- public static FlattenListOfPoints decodeToFlattenListOfPoints(
+ public static double[] decodeToFlattenListOfPoints(
@NonNull
final String encodedPath,
int precision
@@ -142,7 +143,7 @@ public static FlattenListOfPoints decodeToFlattenListOfPoints(
trimmedFlattenLngLatCoordinates, 0,
itemsCount * 2
);
- return new FlattenListOfPoints(trimmedFlattenLngLatCoordinates, null);
+ return trimmedFlattenLngLatCoordinates;
}
/**
diff --git a/services-geojson/src/test/java/com/mapbox/geojson/LineStringTest.java b/services-geojson/src/test/java/com/mapbox/geojson/LineStringTest.java
index 8717509c0..b38e64dba 100644
--- a/services-geojson/src/test/java/com/mapbox/geojson/LineStringTest.java
+++ b/services-geojson/src/test/java/com/mapbox/geojson/LineStringTest.java
@@ -47,8 +47,7 @@ public void fromLngLats_generatedFromMultipoint() throws Exception {
@Test
public void fromFlattenListOfPoints() throws Exception {
double[] flattenLngLatPoints= new double[]{1.0, 2.0, 4.0, 8.0};
- FlattenListOfPoints flattenListOfPoints = new FlattenListOfPoints(flattenLngLatPoints, null);
- LineString lineString = LineString.fromFlattenListOfPoints(flattenListOfPoints, null);
+ LineString lineString = LineString.fromFlattenArrayOfPoints(flattenLngLatPoints, null);
assertEquals("_gayB_c`|@_wemJ_kbvD", lineString.toPolyline(PRECISION_6));
}
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
index 67353d3cd..b81a46e2c 100644
--- a/services-geojson/src/test/java/com/mapbox/geojson/shifter/ShifterTest.java
+++ b/services-geojson/src/test/java/com/mapbox/geojson/shifter/ShifterTest.java
@@ -224,8 +224,7 @@ public void linestring_basic_shift_with_bbox() {
jsonString);
double[] flattenLngLatPoints= new double[]{1.0, 1.0, 2.0, 2.0, 3.0, 3.0};
- FlattenListOfPoints flattenListOfPoints = new FlattenListOfPoints(flattenLngLatPoints, null);
- LineString lineString2 = LineString.fromFlattenListOfPoints(flattenListOfPoints, bbox);
+ LineString lineString2 = LineString.fromFlattenArrayOfPoints(flattenLngLatPoints, bbox);
String jsonString2 = lineString2.toJson();
compareJson("{\"coordinates\":[[1,1],[2,2],[3,3]],"
+ "\"type\":\"LineString\",\"bbox\":[1.0,2.0,3.0,4.0]}",
From 9c85c7d0a4d544b543c411da67b941298b951939 Mon Sep 17 00:00:00 2001
From: Ramon
Date: Thu, 29 Jan 2026 21:44:00 +0200
Subject: [PATCH 22/31] Fixed TurfMiscTest tests
---
.../java/com/mapbox/turf/TurfMiscTest.java | 68 +++++++++++--------
1 file changed, 40 insertions(+), 28 deletions(-)
diff --git a/services-turf/src/test/java/com/mapbox/turf/TurfMiscTest.java b/services-turf/src/test/java/com/mapbox/turf/TurfMiscTest.java
index ab87ad030..266bc3a54 100644
--- a/services-turf/src/test/java/com/mapbox/turf/TurfMiscTest.java
+++ b/services-turf/src/test/java/com/mapbox/turf/TurfMiscTest.java
@@ -15,6 +15,7 @@
import static junit.framework.TestCase.assertNotNull;
import static org.hamcrest.CoreMatchers.startsWith;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
@@ -453,12 +454,14 @@ public void testLineSliceAlongLine1() throws IOException, TurfException {
Point end_point = TurfMeasurement.along(lineStringLine1, stop, TurfConstants.UNIT_MILES);
LineString sliced = TurfMisc.lineSliceAlong(line1, start, stop, TurfConstants.UNIT_MILES);
- assertEquals(sliced.coordinates().get(0).coordinates(), start_point.coordinates());
- assertEquals(sliced.coordinates().get(0).flattenCoordinates(), start_point.flattenCoordinates());
- assertEquals(sliced.coordinates().get(sliced.coordinates().size() - 1).coordinates(),
+ List slicedCoordinates = sliced.coordinates();
+ assertEquals(slicedCoordinates.get(0).coordinates(), start_point.coordinates());
+ assertArrayEquals(slicedCoordinates.get(0).flattenCoordinates(),
+ start_point.flattenCoordinates(), DELTA);
+ assertEquals(slicedCoordinates.get(slicedCoordinates.size() - 1).coordinates(),
end_point.coordinates());
- assertEquals(sliced.coordinates().get(sliced.coordinates().size() - 1).flattenCoordinates(),
- end_point.flattenCoordinates());
+ assertArrayEquals(slicedCoordinates.get(slicedCoordinates.size() - 1).flattenCoordinates(),
+ end_point.flattenCoordinates(), DELTA);
}
@Test
@@ -473,12 +476,14 @@ public void testLineSliceAlongOvershootLine1() throws IOException, TurfException
Point end_point = TurfMeasurement.along(lineStringLine1, stop, TurfConstants.UNIT_MILES);
LineString sliced = TurfMisc.lineSliceAlong(line1, start, stop, TurfConstants.UNIT_MILES);
- assertEquals(sliced.coordinates().get(0).coordinates(), start_point.coordinates());
- assertEquals(sliced.coordinates().get(0).flattenCoordinates(), start_point.flattenCoordinates());
- assertEquals(sliced.coordinates().get(sliced.coordinates().size() - 1).coordinates(),
+ List slicedCoordinates = sliced.coordinates();
+ assertEquals(slicedCoordinates.get(0).coordinates(), start_point.coordinates());
+ assertArrayEquals(slicedCoordinates.get(0).flattenCoordinates(),
+ start_point.flattenCoordinates(), DELTA);
+ assertEquals(slicedCoordinates.get(slicedCoordinates.size() - 1).coordinates(),
end_point.coordinates());
- assertEquals(sliced.coordinates().get(sliced.coordinates().size() - 1).flattenCoordinates(),
- end_point.flattenCoordinates());
+ assertArrayEquals(slicedCoordinates.get(slicedCoordinates.size() - 1).flattenCoordinates(),
+ end_point.flattenCoordinates(), DELTA);
}
@Test
@@ -494,12 +499,14 @@ public void testLineSliceAlongRoute1() throws IOException, TurfException {
LineString sliced = TurfMisc.lineSliceAlong(route1, start, stop, TurfConstants.UNIT_MILES);
- assertEquals(sliced.coordinates().get(0).coordinates(), start_point.coordinates());
- assertEquals(sliced.coordinates().get(0).flattenCoordinates(), start_point.flattenCoordinates());
- assertEquals(sliced.coordinates().get(sliced.coordinates().size() - 1).coordinates(),
+ List slicedCoordinates = sliced.coordinates();
+ assertEquals(slicedCoordinates.get(0).coordinates(), start_point.coordinates());
+ assertArrayEquals(slicedCoordinates.get(0).flattenCoordinates(),
+ start_point.flattenCoordinates(), DELTA);
+ assertEquals(slicedCoordinates.get(slicedCoordinates.size() - 1).coordinates(),
end_point.coordinates());
- assertEquals(sliced.coordinates().get(sliced.coordinates().size() - 1).flattenCoordinates(),
- end_point.flattenCoordinates());
+ assertArrayEquals(slicedCoordinates.get(slicedCoordinates.size() - 1).flattenCoordinates(),
+ end_point.flattenCoordinates(), DELTA);
}
@Test
@@ -515,11 +522,12 @@ public void testLineSliceAlongRoute2() throws IOException, TurfException {
LineString sliced = TurfMisc.lineSliceAlong(route2, start, stop, TurfConstants.UNIT_MILES);
assertEquals(sliced.coordinates().get(0).coordinates(), start_point.coordinates());
- assertEquals(sliced.coordinates().get(0).flattenCoordinates(), start_point.flattenCoordinates());
+ assertArrayEquals(sliced.coordinates().get(0).flattenCoordinates(),
+ start_point.flattenCoordinates(), DELTA);
assertEquals(sliced.coordinates().get(sliced.coordinates().size() - 1).coordinates(),
end_point.coordinates());
- assertEquals(sliced.coordinates().get(sliced.coordinates().size() - 1).flattenCoordinates(),
- end_point.flattenCoordinates());
+ assertArrayEquals(sliced.coordinates().get(sliced.coordinates().size() - 1).flattenCoordinates(),
+ end_point.flattenCoordinates(), DELTA);
}
@Test
@@ -544,13 +552,15 @@ public void testLineAlongStopLongerThanLength() throws IOException, TurfExceptio
Point start_point = TurfMeasurement.along(lineStringLine1, start, TurfConstants.UNIT_MILES);
List lineCoordinates = lineStringLine1.coordinates();
LineString sliced = TurfMisc.lineSliceAlong(line1, start, stop, TurfConstants.UNIT_MILES);
- assertEquals(sliced.coordinates().get(0).coordinates(), start_point.coordinates());
- assertEquals(sliced.coordinates().get(0).flattenCoordinates(), start_point.flattenCoordinates());
+ List slicedCoordinates = sliced.coordinates();
+ assertEquals(slicedCoordinates.get(0).coordinates(), start_point.coordinates());
+ assertArrayEquals(slicedCoordinates.get(0).flattenCoordinates(),
+ start_point.flattenCoordinates(), DELTA);
- assertEquals(sliced.coordinates().get(sliced.coordinates().size() - 1).coordinates(),
+ assertEquals(slicedCoordinates.get(slicedCoordinates.size() - 1).coordinates(),
lineCoordinates.get(lineCoordinates.size() - 1).coordinates());
- assertEquals(sliced.coordinates().get(sliced.coordinates().size() - 1).flattenCoordinates(),
- lineCoordinates.get(lineCoordinates.size() - 1).flattenCoordinates());
+ assertArrayEquals(slicedCoordinates.get(slicedCoordinates.size() - 1).flattenCoordinates(),
+ lineCoordinates.get(lineCoordinates.size() - 1).flattenCoordinates(), DELTA);
}
@Test
@@ -568,11 +578,13 @@ public void testShortLine() throws IOException, TurfException {
Point end_point = TurfMeasurement.along(lineStringLine1, stop, TurfConstants.UNIT_MILES);
LineString sliced = TurfMisc.lineSliceAlong(lineStringLine1, start, stop, TurfConstants.UNIT_MILES);
- assertEquals(sliced.coordinates().get(0).coordinates(), start_point.coordinates());
- assertEquals(sliced.coordinates().get(0).flattenCoordinates(), start_point.flattenCoordinates());
- assertEquals(sliced.coordinates().get(sliced.coordinates().size() - 1).coordinates(),
+ List slicedCoordinates = sliced.coordinates();
+ assertEquals(slicedCoordinates.get(0).coordinates(), start_point.coordinates());
+ assertArrayEquals(slicedCoordinates.get(0).flattenCoordinates(),
+ start_point.flattenCoordinates(), DELTA);
+ assertEquals(slicedCoordinates.get(slicedCoordinates.size() - 1).coordinates(),
end_point.coordinates());
- assertEquals(sliced.coordinates().get(sliced.coordinates().size() - 1).flattenCoordinates(),
- end_point.flattenCoordinates());
+ assertArrayEquals(slicedCoordinates.get(slicedCoordinates.size() - 1).flattenCoordinates(),
+ end_point.flattenCoordinates(), DELTA);
}
}
\ No newline at end of file
From be6d8b83aaa64026c87c3f43bab42794de6ac310 Mon Sep 17 00:00:00 2001
From: Ramon
Date: Fri, 30 Jan 2026 07:48:37 +0200
Subject: [PATCH 23/31] Add FlattenListOfPoints unit tests
---
.../geojson/FlattenListOfPointsTest.java | 419 ++++++++++++++++++
1 file changed, 419 insertions(+)
create mode 100644 services-geojson/src/test/java/com/mapbox/geojson/FlattenListOfPointsTest.java
diff --git a/services-geojson/src/test/java/com/mapbox/geojson/FlattenListOfPointsTest.java b/services-geojson/src/test/java/com/mapbox/geojson/FlattenListOfPointsTest.java
new file mode 100644
index 000000000..76af95bfb
--- /dev/null
+++ b/services-geojson/src/test/java/com/mapbox/geojson/FlattenListOfPointsTest.java
@@ -0,0 +1,419 @@
+package com.mapbox.geojson;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class FlattenListOfPointsTest extends TestUtils {
+
+ @Test
+ public void constructor_withArrays_storesDataCorrectly() {
+ double[] lngLatArray = new double[]{1.0, 2.0, 3.0, 4.0};
+ double[] altitudes = new double[]{5.0, 6.0};
+
+ FlattenListOfPoints flatten = new FlattenListOfPoints(lngLatArray, altitudes);
+
+ assertNotNull(flatten);
+ assertArrayEquals(lngLatArray, flatten.getFlattenLngLatArray(), DELTA);
+ assertArrayEquals(altitudes, flatten.getAltitudes(), DELTA);
+ }
+
+ @Test
+ public void constructor_withArrays_nullAltitudes() {
+ double[] lngLatArray = new double[]{1.0, 2.0, 3.0, 4.0};
+
+ FlattenListOfPoints flatten = new FlattenListOfPoints(lngLatArray, null);
+
+ assertNotNull(flatten);
+ assertArrayEquals(lngLatArray, flatten.getFlattenLngLatArray(), DELTA);
+ assertNull(flatten.getAltitudes());
+ }
+
+ @Test
+ public void constructor_withEmptyList_createsEmptyFlatten() {
+ List points = new ArrayList<>();
+
+ FlattenListOfPoints flatten = new FlattenListOfPoints(points);
+
+ assertNotNull(flatten);
+ assertEquals(0, flatten.getFlattenLngLatArray().length);
+ assertNull(flatten.getAltitudes());
+ }
+
+ @Test
+ public void constructor_withPointsNoAltitude_storesLngLatOnly() {
+ List points = new ArrayList<>();
+ points.add(Point.fromLngLat(1.0, 2.0));
+ points.add(Point.fromLngLat(3.0, 4.0));
+ points.add(Point.fromLngLat(5.0, 6.0));
+
+ FlattenListOfPoints flatten = new FlattenListOfPoints(points);
+
+ assertNotNull(flatten);
+ double[] expected = new double[]{1.0, 2.0, 3.0, 4.0, 5.0, 6.0};
+ assertArrayEquals(expected, flatten.getFlattenLngLatArray(), DELTA);
+ assertNull(flatten.getAltitudes());
+ }
+
+ @Test
+ public void constructor_withPointsWithAltitude_storesAllData() {
+ List points = new ArrayList<>();
+ points.add(Point.fromLngLat(1.0, 2.0, 10.0));
+ points.add(Point.fromLngLat(3.0, 4.0, 20.0));
+ points.add(Point.fromLngLat(5.0, 6.0, 30.0));
+
+ FlattenListOfPoints flatten = new FlattenListOfPoints(points);
+
+ assertNotNull(flatten);
+ double[] expectedLngLat = new double[]{1.0, 2.0, 3.0, 4.0, 5.0, 6.0};
+ double[] expectedAlt = new double[]{10.0, 20.0, 30.0};
+ assertArrayEquals(expectedLngLat, flatten.getFlattenLngLatArray(), DELTA);
+ assertArrayEquals(expectedAlt, flatten.getAltitudes(), DELTA);
+ }
+
+ @Test
+ public void constructor_withMixedAltitudes_storesNaNForMissingAltitudes() {
+ List points = new ArrayList<>();
+ points.add(Point.fromLngLat(1.0, 2.0)); // no altitude
+ points.add(Point.fromLngLat(3.0, 4.0, 20.0)); // with altitude
+ points.add(Point.fromLngLat(5.0, 6.0)); // no altitude
+ points.add(Point.fromLngLat(7.0, 8.0, 40.0)); // with altitude
+
+ FlattenListOfPoints flatten = new FlattenListOfPoints(points);
+
+ assertNotNull(flatten);
+ double[] expectedLngLat = new double[]{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0};
+ assertArrayEquals(expectedLngLat, flatten.getFlattenLngLatArray(), DELTA);
+
+ double[] altitudes = flatten.getAltitudes();
+ assertNotNull(altitudes);
+ assertEquals(4, altitudes.length);
+ assertTrue(Double.isNaN(altitudes[0]));
+ assertEquals(20.0, altitudes[1], DELTA);
+ assertTrue(Double.isNaN(altitudes[2]));
+ assertEquals(40.0, altitudes[3], DELTA);
+ }
+
+ @Test
+ public void constructor_withSinglePoint_worksCorrectly() {
+ List points = new ArrayList<>();
+ points.add(Point.fromLngLat(1.0, 2.0, 10.0));
+
+ FlattenListOfPoints flatten = new FlattenListOfPoints(points);
+
+ assertNotNull(flatten);
+ double[] expectedLngLat = new double[]{1.0, 2.0};
+ double[] expectedAlt = new double[]{10.0};
+ assertArrayEquals(expectedLngLat, flatten.getFlattenLngLatArray(), DELTA);
+ assertArrayEquals(expectedAlt, flatten.getAltitudes(), DELTA);
+ }
+
+ @Test
+ public void points_returnsEmptyListForEmptyFlatten() {
+ double[] lngLatArray = new double[]{};
+ FlattenListOfPoints flatten = new FlattenListOfPoints(lngLatArray, null);
+
+ List points = flatten.points();
+
+ assertNotNull(points);
+ assertEquals(0, points.size());
+ }
+
+ @Test
+ public void points_reconstructsPointsWithoutAltitude() {
+ double[] lngLatArray = new double[]{1.0, 2.0, 3.0, 4.0};
+ FlattenListOfPoints flatten = new FlattenListOfPoints(lngLatArray, null);
+
+ List points = flatten.points();
+
+ assertNotNull(points);
+ assertEquals(2, points.size());
+ assertEquals(1.0, points.get(0).longitude(), DELTA);
+ assertEquals(2.0, points.get(0).latitude(), DELTA);
+ assertFalse(points.get(0).hasAltitude());
+ assertEquals(3.0, points.get(1).longitude(), DELTA);
+ assertEquals(4.0, points.get(1).latitude(), DELTA);
+ assertFalse(points.get(1).hasAltitude());
+ }
+
+ @Test
+ public void points_reconstructsPointsWithAltitude() {
+ double[] lngLatArray = new double[]{1.0, 2.0, 3.0, 4.0};
+ double[] altitudes = new double[]{10.0, 20.0};
+ FlattenListOfPoints flatten = new FlattenListOfPoints(lngLatArray, altitudes);
+
+ List points = flatten.points();
+
+ assertNotNull(points);
+ assertEquals(2, points.size());
+ assertEquals(1.0, points.get(0).longitude(), DELTA);
+ assertEquals(2.0, points.get(0).latitude(), DELTA);
+ assertEquals(10.0, points.get(0).altitude(), DELTA);
+ assertTrue(points.get(0).hasAltitude());
+ assertEquals(3.0, points.get(1).longitude(), DELTA);
+ assertEquals(4.0, points.get(1).latitude(), DELTA);
+ assertEquals(20.0, points.get(1).altitude(), DELTA);
+ assertTrue(points.get(1).hasAltitude());
+ }
+
+ @Test
+ public void points_reconstructsPointsWithMixedAltitudes() {
+ double[] lngLatArray = new double[]{1.0, 2.0, 3.0, 4.0, 5.0, 6.0};
+ double[] altitudes = new double[]{Double.NaN, 20.0, 30.0};
+ FlattenListOfPoints flatten = new FlattenListOfPoints(lngLatArray, altitudes);
+
+ List points = flatten.points();
+
+ assertNotNull(points);
+ assertEquals(3, points.size());
+ assertFalse(points.get(0).hasAltitude());
+ assertTrue(points.get(1).hasAltitude());
+ assertEquals(20.0, points.get(1).altitude(), DELTA);
+ assertTrue(points.get(2).hasAltitude());
+ assertEquals(30.0, points.get(2).altitude(), DELTA);
+ }
+
+ @Test
+ public void points_roundTrip_preservesData() {
+ List originalPoints = new ArrayList<>();
+ originalPoints.add(Point.fromLngLat(1.0, 2.0, 10.0));
+ originalPoints.add(Point.fromLngLat(3.0, 4.0));
+ originalPoints.add(Point.fromLngLat(5.0, 6.0, 30.0));
+
+ FlattenListOfPoints flatten = new FlattenListOfPoints(originalPoints);
+ List reconstructedPoints = flatten.points();
+
+ assertEquals(originalPoints.size(), reconstructedPoints.size());
+ for (int i = 0; i < originalPoints.size(); i++) {
+ Point original = originalPoints.get(i);
+ Point reconstructed = reconstructedPoints.get(i);
+ assertEquals(original.longitude(), reconstructed.longitude(), DELTA);
+ assertEquals(original.latitude(), reconstructed.latitude(), DELTA);
+ assertEquals(original.hasAltitude(), reconstructed.hasAltitude());
+ if (original.hasAltitude()) {
+ assertEquals(original.altitude(), reconstructed.altitude(), DELTA);
+ }
+ }
+ }
+
+ @Test
+ public void equals_sameFlatten_returnsTrue() {
+ double[] lngLatArray = new double[]{1.0, 2.0, 3.0, 4.0};
+ double[] altitudes = new double[]{10.0, 20.0};
+ FlattenListOfPoints flatten1 = new FlattenListOfPoints(lngLatArray, altitudes);
+ FlattenListOfPoints flatten2 = new FlattenListOfPoints(lngLatArray, altitudes);
+
+ assertTrue(flatten1.equals(flatten2));
+ assertTrue(flatten2.equals(flatten1));
+ }
+
+ @Test
+ public void equals_differentLngLat_returnsFalse() {
+ double[] lngLatArray1 = new double[]{1.0, 2.0, 3.0, 4.0};
+ double[] lngLatArray2 = new double[]{1.0, 2.0, 5.0, 6.0};
+ double[] altitudes = new double[]{10.0, 20.0};
+ FlattenListOfPoints flatten1 = new FlattenListOfPoints(lngLatArray1, altitudes);
+ FlattenListOfPoints flatten2 = new FlattenListOfPoints(lngLatArray2, altitudes);
+
+ assertFalse(flatten1.equals(flatten2));
+ }
+
+ @Test
+ public void equals_differentAltitudes_returnsFalse() {
+ double[] lngLatArray = new double[]{1.0, 2.0, 3.0, 4.0};
+ double[] altitudes1 = new double[]{10.0, 20.0};
+ double[] altitudes2 = new double[]{10.0, 30.0};
+ FlattenListOfPoints flatten1 = new FlattenListOfPoints(lngLatArray, altitudes1);
+ FlattenListOfPoints flatten2 = new FlattenListOfPoints(lngLatArray, altitudes2);
+
+ assertFalse(flatten1.equals(flatten2));
+ }
+
+ @Test
+ public void equals_oneWithAltitudeOneWithout_returnsFalse() {
+ double[] lngLatArray = new double[]{1.0, 2.0, 3.0, 4.0};
+ double[] altitudes = new double[]{10.0, 20.0};
+ FlattenListOfPoints flatten1 = new FlattenListOfPoints(lngLatArray, altitudes);
+ FlattenListOfPoints flatten2 = new FlattenListOfPoints(lngLatArray, null);
+
+ assertFalse(flatten1.equals(flatten2));
+ }
+
+ @Test
+ public void equals_withNull_returnsFalse() {
+ double[] lngLatArray = new double[]{1.0, 2.0, 3.0, 4.0};
+ FlattenListOfPoints flatten = new FlattenListOfPoints(lngLatArray, null);
+
+ assertFalse(flatten.equals(null));
+ }
+
+ @Test
+ public void equals_withDifferentType_returnsFalse() {
+ double[] lngLatArray = new double[]{1.0, 2.0, 3.0, 4.0};
+ FlattenListOfPoints flatten = new FlattenListOfPoints(lngLatArray, null);
+
+ assertFalse(flatten.equals("not a FlattenListOfPoints"));
+ }
+
+ @Test
+ public void hashCode_sameData_returnsSameHashCode() {
+ double[] lngLatArray = new double[]{1.0, 2.0, 3.0, 4.0};
+ double[] altitudes = new double[]{10.0, 20.0};
+ FlattenListOfPoints flatten1 = new FlattenListOfPoints(lngLatArray, altitudes);
+ FlattenListOfPoints flatten2 = new FlattenListOfPoints(lngLatArray, altitudes);
+
+ assertEquals(flatten1.hashCode(), flatten2.hashCode());
+ }
+
+ @Test
+ public void hashCode_differentData_returnsDifferentHashCode() {
+ double[] lngLatArray1 = new double[]{1.0, 2.0, 3.0, 4.0};
+ double[] lngLatArray2 = new double[]{1.0, 2.0, 5.0, 6.0};
+ double[] altitudes = new double[]{10.0, 20.0};
+ FlattenListOfPoints flatten1 = new FlattenListOfPoints(lngLatArray1, altitudes);
+ FlattenListOfPoints flatten2 = new FlattenListOfPoints(lngLatArray2, altitudes);
+
+ assertNotEquals(flatten1.hashCode(), flatten2.hashCode());
+ }
+
+ @Test
+ public void toString_emptyFlatten_returnsEmptyBrackets() {
+ double[] lngLatArray = new double[]{};
+ FlattenListOfPoints flatten = new FlattenListOfPoints(lngLatArray, null);
+
+ String result = flatten.toString();
+
+ assertEquals("[]", result);
+ }
+
+ @Test
+ public void toString_singlePointWithoutAltitude_returnsCorrectFormat() {
+ double[] lngLatArray = new double[]{1.0, 2.0};
+ FlattenListOfPoints flatten = new FlattenListOfPoints(lngLatArray, null);
+
+ String result = flatten.toString();
+
+ assertEquals("[Point{type=Point, bbox=null, coordinates=[1.0, 2.0]}]", result);
+ }
+
+ @Test
+ public void toString_singlePointWithAltitude_returnsCorrectFormat() {
+ double[] lngLatArray = new double[]{1.0, 2.0};
+ double[] altitudes = new double[]{10.0};
+ FlattenListOfPoints flatten = new FlattenListOfPoints(lngLatArray, altitudes);
+
+ String result = flatten.toString();
+
+ assertEquals("[Point{type=Point, bbox=null, coordinates=[1.0, 2.0, 10.0]}]", result);
+ }
+
+ @Test
+ public void toString_multiplePointsWithoutAltitude_returnsCorrectFormat() {
+ double[] lngLatArray = new double[]{1.0, 2.0, 3.0, 4.0};
+ FlattenListOfPoints flatten = new FlattenListOfPoints(lngLatArray, null);
+
+ String result = flatten.toString();
+
+ String expected = "[Point{type=Point, bbox=null, coordinates=[1.0, 2.0]}, "
+ + "Point{type=Point, bbox=null, coordinates=[3.0, 4.0]}]";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ public void toString_multiplePointsWithAltitude_returnsCorrectFormat() {
+ double[] lngLatArray = new double[]{1.0, 2.0, 3.0, 4.0};
+ double[] altitudes = new double[]{10.0, 20.0};
+ FlattenListOfPoints flatten = new FlattenListOfPoints(lngLatArray, altitudes);
+
+ String result = flatten.toString();
+
+ String expected = "[Point{type=Point, bbox=null, coordinates=[1.0, 2.0, 10.0]}, "
+ + "Point{type=Point, bbox=null, coordinates=[3.0, 4.0, 20.0]}]";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ public void toString_mixedAltitudes_returnsCorrectFormat() {
+ double[] lngLatArray = new double[]{1.0, 2.0, 3.0, 4.0, 5.0, 6.0};
+ double[] altitudes = new double[]{10.0, Double.NaN, 30.0};
+ FlattenListOfPoints flatten = new FlattenListOfPoints(lngLatArray, altitudes);
+
+ String result = flatten.toString();
+
+ String expected = "[Point{type=Point, bbox=null, coordinates=[1.0, 2.0, 10.0]}, "
+ + "Point{type=Point, bbox=null, coordinates=[3.0, 4.0]}, "
+ + "Point{type=Point, bbox=null, coordinates=[5.0, 6.0, 30.0]}]";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ public void testSerializable() throws IOException, ClassNotFoundException {
+ List points = new ArrayList<>();
+ points.add(Point.fromLngLat(1.0, 2.0, 10.0));
+ points.add(Point.fromLngLat(3.0, 4.0));
+ points.add(Point.fromLngLat(5.0, 6.0, 30.0));
+
+ FlattenListOfPoints flatten = new FlattenListOfPoints(points);
+
+ byte[] bytes = serialize(flatten);
+ FlattenListOfPoints deserialized = deserialize(bytes, FlattenListOfPoints.class);
+
+ assertEquals(flatten, deserialized);
+ }
+
+ @Test
+ public void constructor_withPointsWithBoundingBox_storesBoundingBoxes() {
+ List points = new ArrayList<>();
+ BoundingBox bbox1 = BoundingBox.fromLngLats(0.0, 0.0, 1.0, 1.0);
+ BoundingBox bbox2 = BoundingBox.fromLngLats(2.0, 2.0, 3.0, 3.0);
+ points.add(Point.fromLngLat(1.0, 2.0, bbox1));
+ points.add(Point.fromLngLat(3.0, 4.0, bbox2));
+
+ FlattenListOfPoints flatten = new FlattenListOfPoints(points);
+ List reconstructedPoints = flatten.points();
+
+ assertNotNull(reconstructedPoints.get(0).bbox());
+ assertNotNull(reconstructedPoints.get(1).bbox());
+ assertEquals(bbox1, reconstructedPoints.get(0).bbox());
+ assertEquals(bbox2, reconstructedPoints.get(1).bbox());
+ }
+
+ @Test
+ public void constructor_withMixedBoundingBoxes_handlesCorrectly() {
+ List points = new ArrayList<>();
+ BoundingBox bbox = BoundingBox.fromLngLats(0.0, 0.0, 1.0, 1.0);
+ points.add(Point.fromLngLat(1.0, 2.0, bbox));
+ points.add(Point.fromLngLat(3.0, 4.0)); // no bbox
+
+ FlattenListOfPoints flatten = new FlattenListOfPoints(points);
+ List reconstructedPoints = flatten.points();
+
+ assertNotNull(reconstructedPoints.get(0).bbox());
+ assertNull(reconstructedPoints.get(1).bbox());
+ assertEquals(bbox, reconstructedPoints.get(0).bbox());
+ }
+
+ @Test
+ public void toString_withBoundingBox_includesBoundingBoxInOutput() {
+ List points = new ArrayList<>();
+ BoundingBox bbox = BoundingBox.fromLngLats(0.0, 0.0, 1.0, 1.0);
+ points.add(Point.fromLngLat(1.0, 2.0, bbox));
+
+ FlattenListOfPoints flatten = new FlattenListOfPoints(points);
+ String result = flatten.toString();
+
+ assertTrue(result.contains("BoundingBox"));
+ assertTrue(result.contains("1.0"));
+ assertTrue(result.contains("2.0"));
+ }
+}
From ef71786768dd285a83ac18f11b666b31934bfe53 Mon Sep 17 00:00:00 2001
From: Ramon
Date: Fri, 30 Jan 2026 07:50:49 +0200
Subject: [PATCH 24/31] Add size for FlattenListOfPoints
---
.../mapbox/geojson/FlattenListOfPoints.java | 9 +++++
.../geojson/FlattenListOfPointsTest.java | 37 +++++++++++++++++++
2 files changed, 46 insertions(+)
diff --git a/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPoints.java b/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPoints.java
index 51f4d34b7..7442ecc1f 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPoints.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPoints.java
@@ -112,6 +112,15 @@ public double[] getAltitudes() {
return altitudes;
}
+ /**
+ * Returns the total number of points stored in this flattened structure.
+ *
+ * @return the total number of points.
+ */
+ public int size() {
+ return flattenLngLatPoints.length / 2;
+ }
+
/**
* Creates a list of {@link Point}s and returns it.
*
diff --git a/services-geojson/src/test/java/com/mapbox/geojson/FlattenListOfPointsTest.java b/services-geojson/src/test/java/com/mapbox/geojson/FlattenListOfPointsTest.java
index 76af95bfb..0ff05da7f 100644
--- a/services-geojson/src/test/java/com/mapbox/geojson/FlattenListOfPointsTest.java
+++ b/services-geojson/src/test/java/com/mapbox/geojson/FlattenListOfPointsTest.java
@@ -39,6 +39,43 @@ public void constructor_withArrays_nullAltitudes() {
assertNull(flatten.getAltitudes());
}
+ @Test
+ public void size_emptyFlatten_returnsZero() {
+ double[] lngLatArray = new double[]{};
+ FlattenListOfPoints flatten = new FlattenListOfPoints(lngLatArray, null);
+
+ assertEquals(0, flatten.size());
+ }
+
+ @Test
+ public void size_singlePoint_returnsOne() {
+ double[] lngLatArray = new double[]{1.0, 2.0};
+ FlattenListOfPoints flatten = new FlattenListOfPoints(lngLatArray, null);
+
+ assertEquals(1, flatten.size());
+ }
+
+ @Test
+ public void size_multiplePoints_returnsCorrectCount() {
+ double[] lngLatArray = new double[]{1.0, 2.0, 3.0, 4.0, 5.0, 6.0};
+ FlattenListOfPoints flatten = new FlattenListOfPoints(lngLatArray, null);
+
+ assertEquals(3, flatten.size());
+ }
+
+ @Test
+ public void size_fromListOfPoints_returnsCorrectCount() {
+ List points = new ArrayList<>();
+ points.add(Point.fromLngLat(1.0, 2.0));
+ points.add(Point.fromLngLat(3.0, 4.0));
+ points.add(Point.fromLngLat(5.0, 6.0));
+ points.add(Point.fromLngLat(7.0, 8.0));
+
+ FlattenListOfPoints flatten = new FlattenListOfPoints(points);
+
+ assertEquals(4, flatten.size());
+ }
+
@Test
public void constructor_withEmptyList_createsEmptyFlatten() {
List points = new ArrayList<>();
From ffde2c793533c785f164fc0d9db0c518ad9c1152 Mon Sep 17 00:00:00 2001
From: Ramon
Date: Fri, 30 Jan 2026 08:51:34 +0200
Subject: [PATCH 25/31] Add more shifter test for default one
---
.../mapbox/geojson/shifter/ShifterTest.java | 68 +++++++++++++++++++
1 file changed, 68 insertions(+)
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
index b81a46e2c..3940d8fb7 100644
--- a/services-geojson/src/test/java/com/mapbox/geojson/shifter/ShifterTest.java
+++ b/services-geojson/src/test/java/com/mapbox/geojson/shifter/ShifterTest.java
@@ -17,6 +17,8 @@
public class ShifterTest {
+ private static final double DELTA = 0.0001;
+
static class TestCoordinateShifter implements CoordinateShifter {
@Override
public List shiftLonLat(double lon, double lat) {
@@ -89,6 +91,72 @@ public void default_shifter(){
CoordinateShifterManager.setCoordinateShifter(null);
assertTrue(CoordinateShifterManager.isUsingDefaultShifter());
+
+ // Test default shifter behavior - it should return coordinates unchanged
+ CoordinateShifter defaultShifter = CoordinateShifterManager.getCoordinateShifter();
+ assertNotNull(defaultShifter);
+
+ // Test shiftLonLat - should return coordinates unchanged
+ List shiftedLonLat = defaultShifter.shiftLonLat(10.5, 20.3);
+ assertNotNull(shiftedLonLat);
+ assertEquals(2, shiftedLonLat.size());
+ assertEquals(10.5, shiftedLonLat.get(0), DELTA);
+ assertEquals(20.3, shiftedLonLat.get(1), DELTA);
+
+ // Test shiftLonLatAlt with valid altitude - should return all three coordinates
+ List shiftedWithAlt = defaultShifter.shiftLonLatAlt(10.5, 20.3, 30.7);
+ assertNotNull(shiftedWithAlt);
+ assertEquals(3, shiftedWithAlt.size());
+ assertEquals(10.5, shiftedWithAlt.get(0), DELTA);
+ assertEquals(20.3, shiftedWithAlt.get(1), DELTA);
+ assertEquals(30.7, shiftedWithAlt.get(2), DELTA);
+
+ // Test shiftLonLatAlt with NaN altitude - should return only lon, lat
+ List shiftedNoAlt = defaultShifter.shiftLonLatAlt(10.5, 20.3, Double.NaN);
+ assertNotNull(shiftedNoAlt);
+ assertEquals(2, shiftedNoAlt.size());
+ assertEquals(10.5, shiftedNoAlt.get(0), DELTA);
+ assertEquals(20.3, shiftedNoAlt.get(1), DELTA);
+
+ // Test shift with lon, lat - should return array unchanged
+ double[] shiftedArray = defaultShifter.shift(15.2, 25.8);
+ assertNotNull(shiftedArray);
+ assertEquals(2, shiftedArray.length);
+ assertEquals(15.2, shiftedArray[0], DELTA);
+ assertEquals(25.8, shiftedArray[1], DELTA);
+
+ // Test shift with lon, lat, altitude - should return all three
+ double[] shiftedArrayWithAlt = defaultShifter.shift(15.2, 25.8, 35.4);
+ assertNotNull(shiftedArrayWithAlt);
+ assertEquals(3, shiftedArrayWithAlt.length);
+ assertEquals(15.2, shiftedArrayWithAlt[0], DELTA);
+ assertEquals(25.8, shiftedArrayWithAlt[1], DELTA);
+ assertEquals(35.4, shiftedArrayWithAlt[2], DELTA);
+
+ // Test shift with lon, lat, NaN altitude - should return only lon, lat
+ double[] shiftedArrayNoAlt = defaultShifter.shift(15.2, 25.8, Double.NaN);
+ assertNotNull(shiftedArrayNoAlt);
+ assertEquals(2, shiftedArrayNoAlt.length);
+ assertEquals(15.2, shiftedArrayNoAlt[0], DELTA);
+ assertEquals(25.8, shiftedArrayNoAlt[1], DELTA);
+
+ // Test unshiftPoint with Point object - should return point coordinates unchanged
+ Point testPoint = Point.fromLngLat(12.3, 45.6, 78.9);
+ List unshiftedPoint = defaultShifter.unshiftPoint(testPoint);
+ assertNotNull(unshiftedPoint);
+ assertEquals(testPoint.coordinates(), unshiftedPoint);
+
+ // Test unshiftPoint with coordinates list - should return list unchanged
+ List testCoordinates = Arrays.asList(5.5, 6.6, 7.7);
+ List unshiftedCoordinates = defaultShifter.unshiftPoint(testCoordinates);
+ assertNotNull(unshiftedCoordinates);
+ assertEquals(testCoordinates, unshiftedCoordinates);
+
+ // Test unshiftPointArray - should return array unchanged
+ double[] testArray = new double[]{8.8, 9.9, 10.1};
+ double[] unshiftedArray = defaultShifter.unshiftPointArray(testArray);
+ assertNotNull(unshiftedArray);
+ assertArrayEquals(testArray, unshiftedArray, DELTA);
}
@Test
From 2ee2d0402c7bfc5d6763749ebd84eae1fa9fa9b9 Mon Sep 17 00:00:00 2001
From: Ramon
Date: Fri, 30 Jan 2026 09:14:35 +0200
Subject: [PATCH 26/31] Read JSON with extra coordinate values
---
.../com/mapbox/geojson/LineStringTest.java | 32 +++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/services-geojson/src/test/java/com/mapbox/geojson/LineStringTest.java b/services-geojson/src/test/java/com/mapbox/geojson/LineStringTest.java
index b38e64dba..31f3b5786 100644
--- a/services-geojson/src/test/java/com/mapbox/geojson/LineStringTest.java
+++ b/services-geojson/src/test/java/com/mapbox/geojson/LineStringTest.java
@@ -172,6 +172,38 @@ public void fromJson() throws IOException {
assertEquals(Double.NaN, altitudes[1], 0.0);
}
+ @Test
+ public void fromJsonWithExtraValuesAreIgnored() throws IOException {
+ final String json = "{\"type\": \"LineString\"," +
+ " \"coordinates\": [[ 100, 0, 1000, 2, 3], [101, 1]]} ";
+ LineString geo = LineString.fromJson(json);
+ assertEquals("LineString", geo.type());
+ List points = geo.coordinates();
+ Point firstPoint = points.get(0);
+ assertEquals(100.0, firstPoint.longitude(), 0.0);
+ assertEquals(0.0, firstPoint.latitude(), 0.0);
+ assertTrue(firstPoint.hasAltitude());
+ assertEquals(1000.0, firstPoint.altitude(), 0.0);
+
+ Point secondPoint = points.get(1);
+ assertEquals(101.0, secondPoint.longitude(), 0.0);
+ assertEquals(1.0, secondPoint.latitude(), 0.0);
+ assertFalse(secondPoint.hasAltitude());
+
+ double[] coordinates = geo.flattenCoordinates().getFlattenLngLatArray();
+ assertEquals(4, geo.flattenCoordinates().getFlattenLngLatArray().length);
+ double[] altitudes = geo.flattenCoordinates().getAltitudes();
+ assertEquals(100.0, coordinates[0], 0.0);
+ assertEquals(0.0, coordinates[1], 0.0);
+ assertNotNull(altitudes);
+ assertEquals(1000.0, altitudes[0], 0.0);
+
+ // Second point
+ assertEquals(101.0, coordinates[2], 0.0);
+ assertEquals(1.0, coordinates[3], 0.0);
+ assertEquals(Double.NaN, altitudes[1], 0.0);
+ }
+
@Test
public void toJson() throws IOException {
final String json = "{\"type\": \"LineString\"," +
From a3c8f4c3a48bb3b93be25a03ee64f1b2b2c98e5d Mon Sep 17 00:00:00 2001
From: Ramon
Date: Fri, 30 Jan 2026 09:32:42 +0200
Subject: [PATCH 27/31] Test initial capacity for the line string JSON logic
---
.../com/mapbox/geojson/LineStringTest.java | 79 +++++++++++++++++++
1 file changed, 79 insertions(+)
diff --git a/services-geojson/src/test/java/com/mapbox/geojson/LineStringTest.java b/services-geojson/src/test/java/com/mapbox/geojson/LineStringTest.java
index 31f3b5786..982f86d97 100644
--- a/services-geojson/src/test/java/com/mapbox/geojson/LineStringTest.java
+++ b/services-geojson/src/test/java/com/mapbox/geojson/LineStringTest.java
@@ -204,6 +204,85 @@ public void fromJsonWithExtraValuesAreIgnored() throws IOException {
assertEquals(Double.NaN, altitudes[1], 0.0);
}
+ /**
+ * Test to trigger reading a JSON that needs to extend the array capacity while parsing.
+ * {@link FlattenListOfPointsTypeAdapter#INITIAL_CAPACITY}.
+ *
+ * @throws IOException
+ */
+ @SuppressWarnings("JavadocReference")
+ @Test
+ public void readJsonWithMoreThan_INITIAL_CAPACITY() throws IOException {
+ final StringBuilder json = new StringBuilder("{\"type\": \"LineString\"," +
+ " \"coordinates\": [");
+ int totalPoints = 100 * 2;
+ for (int i = 0; i < totalPoints; i++) {
+ json.append("[ ");
+ double lng = 100 + i;
+ double lat = i;
+ json.append(lng).append(",");
+ json.append(lat);
+ // Only add altitude for half of the points
+ if (i >= 100) {
+ double alt = 1000 + i;
+ json.append(",").append(alt);
+ }
+ json.append("], ");
+ }
+ // Trim the last `, `
+ json.deleteCharAt(json.length() - 2);
+ json.append("]}");
+
+ LineString geo = LineString.fromJson(json.toString());
+ assertEquals("LineString", geo.type());
+ List points = geo.coordinates();
+ assertEquals(totalPoints, points.size());
+
+ // Verify the list of points contents
+ for (int i = 0; i < totalPoints; i++) {
+ Point point = points.get(i);
+ double expectedLng = 100 + i;
+ double expectedLat = i;
+ assertEquals(expectedLng, point.longitude(), DELTA);
+ assertEquals(expectedLat, point.latitude(), DELTA);
+
+ if (i >= 100) {
+ // Second half should have altitude
+ assertTrue(point.hasAltitude());
+ double expectedAlt = 1000 + i;
+ assertEquals(expectedAlt, point.altitude(), DELTA);
+ } else {
+ // First half should not have altitude
+ assertFalse(point.hasAltitude());
+ }
+ }
+
+ FlattenListOfPoints flattenListOfPoints = geo.flattenCoordinates();
+ assertEquals(totalPoints, flattenListOfPoints.size());
+ double[] flattenLngLatArray = flattenListOfPoints.getFlattenLngLatArray();
+ assertEquals(totalPoints * 2, flattenLngLatArray.length);
+ double[] altitudes = flattenListOfPoints.getAltitudes();
+ assertNotNull(altitudes);
+ assertEquals(totalPoints, altitudes.length);
+
+ // Verify the contents of flattenLngLatArray and altitudes
+ for (int i = 0; i < totalPoints; i++) {
+ double expectedLng = 100 + i;
+ double expectedLat = i;
+ assertEquals(expectedLng, flattenLngLatArray[i * 2], DELTA);
+ assertEquals(expectedLat, flattenLngLatArray[i * 2 + 1], DELTA);
+
+ if (i >= 100) {
+ // Second half should have altitude
+ double expectedAlt = 1000 + i;
+ assertEquals(expectedAlt, altitudes[i], DELTA);
+ } else {
+ // First half should have NaN for altitude
+ assertTrue(Double.isNaN(altitudes[i]));
+ }
+ }
+ }
+
@Test
public void toJson() throws IOException {
final String json = "{\"type\": \"LineString\"," +
From b978c918d2bf7cdb17a7998ef1c1e9c1b307cde0 Mon Sep 17 00:00:00 2001
From: Ramon
Date: Fri, 30 Jan 2026 10:43:53 +0200
Subject: [PATCH 28/31] Changes to docs and minor API alignments
---
.../geojson/BaseGeometryTypeAdapter.java | 33 +++++++++----------
.../FlattenListOfPointsTypeAdapter.java | 14 +++++---
.../geojson/FlattenedCoordinateContainer.java | 20 +++++++++++
.../java/com/mapbox/geojson/LineString.java | 18 ++--------
.../java/com/mapbox/geojson/MultiPoint.java | 2 +-
.../main/java/com/mapbox/geojson/Point.java | 2 +-
.../mapbox/geojson/utils/PolylineUtils.java | 9 ++---
.../com/mapbox/geojson/LineStringTest.java | 16 +++++++++
.../com/mapbox/geojson/MultiPointTest.java | 16 +++++++++
.../geojson/utils/PolylineUtilsTest.java | 7 ++++
10 files changed, 93 insertions(+), 44 deletions(-)
diff --git a/services-geojson/src/main/java/com/mapbox/geojson/BaseGeometryTypeAdapter.java b/services-geojson/src/main/java/com/mapbox/geojson/BaseGeometryTypeAdapter.java
index 56d1d726f..9c435869c 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/BaseGeometryTypeAdapter.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/BaseGeometryTypeAdapter.java
@@ -18,6 +18,7 @@
*
* @param Geometry
* @param Type of coordinates
+ * @param The type of coordinates adapter
* @since 4.6.0
*/
@Keep
@@ -25,17 +26,20 @@ abstract class BaseGeometryTypeAdapter extends TypeAdapter {
private volatile TypeAdapter stringAdapter;
private volatile TypeAdapter boundingBoxAdapter;
- private volatile TypeAdapter coordinatesAdapter;
+ private final BaseCoordinatesTypeAdapter coordinatesAdapter;
private final Gson gson;
- BaseGeometryTypeAdapter(Gson gson, TypeAdapter coordinatesAdapter) {
+ BaseGeometryTypeAdapter(Gson gson, BaseCoordinatesTypeAdapter coordinatesAdapter) {
+ if (coordinatesAdapter == null) {
+ throw new GeoJsonException("Coordinates type adapter is null");
+ }
this.gson = gson;
this.coordinatesAdapter = coordinatesAdapter;
this.boundingBoxAdapter = new BoundingBoxTypeAdapter();
}
- public void writeCoordinateContainerPrimitive(
+ public void writeFlattenedCoordinateContainer(
JsonWriter jsonWriter,
FlattenedCoordinateContainer object
) throws IOException {
@@ -45,15 +49,7 @@ public void writeCoordinateContainerPrimitive(
}
writeCommon(jsonWriter, object);
jsonWriter.name("coordinates");
- if (object.coordinates() == null) {
- jsonWriter.nullValue();
- } else {
- TypeAdapter coordinatesAdapter = this.coordinatesAdapter;
- if (coordinatesAdapter == null) {
- throw new GeoJsonException("Coordinates type adapter is null");
- }
- coordinatesAdapter.write(jsonWriter, object.flattenCoordinates());
- }
+ coordinatesAdapter.write(jsonWriter, object.flattenCoordinates());
jsonWriter.endObject();
}
@@ -70,17 +66,20 @@ public void writeCoordinateContainer(JsonWriter jsonWriter, CoordinateContainer<
if (object.coordinates() == null) {
jsonWriter.nullValue();
} else {
- TypeAdapter coordinatesAdapter = this.coordinatesAdapter;
- if (coordinatesAdapter == null) {
- throw new GeoJsonException("Coordinates type adapter is null");
- }
coordinatesAdapter.write(jsonWriter, object.coordinates());
}
jsonWriter.endObject();
}
- private void writeCommon(JsonWriter jsonWriter, CoordinateContainer object) throws IOException {
+ /**
+ * Write the common part of the coordinate container: "type" and "bbox".
+ */
+ private void writeCommon(
+ JsonWriter jsonWriter,
+ @SuppressWarnings("rawtypes")
+ CoordinateContainer object
+ ) throws IOException {
jsonWriter.beginObject();
jsonWriter.name("type");
if (object.type() == null) {
diff --git a/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPointsTypeAdapter.java b/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPointsTypeAdapter.java
index e12333789..4c0c3a700 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPointsTypeAdapter.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/FlattenListOfPointsTypeAdapter.java
@@ -10,9 +10,8 @@
import java.io.IOException;
/**
- * Type Adapter to serialize/deserialize List<Point> into/from two dimentional double array.
- *
- * @since 4.6.0
+ * Type Adapter to serialize/deserialize {@link FlattenListOfPoints} into/from two dimensional
+ * JSON array.
*/
@Keep
class FlattenListOfPointsTypeAdapter extends BaseCoordinatesTypeAdapter {
@@ -21,17 +20,22 @@ class FlattenListOfPointsTypeAdapter extends BaseCoordinatesTypeAdapter
+ * This interface extends {@link CoordinateContainer} to provide access to coordinates in their
+ * standard form (typically as {@link Point} objects) while also offering a flattened form
+ * optimized for low-level operations.
+ *
+ * @param the standard coordinate container type (e.g., {@code List})
+ * @param the flattened coordinate representation type (e.g., {@link FlattenListOfPoints})
+ */
@Keep
interface FlattenedCoordinateContainer extends CoordinateContainer {
+ /**
+ * Returns the flattened coordinate representation of this geometry.
+ *
+ * The flattened form stores coordinates in primitive arrays, which provides better performance
+ * for operations that require direct array access, particularly in JNI contexts.
+ *
+ * @return the flattened coordinate representation.
+ */
P flattenCoordinates();
}
diff --git a/services-geojson/src/main/java/com/mapbox/geojson/LineString.java b/services-geojson/src/main/java/com/mapbox/geojson/LineString.java
index f81a4bd49..a033a4f36 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/LineString.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/LineString.java
@@ -95,20 +95,6 @@ public static LineString fromLngLats(@NonNull MultiPoint multiPoint) {
return new LineString(TYPE, null, multiPoint.coordinates());
}
- /**
- * Create a new instance of this class by defining a {@link MultiPoint} object and passing. The
- * multipoint object should comply with the GeoJson specifications described in the documentation.
- *
- * @param multiPoint which will make up the LineString geometry
- * @param bbox optionally include a bbox definition as a double array
- * @return a new instance of this class defined by the values passed inside this static factory
- * method
- * @since 3.0.0
- */
- public static LineString fromLngLats(@NonNull MultiPoint multiPoint, @Nullable BoundingBox bbox) {
- return new LineString(TYPE, bbox, multiPoint.coordinates());
- }
-
/**
* Create a new instance of this class by defining a list of {@link Point}s which follow the
* correct specifications described in the Point documentation. Note that there should not be any
@@ -287,7 +273,7 @@ public String toJson() {
* @since 1.0.0
*/
public String toPolyline(int precision) {
- return PolylineUtils.encode(flattenListOfPoints, precision);
+ return PolylineUtils.encode(flattenListOfPoints.getFlattenLngLatArray(), precision);
}
/**
@@ -345,7 +331,7 @@ static final class GsonTypeAdapter extends
@Override
public void write(JsonWriter jsonWriter, LineString object) throws IOException {
- writeCoordinateContainerPrimitive(jsonWriter, object);
+ writeFlattenedCoordinateContainer(jsonWriter, object);
}
@Override
diff --git a/services-geojson/src/main/java/com/mapbox/geojson/MultiPoint.java b/services-geojson/src/main/java/com/mapbox/geojson/MultiPoint.java
index 5bd16bb7c..8eb69a104 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/MultiPoint.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/MultiPoint.java
@@ -232,7 +232,7 @@ static final class GsonTypeAdapter extends
@Override
public void write(JsonWriter jsonWriter, MultiPoint object) throws IOException {
- writeCoordinateContainerPrimitive(jsonWriter, object);
+ writeFlattenedCoordinateContainer(jsonWriter, object);
}
@Override
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 1ee3b9a18..60e463898 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/Point.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/Point.java
@@ -385,7 +385,7 @@ static final class GsonTypeAdapter extends
@Override
@SuppressWarnings("unchecked")
public void write(JsonWriter jsonWriter, Point object) throws IOException {
- writeCoordinateContainerPrimitive(jsonWriter, object);
+ writeFlattenedCoordinateContainer(jsonWriter, object);
}
@Override
diff --git a/services-geojson/src/main/java/com/mapbox/geojson/utils/PolylineUtils.java b/services-geojson/src/main/java/com/mapbox/geojson/utils/PolylineUtils.java
index ecdb35c14..04e6c4f19 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/utils/PolylineUtils.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/utils/PolylineUtils.java
@@ -181,16 +181,18 @@ public static String encode(@NonNull final List path, int precision) {
}
/**
- * Encodes a {@link FlattenListOfPoints} into an encoded path string.
+ * Encodes an array of doubles representing a line geometry with flattened points into an encoded
+ * path string.
*
- * @param flattenListOfPoints a {@link FlattenListOfPoints} making up the line
+ * @param flattenLngLatArray an array of doubles representing a line geometry with flattened
+ * points in the form: [lng1, lat1, lng2, lat2, ...]
* @param precision OSRMv4 uses 6, OSRMv5 and Google uses 5
* @return a String representing a path string
*/
@NonNull
public static String encode(
@NonNull
- final FlattenListOfPoints flattenListOfPoints,
+ double[] flattenLngLatArray,
int precision
) {
long lastLat = 0;
@@ -201,7 +203,6 @@ public static String encode(
// OSRM uses precision=6, the default Polyline spec divides by 1E5, capping at precision=5
double factor = Math.pow(10, precision);
- double[] flattenLngLatArray = flattenListOfPoints.getFlattenLngLatArray();
for (int i = 0; i < flattenLngLatArray.length / 2; i++) {
double longitude = flattenLngLatArray[i * 2];
double latitude = flattenLngLatArray[i * 2 + 1];
diff --git a/services-geojson/src/test/java/com/mapbox/geojson/LineStringTest.java b/services-geojson/src/test/java/com/mapbox/geojson/LineStringTest.java
index 982f86d97..fd4960122 100644
--- a/services-geojson/src/test/java/com/mapbox/geojson/LineStringTest.java
+++ b/services-geojson/src/test/java/com/mapbox/geojson/LineStringTest.java
@@ -292,6 +292,22 @@ public void toJson() throws IOException {
compareJson(geoJsonString, json);
}
+ @Test
+ public void toJsonEmpty() throws IOException {
+ final String json = "{\"type\": \"LineString\", \"coordinates\": [ ]} ";
+ LineString geo = LineString.fromLngLats(new ArrayList<>());
+ String geoJsonString = geo.toJson();
+ compareJson(geoJsonString, json);
+ }
+
+ @Test
+ public void fromEmptyJson() throws IOException {
+ final String json = "{\"type\": \"LineString\", \"coordinates\": [ ]} ";
+ LineString geo = LineString.fromJson(json);
+ String geoJsonString = geo.toJson();
+ compareJson(geoJsonString, json);
+ }
+
@Test
public void fromJson_coordinatesPresent() throws Exception {
thrown.expect(NullPointerException.class);
diff --git a/services-geojson/src/test/java/com/mapbox/geojson/MultiPointTest.java b/services-geojson/src/test/java/com/mapbox/geojson/MultiPointTest.java
index dee5add5b..f3c95a60b 100644
--- a/services-geojson/src/test/java/com/mapbox/geojson/MultiPointTest.java
+++ b/services-geojson/src/test/java/com/mapbox/geojson/MultiPointTest.java
@@ -128,6 +128,22 @@ public void toJson() throws IOException {
MultiPoint geo = MultiPoint.fromJson(json);
compareJson(json, geo.toJson());
}
+ @Test
+ public void toJsonEmpty() throws IOException {
+ final String json = "{ \"type\": \"MultiPoint\"," +
+ "\"coordinates\": [ ] } ";
+ MultiPoint geo = MultiPoint.fromLngLats(new ArrayList<>());
+ String geoJson = geo.toJson();
+ compareJson(json, geoJson);
+ }
+
+ @Test
+ public void fromJsonEmpty() throws IOException {
+ final String json = "{ \"type\": \"MultiPoint\",\"coordinates\": [ ] } ";
+ MultiPoint geo = MultiPoint.fromJson(json);
+ String geoJson = geo.toJson();
+ compareJson(json, geoJson);
+ }
@Test
public void fromJson_coordinatesPresent() throws Exception {
diff --git a/services-geojson/src/test/java/com/mapbox/geojson/utils/PolylineUtilsTest.java b/services-geojson/src/test/java/com/mapbox/geojson/utils/PolylineUtilsTest.java
index bd0a026b5..47f8f88fb 100644
--- a/services-geojson/src/test/java/com/mapbox/geojson/utils/PolylineUtilsTest.java
+++ b/services-geojson/src/test/java/com/mapbox/geojson/utils/PolylineUtilsTest.java
@@ -1,12 +1,14 @@
package com.mapbox.geojson.utils;
import static com.mapbox.geojson.utils.PolylineUtils.decode;
+import static com.mapbox.geojson.utils.PolylineUtils.decodeToFlattenListOfPoints;
import static com.mapbox.geojson.utils.PolylineUtils.encode;
import static com.mapbox.geojson.utils.PolylineUtils.simplify;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import com.mapbox.geojson.FlattenListOfPoints;
import com.mapbox.geojson.LineString;
import com.mapbox.geojson.Point;
import com.mapbox.geojson.TestUtils;
@@ -114,12 +116,17 @@ public void decode_neverReturnsNullButRatherAnEmptyList() throws Exception {
List path = decode("", PRECISION_5);
assertNotNull(path);
assertEquals(0, path.size());
+ double[] pathArray = decodeToFlattenListOfPoints("", PRECISION_5);
+ assertNotNull(pathArray);
+ assertEquals(0, pathArray.length);
}
@Test
public void encode_neverReturnsNull() throws Exception {
String encodedString = encode(new ArrayList(), PRECISION_6);
assertNotNull(encodedString);
+ String encodedArrayString = encode(new double[]{}, PRECISION_6);
+ assertNotNull(encodedArrayString);
}
@Test
From 6750ed330e9c9445d8705f2199451b72ffab17ac Mon Sep 17 00:00:00 2001
From: Ramon
Date: Fri, 30 Jan 2026 14:30:38 +0200
Subject: [PATCH 29/31] Added changelog entries
---
CHANGELOG.md | 3 +++
1 file changed, 3 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b1d098210..ae8779c27 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,9 @@ Mapbox welcomes participation and contributions from everyone.
### main
- Added `DirectionsRefreshResponse#fromJson(Reader)`, a static factory method that deserializes a `DirectionsRefreshResponse` from a `java.io.Reader`.
+- Added `FlattenListOfPoints` to hold a list of points in a more memory-efficient way.
+- Deprecate `LineString#coordinates()` and `Point#coordinates()`. It's encouraged to use the new `flattenCoordinates()` methods.
+
### v7.9.0 - November 20, 2025
From 1007965f7926caeef441e347848b2317102faead Mon Sep 17 00:00:00 2001
From: Ramon
Date: Fri, 30 Jan 2026 15:31:13 +0200
Subject: [PATCH 30/31] Squeezed a few more bytes from Point
---
.../main/java/com/mapbox/geojson/Point.java | 71 +++++++++++--------
1 file changed, 40 insertions(+), 31 deletions(-)
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 60e463898..4b151d0d3 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/Point.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/Point.java
@@ -13,7 +13,6 @@
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -59,8 +58,9 @@ public final class Point implements FlattenedCoordinateContainer, d
@Nullable
private final BoundingBox bbox;
- @NonNull
- private final double[] coordinates;
+ private final double longitude;
+ private final double latitude;
+ private final double altitude;
/**
* Create a new instance of this class by passing in a formatted valid JSON String. If you are
@@ -183,7 +183,9 @@ static Point fromLngLat(@NonNull double[] coords) {
if (coordinates.length == 0) {
throw new NullPointerException("Null coordinates");
}
- this.coordinates = coordinates;
+ longitude = coordinates[0];
+ latitude = coordinates[1];
+ altitude = coordinates.length > 2 ? coordinates[2] : Double.NaN;
}
/**
@@ -196,7 +198,7 @@ static Point fromLngLat(@NonNull double[] coords) {
* @since 3.0.0
*/
public double longitude() {
- return coordinates[0];
+ return longitude;
}
/**
@@ -209,7 +211,7 @@ public double longitude() {
* @since 3.0.0
*/
public double latitude() {
- return coordinates[1];
+ return latitude;
}
/**
@@ -222,10 +224,7 @@ public double latitude() {
* @since 3.0.0
*/
public double altitude() {
- if (coordinates.length < 3) {
- return Double.NaN;
- }
- return coordinates[2];
+ return altitude;
}
/**
@@ -283,9 +282,15 @@ public BoundingBox bbox() {
@Override
@Deprecated
public List coordinates() {
- ArrayList list = new ArrayList<>(coordinates.length);
- for (double coordinate : coordinates) {
- list.add(coordinate);
+ int size = 2;
+ if (!Double.isNaN(altitude())) {
+ size++;
+ }
+ ArrayList list = new ArrayList<>(size);
+ list.add(longitude);
+ list.add(latitude);
+ if (!Double.isNaN(altitude())) {
+ list.add(altitude);
}
return list;
}
@@ -300,7 +305,11 @@ public List coordinates() {
*/
@Override
public double[] flattenCoordinates() {
- return coordinates;
+ if (Double.isNaN(altitude)) {
+ return new double[]{longitude, latitude};
+ } else {
+ return new double[]{longitude, latitude, altitude};
+ }
}
/**
@@ -331,14 +340,14 @@ public static TypeAdapter typeAdapter(Gson gson) {
@Override
public String toString() {
String coordinatesStr;
- if (coordinates.length > 2) {
+ if (Double.isNaN(altitude)) {
+ coordinatesStr = "[" + longitude + ", " + latitude + "]";
+ } else {
coordinatesStr = "["
- + this.coordinates[0] + ", "
- + this.coordinates[1] + ", "
- + this.coordinates[2]
+ + longitude + ", "
+ + latitude + ", "
+ + altitude
+ "]";
- } else {
- coordinatesStr = "[" + this.coordinates[0] + ", " + this.coordinates[1] + "]";
}
return "Point{"
+ "type=" + type + ", "
@@ -353,21 +362,21 @@ public boolean equals(Object o) {
return false;
}
Point point = (Point) o;
- return Objects.equals(type, point.type)
- && Objects.equals(bbox, point.bbox)
- && Objects.deepEquals(coordinates, point.coordinates);
+ return Double.compare(longitude, point.longitude) == 0
+ && Double.compare(latitude, point.latitude) == 0
+ && Double.compare(altitude, point.altitude) == 0
+ && Objects.equals(type, point.type)
+ && Objects.equals(bbox, point.bbox);
}
@Override
public int hashCode() {
- int hashCode = 1;
- hashCode *= 1000003;
- hashCode ^= type.hashCode();
- hashCode *= 1000003;
- hashCode ^= (bbox == null) ? 0 : bbox.hashCode();
- hashCode *= 1000003;
- hashCode ^= Arrays.hashCode(coordinates);
- return hashCode;
+ int result = type.hashCode();
+ result = 31 * result + Objects.hashCode(bbox);
+ result = 31 * result + Double.hashCode(longitude);
+ result = 31 * result + Double.hashCode(latitude);
+ result = 31 * result + Double.hashCode(altitude);
+ return result;
}
/**
From 164c42d4a3c1cd3f1bff3e0612cb090b04c75041 Mon Sep 17 00:00:00 2001
From: Ramon
Date: Fri, 30 Jan 2026 16:29:07 +0200
Subject: [PATCH 31/31] Revert "Squeezed a few more bytes from Point"
This reverts commit 1007965f7926caeef441e347848b2317102faead.
---
.../main/java/com/mapbox/geojson/Point.java | 71 ++++++++-----------
1 file changed, 31 insertions(+), 40 deletions(-)
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 4b151d0d3..60e463898 100644
--- a/services-geojson/src/main/java/com/mapbox/geojson/Point.java
+++ b/services-geojson/src/main/java/com/mapbox/geojson/Point.java
@@ -13,6 +13,7 @@
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -58,9 +59,8 @@ public final class Point implements FlattenedCoordinateContainer, d
@Nullable
private final BoundingBox bbox;
- private final double longitude;
- private final double latitude;
- private final double altitude;
+ @NonNull
+ private final double[] coordinates;
/**
* Create a new instance of this class by passing in a formatted valid JSON String. If you are
@@ -183,9 +183,7 @@ static Point fromLngLat(@NonNull double[] coords) {
if (coordinates.length == 0) {
throw new NullPointerException("Null coordinates");
}
- longitude = coordinates[0];
- latitude = coordinates[1];
- altitude = coordinates.length > 2 ? coordinates[2] : Double.NaN;
+ this.coordinates = coordinates;
}
/**
@@ -198,7 +196,7 @@ static Point fromLngLat(@NonNull double[] coords) {
* @since 3.0.0
*/
public double longitude() {
- return longitude;
+ return coordinates[0];
}
/**
@@ -211,7 +209,7 @@ public double longitude() {
* @since 3.0.0
*/
public double latitude() {
- return latitude;
+ return coordinates[1];
}
/**
@@ -224,7 +222,10 @@ public double latitude() {
* @since 3.0.0
*/
public double altitude() {
- return altitude;
+ if (coordinates.length < 3) {
+ return Double.NaN;
+ }
+ return coordinates[2];
}
/**
@@ -282,15 +283,9 @@ public BoundingBox bbox() {
@Override
@Deprecated
public List coordinates() {
- int size = 2;
- if (!Double.isNaN(altitude())) {
- size++;
- }
- ArrayList list = new ArrayList<>(size);
- list.add(longitude);
- list.add(latitude);
- if (!Double.isNaN(altitude())) {
- list.add(altitude);
+ ArrayList list = new ArrayList<>(coordinates.length);
+ for (double coordinate : coordinates) {
+ list.add(coordinate);
}
return list;
}
@@ -305,11 +300,7 @@ public List coordinates() {
*/
@Override
public double[] flattenCoordinates() {
- if (Double.isNaN(altitude)) {
- return new double[]{longitude, latitude};
- } else {
- return new double[]{longitude, latitude, altitude};
- }
+ return coordinates;
}
/**
@@ -340,14 +331,14 @@ public static TypeAdapter typeAdapter(Gson gson) {
@Override
public String toString() {
String coordinatesStr;
- if (Double.isNaN(altitude)) {
- coordinatesStr = "[" + longitude + ", " + latitude + "]";
- } else {
+ if (coordinates.length > 2) {
coordinatesStr = "["
- + longitude + ", "
- + latitude + ", "
- + altitude
+ + this.coordinates[0] + ", "
+ + this.coordinates[1] + ", "
+ + this.coordinates[2]
+ "]";
+ } else {
+ coordinatesStr = "[" + this.coordinates[0] + ", " + this.coordinates[1] + "]";
}
return "Point{"
+ "type=" + type + ", "
@@ -362,21 +353,21 @@ public boolean equals(Object o) {
return false;
}
Point point = (Point) o;
- return Double.compare(longitude, point.longitude) == 0
- && Double.compare(latitude, point.latitude) == 0
- && Double.compare(altitude, point.altitude) == 0
- && Objects.equals(type, point.type)
- && Objects.equals(bbox, point.bbox);
+ return Objects.equals(type, point.type)
+ && Objects.equals(bbox, point.bbox)
+ && Objects.deepEquals(coordinates, point.coordinates);
}
@Override
public int hashCode() {
- int result = type.hashCode();
- result = 31 * result + Objects.hashCode(bbox);
- result = 31 * result + Double.hashCode(longitude);
- result = 31 * result + Double.hashCode(latitude);
- result = 31 * result + Double.hashCode(altitude);
- return result;
+ int hashCode = 1;
+ hashCode *= 1000003;
+ hashCode ^= type.hashCode();
+ hashCode *= 1000003;
+ hashCode ^= (bbox == null) ? 0 : bbox.hashCode();
+ hashCode *= 1000003;
+ hashCode ^= Arrays.hashCode(coordinates);
+ return hashCode;
}
/**