Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Empty checks to prevent breaking when receiving empty openrouteservice collections #22

Merged
merged 3 commits into from
Apr 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
);
}
}
});
}
Loading