react-native-google-maps-plus 1.10.1-dev.1 → 1.10.1-dev.2
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/android/src/main/java/com/rngooglemapsplus/GoogleMapsViewImpl.kt +2 -4
- package/android/src/main/java/com/rngooglemapsplus/LocationHandler.kt +0 -1
- package/android/src/main/java/com/rngooglemapsplus/MapCircleBuilder.kt +0 -1
- package/android/src/main/java/com/rngooglemapsplus/MapHelper.kt +14 -1
- package/android/src/main/java/com/rngooglemapsplus/MapMarkerBuilder.kt +102 -44
- package/android/src/main/java/com/rngooglemapsplus/MapPolygonBuilder.kt +0 -1
- package/android/src/main/java/com/rngooglemapsplus/MapPolylineBuilder.kt.kt +0 -1
- package/android/src/main/java/com/rngooglemapsplus/MapUrlTileOverlayBuilder.kt +1 -0
- package/android/src/main/java/com/rngooglemapsplus/extensions/BitmapExtension.kt +3 -1
- package/ios/GoogleMapViewImpl.swift +4 -4
- package/ios/MapHelper.swift +14 -0
- package/ios/MapMarkerBuilder.swift +96 -25
- package/ios/extensions/UIImage+Extension.swift +1 -0
- package/package.json +1 -1
|
@@ -41,8 +41,6 @@ import com.google.android.gms.maps.model.TileOverlayOptions
|
|
|
41
41
|
import com.google.maps.android.data.kml.KmlLayer
|
|
42
42
|
import com.margelo.nitro.core.Promise
|
|
43
43
|
import com.rngooglemapsplus.extensions.encode
|
|
44
|
-
import com.rngooglemapsplus.extensions.onUi
|
|
45
|
-
import com.rngooglemapsplus.extensions.onUiSync
|
|
46
44
|
import com.rngooglemapsplus.extensions.toGooglePriority
|
|
47
45
|
import com.rngooglemapsplus.extensions.toLatLng
|
|
48
46
|
import com.rngooglemapsplus.extensions.toLocationErrorCode
|
|
@@ -749,7 +747,7 @@ class GoogleMapsViewImpl(
|
|
|
749
747
|
kmlLayersById[id] = layer
|
|
750
748
|
layer.addLayerToMap()
|
|
751
749
|
} catch (_: Exception) {
|
|
752
|
-
|
|
750
|
+
mapsLog("kml layer parse failed: id=$id")
|
|
753
751
|
}
|
|
754
752
|
}
|
|
755
753
|
|
|
@@ -971,5 +969,5 @@ class GoogleMapsViewImpl(
|
|
|
971
969
|
|
|
972
970
|
override fun getInfoContents(marker: Marker): View? = null
|
|
973
971
|
|
|
974
|
-
override fun getInfoWindow(marker: Marker): View? = markerBuilder.buildInfoWindow(marker.tagData
|
|
972
|
+
override fun getInfoWindow(marker: Marker): View? = markerBuilder.buildInfoWindow(marker.tagData)
|
|
975
973
|
}
|
|
@@ -19,7 +19,6 @@ import com.google.android.gms.location.LocationServices
|
|
|
19
19
|
import com.google.android.gms.location.LocationSettingsRequest
|
|
20
20
|
import com.google.android.gms.location.Priority
|
|
21
21
|
import com.google.android.gms.maps.LocationSource
|
|
22
|
-
import com.rngooglemapsplus.extensions.onUi
|
|
23
22
|
import com.rngooglemapsplus.extensions.toLocationErrorCode
|
|
24
23
|
|
|
25
24
|
private const val REQ_LOCATION_SETTINGS = 2001
|
|
@@ -5,7 +5,6 @@ import com.facebook.react.uimanager.PixelUtil.dpToPx
|
|
|
5
5
|
import com.google.android.gms.maps.model.Circle
|
|
6
6
|
import com.google.android.gms.maps.model.CircleOptions
|
|
7
7
|
import com.rngooglemapsplus.extensions.centerEquals
|
|
8
|
-
import com.rngooglemapsplus.extensions.onUi
|
|
9
8
|
import com.rngooglemapsplus.extensions.toColor
|
|
10
9
|
import com.rngooglemapsplus.extensions.toLatLng
|
|
11
10
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
package com.rngooglemapsplus
|
|
1
|
+
package com.rngooglemapsplus
|
|
2
2
|
|
|
3
3
|
import com.facebook.react.bridge.UiThreadUtil
|
|
4
4
|
import kotlinx.coroutines.CompletableDeferred
|
|
@@ -20,3 +20,16 @@ inline fun <T> onUiSync(crossinline block: () -> T): T {
|
|
|
20
20
|
}
|
|
21
21
|
return runBlocking { result.await() }
|
|
22
22
|
}
|
|
23
|
+
|
|
24
|
+
private const val MAPS_LOG_TAG = "react-native-google-maps-plus"
|
|
25
|
+
|
|
26
|
+
fun mapsLog(msg: String) {
|
|
27
|
+
android.util.Log.w(MAPS_LOG_TAG, msg)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
fun mapsLog(
|
|
31
|
+
msg: String,
|
|
32
|
+
t: Throwable,
|
|
33
|
+
) {
|
|
34
|
+
android.util.Log.w(MAPS_LOG_TAG, msg, t)
|
|
35
|
+
}
|
|
@@ -13,6 +13,7 @@ import android.widget.LinearLayout
|
|
|
13
13
|
import androidx.core.graphics.createBitmap
|
|
14
14
|
import com.caverock.androidsvg.SVG
|
|
15
15
|
import com.caverock.androidsvg.SVGExternalFileResolver
|
|
16
|
+
import com.caverock.androidsvg.SVGParseException
|
|
16
17
|
import com.facebook.react.uimanager.PixelUtil.dpToPx
|
|
17
18
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
18
19
|
import com.google.android.gms.maps.model.BitmapDescriptor
|
|
@@ -24,13 +25,13 @@ import com.rngooglemapsplus.extensions.coordinatesEquals
|
|
|
24
25
|
import com.rngooglemapsplus.extensions.infoWindowAnchorEquals
|
|
25
26
|
import com.rngooglemapsplus.extensions.markerInfoWindowStyleEquals
|
|
26
27
|
import com.rngooglemapsplus.extensions.markerStyleEquals
|
|
27
|
-
import com.rngooglemapsplus.extensions.onUi
|
|
28
28
|
import com.rngooglemapsplus.extensions.styleHash
|
|
29
29
|
import com.rngooglemapsplus.extensions.toLatLng
|
|
30
30
|
import kotlinx.coroutines.CoroutineScope
|
|
31
31
|
import kotlinx.coroutines.Dispatchers
|
|
32
32
|
import kotlinx.coroutines.Job
|
|
33
33
|
import kotlinx.coroutines.SupervisorJob
|
|
34
|
+
import kotlinx.coroutines.currentCoroutineContext
|
|
34
35
|
import kotlinx.coroutines.ensureActive
|
|
35
36
|
import kotlinx.coroutines.launch
|
|
36
37
|
import kotlinx.coroutines.withContext
|
|
@@ -38,7 +39,7 @@ import java.net.HttpURLConnection
|
|
|
38
39
|
import java.net.URL
|
|
39
40
|
import java.net.URLDecoder
|
|
40
41
|
import java.util.concurrent.ConcurrentHashMap
|
|
41
|
-
import kotlin.coroutines.
|
|
42
|
+
import kotlin.coroutines.cancellation.CancellationException
|
|
42
43
|
|
|
43
44
|
class MapMarkerBuilder(
|
|
44
45
|
val context: ThemedReactContext,
|
|
@@ -117,6 +118,8 @@ class MapMarkerBuilder(
|
|
|
117
118
|
|
|
118
119
|
else -> null
|
|
119
120
|
}
|
|
121
|
+
}.onFailure {
|
|
122
|
+
mapsLog("external svg resolve failed")
|
|
120
123
|
}.getOrNull()
|
|
121
124
|
}
|
|
122
125
|
|
|
@@ -140,7 +143,7 @@ class MapMarkerBuilder(
|
|
|
140
143
|
try {
|
|
141
144
|
return Typeface.createFromAsset(assetManager, path)
|
|
142
145
|
} catch (_: Throwable) {
|
|
143
|
-
|
|
146
|
+
mapsLog("font resolve failed: $path")
|
|
144
147
|
}
|
|
145
148
|
}
|
|
146
149
|
|
|
@@ -264,32 +267,40 @@ class MapMarkerBuilder(
|
|
|
264
267
|
scope.launch {
|
|
265
268
|
try {
|
|
266
269
|
ensureActive()
|
|
267
|
-
val
|
|
270
|
+
val renderResult = renderBitmap(m.iconSvg, m.id)
|
|
268
271
|
|
|
269
|
-
if (
|
|
270
|
-
withContext(Dispatchers.Main) {
|
|
272
|
+
if (renderResult?.bitmap == null) {
|
|
273
|
+
withContext(Dispatchers.Main) {
|
|
274
|
+
ensureActive()
|
|
275
|
+
onReady(createFallbackDescriptor())
|
|
276
|
+
}
|
|
271
277
|
return@launch
|
|
272
278
|
}
|
|
279
|
+
|
|
273
280
|
ensureActive()
|
|
274
|
-
val desc = BitmapDescriptorFactory.fromBitmap(
|
|
281
|
+
val desc = BitmapDescriptorFactory.fromBitmap(renderResult.bitmap)
|
|
275
282
|
|
|
276
|
-
|
|
277
|
-
|
|
283
|
+
if (!renderResult.isFallback) {
|
|
284
|
+
iconCache.put(key, desc)
|
|
285
|
+
}
|
|
286
|
+
renderResult.bitmap.recycle()
|
|
278
287
|
|
|
279
288
|
withContext(Dispatchers.Main) {
|
|
280
289
|
ensureActive()
|
|
281
290
|
onReady(desc)
|
|
282
291
|
}
|
|
283
292
|
} catch (_: OutOfMemoryError) {
|
|
293
|
+
mapsLog("markerId=${m.id} buildIconAsync out of memory")
|
|
284
294
|
clearIconCache()
|
|
285
295
|
withContext(Dispatchers.Main) {
|
|
286
296
|
ensureActive()
|
|
287
|
-
onReady(
|
|
297
|
+
onReady(createFallbackDescriptor())
|
|
288
298
|
}
|
|
289
299
|
} catch (_: Throwable) {
|
|
300
|
+
mapsLog("markerId=${m.id} buildIconAsync failed")
|
|
290
301
|
withContext(Dispatchers.Main) {
|
|
291
302
|
ensureActive()
|
|
292
|
-
onReady(
|
|
303
|
+
onReady(createFallbackDescriptor())
|
|
293
304
|
}
|
|
294
305
|
} finally {
|
|
295
306
|
jobsById.remove(m.id)
|
|
@@ -317,8 +328,22 @@ class MapMarkerBuilder(
|
|
|
317
328
|
iconCache.evictAll()
|
|
318
329
|
}
|
|
319
330
|
|
|
320
|
-
fun buildInfoWindow(
|
|
321
|
-
val iconSvg = iconSvg ?: return null
|
|
331
|
+
fun buildInfoWindow(markerTag: MarkerTag): ImageView? {
|
|
332
|
+
val iconSvg = markerTag.iconSvg ?: return null
|
|
333
|
+
|
|
334
|
+
val wPx =
|
|
335
|
+
markerTag.iconSvg.width
|
|
336
|
+
.dpToPx()
|
|
337
|
+
.toInt()
|
|
338
|
+
val hPx =
|
|
339
|
+
markerTag.iconSvg.height
|
|
340
|
+
.dpToPx()
|
|
341
|
+
.toInt()
|
|
342
|
+
|
|
343
|
+
if (wPx <= 0 || hPx <= 0) {
|
|
344
|
+
mapsLog("markerId=${markerTag.id} invalid svg size")
|
|
345
|
+
return ImageView(context)
|
|
346
|
+
}
|
|
322
347
|
|
|
323
348
|
val svgView =
|
|
324
349
|
ImageView(context).apply {
|
|
@@ -330,40 +355,73 @@ class MapMarkerBuilder(
|
|
|
330
355
|
}
|
|
331
356
|
|
|
332
357
|
try {
|
|
333
|
-
val svg =
|
|
334
|
-
|
|
335
|
-
|
|
358
|
+
val svg =
|
|
359
|
+
SVG.getFromString(iconSvg.svgString).apply {
|
|
360
|
+
documentWidth = wPx.toFloat()
|
|
361
|
+
documentHeight = hPx.toFloat()
|
|
362
|
+
}
|
|
336
363
|
val drawable = PictureDrawable(svg.renderToPicture())
|
|
337
364
|
svgView.setImageDrawable(drawable)
|
|
338
|
-
} catch (
|
|
339
|
-
|
|
365
|
+
} catch (_: Exception) {
|
|
366
|
+
mapsLog("markerId=${markerTag.id} infoWindow: svg render failed")
|
|
367
|
+
return ImageView(context)
|
|
340
368
|
}
|
|
341
369
|
|
|
342
370
|
return svgView
|
|
343
371
|
}
|
|
344
372
|
|
|
345
|
-
private
|
|
346
|
-
|
|
373
|
+
private fun createFallbackBitmap(): Bitmap =
|
|
374
|
+
createBitmap(1, 1, Bitmap.Config.ARGB_8888).apply {
|
|
375
|
+
setHasAlpha(true)
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
private fun createFallbackDescriptor(): BitmapDescriptor {
|
|
379
|
+
val bmp = createFallbackBitmap()
|
|
380
|
+
return BitmapDescriptorFactory.fromBitmap(bmp).also {
|
|
381
|
+
bmp.recycle()
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
private data class RenderBitmapResult(
|
|
386
|
+
val bitmap: Bitmap,
|
|
387
|
+
val isFallback: Boolean,
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
private suspend fun renderBitmap(
|
|
391
|
+
iconSvg: RNMarkerSvg,
|
|
392
|
+
markerId: String,
|
|
393
|
+
): RenderBitmapResult? {
|
|
394
|
+
val wPx =
|
|
395
|
+
iconSvg.width
|
|
396
|
+
.dpToPx()
|
|
397
|
+
.toInt()
|
|
398
|
+
val hPx =
|
|
399
|
+
iconSvg.height
|
|
400
|
+
.dpToPx()
|
|
401
|
+
.toInt()
|
|
402
|
+
|
|
403
|
+
if (wPx <= 0 || hPx <= 0) {
|
|
404
|
+
mapsLog("markerId=$markerId invalid svg size")
|
|
405
|
+
return RenderBitmapResult(createFallbackBitmap(), true)
|
|
406
|
+
}
|
|
347
407
|
|
|
348
408
|
var bmp: Bitmap? = null
|
|
349
409
|
try {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
coroutineContext.ensureActive()
|
|
410
|
+
val svg =
|
|
411
|
+
try {
|
|
412
|
+
SVG.getFromString(iconSvg.svgString).apply {
|
|
413
|
+
documentWidth = wPx.toFloat()
|
|
414
|
+
documentHeight = hPx.toFloat()
|
|
415
|
+
}
|
|
416
|
+
} catch (_: SVGParseException) {
|
|
417
|
+
mapsLog("markerId=$markerId icon: svg parse failed")
|
|
418
|
+
return RenderBitmapResult(createFallbackBitmap(), true)
|
|
419
|
+
} catch (_: IllegalArgumentException) {
|
|
420
|
+
mapsLog("markerId=$markerId icon: svg invalid")
|
|
421
|
+
return RenderBitmapResult(createFallbackBitmap(), true)
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
currentCoroutineContext().ensureActive()
|
|
367
425
|
bmp =
|
|
368
426
|
createBitmap(wPx, hPx, Bitmap.Config.ARGB_8888).apply {
|
|
369
427
|
density = context.resources.displayMetrics.densityDpi
|
|
@@ -372,13 +430,13 @@ class MapMarkerBuilder(
|
|
|
372
430
|
}
|
|
373
431
|
}
|
|
374
432
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
throw
|
|
433
|
+
currentCoroutineContext().ensureActive()
|
|
434
|
+
|
|
435
|
+
return RenderBitmapResult(bmp, false)
|
|
436
|
+
} catch (e: Exception) {
|
|
437
|
+
if (e is CancellationException) throw e
|
|
438
|
+
bmp?.recycle()
|
|
439
|
+
throw e
|
|
382
440
|
}
|
|
383
441
|
}
|
|
384
442
|
}
|
|
@@ -6,7 +6,6 @@ import com.google.android.gms.maps.model.Polygon
|
|
|
6
6
|
import com.google.android.gms.maps.model.PolygonOptions
|
|
7
7
|
import com.rngooglemapsplus.extensions.coordinatesEquals
|
|
8
8
|
import com.rngooglemapsplus.extensions.holesEquals
|
|
9
|
-
import com.rngooglemapsplus.extensions.onUi
|
|
10
9
|
import com.rngooglemapsplus.extensions.toColor
|
|
11
10
|
import com.rngooglemapsplus.extensions.toLatLng
|
|
12
11
|
import com.rngooglemapsplus.extensions.toMapsPolygonHoles
|
|
@@ -5,7 +5,6 @@ import com.facebook.react.uimanager.PixelUtil.dpToPx
|
|
|
5
5
|
import com.google.android.gms.maps.model.Polyline
|
|
6
6
|
import com.google.android.gms.maps.model.PolylineOptions
|
|
7
7
|
import com.rngooglemapsplus.extensions.coordinatesEquals
|
|
8
|
-
import com.rngooglemapsplus.extensions.onUi
|
|
9
8
|
import com.rngooglemapsplus.extensions.toColor
|
|
10
9
|
import com.rngooglemapsplus.extensions.toLatLng
|
|
11
10
|
import com.rngooglemapsplus.extensions.toMapJointType
|
|
@@ -5,6 +5,7 @@ import android.graphics.Bitmap
|
|
|
5
5
|
import android.util.Base64
|
|
6
6
|
import android.util.Size
|
|
7
7
|
import androidx.core.graphics.scale
|
|
8
|
+
import com.rngooglemapsplus.mapsLog
|
|
8
9
|
import java.io.ByteArrayOutputStream
|
|
9
10
|
import java.io.File
|
|
10
11
|
import java.io.FileOutputStream
|
|
@@ -30,6 +31,7 @@ fun Bitmap.encode(
|
|
|
30
31
|
} else {
|
|
31
32
|
"data:image/$format;base64," + Base64.encodeToString(bytes, Base64.NO_WRAP)
|
|
32
33
|
}
|
|
33
|
-
} catch (
|
|
34
|
+
} catch (e: Exception) {
|
|
35
|
+
mapsLog("snapshot export failed", e)
|
|
34
36
|
null
|
|
35
37
|
}
|
|
@@ -20,7 +20,8 @@ GMSIndoorDisplayDelegate {
|
|
|
20
20
|
private var pendingCircles: [(id: String, circle: GMSCircle)] = []
|
|
21
21
|
private var pendingHeatmaps: [(id: String, heatmap: GMUHeatmapTileLayer)] = []
|
|
22
22
|
private var pendingKmlLayers: [(id: String, kmlString: String)] = []
|
|
23
|
-
private var pendingUrlTileOverlays:
|
|
23
|
+
private var pendingUrlTileOverlays:
|
|
24
|
+
[(id: String, urlTileOverlay: GMSURLTileLayer)] = []
|
|
24
25
|
|
|
25
26
|
private var markersById: [String: GMSMarker] = [:]
|
|
26
27
|
private var polylinesById: [String: GMSPolyline] = [:]
|
|
@@ -1004,11 +1005,10 @@ GMSIndoorDisplayDelegate {
|
|
|
1004
1005
|
}
|
|
1005
1006
|
|
|
1006
1007
|
func mapView(_ mapView: GMSMapView, markerInfoWindow marker: GMSMarker) -> UIView? {
|
|
1007
|
-
return markerBuilder.buildInfoWindow(
|
|
1008
|
+
return markerBuilder.buildInfoWindow(markerTag: marker.tagData)
|
|
1008
1009
|
}
|
|
1009
1010
|
|
|
1010
|
-
func mapView(_ mapView: GMSMapView, markerInfoContents marker: GMSMarker)
|
|
1011
|
-
-> UIView? {
|
|
1011
|
+
func mapView(_ mapView: GMSMapView, markerInfoContents marker: GMSMarker) -> UIView? {
|
|
1012
1012
|
return nil
|
|
1013
1013
|
}
|
|
1014
1014
|
}
|
package/ios/MapHelper.swift
CHANGED
|
@@ -33,3 +33,17 @@ func onMain(
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
+
|
|
37
|
+
@inline(__always)
|
|
38
|
+
func mapsLog(_ message: String) {
|
|
39
|
+
NSLog("[react-native-google-maps-plus] %@", message)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@inline(__always)
|
|
43
|
+
func mapsLog(_ message: String, _ error: Error) {
|
|
44
|
+
NSLog(
|
|
45
|
+
"[react-native-google-maps-plus] %@ | %@",
|
|
46
|
+
message,
|
|
47
|
+
String(describing: error)
|
|
48
|
+
)
|
|
49
|
+
}
|
|
@@ -10,6 +10,10 @@ final class MapMarkerBuilder {
|
|
|
10
10
|
}()
|
|
11
11
|
private var tasks: [String: Task<Void, Never>] = [:]
|
|
12
12
|
|
|
13
|
+
init() {
|
|
14
|
+
warmupSVGKit()
|
|
15
|
+
}
|
|
16
|
+
|
|
13
17
|
func build(_ m: RNMarker, icon: UIImage?) -> GMSMarker {
|
|
14
18
|
let marker = GMSMarker(
|
|
15
19
|
position: m.coordinate.toCLLocationCoordinate2D()
|
|
@@ -154,7 +158,7 @@ final class MapMarkerBuilder {
|
|
|
154
158
|
) {
|
|
155
159
|
tasks[m.id]?.cancel()
|
|
156
160
|
|
|
157
|
-
|
|
161
|
+
guard let iconSvg = m.iconSvg else {
|
|
158
162
|
onReady(nil)
|
|
159
163
|
return
|
|
160
164
|
}
|
|
@@ -173,14 +177,24 @@ final class MapMarkerBuilder {
|
|
|
173
177
|
Task { @MainActor in self.tasks.removeValue(forKey: m.id) }
|
|
174
178
|
}
|
|
175
179
|
|
|
176
|
-
let
|
|
177
|
-
guard
|
|
180
|
+
let renderResult = self.renderUIImage(iconSvg, m.id, scale)
|
|
181
|
+
guard !Task.isCancelled else { return }
|
|
178
182
|
|
|
179
|
-
|
|
183
|
+
guard let renderResult = renderResult else {
|
|
184
|
+
await MainActor.run {
|
|
185
|
+
guard !Task.isCancelled else { return }
|
|
186
|
+
onReady(self.createFallbackUIImage())
|
|
187
|
+
}
|
|
188
|
+
return
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if !renderResult.isFallback {
|
|
192
|
+
self.iconCache.setObject(renderResult.image, forKey: key)
|
|
193
|
+
}
|
|
180
194
|
|
|
181
195
|
await MainActor.run {
|
|
182
196
|
guard !Task.isCancelled else { return }
|
|
183
|
-
onReady(
|
|
197
|
+
onReady(renderResult.image)
|
|
184
198
|
}
|
|
185
199
|
}
|
|
186
200
|
|
|
@@ -202,27 +216,36 @@ final class MapMarkerBuilder {
|
|
|
202
216
|
CATransaction.flush()
|
|
203
217
|
}
|
|
204
218
|
|
|
205
|
-
func buildInfoWindow(
|
|
206
|
-
guard let iconSvg = iconSvg else {
|
|
219
|
+
func buildInfoWindow(markerTag: MarkerTag) -> UIImageView? {
|
|
220
|
+
guard let iconSvg = markerTag.iconSvg else {
|
|
207
221
|
return nil
|
|
208
222
|
}
|
|
209
223
|
|
|
224
|
+
let w = CGFloat(iconSvg.width)
|
|
225
|
+
let h = CGFloat(iconSvg.height)
|
|
226
|
+
|
|
227
|
+
if w <= 0 || h <= 0 {
|
|
228
|
+
mapsLog("markerId=\(markerTag.id) icon: invalid svg size")
|
|
229
|
+
return createFallbackImageView()
|
|
230
|
+
}
|
|
231
|
+
|
|
210
232
|
guard let data = iconSvg.svgString.data(using: .utf8),
|
|
211
233
|
let svgImg = SVGKImage(data: data)
|
|
212
234
|
else {
|
|
213
|
-
|
|
235
|
+
mapsLog("markerId=\(markerTag.id) infoWindow: svg utf8 decode failed")
|
|
236
|
+
return createFallbackImageView()
|
|
214
237
|
}
|
|
215
238
|
|
|
216
|
-
let size = CGSize(
|
|
217
|
-
width: max(1, CGFloat(iconSvg.width)),
|
|
218
|
-
height: max(1, CGFloat(iconSvg.height))
|
|
219
|
-
)
|
|
239
|
+
let size = CGSize(width: w, height: h)
|
|
220
240
|
|
|
221
241
|
svgImg.size = size
|
|
222
242
|
|
|
223
243
|
guard let finalImage = SVGKExporterUIImage.export(asUIImage: svgImg) else {
|
|
244
|
+
mapsLog(
|
|
245
|
+
"markerId=\(markerTag.id) infoWindow: svg export to UIImage failed"
|
|
246
|
+
)
|
|
224
247
|
svgImg.clear()
|
|
225
|
-
return
|
|
248
|
+
return createFallbackImageView()
|
|
226
249
|
}
|
|
227
250
|
svgImg.clear()
|
|
228
251
|
|
|
@@ -234,21 +257,63 @@ final class MapMarkerBuilder {
|
|
|
234
257
|
return imageView
|
|
235
258
|
}
|
|
236
259
|
|
|
237
|
-
private func
|
|
260
|
+
private func warmupSVGKit() {
|
|
261
|
+
autoreleasepool {
|
|
262
|
+
let emptySvg = """
|
|
263
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1" height="1"></svg>
|
|
264
|
+
"""
|
|
265
|
+
guard let data = emptySvg.data(using: .utf8),
|
|
266
|
+
let svgImg = SVGKImage(data: data)
|
|
267
|
+
else { return }
|
|
268
|
+
svgImg.clear()
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
private func createFallbackUIImage() -> UIImage {
|
|
273
|
+
let size = CGSize(width: 1, height: 1)
|
|
274
|
+
let renderer = UIGraphicsImageRenderer(size: size)
|
|
275
|
+
return renderer.image { _ in }
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
private func createFallbackImageView() -> UIImageView {
|
|
279
|
+
let iv = UIImageView(image: createFallbackUIImage())
|
|
280
|
+
iv.contentMode = .scaleAspectFit
|
|
281
|
+
iv.backgroundColor = .clear
|
|
282
|
+
return iv
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
private func renderUIImage(
|
|
286
|
+
_ iconSvg: RNMarkerSvg,
|
|
287
|
+
_ markerId: String,
|
|
288
|
+
_ scale: CGFloat
|
|
289
|
+
) -> (
|
|
290
|
+
image: UIImage, isFallback: Bool
|
|
291
|
+
)? {
|
|
292
|
+
|
|
293
|
+
let w = CGFloat(iconSvg.width)
|
|
294
|
+
let h = CGFloat(iconSvg.height)
|
|
295
|
+
|
|
296
|
+
if w <= 0 || h <= 0 {
|
|
297
|
+
mapsLog("markerId=\(markerId) icon: invalid svg size")
|
|
298
|
+
return (createFallbackUIImage(), true)
|
|
299
|
+
}
|
|
300
|
+
|
|
238
301
|
guard
|
|
239
|
-
let iconSvg = m.iconSvg,
|
|
240
302
|
let data = iconSvg.svgString.data(using: .utf8)
|
|
241
|
-
else {
|
|
242
|
-
|
|
303
|
+
else {
|
|
304
|
+
mapsLog("markerId=\(markerId) icon: svg utf8 decode failed")
|
|
305
|
+
return (createFallbackUIImage(), true)
|
|
306
|
+
}
|
|
243
307
|
|
|
244
|
-
let size = CGSize(
|
|
245
|
-
width: max(1, CGFloat(iconSvg.width)),
|
|
246
|
-
height: max(1, CGFloat(iconSvg.height))
|
|
247
|
-
)
|
|
308
|
+
let size = CGSize(width: w, height: h)
|
|
248
309
|
|
|
249
|
-
return autoreleasepool { () -> UIImage? in
|
|
310
|
+
return autoreleasepool { () -> (image: UIImage, isFallback: Bool)? in
|
|
250
311
|
guard !Task.isCancelled else { return nil }
|
|
251
|
-
|
|
312
|
+
|
|
313
|
+
guard let svgImg = SVGKImage(data: data) else {
|
|
314
|
+
mapsLog("markerId=\(markerId) icon: SVGKImage init failed")
|
|
315
|
+
return (createFallbackUIImage(), true)
|
|
316
|
+
}
|
|
252
317
|
|
|
253
318
|
svgImg.size = size
|
|
254
319
|
|
|
@@ -259,8 +324,14 @@ final class MapMarkerBuilder {
|
|
|
259
324
|
|
|
260
325
|
let uiImage = SVGKExporterUIImage.export(asUIImage: svgImg)
|
|
261
326
|
svgImg.clear()
|
|
262
|
-
|
|
327
|
+
|
|
328
|
+
guard !Task.isCancelled else { return nil }
|
|
329
|
+
|
|
330
|
+
if let uiImage = uiImage {
|
|
331
|
+
return (uiImage, false)
|
|
332
|
+
} else {
|
|
333
|
+
return (createFallbackUIImage(), true)
|
|
334
|
+
}
|
|
263
335
|
}
|
|
264
336
|
}
|
|
265
|
-
|
|
266
337
|
}
|
package/package.json
CHANGED