Skip to content

Commit 69b44bd

Browse files
authored
Introduce Color Theme API (aka LUT) + example (#2928)
* Introduce Color Theme API (aka LUT) + example * Add experimental tag to compose style * update api * fix doc * Fix dokka issue * Fix formatting * Fix changelog * Fix visibility of initial StyleColorTheme * Add tests and improve Compose API * Fix changelog * Update changelog * Mark experimental data class + more comments to samples * JvmName fix * Create convenient rememberStyleColorTheme * Remove unnecessary StyleColorTheme wrapper and use ColorTheme directly * Better changelog wording * Use non-null ColorTheme in StyleState * Add StyleColorTheme to hold different color theme values * Fix function name * Update api * Rebase and fix changelog * Rename isInitial to isStyleDefault
1 parent 298a162 commit 69b44bd

File tree

32 files changed

+811
-3
lines changed

32 files changed

+811
-3
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ Mapbox welcomes participation and contributions from everyone.
1111
* Add `*UseTheme` String style properties to override color theme for particular color properties in all layers and their Compose counterparts.
1212
* Remove experimental `ShadowQuality` properties from direct light layer.
1313
* Extend tilecover for elevated roads avoiding missing road segments.
14+
* Introduce experimental Color Theme API, which allows changing the style of the map using `Style.setStyleColorTheme`.
15+
* [compose] Introduce experimental `StyleState.colorTheme` state for setting `ColorTheme` for the style.
1416

1517
## Bug fixes 🐞
1618
* Prefer last used anchor for Dynamic View Annotation placement.

app/src/main/AndroidManifest.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1426,5 +1426,17 @@
14261426
android:name="android.support.PARENT_ACTIVITY"
14271427
android:value=".ExampleOverviewActivity" />
14281428
</activity>
1429+
<activity
1430+
android:name=".examples.style.ColorThemeActivity"
1431+
android:description="@string/description_color_theme"
1432+
android:exported="true"
1433+
android:label="@string/activity_color_theme">
1434+
<meta-data
1435+
android:name="@string/category"
1436+
android:value="@string/category_styles" />
1437+
<meta-data
1438+
android:name="android.support.PARENT_ACTIVITY"
1439+
android:value=".ExampleOverviewActivity" />
1440+
</activity>
14291441
</application>
14301442
</manifest>
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package com.mapbox.maps.testapp.examples.style
2+
3+
import android.os.Bundle
4+
import android.view.Menu
5+
import android.view.MenuItem
6+
import androidx.appcompat.app.AppCompatActivity
7+
import com.mapbox.geojson.Point
8+
import com.mapbox.maps.MapInitOptions
9+
import com.mapbox.maps.MapView
10+
import com.mapbox.maps.MapboxExperimental
11+
import com.mapbox.maps.MapboxMap
12+
import com.mapbox.maps.Style
13+
import com.mapbox.maps.dsl.cameraOptions
14+
import com.mapbox.maps.extension.style.color.colorTheme
15+
import com.mapbox.maps.extension.style.style
16+
import com.mapbox.maps.testapp.R
17+
import com.mapbox.maps.testapp.utils.BitmapUtils.bitmapFromDrawableRes
18+
19+
/**
20+
* Example showcasing the usage of Color Theme API.
21+
* To create custom LUT, you can follow [this](https://docs.mapbox.com/help/tutorials/create-a-custom-color-theme/) guide.
22+
*/
23+
@OptIn(MapboxExperimental::class)
24+
class ColorThemeActivity : AppCompatActivity() {
25+
26+
private lateinit var mapboxMap: MapboxMap
27+
28+
override fun onCreate(savedInstanceState: Bundle?) {
29+
super.onCreate(savedInstanceState)
30+
MapView(
31+
this,
32+
MapInitOptions(
33+
this, cameraOptions = START_CAMERA_POSITION
34+
)
35+
).also { mapView ->
36+
mapboxMap = mapView.mapboxMap
37+
setContentView(mapView)
38+
mapboxMap.loadStyle(
39+
style(Style.MAPBOX_STREETS) {
40+
+colorTheme(base64 = BASE64_ENCODED_RED_THEME)
41+
}
42+
)
43+
}
44+
}
45+
46+
override fun onCreateOptionsMenu(menu: Menu): Boolean {
47+
menuInflater.inflate(R.menu.menu_color_theme, menu)
48+
return true
49+
}
50+
51+
override fun onOptionsItemSelected(item: MenuItem): Boolean {
52+
when (item.itemId) {
53+
R.id.menu_action_color_theme_image -> {
54+
mapboxMap.setStyleColorTheme(bitmap = bitmapFromDrawableRes(R.drawable.monochrome_lut))
55+
}
56+
57+
R.id.menu_action_color_theme_base64 -> {
58+
mapboxMap.setStyleColorTheme(base64 = BASE64_ENCODED_RED_THEME)
59+
}
60+
61+
R.id.menu_action_color_theme_reset -> {
62+
mapboxMap.setStyleColorTheme(colorTheme = null)
63+
}
64+
65+
else -> return super.onOptionsItemSelected(item)
66+
}
67+
return true
68+
}
69+
70+
companion object {
71+
/**
72+
* Base64 encoded version of a custom LUT (Look-Up Table) image.
73+
* LUT image can be created by following [this](https://docs.mapbox.com/help/tutorials/create-a-custom-color-theme/) guide.
74+
*
75+
* To convert your LUT image to a Base64 string, you can use an online tool or a script.
76+
* For example, you can use the following command in a terminal:
77+
*
78+
* ```sh
79+
* base64 -i path/to/your/lut-image.png -o output.txt
80+
* ```
81+
*/
82+
private const val BASE64_ENCODED_RED_THEME =
83+
"iVBORw0KGgoAAAANSUhEUgAABAAAAAAgCAYAAACM/gqmAAAAAXNSR0IArs4c6QAABSFJREFUeF7t3cFO40AQAFHnBv//wSAEEgmJPeUDsid5h9VqtcMiZsfdPdXVzmVZlo+3ZVm+fr3//L7257Lm778x+prL1ff0/b//H+z/4/M4OkuP/n70Nc7f+nnb+yzb//sY6vxt5xXPn+dP/aH+GsXJekb25izxR/ypZ6ucUefv9g4z2jPP3/HPHwAAgABAABgACIACkAAsAL1SD4yKWQAUAHUBdAG8buKNYoYL8PEX4FcHQAAAAAAAAAAAAAAAAAAAAAAA8LAeGF1mABAABAABQACQbZP7+hk5AwACAAAAAAAAAAAAAAAAAAAAAAAA4EE9AICMx4QBAAAAAAAANgvJsxGQV1dA/PxmMEtxU9YoABQACoC5CgDxX/wvsb2sEf/Ff/Ff/N96l5n73+/5YAB4CeBqx2VvMqXgUfD2npkzBCAXEBeQcrkoa5x/FxAXEBcQF5A2Wy3/t32qNYr8I//Mln+MABgBMAJgBMAIgBEAIwBGAIwAGAEwAmAE4K4eAGCNQIw+qQ0AmQ+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/6gEABAB5RgACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAN/UAAPKcAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgEFNODICRtDkDO/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOhvlPUWem+h9xKQ+V4CUt9wO6KZnn/Pv+ff8z/bW5DFP59CUnJbWSP+iX/iX78znqED/urxnwHAAGAAMAAYAAwABgADgAHAAGAAMAAYAAwABgADoNMcHUAdQAQcAUfAe8xEwH0O86t3IPz8OvClu17WqD/UH+oP9cf1Gdia01d/LQsDgAHAAGAAMAAYAAwABgADgAHAAGAAMAAYAAwABkCnSQwABgACj8Aj8D1mItAMAB1wHfDS3S5r5F/5V/6Vf3XAW12h/mIArHY89iZTAAQA2XtmBKAWqOslyf4rgBXACmAFcIur8k/bJ/mnQTr5V/6Vf+fKv0YAjAAYATACYATACIARACMARgCMABgBMAJgBMAIgBEAIwCdZuiA64AjwAgwAtxjpg6cDlztLlLA7/Pr1gueyr56/jx/5ZzUNeof9Y/6R/0zk4HGAGAAMAAYAAwABgADgAHAAGAAMAAYAAwABgADgAHQaQ4DgAGAgCPgCHiPmTqQOpC1u8gAYACMjAf5V/6Vf+XfmTrQ8l97v8Z/5X8GAAOAAcAAYAAwABgADAAGAAOAAcAAYAAwABgADIBO0xgADAAdCB0IHYgeMxkADAAdkGM7IPbf/pfuWlmj/lH/qH/UPzMZGAwABgADgAHAAGAAMAAYAAwABgADgAHAAGAAMAAYAJ3mMAAYAAg4Ao6A95jJAGAA6EDrQJfuclkj/8q/8q/8O1MHWv47Nv8xABgADAAGAAOAAcAAYAAwABgADAAGAAOAAcAAYAB0msYAYADoQOhA6ED0mMkAYADogBzbAbH/9r/YFWWN+kf9o/5R/8xkYDAAGAAMAAYAA4ABwABgADAAGAAMAAYAA4ABwABgAHSawwBgACDgCDgC3mMmA4ABoAOtA126y2WN/Cv/yr/y70wdaPnv2PzHAGAAMAAYAAwABgADgAHAAGAAMAAYAAwABgADgAHQaRoDgAGgA6EDoQPRYyYDgAGgA3JsB8T+2/9iV5Q16h/1j/pH/TOTgcEAYAAwABgADAAGAAOAAcAAYAAwABgADAAGAAPgyQ2AT4NBIB3ew5dkAAAAAElFTkSuQmCC"
84+
private const val LATITUDE = 40.72
85+
private const val LONGITUDE = -73.99
86+
private val CENTER = Point.fromLngLat(LONGITUDE, LATITUDE)
87+
private val START_CAMERA_POSITION = cameraOptions {
88+
center(CENTER)
89+
zoom(11.0)
90+
pitch(45.0)
91+
}
92+
}
93+
}
7.13 KB
Loading
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<menu xmlns:android="http://schemas.android.com/apk/res/android">
3+
<item
4+
android:id="@+id/menu_action_color_theme_image"
5+
android:title="@string/color_theme_monochrome"/>
6+
<item
7+
android:id="@+id/menu_action_color_theme_base64"
8+
android:title="@string/color_theme_red"/>
9+
<item
10+
android:id="@+id/menu_action_color_theme_reset"
11+
android:title="@string/color_theme_reset"/>
12+
</menu>

app/src/main/res/values/example_descriptions.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,5 @@
111111
<string name="description_standard_interactions">Showcase of the Standard Style interactions.</string>
112112
<string name="description_location_component_model_animation">Animate 3D location puck on the map</string>
113113
<string name="description_precipitation">Showcase rain and snow effects.</string>
114+
<string name="description_color_theme">Showcase color theme.</string>
114115
</resources>

app/src/main/res/values/example_titles.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,4 +113,5 @@
113113
<string name="activity_standard_interactions">Standard Style interactions</string>
114114
<string name="activity_location_component_model_animation">Location component model animation</string>
115115
<string name="activity_precipitation">Precipitations example</string>
116+
<string name="activity_color_theme">Color theme example</string>
116117
</resources>

app/src/main/res/values/strings.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,4 +184,7 @@
184184
<string name="clip_symbol">Clip symbol</string>
185185
<string name="clip_model_and_symbol">Clip model and symbol</string>
186186
<string name="clip_reset">Reset</string>
187+
<string name="color_theme_red">Red</string>
188+
<string name="color_theme_reset">Reset</string>
189+
<string name="color_theme_monochrome">Monochrome</string>
187190
</resources>

compose-app/src/main/AndroidManifest.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,16 @@
317317
android:name="@string/category"
318318
android:value="@string/category_styles" />
319319
</activity>
320+
<activity
321+
android:name=".examples.style.ColorThemeActivity"
322+
android:description="@string/description_color_theme"
323+
android:exported="true"
324+
android:label="@string/activity_color_theme"
325+
android:parentActivityName=".ExampleOverviewActivity">
326+
<meta-data
327+
android:name="@string/category"
328+
android:value="@string/category_styles" />
329+
</activity>
320330
</application>
321331

322332
</manifest>
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package com.mapbox.maps.compose.testapp.examples.style
2+
3+
import android.os.Bundle
4+
import androidx.activity.ComponentActivity
5+
import androidx.activity.compose.setContent
6+
import androidx.compose.foundation.layout.Column
7+
import androidx.compose.foundation.layout.fillMaxSize
8+
import androidx.compose.foundation.layout.padding
9+
import androidx.compose.foundation.shape.RoundedCornerShape
10+
import androidx.compose.material.FloatingActionButton
11+
import androidx.compose.material.Text
12+
import androidx.compose.runtime.getValue
13+
import androidx.compose.runtime.mutableStateOf
14+
import androidx.compose.runtime.remember
15+
import androidx.compose.runtime.setValue
16+
import androidx.compose.ui.Modifier
17+
import androidx.compose.ui.unit.dp
18+
import com.mapbox.geojson.Point
19+
import com.mapbox.maps.MapboxExperimental
20+
import com.mapbox.maps.Style
21+
import com.mapbox.maps.compose.testapp.ExampleScaffold
22+
import com.mapbox.maps.compose.testapp.R
23+
import com.mapbox.maps.compose.testapp.ui.theme.MapboxMapComposeTheme
24+
import com.mapbox.maps.dsl.cameraOptions
25+
import com.mapbox.maps.extension.compose.MapboxMap
26+
import com.mapbox.maps.extension.compose.animation.viewport.rememberMapViewportState
27+
import com.mapbox.maps.extension.compose.style.GenericStyle
28+
import com.mapbox.maps.extension.compose.style.rememberColorTheme
29+
import com.mapbox.maps.extension.compose.style.rememberStyleColorTheme
30+
import com.mapbox.maps.extension.compose.style.rememberStyleState
31+
import com.mapbox.maps.extension.style.color.colorTheme
32+
33+
/**
34+
* Example showcasing the usage of Color Theme API.
35+
* To create custom LUT, you can follow [this](https://docs.mapbox.com/help/tutorials/create-a-custom-color-theme/) guide.
36+
*/
37+
public class ColorThemeActivity : ComponentActivity() {
38+
39+
@OptIn(MapboxExperimental::class)
40+
override fun onCreate(savedInstanceState: Bundle?) {
41+
super.onCreate(savedInstanceState)
42+
setContent {
43+
// creating and style color theme using base64 encoded LUT image
44+
val base64ColorTheme = rememberColorTheme(base64 = BASE64_ENCODED_RED_THEME)
45+
// creating monochrome StyleColorTheme from a drawable LUT resource
46+
val monochromeColorTheme = rememberColorTheme(resourceId = R.drawable.monochrome_lut)
47+
// holder for current color theme
48+
var currentColorTheme by remember {
49+
mutableStateOf(base64ColorTheme)
50+
}
51+
var currentStyleColorTheme = rememberStyleColorTheme(currentColorTheme)
52+
53+
MapboxMapComposeTheme {
54+
ExampleScaffold(
55+
floatingActionButton = {
56+
Column {
57+
FloatingActionButton(
58+
modifier = Modifier.padding(bottom = 10.dp),
59+
onClick = {
60+
currentColorTheme = monochromeColorTheme
61+
},
62+
shape = RoundedCornerShape(16.dp),
63+
) {
64+
Text(modifier = Modifier.padding(10.dp), text = "Monochrome")
65+
}
66+
FloatingActionButton(
67+
modifier = Modifier.padding(bottom = 10.dp),
68+
onClick = {
69+
currentColorTheme = base64ColorTheme
70+
},
71+
shape = RoundedCornerShape(16.dp),
72+
) {
73+
Text(modifier = Modifier.padding(10.dp), text = "Red")
74+
}
75+
FloatingActionButton(
76+
modifier = Modifier.padding(bottom = 10.dp),
77+
onClick = {
78+
// creating empty style color theme which will reset color theme when set to color theme state.
79+
currentColorTheme = colorTheme()
80+
},
81+
shape = RoundedCornerShape(16.dp),
82+
) {
83+
Text(modifier = Modifier.padding(10.dp), text = "Reset")
84+
}
85+
}
86+
}
87+
) {
88+
MapboxMap(
89+
Modifier.fillMaxSize(),
90+
mapViewportState = rememberMapViewportState {
91+
setCameraOptions(START_CAMERA_POSITION)
92+
},
93+
style = {
94+
GenericStyle(
95+
style = Style.MAPBOX_STREETS,
96+
styleState = rememberStyleState {
97+
styleColorTheme = currentStyleColorTheme
98+
},
99+
)
100+
}
101+
) {
102+
}
103+
}
104+
}
105+
}
106+
}
107+
108+
public companion object {
109+
/**
110+
* Base64 encoded version of a custom LUT (Look-Up Table) image.
111+
* LUT image can be created by following [this](https://docs.mapbox.com/help/tutorials/create-a-custom-color-theme/) guide.
112+
*
113+
* To convert your LUT image to a Base64 string, you can use an online tool or a script.
114+
* For example, you can use the following command in a terminal:
115+
*
116+
* ```sh
117+
* base64 -i path/to/your/lut-image.png -o output.txt
118+
* ```
119+
*/
120+
private const val BASE64_ENCODED_RED_THEME =
121+
"iVBORw0KGgoAAAANSUhEUgAABAAAAAAgCAYAAACM/gqmAAAAAXNSR0IArs4c6QAABSFJREFUeF7t3cFO40AQAFHnBv//wSAEEgmJPeUDsid5h9VqtcMiZsfdPdXVzmVZlo+3ZVm+fr3//L7257Lm778x+prL1ff0/b//H+z/4/M4OkuP/n70Nc7f+nnb+yzb//sY6vxt5xXPn+dP/aH+GsXJekb25izxR/ypZ6ucUefv9g4z2jPP3/HPHwAAgABAABgACIACkAAsAL1SD4yKWQAUAHUBdAG8buKNYoYL8PEX4FcHQAAAAAAAAAAAAAAAAAAAAAAA8LAeGF1mABAABAABQACQbZP7+hk5AwACAAAAAAAAAAAAAAAAAAAAAAAA4EE9AICMx4QBAAAAAAAANgvJsxGQV1dA/PxmMEtxU9YoABQACoC5CgDxX/wvsb2sEf/Ff/Ff/N96l5n73+/5YAB4CeBqx2VvMqXgUfD2npkzBCAXEBeQcrkoa5x/FxAXEBcQF5A2Wy3/t32qNYr8I//Mln+MABgBMAJgBMAIgBEAIwBGAIwAGAEwAmAE4K4eAGCNQIw+qQ0AmQ+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/6gEABAB5RgACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAN/UAAPKcAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgEFNODICRtDkDO/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOhvlPUWem+h9xKQ+V4CUt9wO6KZnn/Pv+ff8z/bW5DFP59CUnJbWSP+iX/iX78znqED/urxnwHAAGAAMAAYAAwABgADgAHAAGAAMAAYAAwABgADoNMcHUAdQAQcAUfAe8xEwH0O86t3IPz8OvClu17WqD/UH+oP9cf1Gdia01d/LQsDgAHAAGAAMAAYAAwABgADgAHAAGAAMAAYAAwABkCnSQwABgACj8Aj8D1mItAMAB1wHfDS3S5r5F/5V/6Vf3XAW12h/mIArHY89iZTAAQA2XtmBKAWqOslyf4rgBXACmAFcIur8k/bJ/mnQTr5V/6Vf+fKv0YAjAAYATACYATACIARACMARgCMABgBMAJgBMAIgBEAIwCdZuiA64AjwAgwAtxjpg6cDlztLlLA7/Pr1gueyr56/jx/5ZzUNeof9Y/6R/0zk4HGAGAAMAAYAAwABgADgAHAAGAAMAAYAAwABgADgAHQaQ4DgAGAgCPgCHiPmTqQOpC1u8gAYACMjAf5V/6Vf+XfmTrQ8l97v8Z/5X8GAAOAAcAAYAAwABgADAAGAAOAAcAAYAAwABgADIBO0xgADAAdCB0IHYgeMxkADAAdkGM7IPbf/pfuWlmj/lH/qH/UPzMZGAwABgADgAHAAGAAMAAYAAwABgADgAHAAGAAMAAYAJ3mMAAYAAg4Ao6A95jJAGAA6EDrQJfuclkj/8q/8q/8O1MHWv47Nv8xABgADAAGAAOAAcAAYAAwABgADAAGAAOAAcAAYAB0msYAYADoQOhA6ED0mMkAYADogBzbAbH/9r/YFWWN+kf9o/5R/8xkYDAAGAAMAAYAA4ABwABgADAAGAAMAAYAA4ABwABgAHSawwBgACDgCDgC3mMmA4ABoAOtA126y2WN/Cv/yr/y70wdaPnv2PzHAGAAMAAYAAwABgADgAHAAGAAMAAYAAwABgADgAHQaRoDgAGgA6EDoQPRYyYDgAGgA3JsB8T+2/9iV5Q16h/1j/pH/TOTgcEAYAAwABgADAAGAAOAAcAAYAAwABgADAAGAAPgyQ2AT4NBIB3ew5dkAAAAAElFTkSuQmCC"
122+
private const val LATITUDE = 40.72
123+
private const val LONGITUDE = -73.99
124+
private val CENTER = Point.fromLngLat(LONGITUDE, LATITUDE)
125+
private val START_CAMERA_POSITION = cameraOptions {
126+
center(CENTER)
127+
zoom(11.0)
128+
pitch(45.0)
129+
}
130+
}
131+
}

0 commit comments

Comments
 (0)