From 2a093e3126662d70657947e366c73d97ee42fbef Mon Sep 17 00:00:00 2001 From: randogoth Date: Thu, 27 Feb 2025 14:38:03 +0200 Subject: [PATCH] test --- lib/rugreefer.dart | 38 ++++++++----- pubspec.lock | 10 +++- pubspec.yaml | 3 +- test/rugreefer_test.dart | 118 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 149 insertions(+), 20 deletions(-) diff --git a/lib/rugreefer.dart b/lib/rugreefer.dart index 8467fb8..8a50111 100644 --- a/lib/rugreefer.dart +++ b/lib/rugreefer.dart @@ -8,21 +8,19 @@ import 'package:turf/turf.dart'; class GeoMetadataLookup { final String apiUrl; final Directory cacheDir; - final double distanceThreshold; final Distance distance = const Distance(); GeoMetadataLookup({ required this.apiUrl, required this.cacheDir, - this.distanceThreshold = 10.0, // Default 10 meters }) { if (!cacheDir.existsSync()) { cacheDir.createSync(recursive: true); } } - Future>> lookup(double latitude, double longitude) async { - final String geohash = GeoHasher().encode(latitude, longitude, precision: 4); + Future>> lookup(double latitude, double longitude, {double distance = 10.0}) async { + final String geohash = GeoHasher().encode(longitude, latitude); final File cacheFile = File('${cacheDir.path}/$geohash.geojson.gz'); if (!cacheFile.existsSync()) { @@ -31,11 +29,11 @@ class GeoMetadataLookup { final String geoJsonStr = utf8.decode(GZipCodec().decode(cacheFile.readAsBytesSync())); final Map geoJson = jsonDecode(geoJsonStr); - return _findMetadata(latitude, longitude, geoJson); + return _findMetadata(latitude, longitude, distance, geoJson); } Future _downloadGeoJson(String geohash, File cacheFile) async { - final response = await http.get(Uri.parse('$apiUrl/$geohash.geojson.gz')); + final response = await http.get(Uri.parse('$apiUrl$geohash')); if (response.statusCode == 200) { cacheFile.writeAsBytesSync(GZipCodec().encode(response.bodyBytes)); } else { @@ -43,8 +41,8 @@ class GeoMetadataLookup { } } - List> _findMetadata(double latitude, double longitude, Map geoJson) { - final Point point = Point(coordinates: Position(longitude, latitude)); + List> _findMetadata(double latitude, double longitude, double distanceThreshold, Map geoJson) { + final Position point = Position(longitude, latitude); List> results = []; for (var feature in geoJson['features']) { @@ -59,19 +57,30 @@ class GeoMetadataLookup { .toList()) .toList(), ); + if (booleanPointInPolygon(point, polygon)) { + results.add({'type': 'polygon', ...properties}); + } } else if (geometry['type'] == 'LineString') { - final line = LineString(coordinates: (geometry['coordinates'] as List).map((p) => Position(p[0], p[1])).toList()); - if (_isPointNearLine(point, line)) { + final line = LineString( + coordinates: (geometry['coordinates'] as List) + .map((p) => Position(p[0] as double, p[1] as double)) + .toList(), + ); + if (_isPointNearLine(point, line, distanceThreshold)) { results.add({'type': 'line', ...properties}); } } else if (geometry['type'] == 'MultiLineString') { for (var lineCoords in geometry['coordinates']) { - final line = LineString(coordinates: lineCoords.map((p) => Position(p[0], p[1])).toList()); - if (_isPointNearLine(point, line)) { + final line = LineString( + coordinates: (lineCoords as List) + .map((p) => Position(p[0] as double, p[1] as double)) + .toList(), + ); + if (_isPointNearLine(point, line, distanceThreshold)) { results.add({'type': 'multi_line', ...properties}); break; } @@ -81,12 +90,11 @@ class GeoMetadataLookup { return results; } - bool _isPointNearLine(Point point, LineString line) { + bool _isPointNearLine(Position point, LineString line, double distanceThreshold) { for (var i = 0; i < line.coordinates.length - 1; i++) { final p1 = line.coordinates[i]; - final p2 = line.coordinates[i + 1]; final double dist = distance.distance( - LatLng(point.coordinates.lat.toDouble(), point.coordinates.lng.toDouble()), + LatLng(point.lat.toDouble(), point.lng.toDouble()), LatLng(p1.lat.toDouble(), p1.lng.toDouble()), ); if (dist <= distanceThreshold) return true; diff --git a/pubspec.lock b/pubspec.lock index 84d3b8c..0da033f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -241,6 +241,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" + mocktail: + dependency: "direct main" + description: + name: mocktail + sha256: "890df3f9688106f25755f26b1c60589a92b3ab91a22b8b224947ad041bf172d8" + url: "https://pub.dev" + source: hosted + version: "1.0.4" node_preamble: dependency: transitive description: @@ -386,7 +394,7 @@ packages: source: hosted version: "1.2.2" test: - dependency: "direct dev" + dependency: "direct main" description: name: test sha256: "301b213cd241ca982e9ba50266bd3f5bd1ea33f1455554c5abb85d1be0e2d87e" diff --git a/pubspec.yaml b/pubspec.yaml index 6be8384..9f07937 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,9 +11,10 @@ dependencies: dart_geohash: ^2.1.0 http: ^1.3.0 latlong2: ^0.9.1 + mocktail: ^1.0.4 + test: ^1.25.15 turf: ^0.0.10 # path: ^1.8.0 dev_dependencies: lints: ^5.0.0 - test: ^1.24.0 diff --git a/test/rugreefer_test.dart b/test/rugreefer_test.dart index 3bcbf1b..e2ebb51 100644 --- a/test/rugreefer_test.dart +++ b/test/rugreefer_test.dart @@ -1,5 +1,117 @@ -import 'package:rugreefer/rugreefer.dart'; // Ensure this import is correct +import 'dart:convert'; +import 'dart:io'; +import 'package:test/test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:http/http.dart' as http; +import 'package:rugreefer/rugreefer.dart'; // Replace with actual package name + +class MockClient extends Mock implements http.Client {} void main() { - print('Hello world!'); // Remove the missing method call -} + late GeoMetadataLookup lookup; + late Directory cacheDir; + late MockClient mockHttpClient; + + setUp(() { + cacheDir = Directory.systemTemp.createTempSync(); + mockHttpClient = MockClient(); + lookup = GeoMetadataLookup( + apiUrl: 'http://localhost:8000/get_geodata/?format=geojson&geohash=', + cacheDir: cacheDir, + ); + }); + + tearDown(() { + cacheDir.deleteSync(recursive: true); + }); + + test('Fetches and correctly parses GeoJSON with a residential landuse feature', () async { + final String geohash = 'sv2m'; + final File cacheFile = File('${cacheDir.path}/$geohash.geojson.gz'); + + final Map mockGeoJson = { + "type": "FeatureCollection", + "features": [ + { + "id": "0", + "type": "Feature", + "properties": { + "feature_id": "way/579610585", + "tags": {"waterway": "stream"} + }, + "geometry": { + "type": "LineString", + "coordinates": [ + [34.453125, 30.5735040], + [34.4515371, 30.5729953], + [34.4507646, 30.5725335] + ] + } + } + ] + }; + + final mockResponse = utf8.encode(jsonEncode(mockGeoJson)); + cacheFile.writeAsBytesSync(GZipCodec().encode(mockResponse)); + + final results = await lookup.lookup(32.827714492634676, 34.96983862139979); + + String prettyJson = const JsonEncoder.withIndent(' ').convert(results); + print(prettyJson); + + expect(results, isNotEmpty); + expect(results.any((r) => r['type'] == 'polygon' && r['tags']['landuse'] == 'residential'), isTrue); + }); + + test('Ensures caching of GeoJSON data', () async { + final String geohash = 'sv2m'; + final File cacheFile = File('${cacheDir.path}/$geohash.geojson.gz'); + + final mockGeoJson = jsonEncode({ + "type": "FeatureCollection", + "features": [] + }); + + final mockResponse = utf8.encode(mockGeoJson); + cacheFile.writeAsBytesSync(GZipCodec().encode(mockResponse)); + + expect(cacheFile.existsSync(), isTrue); + + await lookup.lookup(30.5730, 34.4520); + expect(cacheFile.existsSync(), isTrue); + }); + + test('Handles API call correctly when file is not cached', () async { + final String geohash = 'sv2m'; + + when(() => mockHttpClient.get(Uri.parse('http://localhost:8000/get_geodata/?format=geojson&geohash=$geohash'))) + .thenAnswer((_) async => http.Response(jsonEncode({ + "type": "FeatureCollection", + "features": [ + { + "id": "0", + "type": "Feature", + "properties": { + "feature_id": "way/579610585", + "tags": {"waterway": "stream"} + }, + "geometry": { + "type": "LineString", + "coordinates": [ + [34.453125, 30.5735040], + [34.4515371, 30.5729953] + ] + } + } + ] + }), 200)); + + final results = await lookup.lookup(31.776673242005337, 35.23441728409178); + + String prettyJson = const JsonEncoder.withIndent(' ').convert(results); + print(prettyJson); + + expect(results, isNotEmpty); + expect(results.any((r) => r['type'] == 'polygon' && r['tags']['historic'] == 'heritage'), isTrue); + }); +} \ No newline at end of file