Перейти к содержанию

Яндекс.Карты API - Документация

Обзор

Яндекс.Карты API предоставляет инструменты для встраивания интерактивных карт в веб-приложения, геокодирования адресов и работы с геоданными.

Официальная документация: https://yandex.com/maps-api/docs/js-api/index.html

Компоненты API

  1. JavaScript API 3.0 - клиентская библиотека для интерактивных карт
  2. Geocoder HTTP API - HTTP API для геокодирования
  3. React интеграция - готовые компоненты для React приложений

Получение API ключа

1. Регистрация

  1. Перейдите на https://developer.tech.yandex.ru/
  2. Авторизуйтесь через Яндекс ID
  3. Создайте новый проект

2. Подключение API

Выберите пакет "JavaScript API и HTTP Геокодер": - JavaScript API 3.0 для интерактивных карт - Geocoder HTTP API для геокодирования

3. Получение ключа

  1. В настройках проекта нажмите "Получить ключ"
  2. Укажите домены для JavaScript API (например, localhost, silent-meadow.com)
  3. Сохраните API ключ

ВАЖНО: JavaScript API 3.0 работает только с ключами, у которых заполнено поле "Ограничение по HTTP Referer".

4. Активация

Ключ активируется в течение 15 минут после создания.


JavaScript API 3.0

Подключение

Базовое подключение

<!DOCTYPE html>
<html>
<head>
  <script src="https://api-maps.yandex.ru/3.0/?apikey=YOUR_API_KEY&lang=ru_RU"></script>
  <script type="module">
    await ymaps3.ready;
    const {YMap, YMapDefaultSchemeLayer, YMapDefaultFeaturesLayer} = ymaps3;

    const map = new YMap(
      document.getElementById('map'),
      {
        location: {
          center: [36.234567, 55.523456], // [lng, lat] для ДНП Тихие Луга
          zoom: 15
        }
      }
    );

    map.addChild(new YMapDefaultSchemeLayer());
    map.addChild(new YMapDefaultFeaturesLayer());
  </script>
</head>
<body>
  <div id="map" style="width: 100%; height: 600px;"></div>
</body>
</html>

С динамической загрузкой

// lib/ymaps.ts
export async function loadYandexMaps(apiKey: string) {
  return new Promise((resolve, reject) => {
    if (window.ymaps3) {
      resolve(window.ymaps3);
      return;
    }

    const script = document.createElement('script');
    script.src = `https://api-maps.yandex.ru/3.0/?apikey=${apiKey}&lang=ru_RU`;
    script.async = true;
    script.onload = async () => {
      await window.ymaps3.ready;
      resolve(window.ymaps3);
    };
    script.onerror = reject;
    document.head.appendChild(script);
  });
}

Основные сущности

1. YMap - Карта

Главный объект карты.

const map = new YMap(
  document.getElementById('map'),
  {
    location: {
      center: [37.415663, 55.721749], // [longitude, latitude]
      zoom: 10,
      duration: 300 // Анимация перехода (мс)
    },
    mode: 'vector', // 'vector' | 'raster'
    behaviors: ['drag', 'scrollZoom', 'dblClick', 'multiTouch']
  }
);

Параметры: - center - центр карты [lng, lat] - zoom - уровень масштабирования (0-21) - mode - тип карты (vector или raster) - behaviors - массив включенных поведений

2. YMapDefaultSchemeLayer - Слой карты

Базовый слой с картой местности.

map.addChild(new YMapDefaultSchemeLayer({
  customization: [
    {
      tags: 'water',
      elements: 'geometry',
      stylers: [{color: '#00ccff'}]
    }
  ]
}));

3. YMapDefaultFeaturesLayer - Слой объектов

Слой для отображения меток, линий, полигонов.

map.addChild(new YMapDefaultFeaturesLayer());

4. YMapMarker - Метки

Отображение маркеров на карте.

import {YMapMarker} from '@yandex/ymaps3-types';

const marker = new YMapMarker(
  {
    coordinates: [37.415663, 55.721749],
    draggable: false
  },
  // HTML контент метки
  document.createElement('div')
);

// Добавить метку на карту
map.addChild(marker);

Пример с кастомным маркером:

const markerElement = document.createElement('div');
markerElement.className = 'custom-marker';
markerElement.innerHTML = `
  <div class="marker-icon">
    <span class="marker-label">Участок №502</span>
  </div>
`;

const marker = new YMapMarker(
  {
    coordinates: [36.234567, 55.523456],
    onClick: () => {
      console.log('Участок №502 выбран');
    }
  },
  markerElement
);

map.addChild(marker);

5. YMapListener - События

Обработка событий карты.

import {YMapListener} from '@yandex/ymaps3-types';

const listener = new YMapListener({
  onClick: (object, event) => {
    console.log('Координаты клика:', event.coordinates);
  },
  onMouseMove: (object, event) => {
    console.log('Координаты курсора:', event.coordinates);
  }
});

map.addChild(listener);

6. YMapControls - Элементы управления

Добавление кнопок управления картой.

import {
  YMapZoomControl,
  YMapGeolocationControl
} from '@yandex/ymaps3-types/packages/controls';

// Кнопки зума
const zoomControl = new YMapZoomControl({});
map.addChild(zoomControl);

// Кнопка геолокации
const geolocationControl = new YMapGeolocationControl({});
map.addChild(geolocationControl);

Кластеризация меток

Для большого количества меток (например, 436 участков).

import {
  YMapClusterer,
  clusterByGrid
} from '@yandex/ymaps3-types/packages/clusterer';

// Создать массив маркеров
const markers = properties.map(property => ({
  type: 'Feature',
  id: property.id,
  geometry: {
    type: 'Point',
    coordinates: [property.longitude, property.latitude]
  },
  properties: {
    name: `Участок №${property.number}`,
    price: property.price
  }
}));

// Создать кластеризатор
const clusterer = new YMapClusterer({
  method: clusterByGrid({gridSize: 64}),
  features: markers,
  marker: (feature) => {
    const element = document.createElement('div');
    element.className = 'property-marker';
    element.innerHTML = `
      <div class="marker-content">
        ${feature.properties.name}
      </div>
    `;
    return new YMapMarker(
      {
        coordinates: feature.geometry.coordinates,
        onClick: () => handlePropertyClick(feature.id)
      },
      element
    );
  },
  cluster: (coordinates, features) => {
    const element = document.createElement('div');
    element.className = 'cluster-marker';
    element.innerHTML = `<span>${features.length}</span>`;
    return new YMapMarker(
      {
        coordinates,
        onClick: () => {
          map.setLocation({
            center: coordinates,
            zoom: map.zoom + 2,
            duration: 300
          });
        }
      },
      element
    );
  }
});

map.addChild(clusterer);

Geocoder HTTP API

Прямое геокодирование (адрес → координаты)

GET https://geocode-maps.yandex.ru/1.x/?apikey=YOUR_API_KEY&geocode=Московская+область,+Можайский+район,+ДНП+Тихие+Луга&format=json&lang=ru_RU

Response:

{
  "response": {
    "GeoObjectCollection": {
      "featureMember": [
        {
          "GeoObject": {
            "Point": {
              "pos": "36.234567 55.523456"
            },
            "name": "ДНП Тихие Луга",
            "description": "Можайский район, Московская область, Россия",
            "boundedBy": {
              "Envelope": {
                "lowerCorner": "36.230000 55.520000",
                "upperCorner": "36.240000 55.530000"
              }
            }
          }
        }
      ]
    }
  }
}

Обратное геокодирование (координаты → адрес)

GET https://geocode-maps.yandex.ru/1.x/?apikey=YOUR_API_KEY&geocode=36.234567,55.523456&format=json&lang=ru_RU

Response:

{
  "response": {
    "GeoObjectCollection": {
      "featureMember": [
        {
          "GeoObject": {
            "name": "ДНП Тихие Луга",
            "description": "Можайский район, Московская область, Россия",
            "Point": {
              "pos": "36.234567 55.523456"
            }
          }
        }
      ]
    }
  }
}

Параметры запроса

Параметр Тип Описание
apikey string API ключ (обязательно)
geocode string Адрес или координаты для геокодирования
format string Формат ответа: json, xml (default: xml)
lang string Язык ответа: ru_RU, en_US, tr_TR
results number Количество результатов (default: 10, max: 100)
skip number Пропустить первые N результатов
kind string Тип объекта: house, street, metro, district, locality
rspn number Ограничить область поиска (используется с ll и spn)
ll string Центр области поиска longitude,latitude
spn string Размер области поиска width,height
bbox string Альтернатива ll+spn: left,bottom~right,top

Пример в коде

async function geocodeAddress(address: string): Promise<{lat: number, lng: number} | null> {
  const apiKey = process.env.YANDEX_MAPS_API_KEY;
  const url = `https://geocode-maps.yandex.ru/1.x/?apikey=${apiKey}&geocode=${encodeURIComponent(address)}&format=json&lang=ru_RU`;

  try {
    const response = await fetch(url);
    const data = await response.json();

    const geoObject = data.response.GeoObjectCollection.featureMember[0]?.GeoObject;
    if (!geoObject) return null;

    const [lng, lat] = geoObject.Point.pos.split(' ').map(Number);
    return {lat, lng};
  } catch (error) {
    console.error('Geocoding error:', error);
    return null;
  }
}

// Использование
const coords = await geocodeAddress('Московская область, Можайский район, ДНП Тихие Луга');
console.log(coords); // {lat: 55.523456, lng: 36.234567}

React интеграция

Вариант 1: Официальный @yandex/ymaps3-reactify

npm install @yandex/ymaps3-reactify

Настройка

// lib/ymaps.ts
import {YMap, YMapDefaultSchemeLayer, YMapDefaultFeaturesLayer, YMapMarker} from '@yandex/ymaps3-types';
import reactify from '@yandex/ymaps3-reactify';

let ymaps: typeof import('@yandex/ymaps3-types') | null = null;

export async function initYMaps(apiKey: string) {
  if (ymaps) return ymaps;

  await new Promise((resolve) => {
    const script = document.createElement('script');
    script.src = `https://api-maps.yandex.ru/3.0/?apikey=${apiKey}&lang=ru_RU`;
    script.onload = resolve;
    document.head.appendChild(script);
  });

  await window.ymaps3.ready;
  ymaps = window.ymaps3;
  return ymaps;
}

export async function getReactComponents(apiKey: string) {
  const ymaps = await initYMaps(apiKey);
  const reactify = await import('@yandex/ymaps3-reactify');

  const {
    YMap: YMapReact,
    YMapDefaultSchemeLayer: YMapDefaultSchemeLayerReact,
    YMapDefaultFeaturesLayer: YMapDefaultFeaturesLayerReact,
    YMapMarker: YMapMarkerReact
  } = reactify.reactify.module(ymaps);

  return {
    YMapReact,
    YMapDefaultSchemeLayerReact,
    YMapDefaultFeaturesLayerReact,
    YMapMarkerReact
  };
}

Использование в компоненте

// components/PropertiesMap.tsx
import React, {useEffect, useState} from 'react';
import {getReactComponents} from '@/lib/ymaps';

interface Property {
  id: string;
  number: string;
  latitude: number;
  longitude: number;
  price: number;
  status: 'available' | 'reserved' | 'sold';
}

interface PropertiesMapProps {
  properties: Property[];
  onPropertyClick: (propertyId: string) => void;
}

export function PropertiesMap({properties, onPropertyClick}: PropertiesMapProps) {
  const [components, setComponents] = useState<any>(null);

  useEffect(() => {
    const apiKey = import.meta.env.VITE_YANDEX_MAPS_API_KEY;
    getReactComponents(apiKey).then(setComponents);
  }, []);

  if (!components) {
    return <div className="flex items-center justify-center h-[600px]">Загрузка карты...</div>;
  }

  const {YMapReact, YMapDefaultSchemeLayerReact, YMapDefaultFeaturesLayerReact, YMapMarkerReact} = components;

  return (
    <YMapReact
      location={{
        center: [36.234567, 55.523456],
        zoom: 14
      }}
      mode="vector"
      className="w-full h-[600px]"
    >
      <YMapDefaultSchemeLayerReact />
      <YMapDefaultFeaturesLayerReact />

      {properties.map((property) => (
        <YMapMarkerReact
          key={property.id}
          coordinates={[property.longitude, property.latitude]}
          onClick={() => onPropertyClick(property.id)}
        >
          <div className={`
            property-marker
            ${property.status === 'available' ? 'bg-green-500' :
              property.status === 'reserved' ? 'bg-yellow-500' : 'bg-gray-500'}
            text-white px-3 py-2 rounded-lg shadow-lg cursor-pointer
            hover:scale-110 transition-transform
          `}>
            <div className="text-xs font-bold">{property.number}</div>
            <div className="text-xs">{(property.price / 1000).toFixed(0)}K </div>
          </div>
        </YMapMarkerReact>
      ))}
    </YMapReact>
  );
}

Вариант 2: Сторонняя библиотека @pbe/react-yandex-maps

npm install @pbe/react-yandex-maps

Использование

import React from 'react';
import {YMaps, Map, Placemark, Clusterer} from '@pbe/react-yandex-maps';

interface PropertiesMapProps {
  properties: Property[];
}

export function PropertiesMap({properties}: PropertiesMapProps) {
  const apiKey = import.meta.env.VITE_YANDEX_MAPS_API_KEY;

  return (
    <YMaps query={{apikey: apiKey, lang: 'ru_RU'}}>
      <Map
        defaultState={{
          center: [55.523456, 36.234567],
          zoom: 14
        }}
        width="100%"
        height="600px"
      >
        <Clusterer
          options={{
            preset: 'islands#greenClusterIcons',
            groupByCoordinates: false,
            clusterDisableClickZoom: false
          }}
        >
          {properties.map((property) => (
            <Placemark
              key={property.id}
              geometry={[property.latitude, property.longitude]}
              properties={{
                balloonContent: `
                  <div>
                    <h3>Участок №${property.number}</h3>
                    <p>Цена: ${property.price.toLocaleString('ru-RU')} ₽</p>
                    <p>Статус: ${property.status}</p>
                  </div>
                `
              }}
              options={{
                preset: property.status === 'available'
                  ? 'islands#greenIcon'
                  : 'islands#grayIcon'
              }}
            />
          ))}
        </Clusterer>
      </Map>
    </YMaps>
  );
}

Rate Limits и квоты

JavaScript API 3.0

  • Бесплатный лимит:
  • 25,000 загрузок карты в месяц
  • После превышения: 50 ₽ за 1000 загрузок

  • Подсчет загрузок:

  • Каждое обращение к API (загрузка страницы с картой) = 1 загрузка
  • Перезагрузка страницы = новая загрузка

Geocoder HTTP API

  • Бесплатный лимит:
  • 25,000 запросов в сутки

  • Rate limit:

  • До 10 запросов в секунду
  • При превышении: HTTP 429 (Too Many Requests)

Рекомендации

  1. Кэшируйте результаты геокодирования:

    const geocodeCache = new Map<string, {lat: number, lng: number}>();
    
    async function geocodeWithCache(address: string) {
      if (geocodeCache.has(address)) {
        return geocodeCache.get(address)!;
      }
    
      const result = await geocodeAddress(address);
      if (result) {
        geocodeCache.set(address, result);
      }
      return result;
    }
    

  2. Используйте debounce для поиска:

    import {useDebouncedCallback} from 'use-debounce';
    
    const debouncedGeocode = useDebouncedCallback(
      async (query: string) => {
        const result = await geocodeAddress(query);
        setResults(result);
      },
      500 // 500ms задержка
    );
    

  3. Батчинг запросов: Если нужно геокодировать много адресов, делайте паузы между запросами:

    async function geocodeBatch(addresses: string[]) {
      const results = [];
      for (let i = 0; i < addresses.length; i++) {
        results.push(await geocodeAddress(addresses[i]));
    
        // Пауза каждые 10 запросов
        if ((i + 1) % 10 === 0) {
          await new Promise(resolve => setTimeout(resolve, 1000));
        }
      }
      return results;
    }
    


Best Practices

1. Ленивая загрузка карты

Загружайте карту только когда она нужна:

import {lazy, Suspense} from 'react';

const PropertiesMap = lazy(() => import('./components/PropertiesMap'));

function PropertyPage() {
  return (
    <Suspense fallback={<div>Загрузка карты...</div>}>
      <PropertiesMap properties={properties} />
    </Suspense>
  );
}

2. Оптимизация маркеров

Для большого количества участков используйте кластеризацию:

// Всегда используйте clusterByGrid для 436 участков
const clusterer = new YMapClusterer({
  method: clusterByGrid({gridSize: 64}),
  features: markers
});

3. Ограничение области карты

Ограничьте область просмотра районом Можайска:

const map = new YMap(element, {
  location: {
    center: [36.234567, 55.523456],
    zoom: 14
  },
  restrictMapArea: [
    [36.0, 55.4], // Юго-западный угол
    [36.5, 55.7]  // Северо-восточный угол
  ]
});

4. Мобильная оптимизация

const isMobile = window.innerWidth < 768;

const map = new YMap(element, {
  location: {
    center: [36.234567, 55.523456],
    zoom: isMobile ? 13 : 14
  },
  behaviors: isMobile
    ? ['drag', 'multiTouch']
    : ['drag', 'scrollZoom', 'dblClick']
});

5. TypeScript типы

npm install --save-dev @yandex/ymaps3-types
/// <reference types="@yandex/ymaps3-types" />

import type {YMap, YMapMarker, LngLat} from '@yandex/ymaps3-types';

interface MapConfig {
  center: LngLat;
  zoom: number;
}

const config: MapConfig = {
  center: [36.234567, 55.523456],
  zoom: 14
};

Примеры использования в Silent Meadow

1. Карта участков с фильтрацией

// pages/properties/index.tsx
import {useState} from 'react';
import {PropertiesMap} from '@/components/PropertiesMap';
import {PropertyFilters} from '@/components/PropertyFilters';

export default function PropertiesPage() {
  const [filters, setFilters] = useState({
    minPrice: 0,
    maxPrice: 1000000,
    minArea: 0,
    maxArea: 20,
    status: 'available'
  });

  const {data: properties} = useQuery(
    ['properties', filters],
    () => api.getProperties(filters)
  );

  return (
    <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
      <div className="lg:col-span-1">
        <PropertyFilters filters={filters} onChange={setFilters} />
      </div>
      <div className="lg:col-span-2">
        <PropertiesMap
          properties={properties || []}
          onPropertyClick={(id) => router.push(`/properties/${id}`)}
        />
      </div>
    </div>
  );
}

2. Страница детального просмотра участка

// pages/properties/[id].tsx
import {YMapReact, YMapMarkerReact} from '@/lib/ymaps';

export default function PropertyDetailPage() {
  const {id} = useParams();
  const {data: property} = useQuery(['property', id], () => api.getProperty(id));

  if (!property) return <div>Загрузка...</div>;

  return (
    <div>
      <h1>Участок {property.number}</h1>

      <YMapReact
        location={{
          center: [property.longitude, property.latitude],
          zoom: 16
        }}
        className="w-full h-[400px] rounded-lg"
      >
        <YMapDefaultSchemeLayerReact />
        <YMapDefaultFeaturesLayerReact />

        <YMapMarkerReact coordinates={[property.longitude, property.latitude]}>
          <div className="w-8 h-8 bg-green-500 rounded-full border-4 border-white shadow-lg"></div>
        </YMapMarkerReact>
      </YMapReact>

      <div className="mt-6">
        <p>Адрес: {property.full_address}</p>
        <p>Площадь: {property.area_sotok} соток</p>
        <p>Цена: {property.price.toLocaleString('ru-RU')} </p>
      </div>
    </div>
  );
}

3. Геокодирование при добавлении участка

// pages/admin/properties/new.tsx
import {geocodeAddress} from '@/lib/yandex-geocoder';

export default function NewPropertyPage() {
  const [form, setForm] = useState({
    number: '',
    address: '',
    latitude: 0,
    longitude: 0
  });

  const handleGeocodeAddress = async () => {
    const coords = await geocodeAddress(form.address);
    if (coords) {
      setForm(prev => ({
        ...prev,
        latitude: coords.lat,
        longitude: coords.lng
      }));
      toast.success('Координаты найдены!');
    } else {
      toast.error('Не удалось найти адрес');
    }
  };

  return (
    <form>
      <input
        value={form.address}
        onChange={(e) => setForm({...form, address: e.target.value})}
        placeholder="Адрес участка"
      />
      <button type="button" onClick={handleGeocodeAddress}>
        Найти координаты
      </button>

      <input value={form.latitude} disabled placeholder="Широта" />
      <input value={form.longitude} disabled placeholder="Долгота" />
    </form>
  );
}

Обработка ошибок

// lib/yandex-maps-error-handler.ts
export class YandexMapsError extends Error {
  constructor(
    message: string,
    public code: string,
    public details?: any
  ) {
    super(message);
    this.name = 'YandexMapsError';
  }
}

export async function safeGeocode(address: string) {
  try {
    const result = await geocodeAddress(address);
    return {success: true, data: result};
  } catch (error) {
    if (error.response?.status === 403) {
      return {
        success: false,
        error: new YandexMapsError(
          'Неверный API ключ',
          'INVALID_API_KEY'
        )
      };
    }

    if (error.response?.status === 429) {
      return {
        success: false,
        error: new YandexMapsError(
          'Превышен лимит запросов',
          'RATE_LIMIT_EXCEEDED'
        )
      };
    }

    return {
      success: false,
      error: new YandexMapsError(
        'Ошибка геокодирования',
        'GEOCODE_ERROR',
        error
      )
    };
  }
}

Полезные ссылки


Обновлено: 2026-02-16

Sources: