@maydon_tech/react-native-nitro-maps 0.1.3 → 0.2.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 +1 -1
- package/NitroMap.podspec +1 -1
- package/README.md +82 -9
- package/android/CMakeLists.txt +4 -1
- package/android/gradle.properties +4 -4
- package/android/src/main/cpp/ClusterEngineJNI.cpp +198 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/NitroMap.kt +397 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/NitroMapConfig.kt +53 -0
- package/android/src/main/{java → kotlin}/com/margelo/nitro/nitromap/NitroMapPackage.kt +4 -4
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/NitroMapView.kt +73 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/UserLocationManager.kt +295 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/clustering/ClusterIconRenderer.kt +111 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/clustering/ClusteringManager.kt +104 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/clustering/NitroClusterEngine.kt +166 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/markers/MarkerIconFactory.kt +303 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/markers/MarkerSelectionHandler.kt +72 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/markers/PriceMarkerRenderer.kt +159 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/MapProviderFactory.kt +24 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/MapProviderInterface.kt +128 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapDelegate.kt +317 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapProvider+Clustering.kt +524 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapProvider+Markers.kt +358 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapProvider+Overlays.kt +272 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapProvider+UserLocation.kt +296 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapProvider.kt +815 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/MarkerTagData.kt +19 -0
- package/ios/Clustering/ClusterIconRenderer.swift +3 -3
- package/ios/Location/NitroLocationManager.swift +116 -0
- package/ios/MarkerRenderer/MarkerIconFactory.swift +1 -3
- package/ios/MarkerRenderer/PriceMarkerRenderer.swift +10 -6
- package/ios/NitroMap.swift +279 -13
- package/ios/NitroMapConfig/NitroMapConfig.swift +45 -0
- package/ios/Providers/{GoogleMapDelegate.swift → Google/GoogleMapDelegate.swift} +48 -23
- package/ios/Providers/Google/GoogleMapProvider+Camera.swift +180 -0
- package/ios/Providers/Google/GoogleMapProvider+Clustering.swift +541 -0
- package/ios/Providers/Google/GoogleMapProvider+Markers.swift +270 -0
- package/ios/Providers/Google/GoogleMapProvider+Overlays.swift +245 -0
- package/ios/Providers/Google/GoogleMapProvider+UserLocation.swift +180 -0
- package/ios/Providers/Google/GoogleMapProvider.swift +342 -0
- package/ios/Providers/MapProviderFactory.swift +17 -0
- package/ios/Providers/MapProviderProtocol.swift +48 -1
- package/ios/Shared/ClusterConfig+Factory.swift +2 -2
- package/ios/Shared/MapStyleProvider.swift +6 -4
- package/ios/Shared/MarkerSelectionHandler.swift +4 -1
- package/ios/Utils/ColorValueExtension.swift +46 -67
- package/lib/module/components/ImageMarker.js +39 -29
- package/lib/module/components/ImageMarker.js.map +1 -1
- package/lib/module/components/Marker.js +118 -0
- package/lib/module/components/Marker.js.map +1 -0
- package/lib/module/components/NitroCircle.js +92 -0
- package/lib/module/components/NitroCircle.js.map +1 -0
- package/lib/module/components/NitroMap.js +216 -76
- package/lib/module/components/NitroMap.js.map +1 -1
- package/lib/module/components/NitroPolygon.js +135 -0
- package/lib/module/components/NitroPolygon.js.map +1 -0
- package/lib/module/components/NitroPolyline.js +115 -0
- package/lib/module/components/NitroPolyline.js.map +1 -0
- package/lib/module/components/PriceMarker.js +16 -29
- package/lib/module/components/PriceMarker.js.map +1 -1
- package/lib/module/context/NitroMapContext.js.map +1 -1
- package/lib/module/hooks/useNitroCircle.js +18 -0
- package/lib/module/hooks/useNitroCircle.js.map +1 -0
- package/lib/module/hooks/useNitroMarker.js +26 -9
- package/lib/module/hooks/useNitroMarker.js.map +1 -1
- package/lib/module/hooks/useNitroOverlay.js +59 -0
- package/lib/module/hooks/useNitroOverlay.js.map +1 -0
- package/lib/module/hooks/useNitroPolygon.js +18 -0
- package/lib/module/hooks/useNitroPolygon.js.map +1 -0
- package/lib/module/hooks/useNitroPolyline.js +18 -0
- package/lib/module/hooks/useNitroPolyline.js.map +1 -0
- package/lib/module/index.js +5 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/types/overlay.js +4 -0
- package/lib/module/types/overlay.js.map +1 -0
- package/lib/module/types/theme.js +4 -0
- package/lib/module/types/theme.js.map +1 -0
- package/lib/module/utils/colors.js +41 -13
- package/lib/module/utils/colors.js.map +1 -1
- package/lib/module/utils/validation.js +45 -0
- package/lib/module/utils/validation.js.map +1 -0
- package/lib/typescript/src/components/ImageMarker.d.ts.map +1 -1
- package/lib/typescript/src/components/Marker.d.ts +34 -0
- package/lib/typescript/src/components/Marker.d.ts.map +1 -0
- package/lib/typescript/src/components/NitroCircle.d.ts +70 -0
- package/lib/typescript/src/components/NitroCircle.d.ts.map +1 -0
- package/lib/typescript/src/components/NitroMap.d.ts +60 -3
- package/lib/typescript/src/components/NitroMap.d.ts.map +1 -1
- package/lib/typescript/src/components/NitroPolygon.d.ts +86 -0
- package/lib/typescript/src/components/NitroPolygon.d.ts.map +1 -0
- package/lib/typescript/src/components/NitroPolyline.d.ts +84 -0
- package/lib/typescript/src/components/NitroPolyline.d.ts.map +1 -0
- package/lib/typescript/src/components/PriceMarker.d.ts +0 -5
- package/lib/typescript/src/components/PriceMarker.d.ts.map +1 -1
- package/lib/typescript/src/context/NitroMapContext.d.ts +2 -0
- package/lib/typescript/src/context/NitroMapContext.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useNitroCircle.d.ts +7 -0
- package/lib/typescript/src/hooks/useNitroCircle.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useNitroMarker.d.ts +20 -0
- package/lib/typescript/src/hooks/useNitroMarker.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useNitroOverlay.d.ts +26 -0
- package/lib/typescript/src/hooks/useNitroOverlay.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useNitroPolygon.d.ts +7 -0
- package/lib/typescript/src/hooks/useNitroPolygon.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useNitroPolyline.d.ts +7 -0
- package/lib/typescript/src/hooks/useNitroPolyline.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +15 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/specs/NitroMap.nitro.d.ts +248 -6
- package/lib/typescript/src/specs/NitroMap.nitro.d.ts.map +1 -1
- package/lib/typescript/src/types/map.d.ts +34 -4
- package/lib/typescript/src/types/map.d.ts.map +1 -1
- package/lib/typescript/src/types/marker.d.ts +24 -36
- package/lib/typescript/src/types/marker.d.ts.map +1 -1
- package/lib/typescript/src/types/overlay.d.ts +75 -0
- package/lib/typescript/src/types/overlay.d.ts.map +1 -0
- package/lib/typescript/src/types/theme.d.ts +93 -0
- package/lib/typescript/src/types/theme.d.ts.map +1 -0
- package/lib/typescript/src/utils/colors.d.ts +6 -8
- package/lib/typescript/src/utils/colors.d.ts.map +1 -1
- package/lib/typescript/src/utils/validation.d.ts +12 -0
- package/lib/typescript/src/utils/validation.d.ts.map +1 -0
- package/nitrogen/generated/android/c++/JCircleData.hpp +94 -0
- package/nitrogen/generated/android/c++/JClusterConfig.hpp +5 -7
- package/nitrogen/generated/android/c++/JFunc_void_UserLocationChangeEvent.hpp +79 -0
- package/nitrogen/generated/android/c++/JFunc_void_UserTrackingMode.hpp +77 -0
- package/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +76 -0
- package/nitrogen/generated/android/c++/JHybridNitroMapSpec.cpp +328 -21
- package/nitrogen/generated/android/c++/JHybridNitroMapSpec.hpp +53 -2
- package/nitrogen/generated/android/c++/JMarkerAnimation.hpp +3 -6
- package/nitrogen/generated/android/c++/JMarkerData.hpp +15 -3
- package/nitrogen/generated/android/c++/JPolygonData.hpp +149 -0
- package/nitrogen/generated/android/c++/JPolylineData.hpp +113 -0
- package/nitrogen/generated/android/c++/JUserLocationChangeEvent.hpp +70 -0
- package/nitrogen/generated/android/c++/JUserTrackingMode.hpp +62 -0
- package/nitrogen/generated/android/c++/views/JHybridNitroMapStateUpdater.cpp +72 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/CircleData.kt +62 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/ClusterConfig.kt +4 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_UserLocationChangeEvent.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_UserTrackingMode.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_std__string.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/HybridNitroMapSpec.kt +228 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MarkerAnimation.kt +1 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MarkerData.kt +12 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/PolygonData.kt +62 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/PolylineData.kt +62 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/UserLocationChangeEvent.kt +47 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/{ClusterAnimationStyle.kt → UserTrackingMode.kt} +6 -8
- package/nitrogen/generated/android/nitromapOnLoad.cpp +6 -0
- package/nitrogen/generated/ios/NitroMap-Swift-Cxx-Bridge.cpp +24 -0
- package/nitrogen/generated/ios/NitroMap-Swift-Cxx-Bridge.hpp +175 -17
- package/nitrogen/generated/ios/NitroMap-Swift-Cxx-Umbrella.hpp +15 -3
- package/nitrogen/generated/ios/c++/HybridNitroMapSpecSwift.hpp +249 -16
- package/nitrogen/generated/ios/c++/views/HybridNitroMapComponent.mm +90 -5
- package/nitrogen/generated/ios/swift/CircleData.swift +143 -0
- package/nitrogen/generated/ios/swift/ClusterConfig.swift +22 -15
- package/nitrogen/generated/ios/swift/Func_void_UserLocationChangeEvent.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_UserTrackingMode.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_std__string.swift +47 -0
- package/nitrogen/generated/ios/swift/HybridNitroMapSpec.swift +35 -1
- package/nitrogen/generated/ios/swift/HybridNitroMapSpec_cxx.swift +582 -8
- package/nitrogen/generated/ios/swift/MarkerAnimation.swift +4 -8
- package/nitrogen/generated/ios/swift/MarkerData.swift +54 -2
- package/nitrogen/generated/ios/swift/PolygonData.swift +179 -0
- package/nitrogen/generated/ios/swift/PolylineData.swift +155 -0
- package/nitrogen/generated/ios/swift/UserLocationChangeEvent.swift +69 -0
- package/nitrogen/generated/ios/swift/UserTrackingMode.swift +44 -0
- package/nitrogen/generated/shared/c++/CircleData.hpp +113 -0
- package/nitrogen/generated/shared/c++/ClusterConfig.hpp +5 -8
- package/nitrogen/generated/shared/c++/HybridNitroMapSpec.cpp +53 -2
- package/nitrogen/generated/shared/c++/HybridNitroMapSpec.hpp +75 -6
- package/nitrogen/generated/shared/c++/MarkerAnimation.hpp +4 -8
- package/nitrogen/generated/shared/c++/MarkerData.hpp +14 -2
- package/nitrogen/generated/shared/c++/PolygonData.hpp +114 -0
- package/nitrogen/generated/shared/c++/PolylineData.hpp +114 -0
- package/nitrogen/generated/shared/c++/UserLocationChangeEvent.hpp +88 -0
- package/nitrogen/generated/shared/c++/UserTrackingMode.hpp +80 -0
- package/nitrogen/generated/shared/c++/views/HybridNitroMapComponent.cpp +216 -12
- package/nitrogen/generated/shared/c++/views/HybridNitroMapComponent.hpp +23 -1
- package/nitrogen/generated/shared/json/NitroMapConfig.json +18 -1
- package/package.json +36 -5
- package/src/components/ImageMarker.tsx +58 -42
- package/src/components/Marker.tsx +161 -0
- package/src/components/NitroCircle.tsx +183 -0
- package/src/components/NitroMap.tsx +328 -78
- package/src/components/NitroPolygon.tsx +229 -0
- package/src/components/NitroPolyline.tsx +208 -0
- package/src/components/PriceMarker.tsx +23 -48
- package/src/context/NitroMapContext.tsx +4 -0
- package/src/hooks/useNitroCircle.ts +25 -0
- package/src/hooks/useNitroMarker.ts +49 -10
- package/src/hooks/useNitroOverlay.ts +68 -0
- package/src/hooks/useNitroPolygon.ts +25 -0
- package/src/hooks/useNitroPolyline.ts +25 -0
- package/src/index.tsx +23 -2
- package/src/specs/NitroMap.nitro.ts +294 -5
- package/src/types/map.ts +36 -4
- package/src/types/marker.ts +24 -44
- package/src/types/overlay.ts +77 -0
- package/src/types/theme.ts +101 -0
- package/src/utils/colors.ts +48 -16
- package/src/utils/validation.ts +69 -0
- package/android/src/main/java/com/margelo/nitro/nitromap/ClusterIconGenerator.kt +0 -108
- package/android/src/main/java/com/margelo/nitro/nitromap/ColorUtils.kt +0 -63
- package/android/src/main/java/com/margelo/nitro/nitromap/HybridNitroMap.kt +0 -408
- package/android/src/main/java/com/margelo/nitro/nitromap/HybridNitroMapConfig.kt +0 -68
- package/android/src/main/java/com/margelo/nitro/nitromap/MarkerIconCache.kt +0 -176
- package/android/src/main/java/com/margelo/nitro/nitromap/MarkerIconFactory.kt +0 -252
- package/android/src/main/java/com/margelo/nitro/nitromap/clustering/NitroClusterEngine.kt +0 -252
- package/android/src/main/java/com/margelo/nitro/nitromap/clustering/QuadTree.kt +0 -195
- package/android/src/main/java/com/margelo/nitro/nitromap/providers/GoogleMapProvider.kt +0 -912
- package/android/src/main/java/com/margelo/nitro/nitromap/providers/MapProviderInterface.kt +0 -70
- package/cpp/QuadTree.hpp +0 -246
- package/ios/NitroMapConfig/HybridNitroMapConfig.swift +0 -33
- package/ios/Providers/GoogleMapProvider+Camera.swift +0 -164
- package/ios/Providers/GoogleMapProvider.swift +0 -924
- package/nitrogen/generated/android/c++/JClusterAnimationStyle.hpp +0 -68
- package/nitrogen/generated/ios/swift/ClusterAnimationStyle.swift +0 -52
- package/nitrogen/generated/shared/c++/ClusterAnimationStyle.hpp +0 -88
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
package com.margelo.nitro.nitromap
|
|
2
|
-
|
|
3
|
-
import android.content.Context
|
|
4
|
-
import android.graphics.*
|
|
5
|
-
import android.util.Base64
|
|
6
|
-
import com.google.android.gms.maps.model.BitmapDescriptor
|
|
7
|
-
import com.google.android.gms.maps.model.BitmapDescriptorFactory
|
|
8
|
-
import java.net.URL
|
|
9
|
-
import kotlin.math.roundToInt
|
|
10
|
-
|
|
11
|
-
/** Factory for creating marker icons from MarkerData configuration */
|
|
12
|
-
object MarkerIconFactory {
|
|
13
|
-
|
|
14
|
-
private var reactContext: com.facebook.react.bridge.ReactContext? = null
|
|
15
|
-
|
|
16
|
-
/** Set the React context for view capture (call this from HybridNitroMap initialization) */
|
|
17
|
-
fun setReactContext(context: com.facebook.react.bridge.ReactContext?) {
|
|
18
|
-
reactContext = context
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
fun createIcon(context: Context, markerData: MarkerData): BitmapDescriptor? {
|
|
22
|
-
return when (markerData.config.style) {
|
|
23
|
-
MarkerStyle.IMAGE -> createImageMarkerIcon(context, markerData.config.image)
|
|
24
|
-
MarkerStyle.PRICEMARKER ->
|
|
25
|
-
createPriceMarkerStyleIcon(context, markerData.config.priceMarker)
|
|
26
|
-
MarkerStyle.DEFAULT -> null
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
private fun createPriceMarkerStyleIcon(
|
|
31
|
-
context: Context,
|
|
32
|
-
config: PriceMarkerStyle?
|
|
33
|
-
): BitmapDescriptor? {
|
|
34
|
-
config ?: return null
|
|
35
|
-
|
|
36
|
-
// Generate cache key (Flyweight pattern)
|
|
37
|
-
val cacheKey =
|
|
38
|
-
MarkerIconCache.priceMarkerKey(config.price, config.currency, config.selected)
|
|
39
|
-
|
|
40
|
-
// Check cache first
|
|
41
|
-
MarkerIconCache.getPriceMarker(cacheKey)?.let { cachedBitmap ->
|
|
42
|
-
return BitmapDescriptorFactory.fromBitmap(cachedBitmap)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
val density = context.resources.displayMetrics.density
|
|
46
|
-
val priceText = "${config.price} ${config.currency}"
|
|
47
|
-
val fontSize = (config.fontSize ?: 14.0) * density
|
|
48
|
-
val paddingH = (config.paddingHorizontal ?: 12.0) * density
|
|
49
|
-
val paddingV = (config.paddingVertical ?: 8.0) * density
|
|
50
|
-
|
|
51
|
-
val textPaint =
|
|
52
|
-
Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
|
53
|
-
textSize = fontSize.toFloat()
|
|
54
|
-
typeface = Typeface.DEFAULT_BOLD
|
|
55
|
-
textAlign = Paint.Align.LEFT
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Set colors based on selection state
|
|
59
|
-
val bgColor =
|
|
60
|
-
if (config.selected) {
|
|
61
|
-
config.selectedBackgroundColor?.let { ColorUtils.fromVariant(it) }
|
|
62
|
-
?: Color.parseColor("#E53935") // Red for selected
|
|
63
|
-
} else {
|
|
64
|
-
config.backgroundColor?.let { ColorUtils.fromVariant(it) } ?: Color.WHITE
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
val textColor =
|
|
68
|
-
if (config.selected) {
|
|
69
|
-
config.selectedTextColor?.let { ColorUtils.fromVariant(it) } ?: Color.WHITE
|
|
70
|
-
} else {
|
|
71
|
-
config.textColor?.let { ColorUtils.fromVariant(it) } ?: Color.BLACK
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
textPaint.color = textColor
|
|
75
|
-
|
|
76
|
-
val textBounds = Rect()
|
|
77
|
-
textPaint.getTextBounds(priceText, 0, priceText.length, textBounds)
|
|
78
|
-
|
|
79
|
-
val arrowHeight = 8 * density
|
|
80
|
-
val width = textBounds.width() + paddingH * 2
|
|
81
|
-
val height = textBounds.height() + paddingV * 2 + arrowHeight
|
|
82
|
-
val cornerRadius = 8 * density
|
|
83
|
-
|
|
84
|
-
val bitmap =
|
|
85
|
-
Bitmap.createBitmap(
|
|
86
|
-
width.roundToInt(),
|
|
87
|
-
height.roundToInt(),
|
|
88
|
-
Bitmap.Config.ARGB_8888
|
|
89
|
-
)
|
|
90
|
-
val canvas = Canvas(bitmap)
|
|
91
|
-
|
|
92
|
-
val bgPaint =
|
|
93
|
-
Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
|
94
|
-
color = bgColor
|
|
95
|
-
style = Paint.Style.FILL
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Add shadow
|
|
99
|
-
val shadowPaint =
|
|
100
|
-
Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
|
101
|
-
color = Color.BLACK
|
|
102
|
-
alpha = ((config.shadowOpacity ?: 0.2) * 255).toInt()
|
|
103
|
-
maskFilter = BlurMaskFilter((4 * density).toFloat(), BlurMaskFilter.Blur.NORMAL)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
val bubbleRect =
|
|
107
|
-
RectF(
|
|
108
|
-
(2 * density).toFloat(),
|
|
109
|
-
0f,
|
|
110
|
-
(width - 2 * density).toFloat(),
|
|
111
|
-
(height - arrowHeight).toFloat()
|
|
112
|
-
)
|
|
113
|
-
canvas.drawRoundRect(
|
|
114
|
-
bubbleRect.apply { offset(0f, (2 * density).toFloat()) },
|
|
115
|
-
cornerRadius.toFloat(),
|
|
116
|
-
cornerRadius.toFloat(),
|
|
117
|
-
shadowPaint
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
bubbleRect.offset(0f, (-2 * density).toFloat())
|
|
121
|
-
canvas.drawRoundRect(bubbleRect, cornerRadius.toFloat(), cornerRadius.toFloat(), bgPaint)
|
|
122
|
-
|
|
123
|
-
// Draw arrow
|
|
124
|
-
val path =
|
|
125
|
-
Path().apply {
|
|
126
|
-
moveTo((width / 2 - arrowHeight).toFloat(), (height - arrowHeight).toFloat())
|
|
127
|
-
lineTo((width / 2).toFloat(), height.toFloat())
|
|
128
|
-
lineTo((width / 2 + arrowHeight).toFloat(), (height - arrowHeight).toFloat())
|
|
129
|
-
close()
|
|
130
|
-
}
|
|
131
|
-
canvas.drawPath(path, bgPaint)
|
|
132
|
-
|
|
133
|
-
// Draw text
|
|
134
|
-
val textX = paddingH.toFloat()
|
|
135
|
-
val textY = paddingV.toFloat() + textBounds.height()
|
|
136
|
-
canvas.drawText(priceText, textX, textY, textPaint)
|
|
137
|
-
|
|
138
|
-
// Cache the bitmap for reuse
|
|
139
|
-
MarkerIconCache.putPriceMarker(cacheKey, bitmap)
|
|
140
|
-
|
|
141
|
-
return BitmapDescriptorFactory.fromBitmap(bitmap)
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
private fun createImageMarkerIcon(
|
|
145
|
-
context: Context,
|
|
146
|
-
config: ImageMarkerConfig?
|
|
147
|
-
): BitmapDescriptor? {
|
|
148
|
-
config ?: return null
|
|
149
|
-
|
|
150
|
-
val imageSource = config.imageBase64 ?: config.imageUrl ?: ""
|
|
151
|
-
|
|
152
|
-
// Generate cache key (Flyweight pattern)
|
|
153
|
-
val cacheKey =
|
|
154
|
-
MarkerIconCache.imageMarkerKey(
|
|
155
|
-
imageSource,
|
|
156
|
-
config.width,
|
|
157
|
-
config.height,
|
|
158
|
-
config.cornerRadius
|
|
159
|
-
)
|
|
160
|
-
|
|
161
|
-
// Check cache first
|
|
162
|
-
MarkerIconCache.getImageMarker(cacheKey)?.let { cachedBitmap ->
|
|
163
|
-
return BitmapDescriptorFactory.fromBitmap(cachedBitmap)
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
val density = context.resources.displayMetrics.density
|
|
167
|
-
val width = (config.width * density).roundToInt()
|
|
168
|
-
val height = (config.height * density).roundToInt()
|
|
169
|
-
val cornerRadius = config.cornerRadius * density
|
|
170
|
-
val borderWidth = config.borderWidth * density
|
|
171
|
-
|
|
172
|
-
var sourceImage: Bitmap? = null
|
|
173
|
-
|
|
174
|
-
// Try to load image from base64
|
|
175
|
-
config.imageBase64?.let { base64 ->
|
|
176
|
-
try {
|
|
177
|
-
val bytes = Base64.decode(base64, Base64.DEFAULT)
|
|
178
|
-
sourceImage = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
|
|
179
|
-
} catch (e: Exception) {
|
|
180
|
-
e.printStackTrace()
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// Try to load from URL (blocking - should be done on background thread in production)
|
|
185
|
-
if (sourceImage == null) {
|
|
186
|
-
config.imageUrl?.let { urlString ->
|
|
187
|
-
try {
|
|
188
|
-
val url = URL(urlString)
|
|
189
|
-
val connection = url.openConnection()
|
|
190
|
-
connection.connectTimeout = 5000
|
|
191
|
-
connection.readTimeout = 5000
|
|
192
|
-
val inputStream = connection.getInputStream()
|
|
193
|
-
sourceImage = BitmapFactory.decodeStream(inputStream)
|
|
194
|
-
inputStream.close()
|
|
195
|
-
} catch (e: Exception) {
|
|
196
|
-
e.printStackTrace()
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
|
202
|
-
val canvas = Canvas(bitmap)
|
|
203
|
-
|
|
204
|
-
val clipPath =
|
|
205
|
-
Path().apply {
|
|
206
|
-
addRoundRect(
|
|
207
|
-
RectF(0f, 0f, width.toFloat(), height.toFloat()),
|
|
208
|
-
cornerRadius.toFloat(),
|
|
209
|
-
cornerRadius.toFloat(),
|
|
210
|
-
Path.Direction.CW
|
|
211
|
-
)
|
|
212
|
-
}
|
|
213
|
-
canvas.clipPath(clipPath)
|
|
214
|
-
|
|
215
|
-
// Draw image or placeholder
|
|
216
|
-
sourceImage?.let { img ->
|
|
217
|
-
val scaledBitmap = Bitmap.createScaledBitmap(img, width, height, true)
|
|
218
|
-
canvas.drawBitmap(scaledBitmap, 0f, 0f, null)
|
|
219
|
-
if (scaledBitmap != img) scaledBitmap.recycle()
|
|
220
|
-
}
|
|
221
|
-
?: run {
|
|
222
|
-
val placeholderPaint = Paint().apply { color = Color.LTGRAY }
|
|
223
|
-
canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), placeholderPaint)
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// Draw border
|
|
227
|
-
if (borderWidth > 0) {
|
|
228
|
-
val borderPaint =
|
|
229
|
-
Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
|
230
|
-
color = ColorUtils.fromColorValue(config.borderColor)
|
|
231
|
-
style = Paint.Style.STROKE
|
|
232
|
-
strokeWidth = borderWidth.toFloat()
|
|
233
|
-
}
|
|
234
|
-
canvas.drawRoundRect(
|
|
235
|
-
RectF(
|
|
236
|
-
borderWidth.toFloat() / 2,
|
|
237
|
-
borderWidth.toFloat() / 2,
|
|
238
|
-
width - borderWidth.toFloat() / 2,
|
|
239
|
-
height - borderWidth.toFloat() / 2
|
|
240
|
-
),
|
|
241
|
-
cornerRadius.toFloat(),
|
|
242
|
-
cornerRadius.toFloat(),
|
|
243
|
-
borderPaint
|
|
244
|
-
)
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
// Cache the bitmap for reuse
|
|
248
|
-
MarkerIconCache.putImageMarker(cacheKey, bitmap)
|
|
249
|
-
|
|
250
|
-
return BitmapDescriptorFactory.fromBitmap(bitmap)
|
|
251
|
-
}
|
|
252
|
-
}
|
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
package com.margelo.nitro.nitromap.clustering
|
|
2
|
-
|
|
3
|
-
import com.google.android.gms.maps.model.LatLng
|
|
4
|
-
import com.google.android.gms.maps.model.LatLngBounds
|
|
5
|
-
import com.margelo.nitro.nitromap.ClusterConfig
|
|
6
|
-
import com.margelo.nitro.nitromap.MarkerData
|
|
7
|
-
import kotlin.math.*
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Marker point for clustering
|
|
11
|
-
*/
|
|
12
|
-
data class MarkerPoint(
|
|
13
|
-
val id: String,
|
|
14
|
-
val latitude: Double,
|
|
15
|
-
val longitude: Double
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Cluster result containing multiple markers
|
|
20
|
-
*/
|
|
21
|
-
data class Cluster(
|
|
22
|
-
val latitude: Double,
|
|
23
|
-
val longitude: Double,
|
|
24
|
-
val markerIds: List<String>,
|
|
25
|
-
val count: Int
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Full clustering result
|
|
30
|
-
*/
|
|
31
|
-
data class ClusterResult(
|
|
32
|
-
val clusters: List<Cluster>,
|
|
33
|
-
val singleMarkers: List<MarkerPoint>
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* High-performance clustering engine using QuadTree spatial indexing
|
|
38
|
-
* Kotlin port of C++ ClusterEngine
|
|
39
|
-
*/
|
|
40
|
-
class NitroClusterEngine {
|
|
41
|
-
|
|
42
|
-
// QuadTree for O(log n) spatial queries
|
|
43
|
-
private var quadTree: QuadTree? = null
|
|
44
|
-
|
|
45
|
-
// All markers stored by ID
|
|
46
|
-
private val markers = mutableMapOf<String, MarkerPoint>()
|
|
47
|
-
|
|
48
|
-
// Marker data cache for full marker info
|
|
49
|
-
private val markerDataCache = mutableMapOf<String, MarkerData>()
|
|
50
|
-
|
|
51
|
-
// Configuration
|
|
52
|
-
private var clusterRadius = 60.0
|
|
53
|
-
private var minClusterSize = 2
|
|
54
|
-
private var maxZoom = 20.0
|
|
55
|
-
|
|
56
|
-
// MARK: - Configuration
|
|
57
|
-
|
|
58
|
-
fun setClusterRadius(radius: Double) {
|
|
59
|
-
clusterRadius = radius
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
fun setMinClusterSize(size: Int) {
|
|
63
|
-
minClusterSize = size
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
fun setMaxZoom(zoom: Double) {
|
|
67
|
-
maxZoom = zoom
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
fun updateConfig(config: ClusterConfig?) {
|
|
71
|
-
config?.let {
|
|
72
|
-
minClusterSize = it.minimumClusterSize.toInt()
|
|
73
|
-
maxZoom = it.maxZoom
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// MARK: - Marker Management
|
|
78
|
-
|
|
79
|
-
fun addMarker(markerData: MarkerData) {
|
|
80
|
-
val id = markerData.id
|
|
81
|
-
val point = MarkerPoint(
|
|
82
|
-
id = id,
|
|
83
|
-
latitude = markerData.coordinate.latitude,
|
|
84
|
-
longitude = markerData.coordinate.longitude
|
|
85
|
-
)
|
|
86
|
-
markers[id] = point
|
|
87
|
-
markerDataCache[id] = markerData
|
|
88
|
-
rebuildQuadTree()
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
fun addMarkers(markersList: List<MarkerData>) {
|
|
92
|
-
for (marker in markersList) {
|
|
93
|
-
val id = marker.id
|
|
94
|
-
val point = MarkerPoint(
|
|
95
|
-
id = id,
|
|
96
|
-
latitude = marker.coordinate.latitude,
|
|
97
|
-
longitude = marker.coordinate.longitude
|
|
98
|
-
)
|
|
99
|
-
markers[id] = point
|
|
100
|
-
markerDataCache[id] = marker
|
|
101
|
-
}
|
|
102
|
-
rebuildQuadTree()
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
fun removeMarker(id: String) {
|
|
106
|
-
markers.remove(id)
|
|
107
|
-
markerDataCache.remove(id)
|
|
108
|
-
rebuildQuadTree()
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
fun clearMarkers() {
|
|
112
|
-
markers.clear()
|
|
113
|
-
markerDataCache.clear()
|
|
114
|
-
quadTree?.clear()
|
|
115
|
-
quadTree = null
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
fun getMarkerCount(): Int = markers.size
|
|
119
|
-
|
|
120
|
-
fun getMarkerData(id: String): MarkerData? = markerDataCache[id]
|
|
121
|
-
|
|
122
|
-
// MARK: - Clustering
|
|
123
|
-
|
|
124
|
-
fun cluster(
|
|
125
|
-
bounds: LatLngBounds,
|
|
126
|
-
zoom: Float,
|
|
127
|
-
mapWidth: Int,
|
|
128
|
-
mapHeight: Int
|
|
129
|
-
): ClusterResult {
|
|
130
|
-
// At max zoom or beyond, don't cluster
|
|
131
|
-
if (zoom >= maxZoom) {
|
|
132
|
-
return ClusterResult(
|
|
133
|
-
clusters = emptyList(),
|
|
134
|
-
singleMarkers = markers.values.toList()
|
|
135
|
-
)
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
val tree = quadTree ?: return ClusterResult(emptyList(), markers.values.toList())
|
|
139
|
-
|
|
140
|
-
// Calculate cluster radius in coordinate space
|
|
141
|
-
val latSpan = bounds.northeast.latitude - bounds.southwest.latitude
|
|
142
|
-
val lngSpan = bounds.northeast.longitude - bounds.southwest.longitude
|
|
143
|
-
|
|
144
|
-
// Pixels to degrees conversion
|
|
145
|
-
val pixelsPerDegreeLat = if (latSpan > 0) mapHeight / latSpan else 1.0
|
|
146
|
-
val pixelsPerDegreeLng = if (lngSpan > 0) mapWidth / lngSpan else 1.0
|
|
147
|
-
|
|
148
|
-
// Cluster radius in degrees
|
|
149
|
-
val radiusLat = clusterRadius / pixelsPerDegreeLat
|
|
150
|
-
val radiusLng = clusterRadius / pixelsPerDegreeLng
|
|
151
|
-
val avgRadius = (radiusLat + radiusLng) / 2.0
|
|
152
|
-
|
|
153
|
-
// Grid-based clustering for O(n) performance
|
|
154
|
-
val gridSizeLat = radiusLat * 2
|
|
155
|
-
val gridSizeLng = radiusLng * 2
|
|
156
|
-
|
|
157
|
-
// Get visible markers
|
|
158
|
-
val visiblePoints = mutableListOf<QuadPoint>()
|
|
159
|
-
val range = BoundingBox(
|
|
160
|
-
bounds.southwest.longitude,
|
|
161
|
-
bounds.southwest.latitude,
|
|
162
|
-
bounds.northeast.longitude,
|
|
163
|
-
bounds.northeast.latitude
|
|
164
|
-
)
|
|
165
|
-
tree.query(range, visiblePoints)
|
|
166
|
-
|
|
167
|
-
// Track processed markers
|
|
168
|
-
val processed = mutableSetOf<String>()
|
|
169
|
-
val clusters = mutableListOf<Cluster>()
|
|
170
|
-
val singleMarkers = mutableListOf<MarkerPoint>()
|
|
171
|
-
|
|
172
|
-
for (point in visiblePoints) {
|
|
173
|
-
if (processed.contains(point.id)) continue
|
|
174
|
-
|
|
175
|
-
// Find nearby markers
|
|
176
|
-
val nearbyPoints = mutableListOf<QuadPoint>()
|
|
177
|
-
tree.queryRadius(point.x, point.y, avgRadius, nearbyPoints)
|
|
178
|
-
|
|
179
|
-
// Filter to unprocessed points
|
|
180
|
-
val unprocessedNearby = nearbyPoints.filter { !processed.contains(it.id) }
|
|
181
|
-
|
|
182
|
-
if (unprocessedNearby.size >= minClusterSize) {
|
|
183
|
-
// Create cluster
|
|
184
|
-
var sumLat = 0.0
|
|
185
|
-
var sumLng = 0.0
|
|
186
|
-
val markerIds = mutableListOf<String>()
|
|
187
|
-
|
|
188
|
-
for (nearby in unprocessedNearby) {
|
|
189
|
-
sumLng += nearby.x
|
|
190
|
-
sumLat += nearby.y
|
|
191
|
-
markerIds.add(nearby.id)
|
|
192
|
-
processed.add(nearby.id)
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
val count = unprocessedNearby.size
|
|
196
|
-
clusters.add(
|
|
197
|
-
Cluster(
|
|
198
|
-
latitude = sumLat / count,
|
|
199
|
-
longitude = sumLng / count,
|
|
200
|
-
markerIds = markerIds,
|
|
201
|
-
count = count
|
|
202
|
-
)
|
|
203
|
-
)
|
|
204
|
-
} else {
|
|
205
|
-
// Single marker
|
|
206
|
-
processed.add(point.id)
|
|
207
|
-
markers[point.id]?.let { singleMarkers.add(it) }
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
return ClusterResult(clusters, singleMarkers)
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
private fun rebuildQuadTree() {
|
|
215
|
-
if (markers.isEmpty()) {
|
|
216
|
-
quadTree = null
|
|
217
|
-
return
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// Find bounds
|
|
221
|
-
var minLat = Double.MAX_VALUE
|
|
222
|
-
var maxLat = -Double.MAX_VALUE
|
|
223
|
-
var minLng = Double.MAX_VALUE
|
|
224
|
-
var maxLng = -Double.MAX_VALUE
|
|
225
|
-
|
|
226
|
-
for (marker in markers.values) {
|
|
227
|
-
minLat = min(minLat, marker.latitude)
|
|
228
|
-
maxLat = max(maxLat, marker.latitude)
|
|
229
|
-
minLng = min(minLng, marker.longitude)
|
|
230
|
-
maxLng = max(maxLng, marker.longitude)
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// Add padding
|
|
234
|
-
val latPadding = (maxLat - minLat) * 0.1 + 0.001
|
|
235
|
-
val lngPadding = (maxLng - minLng) * 0.1 + 0.001
|
|
236
|
-
|
|
237
|
-
val bounds = BoundingBox(
|
|
238
|
-
minLng - lngPadding,
|
|
239
|
-
minLat - latPadding,
|
|
240
|
-
maxLng + lngPadding,
|
|
241
|
-
maxLat + latPadding
|
|
242
|
-
)
|
|
243
|
-
|
|
244
|
-
quadTree = QuadTree(bounds)
|
|
245
|
-
|
|
246
|
-
for (marker in markers.values) {
|
|
247
|
-
quadTree?.insert(
|
|
248
|
-
QuadPoint(marker.longitude, marker.latitude, marker.id)
|
|
249
|
-
)
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
}
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
package com.margelo.nitro.nitromap.clustering
|
|
2
|
-
|
|
3
|
-
import kotlin.math.sqrt
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Point in 2D space with optional ID
|
|
7
|
-
*/
|
|
8
|
-
data class QuadPoint(
|
|
9
|
-
val x: Double,
|
|
10
|
-
val y: Double,
|
|
11
|
-
val id: String = ""
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Bounding box for spatial queries
|
|
16
|
-
*/
|
|
17
|
-
data class BoundingBox(
|
|
18
|
-
val minX: Double,
|
|
19
|
-
val minY: Double,
|
|
20
|
-
val maxX: Double,
|
|
21
|
-
val maxY: Double
|
|
22
|
-
) {
|
|
23
|
-
fun contains(point: QuadPoint): Boolean {
|
|
24
|
-
return point.x >= minX && point.x <= maxX &&
|
|
25
|
-
point.y >= minY && point.y <= maxY
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
fun intersects(other: BoundingBox): Boolean {
|
|
29
|
-
return !(other.minX > maxX || other.maxX < minX ||
|
|
30
|
-
other.minY > maxY || other.maxY < minY)
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* High-performance QuadTree for spatial indexing
|
|
36
|
-
* O(log n) point insertion and range queries
|
|
37
|
-
*/
|
|
38
|
-
class QuadTree(
|
|
39
|
-
private val boundary: BoundingBox,
|
|
40
|
-
private val capacity: Int = MAX_POINTS,
|
|
41
|
-
private val depth: Int = 0
|
|
42
|
-
) {
|
|
43
|
-
companion object {
|
|
44
|
-
const val MAX_POINTS = 4
|
|
45
|
-
const val MAX_DEPTH = 12
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
private val points = mutableListOf<QuadPoint>()
|
|
49
|
-
private var divided = false
|
|
50
|
-
private var northwest: QuadTree? = null
|
|
51
|
-
private var northeast: QuadTree? = null
|
|
52
|
-
private var southwest: QuadTree? = null
|
|
53
|
-
private var southeast: QuadTree? = null
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Insert a point into the tree
|
|
57
|
-
*/
|
|
58
|
-
fun insert(point: QuadPoint): Boolean {
|
|
59
|
-
if (!boundary.contains(point)) {
|
|
60
|
-
return false
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (points.size < capacity || depth >= MAX_DEPTH) {
|
|
64
|
-
points.add(point)
|
|
65
|
-
return true
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (!divided) {
|
|
69
|
-
subdivide()
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return northwest?.insert(point) == true ||
|
|
73
|
-
northeast?.insert(point) == true ||
|
|
74
|
-
southwest?.insert(point) == true ||
|
|
75
|
-
southeast?.insert(point) == true
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Query points within a bounding box
|
|
80
|
-
*/
|
|
81
|
-
fun query(range: BoundingBox, found: MutableList<QuadPoint>) {
|
|
82
|
-
if (!boundary.intersects(range)) {
|
|
83
|
-
return
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
for (point in points) {
|
|
87
|
-
if (range.contains(point)) {
|
|
88
|
-
found.add(point)
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (divided) {
|
|
93
|
-
northwest?.query(range, found)
|
|
94
|
-
northeast?.query(range, found)
|
|
95
|
-
southwest?.query(range, found)
|
|
96
|
-
southeast?.query(range, found)
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Query points within a circular radius
|
|
102
|
-
*/
|
|
103
|
-
fun queryRadius(
|
|
104
|
-
centerX: Double,
|
|
105
|
-
centerY: Double,
|
|
106
|
-
radius: Double,
|
|
107
|
-
found: MutableList<QuadPoint>
|
|
108
|
-
) {
|
|
109
|
-
val range = BoundingBox(
|
|
110
|
-
centerX - radius,
|
|
111
|
-
centerY - radius,
|
|
112
|
-
centerX + radius,
|
|
113
|
-
centerY + radius
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
if (!boundary.intersects(range)) {
|
|
117
|
-
return
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
val radiusSq = radius * radius
|
|
121
|
-
for (point in points) {
|
|
122
|
-
val dx = point.x - centerX
|
|
123
|
-
val dy = point.y - centerY
|
|
124
|
-
if (dx * dx + dy * dy <= radiusSq) {
|
|
125
|
-
found.add(point)
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
if (divided) {
|
|
130
|
-
northwest?.queryRadius(centerX, centerY, radius, found)
|
|
131
|
-
northeast?.queryRadius(centerX, centerY, radius, found)
|
|
132
|
-
southwest?.queryRadius(centerX, centerY, radius, found)
|
|
133
|
-
southeast?.queryRadius(centerX, centerY, radius, found)
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Get all points in the tree
|
|
139
|
-
*/
|
|
140
|
-
fun getAllPoints(allPoints: MutableList<QuadPoint>) {
|
|
141
|
-
allPoints.addAll(points)
|
|
142
|
-
if (divided) {
|
|
143
|
-
northwest?.getAllPoints(allPoints)
|
|
144
|
-
northeast?.getAllPoints(allPoints)
|
|
145
|
-
southwest?.getAllPoints(allPoints)
|
|
146
|
-
southeast?.getAllPoints(allPoints)
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Clear all points from the tree
|
|
152
|
-
*/
|
|
153
|
-
fun clear() {
|
|
154
|
-
points.clear()
|
|
155
|
-
if (divided) {
|
|
156
|
-
northwest?.clear()
|
|
157
|
-
northeast?.clear()
|
|
158
|
-
southwest?.clear()
|
|
159
|
-
southeast?.clear()
|
|
160
|
-
northwest = null
|
|
161
|
-
northeast = null
|
|
162
|
-
southwest = null
|
|
163
|
-
southeast = null
|
|
164
|
-
divided = false
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
private fun subdivide() {
|
|
169
|
-
val midX = (boundary.minX + boundary.maxX) / 2
|
|
170
|
-
val midY = (boundary.minY + boundary.maxY) / 2
|
|
171
|
-
|
|
172
|
-
northwest = QuadTree(
|
|
173
|
-
BoundingBox(boundary.minX, midY, midX, boundary.maxY),
|
|
174
|
-
capacity,
|
|
175
|
-
depth + 1
|
|
176
|
-
)
|
|
177
|
-
northeast = QuadTree(
|
|
178
|
-
BoundingBox(midX, midY, boundary.maxX, boundary.maxY),
|
|
179
|
-
capacity,
|
|
180
|
-
depth + 1
|
|
181
|
-
)
|
|
182
|
-
southwest = QuadTree(
|
|
183
|
-
BoundingBox(boundary.minX, boundary.minY, midX, midY),
|
|
184
|
-
capacity,
|
|
185
|
-
depth + 1
|
|
186
|
-
)
|
|
187
|
-
southeast = QuadTree(
|
|
188
|
-
BoundingBox(midX, boundary.minY, boundary.maxX, midY),
|
|
189
|
-
capacity,
|
|
190
|
-
depth + 1
|
|
191
|
-
)
|
|
192
|
-
|
|
193
|
-
divided = true
|
|
194
|
-
}
|
|
195
|
-
}
|