Joylashuv va xarita — expo-location, react-native-maps
Muammodan boshlaylik
Yetkazib berish ilovasi:
- Manzilni qo'lda yozish — "Chilonzor, 9-kvartal, 12-uy, 3-podyezd..." — xatolar va kuryer adashadi
- Kerak — "Mening joylashuvim" tugmasi: GPS dan koordinata, xaritada belgi, foydalanuvchi belgini aniq uyiga suradi
- Va xaritada eng yaqin do'konlar
O'rnatish
npx expo install expo-location react-native-maps
"plugins": [
["expo-location", {
"locationWhenInUsePermission": "Yetkazib berish manzilini va eng yaqin do'konni aniqlash uchun."
}]
]
Joriy joylashuv
import * as Location from 'expo-location';
async function joylashuvniOl() {
const { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
Alert.alert('Ruxsat kerak', "Manzilni xaritadan qo'lda tanlashingiz mumkin.");
return null;
}
const joy = await Location.getCurrentPositionAsync({
accuracy: Location.Accuracy.Balanced,
});
return { lat: joy.coords.latitude, lng: joy.coords.longitude };
}
Aniqlik
Accuracy |
Aniqlik | Batareya | Qachon |
|---|---|---|---|
Lowest / Low |
~3 km / ~1 km | Kam | Shaharni aniqlash |
Balanced |
~100 m | O'rtacha | Eng yaqin do'kon |
High |
~10 m | Ko'p | Yetkazish manzili |
BestForNavigation |
Eng aniq | Juda ko'p | Navigatsiya |
GPS signali binolar ichida zaif — High bir necha soniya olishi mumkin. Tez javob uchun — avval oxirgi ma'lum joylashuv:
const tez = await Location.getLastKnownPositionAsync(); // bir zumda, lekin eski bo'lishi mumkin
Koordinata ↔ manzil
// Koordinatadan manzil
const [manzil] = await Location.reverseGeocodeAsync({ latitude: 41.3111, longitude: 69.2797 });
// { city: 'Tashkent', district: 'Yunusobod', street: '...', name: '...' }
// Manzildan koordinata
const [joy] = await Location.geocodeAsync('Amir Temur ko\'chasi, Toshkent');
Tizim geokoderi O'zbekiston manzillarini har doim ham aniq bilmaydi. Professional ilovalarda — Yandex, Google yoki 2GIS geocoding API lari.
Xarita
import MapView, { Marker, PROVIDER_GOOGLE } from 'react-native-maps';
const TOSHKENT = { latitude: 41.3111, longitude: 69.2797, latitudeDelta: 0.1, longitudeDelta: 0.1 };
export default function Xarita() {
return (
<MapView
style={{ flex: 1 }}
initialRegion={TOSHKENT}
showsUserLocation
showsMyLocationButton
>
<Marker
coordinate={{ latitude: 41.3111, longitude: 69.2797 }}
title="Markaziy do'kon"
description="09:00 – 22:00"
/>
</MapView>
);
}
region/initialRegion— markaz vadelta(masshtab: kichik delta — yaqinroq)showsUserLocation— foydalanuvchi joylashuvi ko'k nuqta bilan- iOS da standart — Apple Maps; Android da — Google Maps
[!INFO] Android da Google Maps production build uchun API kalit talab qiladi (
app.json→android.config.googleMaps.apiKey). Expo Go da sinash uchun kalit kerak emas. Kalitni Google Cloud Console da faqat ilovangiz paket nomi va imzosi bilan cheklang.
Do'konlar ro'yxati
<MapView style={{ flex: 1 }} initialRegion={TOSHKENT}>
{dokonlar.map((d) => (
<Marker
key={d.id}
coordinate={{ latitude: d.lat, longitude: d.lng }}
title={d.nom}
pinColor="#0284c7"
onCalloutPress={() => router.push(`/dokon/${d.id}`)}
/>
))}
</MapView>
Yuzlab belgi bo'lsa — klasterlash kutubxonasi (react-native-map-clustering), aks holda xarita sekinlashadi.
Manzil tanlash — markazdagi belgi
Yetkazish ilovalaridagi klassik naqsh: belgi ekran markazida qotib turadi, foydalanuvchi xaritani suradi:
export default function ManzilTanlash() {
const ref = useRef<MapView>(null);
const [markaz, setMarkaz] = useState(TOSHKENT);
const [manzil, setManzil] = useState('');
async function menga() {
const j = await joylashuvniOl();
if (j) ref.current?.animateToRegion({ latitude: j.lat, longitude: j.lng, latitudeDelta: 0.005, longitudeDelta: 0.005 }, 500);
}
async function harakatTugadi(r: Region) {
setMarkaz(r);
const [m] = await Location.reverseGeocodeAsync({ latitude: r.latitude, longitude: r.longitude });
setManzil([m?.street, m?.name].filter(Boolean).join(', '));
}
return (
<View style={{ flex: 1 }}>
<MapView ref={ref} style={{ flex: 1 }} initialRegion={TOSHKENT} onRegionChangeComplete={harakatTugadi} />
{/* Markazdagi belgi — xarita ustida, harakatsiz */}
<View pointerEvents="none" style={[StyleSheet.absoluteFill, { justifyContent: 'center', alignItems: 'center' }]}>
<Ionicons name="location" size={44} color="#dc2626" style={{ marginBottom: 44 }} />
</View>
<Pressable onPress={menga} style={{ position: 'absolute', right: 16, bottom: 180, backgroundColor: 'white', padding: 12, borderRadius: 24 }}>
<Ionicons name="locate" size={24} color="#0284c7" />
</Pressable>
<View style={{ padding: 16, gap: 12, backgroundColor: 'white' }}>
<Text style={{ fontSize: 16 }}>{manzil || 'Manzilni tanlang'}</Text>
<Tugma matn="Shu manzil" onPress={() => saqla({ lat: markaz.latitude, lng: markaz.longitude, matn: manzil })} />
</View>
</View>
);
}
pointerEvents="none"— belgi xarita imo-ishoralarini to'smasinmarginBottom: 44— belgining uchi markazga tushsin, o'rtasi emasonRegionChangeComplete— surish tugaganda (har kadrda emas) — geokoder so'rovlari kamroq
Masofa — eng yaqin do'kon
function masofaKm(a: { lat: number; lng: number }, b: { lat: number; lng: number }) {
const R = 6371;
const dLat = ((b.lat - a.lat) * Math.PI) / 180;
const dLng = ((b.lng - a.lng) * Math.PI) / 180;
const x = Math.sin(dLat / 2) ** 2 +
Math.cos((a.lat * Math.PI) / 180) * Math.cos((b.lat * Math.PI) / 180) * Math.sin(dLng / 2) ** 2;
return 2 * R * Math.asin(Math.sqrt(x));
}
const yaqin = [...dokonlar].sort((d1, d2) => masofaKm(men, d1) - masofaKm(men, d2))[0];
Haversine formulasi — to'g'ri chiziqdagi masofa. Yo'l bo'yicha masofa va vaqt — marshrut API lari (Google Directions, OSRM).
Ko'p do'kon bo'lsa — saralashni serverda qiling (MySQL ST_Distance_Sphere, PostgreSQL PostGIS).
Fon rejimida kuzatish
Kuryer ilovasi — ilova yopiq bo'lsa ham joylashuvni yuborish:
await Location.requestBackgroundPermissionsAsync();
await Location.startLocationUpdatesAsync('kuryer-kuzatuv', { accuracy: Location.Accuracy.High, timeInterval: 15000 });
expo-task-manager bilan, development build da. Fon joylashuvi — eng "nozik" ruxsat: Google Play va App Store qat'iy asoslash talab qiladi, foydalanuvchilar esa ko'pincha rad etadi. Faqat haqiqatan zarur bo'lganda.
Nima uchun shunday?
Nima uchun joylashuvning turli aniqlik darajalari bor — doim eng aniqini olsa bo'lmaydimi?
Chunki aniqlik — batareya va vaqt evaziga. Eng aniq joylashuv uchun GPS sun'iy yo'ldoshlarini qidirish kerak — bu bir necha soniya va ko'p energiya, ayniqsa binolar ichida. Past aniqlik esa Wi-Fi va mobil minoralar orqali bir zumda keladi. "Qaysi shahardasiz?" savoli uchun GPS ni yoqish — isrof. Mobil dasturlashda har bir imkoniyat kerakli darajada ishlatiladi.
Xulosa
Eslab qoling:
requestForegroundPermissionsAsync→getCurrentPositionAsync({ accuracy })- Aniqlik — vazifaga mos:
Balanced— do'kon,High— manzil getLastKnownPositionAsync— tez, lekin eski bo'lishi mumkinreverseGeocodeAsync/geocodeAsync— koordinata ↔ manzilMapView,Marker,showsUserLocation,animateToRegion- Manzil tanlash: markazdagi qotgan belgi +
onRegionChangeComplete - Android Google Maps — production da API kalit
- Fon joylashuvi — faqat zarur bo'lganda
Keyingi dars
42-dars: Push bildirishnomalar — expo-notifications