feat(app): seed restaurants, geocode addresses, refresh sharing
This commit is contained in:
@@ -12,6 +12,7 @@ class AppColors {
|
||||
static const lightError = Color(0xFFFF5252);
|
||||
static const lightText = Color(0xFF222222); // 추가
|
||||
static const lightCard = Colors.white; // 추가
|
||||
static const lightWarning = Color(0xFFFFA000);
|
||||
|
||||
// Dark Theme Colors
|
||||
static const darkPrimary = Color(0xFF03C75A);
|
||||
@@ -24,4 +25,5 @@ class AppColors {
|
||||
static const darkError = Color(0xFFFF5252);
|
||||
static const darkText = Color(0xFFFFFFFF); // 추가
|
||||
static const darkCard = Color(0xFF1E1E1E); // 추가
|
||||
static const darkWarning = Color(0xFFFFB74D);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,9 @@ class AppConstants {
|
||||
static const String visitRecordBox = 'visit_records';
|
||||
static const String recommendationBox = 'recommendations';
|
||||
static const String settingsBox = 'settings';
|
||||
static const String storeSeedVersionKey = 'store_seed_version';
|
||||
static const String storeSeedDataAsset = 'assets/data/store_seed.json';
|
||||
static const String storeSeedMetaAsset = 'assets/data/store_seed.meta.json';
|
||||
|
||||
// Default Settings
|
||||
static const int defaultDaysToExclude = 7;
|
||||
|
||||
58
lib/core/services/geocoding_service.dart
Normal file
58
lib/core/services/geocoding_service.dart
Normal file
@@ -0,0 +1,58 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:lunchpick/core/utils/app_logger.dart';
|
||||
|
||||
/// 주소를 위도/경도로 변환하는 간단한 지오코딩(Geocoding) 서비스
|
||||
class GeocodingService {
|
||||
static const _endpoint = 'https://nominatim.openstreetmap.org/search';
|
||||
static const _fallbackLatitude = 37.5665; // 서울시청 위도
|
||||
static const _fallbackLongitude = 126.9780; // 서울시청 경도
|
||||
|
||||
/// 도로명/지번 주소를 기반으로 위경도를 조회한다.
|
||||
///
|
||||
/// 무료(Nominatim) 엔드포인트를 사용하며 별도 API 키가 필요 없다.
|
||||
/// 실패 시 null을 반환하고, 호출 측에서 기본 좌표를 사용할 수 있게 둔다.
|
||||
Future<({double latitude, double longitude})?> geocode(String address) async {
|
||||
if (address.trim().isEmpty) return null;
|
||||
|
||||
try {
|
||||
final uri = Uri.parse(
|
||||
'$_endpoint?format=json&limit=1&q=${Uri.encodeQueryComponent(address)}',
|
||||
);
|
||||
|
||||
// Nominatim은 User-Agent 헤더를 요구한다.
|
||||
final response = await http.get(
|
||||
uri,
|
||||
headers: const {'User-Agent': 'lunchpick-geocoder/1.0'},
|
||||
);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
AppLogger.debug('[GeocodingService] 실패 status: ${response.statusCode}');
|
||||
return null;
|
||||
}
|
||||
|
||||
final List<dynamic> results = jsonDecode(response.body) as List<dynamic>;
|
||||
if (results.isEmpty) return null;
|
||||
|
||||
final first = results.first as Map<String, dynamic>;
|
||||
final lat = double.tryParse(first['lat']?.toString() ?? '');
|
||||
final lon = double.tryParse(first['lon']?.toString() ?? '');
|
||||
|
||||
if (lat == null || lon == null) {
|
||||
AppLogger.debug('[GeocodingService] 응답 파싱 실패: ${first.toString()}');
|
||||
return null;
|
||||
}
|
||||
|
||||
return (latitude: lat, longitude: lon);
|
||||
} catch (e) {
|
||||
AppLogger.debug('[GeocodingService] 예외 발생: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// 기본 좌표(서울시청)를 반환한다.
|
||||
({double latitude, double longitude}) defaultCoordinates() {
|
||||
return (latitude: _fallbackLatitude, longitude: _fallbackLongitude);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user