Mundarija (23)
- Bu darsda
- 1. Nega bu kerak?
- 2. Koordinata: kenglik, uzunlik va tekshiruv
- 2.1 Ikki son va bitta standart
- 2.2 Eng ko'p xato: tartib
- 3. Masofa va eng yaqin obyekt
- 3.1 Haversine
- 3.2 Eng yaqin kuryer: to'rtburchak, keyin formula
- 4. Yetkazish zonasi: nuqta poligon ichidami?
- 5. Geocoding va IP geolokatsiya
- 5.1 Manzildan koordinataga
- 5.2 IP bo'yicha joylashuv
- 6. Kuryer kuzatuvi: arxitektura
- 7. Hujumchi nigohi
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson): bo'shliqlarni to'ldiring
- 2-mashq (o'rta): soxta GPS'ni tezlik bo'yicha ushlash
- 3-mashq (qiyin): zona bo'yicha yetkazish narxi
- 10. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi
- 11. Real ishda
- Xulosa
- Manbalar
Geolokatsiya backend'da: masofa, eng yaqin obyekt, yetkazish zonasi va geocoding
Qisqacha: Joylashuv — kenglik (latitude) va uzunlik (longitude) juftligi. Ikki nuqta orasidagi masofa Haversine formulasi bilan hisoblanadi, ko'p nuqta ichidan eng yaqinini topish esa geo-indeks (PostGIS, Redis GEO) ishi. Yetkazish zonasi — poligon, "manzil zona ichidami?" — nuqta-poligon tekshiruvi. Manzil matnini koordinataga aylantirish (geocoding) tashqi xizmat orqali, kalit, limit va kesh bilan qilinadi. Joylashuv — shaxsga doir ma'lumot: aniqligini kerakli darajada saqlang.
Bu darsda
- Kelgan koordinatani tekshirib, kenglik va uzunlik almashib ketganini ushlaysiz.
- Haversine bilan masofa hisoblab, chegaralovchi to'rtburchak bilan eng yaqin kuryerlarni tez topasiz.
- Poligon bo'yicha "manzil yetkazish zonasidami?" degan savolga javob berasiz.
- GeoJSON tartibi, geocoding va IP geolokatsiya xizmatlarining shartlari va xavflarini bilasiz.
- Kuryer kuzatuvi arxitekturasini (Redis GEO + SSE) chizib bera olasiz.
Oldin bilishingiz kerak: PostGIS va geo so'rovlar, Maxsus tuzilmalar (Redis GEO), Server-Sent Events, Xaritalar II: Yandex Maps, Google Maps va MapLibre.
1. Nega bu kerak?
«Bahor» oshxonasi yetkazib berishni boshladi. Backend'ga darhol to'rtta savol keladi:
- Mijoz manzili oshxonadan qancha uzoqda — yetkazish narxi qancha?
- Buyurtmani qaysi kuryer oladi — kim eng yaqin?
- Manzil yetkazish hududi ichidami yoki Yangiyo'lda?
- Mijoz "Chilonzor 9-kvartal, 12-uy" deb yozdi — bu xaritada qayer?
27-qismda bazaning geo imkoniyatlarini o'rgandik: PostGIS (ST_DWithin, geography) va Redis GEO (GEOADD, GEOSEARCH). U yerda Haversine formulasini ham SQL'da yozgandik. Bu darsda backend tomoniga qaraymiz: koordinata qayerdan keladi, qanday tekshiriladi, qaysi vosita qaysi savolga, tashqi xizmatlar qanday shartlar bilan ishlaydi va joylashuvni qanday himoya qilish kerak.
Kanon tajribasi (2026-10-27): Haversine, Redis GEO va GeoJSON. Geocoding (Yandex, Google, Nominatim) va IP geolokatsiya (MaxMind GeoLite2) — sinalmagan: kalit va litsenziya talab qiladi, ular hujjat bo'yicha beriladi. Kanondagi koordinatalar xarita bo'yicha taxminiy, o'quv misoli.
2. Koordinata: kenglik, uzunlik va tekshiruv
2.1 Ikki son va bitta standart
- Kenglik (latitude, lat) — ekvatordan shimol yoki janubga: −90 dan 90 gacha. Toshkent ~41.3.
- Uzunlik (longitude, lon, lng) — Grinvichdan sharq yoki g'arbga: −180 dan 180 gacha. Toshkent ~69.2.
Telefon GPS'i, xarita kutubxonalari va geocoding xizmatlari koordinatani WGS 84 tizimida beradi. Agar boshqa tizim aytilmagan bo'lsa — shu.
Aniqlik — kasr qismining xonalari soni:
| Xona | Ekvatorda taxminan | Nimaga yetadi |
|---|---|---|
| 2 (41.33) | 1.1 km | shahar tumani |
| 3 (41.326) | 110 m | mahalla, ko'cha |
| 4 (41.3263) | 11 m | bino |
| 5 (41.32634) | 1.1 m | kirish eshigi |
GPS telefon odatda 5–20 m aniqlik beradi. Bazada 7–8 xonadan ko'pini saqlash ma'nosiz — ular shovqin. Maxfiylik uchun esa kamaytirish foydali (pastda).
2.2 Eng ko'p xato: tartib
Ikki son, ikki tartib. Turli vositalar turlicha yozadi:
| Qayerda | Tartib |
|---|---|
| Google Maps havola, ko'p UI | lat, lon |
GeoJSON, PostGIS POINT, Redis GEOADD |
lon, lat |
Leaflet L.latLng |
lat, lon |
Kanon ikkalasini almashtirib ko'rgan:
lat/lon almashib qolsa: 5.0 km (noto'g'ri, lekin xato bermaydi)To'g'ri masofa ~4.1 km edi. Almashgan nuqtalar ham "haqiqiy" koordinata: 69.2 kenglik — Shimoliy Muz okeani qirg'og'i, 41.3 uzunlik — Rossiya shimolida. Formula hech qanday xato bermadi, natija shunchaki noto'g'ri. Bunday xato testdan ham o'tib ketadi, agar test nuqtalari bir-biriga yaqin bo'lsa.
Himoya — validatsiya. Diapazon tekshiruvi (|lat| ≤ 90) bu xatoni ushlamaydi: 69.2 ham kenglik bo'la oladi. Xizmat hududini bilsangiz — chegaralovchi to'rtburchak (bounding box) bilan tekshiring:
// O'zbekiston atrofi — taxminiy, o'quv misoli
const HUDUD = { minLat: 37, maxLat: 46, minLon: 55, maxLon: 74 };
function koordinataTekshir(lat, lon) {
if (!Number.isFinite(lat) || !Number.isFinite(lon)) {
return "son emas";
}
if (Math.abs(lat) > 90 || Math.abs(lon) > 180) {
return "diapazondan tashqari";
}
const ichida = (a, b) =>
a >= HUDUD.minLat && a <= HUDUD.maxLat &&
b >= HUDUD.minLon && b <= HUDUD.maxLon;
if (ichida(lat, lon)) return "ok";
if (ichida(lon, lat)) return "lat va lon almashgan";
return "xizmat hududidan tashqarida";
}
console.log(koordinataTekshir(41.3263, 69.2348));
console.log(koordinataTekshir(69.2348, 41.3263));
console.log(koordinataTekshir(48.8566, 2.3522));
console.log(koordinataTekshir(Number("41,32"), 69.2));
console.log(koordinataTekshir(141.3, 69.2));Konsolda:
ok
lat va lon almashgan
xizmat hududidan tashqarida
son emas
diapazondan tashqariTo'rtinchi qatorda yana bir tuzoq: "41,32" (vergul bilan — rus va o'zbek klaviaturasidagi odat) Number() da NaN bo'ladi. parseFloat("41,32") esa jim 41 qaytarardi — 36 km xato. Shuning uchun Number + isFinite. Zod bilan: z.number().min(-90).max(90) va refine bilan hudud (Zod 4 serverda).
3. Masofa va eng yaqin obyekt
3.1 Haversine
PostGIS va geo so'rovlar darsida Haversine'ni SQL'da yozgandik. Bu — o'sha formulaning kanondagi JS varianti:
function haversine([lon1, lat1], [lon2, lat2]) {
const R = 6371008.8; // Yer o'rtacha radiusi, metr
const r = (x) => (x * Math.PI) / 180;
const a =
Math.sin(r(lat2 - lat1) / 2) ** 2 +
Math.cos(r(lat1)) * Math.cos(r(lat2)) *
Math.sin(r(lon2 - lon1) / 2) ** 2;
return 2 * R * Math.asin(Math.sqrt(a));
}
const chorsu = [69.2348, 41.3263];
const amirTemur = [69.2797, 41.3111];
const registon = [66.975, 39.6547];
console.log(Math.round(haversine(chorsu, amirTemur)), "m");
console.log((haversine(chorsu, registon) / 1000).toFixed(1), "km");Konsolda:
4113 m
266.6 kmKanon natijasi bilan bir xil (4 113 m va 266.6 km). Funksiya [lon, lat] tartibini kutadi — GeoJSON bilan bir xil, shuning uchun parametrlarni destrukturizatsiyada nomladik: tartib kodning o'zida ko'rinadi.
Kanon Redis bilan ham solishtirgan:
Redis GEOSEARCH 5 km (Chorsu): chorsu 0 m, ilmhamrohOfis 3014 m, amirTemur 4114 m
GEODIST chorsu–amirTemur: 4114 m (Redis — sfera R=6372797.56 m, shuning uchun Haversine'dan biroz farq)1 metr farq — Redis boshqa radius ishlatadi va koordinatani geohash qilib saqlaydi (Maxsus tuzilmalar darsida batafsil). Bu to'g'ri chiziq masofasi. Kuryer esa ko'chalar bo'ylab yuradi: haqiqiy yo'l odatda 1.2–1.5 barobar uzun. Yo'l masofasi va vaqti — marshrut xizmatlari (OSRM, Yandex, Google Routing) ishi; narxni to'g'ri chiziq bilan hisoblasangiz, buni mijozga ochiq ayting.
3.2 Eng yaqin kuryer: to'rtburchak, keyin formula
200 ta kuryer — har buyurtmada 200 marta Haversine. Bu hali tez. 200 000 nuqta — sekin. Indekssiz ham ishni ancha kamaytirish mumkin: avval arzon to'rtburchak filtri (faqat solishtirish), keyin qolganlarga qimmat formula. 1 daraja kenglik ≈ 111 km, 1 daraja uzunlik ≈ 111 km × cos(kenglik):
function haversine([lon1, lat1], [lon2, lat2]) {
const R = 6371008.8;
const r = (x) => (x * Math.PI) / 180;
const a =
Math.sin(r(lat2 - lat1) / 2) ** 2 +
Math.cos(r(lat1)) * Math.cos(r(lat2)) *
Math.sin(r(lon2 - lon1) / 2) ** 2;
return 2 * R * Math.asin(Math.sqrt(a));
}
function yaqinlar(markaz, nuqtalar, radiusM) {
const [lon, lat] = markaz;
const dLat = radiusM / 111_320;
const dLon = radiusM / (111_320 * Math.cos((lat * Math.PI) / 180));
let formula = 0;
const natija = [];
for (const n of nuqtalar) {
const [x, y] = n.joy;
const uzoq = Math.abs(y - lat) > dLat || Math.abs(x - lon) > dLon;
if (uzoq) continue;
formula++;
const m = haversine(markaz, n.joy);
if (m <= radiusM) natija.push({ id: n.id, m: Math.round(m) });
}
natija.sort((a, b) => a.m - b.m);
return { natija, formula };
}
const kuryerlar = [
{ id: "k1", joy: [69.2401, 41.2995] },
{ id: "k2", joy: [69.2797, 41.3111] },
{ id: "k3", joy: [69.2866, 41.2928] },
{ id: "k4", joy: [64.4286, 39.7747] },
{ id: "k5", joy: [69.2350, 41.3270] },
];
console.log(yaqinlar([69.2348, 41.3263], kuryerlar, 5000));Konsolda:
{
natija: [ { id: 'k5', m: 80 }, { id: 'k1', m: 3013 }, { id: 'k2', m: 4113 } ],
formula: 4
}Beshta kuryerdan to'rttasi to'rtburchakdan o'tdi, Buxorodagi k4 arzon solishtirishda tashlandi. To'rtburchak — taxminiy: uning burchaklari doiradan tashqarida, shuning uchun aniq tekshiruv baribir formula bilan. PostGIS'ning GiST indeksi va Redis'ning geohash'i aynan shu g'oyani katta hajmda qiladi: avval indeks bilan nomzodlar, keyin aniq masofa.
| Vosita | Qachon |
|---|---|
| JS'da to'rtburchak + Haversine | yuzlab nuqta, xotirada |
| Redis GEO | tez o'zgaradigan nuqtalar (kuryer, taksi), radius bo'yicha |
| PostGIS | doimiy obyektlar, poligonlar, murakkab so'rov, SQL bilan birga |
MongoDB 2dsphere |
ma'lumot allaqachon Mongo'da bo'lsa |
4. Yetkazish zonasi: nuqta poligon ichidami?
Yetkazish hududi aylana emas — u tumanlar chegarasi, daryo, shahar halqa yo'li bilan chizilgan ko'pburchak. Uni GeoJSON formatida saqlash qulay (RFC 7946). Kanon chiqishi:
GeoJSON (RFC 7946 — [lon, lat] tartibi): {"type":"Feature","geometry":{"type":"Point","coordinates":[69.2348,41.3263]},"properties":{"nom":"Chorsu"}}GeoJSON'da poligon — {"type": "Polygon", "coordinates": [[[lon, lat], ...]]}: birinchi va oxirgi nuqta bir xil (halqa yopiq). Admin panelda xarita ustida chiziladi (Leaflet, MapLibre — Xaritalar I), backend JSONB yoki PostGIS geometry ustunida saqlaydi.
"Nuqta ichidami?" savoli — ray casting (nur otish) algoritmi: nuqtadan bir tomonga nur chizasiz va u poligon chegarasini necha marta kesib o'tishini sanaysiz. Toq — ichida, juft — tashqarida.
function ichidami([x, y], halqa) {
let ichida = false;
for (let i = 0, j = halqa.length - 1; i < halqa.length; j = i++) {
const [xi, yi] = halqa[i];
const [xj, yj] = halqa[j];
const kesadi =
yi > y !== yj > y &&
x < ((xj - xi) * (y - yi)) / (yj - yi) + xi;
if (kesadi) ichida = !ichida;
}
return ichida;
}
// Chilonzor atrofidagi soddalashtirilgan zona — o'quv misoli
const zona = [
[69.18, 41.26], [69.24, 41.26], [69.24, 41.30],
[69.21, 41.32], [69.18, 41.30], [69.18, 41.26],
];
console.log(ichidami([69.2, 41.28], zona));
console.log(ichidami([69.2348, 41.3263], zona));
console.log(ichidami([69.235, 41.305], zona));Konsolda:
true
false
falseUchinchi nuqta poligonning "kesilgan burchagi" yonida: to'rtburchak tekshiruvi (69.18–69.24, 41.26–41.32) uni ichida der edi, poligon esa yo'q. Shuning uchun zona uchun to'rtburchak yetmaydi.
Bu algoritm koordinatalarni tekis deb oladi. Shahar ichidagi zona uchun xato sezilmaydi. Yuzlab kilometrli hudud, qutbga yaqin joylar yoki 180-meridian kesishmasi — PostGIS ST_Contains / ST_Covers ishi.
Tekshirib ko'ring: Nuqta poligon chegarasining aynan ustida bo'lsa-chi?
Javob
Ray casting natijasi chegarada barqaror emas: tomon qanday yo'nalgani va suzuvchi nuqta yaxlitlashiga qarab true yoki false chiqishi mumkin. Biznes uchun qaror kerak: "chegaradagi uy — zona ichida". PostGIS'da buning uchun ikki funksiya bor: ST_Contains chegarani ichiga kiritmaydi, ST_Covers kiritadi. Real hayotda GPS xatosi 5–20 m — chegaraga juda yaqin manzillar uchun operator tasdig'i yoki zonani biroz kengaytirish (buffer) amaliy yechim.
5. Geocoding va IP geolokatsiya
5.1 Manzildan koordinataga
Geocoding — "Toshkent, Chilonzor 9-kvartal, 12-uy" → [69.20, 41.28]. Reverse geocoding — teskarisi: koordinatadan manzil. Buni o'zingiz qilolmaysiz — manzillar bazasi kerak. Xizmatlar (2026-10 holatiga, rasmiy hujjat bo'yicha; kanonda sinalmagan):
| Xizmat | Kalit | Eslatma |
|---|---|---|
| Yandex Geocoder API | kerak | O'zbekiston manzillari bo'yicha keng tarqalgan; tarif va bepul limit shartlari bor |
| Google Geocoding API | kerak, billing hisobi bilan | foydalanish shartlari natijani Google xaritasi bilan ko'rsatishni talab qiladi |
| Nominatim (OpenStreetMap) | kerak emas | ommaviy server: ko'pi bilan 1 so'rov/soniya, aniq User-Agent, ommaviy geocoding taqiqlangan |
Har birining foydalanish shartlari bor — ular kod emas, huquqiy talab. Masalan, Nominatim'ning ommaviy serveriga har buyurtmada so'rov yuborish — siyosatni buzish, bloklanasiz. Katta hajm uchun o'z Nominatim serveringizni ko'tarasiz yoki pullik xizmat olasiz.
Hujjat bo'yicha Nominatim so'rovi (nocheck — tashqi tarmoq, chiqish ko'rsatilmaydi):
const url = new URL("https://nominatim.openstreetmap.org/search");
url.search = new URLSearchParams({
q: "Chorsu bozori, Toshkent",
format: "jsonv2",
limit: "1",
countrycodes: "uz",
});
const r = await fetch(url, {
headers: { "User-Agent": "vazifalar-api/1.0 (admin@example.com)" },
signal: AbortSignal.timeout(3000),
});
const [joy] = await r.json();
console.log(joy?.lat, joy?.lon, joy?.display_name);Uch backend qoidasi:
- Kesh. Bir manzil bir marta geocoding qilinadi va natija bazada saqlanadi. Har so'rovda tashqi API — pul, limit va kechikish.
- Timeout va zaxira yo'l. Xizmat javob bermasa, buyurtma to'xtab qolmasin:
AbortSignal.timeout, keyin "operator manzilni aniqlaydi" holati (Built-in fetch va undici). - Natijani tasdiqlatish. Geocoding taxmin qiladi: "Chilonzor 9" bir nechta joyga mos kelishi mumkin. Eng yaxshi UX — natijani xaritada ko'rsatib, mijozga pin'ni surish imkonini berish. Telefon GPS koordinatasi esa odatda matndan aniqroq.
5.2 IP bo'yicha joylashuv
IP geolokatsiya — so'rov IP manzilidan taxminiy mamlakat va shahar. Foydalanuvchi joylashuvga ruxsat bermasa ham ishlaydi, lekin aniqligi past: mamlakat odatda to'g'ri, shahar — ko'pincha, ko'cha — hech qachon. Mobil operatorlar va VPN uni yanada buzadi.
Keng ishlatiladigan bepul baza — MaxMind GeoLite2. Hujjatga ko'ra uni yuklab olish uchun bepul hisob va litsenziya kaliti kerak (2019-yil oxiridan), litsenziya shartlari esa ma'lumotni yangilab turishni talab qiladi. Bu kanonda sinalmagan. Node'da .mmdb faylni o'qiydigan kutubxonalar bor (masalan @maxmind/geoip2-node).
IP geolokatsiya nimaga yaraydi: sukut tilini, valyutani yoki shaharni taklif qilish; shubhali kirishni aniqlash ("odatda Toshkentdan kirardi, hozir boshqa qit'adan" — 28-qism xavfsizlik g'oyasi). Nimaga yaramaydi: yetkazish manzili, narx hisobi. Proksi orqasida IP'ni to'g'ri olishni unutmang — trust proxy sozlamasi (Reverse proxy orqasida Node).
6. Kuryer kuzatuvi: arxitektura
Mijoz kuryerni xaritada jonli ko'rmoqchi. Kanon bu yerda kod yozmagan — u mavjud ikki g'oyani birlashtirishni taklif qiladi: Redis GEO (joylar) va Server-Sent Events (mijozga oqim).
sequenceDiagram
participant K as Kuryer ilovasi
participant A as API
participant R as Redis GEO
participant M as Mijoz brauzeri
M->>A: GET /buyurtma/7/kuzatuv (SSE)
loop har 5-10 soniyada
K->>A: POST /joy lat, lon
A->>A: tekshiruv, tezlik chegarasi
A->>R: GEOADD kuryerlar
A-->>M: event joy (yaxlitlangan)
end
Note over A,R: Yangi buyurtma GEOSEARCH bilan yaqin kuryerni oladiDiagrammaga qarang: kuryer joyni yuboradi, API uni ikki joyga beradi — Redis'ga (kim qayerda, eng yaqinni qidirish uchun) va kuzatayotgan mijozga. Bir nechta API nusxasi bo'lsa, SSE xabari Redis Pub/Sub orqali tarqatiladi (Realtime'ni masshtablash).
Amaliy qarorlar:
- Chastota. Har soniyada yuborish — batareya va trafik. 5–10 soniya va "100 m dan ko'p siljisa" qoidasi odatda yetadi.
- Tarix. Redis GEO faqat oxirgi joyni saqlaydi. Yo'l tarixi kerak bo'lsa (nizolar, hisob) — alohida jadval, va uni qancha saqlash — maxfiylik qarori.
- Eskirish. Kuryer ilovani yopdi — Redis'da u "joyida" qoladi. Har joyga vaqt qo'shing va 2 daqiqadan eski joylarni qidiruvdan chiqaring.
7. Hujumchi nigohi
Joylashuv — shaxsga doir ma'lumot (O'zbekistonda O'RQ-547 qonuni, 21-qismda ko'rgandik). Uy manzili, har kungi yo'l, ish joyi — bular odamni topish uchun yetadi. Hujumchi nima qiladi:
- Aniq masofadan joyni hisoblaydi (trilateratsiya). Ilova "bu kuryer sizdan 1 234 m uzoqda" deb ko'rsatsa, hujumchi uch xil nuqtadan so'rab, kuryerning aniq joyini topadi. Tanishuv ilovalarida shunday hodisalar ommaviy yozilgan. Himoya: masofani yaxlitlash ("~1 km"), koordinatani mijozga faqat kerak bo'lganda va qisqartirilgan aniqlikda berish.
- Soxta GPS yuboradi. Kuryer ilovasi joyni o'zgartiradigan dastur bilan "yetib keldim" deydi. Himoya: server tezlikni tekshiradi — ikki nuqta orasidagi masofa / vaqt 150 km/soatdan oshsa, shubhali. Muhim amallar (yetkazildi) uchun qo'shimcha dalil (kod, rasm).
- Begona buyurtmani kuzatadi.
/buyurtma/8/kuzatuv— boshqa odamning kuryeri va manzili. Himoya: resurs egaligi tekshiruvi (28-qism) va kuzatuv faqat buyurtma faol bo'lganda. - Geocoding orqali SSRF. Foydalanuvchi bergan URL bilan "xarita xizmatiga" so'rov yuborish — SSRF va tashqi URL'lar. Himoya: tashqi xizmat manzili konfiguratsiyada, foydalanuvchidan faqat matn.
Qoida: kerakli aniqlik — kerakli muddatga. Statistika uchun 2 xona (1 km) yetadi. Yetkazilgan buyurtmaning aniq yo'l tarixi — bir necha kundan keyin o'chiriladi yoki yaxlitlanadi.
8. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| Masofa biroz noto'g'ri, xato yo'q | lat va lon almashgan | hudud to'rtburchagi bilan validatsiya |
41,32 → 41 |
parseFloat vergulni kesdi |
Number + isFinite, frontend nuqta yuborsin |
| Kuryer "joyida", aslida uyida | eski joy Redis'da qoldi | joy vaqti va eskirish chegarasi |
| Narx mijoz kutganidan kam | to'g'ri chiziq masofasi | marshrut xizmati yoki koeffitsient, ochiq aytish |
| Nominatim 403 yoki blok | User-Agent yo'q yoki 1 so'rov/s oshdi |
kesh, limit, o'z serveri |
| Zona chegarasidagi uy goh ichida, goh tashqarida | ray casting chegarada beqaror | ST_Covers yoki buffer |
9. Mashqlar
Mashqlar kurs/mashqlar/29/56-geo/ da.
1-mashq (oson): bo'shliqlarni to'ldiring
- GeoJSON'da koordinata tartibi —
[, lat] - Kenglik diapazoni: −90 dan gacha
- Taxminan 11 m aniqlik uchun kasr xonalari soni —
- Manzil matnini koordinataga aylantirish —
- Nuqta poligon ichidami — nur kesishishlar soni bo'lsa ichida
Yechim
- lon — avval uzunlik, keyin kenglik (RFC 7946).
- 90 — uzunlik esa −180 dan 180 gacha.
- 4 — 0.0001 daraja ≈ 11 m.
- geocoding — teskarisi reverse geocoding.
- toq — ray casting.
2-mashq (o'rta): soxta GPS'ni tezlik bo'yicha ushlash
shubhali(nuqtalar, maksKmS) funksiyasini yozing. nuqtalar — kuryerning vaqt tartibidagi joylari { t, joy: [lon, lat] } (t — soniya). Har ketma-ket juftlik uchun tezlikni (km/soat) Haversine bilan hisoblang va maksKmS dan oshgan juftliklarni { dan, gacha, kmS } ko'rinishida qaytaring (kmS — butun songa yaxlitlangan). Vaqt farqi 0 yoki manfiy bo'lsa — u ham shubhali (kmS: Infinity).
Yechim
function haversine([lon1, lat1], [lon2, lat2]) {
const R = 6371008.8;
const r = (x) => (x * Math.PI) / 180;
const a =
Math.sin(r(lat2 - lat1) / 2) ** 2 +
Math.cos(r(lat1)) * Math.cos(r(lat2)) *
Math.sin(r(lon2 - lon1) / 2) ** 2;
return 2 * R * Math.asin(Math.sqrt(a));
}
function shubhali(nuqtalar, maksKmS) {
const natija = [];
for (let i = 1; i < nuqtalar.length; i++) {
const a = nuqtalar[i - 1];
const b = nuqtalar[i];
const soat = (b.t - a.t) / 3600;
const km = haversine(a.joy, b.joy) / 1000;
const kmS = soat > 0 ? Math.round(km / soat) : Infinity;
if (kmS > maksKmS) natija.push({ dan: a.t, gacha: b.t, kmS });
}
return natija;
}
const yol = [
{ t: 0, joy: [69.2348, 41.3263] },
{ t: 300, joy: [69.2500, 41.3200] },
{ t: 360, joy: [66.9750, 39.6547] },
{ t: 900, joy: [66.9760, 39.6550] },
{ t: 900, joy: [66.9770, 39.6551] },
];
console.log(shubhali(yol, 150));Konsolda:
[
{ dan: 300, gacha: 360, kmS: 16021 },
{ dan: 900, gacha: 900, kmS: Infinity }
]Birinchi qadam (5 daqiqada ~1.4 km) — oddiy. Ikkinchisida bir daqiqada Samarqandga "sakrash" — o'n minglab km/soat. Oxirgisida vaqt bir xil, joy boshqa — ikkita qurilma yoki soxta ma'lumot. Tezlik chegarasi transportga qarab tanlanadi: piyoda kuryer uchun 150 emas, 30 km/soat ham ko'p.
3-mashq (qiyin): zona bo'yicha yetkazish narxi
Bir nechta zona bor, har birida narx: { nom, narx, halqa }. yetkazishNarxi(joy, zonalar) yozing: nuqta qaysi zona(lar) ichida bo'lsa, eng arzon zonani qaytarsin ({ nom, narx }); hech biriga tushmasa — null. Avval har zonaning chegaralovchi to'rtburchagini hisoblab, nuqta undan tashqarida bo'lsa ray casting'ni chaqirmang. Nechta zona uchun ray casting chaqirilganini ham qaytaring.
Yechim
function ichidami([x, y], halqa) {
let ichida = false;
for (let i = 0, j = halqa.length - 1; i < halqa.length; j = i++) {
const [xi, yi] = halqa[i];
const [xj, yj] = halqa[j];
const kesadi =
yi > y !== yj > y &&
x < ((xj - xi) * (y - yi)) / (yj - yi) + xi;
if (kesadi) ichida = !ichida;
}
return ichida;
}
function chegara(halqa) {
const xs = halqa.map((p) => p[0]);
const ys = halqa.map((p) => p[1]);
return [
Math.min(...xs), Math.min(...ys),
Math.max(...xs), Math.max(...ys),
];
}
function yetkazishNarxi(joy, zonalar) {
let eng = null;
let tekshirildi = 0;
for (const z of zonalar) {
const [x1, y1, x2, y2] = chegara(z.halqa);
const [x, y] = joy;
if (x < x1 || x > x2 || y < y1 || y > y2) continue;
tekshirildi++;
if (ichidami(joy, z.halqa) && (!eng || z.narx < eng.narx)) {
eng = { nom: z.nom, narx: z.narx };
}
}
return { zona: eng, tekshirildi };
}
const zonalar = [
{ nom: "Markaz", narx: 15000, halqa: [
[69.22, 41.29], [69.30, 41.29], [69.30, 41.34], [69.22, 41.34],
[69.22, 41.29],
] },
{ nom: "Chilonzor", narx: 12000, halqa: [
[69.18, 41.26], [69.24, 41.26], [69.24, 41.30], [69.21, 41.32],
[69.18, 41.30], [69.18, 41.26],
] },
{ nom: "Shahar", narx: 25000, halqa: [
[69.10, 41.20], [69.40, 41.20], [69.40, 41.40], [69.10, 41.40],
[69.10, 41.20],
] },
];
console.log(yetkazishNarxi([69.2348, 41.3263], zonalar));
console.log(yetkazishNarxi([69.23, 41.295], zonalar));
console.log(yetkazishNarxi([69.35, 41.22], zonalar));
console.log(yetkazishNarxi([64.43, 39.77], zonalar));Konsolda:
{ zona: { nom: 'Markaz', narx: 15000 }, tekshirildi: 2 }
{ zona: { nom: 'Chilonzor', narx: 12000 }, tekshirildi: 3 }
{ zona: { nom: 'Shahar', narx: 25000 }, tekshirildi: 1 }
{ zona: null, tekshirildi: 0 }Chorsu (birinchi so'rov) "Chilonzor" to'rtburchagiga ham tushmadi — ray casting faqat ikki zona uchun chaqirildi, ikkalasida ham ichida, arzonrog'i "Markaz". Ikkinchi nuqta uchala zona ichida — eng arzoni tanlandi. Buxorodagi nuqta uchun ray casting bir marta ham chaqirilmadi: to'rtburchak filtri hammasini tashladi. Zonalar ustma-ust tushganda "eng arzon" o'rniga "eng kichik zona" yoki aniq ustuvorlik ham tanlanishi mumkin — bu biznes qarori.
10. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi
Bu darsda vazifalar-api ga kod qo'shilmaydi: vazifalar ro'yxatida joylashuv yo'q. Kanon Haversine, Redis GEO va GeoJSON'ni tajribada sinadi; kuryer kuzatuvi uchun kod yozmadi — faqat naqsh (Redis GEO + SSE). Geocoding va IP geolokatsiya kalit va litsenziya talab qilgani uchun sinalmagan. Geolokatsiya kursda 38-qismdagi yakuniy loyihalarda (yetkazib berish) ishlatiladi.
O'zingiz sinab ko'ring: «Bahor» uchun POST /yetkazish-narxi marshrutini yozing — body'dan lat, lon ni Zod bilan tekshirsin (hudud to'rtburchagi bilan), 3-mashqdagi zonalar bo'yicha narxni qaytarsin, zonadan tashqarida bo'lsa 422 Problem Details bersin.
11. Real ishda
- Yetkazib berish va taksi — eng yaqin kuryer (Redis GEO), zona narxi (poligon), jonli kuzatuv (SSE/WebSocket).
- Filial qidiruv — "eng yaqin do'kon/bankomat": PostGIS KNN yoki oddiy to'rtburchak + Haversine.
- Davomat va geofencing — xodim ish joyi zonasidami (va soxta GPS muammosi).
- Analitika — buyurtmalar xaritasi, issiqlik xaritasi (koordinata yaxlitlangan holda).
- Intervyuda: "Ikki nuqta orasidagi masofani qanday hisoblaysiz?", "1 million nuqtadan eng yaqin 10 tasini qanday tez topasiz?", "Nega GeoJSON'da avval uzunlik?"
Xulosa
- Koordinata — WGS 84 kenglik va uzunlik; GeoJSON, PostGIS, Redis —
[lon, lat], ko'p UI —lat, lon. - Almashib qolgan koordinata xato bermaydi — hudud to'rtburchagi bilan validatsiya qiling,
Number+isFinite. - Masofa — Haversine (to'g'ri chiziq); ko'p nuqtada avval arzon to'rtburchak, keyin formula yoki geo-indeks.
- Zona — poligon va ray casting; katta yoki aniq ish — PostGIS.
- Geocoding va IP geolokatsiya — tashqi xizmat: shartlar, limit, kesh, timeout. Bu dars qismi sinalmagan.
- Joylashuv — shaxsga doir ma'lumot: aniqlikni kamaytiring, tarixni cheklang, egalikni tekshiring.
Keyingi dars: OpenAPI 3.1 spetsifikatsiyasi — API'ning mashina o'qiy oladigan shartnomasi: paths, components, responses va JSON Schema.
Manbalar
- RFC 7946 — GeoJSON: https://www.rfc-editor.org/rfc/rfc7946
- Nominatim — foydalanish siyosati: https://operations.osmfoundation.org/policies/nominatim/
- Yandex Geocoder API: https://yandex.com/maps-api/docs/geocoder-api/
- Google Geocoding API: https://developers.google.com/maps/documentation/geocoding/overview
- MaxMind GeoLite2: https://dev.maxmind.com/geoip/geolite2-free-geolocation-data
- Redis —
GEOSEARCH: https://redis.io/docs/latest/commands/geosearch/ - Kanon tajribasi (IlmHamroh, 2026-10-27): Haversine, Redis 8.10.2 GEO, GeoJSON, Node 24.21.0. Geocoding va IP geolokatsiya — sinalmagan. Darsdagi JS bloklari va mashq yechimlari — biz ishga tushirdik (2026-10-28, Node 24.21.0).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!