104 lines
3.3 KiB
Dart
104 lines
3.3 KiB
Dart
import '../strings/consultorios_strings.dart';
|
|
|
|
/// Um consultório dentário devolvido pela Overpass API. Os campos de texto
|
|
/// nunca são nulos — qualquer tag em falta no OpenStreetMap já é resolvida
|
|
/// para um texto de substituição aqui — mas [hasPhone]/[hasOpeningHours]
|
|
/// dizem se esse texto é um valor real ou só o substituto, para os widgets
|
|
/// decidirem o que é "acionável" (ex.: só ligar quando há telefone real).
|
|
class Clinic {
|
|
const Clinic({
|
|
required this.id,
|
|
required this.name,
|
|
required this.lat,
|
|
required this.lon,
|
|
required this.address,
|
|
required this.phone,
|
|
required this.hasPhone,
|
|
required this.openingHours,
|
|
required this.hasOpeningHours,
|
|
this.distanceKm,
|
|
});
|
|
|
|
final String id;
|
|
final String name;
|
|
final double lat;
|
|
final double lon;
|
|
final String address;
|
|
final String phone;
|
|
final bool hasPhone;
|
|
final String openingHours;
|
|
final bool hasOpeningHours;
|
|
|
|
/// Distância (km) até à morada guardada — só preenchida depois de
|
|
/// [OverpassService.fetchNearbyClinics] a calcular; `null` até lá.
|
|
final double? distanceKm;
|
|
|
|
Clinic copyWithDistance(double distanceKm) {
|
|
return Clinic(
|
|
id: id,
|
|
name: name,
|
|
lat: lat,
|
|
lon: lon,
|
|
address: address,
|
|
phone: phone,
|
|
hasPhone: hasPhone,
|
|
openingHours: openingHours,
|
|
hasOpeningHours: hasOpeningHours,
|
|
distanceKm: distanceKm,
|
|
);
|
|
}
|
|
|
|
/// Constrói a partir de um elemento da resposta da Overpass API (`node`
|
|
/// ou `way`, com `out center tags;` para que `way` também tenha
|
|
/// coordenadas via `element['center']`).
|
|
factory Clinic.fromOverpassElement(Map<String, dynamic> element) {
|
|
final tags = (element['tags'] as Map?)?.cast<String, dynamic>() ?? const {};
|
|
|
|
final double lat;
|
|
final double lon;
|
|
if (element['type'] == 'node') {
|
|
lat = (element['lat'] as num).toDouble();
|
|
lon = (element['lon'] as num).toDouble();
|
|
} else {
|
|
final center = (element['center'] as Map?)?.cast<String, dynamic>() ?? const {};
|
|
lat = ((center['lat'] as num?) ?? 0).toDouble();
|
|
lon = ((center['lon'] as num?) ?? 0).toDouble();
|
|
}
|
|
|
|
final name = (tags['name'] as String?)?.trim();
|
|
|
|
final addressParts = [
|
|
[
|
|
tags['addr:street'],
|
|
tags['addr:housenumber'],
|
|
].whereType<String>().join(', '),
|
|
tags['addr:postcode'],
|
|
(tags['addr:city'] ?? tags['addr:suburb']),
|
|
].whereType<String>().where((s) => s.trim().isNotEmpty).toList();
|
|
|
|
final phone = (tags['phone'] as String?)?.trim() ??
|
|
(tags['contact:phone'] as String?)?.trim();
|
|
final openingHours = (tags['opening_hours'] as String?)?.trim();
|
|
final hasPhone = phone != null && phone.isNotEmpty;
|
|
final hasOpeningHours = openingHours != null && openingHours.isNotEmpty;
|
|
|
|
return Clinic(
|
|
id: '${element['type']}/${element['id']}',
|
|
name: (name != null && name.isNotEmpty)
|
|
? name
|
|
: ConsultoriosStrings.unnamedClinic,
|
|
lat: lat,
|
|
lon: lon,
|
|
address: addressParts.isNotEmpty
|
|
? addressParts.join(' • ')
|
|
: ConsultoriosStrings.noAddress,
|
|
phone: hasPhone ? phone : ConsultoriosStrings.noPhone,
|
|
hasPhone: hasPhone,
|
|
openingHours: hasOpeningHours
|
|
? openingHours
|
|
: ConsultoriosStrings.noOpeningHours,
|
|
hasOpeningHours: hasOpeningHours,
|
|
);
|
|
}
|
|
}
|