Skip to content

Commit

Permalink
542: get more details for the address if the only village identifier …
Browse files Browse the repository at this point in the history
…is present
  • Loading branch information
Stift committed Jan 29, 2025
1 parent d7a9f55 commit c481742
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 7 deletions.
70 changes: 70 additions & 0 deletions lib/app/geocode/nominatim.dart
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,48 @@ class Nominatim {
final data = json.decode(response.body) as List<dynamic>;
return data.map<Place>((p) => Place.fromJson(p as Map<String, dynamic>)).toList();
}

static Future<AddressDetail> details({
required String osmType,
required int osmId,
String? className,
bool addressDetails = false,
bool keywords = false,
bool linkedPlaces = false,
bool hierarchy = false,
bool group_hierarchy = false,
bool polygon_geojson = false,
String? language,
}) async {
final baseServer = Uri.parse(Config.addressSearchUrl);
assert(baseServer.scheme == 'https', 'It\'s required to have the address search on https');

assert(
['N', 'W', 'R', null].contains(osmType),
'osmType needs to be one of N, W, R',
);

final uri = Uri.https(
baseServer.host,
'${baseServer.path}/details',
{
'format': 'json',
'osmtype': osmType,
'osmid': osmId.toString(),
if (className != null) 'class': className,
if (addressDetails) 'addressdetails': '1',
if (keywords) 'keywords': '1',
if (linkedPlaces) 'linkedplaces': '1',
if (hierarchy) 'hierarchy': '1',
if (group_hierarchy) 'group_hierarchy': '1',
if (polygon_geojson) 'polygon_geojson': '1',
if (language != null) 'accept-language': language,
},
);
final response = await http.get(uri);
final data = json.decode(response.body) as Map<String, dynamic>;
return AddressDetail.fromJson(data as Map<String, dynamic>);
}
}

/// A place in the nominatim system
Expand Down Expand Up @@ -337,6 +379,34 @@ class Place {
final Map<String, dynamic>? nameDetails;
}

/// A place in the nominatim system
class AddressDetail {
// ignore: public_member_api_docs
AddressDetail({
required this.osmType,
required this.osmId,
required this.addressTags,
});

// ignore: public_member_api_docs
factory AddressDetail.fromJson(Map<String, dynamic> json) => AddressDetail(
osmType: json['osm_type'] != null ? json['osm_type'] as String : null,
osmId: json['osm_id'] != null ? json['osm_id'] as int : null,
addressTags: json['addresstags'] != null ? json['addresstags'] as Map<String, dynamic> : null,
);

/// Reference to the OSM object
final String? osmType;

/// Reference to the OSM object
final int? osmId;

/// Map of address tags
/// Only with [Nominatim.searchByName(addressDetails: true)]
/// See https://nominatim.org/release-docs/latest/api/Output/#addressdetails
final Map<String, dynamic>? addressTags;
}

/// View box for searching
class ViewBox {
// ignore: public_member_api_docs
Expand Down
15 changes: 13 additions & 2 deletions lib/app/services/converters/place_parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ extension PlaceParser on Place {
String getAddress() {
var road = getRoad();
var housenumber = getHouseNumber();
var city = getCity();
var city = getCityOrVillage();
var zipCode = getZipCode();
var state = getState();
var municipality = getMunicipality();
Expand Down Expand Up @@ -46,8 +46,13 @@ extension PlaceParser on Place {
'';
}

String getCityOrVillage() {
var city = getCity();
return city.isEmpty ? (address?['village']?.toString() ?? '') : city;
}

String getCity() {
return address?['city']?.toString() ?? address?['town']?.toString() ?? address?['village']?.toString() ?? '';
return address?['city']?.toString() ?? address?['town']?.toString() ?? '';
}

String getZipCode() {
Expand All @@ -74,3 +79,9 @@ extension PlaceParser on Place {
return address?['county']?.toString() ?? '';
}
}

extension AdressDetailParser on AddressDetail {
String? getCity() {
return addressTags?['city']?.toString();
}
}
19 changes: 16 additions & 3 deletions lib/app/services/nominatim_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,20 @@ class NominatimService {
lat: location.latitude,
lon: location.longitude,
);
final address = AddressModel.fromPlace(place);
String? cityOverride;
if (place.getCity().isEmpty) {
var osmType = switch (place.osmType) {
'way' => 'W',
'node' => 'N',
'relation' => 'R',
String() => throw UnimplementedError(),
null => throw UnimplementedError(),
};
final addressDetail = await Nominatim.details(osmType: osmType, osmId: place.osmId!);
cityOverride = addressDetail.getCity();
}

final address = AddressModel.fromPlace(place, cityOverride: cityOverride);
return address;
} catch (e) {
_logger.e(
Expand Down Expand Up @@ -105,9 +118,9 @@ class AddressModel {
* For City we take in descending order city, town, or village, whatever is first.
* Hopefully, this will solve most issues with that kind of address composition.
*/
AddressModel.fromPlace(Place place)
AddressModel.fromPlace(Place place, {String? cityOverride})
: street = place.getRoad(),
houseNumber = place.getHouseNumber(),
zipCode = place.getZipCode(),
city = place.getCity();
city = cityOverride ?? place.getCityOrVillage();
}
2 changes: 1 addition & 1 deletion lib/features/campaigns/screens/map_consumer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ abstract class MapConsumer<T extends StatefulWidget> extends State<T> with Focus
AppRoute<W?>(
builder: (context) {
return FutureBuilder(
future: locationAddress.timeout(const Duration(milliseconds: 800), onTimeout: () => AddressModel()),
future: locationAddress.timeout(const Duration(milliseconds: 1300), onTimeout: () => AddressModel()),
builder: (context, AsyncSnapshot<AddressModel> snapshot) {
if (!snapshot.hasData && !snapshot.hasError) {
return Container(
Expand Down
2 changes: 1 addition & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,13 @@ void main() async {
}

registerSecureStorage();
GetIt.I.registerSingleton<GrueneApiCampaignsStatisticsService>(GrueneApiCampaignsStatisticsService());
GetIt.I.registerSingleton<AppSettings>(AppSettings());
GetIt.I.registerFactory<AuthenticatorService>(MfaFactory.create);
GetIt.I.registerSingleton<IpService>(IpService());
// Warning: The gruene api singleton depends on the auth repository which depends on the authenticator singleton
// Therefore this should be last
GetIt.I.registerSingleton<GrueneApi>(await createGrueneApiClient());
GetIt.I.registerSingleton<GrueneApiCampaignsStatisticsService>(GrueneApiCampaignsStatisticsService());
GetIt.I.registerFactory<NominatimService>(() => NominatimService(countryCode: t.campaigns.search.country_code));

runApp(TranslationProvider(child: const MyApp()));
Expand Down

0 comments on commit c481742

Please sign in to comment.