core: Update Hillshade Algorithms and add Color-Relief Layer support (#3965)

This commit is contained in:
Andrew Calcutt
2026-02-25 20:50:06 -05:00
committed by GitHub
parent 9682e09ae4
commit 369af13ead
187 changed files with 8804 additions and 2352 deletions

View File

@@ -3,9 +3,10 @@ package org.maplibre.android.integration
import androidx.test.filters.LargeTest
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import androidx.test.rule.ActivityTestRule
import androidx.test.uiautomator.UiSelector
import org.junit.Ignore
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
import org.maplibre.android.testapp.activity.fragment.ViewPagerActivity
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -19,6 +20,17 @@ class ViewPagerScrollTest : BaseIntegrationTest() {
@get:Rule
var activityRule: ActivityTestRule<ViewPagerActivity> = ActivityTestRule(ViewPagerActivity::class.java)
@Before
override fun beforeTest() {
super.beforeTest()
// Wait for the initial page tab to be visible before starting the test
// This ensures the ViewPager and PagerTabStrip are fully laid out
val initialTabVisible = device.wait(Until.hasObject(By.text("Page 0")), TIMEOUT_LONG)
if (!initialTabVisible) {
throw AssertionError("ViewPager tabs not visible after ${TIMEOUT_LONG}ms - Page 0 not found")
}
}
@Test
@LargeTest
fun scrollViewPager() {
@@ -32,6 +44,19 @@ class ViewPagerScrollTest : BaseIntegrationTest() {
}
private fun clickTab(index: Int) {
device.findObject(UiSelector().text("Page $index")).click()
val text = "Page $index"
val found = device.wait(Until.hasObject(By.text(text)), TIMEOUT_UI_SEARCH_WAIT)
if (!found) {
throw AssertionError("Tab '$text' not found after ${TIMEOUT_UI_SEARCH_WAIT}ms")
}
val tab = device.findObject(By.text(text))
?: throw AssertionError("Tab '$text' was detected but findObject returned null")
tab.click()
// Small delay to allow ViewPager to settle after tab click
device.waitForIdle(500)
}
companion object {
private const val TIMEOUT_LONG = 15000L
}
}

View File

@@ -186,7 +186,7 @@ public class LocationIndicatorLayerTest extends BaseLayerTest {
// Set and Get
Double[] propertyValue = new Double[] {0.0, 0.0, 0.0};
layer.setProperties(location(propertyValue));
assertEquals(layer.getLocation().getValue(), propertyValue);
assertArrayEquals(layer.getLocation().getValue(), propertyValue);
}
@Test

View File

@@ -386,7 +386,7 @@ public class CircleLayerTest extends BaseLayerTest {
// Set and Get
Float[] propertyValue = new Float[] {0f, 0f};
layer.setProperties(circleTranslate(propertyValue));
assertEquals(layer.getCircleTranslate().getValue(), propertyValue);
assertArrayEquals(layer.getCircleTranslate().getValue(), propertyValue);
}
@Test

View File

@@ -0,0 +1,202 @@
// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`.
package org.maplibre.android.testapp.style;
import android.graphics.Color;
import androidx.test.annotation.UiThreadTest;
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
import org.maplibre.geojson.LineString;
import org.maplibre.geojson.MultiLineString;
import org.maplibre.geojson.MultiPoint;
import org.maplibre.geojson.MultiPolygon;
import org.maplibre.geojson.Point;
import org.maplibre.geojson.Polygon;
import org.maplibre.android.maps.BaseLayerTest;
import org.junit.Before;
import timber.log.Timber;
import org.maplibre.android.style.expressions.Expression;
import org.maplibre.android.style.layers.ColorReliefLayer;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.maplibre.android.style.expressions.Expression.*;
import static org.junit.Assert.*;
import static org.maplibre.android.style.layers.Property.*;
import static org.maplibre.android.style.layers.PropertyFactory.*;
import org.maplibre.android.style.layers.TransitionOptions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Basic smoke tests for ColorReliefLayer
*/
@RunWith(AndroidJUnit4ClassRunner.class)
public class ColorReliefLayerTest extends BaseLayerTest {
private ColorReliefLayer layer;
private final List<Point> pointsList = new ArrayList<Point>() {
{
add(Point.fromLngLat(55.30122473231012, 25.26476622289597));
add(Point.fromLngLat(55.29743486255916, 25.25827212207261));
add(Point.fromLngLat(55.28978863411328, 25.251356725509737));
add(Point.fromLngLat(55.300027931336984, 25.246425506635504));
add(Point.fromLngLat(55.307474692951274, 25.244200378933655));
add(Point.fromLngLat(55.31212891895635, 25.256408010450187));
add(Point.fromLngLat(55.30774064871093, 25.26266169122738));
add(Point.fromLngLat(55.301357710197806, 25.264946609615492));
add(Point.fromLngLat(55.30122473231012, 25.26476622289597));
}
};
@Before
@UiThreadTest
public void beforeTest(){
super.before();
layer = new ColorReliefLayer("my-layer", "composite");
layer.setSourceLayer("composite");
setupLayer(layer);
}
@Test
@UiThreadTest
public void testSourceId() {
Timber.i("SourceId");
assertNotNull(layer);
assertEquals(layer.getSourceId(), "composite");
}
@Test
@UiThreadTest
public void testSetVisibility() {
Timber.i("Visibility");
assertNotNull(layer);
// Get initial
assertEquals(layer.getVisibility().getValue(), VISIBLE);
// Set
layer.setProperties(visibility(NONE));
assertEquals(layer.getVisibility().getValue(), NONE);
}
@Test
@UiThreadTest
public void testSourceLayer() {
Timber.i("SourceLayer");
assertNotNull(layer);
// Get initial
assertEquals(layer.getSourceLayer(), "composite");
// Set
final String sourceLayer = "test";
layer.setSourceLayer(sourceLayer);
assertEquals(layer.getSourceLayer(), sourceLayer);
}
@Test
@UiThreadTest
public void testFilter() {
Timber.i("Filter");
assertNotNull(layer);
// Get initial
assertEquals(layer.getFilter(), null);
// Set
Expression filter = eq(get("undefined"), literal(1.0));
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// Set constant
filter = literal(true);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
}
@Test
@UiThreadTest
public void testFilterDistance() {
Timber.i("FilterDistance");
assertNotNull(layer);
// Get initial
assertEquals(layer.getFilter(), null);
// distance with Point
Expression filter = lt(distance(Point.fromLngLat(1.0, 1.0)), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with LineString
filter = lt(distance(LineString.fromLngLats(pointsList)), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with MultiPoint
filter = lt(distance(MultiPoint.fromLngLats(pointsList)), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with MultiPoint
filter = lt(distance(MultiLineString.fromLngLats(Collections.singletonList(pointsList))), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with Polygon
filter = lt(distance(Polygon.fromLngLats(Collections.singletonList(pointsList))), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with MultiPolygon
filter = lt(distance(MultiPolygon.fromLngLats(Collections
.singletonList(Collections.singletonList(pointsList)))), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
}
@Test
@UiThreadTest
public void testFilterWithin() {
Timber.i("FilterWithin");
assertNotNull(layer);
// Get initial
assertEquals(layer.getFilter(), null);
Expression filter = within(Polygon.fromLngLats(Collections.singletonList(pointsList)));
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
}
@Test
@UiThreadTest
public void testColorReliefOpacityTransition() {
Timber.i("color-relief-opacityTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setColorReliefOpacityTransition(options);
assertEquals(layer.getColorReliefOpacityTransition(), options);
}
@Test
@UiThreadTest
public void testColorReliefOpacityAsConstant() {
Timber.i("color-relief-opacity");
assertNotNull(layer);
assertNull(layer.getColorReliefOpacity().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(colorReliefOpacity(propertyValue));
assertEquals(layer.getColorReliefOpacity().getValue(), propertyValue);
}
}

View File

@@ -271,7 +271,7 @@ public class FillExtrusionLayerTest extends BaseLayerTest {
// Set and Get
Float[] propertyValue = new Float[] {0f, 0f};
layer.setProperties(fillExtrusionTranslate(propertyValue));
assertEquals(layer.getFillExtrusionTranslate().getValue(), propertyValue);
assertArrayEquals(layer.getFillExtrusionTranslate().getValue(), propertyValue);
}
@Test

View File

@@ -372,7 +372,7 @@ public class FillLayerTest extends BaseLayerTest {
// Set and Get
Float[] propertyValue = new Float[] {0f, 0f};
layer.setProperties(fillTranslate(propertyValue));
assertEquals(layer.getFillTranslate().getValue(), propertyValue);
assertArrayEquals(layer.getFillTranslate().getValue(), propertyValue);
}
@Test

View File

@@ -92,9 +92,22 @@ public class HillshadeLayerTest extends BaseLayerTest {
assertNull(layer.getHillshadeIlluminationDirection().getValue());
// Set and Get
Float propertyValue = 0.3f;
Float[] propertyValue = new Float[] {1.0f, 2.0f};
layer.setProperties(hillshadeIlluminationDirection(propertyValue));
assertEquals(layer.getHillshadeIlluminationDirection().getValue(), propertyValue);
assertArrayEquals(layer.getHillshadeIlluminationDirection().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testHillshadeIlluminationAltitudeAsConstant() {
Timber.i("hillshade-illumination-altitude");
assertNotNull(layer);
assertNull(layer.getHillshadeIlluminationAltitude().getValue());
// Set and Get
Float[] propertyValue = new Float[] {1.0f, 2.0f};
layer.setProperties(hillshadeIlluminationAltitude(propertyValue));
assertArrayEquals(layer.getHillshadeIlluminationAltitude().getValue(), propertyValue);
}
@Test
@@ -155,20 +168,9 @@ public class HillshadeLayerTest extends BaseLayerTest {
assertNull(layer.getHillshadeShadowColor().getValue());
// Set and Get
String propertyValue = "rgba(255,128,0,0.7)";
String[] propertyValue = new String[] {"rgba(255,0,0,1)", "rgba(0,0,255,1)"};
layer.setProperties(hillshadeShadowColor(propertyValue));
assertEquals(layer.getHillshadeShadowColor().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testHillshadeShadowColorAsIntConstant() {
Timber.i("hillshade-shadow-color");
assertNotNull(layer);
// Set and Get
layer.setProperties(hillshadeShadowColor(Color.argb(127, 255, 127, 0)));
assertEquals(layer.getHillshadeShadowColorAsInt(), Color.argb(127, 255, 127, 0));
assertArrayEquals(layer.getHillshadeShadowColor().getValue(), propertyValue);
}
@Test
@@ -191,20 +193,9 @@ public class HillshadeLayerTest extends BaseLayerTest {
assertNull(layer.getHillshadeHighlightColor().getValue());
// Set and Get
String propertyValue = "rgba(255,128,0,0.7)";
String[] propertyValue = new String[] {"rgba(255,0,0,1)", "rgba(0,0,255,1)"};
layer.setProperties(hillshadeHighlightColor(propertyValue));
assertEquals(layer.getHillshadeHighlightColor().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testHillshadeHighlightColorAsIntConstant() {
Timber.i("hillshade-highlight-color");
assertNotNull(layer);
// Set and Get
layer.setProperties(hillshadeHighlightColor(Color.argb(127, 255, 127, 0)));
assertEquals(layer.getHillshadeHighlightColorAsInt(), Color.argb(127, 255, 127, 0));
assertArrayEquals(layer.getHillshadeHighlightColor().getValue(), propertyValue);
}
@Test
@@ -242,4 +233,17 @@ public class HillshadeLayerTest extends BaseLayerTest {
layer.setProperties(hillshadeAccentColor(Color.argb(127, 255, 127, 0)));
assertEquals(layer.getHillshadeAccentColorAsInt(), Color.argb(127, 255, 127, 0));
}
@Test
@UiThreadTest
public void testHillshadeMethodAsConstant() {
Timber.i("hillshade-method");
assertNotNull(layer);
assertNull(layer.getHillshadeMethod().getValue());
// Set and Get
String propertyValue = HILLSHADE_METHOD_STANDARD;
layer.setProperties(hillshadeMethod(propertyValue));
assertEquals(layer.getHillshadeMethod().getValue(), propertyValue);
}
}

View File

@@ -375,7 +375,7 @@ public class LineLayerTest extends BaseLayerTest {
// Set and Get
Float[] propertyValue = new Float[] {0f, 0f};
layer.setProperties(lineTranslate(propertyValue));
assertEquals(layer.getLineTranslate().getValue(), propertyValue);
assertArrayEquals(layer.getLineTranslate().getValue(), propertyValue);
}
@Test
@@ -552,7 +552,7 @@ public class LineLayerTest extends BaseLayerTest {
// Set and Get
Float[] propertyValue = new Float[] {};
layer.setProperties(lineDasharray(propertyValue));
assertEquals(layer.getLineDasharray().getValue(), propertyValue);
assertArrayEquals(layer.getLineDasharray().getValue(), propertyValue);
}
@Test

View File

@@ -356,7 +356,7 @@ public class SymbolLayerTest extends BaseLayerTest {
// Set and Get
Float[] propertyValue = new Float[] {0f, 0f, 0f, 0f};
layer.setProperties(iconTextFitPadding(propertyValue));
assertEquals(layer.getIconTextFitPadding().getValue(), propertyValue);
assertArrayEquals(layer.getIconTextFitPadding().getValue(), propertyValue);
}
@Test
@@ -424,11 +424,11 @@ public class SymbolLayerTest extends BaseLayerTest {
// Set and Get
Float[] propertyValue = {2.0f, 2.0f, 2.0f, 2.0f};
layer.setProperties(iconPadding(propertyValue));
assertEquals(layer.getIconPadding().getValue(), propertyValue);
assertArrayEquals(layer.getIconPadding().getValue(), propertyValue);
// Single number value can be used too for backward compatibility
Float number = propertyValue[0] + 1.0f;
layer.setProperties(iconPadding(number));
assertEquals(layer.getIconPadding().getValue(), new Float[]{number, number, number, number});
assertArrayEquals(layer.getIconPadding().getValue(), new Float[]{number, number, number, number});
}
@Test
@@ -467,7 +467,7 @@ public class SymbolLayerTest extends BaseLayerTest {
// Set and Get
Float[] propertyValue = new Float[] {0f, 0f};
layer.setProperties(iconOffset(propertyValue));
assertEquals(layer.getIconOffset().getValue(), propertyValue);
assertArrayEquals(layer.getIconOffset().getValue(), propertyValue);
}
@Test
@@ -576,7 +576,7 @@ public class SymbolLayerTest extends BaseLayerTest {
// Set and Get
String[] propertyValue = new String[]{"Open Sans Regular", "Arial Unicode MS Regular"};
layer.setProperties(textFont(propertyValue));
assertEquals(layer.getTextFont().getValue(), propertyValue);
assertArrayEquals(layer.getTextFont().getValue(), propertyValue);
}
@Test
@@ -719,7 +719,7 @@ public class SymbolLayerTest extends BaseLayerTest {
// Set and Get
String[] propertyValue = new String[0];
layer.setProperties(textVariableAnchor(propertyValue));
assertEquals(layer.getTextVariableAnchor().getValue(), propertyValue);
assertArrayEquals(layer.getTextVariableAnchor().getValue(), propertyValue);
}
@Test
@@ -732,7 +732,7 @@ public class SymbolLayerTest extends BaseLayerTest {
// Set and Get
Object[] propertyValue = new Object[] {"top", new Float[]{1f, 2f}};
layer.setProperties(textVariableAnchorOffset(propertyValue));
assertEquals(layer.getTextVariableAnchorOffset().getValue(), propertyValue);
assertArrayEquals(layer.getTextVariableAnchorOffset().getValue(), propertyValue);
}
@Test
@@ -784,7 +784,7 @@ public class SymbolLayerTest extends BaseLayerTest {
// Set and Get
String[] propertyValue = new String[0];
layer.setProperties(textWritingMode(propertyValue));
assertEquals(layer.getTextWritingMode().getValue(), propertyValue);
assertArrayEquals(layer.getTextWritingMode().getValue(), propertyValue);
}
@Test
@@ -875,7 +875,7 @@ public class SymbolLayerTest extends BaseLayerTest {
// Set and Get
Float[] propertyValue = new Float[] {0f, 0f};
layer.setProperties(textOffset(propertyValue));
assertEquals(layer.getTextOffset().getValue(), propertyValue);
assertArrayEquals(layer.getTextOffset().getValue(), propertyValue);
}
@Test
@@ -1164,7 +1164,7 @@ public class SymbolLayerTest extends BaseLayerTest {
// Set and Get
Float[] propertyValue = new Float[] {0f, 0f};
layer.setProperties(iconTranslate(propertyValue));
assertEquals(layer.getIconTranslate().getValue(), propertyValue);
assertArrayEquals(layer.getIconTranslate().getValue(), propertyValue);
}
@Test
@@ -1414,7 +1414,7 @@ public class SymbolLayerTest extends BaseLayerTest {
// Set and Get
Float[] propertyValue = new Float[] {0f, 0f};
layer.setProperties(textTranslate(propertyValue));
assertEquals(layer.getTextTranslate().getValue(), propertyValue);
assertArrayEquals(layer.getTextTranslate().getValue(), propertyValue);
}
@Test

View File

@@ -231,12 +231,12 @@ public class <%- camelize(type) %>LayerTest extends BaseLayerTest {
assertEquals(layer.get<%- camelize(property.name) %>().getValue(), propertyValue);
<% } -%>
layer.setProperties(<%- camelizeWithLeadingLowercase(property.name) %>(propertyValue));
assertEquals(layer.get<%- camelize(property.name) %>().getValue(), propertyValue);
<%- propertyType(property).endsWith('[]') ? 'assertArrayEquals' : 'assertEquals' %>(layer.get<%- camelize(property.name) %>().getValue(), propertyValue);
<% if (property.type === 'padding') { -%>
// Single number value can be used too for backward compatibility
Float number = propertyValue[0] + 1.0f;
layer.setProperties(<%- camelizeWithLeadingLowercase(property.name) %>(number));
assertEquals(layer.get<%- camelize(property.name) %>().getValue(), new Float[]{number, number, number, number});
assertArrayEquals(layer.get<%- camelize(property.name) %>().getValue(), new Float[]{number, number, number, number});
<% } -%>
<% if (property.tokens) { -%>

View File

@@ -23,7 +23,11 @@ class ViewPagerActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
binding = ActivityViewpagerBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.viewPager.adapter = MapFragmentAdapter(this, supportFragmentManager)
val adapter = MapFragmentAdapter(this, supportFragmentManager)
binding.viewPager.adapter = adapter
// Ensure all pages are instantiated so their titles are available to UI tests immediately
// This keeps all 5 fragments in memory so PagerTabStrip shows all titles
binding.viewPager.offscreenPageLimit = adapter.count
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {