Карты на Android: единый API и картографические провайдеры¶
NodaLogic предоставляет единый набор инструментов для работы с картами в
Android-клиенте. Прикладной обработчик использует функции MapCenter,
MapPoint, MapPolyline, MapRoute и другие Map*-функции, не
обращаясь напрямую к классам картографического SDK.
Конкретный картографический движок выбирается элементом динамической разметки.
Например, YandexMap создаёт карту на основе Yandex MapKit. Команды
направляются нужному экземпляру карты по его id.
Единый API не означает одинаковых возможностей всех движков. Отображение переданной полилинии — одна задача, а поиск адреса или самостоятельный расчёт маршрута — другая: они могут зависеть от выбранного провайдера, лицензии и наличия сети. Доступность операций приведена в разделе Возможности по провайдерам.
Примечание
Описанный здесь встроенный картографический слой предназначен для
Android-клиента. Для веб-интерфейса карта может быть реализована отдельно
средствами HTML и JavaScript. Тип YandexMap не является компонентом
веб-разметки.
Архитектура¶
Работа с картами состоит из трёх уровней:
Решение NodaLogic хранит координаты, точки, зоны и полученные извне маршруты как обычные бизнес-данные.
Единый картографический API принимает команды
Map*, направляет их карте с заданнымidи передаёт события обратно в обработчики решения.Провайдер отображает карту и выполняет поддерживаемые им операции: управление камерой, рисование объектов, поиск или построение маршрута.
Поэтому, если координаты маршрута уже рассчитаны сервером, их можно передать
карте через MapPolyline без вызова провайдерской маршрутизации. Поиск,
обратное геокодирование и расчёт маршрута являются отдельными возможностями,
которые нельзя автоматически ожидать от любого провайдера.
Возможности по провайдерам¶
Таблица относится к функциям, описанным в этой статье. При добавлении новых провайдеров в неё могут появиться дополнительные колонки.
Функция единого API |
YandexMap |
|---|---|
Отображение карты и управление камерой |
Да; Yandex MapKit. |
GPS и отображение положения устройства |
Да, при наличии разрешения Android и доступного местоположения. |
Точки, SVG-маркеры и подписи |
Да. |
Полилинии, полигоны, окружности |
Да. |
Готовый маршрут от сервера как |
Да; маршрутный сервис провайдера не требуется. |
Автоматический расчёт автомобильного маршрута, |
Да; асинхронно, с учётом условий работы MapKit. |
Поиск и обратное геокодирование |
Да; асинхронно, через MapKit. |
События нажатий, результатов поиска и маршрутизации |
Да. |
Офлайновое отображение предварительно загруженного региона |
Да, при наличии соответствующей платной лицензии MapKit. |
Управление офлайновыми регионами |
Через менеджер карт Android-клиента и механизм MapKit. |
Офлайновый поиск и автомобильная маршрутизация |
Поддерживаются MapKit для скачанных регионов при наличии платной лицензии; конкретный сценарий нужно проверять с используемым ключом и режимом маршрутизатора. |
Примечание
Таблица различает функции единого API NodaLogic и возможности SDK
провайдера. Например, сам факт поддержки офлайнового поиска в MapKit
не гарантирует, что любой запрос конкретного решения будет обработан
локально. Для MapReverseGeocode не следует обещать офлайновую работу
без отдельной проверки.
Общий Python API¶
Функции импортируются из nodesclient в Android-обработчике:
from nodesclient import (
MapCall, MapState, MapCamera, MapCenter,
MapPoint, MapPolyline, MapPolygon, MapCircle,
MapRemove, MapClear, MapRoute,
MapSearch, MapReverseGeocode,
MapUserLocation, MapCenterGPS, MapFit,
)
Первый аргумент большинства функций — id экземпляра карты в разметке.
Таким образом, обработчик может адресовать команды нужной карте, даже если
на экране находятся несколько картографических элементов.
Для редких действий и параметров существует универсальный низкоуровневый вызов:
MapCall("map", "action", {...})
Наличие универсального вызова не делает произвольное действие доступным
на любом провайдере. В обычном коде предпочтительны готовые Map*-функции.
Управление камерой и GPS¶
MapCenter принимает координаты из любого источника: поля узла, внешней
системы, результатов поиска или GPS.
MapCenter("map", [55.75, 37.62], zoom=14)
MapCenter(
"map",
{"latitude": 55.75, "longitude": 37.62},
)
Если zoom не передан, текущий масштаб сохраняется.
Для дополнительных параметров камеры:
MapCamera(
"map", 55.75, 37.62,
zoom=15, azimuth=0, tilt=0, animated=True,
)
Показ и центрирование по местоположению самого Android-устройства:
MapUserLocation("map", visible=True)
MapCenterGPS("map", zoom=16)
Если разрешение ещё не выдано, картографический компонент запускает штатный
Android-запрос разрешения. Не нужно дублировать этот запрос в прикладном
обработчике. Координаты, ранее полученные из внешней системы, следует
передавать через MapCenter, а не через MapCenterGPS.
Объекты на карте¶
Точка и её идентификатор:
MapPoint(
"map", "client_1", 55.75, 37.62,
text="Клиент", color="#3f7353",
)
Первый аргумент — id карты, второй — идентификатор объекта. Повторный
вызов с тем же идентификатором заменяет точку.
Можно использовать собственный SVG-маркер:
FLAG_SVG = '<svg ...>...</svg>'
MapPoint(
"map", "warehouse_1", 55.76, 37.64,
text="Склад 1", svg=FLAG_SVG,
svg_size=42, svg_color="#1E88E5", number="1",
text_placement="bottom", text_offset=5,
)
Дополнительные параметры включают size, draggable, z,
number_background, number_color, text_size и
text_offset_from_icon. Фактическое оформление зависит от реализации
маркеров у выбранного провайдера.
Удаление объекта:
MapRemove("map", "warehouse_1")
Готовый маршрут или трек¶
Если сервер или внешняя система уже вернули геометрию маршрута, передайте
массив координат в MapPolyline. Карта сразу отображает линию: ей не
нужно повторно рассчитывать путь.
external_route = [
[55.7510, 37.6100],
[55.7542, 37.6160],
[55.7580, 37.6210],
]
MapPolyline(
"map", "external_route", external_route,
color="#1565C0", width=7,
)
Две точки образуют обычный отрезок:
MapPolyline(
"map", "segment_1",
[[55.75, 37.61], [55.76, 37.64]],
color="#db3030", width=5,
)
Для пунктирной линии доступны дополнительные параметры dash_length и
gap_length. Передавайте точки в порядке следования по маршруту; простая
полилиния не умеет находить дороги между произвольными точками самостоятельно.
Полигоны и окружности¶
MapPolygon(
"map", "zone_1",
[[55.75, 37.62], [55.75, 37.64], [55.77, 37.64]],
stroke="#3f7353", fill="#403f7353", width=3,
)
MapCircle(
"map", "radius_1", 55.75, 37.62, 250,
stroke="#1565C0", fill="#401565C0", width=3,
)
События карты¶
При events=true последний результат записывается в:
self._data["map_event"]
self._data["<map_id>_event"]
Например, у карты delivery_map второе поле называется
delivery_map_event. Если на экране несколько карт и важно исключить
путаницу между ними, используйте поле с конкретным id.
Карта вызывает стандартное событие onInput; listener равен id
карты. Для элемента с id="map" задайте обработчик:
{
"event": "onInput",
"listener": "map",
"actions": [
{
"action": "run",
"source": "internal",
"server": "internal",
"method": "MapEvent",
"order": 1
}
]
}
Пример Python-обработчика:
def MapEvent(self, input_data=None):
event = self._data.get("map_event") or {}
kind = event.get("event")
data = event.get("data") or {}
if kind == "tap":
lat = data.get("latitude")
lon = data.get("longitude")
elif kind == "object_tap":
object_id = data.get("object_id")
lat = data.get("latitude")
lon = data.get("longitude")
elif kind == "search_ready":
results = data.get("results") or []
elif kind == "route_ready":
route_count = data.get("count")
elif kind in ("route_error", "search_error", "location_error"):
error = data.get("error")
return True, self._data
Возможные события: tap, long_tap, object_tap, route_ready,
route_error, search_ready, search_error,
location_permission_required, location_permission_granted,
location_permission_denied и location_error. Некоторые из них
возникают только у провайдера, реализующего соответствующую функцию.
Настройка в onShow¶
Карту удобно описать декларативно, а объекты добавить в onShow:
def InitMap(self, input_data=None):
MapCenter("map", [55.80, 37.59], zoom=10, animated=False)
MapUserLocation("map", visible=True)
MapPoint("map", "office", 55.75, 37.62, "Офис")
return True, self._data
onShow может сработать до окончательного создания дерева Android View.
Короткие команды к ещё не смонтированной карте ставятся в очередь и выполняются
после регистрации соответствующего элемента. При этом разметка процесса
должна содержать сам картографический элемент: последующий Show() с пустой
разметкой может заменить уже созданный экран.
Хранение собственных точек¶
Карта отвечает за отображение, а не за долговременное хранение данных решения.
Сохраняйте точки обычными словарями в _data:
self._data["my_locations"] = [
{
"id": "client_1",
"name": "Клиент",
"address": "Москва, ...",
"latitude": 55.75,
"longitude": 37.62,
}
]
Один массив можно показать в обычном Table и отрисовать через
MapPoint. Адрес является необязательным полем: название и координаты
можно сохранить без вызова геокодера. При выборе строки таблицы используйте
координаты соответствующего элемента в MapCenter.
Открытие точки во внешнем навигаторе¶
from android import share_location, open_location
share_location(55.75, 37.62, "Клиент")
open_location(55.75, 37.62, "Клиент")
Используется системный geo:-intent Android, без привязки к конкретному
приложению-навигации.
NodaScript¶
На Android доступны следующие команды общего картографического API:
map_call(map_id, action, params)
map_state(map_id)
map_camera(map_id, latitude, longitude, zoom)
map_center(map_id, latitude, longitude, optional_zoom)
map_point(map_id, object_id, latitude, longitude, text)
map_route(map_id, params)
map_search(map_id, query)
map_center_gps(map_id, zoom)
map_clear(map_id)
share_location(latitude, longitude, label)
open_location(latitude, longitude, label)
Для расширенных параметров используйте map_call:
map_call(
'map',
'add_polyline',
{
'id':'route_1',
'points':[[55.75,37.61],[55.76,37.64]],
'color':'#1565C0',
'width':7
}
);
Очистка, подбор масштаба и состояние¶
MapClear("map") # Удалить обычные объекты
MapClear("map", routes=True) # Также удалить построенные маршруты
MapFit(
"map", [[55.75, 37.62], [55.77, 37.64]],
max_zoom=14,
)
state = MapState("map")
Состояние содержит координаты и масштаб камеры, провайдера, число объектов и диагностические поля. Некоторые поля относятся только к отдельному SDK (например, источник API-ключа или состояние маршрутной сессии).
Провайдеры¶
YandexMap (Yandex MapKit)¶
YandexMap — элемент динамической разметки, использующий Yandex MapKit.
Его методы отображения и взаимодействия вызываются через общий Map*-API;
особенности поискового и маршрутного SDK описаны ниже.
Элемент YandexMap¶
Минимальная разметка:
{
"type": "YandexMap",
"id": "map",
"height": 420,
"center": [55.751225, 37.62954],
"zoom": 13,
"events": true
}
id обязателен. Для нескольких карт используйте разные id.
Параметры элемента:
center— начальные координаты[latitude, longitude];zoom— начальный масштаб;azimuthиtilt— направление и наклон камеры;show_user_location— показывать местоположение устройства;center_gps— при открытии центрировать карту по местоположению;heading— направление устройства, если поддерживается SDK;night— ночной режим;style— пользовательский стиль MapKit;route_colorиroute_width— цвет и толщина построенных маршрутов;events— отправлять события NodaLogic, по умолчаниюtrue;objects— начальные объекты типовpoint,polyline,polygon,circle;route_debug— диагностические Toast-сообщения маршрутизатора; в обычном решении оставляйтеfalse.
Если высота не указана, применяется стандартная высота Android-компонента. Обычные параметры динамической разметки NodaLogic также применимы.
Ключ MapKit¶
Пользователь может задать ключ в настройках Android-клиента:
Настройки -> Maps -> Yandex MapKit API key.
Если в сборке предусмотрен платформенный ключ по умолчанию, пустое поле использует его. Пользовательский ключ имеет приоритет. MapKit получает ключ при первой инициализации процесса; после изменения ключа полностью перезапустите приложение.
Не храните ключ в конфигурации решения или в Python-обработчиках. Для коммерческих возможностей, включая офлайновые регионы, необходим ключ с соответствующими правами и условиями лицензии.
Автоматическое построение маршрута¶
Текущая обвязка реализует автомобильные маршруты через MapKit:
MapRoute(
"map",
[
[55.9292, 37.5207],
[55.76594, 37.68549],
[55.70306, 37.53028],
],
routes=1, clear=True, fit=True,
max_zoom=12, router_type="online",
)
Для старта с текущего положения устройства:
MapRoute(
"map",
[[55.9292, 37.5207], [55.70306, 37.53028]],
from_gps=True, fit=True, router_type="online",
)
from_gps=True использует общий механизм GPS и при необходимости
запрашивает разрешение Android.
Вызов асинхронный: немедленный ответ обычно содержит pending=true, затем
приходит route_ready или route_error. Событие route_ready
передаёт число маршрутов и число точек геометрии, но не весь массив координат.
Если решение должно получить рассчитанную MapKit геометрию как массив,
понадобится отдельное расширение платформенного API.
Важно
MapRoute просит MapKit рассчитать маршрут. MapPolyline просто
отображает переданную геометрию и подходит для маршрутов, рассчитанных
на сервере или полученных из другой системы.
Поиск и обратное геокодирование¶
MapSearch("map", "Парк Горького", limit=10)
MapReverseGeocode("map", 55.75, 37.62)
Если область поиска не задана через MapCall, используется видимая область
карты. Результаты приходят через событие search_ready. Элементы результата
могут содержать name, description, address и points.
При обратном геокодировании поле query события равно
reverse_geocode. Ошибки передаются в search_error. Результаты можно
хранить как обычные данные решения; карта не сохраняет их автоматически.
Офлайновые карты Яндекса¶
MapKit может заранее скачать регионы и использовать их без соединения с
интернетом. В Android-клиенте для управления такими загрузками предусмотрена
вкладка Yandex экрана Офлайновые карты (если она включена в сборке).
Список регионов и сами данные предоставляет MapKit: это не импорт
произвольного файла карты с устройства.
Во вкладке можно просмотреть каталог регионов, начать загрузку, отслеживать
прогресс, приостанавливать и возобновлять загрузку, обновлять и удалять
скачанные данные. При загрузке используются возможности MapKit по управлению
сетевым подключением и хранилищем офлайнового кеша. Загруженные регионы
используются SDK автоматически; выбирать один «активный регион» для
YandexMap не требуется.
Важно
Офлайновые регионы MapKit доступны только по платной лицензии Яндекса. Наличие вкладки в Android-клиенте само по себе не даёт доступа к скачиванию. Подготовьте нужные регионы при наличии сети и проверьте ключ и условия лицензии до работы устройства без подключения.
Для скачанных регионов MapKit документирует отображение карты, офлайновый
поиск и построение автомобильных маршрутов без пробок. При этом не следует
считать, что router_type="online" автоматически переключится на
локальный расчёт: возможность и режим конкретного запроса нужно проверять
отдельно. Аналогично не гарантируйте локальную работу
MapReverseGeocode без проверки соответствующего сценария.
Официальная документация: Офлайновые карты MapKit для Android.
Практические рекомендации¶
Используйте общий
Map*-API для прикладных действий, но проверяйте доступность провайдерских функций по таблице возможностей.Храните бизнес-точки и готовые маршруты в данных решения. Карта занимается отображением и взаимодействием с пользователем.
Для готового маршрута используйте
MapPolyline; для самостоятельного расчёта маршрута SDK —MapRoute, если выбранный провайдер его поддерживает.MapRoute,MapSearchиMapReverseGeocodeвыполняются асинхронно; обрабатывайте результат через события.Не помещайте API-ключ MapKit в конфигурацию решения.
Если известны координаты, используйте
MapCenter;MapCenterGPSпредназначен для координат самого устройства.В обычном интерфейсе отключайте
route_debug, а большие SVG храните в константах обработчика.