Atasu Maps

Web SDK

@atasu/maps-gl: карта, язык и тема, поиск, маршруты до подъезда, правки пользователей и ошибки в браузере.

@atasu/maps-gl — SDK Atasu Maps для браузера на основе MapLibre GL JS 6.15. Открытый код (Apache-2.0), ESM, типы TypeScript в комплекте. Версия 0.2.0 — см. список изменений.

Shell
npm install @atasu/maps-gl

Нужен веб-ключ, привязанный к адресам вашего сайта (быстрый старт, шаг 1). Примеры ниже, которые начинаются с map., продолжают быстрый старт: map — объект, который вернул AtasuMap.create().

Создание карты

JavaScriptтексерілген
import { AtasuMap } from '@atasu/maps-gl';
import '@atasu/maps-gl/maplibre-gl.css';

const map = await AtasuMap.create({
  container: 'map',                    // element or its id
  apiKey: 'YOUR_WEB_KEY',
  language: 'ru',                      // 'kk' (default) | 'ru' | 'en'
  theme: 'auto',                       // 'day' | 'night' | 'auto' (follows the system)
  center: [76.95692, 43.24313],
  zoom: 15,
  customAttribution: '© My Company',   // added next to the required attribution, never instead of it
  onError: (err) => console.error('map session stopped', err), // e.g. the key was revoked
});
await map.ready(); // first style and tiles loaded
  • create() сначала открывает сессию карты и получает токен тайлов, затем загружает стиль, поэтому ни один тайл не запрашивается без токена. Токен обновляется до истечения срока, когда «уснувшая» вкладка снова становится видимой и когда браузер возвращается в сеть.
  • Можно передать любые другие MapOptions MapLibre, кроме style, transformRequest и attributionControl (ими управляет SDK).
  • Несколько карт на одной странице — нормально, у каждой своя сессия. Удаляя карту, вызовите map.destroy().

Язык и тема без перезагрузки

JavaScriptтексерілген
map.setLanguage('en'); // labels switch at once, the style is not reloaded
map.setTheme('night');
console.log(map.language, map.theme);

Если у объекта нет казахского названия, показывается русское, затем местное. auto следует светлой / тёмной теме системы пользователя.

Поиск

Подсказки при вводе; нажатия клавиш одного поиска делят один токен сессии и учитываются один раз:

JavaScriptтексерілген
const { items } = await map.search.autocomplete('Абай', { limit: 5 });
for (const s of items) console.log(s.label, s.secondary ?? '', s.id);

Полный поиск — свободный текст или по полям. Номера домов, микрорайонов и подъездов должны совпадать точно (12 никогда не найдёт 21):

JavaScriptтексерілген
const free = await map.search.search('Минина 24');
const structured = await map.search.search({ city: 'Алматы', street: 'Минина', house: '24' });
console.log(free.items[0]?.name, structured.items[0]?.precision);

Что находится в точке — адрес и ближайший подъезд:

JavaScriptтексерілген
const { result, nearest_entrance } = await map.search.reverse([76.95692, 43.24313]);
console.log(result?.name, nearest_entrance?.ref);

Карточка места с подъездами (номер, диапазон квартир, тип, доступ):

JavaScriptтексерілген
const { items } = await map.search.search('Минина 24');
const place = await map.search.place(items[0].id);
for (const e of place.entrances) console.log(e.ref, e.flats ?? '', e.entrance_type ?? '', e.lnglat);

Маршрут до подъезда

Точки маршрута — [lng, lat], { place_id } или { entrance_id }. Маршрут до подъезда заканчивается у двери, а не в середине здания:

JavaScriptтексерілген
const { items } = await map.search.search('Минина 24');
const place = await map.search.place(items[0].id);
const entrance = place.entrances[0];
const route = await map.routing.route({
  from: [76.95692, 43.24313],
  to: entrance ? { entrance_id: entrance.id } : { place_id: place.id },
  costing: 'pedestrian',             // 'auto' (default) | 'taxi' | 'pedestrian'
  alternatives: 1,
});
map.routes.draw(route);              // the style's route layers; alternatives are dimmed
console.log(route.waypoints?.at(-1)?.resolved_to); // 'entrance'

map.routes.clear() убирает маршрут. Маршрут, язык, тема и ваши слои сохраняются после map.setStyle().

Свои слои

map.maplibre — полноценная карта MapLibre. Добавляйте свои слои под подписями, чтобы названия оставались читаемыми:

JavaScriptтексерілген
const ml = map.maplibre;
ml.addSource('my-points', {
  type: 'geojson',
  data: { type: 'FeatureCollection', features: [{ type: 'Feature', properties: {}, geometry: { type: 'Point', coordinates: [76.95692, 43.24313] } }] },
});
const firstLabel = ml.getStyle().layers.find((l) => l.id.startsWith('label-'))?.id;
ml.addLayer({ id: 'my-points', type: 'circle', source: 'my-points', paint: { 'circle-radius': 8, 'circle-color': '#e11d48' } }, firstLabel);

Ошибки

Каждый вызов возвращает типизированный ответ или бросает исключение:

  • AtasuApiError — API ответил ошибкой: status и стабильный code (коды ошибок). Показывайте пользователю свой текст по code, а не английский detail.
  • AtasuNetworkError — ответа нет (нет сети, DNS, CORS).
  • AtasuDestroyedError — карта уже удалена.
JavaScriptтексерілген
import { AtasuApiError, isNotAvailable } from '@atasu/maps-gl';

try {
  await map.routing.route({ from: [76.95692, 43.24313], to: [0, 0] });
} catch (err) {
  if (err instanceof AtasuApiError) console.log(err.status, err.code); // 422 outside_coverage
  else if (isNotAvailable(err)) console.log('Atasu Maps is not reachable right now');
  else throw err;
}

Правки пользователей: места, подъезды, дорожные события

Пользователи могут добавлять места и подъезды, исправлять или закрывать места и сообщать о событиях на дороге. Для этих вызовов нужен вошедший пользователь (userToken: токен доступа Atasuai) и ключ с продуктом Правки пользователей. Каждая правка отправляется ровно один раз, даже при обрыве сети: она ждёт в очереди и отправляется повторно с тем же ключом идемпотентности. Все значения берутся из того, что ввёл пользователь, — никогда не копируйте данные карты в правку.

JavaScriptтексерілген
map.setUserToken(() => getAccessToken()); // your sign-in: returns the user's current access token
const sent = await map.reports.addPlace({
  category: 'cafe',
  names: { kk: 'Тест кафе', ru: 'Тест кафе' },
  lnglat: [76.95692, 43.24313],      // where the user put the pin
});
console.log(sent.status);            // 'sent' (or 'queued' while offline)

Правки проверяют модераторы, прежде чем они появятся на карте и в поиске.

Безопасность

SDK добавляет учётные данные только в одном месте: ключ уходит на api.atasumaps.com, токен тайлов — на tiles.atasumaps.com (точное совпадение хоста, https), токен пользователя — только с вызовами API. Любой другой хост — ваши собственные тайлы, похожие домены — не получает ничего. Серверные ключи (atk_srv_…) в браузере отклоняются.

Локальная разработка со своим стеком Atasu

Если вы запускаете стек Atasu локально (apps/api и его край тайлов), укажите его в apiBaseUrl. Принимаются только localhost, 127.0.0.1 и [::1], а токен тайлов уходит только на локальный хост тайлов, который назовёт этот API:

JavaScript
const map = await AtasuMap.create({ container: 'map', apiKey: 'YOUR_WEB_KEY', apiBaseUrl: 'http://127.0.0.1:8787' });

Сборщики и размер

Только ESM. Настраивать сборщик не нужно: SDK поставляет файл воркера MapLibre и сам указывает на него. Собственный код SDK — около 15 КБ в gzip; MapLibre GL JS добавляет около 280 КБ gzip плюс воркер (грузится параллельно). Если вы уже вызываете setWorkerUrl() из MapLibre, SDK сохраняет вашу настройку.