IlmHamroh
JavaScript Full-stack/21-qism. React: media, integratsiyalar, PWA7/32-dars21 daqiqa
Mundarija (31)

Yandex Maps, Google Maps va MapLibre React'da: API kalit, geokoder, marshrut va narx

Qisqacha: Yandex Maps va Google Maps — tijoriy xizmatlar: xarita, manzil qidirish (geokoder) va marshrut beradi, lekin API kalit va shartnoma talab qiladi. Kalit brauzerda ochiq turadi — uni sir emas, cheklov himoya qiladi (sayt manzili, kunlik limit). MapLibre — ochiq kodli vektor xarita dvigateli, plitka provayderi bilan ishlaydi (masalan, kalitsiz OpenFreeMap). Eng ko'p xato — koordinata tartibi: Leaflet va Google lat, lng, Yandex, MapLibre va GeoJSON lng, lat. Bu darsdagi Yandex va Google kodi ishga tushirilmagan: kurs uchun hisob va kalit ochilmadi — kod hujjat bo'yicha yozilgan.

Bu darsda

  • Yandex Maps JS API 3 ni React'ga reactify bilan qanday ulashni bilasiz.
  • Google Maps'ni @vis.gl/react-google-maps bilan, MapLibre'ni react-map-gl bilan ulash kodini o'qiysiz.
  • API kalitni qanday cheklash va limitni qanday tejashni tushuntirasiz.
  • Koordinatalarni bitta ichki formatda saqlab, har kutubxonaga to'g'ri tartibda berasiz.
  • Kalitsiz yo'lni — xarita ilovasini havola bilan ochishni — qo'llaysiz.

Oldin bilishingiz kerak: Xaritalar I: Leaflet va OpenStreetMap, Vite III: env o'zgaruvchilar, Brauzer xavfsizlik modeli, debounce va throttle.

1. Nega bu kerak?

O'tgan darsda «Bahor» filiallarini Leaflet va OpenStreetMap bilan ko'rsatdik. Jasur aka xursand, lekin uchta yangi talab bor.

Birinchisi — manzil qidirish. Mehmon "Chilonzor 9-kvartal, 12-uy" deb yozadi, xarita o'sha uyni topishi kerak. Matndan koordinata topish — geokoder ishi.

Ikkinchisi — marshrut. "Uydan filialgacha qanday boraman, necha daqiqa?". Bu — yo'llar tarmog'ini biladigan xizmat.

Uchinchisi — tanish ko'rinish. Toshkentdagi ko'p odamlar telefonda Yandex Xarita yoki Google Maps ishlatadi. O'zbekiston uchun ko'cha va uy ma'lumotini ko'pchilik dasturchilar Yandex'da to'liqroq deb hisoblaydi. Bu — tajribadan kelgan gap, biz uni o'lchamadik. O'z shahringiz uchun ikkala xaritada bir xil manzillarni qidirib, solishtirib ko'ring.

Bularning hammasi tijoriy xizmat: kalit, limit, shartnoma. Hayotiy o'xshatish — elektr hisoblagichi. Kalit — hisoblagich raqami: kim ishlatsa, sizning hisobingizga yoziladi. Shuning uchun uni yashirish emas, "faqat mening uyimda ishlaydi" deb cheklash kerak.

Diqqat: Kurs uchun Yandex va Google'da hisob ochilmadi va kalit olinmadi. Bu darsdagi Yandex va Google kodi rasmiy hujjat bo'yicha yozilgan va ishga tushirilmagan. Kanon faqat Google kutubxonasining hajmini o'lchadi (+14.5 KB), MapLibre esa kalitsiz sinaldi.

2. Koordinata tartibi: eng ko'p uchraydigan xato

2.1 Kim qaysi tartibda

Bir nuqta — ikki son. Lekin kutubxonalar ularni turlicha kutadi:

Kutubxona / format Ko'rinishi Tartib
Leaflet [41.31, 69.28] lat, lng
Google Maps { lat: 41.31, lng: 69.28 } nomli maydonlar
Yandex Maps 3 [69.28, 41.31] lng, lat
MapLibre, GeoJSON [69.28, 41.31] lng, lat
Yandex geokoder javobi "69.28 41.31" (satr) lng, lat

Yandex hujjatidagi misolda Moskva markazi [37.588144, 55.733842] deb yozilgan — avval uzoqlik (37), keyin kenglik (55). GeoJSON standarti (RFC 7946) ham [lng, lat] ni talab qiladi. Matematikadagi (x, y) ga o'xshaydi: x — sharq-g'arb, y — shimol-janub.

Tartibni adashtirsangiz, xato chiqmaydi. Toshkent [41.31, 69.28] → lng 41.31, lat 69.28 — bu Shimoliy Muz okeani. Marker jim-jit noto'g'ri joyga tushadi.

2.2 Bitta ichki format

Davo — ilova ichida bitta format ({ lat, lng } obyekti, nomli maydonlar) va har kutubxona uchun kichik o'giruvchi funksiya. Geokoder satrini ham shu yerda o'qiymiz. Javob — Yandex hujjatidagi tuzilma bo'yicha yasalgan soxta javob (haqiqiy so'rov kalit talab qiladi):

js
// ilova ichidagi yagona format
const branch = { lat: 41.364, lng: 69.287 };

const toLeaflet = (p) => [p.lat, p.lng];
const toGoogle = (p) => ({ lat: p.lat, lng: p.lng });
const toYandex = (p) => [p.lng, p.lat];
const toGeoJson = (p) => ({
  type: "Point",
  coordinates: [p.lng, p.lat],
});

console.log("Leaflet:", toLeaflet(branch));
console.log("Google:", toGoogle(branch));
console.log("Yandex:", toYandex(branch));
console.log("GeoJSON:", JSON.stringify(toGeoJson(branch)));

// Yandex geokoder javobi (soxta, hujjatdagi tuzilma bo'yicha)
const answer = {
  response: {
    GeoObjectCollection: {
      featureMember: [
        {
          GeoObject: {
            name: "Amir Temur xiyoboni",
            Point: { pos: "69.279737 41.311151" },
          },
        },
      ],
    },
  },
};

function parseYandex(json) {
  const first = json.response.GeoObjectCollection.featureMember[0];
  if (!first) return null;
  const [lng, lat] = first.GeoObject.Point.pos.split(" ").map(Number);
  return { name: first.GeoObject.name, lat, lng };
}
console.log(parseYandex(answer));

Konsolda:

text
Leaflet: [ 41.364, 69.287 ]
Google: { lat: 41.364, lng: 69.287 }
Yandex: [ 69.287, 41.364 ]
GeoJSON: {"type":"Point","coordinates":[69.287,41.364]}
{ name: 'Amir Temur xiyoboni', lat: 41.311151, lng: 69.279737 }

parseYandex da destrukturlash tartibi — [lng, lat]. Bu yagona joyda to'g'ri yozilsa, ilovaning qolgan qismi tartib haqida o'ylamaydi.

Yandex hujjatining matni pos maydoni "longitude latitude" ekanini aytadi. Lekin o'sha sahifadagi Dubay misolida raqamlar teskari ko'rinadi ("25.197300 55.274243" — Dubayning kengligi 25). Hujjat ham xato qilishi mumkin. Shuning uchun birinchi haqiqiy javobni taniqli joy (masalan, Amir Temur xiyoboni) bilan tekshiring.

Tekshirib ko'ring: Kimdir Yandex xaritasiga center: [41.31, 69.28] berdi. Xarita qayerni ko'rsatadi?

Javob

Yandex [lng, lat] kutadi: uzoqlik 41.31, kenglik 69.28. Kenglik 69 — Shimoliy qutb doirasidan ham shimolda, Rossiyaning Arktika qirg'og'i yoki Barens dengizi atrofi. Toshkent emas. Xato chiqmaydi — shuning uchun bunday xatoni test yoki taniqli nuqta bilan ushlash kerak.

3. API kalit: sir emas, cheklov

3.1 Kalit qanday ishlaydi

Xarita skripti kalit bilan yuklanadi: https://api-maps.yandex.ru/v3/?apikey=KALIT&lang=ru_RU. Brauzer bu manzilni ochadi — demak, kalit har kim ko'radigan joyda: sahifa kodi, Network paneli. Uni env o'zgaruvchiga yashirish ham yordam bermaydi: VITE_ bilan boshlangan qiymat build paytida JS faylga yoziladi.

sequenceDiagram
  participant B as Brauzer (sizning sayt)
  participant X as Xarita xizmati
  participant H as Hisob va limit
  B->>X: skript / plitka, apikey=K, Referer: sayt
  X->>H: K kimniki? Referer ruxsatmi? limit qoldimi?
  H-->>X: ha
  X-->>B: xarita
  Note over X,H: har so'rov — hisobingizga

Diagrammaning ikkinchi qadamiga qarang: xizmat kalitdan tashqari Referer (so'rov qaysi saytdan kelgani) ni ham tekshiradi — agar siz shunday sozlagan bo'lsangiz.

3.2 Kalitni cheklash

Yandex Developer kabinetida ham, Google Cloud Console'da ham kalitga cheklov qo'yiladi:

  • Sayt manzili (HTTP Referer) — https://example.com/* dan boshqa joyda kalit ishlamaydi. Kimdir kalitingizni ko'chirib, o'z saytiga qo'ysa — rad etiladi.
  • API cheklovi — kalit faqat kerakli xizmatga (masalan, faqat Maps JavaScript API) ishlaydi.
  • Kunlik limit va byudjet ogohlantirishi — kutilmagan hisob kelmasin.

Brauzerdagi kalit va server kaliti — alohida. Geokoder yoki marshrutni serverdan chaqirsangiz, u kalit sir saqlanadi va brauzerga hech qachon chiqmaydi. Bu backend qismlarida (25-qismdan) yoziladi.

3.3 Narx va shartlar (2026-oktabr holati)

Raqamlar tez-tez o'zgaradi — loyiha boshlashdan oldin rasmiy sahifani o'qing.

  • Google Maps Platform — 2025-yil 1-martdan oylik $200 kredit o'rniga har xizmatga (SKU) alohida bepul chegara keldi. Dynamic Maps (interaktiv veb-xarita) — "Essentials" toifasi, oyiga 10 000 bepul yuklash. Undan keyin — har 1 000 yuklash uchun to'lov: 100 000 tagacha $7, ko'paygan sari arzonlashadi (rasmiy "Pricing" sahifasi, 2026-10). Kalit olish uchun Google Cloud'da billing hisobi (to'lov kartasi) ochiladi.
  • Yandex Maps API — bepul foydalanish shartlari bor, lekin cheklovli: loyiha ommaviy va bepul ochiq bo'lishi, ma'lumotni saqlamaslik, transportni real vaqtda kuzatmaslik. Yopiq tizim, monitoring yoki sotiladigan mahsulot uchun — tijoriy litsenziya. Tariflar sahifasida (yandex.ru/maps-api/tariffs, 2026-10) JavaScript API va Geokoder "bepul foydalanish shartlari bajarilsa — bepul" deb belgilangan; tijoriy JavaScript API litsenziyasi — yiliga 195 000 ₽ dan. Bepul kunlik chegara u sahifada aniq yozilmagan — amaldagi shartni yandex.ru/legal/maps_api da tekshiring.
  • MapLibre — kutubxonaning o'zi bepul (BSD litsenziya). Pul plitka uchun: OpenFreeMap ochiq nusxasi kalitsiz va "ko'rishlar soniga cheklovsiz" deydi (xayriya hisobidan ishlaydi). MapTiler, Stadia kabi provayderlar — kalit va tarif bilan.
  • Mapbox GL JS — 2-versiyadan yopiq litsenziya va access token. MapLibre aynan shu paytda, Mapbox GL JS 1-versiyasidan ochiq nusxa (fork) bo'lib chiqqan.

4. Yandex Maps JS API 3: reactify

4.1 Ulash

Yandex 3-versiyasi npm paket emas — skript <script> bilan yuklanadi. React uchun Yandex'ning o'zi @yandex/ymaps3-reactify modulini beradi: u xarita obyektlarini React komponentlariga aylantiradi. Hujjatdagi tartib:

html
<!-- index.html: kalit va til -->
<script
  src="https://api-maps.yandex.ru/v3/?apikey=KALIT&lang=ru_RU"
></script>
tsx
// lib/ymaps3.ts — hujjat bo'yicha, ishga tushirilmagan
import React from "react";
import ReactDom from "react-dom";

const [ymaps3React] = await Promise.all([
  ymaps3.import("@yandex/ymaps3-reactify"),
  ymaps3.ready,
]);

export const reactify = ymaps3React.reactify.bindTo(React, ReactDom);
export const {
  YMap,
  YMapDefaultSchemeLayer,
  YMapDefaultFeaturesLayer,
  YMapMarker,
} = reactify.module(ymaps3);

ymaps3 — skript yaratgan global obyekt. ymaps3.ready — API to'liq yuklanganini kutadigan Promise. Top-level await modul darajasida ishlaydi (modul semantikasi) — bu fayl import qilinganda xarita tayyor bo'ladi.

Tur fayllari uchun Yandex @yandex/ymaps3-types paketini beradi (npm i -D), shunda ymaps3 global'i TypeScript'da taniladi.

4.2 Xarita va marker

tsx
import {
  YMap,
  YMapDefaultSchemeLayer,
  YMapDefaultFeaturesLayer,
  YMapMarker,
  reactify,
} from "./lib/ymaps3";

// diqqat: [lng, lat]
const LOCATION = { center: [69.2797, 41.3111], zoom: 12 };
const YUNUSOBOD = [69.287, 41.364];

export function BranchMapYandex() {
  return (
    <div style={{ height: 320 }}>
      <YMap location={reactify.useDefault(LOCATION)}>
        <YMapDefaultSchemeLayer />
        <YMapDefaultFeaturesLayer />
        <YMapMarker coordinates={reactify.useDefault(YUNUSOBOD)}>
          <button type="button">Yunusobod</button>
        </YMapMarker>
      </YMap>
    </div>
  );
}

Uchta narsaga qarang:

  1. Ikki qatlam — YMapDefaultSchemeLayer (xaritaning o'zi) va YMapDefaultFeaturesLayer (markerlar shu qatlamga tushadi). Ikkinchisisiz marker ko'rinmaydi.
  2. Marker ichi — oddiy JSX. Tugma, rasm, matn — har narsa. Leaflet'dagi bindPopup(html) muammosi bu yerda yo'q: kontent React elementi.
  3. reactify.useDefault(...) — hujjat ogohlantiradi: reactify komponentlari boshqarilmaydi, xarita o'z holatini o'zi saqlaydi. Har render'da yangi location obyekti bersangiz, xarita foydalanuvchi siljitgan joydan "sakrab" qaytadi. useDefault qiymatni bir marta beradi — xuddi defaultValue kabi. Ikkinchi argument — bog'liqliklar massivi: u o'zgarsa, qiymat yangilanadi.

O'tgan darsdagi react-leaflet center tuzog'i bilan solishtiring: g'oya bir xil. Xarita kutubxonalari o'z ichki holatiga ega, React esa uni faqat boshlang'ich qiymat bilan boshqaradi.

4.3 SSR

Hujjatning Next.js misoli ochiq aytadi: reactify moduli server render'ni qo'llamaydi. Shuning uchun u "use client" komponentda, useEffect ichida yuklanadi va tayyor bo'lguncha hech narsa chizilmaydi. Leaflet dagi window is not defined saboqining o'zi.

5. Google Maps: @vis.gl/react-google-maps

Google'ning o'zi React kutubxonasi chiqarmaydi, lekin vis.gl jamoasining @vis.gl/react-google-maps paketini rasmiy hujjatlarda tavsiya qiladi. Kanon o'lchagan versiya — 1.10.1, hajmi +14.5 KB (xaritaning asosiy kodi Google serveridan keladi — bu raqamga kirmaydi).

tsx
import {
  APIProvider,
  Map,
  AdvancedMarker,
  InfoWindow,
  useAdvancedMarkerRef,
} from "@vis.gl/react-google-maps";

const KEY = import.meta.env.VITE_GOOGLE_MAPS_KEY; // ochiq kalit!

function Branch() {
  const [markerRef, marker] = useAdvancedMarkerRef();
  return (
    <>
      <AdvancedMarker
        position={{ lat: 41.364, lng: 69.287 }}
        ref={markerRef}
      />
      <InfoWindow anchor={marker}>Yunusobod, 9:00–23:00</InfoWindow>
    </>
  );
}

export function BranchMapGoogle() {
  return (
    <APIProvider apiKey={KEY}>
      <Map
        style={{ height: 320 }}
        defaultCenter={{ lat: 41.3111, lng: 69.2797 }}
        defaultZoom={12}
        mapId="DEMO_MAP_ID"
      >
        <Branch />
      </Map>
    </APIProvider>
  );
}
  • APIProvider — Google skriptini kalit bilan yuklaydi, bolalarga kontekst beradi.
  • defaultCenter, defaultZoom — nomidan ko'rinib turibdi: boshlang'ich qiymat. Boshqariladigan variant ham bor (center + onCameraChanged).
  • AdvancedMarker faqat mapId li xaritada ishlaydi (Google Cloud'dagi xarita uslubi). DEMO_MAP_ID — hujjatdagi sinov qiymati.

Geokoder va boshqa xizmatlar useMapsLibrary hook'i bilan, kerak bo'lganda yuklanadi:

tsx
import { useEffect, useMemo } from "react";
import { useMapsLibrary } from "@vis.gl/react-google-maps";

function useGeocode(address: string) {
  const lib = useMapsLibrary("geocoding"); // avval null
  const geocoder = useMemo(() => lib && new lib.Geocoder(), [lib]);
  useEffect(() => {
    if (!geocoder || !address) return;
    void geocoder.geocode({ address }).then(({ results }) => {
      const loc = results[0]?.geometry.location;
      console.log(loc?.lat(), loc?.lng());
    });
  }, [geocoder, address]);
}

Har geocode chaqiruvi — pullik so'rov. Har harf yozilganda chaqirsangiz, "Chilonzor" so'zi 9 ta so'rovga tushadi. Keyingi bo'limda buni tejaymiz.

6. Limitni tejash: kesh va debounce

Geokoder so'rovi qimmat va limitli. Ikki oddiy usul:

  1. Kesh — bir xil manzilni ikkinchi marta so'ramaslik. Manzilni normallashtiramiz (bo'sh joy, katta-kichik harf), keyin Map ga saqlaymiz.
  2. Debounce — foydalanuvchi yozishni to'xtatgandan keyingina so'rash (debounce va throttle).

Kesh g'oyasini soxta geokoder bilan sinab ko'ramiz — u har chaqiruvni sanaydi:

js
let calls = 0;
// soxta geokoder: haqiqiysi — pullik tarmoq so'rovi
async function geocodeRemote(query) {
  calls++;
  return { query, lat: 41.2756, lng: 69.2034 };
}

const cache = new Map();
async function geocode(query) {
  const key = query.trim().toLowerCase().replace(/\s+/g, " ");
  if (!cache.has(key)) cache.set(key, geocodeRemote(key));
  return cache.get(key);
}

const inputs = ["Chilonzor", "chilonzor ", "CHILONZOR", " Chilonzor"];
await Promise.all(inputs.map(geocode));
console.log("so'rovlar:", inputs.length, "→ tarmoqqa:", calls);

Konsolda:

text
so'rovlar: 4 → tarmoqqa: 1

E'tibor bering: keshga natija emas, Promise saqlanadi. To'rt so'rov bir paytda kelsa ham, birinchisi boshlagan so'rovni qolganlari kutadi — tarmoqqa bitta boradi. Natijani saqlasak, birinchi javob kelguncha qolgan uchtasi ham tarmoqqa ketardi.

Google va Yandex shartlari javobni uzoq saqlashni cheklaydi. Brauzer xotirasidagi qisqa kesh (sahifa ochiq turgan paytda) — bir sessiya ichidagi takrorni yo'qotadi. Uzoq muddatli kesh kerak bo'lsa, avval shartnomani o'qing.

Tekshirib ko'ring: Debounce'ni 300 ms ga sozladik. Foydalanuvchi "Chilonzor" ni sekin, har harfdan keyin yarim soniya o'ylab yozdi. Nechta so'rov ketadi va nima qilish mumkin?

Javob

To'qqizta: har pauza 300 ms dan uzun, debounce har harfdan keyin "yozish tugadi" deb hisoblaydi. Yechimlar: kamida 3 harf bo'lganda qidirish, debounce'ni 500 ms ga oshirish va kesh (orqaga o'chirib qayta yozilgan so'z tarmoqqa bormaydi). Eng tejamkori — taklif (autocomplete) o'rniga "Qidirish" tugmasi: bitta so'rov.

7. MapLibre: vektor xarita, o'z provayderingiz bilan

7.1 Raster va vektor plitka

Leaflet plitkalari — tayyor rasm (PNG). Bu — raster plitka. Vektor plitka esa rasm emas, ma'lumot: "bu yerda yo'l, bu yerda bino, nomi shu". Brauzer uni o'zi chizadi (WebGL bilan). Natija: silliq zoom, xaritani burish va qiyshaytirish (3D ko'rinish), uslubni (ranglar, til) brauzerda almashtirish.

MapLibre GL JS — shunday vektor dvigatel. React o'rami — react-map-gl (vis.gl jamoasi), 8-versiyadan MapLibre uchun alohida kirish nuqtasi bor:

tsx
import Map, {
  Marker,
  NavigationControl,
} from "react-map-gl/maplibre";
import { setWorkerUrl } from "maplibre-gl";
import "maplibre-gl/dist/maplibre-gl.css";
// Vite: ishchi (worker) faylini o'zimiz beramiz
import workerUrl from
  "maplibre-gl/dist/maplibre-gl-worker.mjs?worker&url";

setWorkerUrl(workerUrl);

export function BranchMapLibre() {
  return (
    <Map
      initialViewState={{
        longitude: 69.2797,
        latitude: 41.3111,
        zoom: 11,
      }}
      style={{ height: 320 }}
      mapStyle="https://tiles.openfreemap.org/styles/liberty"
    >
      <NavigationControl position="top-right" />
      <Marker longitude={69.287} latitude={41.364}>
        <button type="button">Yunusobod</button>
      </Marker>
    </Map>
  );
}
  • mapStyle — uslub fayli (JSON): qaysi plitka serveridan, qanday ranglar bilan. OpenFreeMap — kalitsiz, ro'yxatdan o'tishsiz (o'z saytida shunday yozilgan).
  • initialViewState — yana "boshlang'ich qiymat". Boshqariladigan rejim — viewState + onMove.
  • setWorkerUrl — MapLibre og'ir hisoblarni Web Worker'da qiladi (Web Worker). MapLibre hujjatida Vite uchun aynan shu qator (?worker&url) bor: oddiy ?url bilan build'da ishchi o'z qo'shni faylini topa olmaydi va "vektor plitkalar yuklanmaydi". Bu qatorni biz sinamadik — hujjat bo'yicha.

7.2 Kanon tajribasi nimani ko'rsatdi

Kanon MapLibre 6.13.0 + react-map-gl 8.1.3 ni demo uslub (demotiles.maplibre.org) bilan, kalitsiz sinadi:

text
xarita-maplibre  JS 358220 (+ 283.7 KB) CSS 10382
  CSP: require-trusted-types-for×3, connect-src×1
  xato: … 'TrustedScriptURL' … ; Failed to construct 'Worker' …
  CSP'siz: tashqi demotiles.maplibre.org
  CSP'siz xato: Error: Worker failed to load. Check that the worker URL is correct.

Uch saboq:

  1. Hajm — +283.7 KB JS va +10.4 KB CSS. Bu o'lchangan kutubxonalar ichida eng og'irlaridan. Faqat xarita sahifasida kech yuklang.
  2. Trusted Types — new Worker(url) ham TT nazoratida: oddiy satr emas, TrustedScriptURL kerak. Uslubni boshqa saytdan olish uchun esa connect-src ga uning manzili.
  3. CSP'siz ham konsolda "Worker failed to load" chiqdi. Tajribada setWorkerUrl sozlanmagan edi — yuqoridagi hujjat qatori aynan shu holat uchun. Plitkalar chizilganini tajriba tekshirmagan, shuning uchun "ishladi" deb aytmaymiz.

8. Google va Yandex: CSP va maxfiylik narxi

Tashqi xarita — tashqi skript. U sizning sahifangizda sizning huquqingiz bilan ishlaydi (Frontend xavfsizligi).

CSP. Google'ning rasmiy CSP qo'llanmasi nonce'li qat'iy siyosatni tavsiya qiladi. Uning namunasida script-src da 'unsafe-eval' va blob:, worker-src blob:, img-src va connect-src da *.googleapis.com, *.gstatic.com, frame-src *.google.com bor. vazifalar siyosati (script-src 'self', nonce yo'q, unsafe-eval yo'q) bilan bu to'g'ridan-to'g'ri sig'maydi. Trusted Types haqida qo'llanma umuman gapirmaydi. Yandex skriptini kanon o'lchamadi.

Maxfiylik. Xarita ochilishi bilan foydalanuvchi brauzeri Google yoki Yandex serveriga so'rov yuboradi: IP manzil, sahifa manzili (Referer), qurilma ma'lumoti. Joylashuv tugmasi bo'lsa — koordinata ham. Maxfiylik siyosatingizda buni yozing. Agar xarita sahifaning asosiy qismi bo'lmasa — "Xaritani ko'rsatish" tugmasi bilan, foydalanuvchi so'raganda yuklang.

9. Kalitsiz yo'l: xarita ilovasini havola bilan ochish

Ko'p saytga to'liq xarita kerak emas. "Filial qayerda va qanday boraman" — telefondagi xarita ilovasi buni yaxshiroq qiladi. Yandex, Google va OSM manzil (URL) orqali nuqta ochishni qo'llaydi — kalitsiz, to'lovsiz, skriptsiz:

jsx
const BRANCHES = [
  { name: "Yunusobod", lat: 41.364, lng: 69.287 },
  { name: "Chilonzor", lat: 41.2756, lng: 69.2034 },
];

// tartib: Yandex — lng,lat; Google va OSM — lat,lng
function mapLinks({ lat, lng }) {
  return [
    ["Yandex", `https://yandex.uz/maps/?pt=${lng},${lat}&z=16&l=map`],
    [
      "Google",
      `https://www.google.com/maps/search/?api=1&query=${lat},${lng}`,
    ],
    [
      "OSM",
      `https://www.openstreetmap.org/?mlat=${lat}&mlon=${lng}` +
        `#map=16/${lat}/${lng}`,
    ],
  ];
}

export default function App() {
  console.log(mapLinks(BRANCHES[0])[0][1]);
  return (
    <ul>
      {BRANCHES.map((b) => (
        <li key={b.name}>
          {b.name}:{" "}
          {mapLinks(b).map(([label, href]) => (
            <a key={label} href={href} target="_blank" rel="noopener">
              {label}{" "}
            </a>
          ))}
        </li>
      ))}
    </ul>
  );
}

Konsolda:

text
https://yandex.uz/maps/?pt=69.287,41.364&z=16&l=map

Ekranda:

text
Yunusobod: Yandex Google OSM
Chilonzor: Yandex Google OSM

Havolani bosing — xarita yangi tabda ochiladi (natija oynasi havolani tashqariga ochishga ruxsat bersa). Telefonda Yandex Xarita yoki Google Maps ilovasi o'rnatilgan bo'lsa, odatda ilova ochiladi. Bu yerda ham koordinata tartibi farq qiladi — Yandex'ning pt parametri lng,lat. Google'ning "Maps URLs" usuli hujjatda kalitsiz deb yozilgan.

Narxi nolga yaqin: skript yo'q, CSP o'zgarmaydi, foydalanuvchi bosmaguncha hech qanday so'rov yo'q. Ko'p hollarda filiallar ro'yxati + shu havolalar + o'tgan darsdagi Leaflet xaritasi yetarli.

10. Marshrut va geokoder: qayerda ishlaydi

Marshrut (routing) — yo'llar grafida eng qisqa yo'l (Dijkstra kabi algoritmlar). Har xizmat buni alohida, alohida narx bilan beradi:

Xizmat Marshrut Geokoder
Google Routes API Geocoding API
Yandex Router API (tijoriy) HTTP Geocoder (geocode-maps.yandex.ru/v1/)
Ochiq OSRM, GraphHopper, Valhalla (o'z serveringizda) Nominatim (OSM; o'z siyosati bor)

Frontend vazifasi — natijani ko'rsatish: marshrut chizig'i (GeoJSON LineString), masofa va vaqt. Hisoblash — server yoki xizmatda. Ochiq yechimlarning ommaviy demo serverlari ham foydalanish siyosatiga ega — production uchun o'z serveringiz.

11. Hujumchi nigohi

Kalitingiz sahifa kodida ochiq. Hujumchi nima qila oladi?

  • Kalitni o'g'irlash. Boshqa saytga qo'yadi — xarita yuklanishlari sizning hisobingizga. Himoya: HTTP Referer cheklovi. Lekin Referer'ni brauzerdan tashqaridagi skript soxtalashtira oladi. Shuning uchun qo'shimcha: API cheklovi (faqat Maps JavaScript API) va kunlik limit.
  • Limitni tugatish. Bot geokoderingizni minglab marta chaqiradi — kunlik limit tugaydi, haqiqiy mehmonlar xaritasiz qoladi. Himoya: geokoder so'rovlarini o'z serveringiz orqali o'tkazish, so'rovlar chegarasi (rate limit) va kesh.
  • Server kalitini frontendga qo'yish. Cheklovsiz "server" kalit JS faylga tushsa — u har qanday API'ni, har qanday hajmda chaqira oladi. Qoida: brauzerga faqat cheklangan "brauzer" kaliti chiqadi.

Kalit o'g'irlanganini sezsangiz — kabinetda yangisini yarating va eskisini o'chiring. Kod tarixida (git) qolgan kalit ham o'g'irlangan hisoblanadi.

12. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
marker okeanda yoki Arktikada lat/lng tartibi adashgan bitta ichki format + o'giruvchi funksiya
Yandex markeri ko'rinmaydi YMapDefaultFeaturesLayer qo'shilmagan ikkala standart qatlam
Yandex xarita siljitilgach "sakraydi" har render'da yangi location reactify.useDefault
Google: AdvancedMarker chizilmaydi mapId yo'q xaritaga mapId
Google xaritasi "For development purposes only" kalit yoki billing sozlanmagan Cloud Console'da billing va kalit
geokoder hisobi kutilmaganda katta har harfda so'rov kesh, debounce, kamida 3 harf
MapLibre: Worker failed to load ishchi fayl manzili yo'q setWorkerUrl(…?worker&url)
CSP: TrustedScriptURL Worker yoki tashqi skript TT'da TT siyosati yoki boshqa yechim
kalit boshqa saytda ishlatilmoqda Referer cheklovi yo'q kalitni cheklang, almashtiring

13. Mashqlar

Mashqlar kurs/mashqlar/21/07-xaritalar-ii/ da. Jonli yechimlarni «Ishga tushir» bilan sinang.

1-mashq (oson): bo'sh joylarni to'ldiring

  • GeoJSON coordinates da birinchi son:
  • Yandex reactify'da prop'ni bir marta beruvchi funksiya: reactify.
  • Google AdvancedMarker uchun xaritaga kerak prop:
  • Kalitni boshqa saytda ishlatishdan himoya qiluvchi cheklov: HTTP
  • Manzildan koordinata topadigan xizmat:
Yechim
  • lng — GeoJSON (RFC 7946): [uzoqlik, kenglik].
  • useDefault — reactify komponentlari boshqarilmaydi, qiymat bir marta beriladi.
  • mapId — AdvancedMarker faqat bulutli uslubli xaritada.
  • Referer — so'rov qaysi saytdan kelgani; kabinetda ruxsat etilgan manzillar ro'yxati.
  • geokoder — matn → koordinata (teskarisi — teskari geokodlash).

2-mashq (o'rta): GeoJSON'dan markerlar ro'yxati

Server filiallarni GeoJSON FeatureCollection ko'rinishida beradi (koordinatalar [lng, lat]):

js
const geo = {
  type: "FeatureCollection",
  features: [
    {
      type: "Feature",
      properties: { name: "Sergeli" },
      geometry: { type: "Point", coordinates: [69.219, 41.227] },
    },
    {
      type: "Feature",
      properties: { name: "Yunusobod" },
      geometry: { type: "Point", coordinates: [69.287, 41.364] },
    },
  ],
};

Komponent yozing: har filial uchun <li> — Sergeli: 41.227, 69.219 (lat, lng — Leaflet tartibida) va yonida Yandex havolasi (pt=lng,lat). Ro'yxat shimoldan janubga tartiblangan bo'lsin (kenglik kamayib boradi).

Ishora: massiv destructuring — const [lng, lat] = f.geometry.coordinates, tartiblash — toSorted.

Yechim
jsx
const geo = {
  type: "FeatureCollection",
  features: [
    {
      type: "Feature",
      properties: { name: "Sergeli" },
      geometry: { type: "Point", coordinates: [69.219, 41.227] },
    },
    {
      type: "Feature",
      properties: { name: "Yunusobod" },
      geometry: { type: "Point", coordinates: [69.287, 41.364] },
    },
  ],
};

export default function App() {
  const points = geo.features
    .map((f) => {
      const [lng, lat] = f.geometry.coordinates;
      return { name: f.properties.name, lat, lng };
    })
    .toSorted((a, b) => b.lat - a.lat);
  return (
    <ul>
      {points.map((p) => (
        <li key={p.name}>
          {`${p.name}: ${p.lat}, ${p.lng} `}
          <a href={`https://yandex.uz/maps/?pt=${p.lng},${p.lat}`}>
            Yandex
          </a>
        </li>
      ))}
    </ul>
  );
}

Ekranda:

text
Yunusobod: 41.364, 69.287 Yandex
Sergeli: 41.227, 69.219 Yandex

Destrukturlash bitta joyda [lng, lat] ni nomli maydonlarga aylantirdi — qolgan kod faqat p.lat, p.lng bilan ishlaydi. Yunusobod shimolda 41.364-bob, shuning uchun birinchi.

3-mashq (qiyin): debounce + kesh

createSearch(remote, wait) funksiyasini yozing. U search(query) ni qaytarsin: oxirgi chaqiruvdan wait ms o'tgachgina remote(query) ni chaqiradi (debounce), bir xil normallashtirilgan so'rovni ikkinchi marta yubormaydi (kesh). Sinov: "C", "Ch", "Chi" ketma-ket (har biri orasida 50 ms), keyin 400 ms dan keyin yana "chi". wait — 300. Tarmoqqa nechta so'rov ketdi?

Ishora: setTimeout/clearTimeout (taymerlar), kesh — Set yoki Map.

Yechim
js
function createSearch(remote, wait) {
  let timer;
  const seen = new Set();
  return function search(query) {
    clearTimeout(timer);
    timer = setTimeout(() => {
      const key = query.trim().toLowerCase();
      if (seen.has(key)) return;
      seen.add(key);
      remote(key);
    }, wait);
  };
}

const sent = [];
const search = createSearch((q) => sent.push(q), 300);
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

search("C");
await sleep(50);
search("Ch");
await sleep(50);
search("Chi");
await sleep(400);
search("chi");
await sleep(400);
console.log("tarmoqqa:", sent);

Konsolda:

text
tarmoqqa: [ 'chi' ]

Uch harf tez yozildi — debounce faqat oxirgisini ("Chi") yubordi. 400 ms dan keyingi "chi" normallashtirilgach keshda bor edi — tarmoqqa bormadi. To'rt chaqiruv → bitta so'rov. Haqiqiy kodda keshga natijani ham saqlang, shunda keshdan qaytgan so'rov ham ekranga javob beradi.

4-mashq: tajriba qadami (ixtiyoriy)

Agar o'zingizda Yandex yoki Google kaliti bo'lsa (o'zingiz ochasiz, kurs talab qilmaydi): alohida Vite loyihada (kurs/tajriba/xarita-ii/) shu darsdagi komponentlardan birini ishga tushiring. Kalitga sayt manzili cheklovini qo'ying va boshqa portdan (masalan, localhost:5174) ochib ko'ring. Kalit yo'q bo'lsa — MapLibre + OpenFreeMap variantini qiling.

Yechim

Tekshiruv ro'yxati:

  • Kalit .env.local da (VITE_…), .gitignore da — git'ga tushmaydi. Lekin build'dan keyin u JS faylda ochiq — buni DevTools'da qidirib ko'ring.
  • Referer cheklovi http://localhost:5173/* bo'lsa, 5174 portdan xarita xato beradi (konsolda xizmatning rad javobi).
  • Koordinata tartibi: Yandex — [lng, lat], Google — { lat, lng }.
  • MapLibre: setWorkerUrl qatorisiz build'da xarita bo'sh qolishi mumkin; qator bilan — plitkalar chiziladi. npm run build da MapLibre chunk'i kanondagidek ~280 KB gzip atrofida.

14. Real ishda

  • Yetkazib berish, taksi, logistika — O'zbekistonda ko'pincha Yandex: manzil qidirish va marshrut. Tijoriy litsenziya — shartnoma bilan.
  • Xalqaro mahsulotlar — Google Maps, ayniqsa Places (joy qidirish, sharhlar) kerak bo'lsa.
  • O'z dizaynli, ko'p ma'lumotli xaritalar (ko'chmas mulk, statistika) — MapLibre + o'z yoki tijoriy vektor plitkalar.
  • Oddiy "biz qayerdamiz" sahifalari — havola yoki statik rasm: skript va kalitsiz.
  • Intervyu savollari: "API kalitni frontendda qanday himoyalaysiz?", "lat va lng tartibi qayerda farq qiladi?", "geokoder xarajatini qanday kamaytirasiz?", "raster va vektor plitka farqi?".

Xulosa

  • Yandex, Google — kalit, limit, shartnoma; kalit brauzerda ochiq, himoya — Referer va API cheklovi, kunlik limit. Server kaliti brauzerga chiqmaydi.
  • Koordinatalar: Leaflet [lat, lng], Google { lat, lng }, Yandex, MapLibre, GeoJSON [lng, lat] — ilova ichida bitta format.
  • Yandex 3: skript + @yandex/ymaps3-reactify, reactify.useDefault, ikki standart qatlam; SSR yo'q.
  • Google: @vis.gl/react-google-maps — APIProvider, Map (defaultCenter), AdvancedMarker (mapId), useMapsLibrary.
  • MapLibre: vektor plitkalar, react-map-gl/maplibre, Vite'da setWorkerUrl; kanonda +283.7 KB va Trusted Types'da Worker to'sildi.
  • Geokoderni kesh (Promise'ni saqlang) va debounce bilan tejang; ko'p hollarda kalitsiz havola yetarli.

Keyingi dars: Markdown render qilish — vazifalar dagi vazifaga izoh qo'shamiz: react-markdown, GFM, xavfsizlik va kech yuklash.

Manbalar

  • Yandex Maps JS API 3: "Connecting via reactify", "Integration with Next.js", Geocoder API "Response format" — yandex.com/maps-api/docs; foydalanish shartlari — yandex.ru/legal/maps_api
  • vis.gl: "React Google Maps" (APIProvider, AdvancedMarker, useMapsLibrary) — visgl.github.io/react-google-maps
  • Google Maps Platform: "Pricing overview" (2025-03-01 o'zgarishi), "Content Security Policy guide", "Maps URLs" — developers.google.com/maps
  • MapLibre GL JS: "Installation: Vite" (setWorkerUrl), "CSP directives" — maplibre.org; react-map-gl 8: "Get started with MapLibre" — visgl.github.io/react-map-gl
  • OpenFreeMap: "Quick start" — openfreemap.org
  • RFC 7946: The GeoJSON Format (koordinata tartibi)
  • Kanon: natijalar/tajriba/kutubxonalar-21.txt, tajriba/kutubxonalar/src/k/xarita-google.tsx, xarita-maplibre.tsx (2026-10-17)
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!