Skip to content

Commit

Permalink
Empty checks to prevent breaking when receiving empty openrouteservic…
Browse files Browse the repository at this point in the history
…e collections (#22)

* Empty checks to prevent breaking when receiving empty collections from openrouteservice, and tests

* Bump version

* Unit test for Empty Feature Collection

---------

Co-authored-by: Dhi13man <dhiman.seal@groww.in>
  • Loading branch information
Dhi13man and DhimanSeal-Groww authored Apr 7, 2024
1 parent 8f68a19 commit 3bbdd7c
Show file tree
Hide file tree
Showing 4 changed files with 221 additions and 26 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# Releases

## [1.2.5] - 6th Feb, 2024
## [1.2.6] - 6th April, 2024

- Empty checks to prevent breaking when receiving empty collections from openrouteservice. Fixes [Issue #21](https://github.com/Dhi13man/open_route_service/issues/21).
- Unit Tests for empty GeoJSON Feature serialisation/deserialisation added.

## [1.2.5] - 29th March, 2024

- Fixed broken compatibility with `geodart` GeoJSON serialisation/deserialisation as reported in [Issue #19](https://github.com/Dhi13man/open_route_service/issues/19).
- Using `geojson` package as a dev dependency, for unit testing compatibility with GeoJSON.
Expand Down
69 changes: 48 additions & 21 deletions lib/src/models/geojson_feature_models.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,14 @@ class GeoJsonFeatureCollection {
/// The 'bbox' key is converted to list of 4 [double]s implying 2 coordinates.
Map<String, dynamic> toJson() => <String, dynamic>{
'type': 'FeatureCollection',
'bbox': <double>[
bbox[0].longitude,
bbox[0].latitude,
bbox[1].longitude,
bbox[1].latitude,
],
'bbox': bbox.length == 2
? <double>[
bbox[0].longitude,
bbox[0].latitude,
bbox[1].longitude,
bbox[1].latitude,
]
: <double>[],
'features': features
.map<Map<String, dynamic>>(
(GeoJsonFeature feature) => feature.toJson(),
Expand Down Expand Up @@ -156,21 +158,31 @@ class GeoJsonFeatureGeometry {
factory GeoJsonFeatureGeometry.fromJson(Map<String, dynamic> json) {
final dynamic type = json['type'];
final dynamic coordinates = json['coordinates'];
if (coordinates is List<dynamic>) {
final List<dynamic> dynamicList = coordinates;
if (dynamicList.first is List<dynamic>) {
final List<List<dynamic>> dynamicListList = dynamicList
.map<List<dynamic>>((dynamic c) => c as List<dynamic>)
.toList();
// For Isochrone feature geometry, it has a list of list of coordinates.
if (dynamicListList.first.first is List<dynamic>) {
return _generateIsochroneGeometry(type, dynamicListList);
}
final bool isNotListOrIsEmptyList =
coordinates is! List<dynamic> || coordinates.isEmpty;
if (isNotListOrIsEmptyList) {
return _generateEmptyGeometry(type);
}

final List<dynamic> dynamicList = coordinates;
final bool isFirstElementList =
dynamicList.isNotEmpty && dynamicList.first is List<dynamic>;
if (isFirstElementList) {
final List<List<dynamic>> dynamicListList = dynamicList
.map<List<dynamic>>((dynamic c) => c as List<dynamic>)
.toList();
// For Isochrone feature geometry, it has a list of list of coordinates.
final bool isFirstFirstElementList = dynamicListList.first.isNotEmpty &&
dynamicListList.first.first is List<dynamic>;
if (isFirstFirstElementList) {
return _generateIsochroneGeometry(type, dynamicListList);
}

// For direction feature geometry, it has a list of coordinates.
if (dynamicListList.first.first is num) {
return _generateDirectionGeometry(type, dynamicListList);
}
// For direction feature geometry, it has a list of coordinates.
final bool isFirstFirstElementNum = dynamicListList.first.isNotEmpty &&
dynamicListList.first.first is num;
if (isFirstFirstElementNum) {
return _generateDirectionGeometry(type, dynamicListList);
}
}

Expand Down Expand Up @@ -226,6 +238,12 @@ class GeoJsonFeatureGeometry {
'type': type,
'coordinates': coordinates.first.first.toList(),
};

case GsonFeatureGeometryCoordinatesType.empty:
return <String, dynamic>{
'type': type,
'coordinates': <List<double>>[],
};
}
}

Expand Down Expand Up @@ -293,6 +311,15 @@ class GeoJsonFeatureGeometry {
internalType: GsonFeatureGeometryCoordinatesType.single,
);
}

/// For Point feature geometry, it has a single coordinate.
static _generateEmptyGeometry(String type) {
return GeoJsonFeatureGeometry(
type: type,
coordinates: <List<ORSCoordinate>>[<ORSCoordinate>[]],
internalType: GsonFeatureGeometryCoordinatesType.empty,
);
}
}

enum GsonFeatureGeometryCoordinatesType { listList, list, single }
enum GsonFeatureGeometryCoordinatesType { listList, list, single, empty }
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: open_route_service
description: An encapsulation made around openrouteservice APIs, for Dart and Flutter projects, to easily generate Routes and their data.
version: 1.2.5
version: 1.2.6
repository: https://github.com/dhi13man/open_route_service/
homepage: https://github.com/dhi13man/open_route_service/
issue_tracker: https://github.com/Dhi13man/open_route_service/issues
Expand Down
169 changes: 166 additions & 3 deletions test/miscellaneous/geojson_tests.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,98 @@ import 'package:open_route_service/open_route_service.dart';
import 'package:test/test.dart';

void geoJsonTests() {
test('Test GeoJSON Coordinate Point serialization', () {
test('Test GeoJSON Feature Collection serialization', () {
// Arrange
final List<ORSCoordinate> coordinates = <ORSCoordinate>[
ORSCoordinate(latitude: 3.0, longitude: 0.0),
ORSCoordinate(latitude: 3.0, longitude: 0.0)
];
final GeoJsonFeatureCollection feature = GeoJsonFeatureCollection(
bbox: coordinates,
features: <GeoJsonFeature>[],
);

// Act
final Map<String, dynamic> result = feature.toJson();

// Assert
expect(
result,
<String, dynamic>{
'type': 'FeatureCollection',
'bbox': <double>[0.0, 3.0, 0.0, 3.0],
'features': <Map<String, dynamic>>[]
},
);
});

test('Test GeoJSON Feature Collection deserialization', () {
// Arrange
final Map<String, dynamic> json = <String, dynamic>{
'type': 'FeatureCollection',
'bbox': <double>[0.0, 3.0, 0.0, 1.5],
'features': <Map<String, dynamic>>[]
};

// Act
final GeoJsonFeatureCollection result =
GeoJsonFeatureCollection.fromJson(json);

// Assert
final GeoJsonFeatureCollection expected = GeoJsonFeatureCollection(
bbox: <ORSCoordinate>[
ORSCoordinate(latitude: 3.0, longitude: 0.0),
ORSCoordinate(latitude: 1.5, longitude: 0.0)
],
features: <GeoJsonFeature>[],
);
expect(result.bbox, expected.bbox);
expect(result.features, expected.features);
});

test('Test empty GeoJSON Feature Collection serialization', () {
// Arrange
final GeoJsonFeatureCollection feature = GeoJsonFeatureCollection(
bbox: <ORSCoordinate>[],
features: <GeoJsonFeature>[],
);

// Act
final Map<String, dynamic> result = feature.toJson();

// Assert
expect(
result,
<String, dynamic>{
'type': 'FeatureCollection',
'bbox': <double>[],
'features': <Map<String, dynamic>>[],
},
);
});

test('Test empty GeoJSON Feature Collection deserialization', () {
// Arrange
final Map<String, dynamic> json = <String, dynamic>{
'type': 'FeatureCollection',
'bbox': <double>[],
'features': <Map<String, dynamic>>[],
};

// Act
final GeoJsonFeatureCollection result =
GeoJsonFeatureCollection.fromJson(json);

// Assert
final GeoJsonFeatureCollection expected = GeoJsonFeatureCollection(
bbox: <ORSCoordinate>[],
features: <GeoJsonFeature>[],
);
expect(result.bbox, expected.bbox);
expect(result.features, expected.features);
});

test('Test GeoJSON Point Coordinate serialization', () {
// Arrange
final List<ORSCoordinate> coordinates = <ORSCoordinate>[
ORSCoordinate(latitude: 1.5, longitude: 0.0)
Expand Down Expand Up @@ -37,7 +128,7 @@ void geoJsonTests() {
);
});

test('Test GeoJSON Coordinate Point deserialization', () {
test('Test GeoJSON Point Coordinate deserialization', () {
// Arrange
final Map<String, dynamic> json = <String, dynamic>{
'type': 'Feature',
Expand Down Expand Up @@ -85,7 +176,7 @@ void geoJsonTests() {
}
});

test('Test GeoJSON Coordinate Point serialization and deserialization', () {
test('Test GeoJSON Point Coordinate serialization and deserialization', () {
// Arrange
final Point original = Point(Coordinate(51.5, 0.0));

Expand All @@ -103,4 +194,76 @@ void geoJsonTests() {
expect(result.bbox.minLong, original.bbox.minLong);
expect(result.properties, original.properties);
});

test('Test empty GeoJSON Coordinates serialization', () {
// Arrange
final GeoJsonFeature feature = GeoJsonFeature(
type: 'Feature',
geometry: GeoJsonFeatureGeometry(
coordinates: <List<ORSCoordinate>>[],
type: 'Point',
internalType: GsonFeatureGeometryCoordinatesType.empty,
),
properties: <String, dynamic>{},
);

// Act
final Map<String, dynamic> result = feature.toJson();

// Assert
expect(
result,
<String, dynamic>{
'type': 'Feature',
'geometry': <String, dynamic>{
'type': 'Point',
'coordinates': <List<double>>[]
},
'properties': <String, dynamic>{}
},
);
});

test('Test empty GeoJSON Coordinate deserialization', () {
// Arrange
final Map<String, dynamic> json = <String, dynamic>{
'type': 'Feature',
'geometry': <String, dynamic>{
'type': 'Point',
'coordinates': <List<double>>[]
},
'properties': <String, dynamic>{}
};

// Act
final GeoJsonFeature result = GeoJsonFeature.fromJson(json);

// Assert
final GeoJsonFeature expected = GeoJsonFeature(
type: 'Feature',
geometry: GeoJsonFeatureGeometry(
coordinates: <List<ORSCoordinate>>[],
type: 'Point',
internalType: GsonFeatureGeometryCoordinatesType.empty,
),
properties: <String, dynamic>{},
);
expect(result.bbox, expected.bbox);
expect(result.properties, expected.properties);
expect(result.type, expected.type);
expect(result.geometry.internalType, expected.geometry.internalType);
expect(result.geometry.type, expected.geometry.type);
for (int i = 0; i < result.geometry.coordinates.length; i++) {
for (int j = 0; j < result.geometry.coordinates[i].length; j++) {
expect(
result.geometry.coordinates[i][j].latitude,
expected.geometry.coordinates[i][j].latitude,
);
expect(
result.geometry.coordinates[i][j].longitude,
expected.geometry.coordinates[i][j].longitude,
);
}
}
});
}

0 comments on commit 3bbdd7c

Please sign in to comment.