Карта
Создание карты
Чтобы создать карту, нужно вызвать метод makeMapFactory() и передать настройки карты в виде структуры MapOptions.
В настройках важно указать корректное для устройства значение PPI. Его можно найти в спецификации устройства. По умолчанию выставлено значение DevicePpi.autodetected.
Кроме этого в настройках можно указать начальную позицию камеры, границы масштабирования и другие параметры.
// Настройки карты.
var mapOptions = MapOptions.default
// Значение PPI для устройства.
// По умолчанию, mapOptions.devicePPI == DevicePpi.autodetected.
mapOptions.devicePPI = devicePPI
// Создание фабрики объектов карты.
let mapFactory: DGis.IMapFactory = try sdk.makeMapFactory(options: mapOptions)
Получить слой карты можно через свойство mapView
. Контроллер карты доступен через свойство map
(см. класс Map).
// Слой карты.
let mapView: UIView & IMapView = mapFactory.mapView
// Контроллер карты.
let map = mapFactory.map
Пример UIViewController для отображения карты:
import Foundation
import DGis
import UIKit
class MapViewController: UIViewController {
private lazy var sdk = DGis.Container()
private var dataLoadingStateCancellable: ICancellable = NoopCancellable()
override func viewDidLoad() {
do {
var mapOptions = MapOptions.default
mapOptions.devicePPI = .autodetected
let mapFactory = try sdk.makeMapFactory(options: mapOptions)
let mapView: UIView & IMapView = mapFactory.mapView
mapView.frame = self.view.bounds
self.view.addSubview(mapView)
/// Стартовую позицию камеры можно рассчитывать только после загрузки карты.
self.dataLoadingStateCancellable = mapFactory.map.dataLoadingStateChannel.sink { loadingState in
if loadingState == .loaded {
print("Now map is loaded")
}
}
} catch let error as SDKError {
print(error.description)
} catch {
print("System error: \(error)")
}
}
}
Важное замечание: объект DGis.Container должен создаваться в одном экземпляре и храниться на уровне UIViewController.
Офлайн-режим
Чтобы настроить карту для работы в офлайн-режиме:
-
Выполните подготовительные шаги, чтобы карта могла работать с предзагруженными данными.
-
Добавьте источник данных для карты. Для этого в функции createDgisSource() установите параметру
workingMode
одно из следующих значений:OFFLINE
— чтобы всегда использовать только предварительно загруженные данные.HYBRID_ONLINE_FIRST
— чтобы преимущественно использовать онлайн-данные с серверов 2ГИС. Предзагруженные данные используются, только когда они совпадают с онлайн-данными или когда данные с серверов получить невозможно.HYBRID_OFFLINE_FIRST
— чтобы преимущественно использовать предзагруженные данные. Онлайн-данные с серверов 2ГИС используются, только когда отсутствуют предзагруженные данные.
let sources = [DgisSource.createDgisSource(context: sdkContext, workingMode: .offline)]
-
При создании карты укажите созданный источник в MapOptions:
var mapOptions = MapOptions.default mapOptions.sources = sources let mapFactory = try sdk.makeMapFactory(options: mapOptions) let mapView = mapFactory.mapView
Кадровая частота
Чтобы получить FPS выше 60 при отображении карты на поддерживаемых устройствах, добавьте ключ CADisableMinimumFrameDurationOnPhone
со значением true
в файл info.plist
вашего приложения.
Добавление объектов
Для добавления динамических объектов на карту (маркеров, линий, кругов, многоугольников) нужно создать менеджер объектов (MapObjectManager), указав инстанс карты.
// Сохраняем объект в свойство, так как при удалении менеджера исчезают все связанные с ним объекты на карте.
self.objectManager = MapObjectManager(map: map)
Для добавления объектов используются методы addObject() и addObjects(). Для каждого динамического объекта можно указать поле userData
, которое будет хранить произвольные данные, связанные с объектом. Настройки объектов можно менять после их создания.
Для удаления объектов используются методы removeObject() и removeObjects(). Чтобы удалить все объекты, можно использовать метод removeAll().
MapObjectManager — это контейнер объектов. Пока объекты нужны на карте,
MapObjectManager
необходимо сохранять на уровне класса.
Маркер
Чтобы добавить маркер на карту, нужно создать объект Marker, указав нужные настройки в MarkerOptions, и передать его в вызов addObject()
менеджера объектов.
Иконку для маркера можно создать с помощью метода make()
фабрики изображений (IImageFactory), используя UIImage, PNG-данные или SVG-разметку.
// Иконка на основе UIImage.
let uiImage = UIImage(systemName: "umbrella.fill")!.withTintColor(.systemRed)
let icon = sdk.imageFactory.make(image: uiImage)
// Иконка на основе SVG-данных.
let icon = sdk.imageFactory.make(svgData: imageData, size: imageSize)
// Иконка на основе PNG-данных (быстрее, чем из UIImage).
let icon = sdk.imageFactory.make(pngData: imageData, size: imageSize)
// Настройки маркера.
let options = MarkerOptions(
position: GeoPointWithElevation(
latitude: 55.752425,
longitude: 37.613983
),
icon: icon
)
// Создание и добавление маркера.
let marker = try Marker(options: options)
objectManager.addObject(object: marker)
Чтобы изменить настройки уже созданного маркера, установите новые значения для параметров объекта Marker
: cм. полный список доступных параметров в описании Marker.
// Изменение позиции маркера
marker.position = GeoPointWithElevation(latitude: 59.93428, longitude: 30.33510)
// Изменение иконки
let uiImage = UIImage(systemName: "bubble.right.fill")!.withTintColor(.systemGreen)
let newIcon = sdk.imageFactory.make(image: uiImage)
marker.icon = newIcon
// Изменение точки привязки иконки
marker.anchor = Anchor(x: 0.5, y: 0.5)
// Изменение прозрачности иконки
marker.iconOpacity = Opacity(value: 1.0)
// Изменение подписи маркера
marker.text = "Новый текст"
// Изменения стиля подписи
marker.textStyle = TextStyle(
fontSize: LogicalPixel(value: 8),
color: Color(argb: 4294967295),
strokeWidth: LogicalPixel(value: 0.3499999940395355),
strokeColor: Color(argb: 4294967295),
textPlacement: TextPlacement.bottomCenter,
textOffset: LogicalPixel(value: 0),
fontName: nil
)
// Изменение флага перемещаемости маркера
marker.isDraggable = true
// Изменение целевой ширины маркера
marker.iconWidth = LogicalPixel(value: 2.0)
// Изменение угла поворота маркера на карте относительно направления на север
marker.iconMapDirection = MapDirection(value: 10)
// Изменение флага анимации для появления маркера
marker.animatedAppearance = true
Линия
Чтобы нарисовать на карте линию, нужно создать объект Polyline, указав нужные настройки в PolylineOptions, и передать его в вызов addObject()
менеджера объектов. Кроме массива координат для точек линии, в настройках можно указать ширину линии, цвет, пунктир, обводку и другие параметры.
// Координаты вершин ломаной линии.
let points = [
GeoPoint(latitude: 55.7513, longitude: value: 37.6236),
GeoPoint(latitude: 55.7405, longitude: value: 37.6235),
GeoPoint(latitude: 55.7439, longitude: value: 37.6506)
]
// Настройки линии.
let options = PolylineOptions(
points: points,
width: LogicalPixel(value: 2),
color: DGis.Color.black
)
// Создание и добавление линии.
let polyline = try Polyline(options: options)
objectManager.addObject(object: polyline)
Чтобы изменить настройки уже созданной линии, установите новые значения для параметров объекта Polyline
: cм. полный список доступных параметров в описании Polyline.
// Изменение координат вершин линии
polyline.points = [
GeoPoint(latitude: 55.7513, longitude: value: 37.6236),
GeoPoint(latitude: 55.7405, longitude: value: 37.6235),
GeoPoint(latitude: 55.7439, longitude: value: 37.6506)
]
// Изменение толщины линии
polyline.width = LogicalPixel(value: 3)
// Изменение цвета линии
polyline.color = Color(argb: 4294967295)
// Стирание части линии
polyline.erasedPart = 0.8
// Изменение параметров пунктирной линии
polyline.dashedPolylineOptions = DashedPolylineOptions(
dashLength: LogicalPixel(value: 5),
dashSpaceLength: LogicalPixel(value: 2)
)
// Изменение параметров градиентной линии
polyline.gradientPolylineOptions = GradientPolylineOptions(
borderWidth: LogicalPixel(value: 0),
secondBorderWidth: LogicalPixel(value: 0),
gradientLength: LogicalPixel(value: 1),
borderColor: Color(argb: 4294967295),
secondBorderColor: Color(argb: 4294967295),
colors: [Color],
colorIndices: Data
)
Многоугольник
Чтобы нарисовать на карте многоугольник, нужно создать объект Polygon, указав нужные настройки в PolygonOptions, и передать его в вызов addObject()
менеджера объектов.
Координаты для многоугольника указываются в виде двумерного массива. Первый вложенный массив должен содержать координаты основных вершин многоугольника. Остальные вложенные массивы не обязательны и могут быть заданы для того, чтобы создать вырез внутри многоугольника (один дополнительный массив - один вырез в виде многоугольника).
// Настройки многоугольника.
let options = PolygonOptions(
contours: [
// Вершины многоугольника.
[
GeoPoint(latitude: 55.72014932919687, longitude: 37.562599182128906),
GeoPoint(latitude: 55.72014932919687, longitude: 37.67555236816406),
GeoPoint(latitude: 55.78004852149085, longitude: 37.67555236816406),
GeoPoint(latitude: 55.78004852149085, longitude: 37.562599182128906),
GeoPoint(latitude: 55.72014932919687, longitude: 37.562599182128906)
],
// Координаты выреза внутри многоугольника.
[
GeoPoint(latitude: 55.754167897761, longitude: 37.62422561645508),
GeoPoint(latitude: 55.74450654680055, longitude: 37.61238098144531),
GeoPoint(latitude: 55.74460317215391, longitude: 37.63435363769531),
GeoPoint(latitude: 55.754167897761, longitude: 37.62422561645508)
]
],
color: DGis.Color.black,
strokeWidth: LogicalPixel(value: 2)
)
// Создание и добавление многоугольника.
let polygon = try Polygon(options: options)
objectManager.addObject(object: polygon)
Чтобы изменить настройки уже созданного многоугольника, установите новые значения для параметров объекта Polygon
: cм. полный список доступных параметров в описании Polygon.
// Изменение вершин многоугольника
polygon.contours = [
GeoPoint(latitude: 55.72014932919687, longitude: 37.562599182128906),
GeoPoint(latitude: 55.72014932919687, longitude: 37.67555236816406),
GeoPoint(latitude: 55.72014932919687, longitude: 37.562599182128906)
]
// Изменение цвета заливки многоугольника
polygon.color = Color(argb: 4294967295)
// Изменение толщины границы многоугольника
polygon.strokeWidth = LogicalPixel(value: 2)
// Изменение цвета границы многоугольника
polygon.strokeColor = Color(argb: 4294967295)
Окружность
Чтобы нарисовать на карте окружность, нужно создать объект Circle, указав нужные настройки в CircleOptions, и передать её в вызов addObject()
менеджера объектов.
// Настройки окружности
let options = CircleOptions(
position: GeoPoint(latitude: 55.72014932919687, longitude: 37.562599182128906),
radius: Meter(value: 10)
)
// Создание и добавление окружности
let circle = try Circle(options: options)
objectManager.addObject(object: circle)
Чтобы изменить настройки уже созданной окружности, установите новые значения для параметров объекта Circle
: cм. полный список доступных параметров в описании Circle.
// Изменение центра окружности
circle.position = GeoPoint(latitude: 55.74460317215391, longitude: 37.63435363769531)
// Изменение радиуса окружности
circle.radius = Meter(value: 1)
// Изменение цвета заливки окружности
circle.color = Color(argb: 4294967295)
// Изменение толщины границы окружности
circle.strokeWidth = LogicalPixel(value: 2)
// Изменение цвета границы окружности
circle.strokeColor = Color(argb: 4294967295)
Добавление нескольких объектов
Если на карту необходимо добавить коллекцию объектов, то добавление через метод addObject в цикле по всей коллекции приведет к потере производительности. Для добавления коллекции объектов нужно сначала подготовить всю коллекцию и добавить её через метод addObjects:
// подготавливаем коллекцию объектов
let options = [MarkerOptions(<params>), MarkerOptions(<params>)]
var markers: [SimpleMapObject] = []
options.forEach{ option in
markers.append(try Marker(options: option))
}
// добавляем коллекцию объектов на карту
mapObjectManager.addObjects(objects: markers)
Кластеризация
Для добавления маркеров на карту в режиме кластеризации нужно создать менеджер объектов (MapObjectManager) через MapObjectManager.withClustering(), указав инстанс карты, расстояние между кластерами в логических пикселях, максимальный zoom-уровень формирования кластеров и пользовательскую имплементацию протокола SimpleClusterRenderer. SimpleClusterRenderer используется для кастомизации кластеров в MapObjectManager.
final class SimpleClusterRendererImpl: SimpleClusterRenderer {
private let image: DGis.Image
private var idx = 0
init(
image: DGis.Image
) {
self.image = image
}
func renderCluster(cluster: SimpleClusterObject) -> SimpleClusterOptions {
let textStyle = TextStyle(
fontSize: LogicalPixel(15.0),
textPlacement: TextPlacement.rightTop
)
let objectCount = cluster.objectCount
let iconMapDirection = objectCount < 5 ? MapDirection(value: 45.0) : nil
idx += 1
return SimpleClusterOptions(
icon: self.image,
iconMapDirection: iconMapDirection,
text: String(objectCount),
textStyle: textStyle,
iconWidth: LogicalPixel(30.0),
userData: idx,
zIndex: ZIndex(value: 6),
animatedAppearance: false
)
}
}
self.objectManager = MapObjectManager.withClustering(
map: map,
logicalPixel: LogicalPixel(80.0),
maxZoom: Zoom(19.0),
clusterRenderer: SimpleClusterRendererImpl(image: self.icon)
)
Стилизация объектов
Вы можете настроить сложный стиль для динамического объекта с помощью Редактора стилей. Например, настроить максимальный и минимальный масштаб, при котором должен отображаться этот объект.
-
Создайте стилевой слой для объекта:
-
Откройте Редактор стилей.
-
Откройте нужный стиль или создайте новый.
-
В разделе Слои нажмите значок
.
-
Выберите тип слоя в зависимости от типа объекта. Для текущей задачи поддерживаются только типы Полигон, Линия и Точка. Подробнее о слоях см. в статье Типы слоёв для мобильного SDK.
-
На вкладке Данные прокрутите список вниз и выберите JSON — добавить вручную.
-
Добавьте атрибут
db_sublayer
с уникальным идентификатором слоя (например,my_object_layer
): вставьте следующий код в текстовое поле:["match", ["get", "db_sublayer"], ["my_object_layer"], true, false]
Этот идентификатор будет далее использован для ссылки на стилевой слой из кода.
-
Настройте другие параметры стиля в соответствующих вкладках.
-
-
Создайте динамический объект с помощью GeometryMapObjectBuilder и укажите идентификатор созданного слоя в методе
setObjectAttribute()
. Например, чтобы добавить объект в виде точки:let geometryObject = GeometryMapObjectBuilder() .setGeometry(geometry: PointGeometry(point: point)) // Геометрия в виде точки .setObjectAttribute(name: "db_sublayer", value: .string("my_object_layer")) .createObject() // Создание объекта
-
Чтобы объект отобразился на карте, добавьте его в источник данных:
-
Создайте источник:
let geometrySource = GeometryMapObjectSourceBuilder(context: sdkContext).createSource()
-
Добавьте источник на карту:
map.addSource(source: geometrySource)
-
Добавьте созданный объект в источник:
geometrySource.addObject(item: geometryObject)
-
Выделение объектов
Настройка стилей
Для того, чтобы объекты на карте визуально реагировали на их выделение, в стилях необходимо настроить разный внешний вид слоя с помощью функции "Добавить зависимость от состояния" для всех необходимых свойств (иконка, шрифт, цвет и т. д.):

Настройки свойства выделенного объекта находятся в табе "Выделенное состояние":

Выделение объектов по нажатию на карту
Для начала получим информацию об объектах, попадающих в область нажатия, с помощью метода getRenderedObjects(), как в примере Получение объектов по экранным координатам
Выделение объектов происходит с помощью вызова метода setHighlighted(), который получает на вход список идентификаторов справочника изменяемых объектов DgisObjectId. Внутри метода getRenderedObjects()
мы можем получить все данные, необходимые для использования этого метода, как то источник данных объектов и их идентификаторы:
private func tap(point: ScreenPoint, tapRadius: ScreenDistance) {
let scale = UIScreen.main.nativeScale
let point = ScreenPoint(x: Float(location.x * scale), y: Float(location.y * scale))
self.getRenderedObjectsCancellable?.cancel()
let cancel = self.map.getRenderedObjects(centerPoint: point, radius: tapRadius).sink(
receiveValue: {
infos in
// Получим ближайший к месту нажатия объект внутри установленного радиуса
guard let info = infos.first(
where: {
$0.item.source is DgisSource
&& $0.item.item is DgisMapObject
}
) else { return }
// Сохраним источник данных объекта и его идентификатор
let source = info.item.source as! DgisSource
let id = (info.item.item as! DgisMapObject).id
// Выделяем объект и входы в него.
let future = searchManager.searchByDirectoryObjectId(objectId: id)
self.getDirectoryObjectCancellable = future.sinkOnMainThread(
receiveValue: {
[weak self] directoryObject in
guard let self = self else { return }
guard let directoryObject = directoryObject else { return }
guard let objectId = directoryObject.id else { return }
var selectedObjectIds = [objectId]
directoryObject.entrances.forEach { entrance in
selectedObjectIds.append(entrance.id)
}
source.setHighlighted(directoryObjectIds: selectedObjectIds, highlighted: true)
},
failure: { ... }
)
},
failure: { ... }
)
...
}
Управление камерой
Для работы с камерой используется объект Camera, доступный через свойство map.camera
.
Перелёт
Чтобы запустить анимацию перелёта камеры, нужно вызвать метод move() и указать параметры перелёта:
position
- конечная позиция камеры (координаты и уровень приближения). Дополнительно можно указать наклон и поворот камеры (см. CameraPosition).time
- продолжительность перелёта в секундах (TimeInterval).animationType
- тип анимации (CameraAnimationType).
Функция move()
возвращает объект Future, который можно использовать, чтобы обработать событие завершения перелета.
// Новая позиция камеры.
let newCameraPosition = CameraPosition(
point: GeoPoint(latitude: 55.752425, longitude: 37.613983),
zoom: Zoom(value: 16)
)
// Запуск перелёта.
let future = map.camera.move(
position: newCameraPosition,
time: 0.4,
animationType: .linear
)
// Получение события завершения перелета.
let cancellable = future.sink { _ in
print("Перелет камеры завершён.")
} failure: { error in
print("Возникла ошибка: \(error.localizedDescription)")
}
Получение состояния камеры
Текущее состояние камеры (находится ли камера в полёте) можно получить, используя свойство state
. См. CameraState для списка возможных состояний камеры.
let currentState = map.camera.state
Подписаться на изменения состояния камеры можно, используя stateChannel.sink
.
// Подписка.
let connection = map.camera.stateChannel.sink { state in
print("Состояние камеры изменилось на \(state)")
}
// Отписка.
connection.cancel()
Получение позиции камеры
Текущую позицию камеры можно получить, используя свойство position
(см. структуру CameraPosition).
let currentPosition = map.camera.position
print("Координаты: \(currentPosition.point)")
print("Приближение: \(currentPosition.zoom)")
print("Наклон: \(currentPosition.tilt)")
print("Поворот: \(currentPosition.bearing)")
Подписаться на изменения позиции камеры (и угла наклона/поворота) можно, используя positionChannel.sink
.
// Подписка.
let connection = map.camera.positionChannel.sink { position in
print("Изменилась позиция камеры или угол наклона/поворота.")
}
// Отписка.
connection.cancel()
Расчёт позиции камеры
Чтобы отобразить на экране объект или группу объектов, используйте метод расчёта позиции камеры calcPosition:
// Чтобы «увидеть» два маркера на карте:
// Создаём геометрию, которая охватывает оба объекта
let geometry = ComplexGeometry(geometries: [PointGeometry(point: point1), PointGeometry(point: point2)])
// Рассчитываем нужную позицию
let position = calcPosition(camera: map.camera, geometry: geometry)
// Используем рассчитанную позицию
map.camera.move(position: position)
Пример выше выдаст результат похожий на такой:

Маркеры обрезаются наполовину, так как у метода есть информация только о геометриях, а не объектах. В данном примере позиция маркера — это его центр. Метод рассчитал позицию так, чтобы вписать центры маркеров в активную область. Активная область отображается в виде красного прямоугольника по краям экрана. Чтобы отобразить маркеры целиком, вы можете задать активную область.
Например, задайте отступы слева и справа экрана:
let geometry = ComplexGeometry(geometries: [PointGeometry(point: point1), PointGeometry(point: point2)])
// Задаём отступы слева и справа, чтобы маркеры отображались полностью
map.camera.padding.left = 100
map.camera.padding.right = 100
let position = calcPosition(camera: map.camera, geometry: geometry)
map.camera.move(position: position)
Результат:

Помимо задания настроек в камере, вы можете задавать определённые параметры только для расчёта позиции. Например, указанные отступы можно задать только в методе расчёта позиции и получить такой же результат.
let geometry = ComplexGeometry(geometries: [PointGeometry(point: point1), PointGeometry(point: point2)])
// Задаём активную область только для расчёта позиции
let position = calcPosition(camera: map.camera, geometry: geometry, screenArea: Padding(left: 100, right: 100))
map.camera.move(position: position)
Результат:

На изображении видно, что активная область не изменилась, но маркеры вписаны полностью. Такой подход может приводить к неожиданному поведению, так как позиция камеры задаёт геокоординату, которая должна быть в точке позиции камеры (красный кружок в центре экрана). Настройки padding
, positionPoint
и size
влияют на положение данной точки.
Если при вычислении позиции в метод будут переданы параметры, которые сместят точку позиции камеры, то использование результата приведёт к неожиданному поведению. Например, если вы задаёте ассиметричную активную область, то изображение может сильно смещаться.
Пример установки одной и той же позиции для разных отступов:

В качестве простого решения вы можете задавать все необходимые настройки в камере и использовать для расчёта позиции только камеру и геометрию. В случае, если используются дополнительные параметры, которые не устанавливаются для камеры, может потребоваться коррекция результата, чтобы сдвинуть изображение в нужную сторону.
Настройка точки позиции и обзора камеры
Вы можете управлять отображением карты на экране, например, менять размер области видимости карты, при этом сохраняя позицию обзора. Для этого используйте экранные точки: точку позиции камеры BaseCamera.positionPoint и точку обзора камеры BaseCamera.viewPoint.
Точка позиции камеры (BaseCamera.positionPoint) — точка экрана, к которой привязана камера, с учётом отступов BaseCamera.padding. Точка задаётся относительно области видимости карты:
let cameraPositionPoint = CameraPositionPoint(x: 0.5, y: 0.5)
map.camera.setPositionPoint(positionPoint: Constants.cameraPositionPoint)
При изменении точки позиции камеры меняется область видимости карты и смещается точка наблюдения CameraPosition.Point — точка на местности в географических координатах, которая находится в точке позиции камеры. Угол наклона CameraPosition.Tilt и угол поворота камеры CameraPosition.Bearing не меняются:
Точка обзора камеры (BaseCamera.viewPoint) — точка экрана, в которую направлен «взгляд» камеры. Точка задаётся относительно области видимости карты:
let cameraViewPoint = CameraViewPoint(x: 0.5, y: 0.5)
map.camera.setViewPoint(viewPoint: Constants.cameraViewPoint)
При изменении точки обзора камеры меняется направление обзора относительно точки наблюдения. Точка наблюдения CameraPosition.Point не смещается, также не меняется угол наклона CameraPosition.Tilt и угол поворота камеры CameraPosition.Bearing:
visibleArea и visibleRect
У камеры есть два свойства, которые описывают геометрию видимой области, но делают это по-разному. visibleRect имеет тип GeoRect и всегда является прямоугольником. visibleArea представляет из себя произвольную геометрию. Проще всего увидеть на примере с разными углами наклона камеры относительно карты:
-
При наклоне 45°
visibleRect
иvisibleArea
не будут равны:visibleRect
, в данном случае, будет больше, т.к. он должен быть прямоугольником и должен содержать в себеvisibleArea
Синим цветом изображена
visibleArea
, красным –visibleRect
-
При наклоне 0°
visibleArea
иvisibleRect
будут совпадать, как видно из изменения цвета.

Определение попадания объекта в видимую область камеры
С помощью свойства visibleArea мы можем получить область карты, которая попадает в камеру, в виде Geometry. С помощью метода intersects() мы можем получить пересечение видимой области камеры с нужной нам геометрией:
//Допустим, у нас есть маркер и нужно узнать, попадает ли он в видимую область карты
// let marker: Marker
let markerGeometry: Geometry = PointGeometry(point: marker.position)
let intersects: Bool = map.camera.visibleArea.intersects(geometry: markerGeometry)
Пробки на карте
Чтобы отобразить слой пробок на карте, создайте источник данных TrafficSource и передайте его в метод карты addSource().
let trafficSource = TrafficSource(context: sdk.context)
map.addSource(source: trafficSource)
Получение объектов по экранным координатам
Информацию об объектах на карте можно получить, используя пиксельные координаты. Для этого нужно вызвать метод карты getRenderedObjects(), указав координаты в пикселях и радиус в экранных миллиметрах (не более 30). Метод вернет отложенный результат, содержащий информацию обо всех найденных объектах в указанном радиусе на видимой области карты (массив RenderedObjectInfo).
Пример функции, которая принимает координаты нажатия на экран и передает их в метод getRenderedObjects()
:
private func tap(location: CGPoint) {
let scale = UIScreen.main.nativeScale
let point = ScreenPoint(x: Float(location.x * scale), y: Float(location.y * scale))
let radius = ScreenDistance(30.0)
self.getRenderedObjectsCancellable?.cancel()
let cancel = map.getRenderedObjects(centerPoint: point, radius: radius).sink(
receiveValue: {
infos in
// Первый объект в массиве - самый близкий к координатам.
guard let info = infos.first else { return }
// Обработка результата в главной очереди.
DispatchQueue.main.async {
[weak self] in
self?.handle(selectedObject: info)
}
},
failure: { error in
print("Ошибка получения информации об объектах: \(error)")
}
)
// Сохраняем результат вызова, так как его удаление отменяет обработку.
self.getRenderedObjectsCancellable = cancel
}
Также можно установить коллбэк MapObjectTappedCallback для тапа или лонгтапа в IMapView с помощью методов addObjectTappedCallback и addObjectLongPressCallback. В этот коллбэк придет RenderedObjectInfo для объекта, который находится ближе всего к точке касания.
...
let mapObjectTappedOrLongPress = MapObjectTappedCallback(callback: { [weak self] objectInfo in
print("Произвольные данные объекта: \(objectInfo.item.item.userData)")
})
...
self.mapView.addObjectTappedCallback(callback: mapObjectTappedOrLongPress)
self.mapView.addObjectLongPressCallback(callback: mapObjectTappedOrLongPress)
Распознаватель жестов карты
Для кастомизации распознавателя жестов карты, необходимо задать реализацию протокола IMapGestureView в IMapView или реализацию IMapGestureViewFactory в MapOptions. Если ни одна из этих имплементаций задана не будет, то будет использована реализация по умолчанию. Пример такой кастомизации распознавателя можно посмотреть здесь.
Размещение View на карте
IMarkerViewFactory – фабрика для создания View, привязанных к геокоординатам, находится в объекте Container. Попробуем создать IMarkerView с помощью этой фабрики:
let sdk: Container
// Любой View, для примера возьмем UILabel
let view: UILabel
// Позиция на карте, к которой прикрепится view
let position: GeoPointWithElevation
// Точка внутри view, к которой будет привязана координата position
let anchor: Anchor = Anchor()
// Смещение в пикселях по осям
let offsetX: CGFloat = 0.0
let offsetY: CGFloat = 0.0
sdk.markerViewFactory.make(
view: view,
position : position,
anchor: anchor,
offsetX: offsetX,
offsetY: offsetY
)
Для отображения полученного View, необходимо добавить его методом add()
объекта IMarkerViewOverlay, полученного из фабрики IMapFactory.