@granite-js/naver-map 1.0.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.
Files changed (104) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/GraniteNaverMap.podspec +81 -0
  3. package/android/build.gradle +160 -0
  4. package/android/gradle.properties +5 -0
  5. package/android/src/builtinProvider/java/run/granite/navermap/builtinProvider/BuiltInGraniteNaverMapProvider.kt +549 -0
  6. package/android/src/main/AndroidManifest.xml +3 -0
  7. package/android/src/main/java/run/granite/navermap/GraniteNaverMapEvents.kt +91 -0
  8. package/android/src/main/java/run/granite/navermap/GraniteNaverMapPackage.kt +16 -0
  9. package/android/src/main/java/run/granite/navermap/GraniteNaverMapProvider.kt +242 -0
  10. package/android/src/main/java/run/granite/navermap/GraniteNaverMapRegistry.kt +116 -0
  11. package/android/src/main/java/run/granite/navermap/GraniteNaverMapView.kt +732 -0
  12. package/android/src/newarch/java/run/granite/navermap/GraniteNaverMapViewManager.kt +446 -0
  13. package/android/src/oldarch/java/run/granite/navermap/GraniteNaverMapViewManager.kt +249 -0
  14. package/dist/module/NaverMapView.js +110 -0
  15. package/dist/module/NaverMapView.js.map +1 -0
  16. package/dist/module/index.js +6 -0
  17. package/dist/module/index.js.map +1 -0
  18. package/dist/module/internals/colorUtils.js +624 -0
  19. package/dist/module/internals/colorUtils.js.map +1 -0
  20. package/dist/module/internals/context.js +8 -0
  21. package/dist/module/internals/context.js.map +1 -0
  22. package/dist/module/internals/id.js +10 -0
  23. package/dist/module/internals/id.js.map +1 -0
  24. package/dist/module/internals/useMapOverlay.js +44 -0
  25. package/dist/module/internals/useMapOverlay.js.map +1 -0
  26. package/dist/module/internals/usePreservedReference.js +36 -0
  27. package/dist/module/internals/usePreservedReference.js.map +1 -0
  28. package/dist/module/overlays/ArrowheadPath.js +37 -0
  29. package/dist/module/overlays/ArrowheadPath.js.map +1 -0
  30. package/dist/module/overlays/Circle.js +35 -0
  31. package/dist/module/overlays/Circle.js.map +1 -0
  32. package/dist/module/overlays/GroundOverlay.js +30 -0
  33. package/dist/module/overlays/GroundOverlay.js.map +1 -0
  34. package/dist/module/overlays/InfoWindow.js +32 -0
  35. package/dist/module/overlays/InfoWindow.js.map +1 -0
  36. package/dist/module/overlays/Marker.js +37 -0
  37. package/dist/module/overlays/Marker.js.map +1 -0
  38. package/dist/module/overlays/Path.js +41 -0
  39. package/dist/module/overlays/Path.js.map +1 -0
  40. package/dist/module/overlays/Polygon.js +51 -0
  41. package/dist/module/overlays/Polygon.js.map +1 -0
  42. package/dist/module/overlays/Polyline.js +60 -0
  43. package/dist/module/overlays/Polyline.js.map +1 -0
  44. package/dist/module/overlays/index.js +11 -0
  45. package/dist/module/overlays/index.js.map +1 -0
  46. package/dist/module/package.json +1 -0
  47. package/dist/module/specs/GraniteNaverMapViewNativeComponent.ts +352 -0
  48. package/dist/module/types/Coord.js +2 -0
  49. package/dist/module/types/Coord.js.map +1 -0
  50. package/dist/module/types/index.js +29 -0
  51. package/dist/module/types/index.js.map +1 -0
  52. package/dist/typescript/NaverMapView.d.ts +41 -0
  53. package/dist/typescript/index.d.ts +3 -0
  54. package/dist/typescript/internals/colorUtils.d.ts +10 -0
  55. package/dist/typescript/internals/context.d.ts +8 -0
  56. package/dist/typescript/internals/id.d.ts +1 -0
  57. package/dist/typescript/internals/useMapOverlay.d.ts +15 -0
  58. package/dist/typescript/internals/usePreservedReference.d.ts +6 -0
  59. package/dist/typescript/overlays/ArrowheadPath.d.ts +11 -0
  60. package/dist/typescript/overlays/Circle.d.ts +10 -0
  61. package/dist/typescript/overlays/GroundOverlay.d.ts +11 -0
  62. package/dist/typescript/overlays/InfoWindow.d.ts +14 -0
  63. package/dist/typescript/overlays/Marker.d.ts +16 -0
  64. package/dist/typescript/overlays/Path.d.ts +15 -0
  65. package/dist/typescript/overlays/Polygon.d.ts +10 -0
  66. package/dist/typescript/overlays/Polyline.d.ts +11 -0
  67. package/dist/typescript/overlays/index.d.ts +16 -0
  68. package/dist/typescript/specs/GraniteNaverMapViewNativeComponent.d.ts +92 -0
  69. package/dist/typescript/types/Coord.d.ts +4 -0
  70. package/dist/typescript/types/index.d.ts +55 -0
  71. package/ios/GraniteNaverMap-Bridging-Header.h +20 -0
  72. package/ios/GraniteNaverMapProvider.swift +312 -0
  73. package/ios/GraniteNaverMapRegistry.swift +91 -0
  74. package/ios/GraniteNaverMapView.h +15 -0
  75. package/ios/GraniteNaverMapView.mm +390 -0
  76. package/ios/GraniteNaverMapViewImpl.swift +496 -0
  77. package/ios/GraniteNaverMapViewManager.m +67 -0
  78. package/ios/GraniteNaverMapViewManager.swift +133 -0
  79. package/ios/GraniteNaverMapViewWrapper.swift +215 -0
  80. package/ios/builtinProvider/BuiltInNaverMapProvider.swift +489 -0
  81. package/ios/builtinProvider/GraniteNaverMapMarkerData.swift +66 -0
  82. package/ios/builtinProvider/NMFMarker+Extension.swift +65 -0
  83. package/ios/builtinProvider/RCTConvert+NMFMapView.h +17 -0
  84. package/ios/builtinProvider/RCTConvert+NMFMapView.m +67 -0
  85. package/package.json +103 -0
  86. package/src/NaverMapView.tsx +168 -0
  87. package/src/index.tsx +3 -0
  88. package/src/internals/colorUtils.ts +697 -0
  89. package/src/internals/context.ts +14 -0
  90. package/src/internals/id.ts +9 -0
  91. package/src/internals/useMapOverlay.ts +59 -0
  92. package/src/internals/usePreservedReference.ts +41 -0
  93. package/src/overlays/ArrowheadPath.ts +71 -0
  94. package/src/overlays/Circle.ts +68 -0
  95. package/src/overlays/GroundOverlay.ts +62 -0
  96. package/src/overlays/InfoWindow.ts +68 -0
  97. package/src/overlays/Marker.ts +83 -0
  98. package/src/overlays/Path.ts +87 -0
  99. package/src/overlays/Polygon.ts +83 -0
  100. package/src/overlays/Polyline.ts +93 -0
  101. package/src/overlays/index.ts +23 -0
  102. package/src/specs/GraniteNaverMapViewNativeComponent.ts +352 -0
  103. package/src/types/Coord.ts +4 -0
  104. package/src/types/index.ts +78 -0
@@ -0,0 +1,215 @@
1
+ //
2
+ // GraniteNaverMapViewWrapper.swift
3
+ // granite-naver-map
4
+ //
5
+ // Provider-based wrapper for Old Architecture (no direct NMapsMap dependency)
6
+ //
7
+
8
+ import React
9
+ import UIKit
10
+
11
+ class GraniteNaverMapView: UIView {
12
+ weak var bridge: RCTBridge?
13
+
14
+ @objc var onInitialized: RCTDirectEventBlock?
15
+ @objc var onCameraChange: RCTDirectEventBlock?
16
+ @objc var onTouch: RCTDirectEventBlock?
17
+ @objc var onMapClick: RCTDirectEventBlock?
18
+ @objc var onMarkerClick: RCTDirectEventBlock?
19
+
20
+ private var provider: GraniteNaverMapProvidable?
21
+ private var mapContentView: UIView?
22
+
23
+ override init(frame: CGRect) {
24
+ super.init(frame: frame)
25
+ setupProvider()
26
+ }
27
+
28
+ required init?(coder: NSCoder) {
29
+ fatalError("init(coder:) has not been implemented")
30
+ }
31
+
32
+ private func setupProvider() {
33
+ // Create a new provider instance for this view
34
+ guard let provider = GraniteNaverMapRegistry.shared.createProvider() else {
35
+ // No provider factory available - show placeholder or error
36
+ let label = UILabel(frame: bounds)
37
+ label.text = "NaverMap provider not registered"
38
+ label.textAlignment = .center
39
+ label.autoresizingMask = [.flexibleWidth, .flexibleHeight]
40
+ addSubview(label)
41
+ return
42
+ }
43
+
44
+ self.provider = provider
45
+ provider.setDelegate(self)
46
+
47
+ let mapView = provider.createMapView(frame: bounds)
48
+ mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
49
+ addSubview(mapView)
50
+ self.mapContentView = mapView
51
+ }
52
+
53
+ // MARK: - Property Setters
54
+
55
+ @objc func setMapType(_ type: Int) {
56
+ guard let mapType = GraniteNaverMapType(rawValue: type) else { return }
57
+ provider?.setMapType(mapType)
58
+ }
59
+
60
+ @objc var mapPadding: UIEdgeInsets = .zero {
61
+ didSet {
62
+ provider?.setMapPadding(mapPadding)
63
+ }
64
+ }
65
+
66
+ @objc var compass: Bool = true {
67
+ didSet {
68
+ provider?.setCompassEnabled(compass)
69
+ }
70
+ }
71
+
72
+ @objc var scaleBar: Bool = true {
73
+ didSet {
74
+ provider?.setScaleBarEnabled(scaleBar)
75
+ }
76
+ }
77
+
78
+ @objc var zoomControl: Bool = true {
79
+ didSet {
80
+ provider?.setZoomControlEnabled(zoomControl)
81
+ }
82
+ }
83
+
84
+ @objc var buildingHeight: Float = 1.0 {
85
+ didSet {
86
+ provider?.setBuildingHeight(buildingHeight)
87
+ }
88
+ }
89
+
90
+ @objc var nightMode: Bool = false {
91
+ didSet {
92
+ provider?.setNightModeEnabled(nightMode)
93
+ }
94
+ }
95
+
96
+ @objc var minZoomLevel: Double = 0 {
97
+ didSet {
98
+ provider?.setMinZoomLevel(minZoomLevel)
99
+ }
100
+ }
101
+
102
+ @objc var maxZoomLevel: Double = 21 {
103
+ didSet {
104
+ provider?.setMaxZoomLevel(maxZoomLevel)
105
+ }
106
+ }
107
+
108
+ @objc var scrollGesturesEnabled: Bool = true {
109
+ didSet {
110
+ provider?.setScrollGesturesEnabled(scrollGesturesEnabled)
111
+ }
112
+ }
113
+
114
+ @objc var tiltGesturesEnabled: Bool = true {
115
+ didSet {
116
+ provider?.setTiltGesturesEnabled(tiltGesturesEnabled)
117
+ }
118
+ }
119
+
120
+ @objc var rotateGesturesEnabled: Bool = true {
121
+ didSet {
122
+ provider?.setRotateGesturesEnabled(rotateGesturesEnabled)
123
+ }
124
+ }
125
+
126
+ @objc var stopGesturesEnabled: Bool = true {
127
+ didSet {
128
+ provider?.setStopGesturesEnabled(stopGesturesEnabled)
129
+ }
130
+ }
131
+
132
+ @objc var tilt: Bool = true {
133
+ didSet {
134
+ provider?.setTiltGesturesEnabled(tilt)
135
+ }
136
+ }
137
+
138
+ @objc var locationTrackingMode: UInt = 0 {
139
+ didSet {
140
+ guard let mode = GraniteNaverMapLocationTrackingMode(rawValue: Int(locationTrackingMode)) else { return }
141
+ provider?.setLocationTrackingMode(mode)
142
+ }
143
+ }
144
+
145
+ @objc var showsMyLocationButton: Bool = false {
146
+ didSet {
147
+ provider?.setLocationButtonEnabled(showsMyLocationButton)
148
+ }
149
+ }
150
+
151
+ @objc var zoomGesturesEnabled: Bool = true {
152
+ didSet {
153
+ provider?.setZoomGesturesEnabled(zoomGesturesEnabled)
154
+ }
155
+ }
156
+
157
+ // MARK: - Commands
158
+
159
+ func animateToCoordinate(_ coord: GraniteNaverMapCoordinate) {
160
+ provider?.animateToCoordinate(coord)
161
+ }
162
+
163
+ func animateToBounds(_ bounds: GraniteNaverMapBounds, padding: CGFloat) {
164
+ provider?.animateToBounds(bounds, padding: padding)
165
+ }
166
+
167
+ func setLayerGroupEnabled(group: String, enabled: Bool) {
168
+ provider?.setLayerGroupEnabled(group: group, enabled: enabled)
169
+ }
170
+
171
+ func addMarker(_ data: ProviderMarkerData) {
172
+ provider?.addMarker(data)
173
+ }
174
+
175
+ func updateMarker(_ data: ProviderMarkerData) {
176
+ provider?.updateMarker(data)
177
+ }
178
+
179
+ func removeMarker(identifier: String) {
180
+ provider?.removeMarker(identifier: identifier)
181
+ }
182
+ }
183
+
184
+ // MARK: - GraniteNaverMapProviderDelegate
185
+
186
+ extension GraniteNaverMapView: GraniteNaverMapProviderDelegate {
187
+ func mapViewDidInitialize() {
188
+ onInitialized?([:])
189
+ }
190
+
191
+ func mapViewDidChangeCamera(position: GraniteNaverMapCameraPosition) {
192
+ onCameraChange?([
193
+ "latitude": position.target.latitude,
194
+ "longitude": position.target.longitude,
195
+ "zoom": position.zoom
196
+ ])
197
+ }
198
+
199
+ func mapViewDidTouch(reason: Int, animated: Bool) {
200
+ onTouch?(["animated": animated, "reason": reason])
201
+ }
202
+
203
+ func mapViewDidClick(x: Double, y: Double, latitude: Double, longitude: Double) {
204
+ onMapClick?([
205
+ "x": x,
206
+ "y": y,
207
+ "latitude": latitude,
208
+ "longitude": longitude
209
+ ])
210
+ }
211
+
212
+ func mapViewDidClickMarker(id: String) {
213
+ onMarkerClick?(["id": id])
214
+ }
215
+ }
@@ -0,0 +1,489 @@
1
+ //
2
+ // BuiltInNaverMapProvider.swift
3
+ // granite-naver-map
4
+ //
5
+ // Built-in provider using NMapsMap SDK
6
+ //
7
+
8
+ import UIKit
9
+ import NMapsMap
10
+
11
+ /// Factory for creating BuiltInNaverMapProvider instances.
12
+ /// Each call to createProvider() returns a new instance.
13
+ @objc public class BuiltInNaverMapProviderFactory: NSObject, GraniteNaverMapProviderFactory {
14
+ @objc public func createProvider() -> GraniteNaverMapProvidable {
15
+ return BuiltInNaverMapProvider()
16
+ }
17
+ }
18
+
19
+ @objc public class BuiltInNaverMapProvider: NSObject, GraniteNaverMapProvidable {
20
+ private var mapView: NMFNaverMapView?
21
+ private weak var delegate: GraniteNaverMapProviderDelegate?
22
+ private var isInitialized = false
23
+
24
+ private var markers: [String: NMFMarker] = [:]
25
+ private var polylines: [String: NMFPolylineOverlay] = [:]
26
+ private var polygons: [String: NMFPolygonOverlay] = [:]
27
+ private var circles: [String: NMFCircleOverlay] = [:]
28
+ private var paths: [String: NMFPath] = [:]
29
+
30
+ // MARK: - GraniteNaverMapProvidable
31
+
32
+ @objc public func createMapView(frame: CGRect) -> UIView {
33
+ let view = NMFNaverMapView(frame: frame)
34
+ self.mapView = view
35
+ setupMap(view)
36
+ return view
37
+ }
38
+
39
+ @objc public func setDelegate(_ delegate: GraniteNaverMapProviderDelegate?) {
40
+ self.delegate = delegate
41
+ }
42
+
43
+ private func setupMap(_ view: NMFNaverMapView) {
44
+ view.mapView.touchDelegate = self
45
+ view.mapView.addCameraDelegate(delegate: self)
46
+ }
47
+
48
+ // MARK: - Camera
49
+
50
+ @objc public func moveCamera(to position: GraniteNaverMapCameraPosition, animated: Bool) {
51
+ guard let mapView = mapView else { return }
52
+ let cameraPosition = NMFCameraPosition(
53
+ NMGLatLng(lat: position.target.latitude, lng: position.target.longitude),
54
+ zoom: position.zoom,
55
+ tilt: position.tilt,
56
+ heading: position.bearing
57
+ )
58
+ let update = NMFCameraUpdate(position: cameraPosition)
59
+ if animated {
60
+ update.animation = .easeIn
61
+ }
62
+ mapView.mapView.moveCamera(update)
63
+ }
64
+
65
+ @objc public func animateToCoordinate(_ coordinate: GraniteNaverMapCoordinate) {
66
+ guard let mapView = mapView else { return }
67
+ let cameraUpdate = NMFCameraUpdate(scrollTo: NMGLatLng(lat: coordinate.latitude, lng: coordinate.longitude))
68
+ cameraUpdate.animation = .easeIn
69
+ mapView.mapView.moveCamera(cameraUpdate)
70
+ }
71
+
72
+ @objc public func animateToBounds(_ bounds: GraniteNaverMapBounds, padding: CGFloat) {
73
+ guard let mapView = mapView else { return }
74
+ let nmgBounds = NMGLatLngBounds(
75
+ southWest: NMGLatLng(lat: bounds.southWest.latitude, lng: bounds.southWest.longitude),
76
+ northEast: NMGLatLng(lat: bounds.northEast.latitude, lng: bounds.northEast.longitude)
77
+ )
78
+ let cameraUpdate = NMFCameraUpdate(fit: nmgBounds, padding: padding)
79
+ cameraUpdate.animation = .easeIn
80
+ mapView.mapView.moveCamera(cameraUpdate)
81
+ }
82
+
83
+ // MARK: - Map Properties
84
+
85
+ @objc public func setMapType(_ type: GraniteNaverMapType) {
86
+ guard let mapView = mapView, let nmfType = NMFMapType(rawValue: type.rawValue) else { return }
87
+ mapView.mapView.mapType = nmfType
88
+ }
89
+
90
+ @objc public func setMapPadding(_ padding: UIEdgeInsets) {
91
+ mapView?.mapView.contentInset = padding
92
+ }
93
+
94
+ @objc public func setCompassEnabled(_ enabled: Bool) {
95
+ mapView?.showCompass = enabled
96
+ }
97
+
98
+ @objc public func setScaleBarEnabled(_ enabled: Bool) {
99
+ mapView?.showScaleBar = enabled
100
+ }
101
+
102
+ @objc public func setZoomControlEnabled(_ enabled: Bool) {
103
+ mapView?.showZoomControls = enabled
104
+ }
105
+
106
+ @objc public func setLocationButtonEnabled(_ enabled: Bool) {
107
+ mapView?.showLocationButton = enabled
108
+ }
109
+
110
+ @objc public func setBuildingHeight(_ height: Float) {
111
+ mapView?.mapView.buildingHeight = height
112
+ }
113
+
114
+ @objc public func setNightModeEnabled(_ enabled: Bool) {
115
+ mapView?.mapView.isNightModeEnabled = enabled
116
+ }
117
+
118
+ @objc public func setMinZoomLevel(_ level: Double) {
119
+ mapView?.mapView.minZoomLevel = level
120
+ }
121
+
122
+ @objc public func setMaxZoomLevel(_ level: Double) {
123
+ mapView?.mapView.maxZoomLevel = level
124
+ }
125
+
126
+ @objc public func setScrollGesturesEnabled(_ enabled: Bool) {
127
+ mapView?.mapView.isScrollGestureEnabled = enabled
128
+ }
129
+
130
+ @objc public func setZoomGesturesEnabled(_ enabled: Bool) {
131
+ mapView?.mapView.isZoomGestureEnabled = enabled
132
+ }
133
+
134
+ @objc public func setTiltGesturesEnabled(_ enabled: Bool) {
135
+ mapView?.mapView.isTiltGestureEnabled = enabled
136
+ }
137
+
138
+ @objc public func setRotateGesturesEnabled(_ enabled: Bool) {
139
+ mapView?.mapView.isRotateGestureEnabled = enabled
140
+ }
141
+
142
+ @objc public func setStopGesturesEnabled(_ enabled: Bool) {
143
+ mapView?.mapView.isStopGestureEnabled = enabled
144
+ }
145
+
146
+ @objc public func setLocationTrackingMode(_ mode: GraniteNaverMapLocationTrackingMode) {
147
+ guard let mapView = mapView else { return }
148
+ mapView.mapView.positionMode = NMFMyPositionMode(rawValue: UInt(mode.rawValue)) ?? .disabled
149
+ }
150
+
151
+ @objc public func setLayerGroupEnabled(group: String, enabled: Bool) {
152
+ guard let mapView = mapView else { return }
153
+ let layerGroups: [String: String] = [
154
+ "building": NMF_LAYER_GROUP_BUILDING,
155
+ "ctt": NMF_LAYER_GROUP_TRAFFIC,
156
+ "transit": NMF_LAYER_GROUP_TRANSIT,
157
+ "bike": NMF_LAYER_GROUP_BICYCLE,
158
+ "mountain": NMF_LAYER_GROUP_MOUNTAIN,
159
+ "landparcel": NMF_LAYER_GROUP_CADASTRAL
160
+ ]
161
+ guard let layerGroup = layerGroups[group] else { return }
162
+ mapView.mapView.setLayerGroup(layerGroup, isEnabled: enabled)
163
+ }
164
+
165
+ // MARK: - Markers
166
+
167
+ @objc public func addMarker(_ data: ProviderMarkerData) {
168
+ guard let mapView = mapView else { return }
169
+
170
+ let marker = NMFMarker()
171
+ marker.position = NMGLatLng(lat: data.coordinate.latitude, lng: data.coordinate.longitude)
172
+ marker.width = CGFloat(data.width)
173
+ marker.height = CGFloat(data.height)
174
+ marker.zIndex = data.zIndex
175
+ marker.angle = CGFloat(data.rotation)
176
+ marker.isFlat = data.flat
177
+ marker.alpha = CGFloat(data.alpha)
178
+
179
+ marker.mapView = mapView.mapView
180
+ markers[data.identifier] = marker
181
+
182
+ let identifier = data.identifier
183
+ marker.touchHandler = { [weak self] _ -> Bool in
184
+ self?.delegate?.mapViewDidClickMarker(id: identifier)
185
+ return true
186
+ }
187
+
188
+ if !data.image.isEmpty {
189
+ loadMarkerImage(marker: marker, image: data.image)
190
+ }
191
+ }
192
+
193
+ @objc public func updateMarker(_ data: ProviderMarkerData) {
194
+ guard let marker = markers[data.identifier] else { return }
195
+
196
+ marker.position = NMGLatLng(lat: data.coordinate.latitude, lng: data.coordinate.longitude)
197
+ marker.width = CGFloat(data.width)
198
+ marker.height = CGFloat(data.height)
199
+ marker.zIndex = data.zIndex
200
+ marker.angle = CGFloat(data.rotation)
201
+ marker.isFlat = data.flat
202
+ marker.alpha = CGFloat(data.alpha)
203
+
204
+ if !data.image.isEmpty {
205
+ loadMarkerImage(marker: marker, image: data.image)
206
+ }
207
+ }
208
+
209
+ @objc public func removeMarker(identifier: String) {
210
+ guard let marker = markers[identifier] else { return }
211
+ marker.mapView = nil
212
+ markers[identifier] = nil
213
+ }
214
+
215
+ private func loadMarkerImage(marker: NMFMarker, image: String) {
216
+ if image.hasPrefix("http://") || image.hasPrefix("https://") {
217
+ guard let url = URL(string: image) else { return }
218
+ Task {
219
+ guard let (data, _) = try? await URLSession.shared.data(from: url),
220
+ let uiImage = UIImage(data: data) else { return }
221
+ await MainActor.run {
222
+ marker.iconImage = NMFOverlayImage(image: uiImage)
223
+ }
224
+ }
225
+ } else {
226
+ marker.iconImage = NMFOverlayImage(name: image)
227
+ }
228
+ }
229
+
230
+ // MARK: - Polylines
231
+
232
+ private func colorFromInt(_ value: Int) -> UIColor {
233
+ let uValue = UInt32(bitPattern: Int32(truncatingIfNeeded: value))
234
+ let alpha = CGFloat((uValue >> 24) & 0xFF) / 255.0
235
+ let red = CGFloat((uValue >> 16) & 0xFF) / 255.0
236
+ let green = CGFloat((uValue >> 8) & 0xFF) / 255.0
237
+ let blue = CGFloat(uValue & 0xFF) / 255.0
238
+ return UIColor(red: red, green: green, blue: blue, alpha: alpha)
239
+ }
240
+
241
+ private func lineCapType(_ value: Int) -> NMFOverlayLineCap {
242
+ switch value {
243
+ case 1: return .round
244
+ case 2: return .square
245
+ default: return .butt
246
+ }
247
+ }
248
+
249
+ private func lineJoinType(_ value: Int) -> NMFOverlayLineJoin {
250
+ switch value {
251
+ case 1: return .miter
252
+ case 2: return .round
253
+ default: return .bevel
254
+ }
255
+ }
256
+
257
+ @objc public func addPolyline(_ data: ProviderPolylineData) {
258
+ guard let mapView = mapView else { return }
259
+
260
+ let coords = data.coordinates.map { NMGLatLng(lat: $0.latitude, lng: $0.longitude) }
261
+ guard coords.count >= 2 else { return }
262
+
263
+ let polyline = NMFPolylineOverlay(NMGLineString(points: coords))
264
+ polyline?.width = CGFloat(data.strokeWidth)
265
+ polyline?.color = colorFromInt(data.strokeColor)
266
+ polyline?.zIndex = data.zIndex
267
+ polyline?.capType = lineCapType(data.lineCap)
268
+ polyline?.joinType = lineJoinType(data.lineJoin)
269
+
270
+ if !data.pattern.isEmpty {
271
+ polyline?.pattern = data.pattern.map { NSNumber(value: $0) }
272
+ }
273
+
274
+ polyline?.mapView = mapView.mapView
275
+ if let polyline = polyline {
276
+ polylines[data.identifier] = polyline
277
+ }
278
+ }
279
+
280
+ @objc public func updatePolyline(_ data: ProviderPolylineData) {
281
+ guard let polyline = polylines[data.identifier] else { return }
282
+
283
+ let coords = data.coordinates.map { NMGLatLng(lat: $0.latitude, lng: $0.longitude) }
284
+ if coords.count >= 2 {
285
+ polyline.line = NMGLineString(points: coords)
286
+ }
287
+ polyline.width = CGFloat(data.strokeWidth)
288
+ polyline.color = colorFromInt(data.strokeColor)
289
+ polyline.zIndex = data.zIndex
290
+ polyline.capType = lineCapType(data.lineCap)
291
+ polyline.joinType = lineJoinType(data.lineJoin)
292
+
293
+ if !data.pattern.isEmpty {
294
+ polyline.pattern = data.pattern.map { NSNumber(value: $0) }
295
+ }
296
+ }
297
+
298
+ @objc public func removePolyline(identifier: String) {
299
+ guard let polyline = polylines[identifier] else { return }
300
+ polyline.mapView = nil
301
+ polylines[identifier] = nil
302
+ }
303
+
304
+ // MARK: - Polygons
305
+
306
+ @objc public func addPolygon(_ data: ProviderPolygonData) {
307
+ guard let mapView = mapView else { return }
308
+
309
+ let coords = data.coordinates.map { NMGLatLng(lat: $0.latitude, lng: $0.longitude) }
310
+ guard coords.count >= 3 else { return }
311
+
312
+ let exteriorRing = NMGLineString(points: coords)
313
+
314
+ let nmgPolygon: NMGPolygon<AnyObject>
315
+ if data.holes.isEmpty {
316
+ nmgPolygon = unsafeBitCast(NMGPolygon(ring: exteriorRing), to: NMGPolygon<AnyObject>.self)
317
+ } else {
318
+ let interiorRings = data.holes.map { hole in
319
+ NMGLineString(points: hole.map { NMGLatLng(lat: $0.latitude, lng: $0.longitude) })
320
+ }
321
+ nmgPolygon = unsafeBitCast(NMGPolygon(ring: exteriorRing, interiorRings: interiorRings), to: NMGPolygon<AnyObject>.self)
322
+ }
323
+
324
+ let polygon = NMFPolygonOverlay(nmgPolygon)
325
+ polygon?.fillColor = colorFromInt(data.fillColor)
326
+ polygon?.outlineColor = colorFromInt(data.strokeColor)
327
+ polygon?.outlineWidth = UInt(data.strokeWidth)
328
+ polygon?.zIndex = data.zIndex
329
+ polygon?.mapView = mapView.mapView
330
+ if let polygon = polygon {
331
+ polygons[data.identifier] = polygon
332
+ }
333
+ }
334
+
335
+ @objc public func updatePolygon(_ data: ProviderPolygonData) {
336
+ guard let polygon = polygons[data.identifier] else { return }
337
+
338
+ let coords = data.coordinates.map { NMGLatLng(lat: $0.latitude, lng: $0.longitude) }
339
+ if coords.count >= 3 {
340
+ let exteriorRing = NMGLineString(points: coords)
341
+
342
+ if data.holes.isEmpty {
343
+ let nmgPolygon = NMGPolygon(ring: exteriorRing)
344
+ polygon.polygon = unsafeBitCast(nmgPolygon, to: NMGPolygon<AnyObject>.self)
345
+ } else {
346
+ let interiorRings = data.holes.map { hole in
347
+ NMGLineString(points: hole.map { NMGLatLng(lat: $0.latitude, lng: $0.longitude) })
348
+ }
349
+ let nmgPolygon = NMGPolygon(ring: exteriorRing, interiorRings: interiorRings)
350
+ polygon.polygon = unsafeBitCast(nmgPolygon, to: NMGPolygon<AnyObject>.self)
351
+ }
352
+ }
353
+
354
+ polygon.fillColor = colorFromInt(data.fillColor)
355
+ polygon.outlineColor = colorFromInt(data.strokeColor)
356
+ polygon.outlineWidth = UInt(data.strokeWidth)
357
+ polygon.zIndex = data.zIndex
358
+ }
359
+
360
+ @objc public func removePolygon(identifier: String) {
361
+ guard let polygon = polygons[identifier] else { return }
362
+ polygon.mapView = nil
363
+ polygons[identifier] = nil
364
+ }
365
+
366
+ // MARK: - Circles
367
+
368
+ @objc public func addCircle(_ data: ProviderCircleData) {
369
+ guard let mapView = mapView else { return }
370
+
371
+ let circle = NMFCircleOverlay(NMGLatLng(lat: data.center.latitude, lng: data.center.longitude), radius: data.radius)
372
+ circle.fillColor = colorFromInt(data.fillColor)
373
+ circle.outlineColor = colorFromInt(data.strokeColor)
374
+ circle.outlineWidth = Double(data.strokeWidth)
375
+ circle.zIndex = data.zIndex
376
+ circle.mapView = mapView.mapView
377
+ circles[data.identifier] = circle
378
+ }
379
+
380
+ @objc public func updateCircle(_ data: ProviderCircleData) {
381
+ guard let circle = circles[data.identifier] else { return }
382
+
383
+ circle.center = NMGLatLng(lat: data.center.latitude, lng: data.center.longitude)
384
+ circle.radius = data.radius
385
+ circle.fillColor = colorFromInt(data.fillColor)
386
+ circle.outlineColor = colorFromInt(data.strokeColor)
387
+ circle.outlineWidth = Double(data.strokeWidth)
388
+ circle.zIndex = data.zIndex
389
+ }
390
+
391
+ @objc public func removeCircle(identifier: String) {
392
+ guard let circle = circles[identifier] else { return }
393
+ circle.mapView = nil
394
+ circles[identifier] = nil
395
+ }
396
+
397
+ // MARK: - Paths
398
+
399
+ @objc public func addPath(_ data: ProviderPathData) {
400
+ guard let mapView = mapView else { return }
401
+
402
+ let coords = data.coordinates.map { NMGLatLng(lat: $0.latitude, lng: $0.longitude) }
403
+ guard coords.count >= 2 else { return }
404
+
405
+ let path = NMFPath(points: coords)
406
+ path?.width = CGFloat(data.width)
407
+ path?.outlineWidth = CGFloat(data.outlineWidth)
408
+ path?.color = colorFromInt(data.color)
409
+ path?.outlineColor = colorFromInt(data.outlineColor)
410
+ path?.passedColor = colorFromInt(data.passedColor)
411
+ path?.passedOutlineColor = colorFromInt(data.passedOutlineColor)
412
+ path?.progress = Double(data.progress)
413
+ path?.zIndex = data.zIndex
414
+
415
+ if !data.patternImage.isEmpty {
416
+ path?.patternIcon = NMFOverlayImage(name: data.patternImage)
417
+ path?.patternInterval = UInt(data.patternInterval)
418
+ }
419
+
420
+ path?.mapView = mapView.mapView
421
+ if let path = path {
422
+ paths[data.identifier] = path
423
+ }
424
+ }
425
+
426
+ @objc public func updatePath(_ data: ProviderPathData) {
427
+ guard let path = paths[data.identifier] else { return }
428
+
429
+ let coords = data.coordinates.map { NMGLatLng(lat: $0.latitude, lng: $0.longitude) }
430
+ if coords.count >= 2 {
431
+ path.path = NMGLineString(points: coords)
432
+ }
433
+ path.width = CGFloat(data.width)
434
+ path.outlineWidth = CGFloat(data.outlineWidth)
435
+ path.color = colorFromInt(data.color)
436
+ path.outlineColor = colorFromInt(data.outlineColor)
437
+ path.passedColor = colorFromInt(data.passedColor)
438
+ path.passedOutlineColor = colorFromInt(data.passedOutlineColor)
439
+ path.progress = Double(data.progress)
440
+ path.zIndex = data.zIndex
441
+
442
+ if !data.patternImage.isEmpty {
443
+ path.patternIcon = NMFOverlayImage(name: data.patternImage)
444
+ path.patternInterval = UInt(data.patternInterval)
445
+ }
446
+ }
447
+
448
+ @objc public func removePath(identifier: String) {
449
+ guard let path = paths[identifier] else { return }
450
+ path.mapView = nil
451
+ paths[identifier] = nil
452
+ }
453
+
454
+ // MARK: - Internal
455
+
456
+ func notifyInitialized() {
457
+ if !isInitialized {
458
+ isInitialized = true
459
+ delegate?.mapViewDidInitialize()
460
+ }
461
+ }
462
+ }
463
+
464
+ // MARK: - NMFMapViewTouchDelegate
465
+
466
+ extension BuiltInNaverMapProvider: NMFMapViewTouchDelegate {
467
+ public func mapView(_ mapView: NMFMapView, didTapMap latlng: NMGLatLng, point: CGPoint) {
468
+ delegate?.mapViewDidClick(x: point.x, y: point.y, latitude: latlng.lat, longitude: latlng.lng)
469
+ }
470
+ }
471
+
472
+ // MARK: - NMFMapViewCameraDelegate
473
+
474
+ extension BuiltInNaverMapProvider: NMFMapViewCameraDelegate {
475
+ public func mapView(_ mapView: NMFMapView, cameraWillChangeByReason reason: Int, animated: Bool) {
476
+ delegate?.mapViewDidTouch(reason: reason, animated: animated)
477
+ }
478
+
479
+ public func mapViewCameraIdle(_ mapView: NMFMapView) {
480
+ let position = mapView.cameraPosition
481
+ let cameraPosition = GraniteNaverMapCameraPosition(
482
+ target: GraniteNaverMapCoordinate(latitude: position.target.lat, longitude: position.target.lng),
483
+ zoom: position.zoom,
484
+ tilt: position.tilt,
485
+ bearing: position.heading
486
+ )
487
+ delegate?.mapViewDidChangeCamera(position: cameraPosition)
488
+ }
489
+ }