# Документация
Подключите карту, адресный поиск, маршрут и матрицу расстояний к своему сайту или приложению.
## С чего начать
Для карты достаточно подключить JavaScript и CSS SDK **1.0.0-rc.9**. Регистрация и ключ не нужны. Для поиска адресов, расчёта маршрута и матрицы создайте проект и API-ключ.
API можно попробовать в пределах бесплатной квоты после регистрации и подтверждения email. Пополнять баланс и привязывать банковскую карту для этого не нужно. [Получить ключ и выполнить первый запрос](https://mmapps.ru/docs/getting-started).
| Задача | Раздел |
| --- | --- |
| Выполнить первый запрос API | [Быстрый старт](https://mmapps.ru/docs/getting-started) |
| Добавить карту на страницу | [Подключение карты](https://mmapps.ru/docs/maps) |
| Работать с объектами, событиями и интерфейсом карты | [Полный справочник SDK RC9](https://mmapps.ru/docs/sdk) |
| Найти адрес или координаты | [Поиск адресов](https://mmapps.ru/docs/addresses) |
| Рассчитать автомобильный путь | [Маршрут](https://mmapps.ru/docs/routes) |
| Сравнить расстояния и время между точками | [Матрица расстояний](https://mmapps.ru/docs/matrix) |
| Выдать права и ограничить доступ | [Ключи API](https://mmapps.ru/docs/keys) |
| Узнать цену и контролировать расходы | [Расходы и лимиты](https://mmapps.ru/docs/billing) |
## Справочники и файлы
- [OpenAPI](https://mmapps.ru/gateway.openapi.yaml) — HTTP-методы, поля запросов и ответов, ошибки и ограничения.
- [Типы SDK](https://cdn.mmapps.ru/sdk/1.0.0-rc.9/index.d.ts) — точные сигнатуры JavaScript API в формате TypeScript.
- [Список разделов в Markdown](https://mmapps.ru/llms.txt).
- [Вся документация одним файлом](https://mmapps.ru/llms-full.txt).
На каждой странице есть оглавление, кнопки копирования примеров и ссылка на Markdown. В примерах замените условный ключ своим, а адреса и координаты — данными Вашего приложения.
## Условия использования
Карта и SDK бесплатны. Запросы сервисного API используют бесплатную квоту, а после её исчерпания — баланс аккаунта. Расчёт матрицы учитывается по числу пар точек. Доступность операций и права ключа проверяйте в кабинете.
Ммаппс можно использовать в CRM и других приложениях за авторизацией. Результаты API разрешено сохранять в своей базе и использовать повторно с соблюдением лицензий исходных данных и требований к персональным данным. [Подробнее о хранении и оплате](https://mmapps.ru/docs/billing).
[Пользовательское соглашение](https://mmapps.ru/documents/terms), [обработка персональных данных](https://mmapps.ru/documents/terms#privacy) и [лицензии SDK и карты](https://mmapps.ru/documents/sdk-license).
## Чтение без интернета
После первого посещения и сохранения страниц документацию можно читать без сети. Добавить сайт на устройство можно через меню браузера. Для загрузки карты, запросов API и работы с кабинетом нужен интернет.
---
# Быстрый старт
Создайте ключ и выполните первый запрос к API Ммаппс.
## Выберите способ подключения
Если нужна только карта, начните с [примера без ключа](https://mmapps.ru/docs/maps). Следующие шаги нужны для поиска адресов, маршрута и матрицы.
## Попробуйте API бесплатно
После регистрации и подтверждения email Вы можете выпустить личный API-ключ и выполнять запросы в пределах [бесплатной месячной квоты](https://mmapps.ru/docs/billing). Пополнять баланс и привязывать банковскую карту для этого не нужно.
Проверьте поиск на адресах, с которыми работает Ваш бизнес. Когда квота закончится, новые платные запросы потребуют средств на балансе и доступного бюджета. Регистрация для бесплатной карты по-прежнему не нужна.
## Создайте проект и ключ
1. [Зарегистрируйтесь](https://mmapps.ru/app/register), подтвердите email и откройте [«Проекты и ключи»](https://mmapps.ru/app/projects).
2. Используйте основной проект или создайте отдельный для своего приложения.
3. Нажмите «Создать ключ», выберите «На сервере» и разрешите подсказки адресов (`search`).
4. Скопируйте ключ: полностью он показывается один раз. Сохраните его в приватных настройках сервера.
Для JavaScript в браузере создайте отдельный ключ «На сайте» и укажите разрешённый сайт. Серверный ключ в браузер не передавайте. [Подробнее о правах и хранении](https://mmapps.ru/docs/keys).
## Выполните запрос
Базовый адрес API — `https://mmapps.ru`. Переменная окружения `MMAPPS_SERVER_KEY` в примере должна содержать Ваш серверный ключ. `YOUR_SERVER_KEY` — условное обозначение, а не действующий ключ.
```bash
curl --request POST 'https://mmapps.ru/v1/search' \
--header "Authorization: Bearer ${MMAPPS_SERVER_KEY}" \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'Idempotency-Key: 4c9cc4ef-b924-40df-8f61-c36a865b2d7e' \
--data '{"q":"Примерная улица","limit":5}'
```
Замените «Примерная улица» на нужный Вам адрес. Для каждого нового запроса создавайте новый `Idempotency-Key`. Если повторяете тот же запрос, сохраните прежний ключ и тело.
## Обработайте ответ
Читайте `outcome` и список `candidates`. Отобразите `candidate.label` и предложите пользователю выбрать подходящий адрес. `candidate.point` может быть `null`: такой вариант нельзя использовать как координату. `ref` сохраняйте целиком, включая `id` и `dataset_version`.
HTTP 200 может означать, что вариантов нет или нужно уточнить запрос. Форматы и значения результатов описаны в [разделе поиска](https://mmapps.ru/docs/addresses).
## Проверьте расход
Откройте [«Использование»](https://mmapps.ru/app/usage), затем задайте бюджет в [«Лимиты и уведомления»](https://mmapps.ru/app/limits). Бесплатная квота общая для проектов аккаунта.
Для диагностики сохраняйте код ошибки и `X-Request-Id`. Не записывайте API-ключи, адреса пользователей и координаты в журналы ошибок и аналитику.
---
# Подключение бесплатной карты
Карта и JavaScript SDK работают без регистрации и API-ключа.
## Минимальный пример
Сохраните HTML и откройте его через веб-сервер. Контейнеру карты нужна явная высота. JS и CSS подключайте из одной версии SDK — **1.0.0-rc.9**.
```html
Карта Ммаппс
```
Подложка загружается с публичного CDN. Не добавляйте к этим запросам API-ключ, заголовок Authorization или cookies. Если подключаете файлы SDK со своего сервера, сохраняйте весь комплект выпуска, включая worker рядом с `index.js` и лицензионные файлы.
## Добавьте метку
Следующий код выполняется после создания карты:
```javascript
const marker = map.objects.add({
id: 'office',
kind: 'marker',
point: { latitude: 55.75, longitude: 37.61 },
label: 'Пример офиса',
style: { color: '#1187ff' },
});
marker.on('click', () => {
marker.popup.open({ text: 'Пример карточки места' });
});
```
Замените координаты на положение Вашего офиса. Используйте `text` для текста карточки, а для собственного оформления — DOM-элемент. [Объекты, коллекции, GeoJSON и кластеры](https://mmapps.ru/docs/sdk#obieekty-i-kollekcii).
## Подключите поиск
[Зарегистрируйтесь](https://mmapps.ru/app/register) и создайте ключ «На сайте» с правами `search`, `geocode`, `reverse` и добавьте адрес Вашего сайта в разрешённые. `YOUR_BROWSER_KEY` ниже заменяется этим браузерным ключом.
```javascript
import { createSearchController } from 'https://cdn.mmapps.ru/sdk/1.0.0-rc.9/index.js';
const services = createClient({
apiBaseUrl: 'https://mmapps.ru',
apiKey: 'YOUR_BROWSER_KEY',
});
const controller = createSearchController({
client: services.geocoder,
map,
fitResults: true,
});
const search = map.controls.addSearch({
controller,
position: 'top-left',
onSelect(selection) {
// Сохраните selection.ref и selection.point в состоянии приложения.
},
});
// При удалении компонента:
// search.remove(); controller.destroy(); services.destroy();
```
Выбор строки — локальное действие без повторного геокодинга. `selection.point` может быть `null`, `selection.queryPoint` сохраняет исходную точку обратного поиска. Кнопки маршрута и передачи результата подключаются через `onRoute` и `onShare`. [Все методы поиска](https://mmapps.ru/docs/sdk#poisk-i-vybor-adresa).
## Тема и номера домов
`theme: 'light'` или `'dark'` задаёт тему при создании, `map.setTheme()` меняет её позже. Для поддерживаемой подложки настройка `houseNumbers` управляет появлением и размером номеров:
```javascript
const map = await geo.createMap({
element: document.getElementById('map'),
mapId: 'ru',
houseNumbers: { minZoom: 15.5, sizeScale: 0.88 },
});
```
`minZoom` — масштаб появления подписей, `sizeScale` — множитель размера шрифта. Настройка не добавляет отсутствующие номера в данные. Не создавайте две карты в одном контейнере: пример выше заменяет параметры первого вызова.
## Готовый виджет и дополнительные возможности
`createMapWidget()` объединяет карту, поиск, редактор маршрута, меню точки и при необходимости таймлайн. Передайте только сервисы, которые нужны приложению. Бесплатная карта работает и без них.
SDK также поддерживает собственные маркеры, карточки, линии, полигоны, окружности, кластеры, управление камерой и воспроизведение Ваших временных данных. [Полный справочник SDK RC9](https://mmapps.ru/docs/sdk).
## Освобождение ресурсов
При удалении карты вызывайте `map.destroy()`. Если использовали виджет — `widget.destroy()`. Отдельно созданные контроллеры, таймлайн и API-клиент освобождайте через их `destroy()`; функции, возвращённые `on()`, отменяют подписку на событие.
Перемещение карты, отображение объектов и готовой линии маршрута бесплатны. Поиск, расчёт маршрута и матрицы — отдельные запросы сервисного API. [Тарифы](https://mmapps.ru/docs/billing).
---
# JavaScript SDK RC9
Справочник публичных методов, параметров и событий SDK 1.0.0-rc.9.
## Подключение и типы
SDK поставляется как браузерный ES-модуль. Подключите `https://cdn.mmapps.ru/sdk/1.0.0-rc.9/index.css`, затем импортируйте нужные функции из `https://cdn.mmapps.ru/sdk/1.0.0-rc.9/index.js`. [Готовая HTML-страница](https://mmapps.ru/docs/maps).
Точные типы поставляются вместе с выпуском: [точка входа TypeScript](https://cdn.mmapps.ru/sdk/1.0.0-rc.9/index.d.ts), [экспорты SDK](https://cdn.mmapps.ru/sdk/1.0.0-rc.9/types/clients/maps/index.d.ts). Ссылки на типы ниже относятся к той же версии. Обозначение `?` в сигнатурах означает необязательный аргумент.
| Экспорт | Назначение |
| --- | --- |
| `createClient` | Карта и клиент сервисного API |
| `createMap` | Отдельная карта |
| `createMapWidget` | Карта с готовыми инструментами |
| `checkedPoint` | Проверка координат |
| `createGeocoder` | Адресный клиент со своим обработчиком запросов |
| `createSearchController` | Состояние поиска и выбор адреса |
| `attachSearchControl` | Форма поиска в DOM-контейнере |
| `attachRouteControl` | Редактор маршрута |
| `attachContextMenu` | Меню действий с точкой |
| `createRouteOverlay` | Отображение рассчитанного маршрута |
| `routeView`, `routeCandidates`, `geocodedRouteView` | Подготовка ответа маршрута для интерфейса |
| `matrixView`, `geocodedMatrixView` | Проверка ответа матрицы |
| `createTimeline`, `attachTimelineControl` | Временные данные и панель воспроизведения |
| `MapSDKError`, `GeocoderError` | Классы ошибок |
## Клиент и сервисные запросы
`createClient(options?) → GeoClient`. Без параметров создаёт клиент бесплатной карты. Параметры `ClientOptions`: `apiBaseUrl`, `apiKey`, `catalogUrl`, `fetch`, `timeoutMs`.
```javascript
import { createClient } from 'https://cdn.mmapps.ru/sdk/1.0.0-rc.9/index.js';
const geo = createClient({
apiBaseUrl: 'https://mmapps.ru',
apiKey: 'YOUR_BROWSER_KEY',
});
const operationKey = crypto.randomUUID();
const result = await geo.geocoder.search(
{ q: 'Примерная улица', limit: 5 },
{ operationKey },
);
```
`apiBaseUrl` — адрес API без пути, параметров и фрагмента. Для сервисных вызовов используйте HTTPS и браузерный ключ с нужными правами и разрешённым origin. `YOUR_BROWSER_KEY` — условное обозначение. Серверный ключ в SDK не передавайте.
| Метод | Результат |
| --- | --- |
| `geo.createMap(options)` | `Promise` |
| `geo.geocoder.search(query, options?)` | Подсказки адресов |
| `geo.geocoder.geocode(query, options?)` | Координаты по адресу |
| `geo.geocoder.reverse(query, options?)` | Адрес по точке |
| `geo.geocoder.resolve(ref, options?)` | Выбранный объект |
| `geo.routes.calculate(request, options?)` | Маршрут по координатам |
| `geo.routes.geocode(request, options?)` | Маршрут по адресам и подтверждённым точкам |
| `geo.routes.matrix(request, options?)` | Матрица по координатам |
| `geo.routes.matrixGeocode(request, options?)` | Матрица по адресам и подтверждённым точкам |
| `geo.on('usage', callback)` | Подписка на учёт операции; возвращает функцию отписки |
| `geo.on('error', callback)` | Подписка на ошибку; возвращает функцию отписки |
| `geo.destroy()` | Завершение работы клиента |
`OperationOptions` содержит `signal?: AbortSignal` и `operationKey?: string`. Для повторов сохраняйте ключ и тело запроса; изменение данных — новая операция. Если ключ не задан, SDK создаёт его сам. Отмена ожидания через `signal` не доказывает, что запрос не успел выполниться.
`usage` передаёт `operationId`, `requestId`, `chargedMicrorub`, `funding`, `retryAfter`, `stage`. При наличии в ответе доступны `quantity`, `freeUnits`, `paidUnits`, `meter`. Суммы и количества передаются строками. [Правила оплаты](https://mmapps.ru/docs/billing), [типы клиента](https://cdn.mmapps.ru/sdk/1.0.0-rc.9/types/clients/maps/client.d.ts).
## Создание карты
`createMap(options) → Promise` и `geo.createMap(options) → Promise`. После создания дождитесь `map.ready`.
| Параметр | Назначение |
| --- | --- |
| `element` | Обязательный DOM-контейнер с заданной высотой |
| `mapId`, `catalogUrl` | Выбор карты и каталога у `geo.createMap`; по умолчанию `ru` и публичный каталог |
| `styleUrl` | URL собственного публичного стиля |
| `basemap` | `{type: 'public', styleUrl, …}` или `{type: 'public_cdn', catalogUrl, mapId}` |
| `view` | Начальные `center`, `zoom`, `bearing`, `pitch` |
| `initialPoint`, `editable` | Исходная точка и возможность её изменения |
| `theme` | `'light'` или `'dark'` |
| `houseNumbers` | `{minZoom?, sizeScale?}` для поддерживаемой подложки |
| `controls`, `interactions` | Инструменты и жесты, описанные ниже |
| `renderMarker` | Отрисовщик маркера исходной точки и совместимых маркеров |
| `workerUrl` | URL worker из того же выпуска |
| `signal`, `timeoutMs` | Отмена и таймаут загрузки |
| `fetch` | Свой fetch у `createMap`; для `geo.createMap` задаётся на клиенте |
| `onReady`, `onLoading`, `onError` | Обратные вызовы состояния |
У публичного `basemap` дополнительно возможны `styleSha256`, `styleBytes`, `assetSizes`. Для обычного подключения достаточно `mapId`. `houseNumbers.minZoom` принимает 0–20, `sizeScale` — 0,5–2; стандартные значения — 16 и 1. Настройки меняют существующие подписи, а не добавляют данные.
Координаты `GeoPoint` имеют вид `{latitude, longitude}`. `checkedPoint(latitude, longitude)` проверяет конечные числа и диапазоны WGS84. GeoJSON использует обратный порядок: `[longitude, latitude]`. `Bounds` — `{west, south, east, north}`, экранная точка — `{x, y}`.
## Состояние и события карты
| Метод или поле | Назначение |
| --- | --- |
| `map.ready` | Ожидание готовности |
| `map.getState()` | `loading`, `ready`, `degraded`, `failed`, `destroyed` |
| `map.waitForIdle({signal?, timeoutMs?}?)` | Ожидание завершения текущей отрисовки |
| `map.resize()` | Обновление размера после изменения контейнера |
| `map.setTheme(theme)` | Смена светлой или тёмной темы |
| `map.setBasemap(basemap)` | Асинхронная смена подложки с сохранением камеры и объектов |
| `map.setPoint(point)` | Установка исходной точки; `null` удаляет её |
| `map.getQueryPoint()` | Исходная точка или `null` |
| `map.focusPoint(point, zoom?)` | Фокус на точке |
| `map.fitPoints(points, options?)`, `map.fitVisible(options?)` | Подбор видимой области |
| `map.on(type, callback)`, `map.once(type, callback)` | Подписка, возвращающая отписку |
| `map.off(type, callback)` | Отмена подписки |
| `map.onPointChange(callback)` | Изменение исходной точки, причина `drag` или `click` |
| `map.onMarkerClick(callback)` | Клик по совместимому маркеру |
| `map.destroy()` | Удаление карты и принадлежащих ей ресурсов |
События указателя: `click`, `dblclick`, `contextmenu`, `mousemove`, `mouseenter`, `mouseleave`, `pointerdown`, `pointerup`, `touchstart`, `touchend`. События камеры: `move`, `movestart`, `moveend`, `zoom`, `zoomend`, `rotate`, `pitch`, `resize`, `idle`. Состояние: `statechange`, `error`, `destroy`.
Событие указателя содержит `point`, `screen`, `objectId`, `originalEvent`, `stopPropagation()` и `preventDefault()`. [Типы карты и событий](https://cdn.mmapps.ru/sdk/1.0.0-rc.9/types/clients/maps/types.d.ts).
Для существующих интеграций сохранены `setVisits(points)`, `setMarkers(points)`, `setTracks(tracks)`. Они принимают типы `MapPoint` и `Track`; при новом подключении используйте `objects` и `createRouteOverlay`. [Типы совместимых методов](https://cdn.mmapps.ru/sdk/1.0.0-rc.9/types/frontend-contracts.d.ts).
## Камера и жесты
| `map.camera` | Назначение |
| --- | --- |
| `getState()`, `getCenter()`, `getZoom()`, `getBounds()` | Чтение вида |
| `setView(view, transition?)` | Центр, масштаб, поворот и наклон |
| `panTo(point, transition?)` | Перемещение центра |
| `setZoom(zoom, transition?)`, `zoomIn(transition?)`, `zoomOut(transition?)` | Масштаб |
| `fitBounds(bounds, options?)` | Показать область |
| `fitObjects(ids?, options?)` | Показать объекты |
| `project(point)`, `unproject(screenPoint)` | Перевод между координатами и пикселями |
| `setLimits({minZoom?, maxZoom?, bounds?})` | Ограничение камеры; `bounds: null` снимает границу |
| `stop()` | Остановка анимации |
`transition` задаёт `duration` и `mode: 'ease'`, `'fly'` или `'jump'`. `FitOptions` содержит `maxZoom`, `duration`, `padding` (число или отступы по четырём сторонам), `resetOrientation`.
`map.interactions.getState()`, `set(options)`, `enable()`, `disable()` управляют жестами. Параметры: `dragPan`, `scrollZoom` (boolean или `'cooperative'`), `doubleClickZoom`, `touchZoom`, `touchRotate`, `dragRotate`, `touchPitch`, `keyboard`.
## Объекты и коллекции
`map.objects.add(object)` возвращает `ObjectHandle`. У каждого объекта обязательны уникальный `id` и `kind`.
| `kind` | Геометрия | Сокращённый метод |
| --- | --- | --- |
| `marker` | `point`, необязательные `draggable`, `renderer` | `addMarker(object)` |
| `line` | `points` | `addLine(object)` |
| `polygon` | `rings` | `addPolygon(object)` |
| `circle` | `center`, `radiusM` | `addCircle(object)` |
| `rectangle` | `bounds` | `addRectangle(object)` |
| `geojson` | `geometry` | `add({id, kind: 'geojson', geometry})` |
Сокращённые методы принимают те же поля без `kind`. Общие параметры: `label`, `properties`, `visible`, `selected`, `style`, `collection`, `zIndex`. Стиль: `color`, `fillColor`, `width`, `opacity`, `fillOpacity`, `radius`, `dash`.
`objects` также предоставляет `get(id)`, `getAll()`, `remove(id)`, `clear()`, `replace(objects)`, `getBounds(ids?)`, `toGeoJSON(ids?)`, `importGeoJSON(value, {replace?, collection?}?)`. `batch(changes)` принимает записи `{action: 'upsert', object}` или `{action: 'remove', id}`.
Поддерживаемая GeoJSON-геометрия: `Point`, `MultiPoint`, `LineString`, `MultiLineString`, `Polygon`, `MultiPolygon`, `GeometryCollection`. Используйте строковые ID объектов, JSON-совместимые `properties` и корректный порядок координат.
`objects.createCollection(id)` создаёт группу. Коллекция имеет `id`, `add`, `get`, `getAll`, `remove`, `clear`, `setVisible`, `setFilter(predicate | null)`, `setStyle`, `getBounds`, `on`, `once`, `destroy`. События: `add`, `change`, `remove`, `statechange`, `destroy`.
## Маркеры, карточки и подсказки
`ObjectHandle` содержит `id`, `getData()`, `update(change)`, `setVisible(boolean)`, `setSelected(boolean)`, `getBounds()`, `getPoint()`, `remove()`, `on()`, `once()`, `popup` и `tooltip`. Через `update` нельзя менять `id` и `kind`.
События объекта: `click`, `dblclick`, `contextmenu`, `mouseenter`, `mouseleave`, `dragstart`, `drag`, `dragend`, `change`, `remove`, `popupopen`, `popupclose`.
`popup` и `tooltip` имеют одинаковые методы: `open(content)`, `update(content)`, `load(loader)`, `close()`, `isOpen()`. `content` — `{text?, element?, offset?, maxWidth?}`. В `load` передаётся функция `(signal) => Promise`; используйте сигнал для отмены своего запроса.
```javascript
const marker = map.objects.addMarker({
id: 'example',
point: { latitude: 55.75, longitude: 37.61 },
label: 'Наша точка',
});
marker.popup.open({ text: 'Карточка места', maxWidth: 270 });
marker.update({ selected: true });
```
Для собственного маркера `renderer(data)` возвращает новый `HTMLElement`, объект `{element, anchor?, offset?, update?, destroy?}` или `null` для стандартного вида. Создавайте отдельный DOM-элемент для каждого маркера. Пользовательский текст вставляйте через `textContent`.
## Кластеры
`map.clusters.create(id, options?) → ClusterGroup`; доступны также `get(id)`, `getAll()`, `clear()`. Параметры группы: `radius`, `maxZoom`, `minPoints`, `zoomOnClick`, `style`. Это группировка Ваших точек в браузере, не адресный поиск.
```javascript
const group = map.clusters.create('places', { zoomOnClick: true });
group.setPoints([
{ id: 'a', point: { latitude: 55.75, longitude: 37.61 } },
{ id: 'b', point: { latitude: 55.76, longitude: 37.62 } },
]);
```
| Метод группы | Назначение |
| --- | --- |
| `getState()` | Состояние, ревизия и количество точек |
| `setPoints(points)`, `getPoint(id)`, `getPoints()`, `clear()` | Данные |
| `batch(changes)` | `{action: 'upsert', point}` или `{action: 'remove', id}` |
| `setVisible(value)`, `setFilter(predicate \| null)`, `setStyle(style)` | Отображение |
| `getBounds()`, `getRendered(limit?)` | Область и отображённые узлы |
| `getChildren(reference, options?)` | Непосредственные дочерние узлы |
| `getLeaves(reference, {limit?, offset?, signal?, timeoutMs?}?)` | Исходные точки кластера |
| `getExpansionZoom(reference, options?)` | Масштаб раскрытия |
| `expand(reference, options?)` | Раскрыть кластер камерой |
| `on(type, callback)`, `once(type, callback)`, `destroy()` | Подписки и удаление |
`ClusterPoint` содержит `id`, `point`, необязательные `label`, `properties`, `selected`. `ClusterSnapshot` — `{nodes, truncated, loading}`. Узел имеет `kind: 'cluster'` с `reference` или `kind: 'point'` с исходной точкой. Reference принадлежит конкретной ревизии группы; после изменения данных получите новый, иначе возможен `STALE_CLUSTER`.
События: `click`, `dblclick`, `contextmenu`, `mouseenter`, `mouseleave`, `change`, `destroy`, `error`. Стиль: `color`, `pointColor`, `selectedColor`, `textColor`, `clusterRadius`, `pointRadius`, `textSize`, `opacity`, `textFont`. Для запросов узлов доступны `signal`, `timeoutMs`, у `getChildren` также `limit`, у `expand` — параметры перехода камеры.
## Инструменты карты
`map.controls.set(id, options)` включает или настраивает встроенный инструмент; `false` выключает его. Общие параметры: `position` (`top-left`, `top-right`, `bottom-left`, `bottom-right`), `visible`, `margin`.
| ID | Дополнительные параметры |
| --- | --- |
| `navigation` | `showZoom`, `showCompass`, `visualizePitch` |
| `scale` | `unit: 'metric' / 'imperial' / 'nautical'`, `maxWidth` |
| `attribution` | `compact`; сохраняйте требуемую атрибуцию данных |
| `fullscreen` | `container`, `pseudo` |
| `geolocation` | `trackUserLocation`, `showUserLocation`, `showAccuracyCircle`, `zoomToUserAccuracy`, `maxZoom`, `positionOptions` |
| `theme` | Общие параметры размещения |
Геолокация использует разрешение браузера. `positionOptions` содержит `enableHighAccuracy`, `timeout`, `maximumAge`.
`controls.add({id, element, position?, visible?, margin?, onAdd?, onRemove?})` добавляет свой инструмент. Также доступны `get(id)`, `getAll()`, `remove(id)`, `clear()`.
`ControlHandle`: `id`, `element`, `getState()`, `setPosition()`, `setVisible()`, `setMargin()`, `focus()`, `remove()`, `destroy()`. [Полные типы инструментов](https://cdn.mmapps.ru/sdk/1.0.0-rc.9/types/clients/maps/control-types.d.ts).
## Поиск и выбор адреса
`createSearchController({client, map?, filters?, language?, limit?, fitResults?})` создаёт контроллер. `client` — `geo.geocoder` или результат `createGeocoder()`.
В выпуске SDK `1.0.0-rc.11` предусмотрен параметр `mapContext`: при наличии `map` он включён по умолчанию и передаёт снимок центра и области карты. `mapContext: false` оставляет намеренно глобальный поиск. Движение карты само по себе запросов не делает; текстовый Enter использует тот же поиск подсказок, не добавляя геокодинг. Для этой возможности требуется подключить именно этот выпуск после его публикации.
| Метод | Назначение |
| --- | --- |
| `getState()`, `getResponse()` | Состояние интерфейса и последний ответ |
| `search(q, options?)`, `geocode(q, options?)` | Текстовый запрос |
| `reverse(point, radiusM?, options?)` | Обратный поиск |
| `confirm(ref)` | Явный локальный выбор; возвращает `Promise` |
| `setSelectionOpen(boolean)` | Открыть или закрыть карточку |
| `useContext(ref)` | Выбрать контекст поиска |
| `setFilters(filters)`, `clear()` | Фильтры и очистка состояния |
| `on(type, callback)`, `destroy()` | Подписки и освобождение ресурсов |
`filters` поддерживает `country_codes` и ссылки `region`, `locality`, `street`. События контроллера: `change`, `confirm`, `response`, `error`. Фазы `SearchState`: `idle`, `loading`, `candidates`, `not_found`, `needs_context`, `outside_coverage`, `selected`, `error`, `destroyed`.
`SearchSelection` содержит `candidate`, `ref`, `point`, `alternative`, `queryPoint`, `userConfirmed: true`. Выбор не вызывает новый HTTP-запрос. `point` может быть `null`; не создавайте из него маршрут.
`map.controls.addSearch(options)` размещает поиск на карте. Передайте либо `controller`, либо `client` с настройками `filters`, `language`, `limit`, `fitResults`. Общие настройки: размещение, `debounceMs`, `placeholder`, `width`, `onSelect`, `onRoute`, `onShare`. Результат `SearchMapControl` дополнительно имеет `controller`, `dockElement`, `openSelection()`, `closeSelection()`.
Для своей вёрстки используйте `attachSearchControl({controller, element, dockElement?, debounceMs?, placeholder?, onSelect?, onRoute?, onShare?})`. Результат: `element`, `dockElement`, `focus()`, `setVisible()`, `openSelection()`, `closeSelection()`, `destroy()`. Самостоятельно созданный контроллер освобождайте отдельно.
`onRoute(selection)` и `onShare(selection)` выполняют действия Вашего приложения; само открытие карточки не рассчитывает маршрут. [Пример подключения](https://mmapps.ru/docs/maps), [типы поиска](https://cdn.mmapps.ru/sdk/1.0.0-rc.9/types/clients/maps/search.d.ts).
## Свой обработчик запросов геокодера
`createGeocoder({baseUrl, fetch?, timeoutMs?, signal?, credentials?, paths?, requestHeaders?}) → Geocoder` нужен для подключения к своему backend. Методы: `search`, `geocode`, `reverse`, `resolve`.
`paths` задаёт пути этих четырёх операций, `requestHeaders(operation, options?)` — заголовки. `credentials` принимает `'omit'` или `'same-origin'`. Для прямого подключения к API Ммаппс используйте `createClient` с браузерным ключом. В `resolve` первый аргумент — сам `ref`, а не `{ref}`; дополнительные параметры — `language`, `signal`, `operationKey`.
## Расчёт и отображение маршрута
`geo.routes.calculate({start, end, via?, profile?}, options?)` принимает `GeoPoint`, профиль — `'car'`. `geo.routes.geocode()` принимает в каждой позиции `{point}`, `{ref}` или `{q}`. Текст требует подтверждения. [Запросы и результаты](https://mmapps.ru/docs/routes).
`routeView(response)` возвращает `{track, input, snapped, distanceM, durationS, routingVersion}`. `track` может быть `null`. `geocodedRouteView(response)` возвращает `{route, waypoints}`, где каждый waypoint содержит `index`, `point`, `candidates`. `routeCandidates(value)` извлекает кандидатов из результата геокодинга. Эти функции работают с готовым ответом и не отправляют запросов.
```javascript
import { createRouteOverlay } from 'https://cdn.mmapps.ru/sdk/1.0.0-rc.9/index.js';
const overlay = createRouteOverlay(map);
overlay.set(routeResponse); // Ответ calculate() или geocode().
overlay.fit();
// overlay.clear();
// overlay.destroy();
```
`createRouteOverlay(map)` создаёт слой. `set(response | null)`, `clear()`, `fit()`, `destroy()` управляют им. Результат `no_route` не превращается в прямую линию.
## Редактор маршрута
`attachRouteControl(map, service, {recalculate?}?) → RouteControl`. `service` должен предоставлять `calculate`, например `geo.routes`. `recalculate: 'on-change'` пересчитывает после завершённых изменений пользователем; `'manual'` требует явного расчёта.
| Метод | Назначение |
| --- | --- |
| `getState()` | `visible`, `picking`, `phase`, `stops`, `operationKey`, `errorCode` |
| `open(destination?)`, `close()` | Открыть или закрыть редактор; `destination` подтверждает конечную точку |
| `setStops(stops)` | Программно задать точки без расчёта |
| `chooseEndpoint('start' / 'end', stop)` | Выбрать конечную точку с учётом режима пересчёта |
| `acceptSelection(selection)` | Принять явно выбранную точку; возвращает boolean |
| `pick(index)` | Выбрать остановку на карте |
| `calculate(options?)` | Выполнить расчёт |
| `cancel()`, `fit()` | Отменить ожидание или показать маршрут |
| `on(type, callback)`, `destroy()` | Подписки и удаление |
`RouteStop` — `{point, label?}`; в списке остановок допускаются незаполненные `null`. События: `change`, `result`, `error`. Фазы: `editing`, `loading`, `route`, `no_route`, `error`. [Типы редактора](https://cdn.mmapps.ru/sdk/1.0.0-rc.9/types/clients/maps/route-control.d.ts).
## Матрица расстояний
`geo.routes.matrix({sources, targets, profile?}, options?) → Promise` рассчитывает пары координат. `geo.routes.matrixGeocode()` использует `{point}`, `{ref}` или `{q}` в списках. Профиль — `'car'`, параметры операции — `signal`, `operationKey`.
```javascript
const operationKey = crypto.randomUUID();
const matrix = await geo.routes.matrix({
sources: [{ latitude: 55.75, longitude: 37.61 }],
targets: [{ latitude: 55.76, longitude: 37.62 }],
profile: 'car',
}, { operationKey });
const cell = matrix.cells[0][0];
if (cell.status === 'ok') {
// cell.distance_m — метры; cell.duration_s — секунды.
}
```
`matrixView(value, expected?)` проверяет готовый ответ, необязательный `expected` — исходный `MatrixRequest`. `geocodedMatrixView(value, expected)` принимает обязательный `GeocodeMatrixRequest`. Обе функции возвращают проверенный ответ и не вызывают API.
До подтверждения текстовых адресов `matrix` равна `null`. Для `no_route` расстояние и время равны `null`. [Ограничения, тариф и подтверждение адресов](https://mmapps.ru/docs/matrix), [типы матрицы](https://cdn.mmapps.ru/sdk/1.0.0-rc.9/types/clients/maps/matrix.d.ts).
## Таймлайн
`createTimeline(map, {data, time?, markerRenderer?, observationInterpolation?}) → Timeline` отображает временные данные Вашего приложения. Время — Unix в **миллисекундах UTC**.
```javascript
import { createTimeline, attachTimelineControl } from 'https://cdn.mmapps.ru/sdk/1.0.0-rc.9/index.js';
const from = Date.UTC(2026, 0, 1, 9);
const to = from + 60 * 60 * 1000;
const timeline = createTimeline(map, {
data: {
version: 1,
range: { from, to },
referenceTime: from,
entities: [{
id: 'example',
label: 'Наш объект',
locations: [{
id: 'stop', from, to, basis: 'planned',
point: { latitude: 55.75, longitude: 37.61 },
}],
}],
},
time: from,
});
const control = attachTimelineControl(map, timeline);
// При удалении: control.destroy(); timeline.destroy();
```
`TimelineData`: `version: 1`, `range: {from, to}`, `referenceTime`, `entities`. Каждый объект имеет `id`, необязательные `label`, `locations`, `observations`. Местоположение: `{id, from, to?, point, basis, label?}`, где `basis` — `'reported'` или `'planned'`; интервал `[from, to)`. `point: null` означает неизвестную координату. Наблюдение: `{at, point, accuracyM?}`.
| Метод | Назначение |
| --- | --- |
| `getSnapshot()` | Время, период, воспроизведение и позиции объектов |
| `setData(data)` | Обновить данные |
| `seek(time)` | Перемотка |
| `play({speed?}?)`, `pause()` | Воспроизведение |
| `getRoutePlans(selection)` | Подготовить планы расчёта без сети |
| `prepareRoutes(client, options)` | Явно выполнить ограниченное число запросов маршрута |
| `clearRoutes()` | Удалить подготовленные маршруты |
| `on('change' / 'destroy', callback)`, `destroy()` | Подписки и удаление |
Без включённой интерполяции промежутки между наблюдениями остаются неизвестными. `observationInterpolation: {maxGapMs}` явно разрешает оценку между соседними измерениями; `maxGapMs` — 1–600 000 мс. В `TemporalPosition.source` различаются `location`, `observation`, `interpolated`, `unknown`.
Выбор для плана — `{entityId, basis, intermediate: 'observations' / 'none', from?, to?}`. Для `prepareRoutes` дополнительно обязательны `maxRequests`, `operationKeyPrefix`, необязателен `signal`. Сохраняйте префикс для повторов. Результат содержит `requested`, `reused`, `prepared`, `noRoute`. Подготовка вызывает платный расчёт маршрута; перемотка и воспроизведение — локальные действия.
`attachTimelineControl(mapOrElement, timeline, {order?, onPrepareRoutes?}?)` создаёт панель. Она предоставляет `element`, `setVisible(boolean)`, `destroy()`. `onPrepareRoutes({entityId, basis})` открывает Ваш интерфейс подготовки; само воспроизведение его не вызывает. [Все типы таймлайна](https://cdn.mmapps.ru/sdk/1.0.0-rc.9/types/clients/maps/timeline.d.ts).
## Панели и меню точки
`map.layout.mount({id, element, slot: 'top' / 'bottom', order?, visible?})` размещает панель в общем стеке. Меньший `order` — ближе к центру карты. `map.layout.reserve({id, element, edge, gap?})` резервирует место под уже размещённую панель приложения; `edge` — `top`, `bottom`, `left`, `right`.
Оба метода возвращают `LayoutHandle`: `element`, `setVisible()`, `remove()`, `destroy()`. `layout.getInsets()` читает отступы, `layout.onChange(callback)` подписывается на их изменение и возвращает отписку.
`attachContextMenu(map, {element, search?, routes?, onError?})` подключает меню к контейнеру карты. Передайте существующий `SearchController` и `RouteControl`. Меню открывается правой кнопкой мыши или с клавиатуры; действия — «Что здесь?», «Маршрут отсюда», «Маршрут сюда». Недоступные сервисы не выполняются.
`MapContextMenu` содержит `element`, `open(point, screen?)`, `close()`, `isOpen()`, `destroy()`. Открытие меню бесплатно. Только выбор действия запускает обратный поиск или выбор точки маршрута с учётом режима пересчёта.
## Готовый виджет
`createMapWidget(options) → Promise` объединяет инструменты карты. Используйте параметры карты, кроме `editable`, и добавьте нужные сервисы:
```javascript
import { createClient, createMapWidget } from 'https://cdn.mmapps.ru/sdk/1.0.0-rc.9/index.js';
const geo = createClient({ apiBaseUrl: 'https://mmapps.ru', apiKey: 'YOUR_BROWSER_KEY' });
const widget = await createMapWidget({
element: document.getElementById('map'),
mapId: 'ru',
services: { geocoder: geo.geocoder, routes: geo.routes },
search: { placeholder: 'Адрес или координаты' },
routing: { recalculate: 'manual' },
});
await widget.ready;
// При удалении: widget.destroy(); geo.destroy();
```
Параметры виджета: `services`, `search` (настройки поиска или `false`), `routing` (boolean или настройки редактора), `controls` (без `search`), `timeline`, `timelineControl`, `contextMenu`, `onSelect`, `onRoute`, `onShare`, `onError`. Меню точки по умолчанию включено, `contextMenu: false` отключает его.
Виджет предоставляет `map`, `ready`, `search`, `routes`, `timeline`, `contextMenu`; необязательные инструменты могут быть `null`. `setServices(services)` меняет сервисы, сохраняя публичную карту и внешние объекты. `destroy()` освобождает виджет. [Типы виджета](https://cdn.mmapps.ru/sdk/1.0.0-rc.9/types/clients/maps/widget.d.ts).
## Ошибки и завершение работы
`MapSDKError` содержит `code`, `httpStatus`, `retryable`, `requestId`, `operationId`, `retryAfter`, `stage`. `GeocoderError` дополнительно предоставляет `status`. Используйте эти поля для сообщения об ошибке и решения о повторе.
Локальные коды включают `INVALID_ARGUMENT`, `DESTROYED`, `ABORTED`, `DEADLINE_EXCEEDED`, `RESOURCE_UNAVAILABLE`, `WORKER_CONFLICT`, `RENDERER_ERROR`, `INVALID_RESPONSE`, `STALE_CLUSTER`, `BUSY`, `RANDOM_SOURCE_UNAVAILABLE`. Коды сервисного API описаны в [разделе ошибок](https://mmapps.ru/docs/billing#osibki).
Храните функции отписки и возвращаемые handles. При размонтировании удаляйте самостоятельно созданные панели и контроллеры, затем карту и клиент. `destroy()` карты и виджета освобождает принадлежащие им ресурсы; внешний API-клиент освобождается отдельно. Не создавайте новые экземпляры при каждом обновлении состояния компонента.
---
# Геокодер и поиск адресов
Подсказки адресов, координаты по адресу и поиск адреса по точке.
## Методы и права
| HTTP | Право ключа | Назначение |
| --- | --- | --- |
| `POST /v1/search` | `search` | Подсказки при вводе |
| `POST /v1/geocode` | `geocode` | Координаты по адресу |
| `POST /v1/reverse` | `reverse` | Адрес по координатам |
| `POST /v1/resolve` | `resolve` | Получить объект по выбранному `ref` |
| `GET /v1/status` | `status`, серверный ключ | Версия и покрытие данных |
API принимает JSON с Bearer-ключом. [Полный пример запроса](https://mmapps.ru/docs/getting-started). В SDK доступны методы `geo.geocoder`; их сигнатуры — в [справочнике SDK](https://mmapps.ru/docs/sdk).
## Покрытие и обновление данных
Адресный поиск основан на OpenStreetMap. Наличие карты населённого пункта не гарантирует полноту адресов в геокодере: здания, номера домов и дороги могут отсутствовать или устареть. Перед интеграцией проверьте адреса, с которыми работает Ваш сервис, на бесплатной квоте API.
Версию и установленное покрытие адресной базы возвращает `GET /v1/status` с серверным ключом и правом `status`. Карта и геокодер обновляются отдельно. Дата сборки карты в [публичном каталоге](https://cdn.mmapps.ru/catalog/channels/stable.json) не является датой обновления геокодера.
## Поиск по тексту
Тело запроса для `search` или `geocode`:
```json
{"q":"Примерная улица","limit":5}
```
`q` — непустая строка до 256 символов, `limit` — от 1 до 10. Для ограничения области поиска используйте `filters` по [схеме запроса](https://mmapps.ru/gateway.openapi.yaml). Не передавайте отсутствующие фильтры как массив `[]`.
`bias` задаёт мягкий приоритет: обязательный `focus` с `latitude`/`longitude` и необязательный `viewport` с `west`, `south`, `east`, `north`. Все координаты конечные, центр находится внутри области, `south <= north`. При `west > east` область пересекает 180-й меридиан. Явные фильтры и распознанный город из текста сохраняют приоритет.
`search_context` в ответе показывает источник области (`filters`, `query`, `viewport`, `none`), `locality` или `null` и признак расширения `expanded`. Это приоритет поиска, а не местоположение пользователя. При повторе операции сохраняйте исходный `bias`; сознательный поиск в другой области получает новый `Idempotency-Key`.
## Обратный геокодинг
Тело запроса для `reverse`. Замените координаты в примере на нужную точку:
```json
{"point":{"latitude":55.75,"longitude":37.61},"radius_m":1000,"limit":5}
```
В API координаты задаются полями `latitude` и `longitude`. Геокодер учитывает попадание в связанное с адресом здание и расстояние до ближайшего объекта. Исходная точка остаётся в `query_point`: не заменяйте её точкой найденного адреса.
| Поле | Как использовать |
| --- | --- |
| `relation` | Отношение точки к найденному объекту |
| `geometry_distance_m` | Расстояние до контура; `null`, если контура нет |
| `point_distance_m` | Расстояние до возвращённой адресной точки |
| `geometry.shape` | Контур здания, если он доступен |
Значения `relation`: `inside_linked_building`, `on_linked_building_boundary`, `nearest_linked_geometry`, `nearest_address_point`. Радиус применяется к объекту: адресная точка может оказаться дальше радиуса, если контур здания покрывает исходную точку.
## Выбор результата
- `label` — текст для отображения.
- `point` — координата или `null`. Наличие адреса не гарантирует наличие координат.
- `needs_confirmation` — необходимость подтверждения пользователем.
- `precision` — детализация распознанного адреса; это не обещание точности входа в здание.
- `alternative_points` — альтернативные точки; выбор остаётся за пользователем.
- `ref` — ссылка на объект, содержащая `id` и `dataset_version`. Передавайте её целиком в `resolve` как `{"ref": selectedRef}`.
Если `ref` устарел, повторите поиск и предложите новый выбор. Не заменяйте выбранный объект автоматически поиском по его подписи.
## Атрибуция OpenStreetMap
Поиск адресов, прямое и обратное геокодирование используют данные OpenStreetMap по ODbL 1.0. Если эти функции доступны пользователям Вашего приложения, при публичном использовании укажите OpenStreetMap и добавьте ссылку на сведения о лицензии.
Атрибуцию можно разместить **один раз в общем футере приложения**, без подписи под каждым полем адреса, подсказкой или отдельным результатом. Футер должен быть читаемым и видимым пользователям на страницах, где доступны поиск адресов или геокодирование. Например:
```html
```
Упоминание OpenStreetMap только на сайте Ммаппс не заменяет атрибуцию в Вашем приложении. При отображении карты соблюдайте [правила OSMF для интерактивных карт](https://osmfoundation.org/wiki/Licence/Attribution_Guidelines#Interactive_maps): указание источника подложки обычно размещают в углу карты или рядом с ней.
Это правило закреплено в [пункте 9.4 пользовательского соглашения](https://mmapps.ru/documents/terms#maps-licenses). Подробнее — в [разъяснениях OSMF по геокодированию](https://osmfoundation.org/wiki/Licence/Community_Guidelines/Geocoding_-_Guideline).
## Контур здания
`geometry` присутствует в кандидате поиска и в результате `resolve`: `null`, если связанного контура нет, иначе содержит `shape` и сведения о геометрии. `shape` — GeoJSON `Polygon` или `MultiPolygon`, в том числе с отверстиями. В GeoJSON порядок координат — **[longitude, latitude]**.
Контур можно отобразить без дополнительного запроса. Если используете `Accept: application/geo+json`, он находится в `properties.compact_geo.geometry.shape`; геометрия самого Feature описывает точку результата.
## Результаты и ошибки
| `outcome` | Действие приложения |
| --- | --- |
| `candidates` | Показать варианты для выбора |
| `not_found` | Предложить уточнить адрес |
| `needs_context` | Уточнить город, улицу или другой контекст |
| `outside_coverage` | Сообщить, что точка вне покрытия |
Пустой успешный ответ тоже учитывается как операция. `resolve` и `status` не имеют отдельной платы, но ограничены по частоте. [Повторы и обработка ошибок](https://mmapps.ru/docs/billing).
---
# Маршрут
Рассчитайте автомобильный маршрут с остановками в заданном порядке.
## Маршрут по координатам
`POST /v1/route` требует право `route`. Передайте начальную и конечную точки; промежуточные остановки задаются в `via`.
```json
{
"start": {"latitude":55.75,"longitude":37.61},
"end": {"latitude":55.76,"longitude":37.62},
"via": [],
"profile": "car"
}
```
Замените координаты в примере на свои начальную и конечную точки. Порядок остановок сохраняется: автоматическая оптимизация порядка посещения не выполняется. Расчётное время не учитывает пробки.
## Вызов из SDK
Создайте клиент с `apiBaseUrl: 'https://mmapps.ru'` и [браузерным ключом](https://mmapps.ru/docs/keys). Используйте координаты, выбранные пользователем:
```javascript
const operationKey = crypto.randomUUID();
const result = await geo.routes.calculate({
start: selectedStart,
end: selectedEnd,
via: orderedStops,
profile: 'car',
}, { operationKey });
```
Сохраняйте `operationKey` для повтора того же запроса. При изменении точек или порядка остановок создайте новый ключ операции.
## Маршрут по адресам
`POST /v1/route/geocode` и `geo.routes.geocode()` принимают для каждой точки один из вариантов: `{q: 'адрес'}`, `{ref: selectedRef}` или `{point: selectedPoint}`.
1. Отправьте адреса в `start`, `end` и при необходимости `via`.
2. Покажите кандидатов из `waypoints` и попросите пользователя подтвердить каждый адрес. Даже единственный кандидат из текстового поиска требует выбора.
3. Замените текст выбранными `ref` или `point` и отправьте запрос с новым ключом операции.
Пока требуется выбор, ответ содержит `outcome: 'needs_confirmation'`, а `route` равен `null`. Устаревший `ref` требует повторного поиска и выбора. Права: `route:geocode`, а также `geocode` для текста, `resolve` для ссылок и `route` для расчёта.
## Отображение результата
Исходные координаты сохраняются в `input_points`, точки привязки к дороге — отдельно в `snapped_points`. Не перемещайте выбранные пользователем маркеры автоматически.
При `outcome: 'no_route'` дорожный путь не найден, `geometry` равна `null`. Сообщите об этом пользователю. Прямую линию между точками нельзя выдавать за рассчитанный маршрут.
Готовую геометрию можно отобразить средствами [SDK](https://mmapps.ru/docs/sdk) без нового расчёта. Проверяйте ошибки запроса по [OpenAPI](https://mmapps.ru/gateway.openapi.yaml). `ROUTING_UNAVAILABLE` означает, что расчёт сейчас недоступен.
## Стоимость
Расчёт — одна операция `route`. В запросе по адресам каждый выполненный текстовый поиск учитывается отдельно как `geocode`. Подтверждённые координаты и ссылки не создают нового списания за геокодинг. До подтверждения всех адресов маршрут не рассчитывается и не оплачивается.
[Тарифы, повторные запросы и учёт расходов](https://mmapps.ru/docs/billing).
---
# Матрица расстояний
Получите автомобильные расстояния и расчётное время для всех пар двух списков точек.
## Что возвращает матрица
`POST /v1/matrix` принимает `sources` и `targets`. Ответ `cells[i][j]` описывает путь от источника `i` к назначению `j`. Например, 2 источника × 3 назначения — один запрос и 6 элементов.
Матрица помогает сравнить расстояния между складами, магазинами и адресами доставки. Направления A→B и B→A могут отличаться. Автомобильный профиль `car` не учитывает пробки и дату выезда; время является оценкой. Матрица не выбирает порядок посещения и не возвращает линию маршрута.
## Где пригодится
- **Доставка:** сравнить пути от складов до адресов заказов и выбрать, откуда отправлять каждый заказ.
- **Выездные бригады:** сравнить время в пути от свободных сотрудников до новой заявки.
- **Пункты выдачи:** предложить покупателю ближайший пункт по дороге.
Передайте два списка точек: откуда и куда нужно ехать. Для двух складов и трёх заказов Вы получите шесть расстояний и шесть оценок времени. По этой таблице Ваше приложение может выбрать подходящий вариант. [Пример на главной](https://mmapps.ru/#distance-matrix).
## Доступ
Разрешите `matrix` в [настройках ключа](https://mmapps.ru/app/projects). Для матрицы по текстовым адресам нужно также `geocode`, для выбранных ссылок — `resolve`.
Матрица должна быть доступна в текущем тарифе аккаунта. Проверить это можно серверным ключом с правом `status`: `GET /platform/v1/capabilities`, поле `matrix.enabled`. В `matrix.limits` возвращаются ограничения конкретного подключения.
## Запрос по координатам
```bash
curl --request POST 'https://mmapps.ru/v1/matrix' \
--header "Authorization: Bearer ${MMAPPS_SERVER_KEY}" \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: matrix-example-operation-001' \
--data '{"sources":[{"latitude":55.75,"longitude":37.61}],"targets":[{"latitude":55.76,"longitude":37.62}],"profile":"car"}'
```
В SDK RC9:
```javascript
const operationKey = crypto.randomUUID();
const result = await geo.routes.matrix({
sources: selectedSources,
targets: selectedTargets,
profile: 'car',
}, { operationKey });
```
Каждый список содержит объекты `{latitude, longitude}`. Текущий запрос может содержать до 25 пар; каждая пара должна находиться в пределах 200 км по прямой. Дорожное расстояние в результате может быть больше. Точные ограничения — в [OpenAPI](https://mmapps.ru/gateway.openapi.yaml).
## Чтение результата
| `cells[i][j].status` | Расстояние и время |
| --- | --- |
| `ok` | `distance_m` в метрах, `duration_s` в секундах |
| `no_route` | Оба значения `null`; путь между этой парой не найден |
Не превращайте `null` в ноль: это создало бы ложный результат «рядом». При ошибке сервиса завершённая матрица не возвращается, элементы матрицы не оплачиваются.
## Матрица по адресам
`POST /v1/matrix/geocode` и `geo.routes.matrixGeocode()` принимают те же списки, но каждая позиция — `{q: 'адрес'}`, `{ref: selectedRef}` или `{point: selectedPoint}`.
Текстовый поиск возвращает кандидатов в `waypoints.sources` и `waypoints.targets`; `matrix` остаётся `null`. Покажите варианты пользователю, замените каждый текст подтверждённой ссылкой или координатой и отправьте новый запрос с новым ключом операции. Даже единственный кандидат требует подтверждения.
## Тариф и повторы
Матрица учитывается **по элементам**, а не по запросам. 2 000 элементов в месяц входят в бесплатную квоту; затем — **39 ₽ за 1 000 элементов** (0,039 ₽ за элемент). Применимый тариф и доступность проверяйте в кабинете.
Если осталось 3 бесплатных элемента, запрос на 25 элементов использует 3 бесплатных и 22 платных: 0,858 ₽. Повторяющиеся пары, диагональ и завершённые `no_route` также учитываются. Выполненный поиск текстовых адресов оплачивается отдельно; `resolve` бесплатен.
`Idempotency-Key` обязателен. Повторяйте тот же запрос с прежним ключом; другое тело с тем же ключом даст 409. Количество элементов, расход квоты и стоимость видны в [статистике и CSV](https://mmapps.ru/app/usage). [Общие правила расходов](https://mmapps.ru/docs/billing).
---
# Ключи и доступ
Выберите тип ключа, разрешите нужные операции и ограничьте доступ своего приложения.
## Типы ключей
| Тип | Где используется | Ограничение доступа |
| --- | --- | --- |
| На сервере | Backend сайта или приложения | Внешние IP и подсети, по желанию |
| На сайте | JavaScript SDK в браузере | Обязательный список разрешённых сайтов |
Для бесплатной карты ключ не нужен. Ключ сервисного API передаётся заголовком `Authorization: Bearer …`; SDK делает это при настройке `apiKey`.
## Создание и права
В [«Проекты и ключи»](https://mmapps.ru/app/projects) выберите проект, нажмите «Создать ключ» и укажите понятное имя. Полный ключ показывается один раз.
| Операция | Необходимые права |
| --- | --- |
| Подсказки | `search` |
| Геокодинг | `geocode` |
| Обратный геокодинг | `reverse` |
| Получение выбранного объекта | `resolve` |
| Маршрут по координатам | `route` |
| Маршрут по адресам | `route:geocode`; для выполняемых действий — `geocode`, `resolve`, `route` |
| Матрица по координатам | `matrix` |
| Матрица по адресам | `matrix`; для текста — `geocode`, для ссылки — `resolve` |
| Статус и возможности аккаунта | `status`, только серверный ключ |
Выбирайте права явно. Матрица доступна, когда она включена для аккаунта; наличие SDK само по себе не выдаёт это право. Фактическую доступность маршрута и матрицы можно проверить серверным ключом через `GET /platform/v1/capabilities`.
## Разрешённые сайты
В поле «Разрешённые сайты» укажите домены по одному на строку, например `shop.example.ru`. Без протокола добавится `https://`. Если нужен нестандартный порт, укажите его явно.
`example.ru` и `www.example.ru` добавляются отдельно. Путь страницы, параметры и `*` не допускаются. Для рабочего подключения требуется HTTPS. В демосреде для локальной разработки можно указать `http://localhost:8080`.
Браузерный ключ виден посетителю сайта. Разрешённый origin ограничивает браузерные запросы, но не заменяет права и бюджет. Серверный ключ храните только на сервере.
## Разрешённые IP
Для серверного ключа поддерживаются IPv4, IPv6 и подсети, например `203.0.113.0/24`. Указывайте адрес выхода сервера в интернет. Пустой список разрешает запросы с любого IP.
## Изменение и отзыв
Кнопка «Настроить» меняет имя, разрешённые сайты или IP, права и денежный бюджет. Тип ключа не меняется: создайте другой ключ, если нужно перенести подключение с сервера в браузер.
Технические ограничения общие для подключений с одного IP-адреса: до 20 запросов в секунду и до 60 новых сетевых соединений в минуту. Они заданы сервисом и не настраиваются для ключа. Используйте постоянные соединения; существующее соединение может обслуживать несколько HTTP-запросов. При HTTP-ограничении ответ содержит `429` и `Retry-After`.
«Отозвать» запрещает новые сервисные запросы. Для замены создайте ключ и обновите настройки приложения. Бесплатная карта продолжит работать.
Ключ Geo API не даёт доступа к платежам и управлению аккаунтом. Для кабинета нужны вход и подтверждённый email.
---
# Расходы и лимиты
Карта бесплатна; сервисные запросы используют квоту, затем оплачиваются с баланса.
## Тарифы
| Услуга | Бесплатно в месяц | За 1 000 сверх квоты |
| --- | --- | --- |
| Карта и SDK | Без ограничений по квоте API | Бесплатно |
| Подсказки адресов | 5 000 | 15 ₽ |
| Геокодинг | 3 000 | 49 ₽ |
| Обратный геокодинг | 3 000 | 49 ₽ |
| Маршрут | 1 000 | 99 ₽ |
| Элемент матрицы расстояний | 2 000 | 39 ₽ |
| resolve, status | В пределах частоты запросов ключа | Без отдельной оплаты |
Квота общая для проектов аккаунта. Матрица учитывается по парам точек, остальные платные операции — по выполненным запросам. Применимый тариф и доступные операции проверяйте в кабинете.
## Бесплатная проба API
[Создайте аккаунт](https://mmapps.ru/app/register), подтвердите email и выпустите ключ в разделе [«Проекты и ключи»](https://mmapps.ru/app/projects). Запросы в пределах бесплатной квоты доступны без пополнения баланса и привязки банковской карты. [Первый запрос по шагам](https://mmapps.ru/docs/getting-started).
Когда квота закончится, новые платные запросы потребуют предоплаты и доступного бюджета. Годовой лицензии и обязательной подписки нет.
## Хранение результатов
Результаты API можно сохранять у себя: в базе данных, кеше, заказах и отчётах. Используйте найденные адреса, координаты и рассчитанные маршруты повторно, в том числе на другой карте. Чтение сохранённого результата не вызывает новый запрос к Ммаппс и не расходует квоту.
Соблюдайте лицензии исходных данных, включая OpenStreetMap, и требования к персональным данным. Право сохранять результаты закреплено в [пункте 2.3 пользовательского соглашения](https://mmapps.ru/documents/terms#sdk).
Для поиска адресов и геокодирования атрибуцию OpenStreetMap можно разместить один раз в общем футере приложения, без подписи под каждым полем адреса. [Правила размещения и пример](https://mmapps.ru/docs/addresses#atribuciia-openstreetmap).
## Что считается операцией
- Поиск, геокодинг и обратный геокодинг учитываются при успешном выполнении, в том числе с пустым результатом или необходимостью уточнения.
- Расчёт маршрута — одна операция `route`.
- Матрица — `N × M` элементов, включая повторяющиеся пары, диагональ и завершённые `no_route`.
- В маршруте и матрице по адресам каждый выполненный поиск текста — отдельный `geocode`. До подтверждения адресов сам маршрут или матрица не оплачивается.
- `resolve` и `status` — без отдельной платы, в пределах частоты запросов ключа.
- Перемещение карты, маркеры, кластеры, отображение готовой геометрии и воспроизведение Ваших данных на таймлайне не расходуют квоту API.
Ошибка запроса или сервиса отличается от успешного ответа «ничего не найдено». У маршрута проверяйте `outcome`, у матрицы — статус каждой пары. При частичном выполнении поиска по адресам уже выполненные операции могут быть учтены; смотрите квитанцию и историю использования.
## Баланс и месячный бюджет
После исчерпания бесплатной квоты нужны доступный баланс и разрешённый бюджет. Бюджет `0` оставляет только бесплатную квоту. Бюджеты аккаунта и проекта действуют одновременно. Создание другого проекта не увеличивает квоту.
Месяц учитывается по Москве. [«Лимиты и уведомления»](https://mmapps.ru/app/limits) позволяют ограничить расходы, [«Использование»](https://mmapps.ru/app/usage) — посмотреть историю, применить фильтры и выгрузить CSV. В статистике матрицы запросы и элементы указаны отдельно.
## Повтор запроса
Для новой логической операции создавайте новый `Idempotency-Key`; в SDK это `operationKey`. При сетевом сбое повторяйте то же тело с прежним ключом. Не создавайте новый ключ при каждом автоматическом повторе.
Изменение тела при прежнем ключе возвращает 409. Сервис хранит ответ для повторной выдачи 15 минут; после этого повтор не запускает новую оплачиваемую операцию. Этот срок не ограничивает хранение полученного результата в Вашей базе по условиям соглашения. Если ответ для повтора истёк или операция ещё обрабатывается, сначала уточните её состояние.
Заголовок `X-Usage-Operation` содержит идентификатор операции. Квитанцию можно прочитать через `GET /platform/v1/usage-operations/{id}` тем же API-ключом с нужными правами. `X-Charged-Microrub` передаёт сумму целым числом в миллионных долях рубля; `X-Geo-Funding` — `free`, `paid` или `mixed`. Для обычной матрицы `X-Geo-Quantity`, `X-Geo-Free-Units` и `X-Geo-Paid-Units` показывают число элементов.
## Ошибки
| HTTP | Что делать |
| --- | --- |
| 401 | Проверить API-ключ и его отзыв |
| 402 | Проверить доступный баланс |
| 403 | Проверить права, разрешённый сайт или IP, бюджет и состояние аккаунта |
| 409 | Проверить повтор операции, её состояние или актуальность `ref` |
| 413 | Уменьшить размер запроса |
| 422 | Исправить поля по схеме запроса |
| 429 | Снизить частоту, учитывать `Retry-After`, если он есть |
| 503 | Показать временную недоступность; при разрешённом повторе сохранить ключ операции |
По умолчанию JSON ошибки содержит `error.code`, `error.message`, `error.request_id`, `error.retryable`. Для `Accept: application/problem+json` используется формат Problem Details из OpenAPI. Решение о повторе принимайте по коду и `retryable`, а не только по HTTP-статусу.
`ROUTING_UNAVAILABLE` означает недоступность расчёта. `INCOMPATIBLE_GEO_SCHEMA` — временную несовместимость сервиса; не пытайтесь обходить её запросом старого формата. При `IDEMPOTENCY_SCHEMA_CHANGED` прежняя операция уже учтена: новый ключ может создать новый расход.
## Пополнения и возвраты
Пополняйте баланс через кабинет, минимум — 300 ₽. Возврат со страницы оплаты ещё не подтверждает зачисление — дождитесь подтверждённого статуса платежа. После регистрации чека в разделе [«Платежи»](https://mmapps.ru/app/payments) появится кнопка «Скачать чек»: электронную копию можно открыть в браузере и распечатать или сохранить в PDF. Правила возвратов описаны в [пользовательском соглашении](https://mmapps.ru/documents/terms#payments).
Для обращения в поддержку достаточно кода ошибки и ID запроса. Не отправляйте ключи и географические данные пользователей.