Web SDK
@atasu/maps-gl: карта, язык и тема, поиск, маршруты до подъезда, правки пользователей и ошибки в браузере.
Осы бетте
@atasu/maps-gl — SDK Atasu Maps для браузера на основе MapLibre GL JS 6.15.
Открытый код (Apache-2.0), ESM, типы TypeScript в комплекте. Версия 0.2.0 — см. список изменений.
npm install @atasu/maps-glНужен веб-ключ, привязанный к адресам вашего сайта (быстрый старт, шаг 1). Примеры ниже,
которые начинаются с map., продолжают быстрый старт: map — объект, который вернул AtasuMap.create().
Создание карты
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 loadedcreate()сначала открывает сессию карты и получает токен тайлов, затем загружает стиль, поэтому ни один тайл не запрашивается без токена. Токен обновляется до истечения срока, когда «уснувшая» вкладка снова становится видимой и когда браузер возвращается в сеть.- Можно передать любые другие
MapOptionsMapLibre, кромеstyle,transformRequestиattributionControl(ими управляет SDK). - Несколько карт на одной странице — нормально, у каждой своя сессия. Удаляя карту, вызовите
map.destroy().
Язык и тема без перезагрузки
map.setLanguage('en'); // labels switch at once, the style is not reloaded
map.setTheme('night');
console.log(map.language, map.theme);Если у объекта нет казахского названия, показывается русское, затем местное. auto следует светлой / тёмной
теме системы пользователя.
Поиск
Подсказки при вводе; нажатия клавиш одного поиска делят один токен сессии и учитываются один раз:
const { items } = await map.search.autocomplete('Абай', { limit: 5 });
for (const s of items) console.log(s.label, s.secondary ?? '', s.id);Полный поиск — свободный текст или по полям. Номера домов, микрорайонов и подъездов должны совпадать точно
(12 никогда не найдёт 21):
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);Что находится в точке — адрес и ближайший подъезд:
const { result, nearest_entrance } = await map.search.reverse([76.95692, 43.24313]);
console.log(result?.name, nearest_entrance?.ref);Карточка места с подъездами (номер, диапазон квартир, тип, доступ):
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 }. Маршрут до подъезда заканчивается у двери,
а не в середине здания:
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. Добавляйте свои слои под подписями, чтобы названия оставались
читаемыми:
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— карта уже удалена.
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) и ключ с продуктом
Правки пользователей. Каждая правка отправляется ровно один раз, даже при обрыве сети: она ждёт в очереди и
отправляется повторно с тем же ключом идемпотентности. Все значения берутся из того, что ввёл пользователь, —
никогда не копируйте данные карты в правку.
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:
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 сохраняет вашу настройку.