@exterio/react-native-yamap-lite 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +510 -0
  3. package/YamapLite.podspec +21 -0
  4. package/android/build.gradle +101 -0
  5. package/android/gradle.properties +14 -0
  6. package/android/settings.gradle +1 -0
  7. package/android/src/main/AndroidManifest.xml +2 -0
  8. package/android/src/main/java/com/yamaplite/ClusteredYamapLiteViewManager.kt +181 -0
  9. package/android/src/main/java/com/yamaplite/Components/ClusteredYamapLiteView.kt +169 -0
  10. package/android/src/main/java/com/yamaplite/Components/YamapLiteCircleView.kt +145 -0
  11. package/android/src/main/java/com/yamaplite/Components/YamapLiteMarkerView.kt +214 -0
  12. package/android/src/main/java/com/yamaplite/Components/YamapLitePolygon.kt +149 -0
  13. package/android/src/main/java/com/yamaplite/Components/YamapLitePolyline.kt +171 -0
  14. package/android/src/main/java/com/yamaplite/Components/YamapLiteView.kt +678 -0
  15. package/android/src/main/java/com/yamaplite/Events/CameraPositionChangeEvent.kt +38 -0
  16. package/android/src/main/java/com/yamaplite/Events/MapLoadEvent.kt +34 -0
  17. package/android/src/main/java/com/yamaplite/Events/PressEvent.kt +24 -0
  18. package/android/src/main/java/com/yamaplite/Utils/ImageCache.kt +45 -0
  19. package/android/src/main/java/com/yamaplite/Utils/PointParser.kt +39 -0
  20. package/android/src/main/java/com/yamaplite/Utils/ResolveImageHelper.kt +122 -0
  21. package/android/src/main/java/com/yamaplite/Utils/YamapUtils.kt +169 -0
  22. package/android/src/main/java/com/yamaplite/YamapLiteCircleViewManager.kt +67 -0
  23. package/android/src/main/java/com/yamaplite/YamapLiteMarkerViewManager.kt +82 -0
  24. package/android/src/main/java/com/yamaplite/YamapLitePackage.kt +41 -0
  25. package/android/src/main/java/com/yamaplite/YamapLitePolygonViewManager.kt +61 -0
  26. package/android/src/main/java/com/yamaplite/YamapLitePolylineViewManager.kt +75 -0
  27. package/android/src/main/java/com/yamaplite/YamapLiteViewManager.kt +176 -0
  28. package/ios/ClusteredYamapLiteView.h +15 -0
  29. package/ios/ClusteredYamapLiteView.mm +325 -0
  30. package/ios/ClusteredYamapLiteViewManager.mm +17 -0
  31. package/ios/Components/Circle.swift +108 -0
  32. package/ios/Components/ClusteredYamap.swift +190 -0
  33. package/ios/Components/Marker.swift +187 -0
  34. package/ios/Components/Polygon.swift +108 -0
  35. package/ios/Components/Polyline.swift +125 -0
  36. package/ios/Components/Yamap.swift +585 -0
  37. package/ios/Listeners/CameraListener.swift +54 -0
  38. package/ios/Listeners/ClusterListener.swift +38 -0
  39. package/ios/Listeners/MapInputListener.swift +35 -0
  40. package/ios/Listeners/MapLoadListener.swift +35 -0
  41. package/ios/Listeners/MapObjectTap.swift +12 -0
  42. package/ios/Listeners/UserLocationObjectListener.swift +29 -0
  43. package/ios/Models/YamapModels.swift +77 -0
  44. package/ios/Utils/ImageCache.swift +20 -0
  45. package/ios/Utils/PointsEqual.h +45 -0
  46. package/ios/Utils/ResolveImageHelper.swift +142 -0
  47. package/ios/YamapLiteCircleView.h +15 -0
  48. package/ios/YamapLiteCircleView.mm +102 -0
  49. package/ios/YamapLiteMarkerView.h +15 -0
  50. package/ios/YamapLiteMarkerView.mm +102 -0
  51. package/ios/YamapLitePolygonView.h +15 -0
  52. package/ios/YamapLitePolygonView.mm +120 -0
  53. package/ios/YamapLitePolylineView.h +15 -0
  54. package/ios/YamapLitePolylineView.mm +115 -0
  55. package/ios/YamapLiteView.h +15 -0
  56. package/ios/YamapLiteView.mm +361 -0
  57. package/ios/YamapLiteViewManager.mm +17 -0
  58. package/ios/YamapUtils.h +5 -0
  59. package/ios/YamapUtils.mm +205 -0
  60. package/lib/module/@types/index.js +8 -0
  61. package/lib/module/@types/index.js.map +1 -0
  62. package/lib/module/ClusteredYamapLiteViewNativeComponent.ts +98 -0
  63. package/lib/module/Components/Circle.js +20 -0
  64. package/lib/module/Components/Circle.js.map +1 -0
  65. package/lib/module/Components/ClusteredYamap.js +133 -0
  66. package/lib/module/Components/ClusteredYamap.js.map +1 -0
  67. package/lib/module/Components/Marker.js +40 -0
  68. package/lib/module/Components/Marker.js.map +1 -0
  69. package/lib/module/Components/Polygon.js +25 -0
  70. package/lib/module/Components/Polygon.js.map +1 -0
  71. package/lib/module/Components/Polyline.js +25 -0
  72. package/lib/module/Components/Polyline.js.map +1 -0
  73. package/lib/module/Components/Yamap.js +124 -0
  74. package/lib/module/Components/Yamap.js.map +1 -0
  75. package/lib/module/Components/index.js +9 -0
  76. package/lib/module/Components/index.js.map +1 -0
  77. package/lib/module/NativeYamapUtils.js +5 -0
  78. package/lib/module/NativeYamapUtils.js.map +1 -0
  79. package/lib/module/Utils/YamapUtils.js +14 -0
  80. package/lib/module/Utils/YamapUtils.js.map +1 -0
  81. package/lib/module/YamapCircleViewNativeComponent.ts +28 -0
  82. package/lib/module/YamapLiteViewNativeComponent.ts +93 -0
  83. package/lib/module/YamapMarkerViewNativeComponent.ts +32 -0
  84. package/lib/module/YamapPolygonViewNativeComponent.ts +28 -0
  85. package/lib/module/YamapPolylineViewNativeComponent.ts +31 -0
  86. package/lib/module/index.js +7 -0
  87. package/lib/module/index.js.map +1 -0
  88. package/lib/module/package.json +1 -0
  89. package/lib/typescript/package.json +1 -0
  90. package/lib/typescript/src/@types/index.d.ts +257 -0
  91. package/lib/typescript/src/@types/index.d.ts.map +1 -0
  92. package/lib/typescript/src/ClusteredYamapLiteViewNativeComponent.d.ts +82 -0
  93. package/lib/typescript/src/ClusteredYamapLiteViewNativeComponent.d.ts.map +1 -0
  94. package/lib/typescript/src/Components/Circle.d.ts +4 -0
  95. package/lib/typescript/src/Components/Circle.d.ts.map +1 -0
  96. package/lib/typescript/src/Components/ClusteredYamap.d.ts +3 -0
  97. package/lib/typescript/src/Components/ClusteredYamap.d.ts.map +1 -0
  98. package/lib/typescript/src/Components/Marker.d.ts +3 -0
  99. package/lib/typescript/src/Components/Marker.d.ts.map +1 -0
  100. package/lib/typescript/src/Components/Polygon.d.ts +4 -0
  101. package/lib/typescript/src/Components/Polygon.d.ts.map +1 -0
  102. package/lib/typescript/src/Components/Polyline.d.ts +4 -0
  103. package/lib/typescript/src/Components/Polyline.d.ts.map +1 -0
  104. package/lib/typescript/src/Components/Yamap.d.ts +3 -0
  105. package/lib/typescript/src/Components/Yamap.d.ts.map +1 -0
  106. package/lib/typescript/src/Components/index.d.ts +7 -0
  107. package/lib/typescript/src/Components/index.d.ts.map +1 -0
  108. package/lib/typescript/src/NativeYamapUtils.d.ts +30 -0
  109. package/lib/typescript/src/NativeYamapUtils.d.ts.map +1 -0
  110. package/lib/typescript/src/Utils/YamapUtils.d.ts +27 -0
  111. package/lib/typescript/src/Utils/YamapUtils.d.ts.map +1 -0
  112. package/lib/typescript/src/YamapCircleViewNativeComponent.d.ts +20 -0
  113. package/lib/typescript/src/YamapCircleViewNativeComponent.d.ts.map +1 -0
  114. package/lib/typescript/src/YamapLiteViewNativeComponent.d.ts +80 -0
  115. package/lib/typescript/src/YamapLiteViewNativeComponent.d.ts.map +1 -0
  116. package/lib/typescript/src/YamapMarkerViewNativeComponent.d.ts +25 -0
  117. package/lib/typescript/src/YamapMarkerViewNativeComponent.d.ts.map +1 -0
  118. package/lib/typescript/src/YamapPolygonViewNativeComponent.d.ts +20 -0
  119. package/lib/typescript/src/YamapPolygonViewNativeComponent.d.ts.map +1 -0
  120. package/lib/typescript/src/YamapPolylineViewNativeComponent.d.ts +23 -0
  121. package/lib/typescript/src/YamapPolylineViewNativeComponent.d.ts.map +1 -0
  122. package/lib/typescript/src/index.d.ts +5 -0
  123. package/lib/typescript/src/index.d.ts.map +1 -0
  124. package/package.json +189 -0
  125. package/src/@types/index.ts +314 -0
  126. package/src/ClusteredYamapLiteViewNativeComponent.ts +98 -0
  127. package/src/Components/Circle.tsx +22 -0
  128. package/src/Components/ClusteredYamap.tsx +194 -0
  129. package/src/Components/Marker.tsx +50 -0
  130. package/src/Components/Polygon.tsx +27 -0
  131. package/src/Components/Polyline.tsx +27 -0
  132. package/src/Components/Yamap.tsx +176 -0
  133. package/src/Components/index.ts +6 -0
  134. package/src/NativeYamapUtils.ts +47 -0
  135. package/src/Utils/YamapUtils.ts +63 -0
  136. package/src/YamapCircleViewNativeComponent.ts +28 -0
  137. package/src/YamapLiteViewNativeComponent.ts +93 -0
  138. package/src/YamapMarkerViewNativeComponent.ts +32 -0
  139. package/src/YamapPolygonViewNativeComponent.ts +28 -0
  140. package/src/YamapPolylineViewNativeComponent.ts +31 -0
  141. package/src/index.tsx +5 -0
@@ -0,0 +1,585 @@
1
+ import Foundation
2
+ import React
3
+ import UIKit
4
+ import YandexMapsMobile
5
+ import os.log
6
+
7
+ private let yamapLogger = OSLog(subsystem: "com.yamaplite", category: "YamapView")
8
+
9
+ @objc(YamapViewComponentDelegate)
10
+ public protocol YamapViewComponentDelegate {
11
+ func handleOnMapLoaded(result: [String: Any])
12
+ func handleOnCameraPositionChange(coords: [String: Any])
13
+ func handleOnCameraPositionChangeEnd(coords: [String: Any])
14
+ func handleOnMapPress(coords: [String: Any])
15
+ func handleOnMapLongPress(coords: [String: Any])
16
+ }
17
+
18
+ @objc(YamapView)
19
+ public class YamapView: UIView {
20
+ @objc public weak var delegate: YamapViewComponentDelegate? = nil {
21
+ didSet {
22
+ if delegate != nil && mapView != nil {
23
+ runOnMainThread {
24
+ self.setupListeners()
25
+ }
26
+ }
27
+ }
28
+ }
29
+
30
+ @objc private var mapObjects: [YamapLiteMarker] = []
31
+
32
+ // MARK: LISTENERS
33
+
34
+ ////////////////////////
35
+ ///////LISTENERS////////
36
+ ////////////////////////
37
+
38
+ private var cameraListener: CameraListener?
39
+ private var loadListener: MapLoadListener?
40
+ private var mapInputListener: MapInputListener?
41
+ private var userLocationListener: UserLocationObjectListener?
42
+ private var userLocationLayer: YMKUserLocationLayer!
43
+ private var userLocationImage: UIImage?
44
+
45
+ // MARK: CALLBACKS
46
+
47
+ ////////////////////////
48
+ ///////CALLBACKS////////
49
+ ////////////////////////
50
+
51
+ @objc public var onCameraPositionChangeCallback: RCTDirectEventBlock?
52
+ @objc public var onCameraPositionChangeEndCallback: RCTDirectEventBlock?
53
+ @objc public var onStop: RCTDirectEventBlock?
54
+ @objc public var onMapLoaded: RCTBubblingEventBlock?
55
+
56
+ @objc public var mapView: YMKMapView!
57
+
58
+ // MARK: PROPERTIES
59
+
60
+ ////////////////////////
61
+ ///////PROPERTIES///////
62
+ ////////////////////////
63
+
64
+ @objc public var mapType: String = "map" {
65
+ didSet {
66
+ guard let map = mapView?.mapWindow?.map else {
67
+ return
68
+ }
69
+ switch mapType {
70
+ case "satellite":
71
+ map.mapType = .satellite
72
+ case "hybrid":
73
+ map.mapType = .hybrid
74
+ default:
75
+ map.mapType = .map
76
+ }
77
+ }
78
+ }
79
+
80
+ @objc public var nightMode: Bool = false
81
+ @objc public var scrollGesturesEnabled: Bool = true
82
+ @objc public var userLocationIconScale: Float = 1.0
83
+ @objc public var showUserPosition = false
84
+ @objc public var userLocationAccuracyFillColor: UIColor = UIColor.green
85
+ @objc public var userLocationAccuracyStrokeColor: UIColor = UIColor.black
86
+ @objc public var userLocationAccuracyStrokeWidth: Float = 2.0
87
+ @objc public var zoomGesturesEnabled: Bool = true
88
+ @objc public var tiltGesturesEnabled: Bool = true
89
+ @objc public var rotateGesturesEnabled: Bool = true
90
+ @objc public var fastTapEnabled: Bool = true
91
+
92
+ @objc public func applyProperties() {
93
+ runOnMainThread {
94
+ self.updateMapProperties()
95
+ }
96
+ }
97
+
98
+ private func updateMapProperties() {
99
+ guard let map = mapView?.mapWindow?.map else { return }
100
+ switch mapType {
101
+ case "satellite": map.mapType = .satellite
102
+ case "hybrid": map.mapType = .hybrid
103
+ default: map.mapType = .map
104
+ }
105
+
106
+ map.isNightModeEnabled = nightMode
107
+ map.isScrollGesturesEnabled = scrollGesturesEnabled
108
+ map.isZoomGesturesEnabled = zoomGesturesEnabled
109
+ map.isTiltGesturesEnabled = tiltGesturesEnabled
110
+ map.isRotateGesturesEnabled = rotateGesturesEnabled
111
+ map.isFastTapEnabled = fastTapEnabled
112
+
113
+ self.updateUserIcon()
114
+ }
115
+
116
+ @objc public func setFollowUser(_ follow: Bool) {
117
+ if userLocationLayer == nil {
118
+ setupListeners()
119
+ }
120
+
121
+ if follow {
122
+ userLocationLayer.isAutoZoomEnabled = true
123
+ guard let mapView = mapView else { return }
124
+ let width = mapView.bounds.width
125
+ let height = mapView.bounds.height
126
+ userLocationLayer.setAnchorWithAnchorNormal(
127
+ CGPoint(x: 0.5 * width, y: 0.5 * height),
128
+ anchorCourse: CGPoint(x: 0.5 * width, y: 0.83 * height))
129
+ } else {
130
+ userLocationLayer.isAutoZoomEnabled = false
131
+ userLocationLayer.resetAnchor()
132
+ }
133
+ }
134
+ @objc public var maxFps: Float = 60 {
135
+ didSet {
136
+ if maxFps <= 0 || maxFps > 60 {
137
+ maxFps = 60
138
+ }
139
+ guard let map = mapView?.mapWindow else { return }
140
+ map.setMaxFpsWithFps(maxFps)
141
+ }
142
+ }
143
+
144
+ override init(frame: CGRect) {
145
+ super.init(frame: frame)
146
+ initImpl()
147
+ }
148
+
149
+ @available(*, unavailable)
150
+ required init?(coder _: NSCoder) {
151
+ fatalError("init(coder:) is not implemented.")
152
+ }
153
+
154
+ private func initImpl() {
155
+ runOnMainThread {
156
+ self.initMapView()
157
+ }
158
+ }
159
+
160
+ private func initMapView() {
161
+ mapView = YMKMapView(frame: bounds, vulkanPreferred: YamapView.isM1Simulator())
162
+ mapView.mapWindow.map.mapType = .map
163
+ applyProperties()
164
+ insertSubview(mapView, at: 0)
165
+ }
166
+
167
+ @objc private func setupListeners() {
168
+ guard let delegate = delegate, let map = mapView?.mapWindow?.map else { return }
169
+
170
+ if loadListener == nil {
171
+ loadListener = MapLoadListener(callback: nil, mapDelegate: delegate)
172
+ map.setMapLoadedListenerWith(loadListener!)
173
+ }
174
+
175
+ if cameraListener == nil {
176
+ let cameraDelegate = CameraListener(callback: nil, delegate: delegate)
177
+ cameraListener = cameraDelegate
178
+ map.addCameraListener(with: cameraDelegate)
179
+ }
180
+
181
+ if mapInputListener == nil {
182
+ mapInputListener = MapInputListener(delegate: delegate)
183
+ map.addInputListener(with: mapInputListener!)
184
+ }
185
+
186
+ if userLocationListener == nil {
187
+ userLocationListener = UserLocationObjectListener(callback: updateUserIcon)
188
+ }
189
+
190
+ if userLocationLayer == nil {
191
+ userLocationLayer = YMKMapKit.sharedInstance().createUserLocationLayer(
192
+ with: mapView.mapWindow)
193
+ userLocationLayer.setObjectListenerWith(userLocationListener)
194
+ }
195
+
196
+ if userLocationLayer != nil {
197
+ userLocationLayer.setVisibleWithOn(showUserPosition)
198
+ }
199
+ }
200
+
201
+ ////////////////////////
202
+ ///////METHODS//////////
203
+ ////////////////////////
204
+
205
+ @objc public func setLogoPosition(position: NSDictionary) {
206
+ guard let map = mapView?.mapWindow?.map else { return }
207
+ let logoPosition = LogoPosition(position: position)
208
+ map.logo.setAlignmentWith(
209
+ YMKLogoAlignment(
210
+ horizontalAlignment: logoPosition.horizontal,
211
+ verticalAlignment: logoPosition.vertical))
212
+ }
213
+
214
+ @objc public func setLogoPadding(vertical: Int, horizontal: Int) {
215
+ guard let map = mapView?.mapWindow?.map else { return }
216
+ map.logo.setPaddingWith(
217
+ YMKLogoPadding(horizontalPadding: UInt(horizontal), verticalPadding: UInt(vertical)))
218
+ }
219
+
220
+ @objc public func setMapStyle(style: [String: Any]) {
221
+ guard let map = mapView?.mapWindow?.map else { return }
222
+ guard let data = try? JSONSerialization.data(withJSONObject: style, options: []),
223
+ let styleString = String(data: data, encoding: .utf8)
224
+ else {
225
+ os_log("Failed to serialize style dictionary to JSON", log: yamapLogger, type: .error)
226
+ return
227
+ }
228
+ map.setMapStyleWithStyle(styleString)
229
+ }
230
+
231
+ @objc func setLocale(_ locale: String) {
232
+ YRTI18nManagerFactory.setLocaleWithLocale(locale)
233
+ }
234
+
235
+ @objc public func setShowUserPositionState(_ show: Bool) {
236
+ showUserPosition = show
237
+ if userLocationLayer != nil {
238
+ userLocationLayer.setVisibleWithOn(show)
239
+ }
240
+ if show {
241
+ updateUserIcon()
242
+ }
243
+ }
244
+
245
+ @objc public func setUserLocationIcon(path: String) {
246
+ let helper = ResolveImageHelper.shared
247
+ let baseSize: CGFloat = 50.0
248
+
249
+ let image = helper.resolveUIImage(uri: path as NSString) { [weak self] loadedImage in
250
+ guard let self = self, let loadedImage = loadedImage else {
251
+ os_log("Failed to load image from URI: %@", log: yamapLogger, type: .error, path)
252
+ return
253
+ }
254
+ let resizedImage = helper.resizeImage(loadedImage, toWidth: baseSize)
255
+ self.userLocationImage = resizedImage
256
+ self.updateUserIcon()
257
+ }
258
+
259
+ if let image = image {
260
+ let resizedImage = helper.resizeImage(image, toWidth: baseSize)
261
+ userLocationImage = resizedImage
262
+ updateUserIcon()
263
+ }
264
+ }
265
+
266
+ @objc public func updateUserIcon() {
267
+ DispatchQueue.main.async { [weak self] in
268
+ guard let self = self else { return }
269
+
270
+ let userIconStyle = YMKIconStyle()
271
+ userIconStyle.scale = self.userLocationIconScale as NSNumber
272
+
273
+ if let image = self.userLocationImage {
274
+ self.userLocationListener?.userLocationView?.pin.setIconWith(
275
+ image, style: userIconStyle)
276
+ self.userLocationListener?.userLocationView?.arrow.setIconWith(
277
+ image, style: userIconStyle)
278
+ }
279
+
280
+ if let circle = self.userLocationListener?.userLocationView?.accuracyCircle {
281
+ circle.fillColor = self.userLocationAccuracyFillColor
282
+ circle.strokeColor = self.userLocationAccuracyStrokeColor
283
+ circle.strokeWidth = self.userLocationAccuracyStrokeWidth
284
+ }
285
+ }
286
+ }
287
+
288
+ @objc func getLocale(_ resolve: RCTPromiseResolveBlock, reject _: RCTPromiseRejectBlock) {
289
+ resolve(YRTI18nManagerFactory.getLocale())
290
+ }
291
+
292
+ @objc func resetLocale(_ resolve: RCTPromiseResolveBlock, reject _: RCTPromiseRejectBlock) {
293
+ YRTI18nManagerFactory.setLocaleWithLocale(nil)
294
+ resolve(nil)
295
+ }
296
+
297
+ @objc func fitMakers(_ markers: [YMKPoint]) {
298
+ guard let map = mapView?.mapWindow?.map else { return }
299
+ if markers.isEmpty {
300
+ return
301
+ }
302
+ let latitudes = markers.map { $0.latitude }
303
+ let longitudes = markers.map { $0.longitude }
304
+ if latitudes.isEmpty || longitudes.isEmpty {
305
+ return
306
+ }
307
+ let minLatitude = latitudes.min() ?? 0
308
+ let minLongitude = longitudes.min() ?? 0
309
+ let maxLatitude = latitudes.max() ?? 0
310
+ let maxLongitude = longitudes.max() ?? 0
311
+ let southWest = YMKPoint(latitude: minLatitude, longitude: minLongitude)
312
+ let northEast = YMKPoint(latitude: maxLatitude, longitude: maxLongitude)
313
+ let box = YMKBoundingBox(southWest: southWest, northEast: northEast)
314
+
315
+ let camera = map.cameraPosition(with: YMKGeometry(boundingBox: box))
316
+ let paddingZoom: Float = 0.75
317
+ let adjustedZoom = max(camera.zoom - paddingZoom, 0)
318
+
319
+ let adjustedCamera = YMKCameraPosition(
320
+ target: camera.target,
321
+ zoom: adjustedZoom,
322
+ azimuth: camera.azimuth,
323
+ tilt: camera.tilt
324
+ )
325
+
326
+ let anim = YMKAnimation(type: .smooth, duration: 0.7)
327
+ map.move(with: adjustedCamera, animation: anim)
328
+ }
329
+
330
+ @objc public func getCameraPosition() -> [String: Any]? {
331
+ guard let map = mapView?.mapWindow?.map else { return nil }
332
+ let position = map.cameraPosition
333
+ let result: [String: Any] = [
334
+ "lat": position.target.latitude,
335
+ "lon": position.target.longitude,
336
+ "zoom": Double(position.zoom),
337
+ "azimuth": Double(position.azimuth),
338
+ "tilt": Double(position.tilt),
339
+ ]
340
+ return result
341
+ }
342
+
343
+ @objc public func getMapObjectCount() -> NSNumber {
344
+ guard let map = mapView?.mapWindow?.map else { return 0 }
345
+ let visitor = MapObjectCountVisitor()
346
+ map.mapObjects.traverse(with: visitor)
347
+ return NSNumber(value: visitor.count)
348
+ }
349
+
350
+ @objc public func getVisibleRegion() -> [String: Any]? {
351
+ guard let mapWindow = mapView?.mapWindow else { return nil }
352
+ let region = mapWindow.map.visibleRegion
353
+ return [
354
+ "bottomLeft": ["lat": region.bottomLeft.latitude, "lon": region.bottomLeft.longitude],
355
+ "bottomRight": ["lat": region.bottomRight.latitude, "lon": region.bottomRight.longitude],
356
+ "topLeft": ["lat": region.topLeft.latitude, "lon": region.topLeft.longitude],
357
+ "topRight": ["lat": region.topRight.latitude, "lon": region.topRight.longitude],
358
+ ]
359
+ }
360
+
361
+ @objc public func getScreenPoints(_ points: [[String: Double]]) -> [[String: Double]] {
362
+ guard let mapWindow = mapView?.mapWindow else { return [] }
363
+ return points.compactMap { p -> [String: Double]? in
364
+ guard let lat = p["lat"], let lon = p["lon"] else { return nil }
365
+ guard
366
+ let screenPoint = mapWindow.worldToScreen(
367
+ withWorldPoint: YMKPoint(latitude: lat, longitude: lon))
368
+ else { return nil }
369
+ return ["x": Double(screenPoint.x), "y": Double(screenPoint.y)]
370
+ }
371
+ }
372
+
373
+ @objc public func getWorldPoints(_ points: [[String: Double]]) -> [[String: Double]] {
374
+ guard let mapWindow = mapView?.mapWindow else { return [] }
375
+ return points.compactMap { p -> [String: Double]? in
376
+ guard let x = p["x"], let y = p["y"] else { return nil }
377
+ guard
378
+ let worldPoint = mapWindow.screenToWorld(
379
+ with: YMKScreenPoint(x: Float(x), y: Float(y)))
380
+ else { return nil }
381
+ return ["lat": worldPoint.latitude, "lon": worldPoint.longitude]
382
+ }
383
+ }
384
+
385
+ @objc public func move(
386
+ _ latitude: Double, _ longitude: Double, _ zoom: Float, _ azimuth: Float, _ tilt: Float
387
+ ) {
388
+ guard latitude.isFinite, longitude.isFinite, zoom.isFinite, azimuth.isFinite, tilt.isFinite
389
+ else { return }
390
+ guard let m = mapView?.mapWindow?.map else { return }
391
+ m.move(
392
+ with: YMKCameraPosition(
393
+ target: YMKPoint(latitude: latitude, longitude: longitude),
394
+ zoom: zoom,
395
+ azimuth: azimuth,
396
+ tilt: tilt
397
+ )
398
+ )
399
+ }
400
+
401
+ static func isM1Simulator() -> Bool {
402
+ #if targetEnvironment(simulator) && arch(arm64)
403
+ return true
404
+ #else
405
+ return false
406
+ #endif
407
+ }
408
+
409
+ @objc public func setCenter(
410
+ latitude: Double,
411
+ longitude: Double,
412
+ zoom: Float,
413
+ azimuth: Float,
414
+ tilt: Float,
415
+ duration: Float,
416
+ animation: String
417
+ ) {
418
+ guard latitude.isFinite, longitude.isFinite, zoom.isFinite, azimuth.isFinite, tilt.isFinite
419
+ else { return }
420
+ guard let map = mapView?.mapWindow?.map else { return }
421
+ let animType: YMKAnimationType
422
+ switch animation {
423
+ case "LINEAR":
424
+ animType = .linear
425
+ default:
426
+ animType = .smooth
427
+ }
428
+ let newPosition = YMKCameraPosition(
429
+ target: YMKPoint(latitude: latitude, longitude: longitude), zoom: Float(zoom),
430
+ azimuth: azimuth, tilt: tilt)
431
+ if duration > 0 {
432
+ let anim = YMKAnimation(type: animType, duration: Float(duration) / 1000.0)
433
+ map.move(with: newPosition, animation: anim)
434
+ } else {
435
+ map.move(with: newPosition)
436
+ }
437
+ }
438
+
439
+ @objc(setZoomWithZoom:duration:animation:)
440
+ public func setZoom(
441
+ zoom: Float,
442
+ duration: Float,
443
+ animation: String
444
+ ) {
445
+ guard zoom.isFinite else { return }
446
+ guard let map = mapView?.mapWindow?.map else { return }
447
+ let animType: YMKAnimationType
448
+ switch animation {
449
+ case "LINEAR":
450
+ animType = .linear
451
+ default:
452
+ animType = .smooth
453
+ }
454
+ let prevPosition = map.cameraPosition
455
+ let newPosition = YMKCameraPosition(
456
+ target: prevPosition.target, zoom: Float(zoom), azimuth: prevPosition.azimuth,
457
+ tilt: prevPosition.tilt)
458
+ if duration > 0 {
459
+ let anim = YMKAnimation(type: animType, duration: Float(duration) / 1000.0)
460
+ map.move(with: newPosition, animation: anim)
461
+ } else {
462
+ map.move(with: newPosition)
463
+ }
464
+ }
465
+
466
+ @objc public func fitAllMarkers() {
467
+ var markerPoints: [YMKPoint] = []
468
+
469
+ for marker in mapObjects {
470
+ if marker.visible, let point = marker.point, point.lat.isFinite, point.lon.isFinite {
471
+ markerPoints.append(YMKPoint(latitude: point.lat, longitude: point.lon))
472
+ }
473
+ }
474
+
475
+ if markerPoints.isEmpty {
476
+ return
477
+ }
478
+
479
+ fitMakers(markerPoints)
480
+ }
481
+
482
+ @objc func cleanMap() {
483
+ guard let map = mapView?.mapWindow?.map else { return }
484
+ map.mapObjects.clear()
485
+ mapObjects.removeAll()
486
+ }
487
+
488
+ static func requiresMainQueueSetup() -> Bool {
489
+ return true
490
+ }
491
+
492
+ @objc override public func insertReactSubview(_ subview: UIView!, at atIndex: Int) {
493
+ let safeIndex = min(atIndex, subviews.count)
494
+ insertSubview(subview, at: safeIndex)
495
+ if let markerContainer = subview as? UIView,
496
+ let markerView = markerContainer.value(forKey: "contentView") as? YamapLiteMarker
497
+ {
498
+ let lat = markerView.point?.lat ?? 0.0
499
+ let lon = markerView.point?.lon ?? 0.0
500
+ let viewPlacemark = mapView.mapWindow.map.mapObjects.addPlacemark()
501
+
502
+ if lat.isFinite, lon.isFinite {
503
+ viewPlacemark.geometry = YMKPoint(latitude: lat, longitude: lon)
504
+ }
505
+ markerView.setMapObject(object: viewPlacemark)
506
+ if !mapObjects.contains(where: { $0 === markerView }) {
507
+ mapObjects.append(markerView)
508
+ }
509
+ }
510
+
511
+ else if let circleContainer = subview as? UIView,
512
+ let circleView = circleContainer.value(forKey: "contentView") as? YamapLiteCircle
513
+ {
514
+ if circleView.radius > 0,
515
+ circleView.circleCenter.latitude.isFinite,
516
+ circleView.circleCenter.longitude.isFinite
517
+ {
518
+ let nativeMapObjects = mapView.mapWindow.map.mapObjects
519
+ let circleObject = nativeMapObjects.addCircle(with: circleView.circle)
520
+ circleView.setMapObject(object: circleObject)
521
+ }
522
+ } else if let polygonContainer = subview as? UIView,
523
+ let polygonView = polygonContainer.value(forKey: "contentView") as? YamapLitePolygon
524
+ {
525
+ let nativeMapObjects = mapView.mapWindow.map.mapObjects
526
+ let polygonObject = nativeMapObjects.addPolygon(with: polygonView.polygon)
527
+ polygonView.setMapObject(object: polygonObject)
528
+ } else if let polylineContainer = subview as? UIView,
529
+ let polylineView = polylineContainer.value(forKey: "contentView") as? YamapLitePolyline
530
+ {
531
+ let nativeMapObjects = mapView.mapWindow.map.mapObjects
532
+ let polylineObject = nativeMapObjects.addPolyline(with: polylineView.polyline)
533
+ polylineView.setMapObject(object: polylineObject)
534
+ }
535
+ }
536
+
537
+ @objc override public func removeReactSubview(_ subview: UIView!) {
538
+ subview.removeFromSuperview()
539
+ guard let container = subview as? UIView,
540
+ let contentView = container.value(forKey: "contentView") as? UIView
541
+ else { return }
542
+
543
+ let nativeMapObjects = mapView.mapWindow.map.mapObjects
544
+
545
+ if let markerView = contentView as? YamapLiteMarker {
546
+ if let obj = markerView.mapObject, obj.isValid {
547
+ nativeMapObjects.remove(with: obj)
548
+ }
549
+ mapObjects.removeAll { $0 === markerView }
550
+ } else if let circleView = contentView as? YamapLiteCircle {
551
+ if let obj = circleView.mapObject, obj.isValid {
552
+ nativeMapObjects.remove(with: obj)
553
+ }
554
+ } else if let polygonView = contentView as? YamapLitePolygon {
555
+ if let obj = polygonView.mapObject, obj.isValid {
556
+ nativeMapObjects.remove(with: obj)
557
+ }
558
+ } else if let polylineView = contentView as? YamapLitePolyline {
559
+ if let obj = polylineView.mapObject, obj.isValid {
560
+ nativeMapObjects.remove(with: obj)
561
+ }
562
+ }
563
+ }
564
+
565
+ func runOnMainThread(_ block: @escaping () -> Void) {
566
+ if Thread.isMainThread { block() } else { DispatchQueue.main.async { block() } }
567
+ }
568
+ }
569
+
570
+ private final class MapObjectCountVisitor: NSObject, YMKMapObjectVisitor {
571
+ var count = 0
572
+
573
+ func onPlacemarkVisited(withPlacemark placemark: YMKPlacemarkMapObject) { count += 1 }
574
+ func onPolylineVisited(withPolyline polyline: YMKPolylineMapObject) { count += 1 }
575
+ func onPolygonVisited(withPolygon: YMKPolygonMapObject) { count += 1 }
576
+ func onCircleVisited(withCircle: YMKCircleMapObject) { count += 1 }
577
+
578
+ func onCollectionVisitStart(with collection: YMKMapObjectCollection) -> Bool { true }
579
+ func onCollectionVisitEnd(with collection: YMKMapObjectCollection) {}
580
+ func onClusterizedCollectionVisitStart(with collection: YMKClusterizedPlacemarkCollection) -> Bool
581
+ {
582
+ true
583
+ }
584
+ func onClusterizedCollectionVisitEnd(with collection: YMKClusterizedPlacemarkCollection) {}
585
+ }
@@ -0,0 +1,54 @@
1
+ import Foundation
2
+ import React
3
+ import YandexMapsMobile
4
+
5
+ class CameraListener: NSObject, YMKMapCameraListener {
6
+ private let callback: RCTDirectEventBlock?
7
+ private weak var delegate: YamapViewComponentDelegate?
8
+
9
+ init(callback: RCTDirectEventBlock?, delegate: YamapViewComponentDelegate?) {
10
+ self.callback = callback
11
+ self.delegate = delegate
12
+ super.init()
13
+ }
14
+
15
+ func onCameraPositionChanged(with _: YMKMap,
16
+ cameraPosition: YMKCameraPosition,
17
+ cameraUpdateReason : YMKCameraUpdateReason,
18
+ finished: Bool)
19
+ {
20
+ let target = cameraPosition.target
21
+ let reasonString: String
22
+ switch cameraUpdateReason {
23
+ case .gestures:
24
+ reasonString = "GESTURES"
25
+ case .application:
26
+ reasonString = "APPLICATION"
27
+ @unknown default:
28
+ reasonString = "GESTURES"
29
+ }
30
+ let params: [String: Any] = [
31
+ "lat": target.latitude,
32
+ "lon": target.longitude,
33
+ "zoom": Double(cameraPosition.zoom),
34
+ "azimuth": Double(cameraPosition.azimuth),
35
+ "tilt": Double(cameraPosition.tilt),
36
+ "finished": finished,
37
+ "target": 0.0,
38
+ "reason": reasonString
39
+ ]
40
+
41
+ DispatchQueue.main.async { [weak self] in
42
+ guard let self = self else { return }
43
+ if let callback = self.callback {
44
+ callback(params)
45
+ return
46
+ } else if let delegate = self.delegate {
47
+ delegate.handleOnCameraPositionChange(coords: params)
48
+ if finished {
49
+ delegate.handleOnCameraPositionChangeEnd(coords: params)
50
+ }
51
+ }
52
+ }
53
+ }
54
+ }
@@ -0,0 +1,38 @@
1
+ import Foundation
2
+ import React
3
+ import YandexMapsMobile
4
+
5
+ class ClusterListener: NSObject, YMKClusterListener, YMKClusterTapListener {
6
+ private let callback: RCTDirectEventBlock?
7
+ private weak var delegate: YamapViewComponentDelegate?
8
+ private weak var clusteredView: ClusteredYamapView?
9
+
10
+ init(callback: RCTDirectEventBlock?, delegate: YamapViewComponentDelegate?) {
11
+ self.callback = callback
12
+ self.delegate = delegate
13
+ super.init()
14
+ }
15
+
16
+ func setClusteredView(_ view: ClusteredYamapView) {
17
+ self.clusteredView = view
18
+ }
19
+
20
+ func onClusterAdded(with cluster: YMKCluster) {
21
+ if let view = clusteredView {
22
+ let clusterSize = cluster.size
23
+ if let image = view.clusterImage(clusterSize: Int(clusterSize)) {
24
+ cluster.appearance.setIconWith(image)
25
+ }
26
+ }
27
+ cluster.addClusterTapListener(with: self)
28
+ }
29
+
30
+ func onClusterTap(with cluster: YMKCluster) -> Bool {
31
+ var lastKnownMarkers: [YMKPoint] = []
32
+ for placemark in cluster.placemarks {
33
+ lastKnownMarkers.append(placemark.geometry)
34
+ }
35
+ clusteredView?.fitMakers(lastKnownMarkers)
36
+ return true
37
+ }
38
+ }