diff --git a/geocoding/CHANGELOG.md b/geocoding/CHANGELOG.md index e0925c8..b9a19c0 100644 --- a/geocoding/CHANGELOG.md +++ b/geocoding/CHANGELOG.md @@ -1,3 +1,8 @@ +## 4.1.0 + +* Added the ability to refine address search by geographic location +* The search results' Location object now have new `title` and `description` fields + ## 4.0.0 * **BREAKING CHANGES** Please update to Flutter 3.29+ before updating to this version diff --git a/geocoding/lib/geocoding.dart b/geocoding/lib/geocoding.dart index d4a2796..f62188d 100644 --- a/geocoding/lib/geocoding.dart +++ b/geocoding/lib/geocoding.dart @@ -11,10 +11,12 @@ export 'package:geocoding_platform_interface/geocoding_platform_interface.dart'; /// resolved into a single [Location], multiple [Location] instances may be /// returned. Future> locationFromAddress( - String address, -) => + String address, { + Region? targetRegion, +}) => GeocodingPlatform.instance!.locationFromAddress( address, + targetRegion: targetRegion, ); /// Returns a list of [Placemark] instances found for the supplied diff --git a/geocoding/pubspec.yaml b/geocoding/pubspec.yaml index df57865..c28efca 100644 --- a/geocoding/pubspec.yaml +++ b/geocoding/pubspec.yaml @@ -1,6 +1,6 @@ name: geocoding description: A Flutter Geocoding plugin which provides easy geocoding and reverse-geocoding features. -version: 4.0.0 +version: 4.1.0 repository: https://github.com/baseflow/flutter-geocoding/tree/main/geocoding issue_tracker: https://github.com/Baseflow/flutter-geocoding/issues @@ -12,9 +12,9 @@ dependencies: flutter: sdk: flutter - geocoding_platform_interface: ^3.0.0 - geocoding_android: ^4.0.0 - geocoding_ios: ^3.0.0 + geocoding_platform_interface: ^4.0.0 + geocoding_android: ^4.1.0 + geocoding_ios: ^3.1.0 dev_dependencies: flutter_test: diff --git a/geocoding/test/geocoding_test.dart b/geocoding/test/geocoding_test.dart index 9bef63a..cc29f32 100644 --- a/geocoding/test/geocoding_test.dart +++ b/geocoding/test/geocoding_test.dart @@ -4,6 +4,8 @@ import 'package:mockito/mockito.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; final mockLocation = Location( + title: '', + description: '', latitude: 52.2165157, longitude: 6.9437819, timestamp: DateTime.fromMillisecondsSinceEpoch(0).toUtc(), @@ -48,8 +50,9 @@ class MockGeocodingPlatform extends Mock GeocodingPlatform { @override Future> locationFromAddress( - String address, - ) async { + String address, { + Region? targetRegion, + }) async { return [mockLocation]; } diff --git a/geocoding_android/android/src/main/java/com/baseflow/geocoding/Geocoding.java b/geocoding_android/android/src/main/java/com/baseflow/geocoding/Geocoding.java index aade3be..857f36f 100644 --- a/geocoding_android/android/src/main/java/com/baseflow/geocoding/Geocoding.java +++ b/geocoding_android/android/src/main/java/com/baseflow/geocoding/Geocoding.java @@ -52,14 +52,37 @@ boolean isPresent() { * @param callback the GeocodeListenerAdapter that listens for success or error * @return a list of Address objects. Returns null or empty list if no matches were found or there is no backend service available. */ - void placemarkFromAddress(String address, GeocodeListenerAdapter callback) { + void placemarkFromAddress( + String address, + Double lowerLeftLatitude, + Double lowerLeftLongitude, + Double upperRightLatitude, + Double upperRightLongitude, + GeocodeListenerAdapter callback + ) { final Geocoder geocoder = createGeocoder(androidContext, locale); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - getAddressesWithGeocodeListener(geocoder, address, 5, callback); + getAddressesWithGeocodeListener( + geocoder, + address, + 5, + lowerLeftLatitude, + lowerLeftLongitude, + upperRightLatitude, + upperRightLongitude, + callback + ); } else { try { - List
addresses = deprecatedGetFromLocationName(geocoder, address); + List
addresses = deprecatedGetFromLocationName( + geocoder, + address, + lowerLeftLatitude, + lowerLeftLongitude, + upperRightLatitude, + upperRightLongitude + ); callback.onGeocode(addresses); } catch (IOException ex) { callback.onError(ex.getMessage()); @@ -68,13 +91,33 @@ void placemarkFromAddress(String address, GeocodeListenerAdapter callback) { } @SuppressWarnings("deprecation") - private List
deprecatedGetFromLocationName(Geocoder geocoder, String address) throws IOException { - return geocoder.getFromLocationName(address, 5); + private List
deprecatedGetFromLocationName( + Geocoder geocoder, + String address, + Double lowerLeftLatitude, + Double lowerLeftLongitude, + Double upperRightLatitude, + Double upperRightLongitude + ) throws IOException { + if (lowerLeftLatitude == null || lowerLeftLongitude == null || upperRightLatitude == null || upperRightLongitude == null) { + return geocoder.getFromLocationName(address, 5); + } else { + return geocoder.getFromLocationName(address, 5, lowerLeftLatitude, lowerLeftLongitude, upperRightLatitude, upperRightLongitude); + } } @RequiresApi(api = Build.VERSION_CODES.TIRAMISU) - private void getAddressesWithGeocodeListener(Geocoder geocoder, String address, int maxResults, GeocodeListenerAdapter callback) { - geocoder.getFromLocationName(address, maxResults, new Geocoder.GeocodeListener() { + private void getAddressesWithGeocodeListener( + Geocoder geocoder, + String address, + int maxResults, + Double lowerLeftLatitude, + Double lowerLeftLongitude, + Double upperRightLatitude, + Double upperRightLongitude, + GeocodeListenerAdapter callback + ) { + final Geocoder.GeocodeListener listener = new Geocoder.GeocodeListener() { @Override public void onGeocode(List
geocodedAddresses) { callback.onGeocode(geocodedAddresses); @@ -84,7 +127,12 @@ public void onGeocode(List
geocodedAddresses) { public void onError(@Nullable String errorMessage) { callback.onError(errorMessage); } - }); + }; + if (lowerLeftLatitude == null || lowerLeftLongitude == null || upperRightLatitude == null || upperRightLongitude == null) { + geocoder.getFromLocationName(address, maxResults, listener); + } else { + geocoder.getFromLocationName(address, maxResults, lowerLeftLatitude, lowerLeftLongitude, upperRightLatitude, upperRightLongitude, listener); + } } diff --git a/geocoding_android/android/src/main/java/com/baseflow/geocoding/MethodCallHandlerImpl.java b/geocoding_android/android/src/main/java/com/baseflow/geocoding/MethodCallHandlerImpl.java index 4bd808a..742a404 100644 --- a/geocoding_android/android/src/main/java/com/baseflow/geocoding/MethodCallHandlerImpl.java +++ b/geocoding_android/android/src/main/java/com/baseflow/geocoding/MethodCallHandlerImpl.java @@ -104,6 +104,16 @@ private void setLocaleIdentifier(MethodCall call, Result result) { result.success(true); } + // Parses a string as a double and returns the parsed value. + // If parsing is not possible or fails, returns null. + private static Double parseDoubleOrReturnNull(final String _string){ + try{ + return Double.parseDouble(_string); + }catch (Throwable t){ + return null; + } + } + private void onLocationFromAddress(MethodCall call, Result result) { final String address = call.argument("address"); @@ -114,8 +124,13 @@ private void onLocationFromAddress(MethodCall call, Result result) { null); } - geocoding.placemarkFromAddress(address, new GeocodeListenerAdapter() { - + geocoding.placemarkFromAddress( + address, + parseDoubleOrReturnNull(call.argument("targetRegionSLat")), // lowerLeftLatitude, + parseDoubleOrReturnNull(call.argument("targetRegionWLng")), // lowerLeftLongitude, + parseDoubleOrReturnNull(call.argument("targetRegionNLat")), // upperRightLatitude, + parseDoubleOrReturnNull(call.argument("targetRegionELng")), // upperRightLongitude + new GeocodeListenerAdapter() { @Override public void onGeocode(List
addresses) { if (addresses != null && addresses.size() > 0) { @@ -148,8 +163,13 @@ private void onPlacemarkFromAddress(final MethodCall call, final Result result) null); } - geocoding.placemarkFromAddress(address, new GeocodeListenerAdapter() { - + geocoding.placemarkFromAddress( + address, + parseDoubleOrReturnNull(call.argument("targetRegionSLat")), // lowerLeftLatitude, + parseDoubleOrReturnNull(call.argument("targetRegionWLng")), // lowerLeftLongitude, + parseDoubleOrReturnNull(call.argument("targetRegionNLat")), // upperRightLatitude, + parseDoubleOrReturnNull(call.argument("targetRegionELng")), // upperRightLongitude + new GeocodeListenerAdapter() { @Override public void onGeocode(List
addresses) { if (addresses != null && addresses.size() > 0) { diff --git a/geocoding_android/android/src/main/java/com/baseflow/geocoding/utils/AddressMapper.java b/geocoding_android/android/src/main/java/com/baseflow/geocoding/utils/AddressMapper.java index afe3ec3..c43e46c 100644 --- a/geocoding_android/android/src/main/java/com/baseflow/geocoding/utils/AddressMapper.java +++ b/geocoding_android/android/src/main/java/com/baseflow/geocoding/utils/AddressMapper.java @@ -55,6 +55,18 @@ public static List> toLocationHashMapList(List
addr private static Map toLocationHashmap(Address address) { Map location = new HashMap<>(); + location.put("title", address.getFeatureName()); + { + final int maxAddressLineIndex = address.getMaxAddressLineIndex(); + final StringBuilder descriptionString = new StringBuilder(); + for (int i = 0; i <= maxAddressLineIndex; i++) { + if (i > 0) { + descriptionString.append("\n"); + } + descriptionString.append(address.getAddressLine(i)); + } + location.put("description", descriptionString.toString()); + } location.put("latitude", address.getLatitude()); location.put("longitude", address.getLongitude()); location.put("timestamp", Calendar.getInstance(TimeZone.getTimeZone("UTC")).getTimeInMillis()); diff --git a/geocoding_android/lib/geocoding_android.dart b/geocoding_android/lib/geocoding_android.dart index 2c2c9a9..f765185 100644 --- a/geocoding_android/lib/geocoding_android.dart +++ b/geocoding_android/lib/geocoding_android.dart @@ -25,8 +25,9 @@ class GeocodingAndroid extends GeocodingPlatform { @override Future> locationFromAddress( - String address, - ) async { + String address, { + Region? targetRegion, + }) async { final parameters = { 'address': address, }; diff --git a/geocoding_android/pubspec.yaml b/geocoding_android/pubspec.yaml index 2ee6895..c2fa25d 100644 --- a/geocoding_android/pubspec.yaml +++ b/geocoding_android/pubspec.yaml @@ -1,6 +1,6 @@ name: geocoding_android description: A Flutter Geocoding plugin which provides easy geocoding and reverse-geocoding features. -version: 4.0.1 +version: 4.1.0 repository: https://github.com/baseflow/flutter-geocoding/tree/main/geocoding_android issue_tracker: https://github.com/Baseflow/flutter-geocoding/issues @@ -12,7 +12,7 @@ dependencies: flutter: sdk: flutter - geocoding_platform_interface: ^3.2.0 + geocoding_platform_interface: ^4.0.0 dev_dependencies: flutter_test: diff --git a/geocoding_android/test/geocoding_test.dart b/geocoding_android/test/geocoding_test.dart index c4e16db..355e1cf 100644 --- a/geocoding_android/test/geocoding_test.dart +++ b/geocoding_android/test/geocoding_test.dart @@ -4,6 +4,8 @@ import 'package:geocoding_android/geocoding_android.dart'; import 'package:geocoding_platform_interface/geocoding_platform_interface.dart'; final mockLocation = Location( + title: '', + description: '', latitude: 52.2165157, longitude: 6.9437819, timestamp: DateTime.fromMillisecondsSinceEpoch(0).toUtc(), diff --git a/geocoding_ios/ios/Classes/Extensions/CLPlacemarkExtensions.m b/geocoding_ios/ios/Classes/Extensions/CLPlacemarkExtensions.m index f3185e9..0055f4e 100644 --- a/geocoding_ios/ios/Classes/Extensions/CLPlacemarkExtensions.m +++ b/geocoding_ios/ios/Classes/Extensions/CLPlacemarkExtensions.m @@ -39,8 +39,12 @@ - (NSDictionary *)toLocationDictionary { if (self.location == nil) { return nil; } + + NSArray *lines = self.addressDictionary[@"FormattedAddressLines"]; NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithDictionary:@{ + @"title": self.name, + @"description": [lines componentsJoinedByString:@", "] ?: [NSNull null], @"latitude": @(self.location.coordinate.latitude), @"longitude": @(self.location.coordinate.longitude), @"timestamp": @([CLPlacemark currentTimeInMilliSeconds: self.location.timestamp]), diff --git a/geocoding_ios/ios/Classes/GeocodingHandler.h b/geocoding_ios/ios/Classes/GeocodingHandler.h index 9f0b25c..6e140e0 100644 --- a/geocoding_ios/ios/Classes/GeocodingHandler.h +++ b/geocoding_ios/ios/Classes/GeocodingHandler.h @@ -17,6 +17,10 @@ typedef void (^GeocodingFailure)(NSString* errorCode, NSString* errorDescription - (void) geocodeFromAddress: (NSString *)address locale: (NSLocale *)locale + sLat: (CGFloat)sLat + wLng: (CGFloat)sLng + nLat: (CGFloat)nLat + eLng: (CGFloat)nLng success: (_Nonnull GeocodingSuccess)successHandler failure: (_Nonnull GeocodingFailure)failureHandler; diff --git a/geocoding_ios/ios/Classes/GeocodingHandler.m b/geocoding_ios/ios/Classes/GeocodingHandler.m index 3e12635..00b792c 100644 --- a/geocoding_ios/ios/Classes/GeocodingHandler.m +++ b/geocoding_ios/ios/Classes/GeocodingHandler.m @@ -23,6 +23,10 @@ - (id)init { - (void) geocodeFromAddress: (NSString *)address locale: (NSLocale *)locale + sLat: (CGFloat) sLat + wLng: (CGFloat) sLng + nLat: (CGFloat) nLat + eLng: (CGFloat) nLng success: (GeocodingSuccess)successHandler failure: (GeocodingFailure)failureHandler { @@ -30,9 +34,21 @@ - (void) geocodeFromAddress: (NSString *)address failureHandler(@"ARGUMENT_ERROR", @"Please supply a valid string containing the address to lookup"); return; } - + + CLRegion* region; + if (sLat == 0 || sLng == 0 || nLat == 0 || nLng == 0){ + region = nil; + }else{ + CLLocationCoordinate2D center = CLLocationCoordinate2DMake(sLat + (nLat-sLat) / 2, sLng + (nLng-sLng) / 2); + //Computing the radius based on lat delta, since 1 lat = 111 km no matter the location + float latDelta = nLat - sLat; + float radiusLat = (latDelta/2); + float radius = radiusLat * 111000; + region = [[CLCircularRegion alloc] initWithCenter:center radius:radius identifier:@"Search Radius"]; + } + [_geocoder geocodeAddressString:address - inRegion:nil + inRegion:region preferredLocale:locale completionHandler:^(NSArray< CLPlacemark *> *__nullable placemarks, NSError *__nullable error) { diff --git a/geocoding_ios/ios/Classes/GeocodingPlugin.m b/geocoding_ios/ios/Classes/GeocodingPlugin.m index 136a764..1c67817 100644 --- a/geocoding_ios/ios/Classes/GeocodingPlugin.m +++ b/geocoding_ios/ios/Classes/GeocodingPlugin.m @@ -18,13 +18,27 @@ + (void)registerWithRegistrar:(NSObject*)registrar { [registrar addMethodCallDelegate:instance channel:channel]; } ++ (CGFloat) parseCGFloat:(NSString*)numberString { + NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; + formatter.numberStyle=NSNumberFormatterDecimalStyle; + return [[formatter numberFromString:numberString] doubleValue]; +} + - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { if ([@"locationFromAddress" isEqualToString:call.method]) { NSString* address = call.arguments[@"address"]; + NSString* targetRegionSLat = call.arguments[@"targetRegionSLat"]; + NSString* targetRegionNLat = call.arguments[@"targetRegionNLat"]; + NSString* targetRegionWLng = call.arguments[@"targetRegionWLng"]; + NSString* targetRegionELng = call.arguments[@"targetRegionELng"]; GeocodingHandler* handler = [[GeocodingHandler alloc] init]; [handler geocodeFromAddress:address locale:[GeocodingPlugin parseLocale: call.arguments] + sLat:[GeocodingPlugin parseCGFloat: targetRegionSLat] + wLng:[GeocodingPlugin parseCGFloat: targetRegionWLng] + nLat:[GeocodingPlugin parseCGFloat: targetRegionNLat] + eLng:[GeocodingPlugin parseCGFloat: targetRegionELng] success:^(NSArray * placemarks) { result([GeocodingPlugin toLocationResult: placemarks]); } diff --git a/geocoding_ios/lib/geocoding_ios.dart b/geocoding_ios/lib/geocoding_ios.dart index 234dbb6..427c24b 100644 --- a/geocoding_ios/lib/geocoding_ios.dart +++ b/geocoding_ios/lib/geocoding_ios.dart @@ -22,7 +22,10 @@ class GeocodingIOS extends GeocodingPlatform { } @override - Future> locationFromAddress(String address) async { + Future> locationFromAddress( + String address, { + Region? targetRegion, + }) async { final parameters = { 'address': address, }; diff --git a/geocoding_ios/pubspec.yaml b/geocoding_ios/pubspec.yaml index 0ced92a..c15fff7 100644 --- a/geocoding_ios/pubspec.yaml +++ b/geocoding_ios/pubspec.yaml @@ -1,6 +1,6 @@ name: geocoding_ios description: A Flutter Geocoding plugin which provides easy geocoding and reverse-geocoding features. -version: 3.0.2 +version: 3.1.0 repository: https://github.com/baseflow/flutter-geocoding/tree/main/geocoding_ios issue_tracker: https://github.com/Baseflow/flutter-geocoding/issues @@ -12,7 +12,7 @@ dependencies: flutter: sdk: flutter - geocoding_platform_interface: ^3.2.0 + geocoding_platform_interface: ^4.0.0 dev_dependencies: flutter_test: diff --git a/geocoding_ios/test/geocoding_test.dart b/geocoding_ios/test/geocoding_test.dart index b87799a..4abddd1 100644 --- a/geocoding_ios/test/geocoding_test.dart +++ b/geocoding_ios/test/geocoding_test.dart @@ -4,6 +4,8 @@ import 'package:geocoding_ios/geocoding_ios.dart'; import 'package:geocoding_platform_interface/geocoding_platform_interface.dart'; final mockLocation = Location( + title: '', + description: '', latitude: 52.2165157, longitude: 6.9437819, timestamp: DateTime.fromMillisecondsSinceEpoch(0).toUtc(), diff --git a/geocoding_platform_interface/lib/src/geocoding_platform_interface.dart b/geocoding_platform_interface/lib/src/geocoding_platform_interface.dart index c046192..b06ce89 100644 --- a/geocoding_platform_interface/lib/src/geocoding_platform_interface.dart +++ b/geocoding_platform_interface/lib/src/geocoding_platform_interface.dart @@ -52,8 +52,9 @@ abstract class GeocodingPlatform extends PlatformInterface { /// resolved into a single [Location], multiple [Location] instances may be /// returned. Future> locationFromAddress( - String address, - ) { + String address, { + Region? targetRegion, + }) { throw UnimplementedError( 'locationFromAddress() has not been implementated.'); } diff --git a/geocoding_platform_interface/lib/src/models/location.dart b/geocoding_platform_interface/lib/src/models/location.dart index 48dd0d6..71bfedb 100644 --- a/geocoding_platform_interface/lib/src/models/location.dart +++ b/geocoding_platform_interface/lib/src/models/location.dart @@ -1,4 +1,4 @@ -import 'package:meta/meta.dart'; +import 'package:flutter/foundation.dart'; /// Contains detailed location information. @immutable @@ -7,17 +7,27 @@ class Location { /// instances constructed this way won't actually reflect any real information /// from the platform, just whatever was passed in at construction time. const Location({ + required this.title, + required this.description, required this.latitude, required this.longitude, required this.timestamp, }); const Location._({ + required this.title, + required this.description, required this.latitude, required this.longitude, required this.timestamp, }); + /// The title associated with the placemark, might be empty + final String title; + + /// The description associated with the placemark, might be empty + final String description; + /// The latitude associated with the placemark. final double latitude; @@ -30,6 +40,8 @@ class Location { @override bool operator ==(Object other) => other is Location && + other.title == title && + other.description == description && other.latitude == latitude && other.longitude == longitude && other.timestamp == timestamp; @@ -65,6 +77,8 @@ class Location { } return Location._( + title: locationMap['title'] ?? '', + description: locationMap['description'] ?? '', latitude: locationMap['latitude'], longitude: locationMap['longitude'], timestamp: timestamp, @@ -74,6 +88,8 @@ class Location { /// Converts the [Location] instance into a [Map] instance that can be /// serialized to JSON. Map toJson() => { + 'title': title, + 'description': description, 'latitude': latitude, 'longitude': longitude, 'timestamp': timestamp.millisecondsSinceEpoch, @@ -82,6 +98,8 @@ class Location { @override String toString() { return ''' + Title: $title, + Description: $description, Latitude: $latitude, Longitude: $longitude, Timestamp: $timestamp'''; diff --git a/geocoding_platform_interface/lib/src/models/models.dart b/geocoding_platform_interface/lib/src/models/models.dart index f26554a..9dde598 100644 --- a/geocoding_platform_interface/lib/src/models/models.dart +++ b/geocoding_platform_interface/lib/src/models/models.dart @@ -1,2 +1,3 @@ export 'location.dart'; export 'placemark.dart'; +export 'region.dart'; diff --git a/geocoding_platform_interface/lib/src/models/placemark.dart b/geocoding_platform_interface/lib/src/models/placemark.dart index e833847..95ba52a 100644 --- a/geocoding_platform_interface/lib/src/models/placemark.dart +++ b/geocoding_platform_interface/lib/src/models/placemark.dart @@ -1,4 +1,4 @@ -import 'package:meta/meta.dart'; +import 'package:flutter/foundation.dart'; /// Contains detailed placemark information. @immutable diff --git a/geocoding_platform_interface/lib/src/models/region.dart b/geocoding_platform_interface/lib/src/models/region.dart new file mode 100644 index 0000000..7e2ff16 --- /dev/null +++ b/geocoding_platform_interface/lib/src/models/region.dart @@ -0,0 +1,126 @@ +import 'package:flutter/foundation.dart'; + +/// Contains information about a geographical region. +@immutable +class Region { + /// The latitude of the region's south border + final double southLatitude; + + /// The latitude of the region's north border + final double northLatitude; + + /// The longitude of the region's west border + final double westLongitude; + + /// The longitude of the region's east border + final double eastLongitude; + + static double _normalizeLatitude(double latitude) { + if (latitude < -90) { + return -90; + } + if (latitude > 90) { + return 90; + } + return latitude; + } + + static double _normalizeLongitude(double longitude) { + if (longitude >= -180 && longitude <= 180) { + return longitude; + } + return ((longitude + 180) % 360) - 180; + } + + /// Constructs a new region from: + /// - [southLatitude] The latitude of the region's south border + /// - [northLatitude] The latitude of the region's north border + /// - [westLongitude] The longitude of the region's west border + /// - [eastLongitude] The longitude of the region's east border + Region({ + required double southLatitude, + required double northLatitude, + required double westLongitude, + required double eastLongitude, + }) : southLatitude = _normalizeLatitude(southLatitude), + northLatitude = _normalizeLatitude(northLatitude), + westLongitude = _normalizeLongitude(westLongitude), + eastLongitude = _normalizeLongitude(eastLongitude); + + @override + bool operator ==(Object other) => + other is Region && + other.southLatitude == southLatitude && + other.northLatitude == northLatitude && + other.westLongitude == westLongitude && + other.eastLongitude == eastLongitude; + + @override + int get hashCode => southLatitude.hashCode ^ + northLatitude.hashCode ^ + westLongitude.hashCode ^ + eastLongitude.hashCode; + + /// Converts a list of [Map] instances to a list of [Location] instances. + static List fromMaps(dynamic message) { + if (message == null) { + throw ArgumentError('The parameter \'message\' should not be null.'); + } + + final List list = message.map(fromMap).toList(); + return list; + } + + /// Converts the supplied [Map] to an instance of the [Location] class. + static Region fromMap(dynamic message) { + if (message == null) { + throw ArgumentError('The parameter \'message\' should not be null.'); + } + + final Map locationMap = message; + + final double? southLatitude = locationMap['southLatitude']; + final double? northLatitude = locationMap['northLatitude']; + final double? westLongitude = locationMap['westLongitude']; + final double? eastLongitude = locationMap['eastLongitude']; + + if (southLatitude == null || + northLatitude == null || + westLongitude == null || + eastLongitude == null + ) { + throw ArgumentError('The parameters' + ' southLatitude' + ' and northLatitude' + ' and westLongitude' + ' and eastLongitude' + ' should not be null.'); + } + + return Region( + southLatitude: southLatitude, + northLatitude: northLatitude, + westLongitude: westLongitude, + eastLongitude: eastLongitude, + ); + } + + /// Converts the [Location] instance into a [Map] instance that can be + /// serialized to JSON. + Map toJson() => { + 'southLatitude': southLatitude, + 'northLatitude': northLatitude, + 'westLongitude': westLongitude, + 'eastLongitude': eastLongitude, + }; + + @override + String toString() { + return '{ ' + 'southLatitude: $southLatitude, ' + 'northLatitude: $northLatitude, ' + 'westLongitude: $westLongitude, ' + 'eastLongitude: $eastLongitude ' + '}'; + } +} diff --git a/geocoding_platform_interface/pubspec.yaml b/geocoding_platform_interface/pubspec.yaml index 0805f12..11ca9b3 100644 --- a/geocoding_platform_interface/pubspec.yaml +++ b/geocoding_platform_interface/pubspec.yaml @@ -3,7 +3,7 @@ description: A common platform interface for the geocoding plugin. homepage: https://github.com/baseflow/flutter-geocoding/tree/main/geocoding_platform_interface # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 3.2.1 +version: 4.0.0 environment: sdk: ">=3.3.0 <4.0.0" diff --git a/geocoding_platform_interface/test/src/models/location_test.dart b/geocoding_platform_interface/test/src/models/location_test.dart index cc8d975..03be6af 100644 --- a/geocoding_platform_interface/test/src/models/location_test.dart +++ b/geocoding_platform_interface/test/src/models/location_test.dart @@ -7,10 +7,14 @@ void main() { () { // Arrange final firstLocation = Location( + title: '', + description: '', latitude: 0, longitude: 0, timestamp: DateTime.fromMillisecondsSinceEpoch((0))); final secondLocation = Location( + title: '', + description: '', latitude: 0, longitude: 0, timestamp: DateTime.fromMillisecondsSinceEpoch((0))); @@ -26,11 +30,15 @@ void main() { () { // Arrange final firstLocation = Location( + title: '', + description: '', latitude: 0, longitude: 0, timestamp: DateTime.fromMillisecondsSinceEpoch(0), ); final secondLocation = Location( + title: '', + description: '', latitude: 1, longitude: 0, timestamp: DateTime.fromMillisecondsSinceEpoch(0), @@ -47,11 +55,15 @@ void main() { () { // Arrange final firstLocation = Location( + title: '', + description: '', latitude: 0, longitude: 0, timestamp: DateTime.fromMillisecondsSinceEpoch(0), ); final secondLocation = Location( + title: '', + description: '', latitude: 0, longitude: 1, timestamp: DateTime.fromMillisecondsSinceEpoch(0), @@ -68,11 +80,15 @@ void main() { () { // Arrange final firstLocation = Location( + title: '', + description: '', latitude: 0, longitude: 0, timestamp: DateTime.fromMillisecondsSinceEpoch(0), ); final secondLocation = Location( + title: '', + description: '', latitude: 0, longitude: 0, timestamp: DateTime.fromMillisecondsSinceEpoch(1), @@ -105,6 +121,8 @@ void main() { group('toString tests:', () { test('toString should list the contents of all properties', () { final mockLocation = Location( + title: '', + description: '', latitude: 52.2165157, longitude: 6.9437819, timestamp: DateTime.fromMillisecondsSinceEpoch(0).toUtc(),