Skip to content

Commit 79d6f33

Browse files
authored
Add ModelLayer support (#554)
* Add ModelLayer support * Add ModelLayer example page * Add Android support * Store test results
1 parent ab5298f commit 79d6f33

File tree

15 files changed

+670
-14
lines changed

15 files changed

+670
-14
lines changed

.circleci/config.yml

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
version: 2.1
22

3-
orbs:
3+
orbs:
44
flutter: circleci/flutter@2.0.2
55
macos: circleci/macos@2.4.1
66
gcp-cli: circleci/gcp-cli@3.1.1
@@ -14,7 +14,7 @@ commands:
1414
command: |
1515
echo "machine api.mapbox.com login mapbox password ${SDK_REGISTRY_TOKEN}" >> ~/.netrc
1616
chmod 0600 ~/.netrc
17-
17+
1818
setup-gcloud:
1919
steps:
2020
- gcp-cli/setup:
@@ -86,7 +86,7 @@ jobs:
8686
--num-flaky-test-attempts 3 \
8787
--no-record-video \
8888
--no-performance-metrics
89-
89+
9090
build-ios:
9191
parameters:
9292
workspace-path:
@@ -145,7 +145,23 @@ jobs:
145145
--test << parameters.workspace-path >>/build_products.zip \
146146
--device model=iphone13pro,version=15.7 \
147147
--timeout 5m \
148-
--num-flaky-test-attempts 3
148+
--num-flaky-test-attempts 3 2>&1 | tee firebase_test_lab_run.log
149+
- run:
150+
name: Download XCResults
151+
command: |
152+
TEST_LAB_PATH=$(cat firebase_test_lab_run.log | grep -o "test-lab.*/")
153+
154+
mkdir testlab_results
155+
gsutil -m cp -r "gs://${TEST_LAB_PATH}i*" testlab_results
156+
zip -r testlab_results.zip testlab_results
157+
when: always
158+
- store_artifacts:
159+
path: firebase_test_lab_run.log
160+
- store_artifacts:
161+
path: testlab_results.zip
162+
- store_test_results:
163+
path: testlab_results
164+
149165

150166

151167

CHANGELOG.md

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
### main
2+
3+
* Add ModelLayer API.
4+
15
### 2.0.0
26

37
#### ⚠️ Breaking changes
@@ -68,7 +72,7 @@ PolygonAnnotationOptions(
6872
)
6973
PolylineAnnotationOptions(
7074
geometry: LineString(coordinates: [
71-
Position(1.0, 2.0),
75+
Position(1.0, 2.0),
7276
Position(10.0, 20.0)
7377
]).toJson()
7478
)
@@ -93,7 +97,7 @@ PolygonAnnotationOptions(
9397
)
9498
PolylineAnnotationOptions(
9599
geometry: LineString(coordinates: [
96-
Position(1.0, 2.0),
100+
Position(1.0, 2.0),
97101
Position(10.0, 20.0)
98102
])
99103
)
@@ -145,8 +149,8 @@ Please note that the `snapshot()` method works best if the Mapbox Map is fully l
145149
* Fix multi-word enum cases decoding/encoding when being sent to/from the platform side.
146150
* [Android] Add Gradle 8 compatibility.
147151
* Introduce experimental `RasterArraySource`, note that `rasterLayers` is a get-only property and cannot be set.
148-
* Introduce `TileCacheBudget`, a property to set per-source cache budgets in either megabytes or tiles.
149-
* Expose `iconColorSaturation`, `rasterArrayBand`, `rasterElevation`, `rasterEmissiveStrength`, `hillshadeEmissiveStrength`, and `fillExtrusionEmissiveStrength` on their respective layers.
152+
* Introduce `TileCacheBudget`, a property to set per-source cache budgets in either megabytes or tiles.
153+
* Expose `iconColorSaturation`, `rasterArrayBand`, `rasterElevation`, `rasterEmissiveStrength`, `hillshadeEmissiveStrength`, and `fillExtrusionEmissiveStrength` on their respective layers.
150154
* Mark `MapboxMapsOptions.get/setWorldview()` and `MapboxMapsOptions.get/setLanguage()` as experimental.
151155
* Bump Pigeon to 17.1.2
152156
* [iOS] Fix crash in `onStyleImageMissingListener`.
@@ -267,7 +271,7 @@ We have matched screen-related units expected by the maps plugin to the units th
267271

268272
## 0.4.3
269273
### Common
270-
* Fix multiple memory leaks.
274+
* Fix multiple memory leaks.
271275

272276
## 0.4.2
273277
### Common
@@ -279,7 +283,7 @@ We have matched screen-related units expected by the maps plugin to the units th
279283
* Fix vertical scrollMode lock on gesture settings update.
280284

281285
### Android
282-
* Fix ImageStretches mapping.
286+
* Fix ImageStretches mapping.
283287

284288
## 0.4.1
285289

@@ -292,16 +296,16 @@ We have matched screen-related units expected by the maps plugin to the units th
292296
### iOS
293297
* Fix `pixelsForCoordinates` implementation.
294298

295-
## 0.4.0
299+
## 0.4.0
296300

297301
### Common
298-
* Expose `style.localizeLabels`.
302+
* Expose `style.localizeLabels`.
299303
* Expose `mapboxMap.attribution`, `mapboxMap.logo`, `mapboxMap.compass` and `mapboxMap.scaleBar` settings.
300304

301305
### iOS
302306
* Fix deployment target for iOS to 11.
303307

304-
## 0.3.0
308+
## 0.3.0
305309

306310
### Common
307311
* Rename library to `mapbox_maps_flutter` due to naming conflict to be able publish to `pub.dev`.

android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package com.mapbox.maps.mapbox_maps
33
import android.content.Context
44
import android.graphics.Bitmap
55
import com.google.gson.Gson
6+
import com.mapbox.bindgen.Expected
7+
import com.mapbox.bindgen.None
68
import com.mapbox.geojson.*
79
import com.mapbox.maps.EdgeInsets
810
import com.mapbox.maps.extension.style.layers.properties.generated.ProjectionName
@@ -524,4 +526,12 @@ fun Bitmap.toMbxImage(): MbxImage {
524526
val outputStream = ByteArrayOutputStream(byteCount)
525527
compress(Bitmap.CompressFormat.PNG, 100, outputStream)
526528
return MbxImage(width.toLong(), height.toLong(), outputStream.toByteArray())
529+
}
530+
531+
fun Expected<String, None>.handleResult(callback: (Result<Unit>) -> Unit) {
532+
if (this.isError) {
533+
callback(Result.failure(Throwable(this.error)))
534+
} else {
535+
callback(Result.success(Unit))
536+
}
527537
}

android/src/main/kotlin/com/mapbox/maps/mapbox_maps/StyleController.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,16 @@ class StyleController(private val context: Context, private val styleManager: Ma
604604
callback(Result.success(Unit))
605605
}
606606
}
607+
608+
override fun addStyleModel(modelId: String, modelUri: String, callback: (Result<Unit>) -> Unit) {
609+
styleManager.addStyleModel(modelId, modelUri)
610+
.handleResult(callback)
611+
}
612+
613+
override fun removeStyleModel(modelId: String, callback: (Result<Unit>) -> Unit) {
614+
styleManager.removeStyleModel(modelId)
615+
.handleResult(callback)
616+
}
607617
}
608618

609619
fun Any.toValue(): Value {

android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5939,6 +5939,26 @@ interface StyleManager {
59395939
* @return True if image exists, false otherwise.
59405940
*/
59415941
fun hasStyleImage(imageId: String, callback: (Result<Boolean>) -> Unit)
5942+
/**
5943+
* Adds a model to be used in the style. This API can also be used for updating
5944+
* a model. If the model for a given `modelId` was already added, it gets replaced by the new model.
5945+
*
5946+
* The model can be used in `model-id` property in model layer.
5947+
*
5948+
* @param modelId An identifier of the model.
5949+
* @param modelUri A URI for the model.
5950+
*
5951+
* @return A string describing an error if the operation was not successful, empty otherwise.
5952+
*/
5953+
fun addStyleModel(modelId: String, modelUri: String, callback: (Result<Unit>) -> Unit)
5954+
/**
5955+
* Removes a model from the style.
5956+
*
5957+
* @param modelId The identifier of the model to remove.
5958+
*
5959+
* @return A string describing an error if the operation was not successful, empty otherwise.
5960+
*/
5961+
fun removeStyleModel(modelId: String, callback: (Result<Unit>) -> Unit)
59425962
/**
59435963
* Set tile data of a custom geometry.
59445964
*
@@ -6899,6 +6919,45 @@ interface StyleManager {
68996919
channel.setMessageHandler(null)
69006920
}
69016921
}
6922+
run {
6923+
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter.StyleManager.addStyleModel$separatedMessageChannelSuffix", codec)
6924+
if (api != null) {
6925+
channel.setMessageHandler { message, reply ->
6926+
val args = message as List<Any?>
6927+
val modelIdArg = args[0] as String
6928+
val modelUriArg = args[1] as String
6929+
api.addStyleModel(modelIdArg, modelUriArg) { result: Result<Unit> ->
6930+
val error = result.exceptionOrNull()
6931+
if (error != null) {
6932+
reply.reply(wrapError(error))
6933+
} else {
6934+
reply.reply(wrapResult(null))
6935+
}
6936+
}
6937+
}
6938+
} else {
6939+
channel.setMessageHandler(null)
6940+
}
6941+
}
6942+
run {
6943+
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter.StyleManager.removeStyleModel$separatedMessageChannelSuffix", codec)
6944+
if (api != null) {
6945+
channel.setMessageHandler { message, reply ->
6946+
val args = message as List<Any?>
6947+
val modelIdArg = args[0] as String
6948+
api.removeStyleModel(modelIdArg) { result: Result<Unit> ->
6949+
val error = result.exceptionOrNull()
6950+
if (error != null) {
6951+
reply.reply(wrapError(error))
6952+
} else {
6953+
reply.reply(wrapResult(null))
6954+
}
6955+
}
6956+
}
6957+
} else {
6958+
channel.setMessageHandler(null)
6959+
}
6960+
}
69026961
run {
69036962
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter.StyleManager.invalidateStyleCustomGeometrySourceTile$separatedMessageChannelSuffix", codec)
69046963
if (api != null) {
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// This file is generated.
2+
import 'dart:convert';
3+
import 'package:flutter/material.dart' hide Visibility;
4+
import 'package:flutter/services.dart';
5+
import 'package:flutter_test/flutter_test.dart';
6+
import 'package:integration_test/integration_test.dart';
7+
import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart';
8+
import 'package:mapbox_maps_example/empty_map_widget.dart' as app;
9+
10+
void main() {
11+
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
12+
13+
testWidgets('Add ModelLayer', (WidgetTester tester) async {
14+
final mapFuture = app.main();
15+
await tester.pumpAndSettle();
16+
final mapboxMap = await mapFuture;
17+
18+
await mapboxMap.style.addLayer(ModelLayer(
19+
id: 'layer',
20+
sourceId: "composite",
21+
sourceLayer: "building",
22+
visibility: Visibility.NONE,
23+
minZoom: 1.0,
24+
maxZoom: 20.0,
25+
slot: LayerSlot.BOTTOM,
26+
modelId: "abc",
27+
modelAmbientOcclusionIntensity: 1.0,
28+
modelCastShadows: true,
29+
modelColor: Colors.red.value,
30+
modelColorMixIntensity: 1.0,
31+
modelCutoffFadeRange: 1.0,
32+
modelEmissiveStrength: 1.0,
33+
modelHeightBasedEmissiveStrengthMultiplier: [0.0, 1.0, 2.0, 3.0, 4.0],
34+
modelOpacity: 1.0,
35+
modelReceiveShadows: true,
36+
modelRotation: [0.0, 1.0, 2.0],
37+
modelRoughness: 1.0,
38+
modelScale: [0.0, 1.0, 2.0],
39+
modelScaleMode: ModelScaleMode.MAP,
40+
modelTranslation: [0.0, 1.0, 2.0],
41+
modelType: ModelType.COMMON_3D,
42+
));
43+
var layer = await mapboxMap.style.getLayer('layer') as ModelLayer;
44+
expect('composite', layer.sourceId);
45+
expect(layer.minZoom, 1);
46+
expect(layer.maxZoom, 20);
47+
expect(layer.slot, LayerSlot.BOTTOM);
48+
expect(layer.visibility, Visibility.NONE);
49+
expect(layer.modelId, "abc");
50+
expect(layer.modelAmbientOcclusionIntensity, 1.0);
51+
expect(layer.modelCastShadows, true);
52+
expect(layer.modelColor, Colors.red.value);
53+
expect(layer.modelColorMixIntensity, 1.0);
54+
expect(layer.modelCutoffFadeRange, 1.0);
55+
expect(layer.modelEmissiveStrength, 1.0);
56+
expect(layer.modelHeightBasedEmissiveStrengthMultiplier,
57+
[0.0, 1.0, 2.0, 3.0, 4.0]);
58+
expect(layer.modelOpacity, 1.0);
59+
expect(layer.modelReceiveShadows, true);
60+
expect(layer.modelRotation, [0.0, 1.0, 2.0]);
61+
expect(layer.modelRoughness, 1.0);
62+
expect(layer.modelScale, [0.0, 1.0, 2.0]);
63+
expect(layer.modelScaleMode, ModelScaleMode.MAP);
64+
expect(layer.modelTranslation, [0.0, 1.0, 2.0]);
65+
expect(layer.modelType, ModelType.COMMON_3D);
66+
});
67+
}
68+
// End of generated file.

example/ios/Podfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ EXTERNAL SOURCES:
4242

4343
SPEC CHECKSUMS:
4444
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
45-
integration_test: ce0a3ffa1de96d1a89ca0ac26fca7ea18a749ef4
45+
integration_test: 13825b8a9334a850581300559b8839134b124670
4646
mapbox_maps_flutter: 2ef4455f071d3c707c30d37fc2990c7c927fb844
4747
MapboxCommon: 6acbd8ff41d66abf498e1558b0739f25c562945a
4848
MapboxCoreMaps: f306bb1b10ebe995a2247b40e99322ab7f9b8071

example/lib/main.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'package:mapbox_maps_example/animation.dart';
44
import 'package:mapbox_maps_example/camera.dart';
55
import 'package:mapbox_maps_example/circle_annotations.dart';
66
import 'package:mapbox_maps_example/cluster.dart';
7+
import 'package:mapbox_maps_example/model_layer.dart';
78
import 'package:mapbox_maps_example/ornaments.dart';
89
import 'package:mapbox_maps_example/geojson_line.dart';
910
import 'package:mapbox_maps_example/image_source.dart';
@@ -46,6 +47,7 @@ final List<ExamplePage> _allPages = <ExamplePage>[
4647
AnimatedRoutePage(),
4748
SnapshotterPage(),
4849
TrafficRouteLinePage(),
50+
ModelLayerPage(),
4951
];
5052

5153
class MapsDemo extends StatelessWidget {

0 commit comments

Comments
 (0)