@maydon_tech/react-native-nitro-maps 0.1.0
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.
- package/LICENSE +20 -0
- package/NitroMap.podspec +42 -0
- package/README.md +172 -0
- package/android/CMakeLists.txt +27 -0
- package/android/build.gradle +141 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/cpp/cpp-adapter.cpp +6 -0
- package/android/src/main/java/com/margelo/nitro/nitromap/ClusterIconGenerator.kt +108 -0
- package/android/src/main/java/com/margelo/nitro/nitromap/ColorUtils.kt +63 -0
- package/android/src/main/java/com/margelo/nitro/nitromap/HybridNitroMap.kt +408 -0
- package/android/src/main/java/com/margelo/nitro/nitromap/HybridNitroMapConfig.kt +68 -0
- package/android/src/main/java/com/margelo/nitro/nitromap/MarkerIconCache.kt +176 -0
- package/android/src/main/java/com/margelo/nitro/nitromap/MarkerIconFactory.kt +252 -0
- package/android/src/main/java/com/margelo/nitro/nitromap/NitroMapPackage.kt +33 -0
- package/android/src/main/java/com/margelo/nitro/nitromap/clustering/NitroClusterEngine.kt +252 -0
- package/android/src/main/java/com/margelo/nitro/nitromap/clustering/QuadTree.kt +195 -0
- package/android/src/main/java/com/margelo/nitro/nitromap/providers/GoogleMapProvider.kt +912 -0
- package/android/src/main/java/com/margelo/nitro/nitromap/providers/MapProviderInterface.kt +70 -0
- package/cpp/ClusterEngine.hpp +411 -0
- package/cpp/KDBush.hpp +238 -0
- package/cpp/QuadTree.hpp +246 -0
- package/ios/Clustering/ClusterEngineWrapper.h +58 -0
- package/ios/Clustering/ClusterEngineWrapper.mm +142 -0
- package/ios/Clustering/ClusterIconRenderer.swift +80 -0
- package/ios/Clustering/NitroClusterEngine.swift +117 -0
- package/ios/Clustering/NitroClusterIconGenerator.swift +35 -0
- package/ios/MarkerRenderer/MarkerIconFactory.swift +322 -0
- package/ios/MarkerRenderer/PriceMarkerRenderer.swift +140 -0
- package/ios/NitroMap.swift +332 -0
- package/ios/NitroMapConfig/HybridNitroMapConfig.swift +33 -0
- package/ios/Providers/GoogleMapDelegate.swift +310 -0
- package/ios/Providers/GoogleMapProvider+Camera.swift +164 -0
- package/ios/Providers/GoogleMapProvider.swift +924 -0
- package/ios/Providers/MapProviderProtocol.swift +78 -0
- package/ios/Shared/ClusterConfig+Factory.swift +58 -0
- package/ios/Shared/ClusteringManager.swift +211 -0
- package/ios/Shared/MapStyleProvider.swift +135 -0
- package/ios/Shared/MarkerSelectionHandler.swift +116 -0
- package/ios/Utils/ColorValueExtension.swift +54 -0
- package/lib/module/components/ImageMarker.js +146 -0
- package/lib/module/components/ImageMarker.js.map +1 -0
- package/lib/module/components/NitroMap.js +320 -0
- package/lib/module/components/NitroMap.js.map +1 -0
- package/lib/module/components/PriceMarker.js +165 -0
- package/lib/module/components/PriceMarker.js.map +1 -0
- package/lib/module/context/NitroMapContext.js +15 -0
- package/lib/module/context/NitroMapContext.js.map +1 -0
- package/lib/module/hooks/useNitroMarker.js +104 -0
- package/lib/module/hooks/useNitroMarker.js.map +1 -0
- package/lib/module/index.js +21 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/modules/index.js +4 -0
- package/lib/module/modules/index.js.map +1 -0
- package/lib/module/modules/module.js +30 -0
- package/lib/module/modules/module.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/specs/NitroMap.nitro.js +4 -0
- package/lib/module/specs/NitroMap.nitro.js.map +1 -0
- package/lib/module/specs/NitroMapConfig.nitro.js +4 -0
- package/lib/module/specs/NitroMapConfig.nitro.js.map +1 -0
- package/lib/module/types/map.js +2 -0
- package/lib/module/types/map.js.map +1 -0
- package/lib/module/types/marker.js +4 -0
- package/lib/module/types/marker.js.map +1 -0
- package/lib/module/utils/colors.js +147 -0
- package/lib/module/utils/colors.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/components/ImageMarker.d.ts +70 -0
- package/lib/typescript/src/components/ImageMarker.d.ts.map +1 -0
- package/lib/typescript/src/components/NitroMap.d.ts +14 -0
- package/lib/typescript/src/components/NitroMap.d.ts.map +1 -0
- package/lib/typescript/src/components/PriceMarker.d.ts +88 -0
- package/lib/typescript/src/components/PriceMarker.d.ts.map +1 -0
- package/lib/typescript/src/context/NitroMapContext.d.ts +16 -0
- package/lib/typescript/src/context/NitroMapContext.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useNitroMarker.d.ts +78 -0
- package/lib/typescript/src/hooks/useNitroMarker.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +12 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/modules/index.d.ts +2 -0
- package/lib/typescript/src/modules/index.d.ts.map +1 -0
- package/lib/typescript/src/modules/module.d.ts +22 -0
- package/lib/typescript/src/modules/module.d.ts.map +1 -0
- package/lib/typescript/src/specs/NitroMap.nitro.d.ts +227 -0
- package/lib/typescript/src/specs/NitroMap.nitro.d.ts.map +1 -0
- package/lib/typescript/src/specs/NitroMapConfig.nitro.d.ts +10 -0
- package/lib/typescript/src/specs/NitroMapConfig.nitro.d.ts.map +1 -0
- package/lib/typescript/src/types/map.d.ts +154 -0
- package/lib/typescript/src/types/map.d.ts.map +1 -0
- package/lib/typescript/src/types/marker.d.ts +248 -0
- package/lib/typescript/src/types/marker.d.ts.map +1 -0
- package/lib/typescript/src/utils/colors.d.ts +82 -0
- package/lib/typescript/src/utils/colors.d.ts.map +1 -0
- package/nitro.json +21 -0
- package/nitrogen/generated/android/c++/JCamera.hpp +74 -0
- package/nitrogen/generated/android/c++/JClusterAnimationStyle.hpp +68 -0
- package/nitrogen/generated/android/c++/JClusterConfig.hpp +121 -0
- package/nitrogen/generated/android/c++/JClusterPressEvent.hpp +86 -0
- package/nitrogen/generated/android/c++/JClusterStrategy.hpp +59 -0
- package/nitrogen/generated/android/c++/JColorValue.cpp +26 -0
- package/nitrogen/generated/android/c++/JColorValue.hpp +70 -0
- package/nitrogen/generated/android/c++/JCoordinate.hpp +61 -0
- package/nitrogen/generated/android/c++/JEdgePadding.hpp +69 -0
- package/nitrogen/generated/android/c++/JFunc_void.hpp +75 -0
- package/nitrogen/generated/android/c++/JFunc_void_ClusterPressEvent.hpp +81 -0
- package/nitrogen/generated/android/c++/JFunc_void_MapError.hpp +78 -0
- package/nitrogen/generated/android/c++/JFunc_void_MapPressEvent.hpp +81 -0
- package/nitrogen/generated/android/c++/JFunc_void_MarkerDragEvent.hpp +80 -0
- package/nitrogen/generated/android/c++/JFunc_void_MarkerPressEvent.hpp +80 -0
- package/nitrogen/generated/android/c++/JFunc_void_RegionChangeEvent.hpp +79 -0
- package/nitrogen/generated/android/c++/JHybridNitroMapConfigSpec.cpp +59 -0
- package/nitrogen/generated/android/c++/JHybridNitroMapConfigSpec.hpp +66 -0
- package/nitrogen/generated/android/c++/JHybridNitroMapSpec.cpp +593 -0
- package/nitrogen/generated/android/c++/JHybridNitroMapSpec.hpp +125 -0
- package/nitrogen/generated/android/c++/JImageMarkerConfig.hpp +86 -0
- package/nitrogen/generated/android/c++/JMapBoundaries.hpp +62 -0
- package/nitrogen/generated/android/c++/JMapError.hpp +61 -0
- package/nitrogen/generated/android/c++/JMapPressEvent.hpp +64 -0
- package/nitrogen/generated/android/c++/JMapProvider.hpp +62 -0
- package/nitrogen/generated/android/c++/JMapStyleElement.hpp +87 -0
- package/nitrogen/generated/android/c++/JMapStyler.hpp +78 -0
- package/nitrogen/generated/android/c++/JMapType.hpp +62 -0
- package/nitrogen/generated/android/c++/JMarkerAnimation.hpp +62 -0
- package/nitrogen/generated/android/c++/JMarkerColor.hpp +69 -0
- package/nitrogen/generated/android/c++/JMarkerConfig.hpp +77 -0
- package/nitrogen/generated/android/c++/JMarkerData.hpp +121 -0
- package/nitrogen/generated/android/c++/JMarkerDragEvent.hpp +63 -0
- package/nitrogen/generated/android/c++/JMarkerPressEvent.hpp +63 -0
- package/nitrogen/generated/android/c++/JMarkerStyle.hpp +62 -0
- package/nitrogen/generated/android/c++/JPoint.hpp +61 -0
- package/nitrogen/generated/android/c++/JPriceMarkerStyle.hpp +102 -0
- package/nitrogen/generated/android/c++/JRegion.hpp +69 -0
- package/nitrogen/generated/android/c++/JRegionChangeEvent.hpp +62 -0
- package/nitrogen/generated/android/c++/JVariant_String_MarkerColor.cpp +26 -0
- package/nitrogen/generated/android/c++/JVariant_String_MarkerColor.hpp +70 -0
- package/nitrogen/generated/android/c++/views/JHybridNitroMapStateUpdater.cpp +144 -0
- package/nitrogen/generated/android/c++/views/JHybridNitroMapStateUpdater.hpp +49 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Camera.kt +50 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/ClusterAnimationStyle.kt +24 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/ClusterConfig.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/ClusterPressEvent.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/ClusterStrategy.kt +21 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/ColorValue.kt +59 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Coordinate.kt +41 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/EdgePadding.kt +47 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_ClusterPressEvent.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_MapError.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_MapPressEvent.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_MarkerDragEvent.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_MarkerPressEvent.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_RegionChangeEvent.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/HybridNitroMapConfigSpec.kt +61 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/HybridNitroMapSpec.kt +342 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/ImageMarkerConfig.kt +56 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MapBoundaries.kt +41 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MapError.kt +41 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MapPressEvent.kt +41 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MapProvider.kt +22 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MapStyleElement.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MapStyler.kt +53 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MapType.kt +22 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MarkerAnimation.kt +22 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MarkerColor.kt +47 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MarkerConfig.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MarkerData.kt +71 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MarkerDragEvent.kt +41 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MarkerPressEvent.kt +41 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MarkerStyle.kt +22 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Point.kt +41 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/PriceMarkerStyle.kt +68 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Region.kt +47 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/RegionChangeEvent.kt +41 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Variant_String_MarkerColor.kt +59 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/nitromapOnLoad.kt +35 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/views/HybridNitroMapManager.kt +50 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/views/HybridNitroMapStateUpdater.kt +23 -0
- package/nitrogen/generated/android/nitromap+autolinking.cmake +87 -0
- package/nitrogen/generated/android/nitromap+autolinking.gradle +27 -0
- package/nitrogen/generated/android/nitromapOnLoad.cpp +70 -0
- package/nitrogen/generated/android/nitromapOnLoad.hpp +25 -0
- package/nitrogen/generated/ios/NitroMap+autolinking.rb +60 -0
- package/nitrogen/generated/ios/NitroMap-Swift-Cxx-Bridge.cpp +130 -0
- package/nitrogen/generated/ios/NitroMap-Swift-Cxx-Bridge.hpp +793 -0
- package/nitrogen/generated/ios/NitroMap-Swift-Cxx-Umbrella.hpp +132 -0
- package/nitrogen/generated/ios/NitroMapAutolinking.mm +41 -0
- package/nitrogen/generated/ios/NitroMapAutolinking.swift +40 -0
- package/nitrogen/generated/ios/c++/HybridNitroMapConfigSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridNitroMapConfigSpecSwift.hpp +84 -0
- package/nitrogen/generated/ios/c++/HybridNitroMapSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridNitroMapSpecSwift.hpp +410 -0
- package/nitrogen/generated/ios/c++/views/HybridNitroMapComponent.mm +206 -0
- package/nitrogen/generated/ios/swift/Camera.swift +80 -0
- package/nitrogen/generated/ios/swift/ClusterAnimationStyle.swift +52 -0
- package/nitrogen/generated/ios/swift/ClusterConfig.swift +268 -0
- package/nitrogen/generated/ios/swift/ClusterPressEvent.swift +70 -0
- package/nitrogen/generated/ios/swift/ClusterStrategy.swift +40 -0
- package/nitrogen/generated/ios/swift/ColorValue.swift +18 -0
- package/nitrogen/generated/ios/swift/Coordinate.swift +47 -0
- package/nitrogen/generated/ios/swift/EdgePadding.swift +69 -0
- package/nitrogen/generated/ios/swift/Func_void.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_Camera.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_ClusterPressEvent.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_MapBoundaries.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_MapError.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_MapPressEvent.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_MarkerDragEvent.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_MarkerPressEvent.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_RegionChangeEvent.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +47 -0
- package/nitrogen/generated/ios/swift/HybridNitroMapConfigSpec.swift +57 -0
- package/nitrogen/generated/ios/swift/HybridNitroMapConfigSpec_cxx.swift +142 -0
- package/nitrogen/generated/ios/swift/HybridNitroMapSpec.swift +93 -0
- package/nitrogen/generated/ios/swift/HybridNitroMapSpec_cxx.swift +953 -0
- package/nitrogen/generated/ios/swift/ImageMarkerConfig.swift +166 -0
- package/nitrogen/generated/ios/swift/MapBoundaries.swift +47 -0
- package/nitrogen/generated/ios/swift/MapError.swift +47 -0
- package/nitrogen/generated/ios/swift/MapPressEvent.swift +47 -0
- package/nitrogen/generated/ios/swift/MapProvider.swift +44 -0
- package/nitrogen/generated/ios/swift/MapStyleElement.swift +108 -0
- package/nitrogen/generated/ios/swift/MapStyler.swift +177 -0
- package/nitrogen/generated/ios/swift/MapType.swift +44 -0
- package/nitrogen/generated/ios/swift/MarkerAnimation.swift +44 -0
- package/nitrogen/generated/ios/swift/MarkerColor.swift +69 -0
- package/nitrogen/generated/ios/swift/MarkerConfig.swift +82 -0
- package/nitrogen/generated/ios/swift/MarkerData.swift +195 -0
- package/nitrogen/generated/ios/swift/MarkerDragEvent.swift +47 -0
- package/nitrogen/generated/ios/swift/MarkerPressEvent.swift +47 -0
- package/nitrogen/generated/ios/swift/MarkerStyle.swift +44 -0
- package/nitrogen/generated/ios/swift/Point.swift +47 -0
- package/nitrogen/generated/ios/swift/PriceMarkerStyle.swift +374 -0
- package/nitrogen/generated/ios/swift/Region.swift +69 -0
- package/nitrogen/generated/ios/swift/RegionChangeEvent.swift +47 -0
- package/nitrogen/generated/ios/swift/Variant_String_MarkerColor.swift +18 -0
- package/nitrogen/generated/shared/c++/Camera.hpp +92 -0
- package/nitrogen/generated/shared/c++/ClusterAnimationStyle.hpp +88 -0
- package/nitrogen/generated/shared/c++/ClusterConfig.hpp +140 -0
- package/nitrogen/generated/shared/c++/ClusterPressEvent.hpp +86 -0
- package/nitrogen/generated/shared/c++/ClusterStrategy.hpp +76 -0
- package/nitrogen/generated/shared/c++/Coordinate.hpp +79 -0
- package/nitrogen/generated/shared/c++/EdgePadding.hpp +87 -0
- package/nitrogen/generated/shared/c++/HybridNitroMapConfigSpec.cpp +22 -0
- package/nitrogen/generated/shared/c++/HybridNitroMapConfigSpec.hpp +65 -0
- package/nitrogen/generated/shared/c++/HybridNitroMapSpec.cpp +82 -0
- package/nitrogen/generated/shared/c++/HybridNitroMapSpec.hpp +173 -0
- package/nitrogen/generated/shared/c++/ImageMarkerConfig.hpp +103 -0
- package/nitrogen/generated/shared/c++/MapBoundaries.hpp +80 -0
- package/nitrogen/generated/shared/c++/MapError.hpp +79 -0
- package/nitrogen/generated/shared/c++/MapPressEvent.hpp +83 -0
- package/nitrogen/generated/shared/c++/MapProvider.hpp +80 -0
- package/nitrogen/generated/shared/c++/MapStyleElement.hpp +87 -0
- package/nitrogen/generated/shared/c++/MapStyler.hpp +96 -0
- package/nitrogen/generated/shared/c++/MapType.hpp +80 -0
- package/nitrogen/generated/shared/c++/MarkerAnimation.hpp +80 -0
- package/nitrogen/generated/shared/c++/MarkerColor.hpp +87 -0
- package/nitrogen/generated/shared/c++/MarkerConfig.hpp +91 -0
- package/nitrogen/generated/shared/c++/MarkerData.hpp +131 -0
- package/nitrogen/generated/shared/c++/MarkerDragEvent.hpp +81 -0
- package/nitrogen/generated/shared/c++/MarkerPressEvent.hpp +81 -0
- package/nitrogen/generated/shared/c++/MarkerStyle.hpp +80 -0
- package/nitrogen/generated/shared/c++/Point.hpp +79 -0
- package/nitrogen/generated/shared/c++/PriceMarkerStyle.hpp +119 -0
- package/nitrogen/generated/shared/c++/Region.hpp +87 -0
- package/nitrogen/generated/shared/c++/RegionChangeEvent.hpp +80 -0
- package/nitrogen/generated/shared/c++/views/HybridNitroMapComponent.cpp +351 -0
- package/nitrogen/generated/shared/c++/views/HybridNitroMapComponent.hpp +141 -0
- package/nitrogen/generated/shared/json/NitroMapConfig.json +32 -0
- package/package.json +176 -0
- package/react-native.config.js +16 -0
- package/src/components/ImageMarker.tsx +254 -0
- package/src/components/NitroMap.tsx +433 -0
- package/src/components/PriceMarker.tsx +311 -0
- package/src/context/NitroMapContext.tsx +33 -0
- package/src/hooks/useNitroMarker.ts +198 -0
- package/src/index.tsx +62 -0
- package/src/modules/index.ts +6 -0
- package/src/modules/module.ts +45 -0
- package/src/specs/NitroMap.nitro.ts +292 -0
- package/src/specs/NitroMapConfig.nitro.ts +8 -0
- package/src/types/map.ts +166 -0
- package/src/types/marker.ts +267 -0
- package/src/utils/colors.ts +159 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import UIKit
|
|
3
|
+
|
|
4
|
+
/// Protocol that all map providers must implement
|
|
5
|
+
/// This allows the library to support multiple map backends (Google, Apple, Yandex)
|
|
6
|
+
protocol MapProviderProtocol: AnyObject {
|
|
7
|
+
|
|
8
|
+
// MARK: - View
|
|
9
|
+
|
|
10
|
+
/// The underlying map view to be displayed
|
|
11
|
+
var mapView: UIView { get }
|
|
12
|
+
|
|
13
|
+
// MARK: - Properties
|
|
14
|
+
|
|
15
|
+
var initialRegion: Region? { get set }
|
|
16
|
+
var showsUserLocation: Bool? { get set }
|
|
17
|
+
var zoomEnabled: Bool? { get set }
|
|
18
|
+
var scrollEnabled: Bool? { get set }
|
|
19
|
+
var rotateEnabled: Bool? { get set }
|
|
20
|
+
var pitchEnabled: Bool? { get set }
|
|
21
|
+
var mapType: MapType? { get set }
|
|
22
|
+
var showsMyLocationButton: Bool? { get set }
|
|
23
|
+
var clusterConfig: ClusterConfig? { get set }
|
|
24
|
+
var customMapStyle: [MapStyleElement]? { get set }
|
|
25
|
+
var darkMode: Bool? { get set }
|
|
26
|
+
|
|
27
|
+
// MARK: - Callbacks
|
|
28
|
+
|
|
29
|
+
var onPress: ((MapPressEvent) -> Void)? { get set }
|
|
30
|
+
var onLongPress: ((MapPressEvent) -> Void)? { get set }
|
|
31
|
+
var onMapReady: (() -> Void)? { get set }
|
|
32
|
+
var onRegionChange: ((RegionChangeEvent) -> Void)? { get set }
|
|
33
|
+
var onRegionChangeComplete: ((RegionChangeEvent) -> Void)? { get set }
|
|
34
|
+
var onMarkerPress: ((MarkerPressEvent) -> Void)? { get set }
|
|
35
|
+
var onMarkerDragStart: ((MarkerDragEvent) -> Void)? { get set }
|
|
36
|
+
var onMarkerDrag: ((MarkerDragEvent) -> Void)? { get set }
|
|
37
|
+
var onMarkerDragEnd: ((MarkerDragEvent) -> Void)? { get set }
|
|
38
|
+
var onClusterPress: ((ClusterPressEvent) -> Void)? { get set }
|
|
39
|
+
var onError: ((MapError) -> Void)? { get set }
|
|
40
|
+
|
|
41
|
+
// MARK: - Lifecycle
|
|
42
|
+
|
|
43
|
+
func setup()
|
|
44
|
+
func updateSettings()
|
|
45
|
+
|
|
46
|
+
// MARK: - Camera Methods
|
|
47
|
+
|
|
48
|
+
func animateToRegion(_ region: Region, duration: Double?)
|
|
49
|
+
func fitToCoordinates(_ coordinates: [Coordinate], edgePadding: EdgePadding?, animated: Bool?)
|
|
50
|
+
func animateCamera(_ camera: Camera, duration: Double?)
|
|
51
|
+
func setCamera(_ camera: Camera)
|
|
52
|
+
func getCamera() -> Camera
|
|
53
|
+
func getMapBoundaries() -> MapBoundaries?
|
|
54
|
+
|
|
55
|
+
// MARK: - Marker Management
|
|
56
|
+
|
|
57
|
+
func addMarker(_ marker: MarkerData)
|
|
58
|
+
func addMarkers(_ markers: [MarkerData])
|
|
59
|
+
func updateMarker(_ marker: MarkerData)
|
|
60
|
+
func removeMarker(_ id: String)
|
|
61
|
+
func clearMarkers()
|
|
62
|
+
func selectMarker(_ id: String)
|
|
63
|
+
|
|
64
|
+
// MARK: - Clustering
|
|
65
|
+
|
|
66
|
+
func setClusteringEnabled(_ enabled: Bool)
|
|
67
|
+
func refreshClusters()
|
|
68
|
+
func performClustering()
|
|
69
|
+
|
|
70
|
+
// MARK: - Map Style
|
|
71
|
+
|
|
72
|
+
func setMapStyle(_ style: [MapStyleElement]?)
|
|
73
|
+
func setDarkMode(_ enabled: Bool)
|
|
74
|
+
|
|
75
|
+
// MARK: - Region
|
|
76
|
+
|
|
77
|
+
func getCurrentRegion() -> Region
|
|
78
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
// MARK: - ClusterConfig Factory Extension
|
|
4
|
+
|
|
5
|
+
extension ClusterConfig {
|
|
6
|
+
|
|
7
|
+
/// Creates a copy with only the `enabled` field changed.
|
|
8
|
+
/// Eliminates boilerplate in each provider's `setClusteringEnabled()` (DRY).
|
|
9
|
+
static func withEnabled(
|
|
10
|
+
_ enabled: Bool,
|
|
11
|
+
existing: ClusterConfig?
|
|
12
|
+
) -> ClusterConfig {
|
|
13
|
+
let strat: ClusterStrategy = existing?.strategy ?? .supercluster
|
|
14
|
+
let rad: Double = existing?.radius ?? 80
|
|
15
|
+
let minSize: Double = existing?.minimumClusterSize ?? 2
|
|
16
|
+
let mz: Double = existing?.maxZoom ?? 20
|
|
17
|
+
let bg: ColorValue = existing?.backgroundColor
|
|
18
|
+
?? .second(MarkerColor(r: 0, g: 122, b: 255, a: 255))
|
|
19
|
+
let tc: ColorValue = existing?.textColor
|
|
20
|
+
?? .second(MarkerColor(r: 255, g: 255, b: 255, a: 255))
|
|
21
|
+
let bw: Double = existing?.borderWidth ?? 2
|
|
22
|
+
let bc: ColorValue = existing?.borderColor
|
|
23
|
+
?? .second(MarkerColor(r: 255, g: 255, b: 255, a: 255))
|
|
24
|
+
let anim: Bool = existing?.animatesClusters ?? true
|
|
25
|
+
let dur: Double = existing?.animationDuration ?? 0.3
|
|
26
|
+
let style: ClusterAnimationStyle = existing?.animationStyle ?? .default
|
|
27
|
+
let realtime: Bool = existing?.realtimeClustering ?? false
|
|
28
|
+
let vpPad: Double = existing?.renderBuffer ?? 0
|
|
29
|
+
let throttle: Double = existing?.throttleInterval ?? 150
|
|
30
|
+
|
|
31
|
+
return ClusterConfig(
|
|
32
|
+
enabled: enabled,
|
|
33
|
+
strategy: strat,
|
|
34
|
+
radius: rad,
|
|
35
|
+
minimumClusterSize: minSize,
|
|
36
|
+
maxZoom: mz,
|
|
37
|
+
backgroundColor: bg,
|
|
38
|
+
textColor: tc,
|
|
39
|
+
borderWidth: bw,
|
|
40
|
+
borderColor: bc,
|
|
41
|
+
animatesClusters: anim,
|
|
42
|
+
animationDuration: dur,
|
|
43
|
+
animationStyle: style,
|
|
44
|
+
realtimeClustering: realtime,
|
|
45
|
+
renderBuffer: vpPad,
|
|
46
|
+
throttleInterval: throttle
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// MARK: - Cluster User Data
|
|
52
|
+
|
|
53
|
+
/// User data attached to cluster markers for tap handling.
|
|
54
|
+
/// Shared between NitroMap, GoogleMapDelegate, etc.
|
|
55
|
+
struct ClusterUserData {
|
|
56
|
+
let markerIds: [String]
|
|
57
|
+
let count: Int
|
|
58
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import UIKit
|
|
3
|
+
import CoreLocation
|
|
4
|
+
|
|
5
|
+
// MARK: - Clustering Manager Protocol
|
|
6
|
+
|
|
7
|
+
/// Protocol defining the clustering manager interface.
|
|
8
|
+
/// Enables dependency injection and mocking for unit tests.
|
|
9
|
+
///
|
|
10
|
+
/// Usage:
|
|
11
|
+
/// ```swift
|
|
12
|
+
/// class GoogleMapProvider: MapProviderProtocol {
|
|
13
|
+
/// private let clusteringManager: ClusteringManagerProtocol
|
|
14
|
+
///
|
|
15
|
+
/// init(clusteringManager: ClusteringManagerProtocol = ClusteringManager()) {
|
|
16
|
+
/// self.clusteringManager = clusteringManager
|
|
17
|
+
/// }
|
|
18
|
+
/// }
|
|
19
|
+
/// ```
|
|
20
|
+
protocol ClusteringManagerProtocol: AnyObject {
|
|
21
|
+
|
|
22
|
+
/// Current cluster configuration
|
|
23
|
+
var clusterConfig: ClusterConfig? { get set }
|
|
24
|
+
|
|
25
|
+
/// Adds a marker to the clustering engine
|
|
26
|
+
func addMarker(_ marker: MarkerData)
|
|
27
|
+
|
|
28
|
+
/// Removes a marker by ID
|
|
29
|
+
func removeMarker(_ id: String)
|
|
30
|
+
|
|
31
|
+
/// Clears all markers from the engine
|
|
32
|
+
func clearMarkers()
|
|
33
|
+
|
|
34
|
+
/// Clusters markers using bounds-based region
|
|
35
|
+
func cluster(
|
|
36
|
+
minLat: Double,
|
|
37
|
+
maxLat: Double,
|
|
38
|
+
minLon: Double,
|
|
39
|
+
maxLon: Double,
|
|
40
|
+
zoom: Double,
|
|
41
|
+
mapSize: CGSize
|
|
42
|
+
) -> NitroClusterEngine.ClusteringResult
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
/// Executes a clustering action with debouncing to prevent rapid re-clustering
|
|
46
|
+
func debounce(_ action: @escaping () -> Void)
|
|
47
|
+
|
|
48
|
+
/// Gets a cluster icon for the given count
|
|
49
|
+
func clusterIcon(forCount count: Int) -> UIImage?
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// MARK: - Clustering Manager Implementation
|
|
53
|
+
|
|
54
|
+
/// Production implementation of ClusteringManagerProtocol.
|
|
55
|
+
/// Wraps the C++ NitroClusterEngine and provides a clean Swift interface.
|
|
56
|
+
final class ClusteringManager: ClusteringManagerProtocol {
|
|
57
|
+
|
|
58
|
+
// MARK: - Private Properties
|
|
59
|
+
|
|
60
|
+
private let engine: NitroClusterEngine
|
|
61
|
+
private let iconGenerator: NitroClusterIconGenerator
|
|
62
|
+
private var debounceTimer: Timer?
|
|
63
|
+
private let debounceInterval: TimeInterval
|
|
64
|
+
|
|
65
|
+
// MARK: - Protocol Properties
|
|
66
|
+
|
|
67
|
+
var clusterConfig: ClusterConfig? {
|
|
68
|
+
didSet {
|
|
69
|
+
iconGenerator.updateConfig(clusterConfig)
|
|
70
|
+
if let config = clusterConfig {
|
|
71
|
+
engine.setClusterRadius(config.radius)
|
|
72
|
+
engine.setMinClusterSize(Int(config.minimumClusterSize))
|
|
73
|
+
engine.setMaxZoom(config.maxZoom)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// MARK: - Initialization
|
|
79
|
+
|
|
80
|
+
/// Creates a new clustering manager with default configuration
|
|
81
|
+
/// - Parameters:
|
|
82
|
+
/// - clusterRadius: Radius for clustering in screen points (default: 80)
|
|
83
|
+
/// - minClusterSize: Minimum markers to form a cluster (default: 2)
|
|
84
|
+
/// - maxZoom: Maximum zoom level for clustering (default: 20)
|
|
85
|
+
/// - debounceInterval: Debounce interval in seconds (default: 0.1)
|
|
86
|
+
init(
|
|
87
|
+
clusterRadius: Double = 80.0,
|
|
88
|
+
minClusterSize: Int = 2,
|
|
89
|
+
maxZoom: Double = 20.0,
|
|
90
|
+
debounceInterval: TimeInterval = 0.1
|
|
91
|
+
) {
|
|
92
|
+
self.engine = NitroClusterEngine()
|
|
93
|
+
self.iconGenerator = NitroClusterIconGenerator()
|
|
94
|
+
self.debounceInterval = debounceInterval
|
|
95
|
+
|
|
96
|
+
engine.setClusterRadius(clusterRadius)
|
|
97
|
+
engine.setMinClusterSize(minClusterSize)
|
|
98
|
+
engine.setMaxZoom(maxZoom)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
deinit {
|
|
102
|
+
debounceTimer?.invalidate()
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// MARK: - Marker Management
|
|
106
|
+
|
|
107
|
+
func addMarker(_ marker: MarkerData) {
|
|
108
|
+
engine.addMarker(marker)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
func removeMarker(_ id: String) {
|
|
112
|
+
engine.removeMarker(id)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
func clearMarkers() {
|
|
116
|
+
engine.clearMarkers()
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// MARK: - Clustering
|
|
120
|
+
|
|
121
|
+
func cluster(
|
|
122
|
+
minLat: Double,
|
|
123
|
+
maxLat: Double,
|
|
124
|
+
minLon: Double,
|
|
125
|
+
maxLon: Double,
|
|
126
|
+
zoom: Double,
|
|
127
|
+
mapSize: CGSize
|
|
128
|
+
) -> NitroClusterEngine.ClusteringResult {
|
|
129
|
+
return engine.clusterWithBounds(
|
|
130
|
+
minLat: minLat,
|
|
131
|
+
maxLat: maxLat,
|
|
132
|
+
minLon: minLon,
|
|
133
|
+
maxLon: maxLon,
|
|
134
|
+
zoom: zoom,
|
|
135
|
+
mapSize: mapSize
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// MARK: - Debouncing
|
|
140
|
+
|
|
141
|
+
func debounce(_ action: @escaping () -> Void) {
|
|
142
|
+
debounceTimer?.invalidate()
|
|
143
|
+
debounceTimer = Timer.scheduledTimer(
|
|
144
|
+
withTimeInterval: debounceInterval,
|
|
145
|
+
repeats: false
|
|
146
|
+
) { _ in
|
|
147
|
+
action()
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// MARK: - Icon Generation
|
|
152
|
+
|
|
153
|
+
func clusterIcon(forCount count: Int) -> UIImage? {
|
|
154
|
+
return iconGenerator.icon(forSize: UInt(count))
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// MARK: - Mock for Unit Tests
|
|
159
|
+
|
|
160
|
+
#if DEBUG
|
|
161
|
+
/// Mock implementation for unit testing
|
|
162
|
+
final class MockClusteringManager: ClusteringManagerProtocol {
|
|
163
|
+
|
|
164
|
+
var clusterConfig: ClusterConfig?
|
|
165
|
+
|
|
166
|
+
// Tracking for assertions
|
|
167
|
+
private(set) var addedMarkers: [MarkerData] = []
|
|
168
|
+
private(set) var removedMarkerIds: [String] = []
|
|
169
|
+
private(set) var clearMarkersCallCount = 0
|
|
170
|
+
private(set) var clusterCallCount = 0
|
|
171
|
+
|
|
172
|
+
// Configurable return value
|
|
173
|
+
var mockClusterResult: NitroClusterEngine.ClusteringResult = NitroClusterEngine.ClusteringResult(
|
|
174
|
+
clusters: [],
|
|
175
|
+
singleMarkers: []
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
func addMarker(_ marker: MarkerData) {
|
|
179
|
+
addedMarkers.append(marker)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
func removeMarker(_ id: String) {
|
|
183
|
+
removedMarkerIds.append(id)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
func clearMarkers() {
|
|
187
|
+
clearMarkersCallCount += 1
|
|
188
|
+
addedMarkers.removeAll()
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
func cluster(
|
|
192
|
+
minLat: Double,
|
|
193
|
+
maxLat: Double,
|
|
194
|
+
minLon: Double,
|
|
195
|
+
maxLon: Double,
|
|
196
|
+
zoom: Double,
|
|
197
|
+
mapSize: CGSize
|
|
198
|
+
) -> NitroClusterEngine.ClusteringResult {
|
|
199
|
+
clusterCallCount += 1
|
|
200
|
+
return mockClusterResult
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
func debounce(_ action: @escaping () -> Void) {
|
|
204
|
+
action() // Execute immediately in tests
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
func clusterIcon(forCount count: Int) -> UIImage? {
|
|
208
|
+
return nil
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
#endif
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import UIKit
|
|
3
|
+
|
|
4
|
+
// MARK: - Map Style Protocol
|
|
5
|
+
|
|
6
|
+
/// Protocol for map styling utilities.
|
|
7
|
+
/// Provides zoom/altitude conversions and style constants.
|
|
8
|
+
protocol MapStyleProviding {
|
|
9
|
+
|
|
10
|
+
/// Converts lat/lon deltas to approximate zoom level
|
|
11
|
+
func zoomFromDeltas(latDelta: Double, lonDelta: Double) -> Double
|
|
12
|
+
|
|
13
|
+
/// Converts zoom level to approximate altitude (meters)
|
|
14
|
+
func altitudeFromZoom(_ zoom: Double) -> Double
|
|
15
|
+
|
|
16
|
+
/// Converts altitude to approximate zoom level
|
|
17
|
+
func zoomFromAltitude(_ altitude: Double) -> Double
|
|
18
|
+
|
|
19
|
+
/// Gets the dark mode JSON style for Google Maps
|
|
20
|
+
var googleDarkModeStyle: String { get }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// MARK: - Implementation
|
|
24
|
+
|
|
25
|
+
/// Production implementation of map styling utilities.
|
|
26
|
+
/// All methods are pure functions - thread-safe and stateless.
|
|
27
|
+
final class MapStyleProvider: MapStyleProviding {
|
|
28
|
+
|
|
29
|
+
// MARK: - Singleton
|
|
30
|
+
|
|
31
|
+
/// Shared instance for convenience (stateless, so safe to share)
|
|
32
|
+
static let shared = MapStyleProvider()
|
|
33
|
+
|
|
34
|
+
// MARK: - Constants
|
|
35
|
+
|
|
36
|
+
/// Earth's approximate circumference at equator in degrees
|
|
37
|
+
private let earthCircumferenceDegrees: Double = 360.0
|
|
38
|
+
|
|
39
|
+
/// Approximate altitude at zoom level 0 (meters)
|
|
40
|
+
private let baseAltitude: Double = 40_000_000
|
|
41
|
+
|
|
42
|
+
/// Maximum supported zoom level
|
|
43
|
+
private let maxZoom: Double = 21.0
|
|
44
|
+
|
|
45
|
+
/// Minimum supported zoom level
|
|
46
|
+
private let minZoom: Double = 0.0
|
|
47
|
+
|
|
48
|
+
// MARK: - Initialization
|
|
49
|
+
|
|
50
|
+
init() {}
|
|
51
|
+
|
|
52
|
+
// MARK: - MapStyleProviding
|
|
53
|
+
|
|
54
|
+
func zoomFromDeltas(latDelta: Double, lonDelta: Double) -> Double {
|
|
55
|
+
let maxDelta = max(latDelta, lonDelta)
|
|
56
|
+
guard maxDelta > 0 else { return maxZoom }
|
|
57
|
+
|
|
58
|
+
let zoom = log2(earthCircumferenceDegrees / maxDelta)
|
|
59
|
+
return clamp(zoom, min: minZoom, max: maxZoom)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
func altitudeFromZoom(_ zoom: Double) -> Double {
|
|
63
|
+
let clampedZoom = clamp(zoom, min: minZoom, max: maxZoom)
|
|
64
|
+
return baseAltitude / pow(2, clampedZoom)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
func zoomFromAltitude(_ altitude: Double) -> Double {
|
|
68
|
+
guard altitude > 0 else { return maxZoom }
|
|
69
|
+
|
|
70
|
+
let zoom = log2(baseAltitude / altitude)
|
|
71
|
+
return clamp(zoom, min: minZoom, max: maxZoom)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
var googleDarkModeStyle: String {
|
|
75
|
+
return Self.darkModeJSON
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// MARK: - Private Helpers
|
|
79
|
+
|
|
80
|
+
private func clamp(_ value: Double, min: Double, max: Double) -> Double {
|
|
81
|
+
return Swift.max(min, Swift.min(max, value))
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// MARK: - Dark Mode Style JSON
|
|
85
|
+
|
|
86
|
+
private static let darkModeJSON = """
|
|
87
|
+
[
|
|
88
|
+
{"elementType": "geometry", "stylers": [{"color": "#242f3e"}]},
|
|
89
|
+
{"elementType": "labels.text.fill", "stylers": [{"color": "#746855"}]},
|
|
90
|
+
{"elementType": "labels.text.stroke", "stylers": [{"color": "#242f3e"}]},
|
|
91
|
+
{"featureType": "administrative.locality", "elementType": "labels.text.fill", "stylers": [{"color": "#d59563"}]},
|
|
92
|
+
{"featureType": "poi", "elementType": "labels.text.fill", "stylers": [{"color": "#d59563"}]},
|
|
93
|
+
{"featureType": "poi.park", "elementType": "geometry", "stylers": [{"color": "#263c3f"}]},
|
|
94
|
+
{"featureType": "poi.park", "elementType": "labels.text.fill", "stylers": [{"color": "#6b9a76"}]},
|
|
95
|
+
{"featureType": "road", "elementType": "geometry", "stylers": [{"color": "#38414e"}]},
|
|
96
|
+
{"featureType": "road", "elementType": "geometry.stroke", "stylers": [{"color": "#212a37"}]},
|
|
97
|
+
{"featureType": "road", "elementType": "labels.text.fill", "stylers": [{"color": "#9ca5b3"}]},
|
|
98
|
+
{"featureType": "road.highway", "elementType": "geometry", "stylers": [{"color": "#746855"}]},
|
|
99
|
+
{"featureType": "road.highway", "elementType": "geometry.stroke", "stylers": [{"color": "#1f2835"}]},
|
|
100
|
+
{"featureType": "road.highway", "elementType": "labels.text.fill", "stylers": [{"color": "#f3d19c"}]},
|
|
101
|
+
{"featureType": "transit", "elementType": "geometry", "stylers": [{"color": "#2f3948"}]},
|
|
102
|
+
{"featureType": "transit.station", "elementType": "labels.text.fill", "stylers": [{"color": "#d59563"}]},
|
|
103
|
+
{"featureType": "water", "elementType": "geometry", "stylers": [{"color": "#17263c"}]},
|
|
104
|
+
{"featureType": "water", "elementType": "labels.text.fill", "stylers": [{"color": "#515c6d"}]},
|
|
105
|
+
{"featureType": "water", "elementType": "labels.text.stroke", "stylers": [{"color": "#17263c"}]}
|
|
106
|
+
]
|
|
107
|
+
"""
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// MARK: - Mock for Unit Tests
|
|
111
|
+
|
|
112
|
+
#if DEBUG
|
|
113
|
+
/// Mock implementation for unit testing
|
|
114
|
+
final class MockMapStyleProvider: MapStyleProviding {
|
|
115
|
+
|
|
116
|
+
var mockZoom: Double = 10.0
|
|
117
|
+
var mockAltitude: Double = 40000.0
|
|
118
|
+
|
|
119
|
+
var googleDarkModeStyle: String {
|
|
120
|
+
return "[]" // Empty JSON for testing
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
func zoomFromDeltas(latDelta: Double, lonDelta: Double) -> Double {
|
|
124
|
+
return mockZoom
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
func altitudeFromZoom(_ zoom: Double) -> Double {
|
|
128
|
+
return mockAltitude
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
func zoomFromAltitude(_ altitude: Double) -> Double {
|
|
132
|
+
return mockZoom
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
#endif
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import UIKit
|
|
3
|
+
|
|
4
|
+
// MARK: - Marker Selection Protocol
|
|
5
|
+
|
|
6
|
+
/// Protocol for handling marker selection state changes.
|
|
7
|
+
/// Enables dependency injection and clean separation of concerns.
|
|
8
|
+
protocol MarkerSelectionHandling {
|
|
9
|
+
|
|
10
|
+
/// Creates an updated MarkerData with the new selection state.
|
|
11
|
+
/// Returns nil if the marker type doesn't support visual selection (only priceMarker does).
|
|
12
|
+
///
|
|
13
|
+
/// - Parameters:
|
|
14
|
+
/// - markerData: The original marker data
|
|
15
|
+
/// - selected: Whether the marker should be selected
|
|
16
|
+
/// - Returns: Updated marker data with new selection state, or nil if unsupported
|
|
17
|
+
func updateSelectionState(for markerData: MarkerData, selected: Bool) -> MarkerData?
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// MARK: - Implementation
|
|
21
|
+
|
|
22
|
+
/// Production implementation of marker selection handling.
|
|
23
|
+
/// Thread-safe, pure function - can be used from any thread.
|
|
24
|
+
final class MarkerSelectionHandler: MarkerSelectionHandling {
|
|
25
|
+
|
|
26
|
+
// MARK: - Singleton
|
|
27
|
+
|
|
28
|
+
/// Shared instance for convenience (stateless, so safe to share)
|
|
29
|
+
static let shared = MarkerSelectionHandler()
|
|
30
|
+
|
|
31
|
+
// MARK: - Initialization
|
|
32
|
+
|
|
33
|
+
init() {}
|
|
34
|
+
|
|
35
|
+
// MARK: - MarkerSelectionHandling
|
|
36
|
+
|
|
37
|
+
func updateSelectionState(for markerData: MarkerData, selected: Bool) -> MarkerData? {
|
|
38
|
+
// Only priceMarker style supports visual selection state
|
|
39
|
+
guard markerData.config.style == .pricemarker,
|
|
40
|
+
let priceConfig = markerData.config.priceMarker else {
|
|
41
|
+
return nil
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Early exit if state hasn't changed
|
|
45
|
+
if priceConfig.selected == selected {
|
|
46
|
+
return nil
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Create updated price config with new selection state
|
|
50
|
+
let updatedPriceConfig = PriceMarkerStyle(
|
|
51
|
+
price: priceConfig.price,
|
|
52
|
+
currency: priceConfig.currency,
|
|
53
|
+
selected: selected,
|
|
54
|
+
backgroundColor: priceConfig.backgroundColor,
|
|
55
|
+
selectedBackgroundColor: priceConfig.selectedBackgroundColor,
|
|
56
|
+
textColor: priceConfig.textColor,
|
|
57
|
+
selectedTextColor: priceConfig.selectedTextColor,
|
|
58
|
+
fontSize: priceConfig.fontSize,
|
|
59
|
+
paddingHorizontal: priceConfig.paddingHorizontal,
|
|
60
|
+
paddingVertical: priceConfig.paddingVertical,
|
|
61
|
+
shadowOpacity: priceConfig.shadowOpacity
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
// Create updated config
|
|
65
|
+
let updatedConfig = MarkerConfig(
|
|
66
|
+
style: markerData.config.style,
|
|
67
|
+
image: markerData.config.image,
|
|
68
|
+
priceMarker: updatedPriceConfig
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
// Return new marker data (immutable pattern)
|
|
72
|
+
return MarkerData(
|
|
73
|
+
id: markerData.id,
|
|
74
|
+
coordinate: markerData.coordinate,
|
|
75
|
+
title: markerData.title,
|
|
76
|
+
description: markerData.description,
|
|
77
|
+
draggable: markerData.draggable,
|
|
78
|
+
opacity: markerData.opacity,
|
|
79
|
+
rotation: markerData.rotation,
|
|
80
|
+
zIndex: markerData.zIndex,
|
|
81
|
+
anchor: markerData.anchor,
|
|
82
|
+
config: updatedConfig,
|
|
83
|
+
clusteringEnabled: markerData.clusteringEnabled,
|
|
84
|
+
animation: markerData.animation
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// MARK: - Mock for Unit Tests
|
|
90
|
+
|
|
91
|
+
#if DEBUG
|
|
92
|
+
/// Mock implementation for unit testing
|
|
93
|
+
final class MockMarkerSelectionHandler: MarkerSelectionHandling {
|
|
94
|
+
|
|
95
|
+
// Tracking for assertions
|
|
96
|
+
private(set) var updateSelectionCallCount = 0
|
|
97
|
+
private(set) var lastMarkerData: MarkerData?
|
|
98
|
+
private(set) var lastSelectedValue: Bool?
|
|
99
|
+
|
|
100
|
+
// Configurable behavior
|
|
101
|
+
var shouldReturnNil = false
|
|
102
|
+
|
|
103
|
+
func updateSelectionState(for markerData: MarkerData, selected: Bool) -> MarkerData? {
|
|
104
|
+
updateSelectionCallCount += 1
|
|
105
|
+
lastMarkerData = markerData
|
|
106
|
+
lastSelectedValue = selected
|
|
107
|
+
|
|
108
|
+
if shouldReturnNil {
|
|
109
|
+
return nil
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Use real implementation for realistic behavior
|
|
113
|
+
return MarkerSelectionHandler.shared.updateSelectionState(for: markerData, selected: selected)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
#endif
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import UIKit
|
|
3
|
+
|
|
4
|
+
// MARK: - ColorValue Extension
|
|
5
|
+
// Shared extension for converting ColorValue (hex string or MarkerColor) to MarkerColor
|
|
6
|
+
extension ColorValue {
|
|
7
|
+
/// Extract MarkerColor from ColorValue, parsing hex string if needed
|
|
8
|
+
func toMarkerColor() -> MarkerColor {
|
|
9
|
+
switch self {
|
|
10
|
+
case .first(let hexString):
|
|
11
|
+
return ColorValue.parseHex(hexString)
|
|
12
|
+
case .second(let markerColor):
|
|
13
|
+
return markerColor
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/// Parse hex color string to MarkerColor
|
|
18
|
+
private static func parseHex(_ hex: String) -> MarkerColor {
|
|
19
|
+
var hexSanitized = hex.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
20
|
+
hexSanitized = hexSanitized.replacingOccurrences(of: "#", with: "")
|
|
21
|
+
|
|
22
|
+
var rgb: UInt64 = 0
|
|
23
|
+
Scanner(string: hexSanitized).scanHexInt64(&rgb)
|
|
24
|
+
|
|
25
|
+
let r, g, b: Double
|
|
26
|
+
if hexSanitized.count == 6 {
|
|
27
|
+
r = Double((rgb & 0xFF0000) >> 16)
|
|
28
|
+
g = Double((rgb & 0x00FF00) >> 8)
|
|
29
|
+
b = Double(rgb & 0x0000FF)
|
|
30
|
+
} else if hexSanitized.count == 3 {
|
|
31
|
+
r = Double((rgb & 0xF00) >> 8) * 17
|
|
32
|
+
g = Double((rgb & 0x0F0) >> 4) * 17
|
|
33
|
+
b = Double(rgb & 0x00F) * 17
|
|
34
|
+
} else {
|
|
35
|
+
r = 0; g = 0; b = 0
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return MarkerColor(r: r, g: g, b: b, a: 255)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// MARK: - UIColor Helper
|
|
43
|
+
extension ColorValue {
|
|
44
|
+
/// Convert ColorValue directly to UIColor
|
|
45
|
+
func toUIColor() -> UIColor {
|
|
46
|
+
let color = self.toMarkerColor()
|
|
47
|
+
return UIColor(
|
|
48
|
+
red: CGFloat(color.r) / 255,
|
|
49
|
+
green: CGFloat(color.g) / 255,
|
|
50
|
+
blue: CGFloat(color.b) / 255,
|
|
51
|
+
alpha: CGFloat(color.a) / 255
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
}
|