الترميز الجغرافي: التحويل بين العناوين والإحداثيات
الترميز الجغرافي: التحويل بين العناوين والإحداثيات
الترميز الجغرافي هو عملية تحويل عنوان يمكن للإنسان قراءته (مثل "1600 Amphitheatre Parkway, Mountain View, CA") إلى إحداثيات جغرافية (خط العرض وخط الطول). العملية العكسية — تحويل الإحداثيات مجدداً إلى عنوان مقروء — تُسمى الترميز الجغرافي العكسي. كلتا العمليتين أساسيتان في أي تطبيق Flutter يدرك الموقع ويحتاج إلى عرض العناوين على الخريطة، أو البحث عن الأماكن، أو تقديم بيانات الموقع بتنسيق سهل الفهم للمستخدم.
حزمة geocoding هي أشهر مكتبة Dart لكلٍّ من الترميز الجغرافي المباشر والعكسي. تُغلِّف خدمات الترميز الجغرافي الأصلية على المنصة لنظامَي Android وiOS، مما يعني أنها تعمل دون اتصال بالإنترنت أو بتكلفة منخفضة دون الحاجة إلى مفتاح API من طرف ثالث للاستخدام الأساسي.
geocoding محرك الترميز الجغرافي المدمج في الجهاز (خدمات Google Play على Android، وCoreLcation على iOS). للترميز الجغرافي من جانب الخادم أو الويب ستستخدم Google Maps Geocoding REST API أو خدمة مشابهة عوضاً عن ذلك.إضافة التبعية
أضف geocoding إلى ملف pubspec.yaml تحت قسم dependencies:
pubspec.yaml
dependencies:
flutter:
sdk: flutter
google_maps_flutter: ^2.5.0
geolocator: ^11.0.0
geocoding: ^3.0.0
شغِّل flutter pub get لتنزيل الحزمة. لا توجد أذونات منصة إضافية غير أذونات الموقع، إذ يستخدم الترميز الجغرافي طلبات شبكة نصية أو بيانات خرائط مخزنة مؤقتاً على الجهاز.
الترميز الجغرافي المباشر: العنوان → LatLng
يحوّل الترميز الجغرافي المباشر سلسلة نصية تمثل عنواناً إلى كائن Location واحد أو أكثر، يحمل كل منها latitude وlongitude. استخدم GeocodingPlatform.instance.locationFromAddress() (أو الاختصار locationFromAddress() بعد استيراد الحزمة).
مثال على الترميز الجغرافي المباشر
import 'package:geocoding/geocoding.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
Future<LatLng?> addressToLatLng(String address) async {
try {
// يُعيد List<Location> — النتيجة الأولى هي عادةً أفضل تطابق
List<Location> locations = await locationFromAddress(address);
if (locations.isEmpty) return null;
final best = locations.first;
return LatLng(best.latitude, best.longitude);
} on NoResultFoundException {
// لم يجد محرك الترميز الجغرافي نتائج لهذا العنوان
debugPrint('No geocoding result for: $address');
return null;
} catch (e) {
debugPrint('Geocoding error: $e');
return null;
}
}
// الاستخدام داخل ودجت
Future<void> _searchAndMoveCamera(String query) async {
final coords = await addressToLatLng(query);
if (coords == null) {
// اعرض SnackBar أو مربع حوار: لم يُعثر على العنوان
return;
}
_mapController.animateCamera(
CameraUpdate.newLatLngZoom(coords, 15.0),
);
setState(() {
_markers = {
Marker(
markerId: const MarkerId('search_result'),
position: coords,
infoWindow: InfoWindow(title: query),
),
};
});
}
locationFromAddress() عناوين جزئية وأسماء أماكن (مثل "برج إيفل" أو "دبي مول"). كلما كان الإدخال أكثر تحديداً، كانت النتيجة أكثر دقة. خذ دائماً locations.first بوصفها أفضل تطابق حين تحتاج إلى دبوس واحد فقط.الترميز الجغرافي العكسي: LatLng → عنوان مقروء
يحوّل الترميز الجغرافي العكسي زوجاً من خطَّي العرض والطول إلى قائمة من كائنات Placemark. يحتوي كل Placemark على حقول عنوان غنية: اسم الشارع، والمنطقة الفرعية، والمحلة (المدينة)، والمنطقة الإدارية (الولاية/الإقليم)، والرمز البريدي، والبلد.
مثال على الترميز الجغرافي العكسي
import 'package:geocoding/geocoding.dart';
Future<String> latLngToAddress(double lat, double lng) async {
try {
List<Placemark> placemarks = await placemarkFromCoordinates(lat, lng);
if (placemarks.isEmpty) return 'موقع غير معروف';
final place = placemarks.first;
// بناء عنوان مقروء من الحقول المتاحة
final parts = [
place.street,
place.subLocality,
place.locality,
place.administrativeArea,
place.country,
].where((p) => p != null && p.isNotEmpty).toList();
return parts.join(', ');
} on NoResultFoundException {
return 'لم يُعثر على عنوان';
} catch (e) {
debugPrint('Reverse geocoding error: $e');
return 'خطأ في استرجاع العنوان';
}
}
// الاستخدام: عند النقر على الخريطة، رمِّز الموضع المنقور عكسياً
void _onMapTapped(LatLng position) async {
setState(() => _tappedPosition = position);
final address = await latLngToAddress(
position.latitude,
position.longitude,
);
setState(() => _tappedAddress = address);
}
عرض النتائج على الخريطة
تتضمن سير عمل الترميز الجغرافي الكاملة ثلاث خطوات عادةً: الحصول على الإحداثيات (مباشر) أو العنوان (عكسي)، تحديث Marker على الخريطة، وعرض النتيجة في واجهة المستخدم. فيما يلي بعض العناصر التي يجمعها ودجت كامل:
- حقل
TextFieldيكتب فيه المستخدم عنواناً — يُشغِّل الترميز الجغرافي المباشر عند الإرسال - خريطة
GoogleMapتحرك الكاميرا نحو الإحداثيات الموجودة وتضع دبوساً - معالج نقر على الخريطة يُرمِّز النقطة المنقورة عكسياً ويعرض عنوان الشارع في ورقة سفلية أو تراكب
- مؤشر تحميل أثناء تنفيذ عمليات الترميز الجغرافي غير المتزامنة
locationFromAddress() وplacemarkFromCoordinates() غير متزامنتَين وقد تُلقيان استثناء NoResultFoundException (لم يُعثر على تطابق) أو PlatformException (خطأ في الشبكة أو الأذونات). غلِّفهما دائماً في try/catch — الاستثناء غير المُعالَج سيُعطل الودجت.الترميز الجغرافي مع الإعداد المحلي
تقبل كلتا الدالتين معامل localeIdentifier اختيارياً (مثل 'ar_AE' أو 'en_US') يطلب النتائج بلغة محددة. هذا مفيد للتطبيقات التي تخدم لغات متعددة.
الترميز الجغرافي العكسي مع الإعداد المحلي
// طلب أسماء أماكن عربية للمستخدمين في الإمارات
List<Placemark> placemarks = await placemarkFromCoordinates(
25.2048, // خط عرض دبي
55.2708, // خط طول دبي
localeIdentifier: 'ar_AE',
);
// سيكون Placemark.locality القادم "دبي" بدلاً من "Dubai"
final cityName = placemarks.first.locality ?? '';
الخلاصة الرئيسية
يجسر الترميز الجغرافي الفجوة بين اللغة البشرية وإحداثيات الآلة. تجعل حزمة geocoding كلا الاتجاهين بسيطَين: الترميز الجغرافي المباشر يحوّل عنواناً نصياً إلى LatLng يمكن تحريك الكاميرا إليه، بينما الترميز الجغرافي العكسي يحوّل إحداثية خام إلى عنوان شارع قابل للعرض. يمنحك الجمع بين هذه الأدوات وgoogle_maps_flutter وgeolocator مجموعة موقع كاملة: اكتشاف موقع الجهاز، وعرضه على الخريطة، والسماح للمستخدمين بالبحث بالعنوان، وعرض أسماء الأماكن الودودة في أي مكان بواجهتك.
locationFromAddress() للترميز الجغرافي المباشر (عنوان → إحداثيات) وplacemarkFromCoordinates() للترميز الجغرافي العكسي (إحداثيات → عنوان). تعامل دائماً مع NoResultFoundException بلطف، واستخدم localeIdentifier الاختياري للتطبيقات متعددة اللغات.