Skip to content

Commit 30a1e23

Browse files
RD-1303: Add image source (#221)
1 parent 015a871 commit 30a1e23

File tree

5 files changed

+200
-0
lines changed

5 files changed

+200
-0
lines changed

Sources/MapTilerSDK/Commands/Style/AddSource.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ package struct AddSource: MTCommand {
2121
return handleMTRasterDEMSource(source)
2222
} else if let source = source as? MTGeoJSONSource {
2323
return handleGeoJSONSource(source)
24+
} else if let source = source as? MTImageSource {
25+
return handleImageSource(source)
2426
}
2527

2628
return emptyReturnValue
@@ -121,6 +123,17 @@ package struct AddSource: MTCommand {
121123

122124
return "\(MTBridge.mapObject).addSource('\(source.identifier)', \(jsSourceString));"
123125
}
126+
127+
private func handleImageSource(_ source: MTImageSource) -> JSString {
128+
guard let url = source.url else { return emptyReturnValue }
129+
return """
130+
\(MTBridge.mapObject).addSource('\(source.identifier)', {
131+
type: '\(source.type.rawValue)',
132+
url: '\(url.absoluteString)',
133+
coordinates: \(source.coordinates)
134+
});
135+
"""
136+
}
124137
}
125138

126139
fileprivate func replaceDataString(sourceString: String) -> String {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//
2+
// Copyright (c) 2025, MapTiler
3+
// All rights reserved.
4+
// SPDX-License-Identifier: BSD 3-Clause
5+
//
6+
// SetCoordinatesToImageSource.swift
7+
// MapTilerSDK
8+
//
9+
10+
import Foundation
11+
12+
package struct SetCoordinatesToImageSource: MTCommand {
13+
var source: MTSource
14+
var coordinates: [[Double]]
15+
16+
package func toJS() -> JSString {
17+
return "\(MTBridge.mapObject).getSource('\(source.identifier)').setCoordinates(\(coordinates));"
18+
}
19+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//
2+
// Copyright (c) 2025, MapTiler
3+
// All rights reserved.
4+
// SPDX-License-Identifier: BSD 3-Clause
5+
//
6+
// UpdateImageInSource.swift
7+
// MapTilerSDK
8+
//
9+
10+
import Foundation
11+
12+
package struct UpdateImageInSource: MTCommand {
13+
var source: MTSource
14+
var url: URL
15+
var coordinates: [[Double]]
16+
17+
package func toJS() -> JSString {
18+
let sourceRef = "\(MTBridge.mapObject).getSource('\(source.identifier)')"
19+
let payload = "{url: '\(url.absoluteString)', coordinates: \(coordinates)}"
20+
return "\(sourceRef).updateImage(\(payload));"
21+
}
22+
}

Sources/MapTilerSDK/Map/Extensions/MTStylable/MTMapView+MTStylable.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,27 @@ extension MTMapView: MTStylable {
646646
) {
647647
runCommand(SetTilesToSource(tiles: tiles, source: source), completion: completionHandler)
648648
}
649+
650+
// MARK: - Image source helpers
651+
package func setCoordinates(
652+
_ coordinates: [[Double]],
653+
to source: MTImageSource,
654+
completionHandler: ((Result<Void, MTError>) -> Void)? = nil
655+
) {
656+
runCommand(SetCoordinatesToImageSource(source: source, coordinates: coordinates), completion: completionHandler)
657+
}
658+
659+
package func updateImage(
660+
url: URL,
661+
coordinates: [[Double]],
662+
to source: MTImageSource,
663+
completionHandler: ((Result<Void, MTError>) -> Void)? = nil
664+
) {
665+
runCommand(
666+
UpdateImageInSource(source: source, url: url, coordinates: coordinates),
667+
completion: completionHandler
668+
)
669+
}
649670
}
650671

651672
// Concurrency: style property setters
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
//
2+
// Copyright (c) 2025, MapTiler
3+
// All rights reserved.
4+
// SPDX-License-Identifier: BSD 3-Clause
5+
//
6+
// MTImageSource.swift
7+
// MapTilerSDK
8+
//
9+
10+
import Foundation
11+
12+
/// An image source.
13+
///
14+
/// The `url` value contains the image location.
15+
/// The `coordinates` array contains [longitude, latitude] pairs for the image corners
16+
/// listed in clockwise order: top left, top right, bottom right, bottom left.
17+
public class MTImageSource: MTSource, @unchecked Sendable {
18+
/// Unique id of the source.
19+
public var identifier: String
20+
21+
/// URL that points to an image.
22+
public var url: URL?
23+
24+
/// Corners of image specified in longitude, latitude pairs.
25+
/// Clockwise order: top-left, top-right, bottom-right, bottom-left.
26+
public var coordinates: [[Double]]
27+
28+
/// Type of the source.
29+
public private(set) var type: MTSourceType = .image
30+
31+
/// Initializes the image source with required values.
32+
/// - Parameters:
33+
/// - identifier: Unique id of the source.
34+
/// - url: URL to the image resource.
35+
/// - coordinates: Corners of image in [lng, lat] clockwise order.
36+
public init(identifier: String, url: URL, coordinates: [[Double]]) {
37+
self.identifier = identifier
38+
self.url = url
39+
self.coordinates = coordinates
40+
}
41+
}
42+
43+
// MARK: - Operations
44+
extension MTImageSource {
45+
/// Updates the coordinates of the image source.
46+
/// - Parameters:
47+
/// - coordinates: New corners of the image specified in [lng, lat] pairs.
48+
/// - mapView: MTMapView which holds the source.
49+
/// - completionHandler: A handler block to execute when function finishes.
50+
@MainActor
51+
@available(iOS, deprecated: 16.0, message: "Prefer the async version for modern concurrency handling")
52+
public func setCoordinates(
53+
_ coordinates: [[Double]],
54+
in mapView: MTMapView,
55+
completionHandler: ((Result<Void, MTError>) -> Void)? = nil
56+
) {
57+
mapView.setCoordinates(coordinates, to: self, completionHandler: completionHandler)
58+
}
59+
60+
/// Updates the image URL and coordinates simultaneously.
61+
/// - Parameters:
62+
/// - url: New image URL.
63+
/// - coordinates: New corners of the image specified in [lng, lat] pairs.
64+
/// - mapView: MTMapView which holds the source.
65+
/// - completionHandler: A handler block to execute when function finishes.
66+
@MainActor
67+
@available(iOS, deprecated: 16.0, message: "Prefer the async version for modern concurrency handling")
68+
public func updateImage(
69+
url: URL,
70+
coordinates: [[Double]],
71+
in mapView: MTMapView,
72+
completionHandler: ((Result<Void, MTError>) -> Void)? = nil
73+
) {
74+
mapView.updateImage(url: url, coordinates: coordinates, to: self, completionHandler: completionHandler)
75+
}
76+
}
77+
78+
// MARK: - Concurrency
79+
extension MTImageSource {
80+
/// Updates the coordinates of the image source.
81+
/// - Parameters:
82+
/// - coordinates: New corners of the image specified in [lng, lat] pairs.
83+
/// - mapView: MTMapView which holds the source.
84+
@MainActor
85+
public func setCoordinates(_ coordinates: [[Double]], in mapView: MTMapView) async {
86+
await withCheckedContinuation { continuation in
87+
setCoordinates(coordinates, in: mapView) { _ in
88+
continuation.resume()
89+
}
90+
}
91+
}
92+
93+
/// Updates the image URL and coordinates simultaneously.
94+
/// - Parameters:
95+
/// - url: New image URL.
96+
/// - coordinates: New corners of the image specified in [lng, lat] pairs.
97+
/// - mapView: MTMapView which holds the source.
98+
@MainActor
99+
public func updateImage(url: URL, coordinates: [[Double]], in mapView: MTMapView) async {
100+
await withCheckedContinuation { continuation in
101+
updateImage(url: url, coordinates: coordinates, in: mapView) { _ in
102+
continuation.resume()
103+
}
104+
}
105+
}
106+
}
107+
108+
// MARK: - DSL
109+
extension MTImageSource {
110+
/// Adds source to map DSL style.
111+
///
112+
/// Prefer ``MTStyle/addSource(_:)`` on MTMapView instead.
113+
public func addToMap(_ mapView: MTMapView) {
114+
Task {
115+
guard let url = self.url else { return }
116+
let source = MTImageSource(
117+
identifier: self.identifier,
118+
url: url,
119+
coordinates: self.coordinates
120+
)
121+
122+
try await mapView.style?.addSource(source)
123+
}
124+
}
125+
}

0 commit comments

Comments
 (0)