@maplibre/maplibre-react-native 11.0.0-alpha.37 → 11.0.0-alpha.38
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/org/maplibre/reactnative/components/images/MLRNImages.kt +172 -0
- package/android/src/main/java/org/maplibre/reactnative/components/images/MLRNImagesManager.kt +73 -0
- package/android/src/main/java/org/maplibre/reactnative/components/layers/style/MLRNStyle.java +1 -1
- package/android/src/main/java/org/maplibre/reactnative/components/layers/style/MLRNStyleValue.java +2 -2
- package/android/src/main/java/org/maplibre/reactnative/components/mapview/MLRNMapView.kt +2 -1
- package/android/src/main/java/org/maplibre/reactnative/events/constants/EventKeys.java +0 -3
- package/android/src/main/java/org/maplibre/reactnative/utils/DownloadMapImageTask.java +6 -4
- package/android/src/main/java/org/maplibre/reactnative/utils/ImageEntry.kt +14 -0
- package/ios/components/images/MLRNImageQueueOperation.m +22 -17
- package/ios/components/images/MLRNImages.h +12 -7
- package/ios/components/images/MLRNImages.m +104 -39
- package/ios/components/images/MLRNImagesComponentView.h +9 -0
- package/ios/components/images/MLRNImagesComponentView.mm +107 -0
- package/ios/components/map-view/MLRNMapViewComponentView.mm +1 -8
- package/ios/components/sources/geojson-source/MLRNGeoJSONSourceComponentView.mm +4 -1
- package/ios/modules/network/MLRNNetworkHTTPHeaders.h +4 -4
- package/lib/commonjs/components/images/Images.js +48 -0
- package/lib/commonjs/components/images/Images.js.map +1 -0
- package/lib/commonjs/components/images/ImagesNativeComponent.ts +27 -0
- package/lib/commonjs/index.js +1 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/components/images/Images.js +42 -0
- package/lib/module/components/images/Images.js.map +1 -0
- package/lib/module/components/images/ImagesNativeComponent.ts +27 -0
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/commonjs/src/components/images/Images.d.ts +61 -0
- package/lib/typescript/commonjs/src/components/images/Images.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/components/images/ImagesNativeComponent.d.ts +17 -0
- package/lib/typescript/commonjs/src/components/images/ImagesNativeComponent.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/index.d.ts +1 -1
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
- package/lib/typescript/module/src/components/images/Images.d.ts +61 -0
- package/lib/typescript/module/src/components/images/Images.d.ts.map +1 -0
- package/lib/typescript/module/src/components/images/ImagesNativeComponent.d.ts +17 -0
- package/lib/typescript/module/src/components/images/ImagesNativeComponent.d.ts.map +1 -0
- package/lib/typescript/module/src/index.d.ts +1 -1
- package/lib/typescript/module/src/index.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/components/images/Images.tsx +103 -0
- package/src/components/images/ImagesNativeComponent.ts +27 -0
- package/src/index.ts +6 -1
- package/android/src/main/java/org/maplibre/reactnative/components/images/MLRNImages.java +0 -233
- package/android/src/main/java/org/maplibre/reactnative/components/images/MLRNImagesManager.java +0 -103
- package/android/src/main/java/org/maplibre/reactnative/events/ImageMissingEvent.java +0 -42
- package/android/src/main/java/org/maplibre/reactnative/utils/ImageEntry.java +0 -26
- package/ios/components/images/MLRNImagesManager.h +0 -5
- package/ios/components/images/MLRNImagesManager.m +0 -20
- package/lib/commonjs/components/Images.js +0 -62
- package/lib/commonjs/components/Images.js.map +0 -1
- package/lib/module/components/Images.js +0 -57
- package/lib/module/components/Images.js.map +0 -1
- package/lib/typescript/commonjs/src/components/Images.d.ts +0 -41
- package/lib/typescript/commonjs/src/components/Images.d.ts.map +0 -1
- package/lib/typescript/module/src/components/Images.d.ts +0 -41
- package/lib/typescript/module/src/components/Images.d.ts.map +0 -1
- package/src/components/Images.tsx +0 -118
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
package org.maplibre.reactnative.components.images
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.graphics.Bitmap
|
|
5
|
+
import androidx.core.content.res.ResourcesCompat
|
|
6
|
+
import com.facebook.react.bridge.Arguments
|
|
7
|
+
import com.facebook.react.bridge.ReactContext
|
|
8
|
+
import com.facebook.react.bridge.WritableMap
|
|
9
|
+
import com.facebook.react.uimanager.UIManagerHelper
|
|
10
|
+
import com.facebook.react.uimanager.events.Event
|
|
11
|
+
import com.facebook.react.uimanager.events.EventDispatcher
|
|
12
|
+
import org.maplibre.android.maps.MapLibreMap
|
|
13
|
+
import org.maplibre.android.utils.BitmapUtils
|
|
14
|
+
import org.maplibre.reactnative.R
|
|
15
|
+
import org.maplibre.reactnative.components.AbstractMapFeature
|
|
16
|
+
import org.maplibre.reactnative.components.mapview.MLRNMapView
|
|
17
|
+
import org.maplibre.reactnative.utils.DownloadMapImageTask
|
|
18
|
+
import org.maplibre.reactnative.utils.ImageEntry
|
|
19
|
+
|
|
20
|
+
class MLRNImages(
|
|
21
|
+
context: Context,
|
|
22
|
+
) : AbstractMapFeature(context) {
|
|
23
|
+
companion object {
|
|
24
|
+
private var imagePlaceholder: Bitmap? = null
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
private val currentImages = mutableSetOf<String>()
|
|
28
|
+
private val images = mutableMapOf<String, ImageEntry>()
|
|
29
|
+
private var map: MapLibreMap? = null
|
|
30
|
+
|
|
31
|
+
init {
|
|
32
|
+
if (imagePlaceholder == null) {
|
|
33
|
+
imagePlaceholder =
|
|
34
|
+
BitmapUtils.getBitmapFromDrawable(
|
|
35
|
+
ResourcesCompat.getDrawable(context.resources, R.drawable.empty_drawable, null),
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Set unified images.
|
|
42
|
+
* ImageEntry.uri can be:
|
|
43
|
+
* - A native asset name (simple name like "pin")
|
|
44
|
+
* - A URL (starts with http/https/file/asset/data or /)
|
|
45
|
+
*/
|
|
46
|
+
fun setImages(
|
|
47
|
+
imagesList: List<Map.Entry<String, ImageEntry>>,
|
|
48
|
+
context: Context,
|
|
49
|
+
) {
|
|
50
|
+
val newImages = mutableMapOf<String, ImageEntry>()
|
|
51
|
+
|
|
52
|
+
for (entry in imagesList) {
|
|
53
|
+
val key = entry.key
|
|
54
|
+
val value = entry.value
|
|
55
|
+
val oldValue = images.put(key, value)
|
|
56
|
+
if (oldValue == null) {
|
|
57
|
+
newImages[key] = value
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
map?.let { mapInstance ->
|
|
62
|
+
if (mapInstance.style != null) {
|
|
63
|
+
processImages(newImages, mapInstance, context)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
override fun removeFromMap(mapView: MLRNMapView) {
|
|
69
|
+
removeImages(mapView)
|
|
70
|
+
map = null
|
|
71
|
+
images.clear()
|
|
72
|
+
currentImages.clear()
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private fun removeImages(mapView: MLRNMapView) {
|
|
76
|
+
mapView.getStyle { style ->
|
|
77
|
+
for (imageName in images.keys) {
|
|
78
|
+
style.removeImage(imageName)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
override fun addToMap(mapView: MLRNMapView) {
|
|
84
|
+
// Wait for style before adding the source to the map.
|
|
85
|
+
// Only then we can pre-load required images/placeholders into the style.
|
|
86
|
+
mapView.getStyle { _ ->
|
|
87
|
+
val mapInstance = mapView.mapLibreMap
|
|
88
|
+
map = mapInstance
|
|
89
|
+
map?.let {
|
|
90
|
+
processImages(images, it, context)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private fun processImages(
|
|
96
|
+
imagesToProcess: Map<String, ImageEntry>,
|
|
97
|
+
mapInstance: MapLibreMap,
|
|
98
|
+
context: Context,
|
|
99
|
+
) {
|
|
100
|
+
if (imagesToProcess.isEmpty()) return
|
|
101
|
+
|
|
102
|
+
val style = mapInstance.style ?: return
|
|
103
|
+
val remoteImages = mutableListOf<Map.Entry<String, ImageEntry>>()
|
|
104
|
+
|
|
105
|
+
for ((imageName, entry) in imagesToProcess) {
|
|
106
|
+
if (hasImage(imageName, mapInstance)) continue
|
|
107
|
+
|
|
108
|
+
style.addImage(imageName, imagePlaceholder!!)
|
|
109
|
+
remoteImages.add(java.util.AbstractMap.SimpleEntry(imageName, entry))
|
|
110
|
+
currentImages.add(imageName)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Download remote images asynchronously
|
|
114
|
+
if (remoteImages.isNotEmpty()) {
|
|
115
|
+
val task = DownloadMapImageTask(context, mapInstance, null)
|
|
116
|
+
task.execute(*remoteImages.toTypedArray())
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
fun addMissingImageToStyle(
|
|
121
|
+
id: String,
|
|
122
|
+
mapInstance: MapLibreMap,
|
|
123
|
+
): Boolean {
|
|
124
|
+
val entry = images[id] ?: return false
|
|
125
|
+
val style = mapInstance.style ?: return false
|
|
126
|
+
|
|
127
|
+
style.addImage(id, imagePlaceholder!!)
|
|
128
|
+
val task = DownloadMapImageTask(context, mapInstance, null)
|
|
129
|
+
task.execute(java.util.AbstractMap.SimpleEntry(id, entry))
|
|
130
|
+
|
|
131
|
+
currentImages.add(id)
|
|
132
|
+
return true
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
val eventDispatcher: EventDispatcher?
|
|
136
|
+
get() {
|
|
137
|
+
val reactContext = context as ReactContext
|
|
138
|
+
|
|
139
|
+
return UIManagerHelper.getEventDispatcherForReactTag(reactContext, id)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
val surfaceId: Int
|
|
143
|
+
get() {
|
|
144
|
+
val reactContext = context as ReactContext
|
|
145
|
+
|
|
146
|
+
return UIManagerHelper.getSurfaceId(reactContext)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
inner class OnImageMissingEvent(
|
|
150
|
+
private val eventData: WritableMap,
|
|
151
|
+
) : Event<OnImageMissingEvent>(this@MLRNImages.surfaceId, this@MLRNImages.id) {
|
|
152
|
+
override fun getEventName() = "onImageMissing"
|
|
153
|
+
|
|
154
|
+
override fun getEventData() = eventData
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
fun sendImageMissingEvent(image: String) {
|
|
158
|
+
val writableMap = Arguments.createMap()
|
|
159
|
+
writableMap.putString("image", image)
|
|
160
|
+
|
|
161
|
+
eventDispatcher?.dispatchEvent(OnImageMissingEvent(writableMap))
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private fun hasImage(
|
|
165
|
+
imageId: String,
|
|
166
|
+
mapInstance: MapLibreMap,
|
|
167
|
+
): Boolean {
|
|
168
|
+
val style = mapInstance.style
|
|
169
|
+
|
|
170
|
+
return style?.getImage(imageId) != null
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
package org.maplibre.reactnative.components.images
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Dynamic
|
|
4
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
5
|
+
import com.facebook.react.bridge.ReadableType
|
|
6
|
+
import com.facebook.react.module.annotations.ReactModule
|
|
7
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
8
|
+
import com.facebook.react.uimanager.ViewGroupManager
|
|
9
|
+
import com.facebook.react.uimanager.ViewManagerDelegate
|
|
10
|
+
import com.facebook.react.uimanager.annotations.ReactProp
|
|
11
|
+
import com.facebook.react.viewmanagers.MLRNImagesManagerDelegate
|
|
12
|
+
import com.facebook.react.viewmanagers.MLRNImagesManagerInterface
|
|
13
|
+
import org.maplibre.reactnative.utils.ImageEntry
|
|
14
|
+
|
|
15
|
+
@ReactModule(name = MLRNImagesManager.REACT_CLASS)
|
|
16
|
+
class MLRNImagesManager(
|
|
17
|
+
private val context: ReactApplicationContext,
|
|
18
|
+
) : ViewGroupManager<MLRNImages>(),
|
|
19
|
+
MLRNImagesManagerInterface<MLRNImages> {
|
|
20
|
+
companion object {
|
|
21
|
+
const val REACT_CLASS = "MLRNImages"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
private val delegate: MLRNImagesManagerDelegate<MLRNImages, MLRNImagesManager> =
|
|
25
|
+
MLRNImagesManagerDelegate(this)
|
|
26
|
+
|
|
27
|
+
override fun getDelegate(): ViewManagerDelegate<MLRNImages> = delegate
|
|
28
|
+
|
|
29
|
+
override fun getName(): String = REACT_CLASS
|
|
30
|
+
|
|
31
|
+
override fun createViewInstance(context: ThemedReactContext): MLRNImages = MLRNImages(context)
|
|
32
|
+
|
|
33
|
+
@ReactProp(name = "images")
|
|
34
|
+
override fun setImages(
|
|
35
|
+
view: MLRNImages,
|
|
36
|
+
value: Dynamic,
|
|
37
|
+
) {
|
|
38
|
+
val readableMap = value.asMap()
|
|
39
|
+
|
|
40
|
+
if (readableMap == null) {
|
|
41
|
+
view.setImages(emptyList(), context)
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
val imagesList = mutableListOf<Map.Entry<String, ImageEntry>>()
|
|
46
|
+
val iterator = readableMap.keySetIterator()
|
|
47
|
+
|
|
48
|
+
while (iterator.hasNextKey()) {
|
|
49
|
+
val imageName = iterator.nextKey()
|
|
50
|
+
val imageEntry: ImageEntry =
|
|
51
|
+
when (readableMap.getType(imageName)) {
|
|
52
|
+
ReadableType.Map -> {
|
|
53
|
+
val imageMap = readableMap.getMap(imageName)!!
|
|
54
|
+
|
|
55
|
+
val uri = imageMap.getString("uri")
|
|
56
|
+
val scale =
|
|
57
|
+
if (imageMap.hasKey("scale")) imageMap.getDouble("scale") else null
|
|
58
|
+
val sdf = imageMap.hasKey("sdf") && imageMap.getBoolean("sdf")
|
|
59
|
+
|
|
60
|
+
ImageEntry(uri, scale, sdf)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
else -> {
|
|
64
|
+
continue
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
imagesList.add(java.util.AbstractMap.SimpleEntry(imageName, imageEntry))
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
view.setImages(imagesList, context)
|
|
72
|
+
}
|
|
73
|
+
}
|
package/android/src/main/java/org/maplibre/reactnative/components/layers/style/MLRNStyle.java
CHANGED
|
@@ -57,7 +57,7 @@ public class MLRNStyle {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
public ImageEntry imageEntry(MLRNStyleValue styleValue) {
|
|
60
|
-
return new ImageEntry(styleValue.getImageURI(), styleValue.getImageScale());
|
|
60
|
+
return new ImageEntry(styleValue.getImageURI(), styleValue.getImageScale(), false);
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
public void addImage(MLRNStyleValue styleValue, DownloadMapImageTask.OnAllImagesLoaded callback) {
|
package/android/src/main/java/org/maplibre/reactnative/components/layers/style/MLRNStyleValue.java
CHANGED
|
@@ -22,7 +22,7 @@ public class MLRNStyleValue {
|
|
|
22
22
|
|
|
23
23
|
private String imageURI = "";
|
|
24
24
|
private boolean isAddImage;
|
|
25
|
-
private Double imageScale = ImageEntry.
|
|
25
|
+
private Double imageScale = ImageEntry.DEFAULT_SCALE;
|
|
26
26
|
|
|
27
27
|
public static final int InterpolationModeExponential = 100;
|
|
28
28
|
public static final int InterpolationModeInterval = 101;
|
|
@@ -34,7 +34,7 @@ public class MLRNStyleValue {
|
|
|
34
34
|
mPayload = config.getMap("stylevalue");
|
|
35
35
|
|
|
36
36
|
if ("image".equals(mType)) {
|
|
37
|
-
imageScale = ImageEntry.
|
|
37
|
+
imageScale = ImageEntry.DEFAULT_SCALE;
|
|
38
38
|
if ("hashmap".equals(mPayload.getString("type"))) {
|
|
39
39
|
ReadableMap map = getMap();
|
|
40
40
|
imageURI = map.getMap("uri").getString("value");
|
|
@@ -15,9 +15,6 @@ public class EventKeys {
|
|
|
15
15
|
public static final String POINT_ANNOTATION_DRAG = ns("pointannotation.drag");
|
|
16
16
|
public static final String POINT_ANNOTATION_DRAG_END = ns("pointannotation.dragend");
|
|
17
17
|
|
|
18
|
-
// images event
|
|
19
|
-
public static final String IMAGES_MISSING = ns("images.missing");
|
|
20
|
-
|
|
21
18
|
|
|
22
19
|
private static String ns(String name) {
|
|
23
20
|
return String.format("%s.%s", NAMESPACE, name);
|
|
@@ -20,6 +20,7 @@ import com.facebook.imagepipeline.image.CloseableStaticBitmap;
|
|
|
20
20
|
import com.facebook.imagepipeline.request.ImageRequest;
|
|
21
21
|
import com.facebook.imagepipeline.request.ImageRequestBuilder;
|
|
22
22
|
import com.facebook.react.views.imagehelper.ImageSource;
|
|
23
|
+
|
|
23
24
|
import org.maplibre.android.maps.MapLibreMap;
|
|
24
25
|
import org.maplibre.android.maps.Style;
|
|
25
26
|
|
|
@@ -27,7 +28,6 @@ import java.io.File;
|
|
|
27
28
|
import java.lang.ref.WeakReference;
|
|
28
29
|
import java.util.AbstractMap;
|
|
29
30
|
import java.util.ArrayList;
|
|
30
|
-
import java.util.HashMap;
|
|
31
31
|
import java.util.List;
|
|
32
32
|
import java.util.Map;
|
|
33
33
|
|
|
@@ -94,7 +94,7 @@ public class DownloadMapImageTask extends AsyncTask<Map.Entry<String, ImageEntry
|
|
|
94
94
|
// Copy the bitmap to make sure it doesn't get recycled when we release
|
|
95
95
|
// the fresco reference.
|
|
96
96
|
.copy(Bitmap.Config.ARGB_8888, true);
|
|
97
|
-
bitmap.setDensity((int) ((double) DisplayMetrics.DENSITY_DEFAULT * imageEntry.
|
|
97
|
+
bitmap.setDensity((int) ((double) DisplayMetrics.DENSITY_DEFAULT * imageEntry.scale));
|
|
98
98
|
images.add(new AbstractMap.SimpleEntry<>(object.getKey(), new DownloadedImage(object.getKey(), bitmap, imageEntry)));
|
|
99
99
|
} else {
|
|
100
100
|
FLog.e(LOG_TAG, "Failed to load bitmap from: " + uri);
|
|
@@ -111,7 +111,7 @@ public class DownloadMapImageTask extends AsyncTask<Map.Entry<String, ImageEntry
|
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
} else {
|
|
114
|
-
//
|
|
114
|
+
// Local asset required from JS require('image.png') or import icon from 'image.png' while in release mode
|
|
115
115
|
Bitmap bitmap = BitmapUtils.getBitmapFromResource(context, uri, getBitmapOptions(metrics, imageEntry.scale));
|
|
116
116
|
if (bitmap != null) {
|
|
117
117
|
images.add(new AbstractMap.SimpleEntry<>(object.getKey(), new DownloadedImage(object.getKey(), bitmap, imageEntry)));
|
|
@@ -145,9 +145,11 @@ public class DownloadMapImageTask extends AsyncTask<Map.Entry<String, ImageEntry
|
|
|
145
145
|
BitmapFactory.Options options = new BitmapFactory.Options();
|
|
146
146
|
options.inScreenDensity = metrics.densityDpi;
|
|
147
147
|
options.inTargetDensity = metrics.densityDpi;
|
|
148
|
-
|
|
148
|
+
|
|
149
|
+
if (scale != ImageEntry.DEFAULT_SCALE) {
|
|
149
150
|
options.inDensity = (int) ((double) DisplayMetrics.DENSITY_DEFAULT * scale);
|
|
150
151
|
}
|
|
152
|
+
|
|
151
153
|
return options;
|
|
152
154
|
}
|
|
153
155
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
package org.maplibre.reactnative.utils
|
|
2
|
+
|
|
3
|
+
class ImageEntry(
|
|
4
|
+
@JvmField var uri: String?,
|
|
5
|
+
scale: Double?,
|
|
6
|
+
@JvmField var sdf: Boolean,
|
|
7
|
+
) {
|
|
8
|
+
@JvmField
|
|
9
|
+
var scale: Double = scale ?: DEFAULT_SCALE
|
|
10
|
+
|
|
11
|
+
companion object {
|
|
12
|
+
const val DEFAULT_SCALE: Double = 1.0
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -100,23 +100,28 @@ typedef NS_ENUM(NSInteger, MLRNImageQueueOperationState) {
|
|
|
100
100
|
}
|
|
101
101
|
__weak MLRNImageQueueOperation *weakSelf = self;
|
|
102
102
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
103
|
-
[weakSelf
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
103
|
+
[weakSelf
|
|
104
|
+
setCancellationBlock:[[weakSelf.bridge moduleForName:@"ImageLoader"
|
|
105
|
+
lazilyLoadIfNecessary:YES]
|
|
106
|
+
loadImageWithURLRequest:weakSelf.urlRequest
|
|
107
|
+
size:CGSizeZero
|
|
108
|
+
scale:weakSelf.scale
|
|
109
|
+
clipped:YES
|
|
110
|
+
resizeMode:RCTResizeModeStretch
|
|
111
|
+
progressBlock:^(int64_t progress, int64_t total) {
|
|
112
|
+
// No-op
|
|
113
|
+
}
|
|
114
|
+
partialLoadBlock:^(UIImage *image) {
|
|
115
|
+
// No-op
|
|
116
|
+
}
|
|
117
|
+
completionBlock:^void(NSError *error, UIImage *image) {
|
|
118
|
+
if (image && weakSelf.sdf) {
|
|
119
|
+
image = [image
|
|
120
|
+
imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
|
|
121
|
+
}
|
|
122
|
+
weakSelf.completionHandler(error, image);
|
|
123
|
+
[weakSelf setState:IOState_Finished except:IOState_Finished];
|
|
124
|
+
}]];
|
|
120
125
|
if ([weakSelf setState:IOState_Executing
|
|
121
126
|
only:IOState_Initial] == IOState_CancelledDoNotExecute) {
|
|
122
127
|
[weakSelf callCancellationBlock];
|
|
@@ -10,15 +10,20 @@
|
|
|
10
10
|
|
|
11
11
|
@property (nonatomic, weak) RCTBridge *bridge;
|
|
12
12
|
|
|
13
|
-
@property (nonatomic, strong) MLRNMapView *map;
|
|
13
|
+
@property (nonatomic, strong) MLRNMapView * _Nullable map;
|
|
14
|
+
@property (nonatomic, strong, nonnull) NSMutableArray<id<RCTComponent>> *reactSubviews;
|
|
14
15
|
|
|
15
|
-
@property (nonatomic, strong) NSDictionary
|
|
16
|
-
@property (nonatomic, strong) NSArray<NSString *> *nativeImages;
|
|
16
|
+
@property (nonatomic, strong, nonnull) NSDictionary * images;
|
|
17
17
|
|
|
18
|
-
@property (nonatomic, copy)
|
|
19
|
-
@property (nonatomic, assign) BOOL hasOnImageMissing;
|
|
18
|
+
@property (nonatomic, copy, nullable) RCTDirectEventBlock onImageMissing;
|
|
20
19
|
|
|
21
|
-
- (
|
|
22
|
-
- (void)
|
|
20
|
+
- (void)addToMap;
|
|
21
|
+
- (void)removeFromMap;
|
|
22
|
+
|
|
23
|
+
- (BOOL)addMissingImageToStyle:(NSString *_Nonnull)imageName;
|
|
24
|
+
- (void)sendImageMissingEvent:(NSString *_Nonnull)imageName;
|
|
25
|
+
|
|
26
|
+
- (void)insertReactSubview:(id<RCTComponent>_Nullable)subview atIndex:(NSInteger)atIndex;
|
|
27
|
+
- (void)removeReactSubview:(id<RCTComponent>_Nullable)subview;
|
|
23
28
|
|
|
24
29
|
@end
|
|
@@ -9,76 +9,149 @@
|
|
|
9
9
|
|
|
10
10
|
static UIImage *_placeHolderImage;
|
|
11
11
|
|
|
12
|
+
- (instancetype)initWithFrame:(CGRect)frame {
|
|
13
|
+
if (self = [super initWithFrame:frame]) {
|
|
14
|
+
_reactSubviews = [[NSMutableArray alloc] init];
|
|
15
|
+
}
|
|
16
|
+
return self;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
#pragma clang diagnostic push
|
|
20
|
+
#pragma clang diagnostic ignored "-Wobjc-missing-super-calls"
|
|
21
|
+
- (void)insertReactSubview:(id<RCTComponent>)subview atIndex:(NSInteger)atIndex {
|
|
22
|
+
[_reactSubviews insertObject:subview atIndex:atIndex];
|
|
23
|
+
}
|
|
24
|
+
#pragma clang diagnostic pop
|
|
25
|
+
|
|
26
|
+
#pragma clang diagnostic push
|
|
27
|
+
#pragma clang diagnostic ignored "-Wobjc-missing-super-calls"
|
|
28
|
+
- (void)removeReactSubview:(id<RCTComponent>)subview {
|
|
29
|
+
[_reactSubviews removeObject:subview];
|
|
30
|
+
}
|
|
31
|
+
#pragma clang diagnostic pop
|
|
32
|
+
|
|
33
|
+
#pragma clang diagnostic push
|
|
34
|
+
#pragma clang diagnostic ignored "-Wobjc-missing-super-calls"
|
|
35
|
+
- (NSArray<id<RCTComponent>> *)reactSubviews {
|
|
36
|
+
return _reactSubviews;
|
|
37
|
+
}
|
|
38
|
+
#pragma clang diagnostic pop
|
|
39
|
+
|
|
12
40
|
- (void)addToMap {
|
|
13
41
|
if (self.map.style == nil) {
|
|
14
42
|
return;
|
|
15
43
|
}
|
|
16
|
-
[self
|
|
17
|
-
[self _addRemoteImages:_images];
|
|
44
|
+
[self _processImages:_images];
|
|
18
45
|
}
|
|
19
46
|
|
|
20
47
|
- (void)removeFromMap {
|
|
21
48
|
if (self.map.style == nil) {
|
|
22
49
|
return;
|
|
23
50
|
}
|
|
24
|
-
|
|
25
51
|
[self _removeImages];
|
|
26
52
|
}
|
|
27
53
|
|
|
28
54
|
- (void)_removeImages {
|
|
29
|
-
if (
|
|
30
|
-
|
|
55
|
+
if (_images == nil || _images.count == 0) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
31
58
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
59
|
+
for (NSString *imageName in _images.allKeys) {
|
|
60
|
+
[self.map.style removeImageForName:imageName];
|
|
35
61
|
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
- (BOOL)_isNativeImageString:(NSString *)value {
|
|
65
|
+
return [UIImage imageNamed:value] != nil;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
- (void)_processImages:(NSDictionary *)images {
|
|
69
|
+
if (!images || images.count == 0) return;
|
|
36
70
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
71
|
+
NSMutableDictionary *nativeAssets = [NSMutableDictionary new];
|
|
72
|
+
NSMutableDictionary *remoteImages = [NSMutableDictionary new];
|
|
73
|
+
|
|
74
|
+
for (NSString *imageName in images.allKeys) {
|
|
75
|
+
id value = images[imageName];
|
|
76
|
+
|
|
77
|
+
if ([value isKindOfClass:[NSString class]]) {
|
|
78
|
+
NSString *stringValue = (NSString *)value;
|
|
79
|
+
if ([self _isNativeImageString:stringValue]) {
|
|
80
|
+
nativeAssets[imageName] = stringValue;
|
|
81
|
+
} else {
|
|
82
|
+
remoteImages[imageName] = @{@"uri" : stringValue};
|
|
83
|
+
}
|
|
84
|
+
} else if ([value isKindOfClass:[NSDictionary class]]) {
|
|
85
|
+
remoteImages[imageName] = value;
|
|
40
86
|
}
|
|
41
87
|
}
|
|
88
|
+
|
|
89
|
+
// Add native assets first (synchronous)
|
|
90
|
+
if (nativeAssets.count > 0) {
|
|
91
|
+
[self _addNativeImages:nativeAssets];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Then add remote images (may be async)
|
|
95
|
+
if (remoteImages.count > 0) {
|
|
96
|
+
[self _addRemoteImages:remoteImages];
|
|
97
|
+
}
|
|
42
98
|
}
|
|
43
99
|
|
|
44
100
|
- (BOOL)addMissingImageToStyle:(NSString *)imageName {
|
|
45
|
-
if (
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
101
|
+
if (_images == nil) return false;
|
|
102
|
+
|
|
103
|
+
id value = _images[imageName];
|
|
104
|
+
if (value == nil) return false;
|
|
49
105
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
[self
|
|
106
|
+
if ([value isKindOfClass:[NSString class]]) {
|
|
107
|
+
NSString *stringValue = (NSString *)value;
|
|
108
|
+
if ([self _isNativeImageString:stringValue]) {
|
|
109
|
+
[self _addNativeImages:@{imageName : stringValue}];
|
|
110
|
+
} else {
|
|
111
|
+
[self _addRemoteImages:@{imageName : @{@"uri" : stringValue}}];
|
|
112
|
+
}
|
|
113
|
+
return true;
|
|
114
|
+
} else if ([value isKindOfClass:[NSDictionary class]]) {
|
|
115
|
+
[self _addRemoteImages:@{imageName : value}];
|
|
53
116
|
return true;
|
|
54
117
|
}
|
|
118
|
+
|
|
55
119
|
return false;
|
|
56
120
|
}
|
|
57
121
|
|
|
58
122
|
- (void)sendImageMissingEvent:(NSString *)imageName {
|
|
59
|
-
NSDictionary *payload = @{@"
|
|
123
|
+
NSDictionary *payload = @{@"image" : imageName};
|
|
60
124
|
MLRNEvent *event = [MLRNEvent makeEvent:RCT_MLRN_MISSING_IMAGE withPayload:payload];
|
|
61
125
|
if (_onImageMissing) {
|
|
62
126
|
_onImageMissing([event toJSON]);
|
|
63
127
|
}
|
|
64
128
|
}
|
|
65
129
|
|
|
66
|
-
- (void)_addNativeImages:(
|
|
67
|
-
if (!nativeImages) return;
|
|
130
|
+
- (void)_addNativeImages:(NSDictionary *)nativeImages {
|
|
131
|
+
if (!nativeImages || nativeImages.count == 0) return;
|
|
68
132
|
|
|
69
|
-
for (NSString *imageName in nativeImages) {
|
|
70
|
-
// only add native images if they are not in the style yet (similar to [MLRNUtils fetchImages:
|
|
71
|
-
// style:])
|
|
133
|
+
for (NSString *imageName in nativeImages.allKeys) {
|
|
72
134
|
if (![self.map.style imageForName:imageName]) {
|
|
73
|
-
|
|
74
|
-
|
|
135
|
+
// Get the asset name from the dictionary value
|
|
136
|
+
NSString *assetName = nativeImages[imageName];
|
|
137
|
+
if ([assetName isKindOfClass:[NSString class]]) {
|
|
138
|
+
UIImage *image = [UIImage imageNamed:assetName];
|
|
139
|
+
if (image) {
|
|
140
|
+
[self.map.style setImage:image forName:imageName];
|
|
141
|
+
}
|
|
142
|
+
}
|
|
75
143
|
}
|
|
76
144
|
}
|
|
77
145
|
}
|
|
78
146
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
147
|
+
/**
|
|
148
|
+
* Add remote images with async loading.
|
|
149
|
+
* remoteImages is a dictionary where values are { uri: string, scale?: number, sdf?: boolean }
|
|
150
|
+
*/
|
|
151
|
+
- (void)_addRemoteImages:(NSDictionary *)remoteImages {
|
|
152
|
+
if (!remoteImages || remoteImages.count == 0) return;
|
|
153
|
+
|
|
154
|
+
NSMutableDictionary *missingImages = [NSMutableDictionary new];
|
|
82
155
|
|
|
83
156
|
// Add image placeholder for images that are not yet available in the style. This way
|
|
84
157
|
// we can load the images asynchronously and add the GeoJSONSource to the map without delay.
|
|
@@ -90,7 +163,7 @@ static UIImage *_placeHolderImage;
|
|
|
90
163
|
for (NSString *imageName in remoteImages.allKeys) {
|
|
91
164
|
if (![self.map.style imageForName:imageName]) {
|
|
92
165
|
[self.map.style setImage:[MLRNImages placeholderImage] forName:imageName];
|
|
93
|
-
|
|
166
|
+
missingImages[imageName] = remoteImages[imageName];
|
|
94
167
|
}
|
|
95
168
|
}
|
|
96
169
|
|
|
@@ -98,21 +171,13 @@ static UIImage *_placeHolderImage;
|
|
|
98
171
|
// forceUpdate to ensure the placeholder images are updated
|
|
99
172
|
[MLRNUtils fetchImages:_bridge
|
|
100
173
|
style:self.map.style
|
|
101
|
-
objects:
|
|
174
|
+
objects:missingImages
|
|
102
175
|
forceUpdate:true
|
|
103
176
|
callback:^{
|
|
104
177
|
}];
|
|
105
178
|
}
|
|
106
179
|
}
|
|
107
180
|
|
|
108
|
-
- (BOOL)_hasImages {
|
|
109
|
-
return _images != nil && _images.count > 0;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
- (BOOL)_hasNativeImages {
|
|
113
|
-
return _nativeImages != nil && _nativeImages.count > 0;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
181
|
+ (UIImage *)placeholderImage {
|
|
117
182
|
if (_placeHolderImage) return _placeHolderImage;
|
|
118
183
|
UIGraphicsBeginImageContextWithOptions(CGSizeMake(1, 1), NO, 0.0);
|