Map
Creating a map
To create a map, call the makeMapFactory() method and specify the required map settings as a MapOptions structure.
It is important to specify the correct PPI settings for the device. You can find them in the technical specification of the device.
You can also specify the initial camera position, zoom limits, and other settings.
// Map settings object.
var mapOptions = MapOptions.default
// PPI settings.
mapOptions.devicePPI = devicePPI
// Create a map.
let mapFactory: PlatformMapSDK.IMapFactory = sdk.makeMapFactory(options: mapOptions)
To get the view of the map, use the mapView
property. To get the control of the map, use the map property.
// Map view.
let mapView: UIView & IMapView = mapFactory.mapView
// Map control.
let map = mapFactory.map
Adding objects
To add dynamic objects to the map (such as markers, lines, circles, and polygons), you must first create a MapObjectManager object, specifying the map instance. Deleting an object manager removes all associated objects from the map, so do not forget to save it to a property.
self.objectsManager = MapObjectManager(map: map)
After you have created an object manager, you can add objects to the map using the addObject() and addObjects() methods. For each dynamic object, you can specify a userData
field to store arbitrary data. Object settings can be changed after their creation.
To remove objects from the map, use removeObject() and removeObjects(). To remove all objects, call the removeAll() method.
Marker
To add a marker to the map, create a Marker object, specifying the required options (MarkerOptions), and pass it to the addObject()
method of the object manager. The most important settings are the coordinates of the marker and its icon.
You can create an icon for the marker by calling the make()
method and using UIImage, PNG data, or SVG markup as input.
// UIImage
let uiImage = UIImage(systemName: "umbrella.fill")!.withTintColor(.systemRed)
let icon = sdk.imageFactory.make(image: uiImage)
// SVG markup.
let icon = sdk.imageFactory.make(svgData: imageData, size: imageSize)
// PNG data.
let icon = sdk.imageFactory.make(pngData: imageData, size: imageSize)
// Marker settings.
let options = MarkerOptions(
position: GeoPointWithElevation(
latitude: 55.752425,
longitude: 37.613983
),
icon: icon
)
// Create and add the marker to the map.
let marker = Marker(options: options)
objectManager.addObject(object: marker)
To change the hotspot of the icon, use the anchor parameter.
Line
To draw a line on the map, create a Polyline object, specifying the required options, and pass it to the addObject()
method of the object manager.
In addition to the coordinates of the line points, you can set the line width, color, stroke type, and other options (see PolylineOptions).
// Coordinates of the vertices of the polyline.
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)
]
// Line settings.
let options = PolylineOptions(
points: points,
width: LogicalPixel(value: 2),
color: DGis.Color.init()
)
// Create and add the line to the map.
let polyline = Polyline(options: options)
objectManager.addObject(object: polyline)
Polygon
To draw a polygon on the map, create a Polygon object, specifying the required options, and pass it to the addObject()
method of the object manager.
Coordinates for the polygon are specified as a two-dimensional array. The first subarray must contain the coordinates of the vertices of the polygon itself. The other subarrays are optional and can be specified to create a cutout (a hole) inside the polygon (one subarray - one polygonal cutout).
Additionally, you can specify the polygon color and stroke options (see PolygonOptions).
// Polygon settings.
let options = PolygonOptions(
contours: [
// Vertices of the polygon.
[
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)
],
// Cutout inside the polygon.
[
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.init(),
strokeWidth: LogicalPixel(value: 2)
)
// Create and add the polygon to the map.
let polygon = Polygon(options: options)
objectManager.addObject(object: polygon)
Clustering
To add markers to the map in clustering mode, you must create a MapObjectManager object using MapObjectManager.withClustering(), specifying the map instance, distance between clusters in logical pixels, maximum value of zoom-level, when MapObjectManager in clustering mode, and user implementation of the protocol SimpleClusterRenderer. SimpleClusterRenderer is used to customize clusters in 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)
)
Controlling the camera
You can control the camera by accessing the map.camera
property. See the Camera class for a full list of available methods and properties.
Changing camera position
You can change the position of the camera by calling the move() method, which initiates a flight animation. This method has three parameters:
position
- new camera position (coordinates and zoom level). Additionally, you can specify the camera tilt and rotation (see CameraPosition).time
- flight duration in seconds (as TimeInterval).animationType
- type of animation to use (CameraAnimationType).
The call will return a Future object, which can be used to handle the animation finish event.
// New position for camera.
let newCameraPosition = CameraPosition(
point: GeoPoint(latitude: 55.752425, longitude: 37.613983),
zoom: Zoom(value: 16)
)
// Start the flight animation.
let future = map.camera.move(
position: newCameraPosition,
time: 0.4,
animationType: .linear
)
// Handle the animation finish event.
let cancellable = future.sink { _ in
print("Camera flight finished.")
} failure: { error in
print("An error occurred: \(error.localizedDescription)")
}
Getting camera state
The current state of the camera (i.e., whether the camera is currently in flight) can be obtained using the state
property. See CameraState for a list of possible camera states.
let currentState = map.camera.state
You can subscribe to changes of camera state using the stateChannel.sink
property.
// Subscribe to camera state changes.
let connection = map.camera.stateChannel.sink { state in
print("Camera state has changed to \(state)")
}
// Unsubscribe when it's no longer needed.
connection.cancel()
Getting camera position
The current position of the camera can be obtained using the position
property (see the CameraPosition structure).
let currentPosition = map.camera.position
print("Coordinates: \(currentPosition.point)")
print("Zoom level: \(currentPosition.zoom)")
print("Tilt: \(currentPosition.tilt)")
print("Rotation: \(currentPosition.bearing)")
You can subscribe to changes of camera position using the positionChannel.sink
property.
// Subscribe to camera position changes.
let connection = map.camera.positionChannel.sink { position in
print("Camera position has changed (coordinates, zoom level, tilt, or rotation).")
}
// Unsubscribe when it's no longer needed.
connection.cancel()
My location
You can add a special marker to the map that will be automatically updated to reflect the current location of the device. To do this, create a data source MyLocationMapObjectSource() function and pass it to the addSource() method of the map.
// Create a data source.
let source = MyLocationMapObjectSource(
context: sdk.context,
directionBehaviour: MyLocationDirectionBehaviour.followMagneticHeading
)
// Add the data source to the map.
map.addSource(source: source)
To remove the marker, call the removeSource() method. You can get the list of active data sources by using the map.sources
property.
map.removeSource(source)
Getting objects using screen coordinates
You can get information about map objects using pixel coordinates. For this, call the getRenderedObjects() method of the map and specify the pixel coordinates and the radius in screen millimeters. The method will return a deferred result (Future) containing information about all found objects within the specified radius on the visible area of the map (an array of RenderedObjectInfo).
An example of a function that takes tap coordinates and passes them to getRenderedObjects()
:
private func tap(location: CGPoint) {
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).sink(
receiveValue: {
infos in
// First array object is the closest to the coordinates.
guard let info = infos.first(
where: {
$0.item.source is DgisSource
&& $0.item.item is DgisMapObject
}
) else { return }
// Process the result in the main thread.
let source = info.item.source as! DgisSource
let id = (info.item.item as! DgisMapObject).id
// Select object and entrances.
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.buildingEntrances.forEach { entrance in
selectedObjectIds.append(entrance.id)
}
source.setHighlighted(directoryObjectIds: selectedObjectIds, highlighted: true)
},
failure: { ... }
)
},
failure: { error in
print("Error retrieving information: \(error)")
}
)
// Save the result to a property to prevent garbage collection.
self.getRenderedObjectsCancellable = cancel
}
You can also set the MapObjectTappedCallback callback for tap or long tap in the IMapView using the addObjectTappedCallback and addObjectLongPressCallback methods. This callback will receive RenderedObjectInfo for the object that is closest to the touch point.
...
let mapObjectTappedOrLongPress = MapObjectTappedCallback(callback: { [weak self] objectInfo in
print("Some object's data: \(objectInfo.item.item.userData)")
})
...
self.mapView.addObjectTappedCallback(callback: mapObjectTappedOrLongPress)
self.mapView.addObjectLongPressCallback(callback: mapObjectTappedOrLongPress)
Map gesture recognizer
To customize the map gesture recognizer, you need to set the IMapGestureView implementation in IMapView or IMapGestureViewFactory implementation in MapOptions. If no implementations are specified, the default implementations will be used. An example of such recognizer is available here.