From 2391c4aee6c6c4035098c645eaa4c35a3fe8d089 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Mon, 2 Oct 2023 13:15:10 +0200 Subject: [PATCH 01/10] Implement new Circle class --- .../annotations/AnnotationContainer.kt | 27 +- .../android/annotations/CircleManager.kt | 439 +++-- .../android/annotations/KAnnotation.kt | 4 + .../maplibre/android/annotations/KCircle.kt | 122 ++ .../maplibre/android/annotations/KSymbol.kt | 1 - .../annotations/OnAnnotationClickListener.kt | 7 +- .../annotations/OnAnnotationDragListener.kt | 7 +- .../OnAnnotationLongClickListener.kt | 7 +- .../annotations/OnCircleClickListener.kt | 6 - .../annotations/OnCircleDragListener.kt | 6 - .../annotations/OnCircleLongClickListener.kt | 6 - .../android/annotations/SymbolManager.kt | 2 +- .../android/annotations/data/Defaults.kt | 11 + .../android/annotations/CircleManagerTest.kt | 1411 ++++++++--------- .../android/annotations/SymbolManagerTest.kt | 91 +- 15 files changed, 1011 insertions(+), 1136 deletions(-) create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/KCircle.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnCircleClickListener.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnCircleDragListener.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnCircleLongClickListener.kt diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/AnnotationContainer.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/AnnotationContainer.kt index dd88343a86e..7ac34906569 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/AnnotationContainer.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/AnnotationContainer.kt @@ -4,6 +4,7 @@ import androidx.annotation.UiThread import org.maplibre.android.maps.MapLibreMap import org.maplibre.android.maps.MapView import org.maplibre.android.maps.Style +import kotlin.reflect.KClass /** * Has logic for spawning annotation managers for its collection of annotations. @@ -46,12 +47,13 @@ class KAnnotationContainer( if (annotation is KSymbol) annotation.icon?.let { style?.addImage(it.image.toString(), it.image) } } - private fun addToManager(annotation: KAnnotation<*>) { - val manager = managers.getOrCreate(annotation.key()) - when (annotation) { - is KSymbol -> (manager as SymbolManager?)?.add(annotation) + private fun addToManager(annotation: KAnnotation<*>) = + managers.getOrCreate(annotation.key())?.let { manager -> + when (annotation) { + is KSymbol -> (manager as SymbolManager).add(annotation) + is KCircle -> (manager as CircleManager).add(annotation) + } } - } @UiThread fun remove(annotation: KAnnotation<*>) { @@ -60,7 +62,8 @@ class KAnnotationContainer( managers[annotation.key()]?.let { manager -> when (annotation) { - is KSymbol -> (manager as SymbolManager).add(annotation) + is KSymbol -> (manager as SymbolManager).delete(annotation) + is KCircle -> (manager as CircleManager).delete(annotation) } } @@ -82,13 +85,19 @@ class KAnnotationContainer( private fun groupAnnotations(): Map>> = annotationList.groupBy { it.key() } - private data class Key(private val type: Class>) // TODO will be expanded in the future + private data class Key(val type: KClass>) // TODO will be expanded in the future - private fun KAnnotation<*>.key() = Key(this.javaClass) + private fun KAnnotation<*>.key() = Key(this::class) private fun MutableMap>.getOrCreate(key: Key): AnnotationManager<*, *>? = get(key) ?: style?.let { - SymbolManager(mapView, mapLibreMap, it) + when (key.type) { + KSymbol::class -> SymbolManager(mapView, mapLibreMap, it) + KCircle::class -> CircleManager(mapView, mapLibreMap, it) + else -> throw IllegalArgumentException( + "Impossible key! This should never occur because KAnnotation is a sealed class." + ) + } }?.also { put(key, it) } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/CircleManager.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/CircleManager.kt index aafdef4e69c..50610ac532e 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/CircleManager.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/CircleManager.kt @@ -1,250 +1,189 @@ -//package org.maplibre.android.annotations -// -//import androidx.annotation.UiThread -//import com.mapbox.geojson.FeatureCollection -//import org.maplibre.android.maps.MapLibreMap -//import org.maplibre.android.maps.MapView -//import org.maplibre.android.maps.Style -//import org.maplibre.android.style.expressions.Expression -//import org.maplibre.android.style.layers.CircleLayer -//import org.maplibre.android.style.layers.PropertyFactory -//import org.maplibre.android.style.layers.PropertyValue -//import org.maplibre.android.style.sources.GeoJsonOptions -// -///** -// * The circle manager allows to add circles to a map. -// * -// * @param maplibreMap the map object to add circles to -// * @param style a valid a fully loaded style object -// * @param belowLayerId the id of the layer above the circle layer -// * @param aboveLayerId the id of the layer below the circle layer -// * @param geoJsonOptions options for the internal source -// */ -//class CircleManager @UiThread internal constructor( -// mapView: MapView, -// maplibreMap: MapLibreMap, -// style: Style, -// coreElementProvider: CoreElementProvider = CircleElementProvider(), -// belowLayerId: String? = null, -// aboveLayerId: String? = null, -// geoJsonOptions: GeoJsonOptions? = null, -// draggableAnnotationController: DraggableAnnotationController = DraggableAnnotationController.getInstance( -// mapView, -// maplibreMap -// ) -//) : AnnotationManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// draggableAnnotationController, -// belowLayerId, -// aboveLayerId, -// geoJsonOptions -//) { -// @JvmOverloads -// @UiThread -// constructor( -// mapView: MapView, -// maplibreMap: MapLibreMap, -// style: Style, -// belowLayerId: String? = null, -// aboveLayerId: String? = null, -// geoJsonOptions: GeoJsonOptions? = null -// ) : this( -// mapView = mapView, -// maplibreMap = maplibreMap, -// style = style, -// coreElementProvider = CircleElementProvider(), -// belowLayerId = belowLayerId, -// aboveLayerId = aboveLayerId, -// geoJsonOptions = geoJsonOptions -// ) -// -// override fun initializeDataDrivenPropertyMap() = listOf( -// CircleOptions.PROPERTY_CIRCLE_RADIUS, -// CircleOptions.PROPERTY_CIRCLE_COLOR, -// CircleOptions.PROPERTY_CIRCLE_BLUR, -// CircleOptions.PROPERTY_CIRCLE_OPACITY, -// CircleOptions.PROPERTY_CIRCLE_STROKE_WIDTH, -// CircleOptions.PROPERTY_CIRCLE_STROKE_COLOR, -// CircleOptions.PROPERTY_CIRCLE_STROKE_OPACITY -// ).associateWith { false }.let { dataDrivenPropertyUsageMap.putAll(it) } -// -// override fun generateDataDrivenPropertyExpression(property: String) { -// when (property) { -// CircleOptions.PROPERTY_CIRCLE_RADIUS -> layer.setProperties( -// PropertyFactory.circleRadius( -// Expression.get(CircleOptions.PROPERTY_CIRCLE_RADIUS) -// ) -// ) -// -// CircleOptions.PROPERTY_CIRCLE_COLOR -> layer.setProperties( -// PropertyFactory.circleColor( -// Expression.get(CircleOptions.PROPERTY_CIRCLE_COLOR) -// ) -// ) -// -// CircleOptions.PROPERTY_CIRCLE_BLUR -> layer.setProperties( -// PropertyFactory.circleBlur( -// Expression.get(CircleOptions.PROPERTY_CIRCLE_BLUR) -// ) -// ) -// -// CircleOptions.PROPERTY_CIRCLE_OPACITY -> layer.setProperties( -// PropertyFactory.circleOpacity( -// Expression.get(CircleOptions.PROPERTY_CIRCLE_OPACITY) -// ) -// ) -// -// CircleOptions.PROPERTY_CIRCLE_STROKE_WIDTH -> layer.setProperties( -// PropertyFactory.circleStrokeWidth( -// Expression.get(CircleOptions.PROPERTY_CIRCLE_STROKE_WIDTH) -// ) -// ) -// -// CircleOptions.PROPERTY_CIRCLE_STROKE_COLOR -> layer.setProperties( -// PropertyFactory.circleStrokeColor( -// Expression.get(CircleOptions.PROPERTY_CIRCLE_STROKE_COLOR) -// ) -// ) -// -// CircleOptions.PROPERTY_CIRCLE_STROKE_OPACITY -> layer.setProperties( -// PropertyFactory.circleStrokeOpacity( -// Expression.get(CircleOptions.PROPERTY_CIRCLE_STROKE_OPACITY) -// ) -// ) -// } -// } -// -// /** -// * Create a list of circles on the map. -// * -// * -// * Circles are going to be created only for features with a matching geometry. -// * -// * -// * All supported properties are:

-// * CircleOptions.PROPERTY_CIRCLE_RADIUS - Float

-// * CircleOptions.PROPERTY_CIRCLE_COLOR - String

-// * CircleOptions.PROPERTY_CIRCLE_BLUR - Float

-// * CircleOptions.PROPERTY_CIRCLE_OPACITY - Float

-// * CircleOptions.PROPERTY_CIRCLE_STROKE_WIDTH - Float

-// * CircleOptions.PROPERTY_CIRCLE_STROKE_COLOR - String

-// * CircleOptions.PROPERTY_CIRCLE_STROKE_OPACITY - Float

-// * Learn more about above properties in the [Style specification](https://maplibre.org/maplibre-style-spec/). -// * -// * -// * Out of spec properties:

-// * "is-draggable" - Boolean, true if the circle should be draggable, false otherwise -// * -// * @param json the GeoJSON defining the list of circles to build -// * @return the list of built circles -// */ -// @UiThread -// fun create(json: String): List = create(FeatureCollection.fromJson(json)) -// -// /** -// * Create a list of circles on the map. -// * -// * -// * Circles are going to be created only for features with a matching geometry. -// * -// * -// * All supported properties are:

-// * CircleOptions.PROPERTY_CIRCLE_RADIUS - Float

-// * CircleOptions.PROPERTY_CIRCLE_COLOR - String

-// * CircleOptions.PROPERTY_CIRCLE_BLUR - Float

-// * CircleOptions.PROPERTY_CIRCLE_OPACITY - Float

-// * CircleOptions.PROPERTY_CIRCLE_STROKE_WIDTH - Float

-// * CircleOptions.PROPERTY_CIRCLE_STROKE_COLOR - String

-// * CircleOptions.PROPERTY_CIRCLE_STROKE_OPACITY - Float

-// * Learn more about above properties in the [Style specification](https://maplibre.org/maplibre-style-spec/). -// * -// * -// * Out of spec properties:

-// * "is-draggable" - Boolean, true if the circle should be draggable, false otherwise -// * -// * @param featureCollection the featureCollection defining the list of circles to build -// * @return the list of built circles -// */ -// @UiThread -// fun create(featureCollection: FeatureCollection): List = -// featureCollection.features()?.mapNotNull { -// CircleOptions.fromFeature(it) -// }.let { create(it ?: emptyList()) } -// -// /** -// * Key of the id of the annotation -// */ -// override val annotationIdKey: String -// get() = Circle.ID_KEY -// -// // Property accessors -// /** -// * The geometry's offset. Values are [x, y] where negatives indicate left and up, respectively. -// */ -// var circleTranslate: Array? -// get() = layer.circleTranslate.value -// set(value) { -// val propertyValue: PropertyValue<*> = PropertyFactory.circleTranslate(value) -// constantPropertyUsageMap[PROPERTY_CIRCLE_TRANSLATE] = propertyValue -// layer.setProperties(propertyValue) -// } -// -// /** -// * Controls the frame of reference for [PropertyFactory.circleTranslate]. -// */ -// var circleTranslateAnchor: String -// get() = layer.circleTranslateAnchor.value -// set(value) { -// val propertyValue: PropertyValue<*> = PropertyFactory.circleTranslateAnchor(value) -// constantPropertyUsageMap[PROPERTY_CIRCLE_TRANSLATE_ANCHOR] = propertyValue -// layer.setProperties(propertyValue) -// } -// -// /** -// * Controls the scaling behavior of the circle when the map is pitched. -// */ -// var circlePitchScale: String? -// get() = layer.circlePitchScale.value -// set(value) { -// val propertyValue: PropertyValue<*> = PropertyFactory.circlePitchScale(value) -// constantPropertyUsageMap[PROPERTY_CIRCLE_PITCH_SCALE] = propertyValue -// layer.setProperties(propertyValue) -// } -// -// /** -// * Orientation of circle when map is pitched. -// */ -// var circlePitchAlignment: String -// get() = layer.circlePitchAlignment.value -// set(value) { -// val propertyValue: PropertyValue<*> = PropertyFactory.circlePitchAlignment(value) -// constantPropertyUsageMap[PROPERTY_CIRCLE_PITCH_ALIGNMENT] = propertyValue -// layer.setProperties(propertyValue) -// } -// -// /** -// * Set filter on the managed circles. -// */ -// override fun setFilter(expression: Expression) { -// layerFilter = expression -// layer.setFilter(expression) -// } -// -// val filter: Expression? -// /** -// * Get filter of the managed circles. -// */ -// get() { -// return layer.filter -// } -// -// companion object { -// private const val PROPERTY_CIRCLE_TRANSLATE: String = "circle-translate" -// private const val PROPERTY_CIRCLE_TRANSLATE_ANCHOR: String = "circle-translate-anchor" -// private const val PROPERTY_CIRCLE_PITCH_SCALE: String = "circle-pitch-scale" -// private const val PROPERTY_CIRCLE_PITCH_ALIGNMENT: String = "circle-pitch-alignment" -// } -//} +package org.maplibre.android.annotations + +import androidx.annotation.UiThread +import org.maplibre.android.maps.MapLibreMap +import org.maplibre.android.maps.MapView +import org.maplibre.android.maps.Style +import org.maplibre.android.style.expressions.Expression +import org.maplibre.android.style.layers.CircleLayer +import org.maplibre.android.style.layers.PropertyFactory +import org.maplibre.android.style.layers.PropertyValue +import org.maplibre.android.style.sources.GeoJsonOptions + +/** + * The circle manager allows to add circles to a map. + * + * @param maplibreMap the map object to add circles to + * @param style a valid a fully loaded style object + * @param belowLayerId the id of the layer above the circle layer + * @param aboveLayerId the id of the layer below the circle layer + * @param geoJsonOptions options for the internal source + */ +class CircleManager @UiThread internal constructor( + mapView: MapView, + maplibreMap: MapLibreMap, + style: Style, + coreElementProvider: CoreElementProvider = CircleElementProvider(), + belowLayerId: String? = null, + aboveLayerId: String? = null, + geoJsonOptions: GeoJsonOptions? = null, + draggableAnnotationController: DraggableAnnotationController = DraggableAnnotationController.getInstance( + mapView, + maplibreMap + ) +) : AnnotationManager( + mapView, + maplibreMap, + style, + coreElementProvider, + draggableAnnotationController, + belowLayerId, + aboveLayerId, + geoJsonOptions +) { + @JvmOverloads + @UiThread + constructor( + mapView: MapView, + maplibreMap: MapLibreMap, + style: Style, + belowLayerId: String? = null, + aboveLayerId: String? = null, + geoJsonOptions: GeoJsonOptions? = null + ) : this( + mapView = mapView, + maplibreMap = maplibreMap, + style = style, + coreElementProvider = CircleElementProvider(), + belowLayerId = belowLayerId, + aboveLayerId = aboveLayerId, + geoJsonOptions = geoJsonOptions + ) + + override fun generateDataDrivenPropertyExpression(property: String): PropertyValue = when (property) { + KCircle.PROPERTY_CIRCLE_RADIUS -> PropertyFactory.circleRadius( + Expression.get(KCircle.PROPERTY_CIRCLE_RADIUS) + ) + + KCircle.PROPERTY_CIRCLE_COLOR -> PropertyFactory.circleColor( + Expression.get(KCircle.PROPERTY_CIRCLE_COLOR) + ) + + KCircle.PROPERTY_CIRCLE_BLUR -> PropertyFactory.circleBlur( + Expression.get(KCircle.PROPERTY_CIRCLE_BLUR) + ) + + KCircle.PROPERTY_CIRCLE_OPACITY -> PropertyFactory.circleOpacity( + Expression.get(KCircle.PROPERTY_CIRCLE_OPACITY) + ) + + KCircle.PROPERTY_CIRCLE_STROKE_WIDTH -> PropertyFactory.circleStrokeWidth( + Expression.get(KCircle.PROPERTY_CIRCLE_STROKE_WIDTH) + + ) + + KCircle.PROPERTY_CIRCLE_STROKE_COLOR -> PropertyFactory.circleStrokeColor( + Expression.get(KCircle.PROPERTY_CIRCLE_STROKE_COLOR) + ) + + KCircle.PROPERTY_CIRCLE_STROKE_OPACITY -> PropertyFactory.circleStrokeOpacity( + Expression.get(KCircle.PROPERTY_CIRCLE_STROKE_OPACITY) + ) + + else -> throw IllegalArgumentException( + "$property is not a valid data-driven property for a circle." + ) + } + + override fun addDragListener(d: OnCircleDragListener) { + super.addDragListener(d) + } + + override fun removeDragListener(d: OnCircleDragListener) { + super.removeDragListener(d) + } + + override fun addClickListener(u: OnCircleClickListener) { + super.addClickListener(u) + } + + override fun removeClickListener(u: OnCircleClickListener) { + super.removeClickListener(u) + } + + override fun addLongClickListener(v: OnCircleLongClickListener) { + super.addLongClickListener(v) + } + + override fun removeLongClickListener(v: OnCircleLongClickListener) { + super.removeLongClickListener(v) + } + + // Property accessors + /** + * The geometry's offset. Values are [x, y] where negatives indicate left and up, respectively. + */ + var circleTranslate: Array? + get() = layer.circleTranslate.value + set(value) { + val propertyValue: PropertyValue<*> = PropertyFactory.circleTranslate(value) + constantPropertyUsageMap[PROPERTY_CIRCLE_TRANSLATE] = propertyValue + layer.setProperties(propertyValue) + } + + /** + * Controls the frame of reference for [PropertyFactory.circleTranslate]. + */ + var circleTranslateAnchor: String + get() = layer.circleTranslateAnchor.value + set(value) { + val propertyValue: PropertyValue<*> = PropertyFactory.circleTranslateAnchor(value) + constantPropertyUsageMap[PROPERTY_CIRCLE_TRANSLATE_ANCHOR] = propertyValue + layer.setProperties(propertyValue) + } + + /** + * Controls the scaling behavior of the circle when the map is pitched. + */ + var circlePitchScale: String? + get() = layer.circlePitchScale.value + set(value) { + val propertyValue: PropertyValue<*> = PropertyFactory.circlePitchScale(value) + constantPropertyUsageMap[PROPERTY_CIRCLE_PITCH_SCALE] = propertyValue + layer.setProperties(propertyValue) + } + + /** + * Orientation of circle when map is pitched. + */ + var circlePitchAlignment: String + get() = layer.circlePitchAlignment.value + set(value) { + val propertyValue: PropertyValue<*> = PropertyFactory.circlePitchAlignment(value) + constantPropertyUsageMap[PROPERTY_CIRCLE_PITCH_ALIGNMENT] = propertyValue + layer.setProperties(propertyValue) + } + + /** + * Set filter on the managed circles. + */ + override fun setFilter(expression: Expression) { + layerFilter = expression + layer.setFilter(expression) + } + + val filter: Expression? + /** + * Get filter of the managed circles. + */ + get() { + return layer.filter + } + + companion object { + private const val PROPERTY_CIRCLE_TRANSLATE: String = "circle-translate" + private const val PROPERTY_CIRCLE_TRANSLATE_ANCHOR: String = "circle-translate-anchor" + private const val PROPERTY_CIRCLE_PITCH_SCALE: String = "circle-pitch-scale" + private const val PROPERTY_CIRCLE_PITCH_ALIGNMENT: String = "circle-pitch-alignment" + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/KAnnotation.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/KAnnotation.kt index 4812d94e542..2ede3bb4b5f 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/KAnnotation.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/KAnnotation.kt @@ -99,4 +99,8 @@ sealed class KAnnotation( touchAreaShiftX: Float, touchAreaShiftY: Float ): Geometry? + + companion object { + internal const val PROPERTY_IS_DRAGGABLE = "is-draggable" + } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/KCircle.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/KCircle.kt new file mode 100644 index 00000000000..a5fbf2db7e6 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/KCircle.kt @@ -0,0 +1,122 @@ +package org.maplibre.android.annotations + +import android.graphics.PointF +import androidx.annotation.ColorInt +import com.mapbox.android.gestures.MoveDistancesObject +import com.mapbox.geojson.Geometry +import com.mapbox.geojson.Point +import org.maplibre.android.annotations.data.Defaults +import org.maplibre.android.constants.GeometryConstants +import org.maplibre.android.geometry.LatLng +import org.maplibre.android.maps.Projection + +class KCircle( + center: LatLng, + radius: Float = Defaults.CIRCLE_RADIUS, + @ColorInt color: Int = Defaults.CIRCLE_COLOR, + blur: Float? = Defaults.CIRCLE_BLUR, + opacity: Float = Defaults.CIRCLE_OPACITY, + stroke: Stroke? = Defaults.CIRCLE_STROKE, + // TODO: NDD translate, pitchScale, pitchAlignment +) : KAnnotation() { + var center: LatLng = center + set(value) { + field = value + updateThis() + } + var radius: Float = radius + set(value) { + field = value + updateThis() + } + @ColorInt var color: Int = color + set(value) { + field = value + updateThis() + } + var blur: Float? = blur + set(value) { + field = value + updateThis() + } + var opacity: Float = opacity + set(value) { + field = value + updateThis() + } + var stroke: Stroke? = stroke + set(value) { + field = value + updateThis() + } + + override var geometry: Point = Point.fromLngLat(center.longitude, center.latitude) + + override val dataDrivenProperties: List + get() = listOf( + PROPERTY_CIRCLE_SORT_KEY to zLayer default Defaults.Z_LAYER, + PROPERTY_IS_DRAGGABLE to draggable default Defaults.DRAGGABLE, + PROPERTY_CIRCLE_RADIUS to radius default Defaults.CIRCLE_RADIUS, + PROPERTY_CIRCLE_COLOR to color.asColorString() default Defaults.CIRCLE_COLOR.asColorString(), + PROPERTY_CIRCLE_BLUR to blur default Defaults.CIRCLE_BLUR, + PROPERTY_CIRCLE_OPACITY to opacity default Defaults.CIRCLE_OPACITY, + ) + (stroke?.flattenedValues ?: emptyList()) + + init { + if (blur != null && blur <= 0) { + throw IllegalArgumentException( + "A blur of $blur has been provided for a Halo object. This means that no blur is to be used. " + + "Please use `null` to indicate that `blur` is not used." + ) + } + } + + override fun getOffsetGeometry( + projection: Projection, + moveDistancesObject: MoveDistancesObject, + touchAreaShiftX: Float, + touchAreaShiftY: Float + ): Geometry? { + val pointF = PointF( + moveDistancesObject.currentX - touchAreaShiftX, + moveDistancesObject.currentY - touchAreaShiftY + ) + val latLng = projection.fromScreenLocation(pointF) + return if (latLng.latitude > GeometryConstants.MAX_MERCATOR_LATITUDE || latLng.latitude < GeometryConstants.MIN_MERCATOR_LATITUDE) { + null + } else { + Point.fromLngLat(latLng.longitude, latLng.latitude) + } + } + + data class Stroke( + val width: Float, + @ColorInt val color: Int = Defaults.CIRCLE_STROKE_COLOR, + val opacity: Float = Defaults.CIRCLE_STROKE_OPACITY + ) { + init { + if (opacity > 1f || opacity < 0f) { + throw IllegalArgumentException("Opacity must be between 0 and 1 (inclusive)") + } + } + + val flattenedValues: List + get() = listOf( + PROPERTY_CIRCLE_STROKE_WIDTH to width default null, + PROPERTY_CIRCLE_STROKE_COLOR to color.asColorString() + default Defaults.CIRCLE_STROKE_COLOR.asColorString(), + PROPERTY_CIRCLE_STROKE_OPACITY to opacity default Defaults.CIRCLE_STROKE_OPACITY + ) + } + + companion object { + internal const val PROPERTY_CIRCLE_SORT_KEY = "circle-sort-key" + internal const val PROPERTY_CIRCLE_RADIUS = "circle-radius" + internal const val PROPERTY_CIRCLE_COLOR = "circle-color" + internal const val PROPERTY_CIRCLE_BLUR = "circle-blur" + internal const val PROPERTY_CIRCLE_OPACITY = "circle-opacity" + internal const val PROPERTY_CIRCLE_STROKE_WIDTH = "circle-stroke-width" + internal const val PROPERTY_CIRCLE_STROKE_COLOR = "circle-stroke-color" + internal const val PROPERTY_CIRCLE_STROKE_OPACITY = "circle-stroke-opacity" + } +} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/KSymbol.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/KSymbol.kt index 27ba64b8be6..ab163fa0d9c 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/KSymbol.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/KSymbol.kt @@ -87,6 +87,5 @@ class KSymbol( internal const val PROPERTY_TEXT_HALO_COLOR = "text-halo-color" internal const val PROPERTY_TEXT_HALO_WIDTH = "text-halo-width" internal const val PROPERTY_TEXT_HALO_BLUR = "text-halo-blur" - internal const val PROPERTY_IS_DRAGGABLE = "is-draggable" } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationClickListener.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationClickListener.kt index cac2314d92b..400ca0f9af8 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationClickListener.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationClickListener.kt @@ -19,4 +19,9 @@ interface OnAnnotationClickListener> { /** * Interface definition for a callback to be invoked when a symbol has been clicked. */ -typealias OnSymbolClickListener = OnAnnotationClickListener \ No newline at end of file +typealias OnSymbolClickListener = OnAnnotationClickListener + +/** + * Interface definition for a callback to be invoked when a circle has been clicked. + */ +typealias OnCircleClickListener = OnAnnotationClickListener \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationDragListener.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationDragListener.kt index c99be80896e..5351042261e 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationDragListener.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationDragListener.kt @@ -31,4 +31,9 @@ interface OnAnnotationDragListener> { /** * Interface definition for a callback to be invoked when a symbol is dragged. */ -typealias OnSymbolDragListener = OnAnnotationDragListener \ No newline at end of file +typealias OnSymbolDragListener = OnAnnotationDragListener + +/** + * Interface definition for a callback to be invoked when a circle is dragged. + */ +typealias OnCircleDragListener = OnAnnotationDragListener \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationLongClickListener.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationLongClickListener.kt index eb11d24f0a4..5357027a2f1 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationLongClickListener.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationLongClickListener.kt @@ -19,4 +19,9 @@ interface OnAnnotationLongClickListener> { /** * Interface definition for a callback to be invoked when a symbol has been long clicked. */ -typealias OnSymbolLongClickListener = OnAnnotationLongClickListener \ No newline at end of file +typealias OnSymbolLongClickListener = OnAnnotationLongClickListener + +/** + * Interface definition for a callback to be invoked when a circle has been long clicked. + */ +typealias OnCircleLongClickListener = OnAnnotationLongClickListener \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnCircleClickListener.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnCircleClickListener.kt deleted file mode 100644 index 10a4336d90a..00000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnCircleClickListener.kt +++ /dev/null @@ -1,6 +0,0 @@ -package org.maplibre.android.annotations - -/** - * Interface definition for a callback to be invoked when a circle has been clicked. - */ -//interface OnCircleClickListener : OnAnnotationClickListener diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnCircleDragListener.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnCircleDragListener.kt deleted file mode 100644 index c7caf43a4ac..00000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnCircleDragListener.kt +++ /dev/null @@ -1,6 +0,0 @@ -package org.maplibre.android.annotations - -/** - * Interface definition for a callback to be invoked when a circle is dragged. - */ -//interface OnCircleDragListener : OnAnnotationDragListener diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnCircleLongClickListener.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnCircleLongClickListener.kt deleted file mode 100644 index e4dd5cbb9c4..00000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnCircleLongClickListener.kt +++ /dev/null @@ -1,6 +0,0 @@ -package org.maplibre.android.annotations - -/** - * Interface definition for a callback to be invoked when a circle has been long clicked. - */ -//interface OnCircleLongClickListener : OnAnnotationLongClickListener diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/SymbolManager.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/SymbolManager.kt index 42c5cd347ec..ca84394ce5b 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/SymbolManager.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/SymbolManager.kt @@ -238,7 +238,7 @@ class SymbolManager @UiThread internal constructor( super.removeLongClickListener(v) } -// Property accessors + // Property accessors /** * Label placement relative to its geometry. */ diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/data/Defaults.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/data/Defaults.kt index c46d5c642af..9d6fab9a00a 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/data/Defaults.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/data/Defaults.kt @@ -4,6 +4,7 @@ import android.graphics.Color import android.graphics.PointF import androidx.annotation.ColorInt import com.google.gson.JsonElement +import org.maplibre.android.annotations.KCircle class Defaults { companion object { @@ -46,5 +47,15 @@ class Defaults { @ColorInt val TEXT_COLOR: Int = Color.BLACK val TEXT_HALO: Halo? = null + + val CIRCLE_RADIUS: Float = 5f + @ColorInt + val CIRCLE_COLOR: Int = Color.BLACK + val CIRCLE_BLUR: Float? = null + val CIRCLE_OPACITY: Float = 1f + val CIRCLE_STROKE: KCircle.Stroke? = null + + val CIRCLE_STROKE_COLOR: Int = Color.BLACK + val CIRCLE_STROKE_OPACITY: Float = 1f } } diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/CircleManagerTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/CircleManagerTest.kt index 538d3e3e8a7..43354207f32 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/CircleManagerTest.kt +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/CircleManagerTest.kt @@ -1,764 +1,647 @@ -//package org.maplibre.android.annotations -// -//import com.google.gson.JsonPrimitive -//import com.mapbox.geojson.Feature -//import com.mapbox.geojson.FeatureCollection -//import com.mapbox.geojson.Geometry -//import com.mapbox.geojson.Point -//import junit.framework.Assert -//import org.junit.Before -//import org.junit.Test -//import org.maplibre.android.geometry.LatLng -//import org.maplibre.android.maps.MapLibreMap -//import org.maplibre.android.maps.MapView -//import org.maplibre.android.maps.MapView.OnDidFinishLoadingStyleListener -//import org.maplibre.android.maps.Style -//import org.maplibre.android.maps.Style.OnStyleLoaded -//import org.maplibre.android.style.expressions.Expression -//import org.maplibre.android.style.layers.CircleLayer -//import org.maplibre.android.style.layers.PropertyFactory -//import org.maplibre.android.style.sources.GeoJsonOptions -//import org.maplibre.android.style.sources.GeoJsonSource -//import org.mockito.ArgumentCaptor -//import org.mockito.ArgumentMatchers -//import org.mockito.Mockito -// -//class CircleManagerTest { -// private val draggableAnnotationController = Mockito.mock( -// DraggableAnnotationController::class.java -// ) -// private val mapView = Mockito.mock(MapView::class.java) -// private val maplibreMap = Mockito.mock(MapLibreMap::class.java) -// private val style = Mockito.mock(Style::class.java) -// private val geoJsonSource = Mockito.mock(GeoJsonSource::class.java) -// private val optionedGeoJsonSource = Mockito.mock(GeoJsonSource::class.java) -// private val layerId = "annotation_layer" -// private val circleLayer = Mockito.mock(CircleLayer::class.java) -// private lateinit var circleManager: CircleManager -// private val coreElementProvider: CoreElementProvider = Mockito.mock( -// CircleElementProvider::class.java -// ) -// private val geoJsonOptions = Mockito.mock(GeoJsonOptions::class.java) -// -// @Before -// fun beforeTest() { -// Mockito.`when`(circleLayer.id).thenReturn(layerId) -// Mockito.`when`(coreElementProvider.layer).thenReturn(circleLayer) -// Mockito.`when`(coreElementProvider.getSource(null)).thenReturn(geoJsonSource) -// Mockito.`when`(coreElementProvider.getSource(geoJsonOptions)) -// .thenReturn(optionedGeoJsonSource) -// Mockito.`when`(style.isFullyLoaded).thenReturn(true) -// } -// -// @Test -// fun testInitialization() { -// circleManager = CircleManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Mockito.verify(style).addSource(geoJsonSource) -// Mockito.verify(style).addLayer(circleLayer) -// Assert.assertTrue(circleManager.dataDrivenPropertyUsageMap.isNotEmpty()) -// for (value in circleManager.dataDrivenPropertyUsageMap.values) { -// Assert.assertFalse(value) -// } -// Mockito.verify(circleLayer) -// .setProperties(*circleManager.constantPropertyUsageMap.values.toTypedArray()) -// Mockito.verify(circleLayer, Mockito.times(0)).setFilter( -// ArgumentMatchers.any( -// Expression::class.java -// ) -// ) -// } -// -// @Test -// fun testInitializationOnStyleReload() { -// circleManager = CircleManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Mockito.verify(style).addSource(geoJsonSource) -// Mockito.verify(style).addLayer(circleLayer) -// Assert.assertTrue(circleManager.dataDrivenPropertyUsageMap.isNotEmpty()) -// for (value in circleManager.dataDrivenPropertyUsageMap.values) { -// Assert.assertFalse(value) -// } -// Mockito.verify(circleLayer) -// .setProperties(*circleManager.constantPropertyUsageMap.values.toTypedArray()) -// val filter = Expression.literal(false) -// circleManager.setFilter(filter) -// val loadingArgumentCaptor = ArgumentCaptor.forClass( -// OnDidFinishLoadingStyleListener::class.java -// ) -// Mockito.verify(mapView).addOnDidFinishLoadingStyleListener(loadingArgumentCaptor.capture()) -// loadingArgumentCaptor.value.onDidFinishLoadingStyle() -// val styleLoadedArgumentCaptor = ArgumentCaptor.forClass( -// OnStyleLoaded::class.java -// ) -// Mockito.verify(maplibreMap).getStyle(styleLoadedArgumentCaptor.capture()) -// val newStyle = Mockito.mock( -// Style::class.java -// ) -// Mockito.`when`(newStyle.isFullyLoaded).thenReturn(true) -// val newSource = Mockito.mock(GeoJsonSource::class.java) -// Mockito.`when`(coreElementProvider.getSource(null)).thenReturn(newSource) -// val newLayer = Mockito.mock(CircleLayer::class.java) -// Mockito.`when`(coreElementProvider.layer).thenReturn(newLayer) -// styleLoadedArgumentCaptor.value.onStyleLoaded(newStyle) -// Mockito.verify(newStyle).addSource(newSource) -// Mockito.verify(newStyle).addLayer(newLayer) -// Assert.assertTrue(circleManager.dataDrivenPropertyUsageMap.isNotEmpty()) -// for (value in circleManager.dataDrivenPropertyUsageMap.values) { -// Assert.assertFalse(value) -// } -// Mockito.verify(newLayer) -// .setProperties(*circleManager.constantPropertyUsageMap.values.toTypedArray()) -// Mockito.verify(circleLayer).setFilter(filter) -// } -// -// @Test -// fun testLayerBelowInitialization() { -// circleManager = CircleManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// "test_layer", -// null, -// null, -// draggableAnnotationController -// ) -// Mockito.verify(style).addSource(geoJsonSource) -// Mockito.verify(style).addLayerBelow(circleLayer, "test_layer") -// Assert.assertTrue(circleManager.dataDrivenPropertyUsageMap.isNotEmpty()) -// for (value in circleManager.dataDrivenPropertyUsageMap.values) { -// Assert.assertFalse(value) -// } -// Mockito.verify(circleLayer) -// .setProperties(*circleManager.constantPropertyUsageMap.values.toTypedArray()) -// } -// -// @Test -// fun testGeoJsonOptionsInitialization() { -// circleManager = CircleManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// geoJsonOptions, -// draggableAnnotationController -// ) -// Mockito.verify(style).addSource(optionedGeoJsonSource) -// Mockito.verify(style).addLayer(circleLayer) -// Assert.assertTrue(circleManager.dataDrivenPropertyUsageMap.isNotEmpty()) -// for (value in circleManager.dataDrivenPropertyUsageMap.values) { -// Assert.assertFalse(value) -// } -// Mockito.verify(circleLayer) -// .setProperties(*circleManager.constantPropertyUsageMap.values.toTypedArray()) -// Mockito.verify(circleLayer, Mockito.times(0)).setFilter( -// ArgumentMatchers.any( -// Expression::class.java -// ) -// ) -// } -// -// @Test -// fun testLayerId() { -// circleManager = CircleManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Assert.assertEquals(layerId, circleManager.layerId) -// } -// -// @Test -// fun testAddCircle() { -// circleManager = CircleManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val circle = circleManager.create(CircleOptions().withLatLng(LatLng())) -// Assert.assertEquals(circleManager.annotations[0], circle) -// } -// -// @Test -// fun addCircleFromFeatureCollection() { -// circleManager = CircleManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val geometry: Geometry = Point.fromLngLat(10.0, 10.0) -// val feature = Feature.fromGeometry(geometry) -// feature.addNumberProperty("circle-radius", 2.0f) -// feature.addStringProperty("circle-color", "rgba(0, 0, 0, 1)") -// feature.addNumberProperty("circle-blur", 2.0f) -// feature.addNumberProperty("circle-opacity", 2.0f) -// feature.addNumberProperty("circle-stroke-width", 2.0f) -// feature.addStringProperty("circle-stroke-color", "rgba(0, 0, 0, 1)") -// feature.addNumberProperty("circle-stroke-opacity", 2.0f) -// feature.addBooleanProperty("is-draggable", true) -// val circles = circleManager.create(FeatureCollection.fromFeature(feature)) -// val circle = circles[0] -// Assert.assertEquals(circle.geometry, geometry) -// Assert.assertEquals(circle.circleRadius, 2.0f) -// Assert.assertEquals(circle.circleColor, "rgba(0, 0, 0, 1)") -// Assert.assertEquals(circle.circleBlur, 2.0f) -// Assert.assertEquals(circle.circleOpacity, 2.0f) -// Assert.assertEquals(circle.circleStrokeWidth, 2.0f) -// Assert.assertEquals(circle.circleStrokeColor, "rgba(0, 0, 0, 1)") -// Assert.assertEquals(circle.circleStrokeOpacity, 2.0f) -// Assert.assertTrue(circle.isDraggable) -// } -// -// @Test -// fun addCircles() { -// circleManager = CircleManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val latLngList: MutableList = ArrayList() -// latLngList.add(LatLng()) -// latLngList.add(LatLng(1.0, 1.0)) -// val options: MutableList = ArrayList() -// for (latLng in latLngList) { -// options.add(CircleOptions().withLatLng(latLng)) -// } -// val circles = circleManager.create(options) -// Assert.assertTrue("Returned value size should match", circles.size == 2) -// Assert.assertTrue("Annotations size should match", circleManager.annotations.size() == 2) -// } -// -// @Test -// fun testDeleteCircle() { -// circleManager = CircleManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val circle = circleManager.create(CircleOptions().withLatLng(LatLng())) -// circleManager.delete(circle) -// Assert.assertTrue(circleManager.annotations.size() == 0) -// } -// -// @Test -// fun testGeometryCircle() { -// circleManager = CircleManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val latLng = LatLng(12.0, 34.0) -// val options = CircleOptions().withLatLng(latLng) -// val circle = circleManager.create(options) -// Assert.assertEquals(options.latLng, latLng) -// Assert.assertEquals(circle.latLng, latLng) -// Assert.assertEquals(options.geometry, Point.fromLngLat(34.0, 12.0)) -// Assert.assertEquals(circle.getGeometry(), Point.fromLngLat(34.0, 12.0)) -// } -// -// @Test -// fun testFeatureIdCircle() { -// circleManager = CircleManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val circleZero = circleManager.create(CircleOptions().withLatLng(LatLng())) -// val circleOne = circleManager.create(CircleOptions().withLatLng(LatLng())) -// Assert.assertEquals(circleZero.feature[Circle.ID_KEY].asLong, 0) -// Assert.assertEquals(circleOne.feature[Circle.ID_KEY].asLong, 1) -// } -// -// @Test -// fun testCircleDraggableFlag() { -// circleManager = CircleManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val circleZero = circleManager.create(CircleOptions().withLatLng(LatLng())) -// Assert.assertFalse(circleZero.isDraggable) -// circleZero.isDraggable = true -// Assert.assertTrue(circleZero.isDraggable) -// circleZero.isDraggable = false -// Assert.assertFalse(circleZero.isDraggable) -// } -// -// @Test -// fun testCircleRadiusLayerProperty() { -// circleManager = CircleManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Mockito.verify(circleLayer, Mockito.times(0)).setProperties( -// ArgumentMatchers.argThat( -// PropertyValueMatcher( -// PropertyFactory.circleRadius(Expression.get("circle-radius")) -// ) -// ) -// ) -// val options = CircleOptions().withLatLng(LatLng()).withCircleRadius(2.0f) -// circleManager.create(options) -// circleManager.updateSourceNow() -// Mockito.verify(circleLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat( -// PropertyValueMatcher( -// PropertyFactory.circleRadius(Expression.get("circle-radius")) -// ) -// ) -// ) -// circleManager.create(options) -// Mockito.verify(circleLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat( -// PropertyValueMatcher( -// PropertyFactory.circleRadius(Expression.get("circle-radius")) -// ) -// ) -// ) -// } -// -// @Test -// fun testCircleColorLayerProperty() { -// circleManager = CircleManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Mockito.verify(circleLayer, Mockito.times(0)).setProperties( -// ArgumentMatchers.argThat( -// PropertyValueMatcher( -// PropertyFactory.circleColor(Expression.get("circle-color")) -// ) -// ) -// ) -// val options = CircleOptions().withLatLng(LatLng()).withCircleColor("rgba(0, 0, 0, 1)") -// circleManager.create(options) -// circleManager.updateSourceNow() -// Mockito.verify(circleLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat( -// PropertyValueMatcher( -// PropertyFactory.circleColor(Expression.get("circle-color")) -// ) -// ) -// ) -// circleManager.create(options) -// Mockito.verify(circleLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat( -// PropertyValueMatcher( -// PropertyFactory.circleColor(Expression.get("circle-color")) -// ) -// ) -// ) -// } -// -// @Test -// fun testCircleBlurLayerProperty() { -// circleManager = CircleManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Mockito.verify(circleLayer, Mockito.times(0)).setProperties( -// ArgumentMatchers.argThat( -// PropertyValueMatcher( -// PropertyFactory.circleBlur(Expression.get("circle-blur")) -// ) -// ) -// ) -// val options = CircleOptions().withLatLng(LatLng()).withCircleBlur(2.0f) -// circleManager.create(options) -// circleManager.updateSourceNow() -// Mockito.verify(circleLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat( -// PropertyValueMatcher( -// PropertyFactory.circleBlur(Expression.get("circle-blur")) -// ) -// ) -// ) -// circleManager.create(options) -// Mockito.verify(circleLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat( -// PropertyValueMatcher( -// PropertyFactory.circleBlur(Expression.get("circle-blur")) -// ) -// ) -// ) -// } -// -// @Test -// fun testCircleOpacityLayerProperty() { -// circleManager = CircleManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Mockito.verify(circleLayer, Mockito.times(0)).setProperties( -// ArgumentMatchers.argThat( -// PropertyValueMatcher( -// PropertyFactory.circleOpacity(Expression.get("circle-opacity")) -// ) -// ) -// ) -// val options = CircleOptions().withLatLng(LatLng()).withCircleOpacity(2.0f) -// circleManager.create(options) -// circleManager.updateSourceNow() -// Mockito.verify(circleLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat( -// PropertyValueMatcher( -// PropertyFactory.circleOpacity(Expression.get("circle-opacity")) -// ) -// ) -// ) -// circleManager.create(options) -// Mockito.verify(circleLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat( -// PropertyValueMatcher( -// PropertyFactory.circleOpacity(Expression.get("circle-opacity")) -// ) -// ) -// ) -// } -// -// @Test -// fun testCircleStrokeWidthLayerProperty() { -// circleManager = CircleManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Mockito.verify(circleLayer, Mockito.times(0)).setProperties( -// ArgumentMatchers.argThat( -// PropertyValueMatcher( -// PropertyFactory.circleStrokeWidth(Expression.get("circle-stroke-width")) -// ) -// ) -// ) -// val options = CircleOptions().withLatLng(LatLng()).withCircleStrokeWidth(2.0f) -// circleManager.create(options) -// circleManager.updateSourceNow() -// Mockito.verify(circleLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat( -// PropertyValueMatcher( -// PropertyFactory.circleStrokeWidth(Expression.get("circle-stroke-width")) -// ) -// ) -// ) -// circleManager.create(options) -// Mockito.verify(circleLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat( -// PropertyValueMatcher( -// PropertyFactory.circleStrokeWidth(Expression.get("circle-stroke-width")) -// ) -// ) -// ) -// } -// -// @Test -// fun testCircleStrokeColorLayerProperty() { -// circleManager = CircleManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Mockito.verify(circleLayer, Mockito.times(0)).setProperties( -// ArgumentMatchers.argThat( -// PropertyValueMatcher( -// PropertyFactory.circleStrokeColor(Expression.get("circle-stroke-color")) -// ) -// ) -// ) -// val options = CircleOptions().withLatLng(LatLng()).withCircleStrokeColor("rgba(0, 0, 0, 1)") -// circleManager.create(options) -// circleManager.updateSourceNow() -// Mockito.verify(circleLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat( -// PropertyValueMatcher( -// PropertyFactory.circleStrokeColor(Expression.get("circle-stroke-color")) -// ) -// ) -// ) -// circleManager.create(options) -// Mockito.verify(circleLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat( -// PropertyValueMatcher( -// PropertyFactory.circleStrokeColor(Expression.get("circle-stroke-color")) -// ) -// ) -// ) -// } -// -// @Test -// fun testCircleStrokeOpacityLayerProperty() { -// circleManager = CircleManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Mockito.verify(circleLayer, Mockito.times(0)).setProperties( -// ArgumentMatchers.argThat( -// PropertyValueMatcher( -// PropertyFactory.circleStrokeOpacity(Expression.get("circle-stroke-opacity")) -// ) -// ) -// ) -// val options = CircleOptions().withLatLng(LatLng()).withCircleStrokeOpacity(2.0f) -// circleManager.create(options) -// circleManager.updateSourceNow() -// Mockito.verify(circleLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat( -// PropertyValueMatcher( -// PropertyFactory.circleStrokeOpacity(Expression.get("circle-stroke-opacity")) -// ) -// ) -// ) -// circleManager.create(options) -// Mockito.verify(circleLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat( -// PropertyValueMatcher( -// PropertyFactory.circleStrokeOpacity(Expression.get("circle-stroke-opacity")) -// ) -// ) -// ) -// } -// -// @Test -// fun testCircleLayerFilter() { -// circleManager = CircleManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val expression = Expression.eq(Expression.get("test"), "selected") -// Mockito.verify(circleLayer, Mockito.times(0)).setFilter(expression) -// circleManager.setFilter(expression) -// Mockito.verify(circleLayer, Mockito.times(1)).setFilter(expression) -// Mockito.`when`(circleLayer.filter).thenReturn(expression) -// Assert.assertEquals(expression, circleManager.filter) -// Assert.assertEquals(expression, circleManager.layerFilter) -// } -// -// @Test -// fun testClickListener() { -// val listener = Mockito.mock( -// OnCircleClickListener::class.java -// ) -// circleManager = CircleManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Assert.assertTrue(circleManager.getClickListeners().isEmpty()) -// circleManager.addClickListener(listener) -// Assert.assertTrue(circleManager.getClickListeners().contains(listener)) -// circleManager.removeClickListener(listener) -// Assert.assertTrue(circleManager.getClickListeners().isEmpty()) -// } -// -// @Test -// fun testLongClickListener() { -// val listener = Mockito.mock( -// OnCircleLongClickListener::class.java -// ) -// circleManager = CircleManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Assert.assertTrue(circleManager.getLongClickListeners().isEmpty()) -// circleManager.addLongClickListener(listener) -// Assert.assertTrue(circleManager.getLongClickListeners().contains(listener)) -// circleManager.removeLongClickListener(listener) -// Assert.assertTrue(circleManager.getLongClickListeners().isEmpty()) -// } -// -// @Test -// fun testDragListener() { -// val listener = Mockito.mock( -// OnCircleDragListener::class.java -// ) -// circleManager = CircleManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Assert.assertTrue(circleManager.getDragListeners().isEmpty()) -// circleManager.addDragListener(listener) -// Assert.assertTrue(circleManager.getDragListeners().contains(listener)) -// circleManager.removeDragListener(listener) -// Assert.assertTrue(circleManager.getDragListeners().isEmpty()) -// } -// -// @Test -// fun testCustomData() { -// circleManager = CircleManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val options = CircleOptions().withLatLng(LatLng()) -// options.withData(JsonPrimitive("hello")) -// val circle = circleManager.create(options) -// Assert.assertEquals(JsonPrimitive("hello"), circle.data) -// } -// -// @Test -// fun testClearAll() { -// circleManager = CircleManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val options = CircleOptions().withLatLng(LatLng()) -// circleManager.create(options) -// Assert.assertEquals(1, circleManager.annotations.size()) -// circleManager.deleteAll() -// Assert.assertEquals(0, circleManager.annotations.size()) -// } -// -// @Test -// fun testIgnoreClearedAnnotations() { -// circleManager = CircleManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val options = CircleOptions().withLatLng(LatLng()) -// val circle = circleManager.create(options) -// Assert.assertEquals(1, circleManager.annotations.size()) -// circleManager.annotations.clear() -// circleManager.updateSource() -// Assert.assertTrue(circleManager.annotations.isEmpty) -// circleManager.update(circle) -// Assert.assertTrue(circleManager.annotations.isEmpty) -// } -//} +package org.maplibre.android.annotations + +import android.graphics.Color +import com.google.gson.JsonPrimitive +import com.mapbox.geojson.Point +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.maplibre.android.geometry.LatLng +import org.maplibre.android.maps.MapLibreMap +import org.maplibre.android.maps.MapView +import org.maplibre.android.maps.MapView.OnDidFinishLoadingStyleListener +import org.maplibre.android.maps.Style +import org.maplibre.android.maps.Style.OnStyleLoaded +import org.maplibre.android.style.expressions.Expression +import org.maplibre.android.style.layers.CircleLayer +import org.maplibre.android.style.layers.PropertyFactory +import org.maplibre.android.style.sources.GeoJsonOptions +import org.maplibre.android.style.sources.GeoJsonSource +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers +import org.mockito.Mockito + +class CircleManagerTest { + private val draggableAnnotationController = Mockito.mock( + DraggableAnnotationController::class.java + ) + private val mapView = Mockito.mock(MapView::class.java) + private val maplibreMap = Mockito.mock(MapLibreMap::class.java) + private val style = Mockito.mock(Style::class.java) + private val geoJsonSource = Mockito.mock(GeoJsonSource::class.java) + private val optionedGeoJsonSource = Mockito.mock(GeoJsonSource::class.java) + private val layerId = "annotation_layer" + private val circleLayer = Mockito.mock(CircleLayer::class.java) + private lateinit var circleManager: CircleManager + private val coreElementProvider: CoreElementProvider = Mockito.mock( + CircleElementProvider::class.java + ) + private val geoJsonOptions = Mockito.mock(GeoJsonOptions::class.java) + + @Before + fun beforeTest() { + Mockito.`when`(circleLayer.id).thenReturn(layerId) + Mockito.`when`(coreElementProvider.layer).thenReturn(circleLayer) + Mockito.`when`(coreElementProvider.getSource(null)).thenReturn(geoJsonSource) + Mockito.`when`(coreElementProvider.getSource(geoJsonOptions)) + .thenReturn(optionedGeoJsonSource) + Mockito.`when`(style.isFullyLoaded).thenReturn(true) + } + + @Test + fun testInitialization() { + circleManager = CircleManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Mockito.verify(style).addSource(geoJsonSource) + Mockito.verify(style).addLayer(circleLayer) + Assert.assertTrue(circleManager.usedDataDrivenProperties.isEmpty()) + Mockito.verify(circleLayer) + .setProperties(*circleManager.constantPropertyUsageMap.values.toTypedArray()) + Mockito.verify(circleLayer, Mockito.times(0)).setFilter( + ArgumentMatchers.any( + Expression::class.java + ) + ) + } + + @Test + fun testInitializationOnStyleReload() { + circleManager = CircleManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Mockito.verify(style).addSource(geoJsonSource) + Mockito.verify(style).addLayer(circleLayer) + Assert.assertTrue(circleManager.usedDataDrivenProperties.isEmpty()) + Mockito.verify(circleLayer) + .setProperties(*circleManager.constantPropertyUsageMap.values.toTypedArray()) + val filter = Expression.literal(false) + circleManager.setFilter(filter) + val loadingArgumentCaptor = ArgumentCaptor.forClass( + OnDidFinishLoadingStyleListener::class.java + ) + Mockito.verify(mapView).addOnDidFinishLoadingStyleListener(loadingArgumentCaptor.capture()) + loadingArgumentCaptor.value.onDidFinishLoadingStyle() + val styleLoadedArgumentCaptor = ArgumentCaptor.forClass( + OnStyleLoaded::class.java + ) + Mockito.verify(maplibreMap).getStyle(styleLoadedArgumentCaptor.capture()) + val newStyle = Mockito.mock( + Style::class.java + ) + Mockito.`when`(newStyle.isFullyLoaded).thenReturn(true) + val newSource = Mockito.mock(GeoJsonSource::class.java) + Mockito.`when`(coreElementProvider.getSource(null)).thenReturn(newSource) + val newLayer = Mockito.mock(CircleLayer::class.java) + Mockito.`when`(coreElementProvider.layer).thenReturn(newLayer) + styleLoadedArgumentCaptor.value.onStyleLoaded(newStyle) + Mockito.verify(newStyle).addSource(newSource) + Mockito.verify(newStyle).addLayer(newLayer) + Assert.assertTrue(circleManager.usedDataDrivenProperties.isEmpty()) + Mockito.verify(newLayer) + .setProperties(*circleManager.constantPropertyUsageMap.values.toTypedArray()) + Mockito.verify(circleLayer).setFilter(filter) + } + + @Test + fun testLayerBelowInitialization() { + circleManager = CircleManager( + mapView, + maplibreMap, + style, + coreElementProvider, + "test_layer", + null, + null, + draggableAnnotationController + ) + Mockito.verify(style).addSource(geoJsonSource) + Mockito.verify(style).addLayerBelow(circleLayer, "test_layer") + Assert.assertTrue(circleManager.usedDataDrivenProperties.isEmpty()) + Mockito.verify(circleLayer) + .setProperties(*circleManager.constantPropertyUsageMap.values.toTypedArray()) + } + + @Test + fun testGeoJsonOptionsInitialization() { + circleManager = CircleManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + geoJsonOptions, + draggableAnnotationController + ) + Mockito.verify(style).addSource(optionedGeoJsonSource) + Mockito.verify(style).addLayer(circleLayer) + Assert.assertTrue(circleManager.usedDataDrivenProperties.isEmpty()) + Mockito.verify(circleLayer) + .setProperties(*circleManager.constantPropertyUsageMap.values.toTypedArray()) + Mockito.verify(circleLayer, Mockito.times(0)).setFilter( + ArgumentMatchers.any( + Expression::class.java + ) + ) + } + + @Test + fun testLayerId() { + circleManager = CircleManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Assert.assertEquals(layerId, circleManager.layerId) + } + + @Test + fun testAddCircle() { + circleManager = CircleManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + val circle = KCircle(LatLng()) + circleManager.add(circle) + Assert.assertSame(circle, circleManager.annotations.values.first()) + } + + @Test + fun addCircles() { + circleManager = CircleManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + listOf( + LatLng(), + LatLng(1.0, 1.0) + ).map { KCircle(it) }.forEach { circleManager.add(it) } + Assert.assertTrue("Amount of annotations should match", circleManager.annotations.size == 2) + } + + @Test + fun testDeleteCircle() { + circleManager = CircleManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + val circle = KCircle(LatLng()) + circleManager.add(circle) + Assert.assertEquals( + "After adding a circle, one circle should be present", + 1, + circleManager.annotations.size + ) + circleManager.delete(circle) + Assert.assertEquals( + "After removing the only circle, no circles should be present anymore", + 0, + circleManager.annotations.size + ) + } + + @Test + fun testGeometryCircle() { + circleManager = CircleManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + val latLng = LatLng(12.0, 34.0) + val circle = KCircle(latLng) + circleManager.add(circle) + Assert.assertEquals(circle.center, latLng) + Assert.assertEquals(circle.geometry, Point.fromLngLat(34.0, 12.0)) + } + + @Test + fun testCircleDraggableFlag() { + circleManager = CircleManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + val circle = KCircle(LatLng()) + circleManager.add(circle) + Assert.assertFalse(circle.draggable) + circle.draggable = true + Assert.assertTrue(circle.draggable) + circle.draggable = false + Assert.assertFalse(circle.draggable) + } + + @Test + fun testCircleRadiusLayerProperty() { + circleManager = CircleManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Mockito.verify(circleLayer, Mockito.times(0)).setProperties( + ArgumentMatchers.argThat( + PropertyValueMatcher( + PropertyFactory.circleRadius(Expression.get("circle-radius")) + ) + ) + ) + for (i in 0 until 2) { + val circle = KCircle(LatLng()).apply { + radius = 2f + } + circleManager.add(circle) + circleManager.updateSourceNow() + Mockito.verify(circleLayer, Mockito.times(1)).setProperties( + ArgumentMatchers.argThat( + PropertyValueMatcher( + PropertyFactory.circleRadius(Expression.get("circle-radius")) + ) + ) + ) + } + } + + @Test + fun testCircleColorLayerProperty() { + circleManager = CircleManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Mockito.verify(circleLayer, Mockito.times(0)).setProperties( + ArgumentMatchers.argThat( + PropertyValueMatcher( + PropertyFactory.circleColor(Expression.get("circle-color")) + ) + ) + ) + for (i in 0 until 2) { + val circle = KCircle(LatLng()).apply { + color = Color.RED + } + circleManager.add(circle) + circleManager.updateSourceNow() + Mockito.verify(circleLayer, Mockito.times(1)).setProperties( + ArgumentMatchers.argThat( + PropertyValueMatcher( + PropertyFactory.circleColor(Expression.get("circle-color")) + ) + ) + ) + } + } + + @Test + fun testCircleBlurLayerProperty() { + circleManager = CircleManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Mockito.verify(circleLayer, Mockito.times(0)).setProperties( + ArgumentMatchers.argThat( + PropertyValueMatcher( + PropertyFactory.circleBlur(Expression.get("circle-blur")) + ) + ) + ) + for (i in 0 until 2) { + val circle = KCircle(LatLng()).apply { + blur = 2f + } + circleManager.add(circle) + circleManager.updateSourceNow() + Mockito.verify(circleLayer, Mockito.times(1)).setProperties( + ArgumentMatchers.argThat( + PropertyValueMatcher( + PropertyFactory.circleBlur(Expression.get("circle-blur")) + ) + ) + ) + } + } + + @Test + fun testCircleOpacityLayerProperty() { + circleManager = CircleManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Mockito.verify(circleLayer, Mockito.times(0)).setProperties( + ArgumentMatchers.argThat( + PropertyValueMatcher( + PropertyFactory.circleOpacity(Expression.get("circle-opacity")) + ) + ) + ) + for (i in 0 until 2) { + val circle = KCircle(LatLng()).apply { + opacity = 0.5f + } + circleManager.add(circle) + circleManager.updateSourceNow() + Mockito.verify(circleLayer, Mockito.times(1)).setProperties( + ArgumentMatchers.argThat( + PropertyValueMatcher( + PropertyFactory.circleOpacity(Expression.get("circle-opacity")) + ) + ) + ) + } + } + + @Test + fun testCircleStrokeWidthLayerProperty() { + circleManager = CircleManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Mockito.verify(circleLayer, Mockito.times(0)).setProperties( + ArgumentMatchers.argThat( + PropertyValueMatcher( + PropertyFactory.circleStrokeWidth(Expression.get("circle-stroke-width")) + ) + ) + ) + for (i in 0 until 2) { + val circle = KCircle(LatLng(), stroke = KCircle.Stroke(width = 2f)) + circleManager.add(circle) + circleManager.updateSourceNow() + Mockito.verify(circleLayer, Mockito.times(1)).setProperties( + ArgumentMatchers.argThat( + PropertyValueMatcher( + PropertyFactory.circleStrokeWidth(Expression.get("circle-stroke-width")) + ) + ) + ) + } + } + + @Test + fun testCircleStrokeColorLayerProperty() { + circleManager = CircleManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Mockito.verify(circleLayer, Mockito.times(0)).setProperties( + ArgumentMatchers.argThat( + PropertyValueMatcher( + PropertyFactory.circleStrokeColor(Expression.get("circle-stroke-color")) + ) + ) + ) + for (i in 0 until 2) { + val circle = KCircle(LatLng(), stroke = KCircle.Stroke(width = 2f, color = Color.CYAN)) + circleManager.add(circle) + circleManager.updateSourceNow() + Mockito.verify(circleLayer, Mockito.times(1)).setProperties( + ArgumentMatchers.argThat( + PropertyValueMatcher( + PropertyFactory.circleStrokeColor(Expression.get("circle-stroke-color")) + ) + ) + ) + } + } + + @Test + fun testCircleStrokeOpacityLayerProperty() { + circleManager = CircleManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Mockito.verify(circleLayer, Mockito.times(0)).setProperties( + ArgumentMatchers.argThat( + PropertyValueMatcher( + PropertyFactory.circleStrokeOpacity(Expression.get("circle-stroke-opacity")) + ) + ) + ) + for (i in 0 until 2) { + val circle = KCircle(LatLng(), stroke = KCircle.Stroke(width = 2f, opacity = 0.5f)) + circleManager.add(circle) + circleManager.updateSourceNow() + Mockito.verify(circleLayer, Mockito.times(1)).setProperties( + ArgumentMatchers.argThat( + PropertyValueMatcher( + PropertyFactory.circleStrokeOpacity(Expression.get("circle-stroke-opacity")) + ) + ) + ) + } + } + + @Test + fun testCircleLayerFilter() { + circleManager = CircleManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + val expression = Expression.eq(Expression.get("test"), "selected") + Mockito.verify(circleLayer, Mockito.times(0)).setFilter(expression) + circleManager.setFilter(expression) + Mockito.verify(circleLayer, Mockito.times(1)).setFilter(expression) + Mockito.`when`(circleLayer.filter).thenReturn(expression) + Assert.assertEquals(expression, circleManager.filter) + Assert.assertEquals(expression, circleManager.layerFilter) + } + + @Test + fun testClickListener() { + val listener = object : OnCircleClickListener { + override fun onAnnotationClick(t: KCircle): Boolean = false + + } + circleManager = CircleManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Assert.assertTrue(circleManager.getClickListeners().isEmpty()) + circleManager.addClickListener(listener) + Assert.assertTrue(circleManager.getClickListeners().contains(listener)) + circleManager.removeClickListener(listener) + Assert.assertTrue(circleManager.getClickListeners().isEmpty()) + } + + @Test + fun testLongClickListener() { + val listener = object : OnCircleLongClickListener { + override fun onAnnotationLongClick(t: KCircle): Boolean = false + } + circleManager = CircleManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Assert.assertTrue(circleManager.getLongClickListeners().isEmpty()) + circleManager.addLongClickListener(listener) + Assert.assertTrue(circleManager.getLongClickListeners().contains(listener)) + circleManager.removeLongClickListener(listener) + Assert.assertTrue(circleManager.getLongClickListeners().isEmpty()) + } + + @Test + fun testDragListener() { + val listener = object : OnCircleDragListener { + override fun onAnnotationDragStarted(annotation: KCircle) = Unit + override fun onAnnotationDrag(annotation: KCircle) = Unit + override fun onAnnotationDragFinished(annotation: KCircle) = Unit + } + circleManager = CircleManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Assert.assertTrue(circleManager.getDragListeners().isEmpty()) + circleManager.addDragListener(listener) + Assert.assertTrue(circleManager.getDragListeners().contains(listener)) + circleManager.removeDragListener(listener) + Assert.assertTrue(circleManager.getDragListeners().isEmpty()) + } + + @Test + fun testCustomData() { + circleManager = CircleManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + val circle = KCircle(LatLng()).apply { + data = JsonPrimitive("hello") + } + circleManager.add(circle) + Assert.assertEquals(JsonPrimitive("hello"), circle.data) + } + + @Test + fun testClearAll() { + circleManager = CircleManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + val circle = KCircle(LatLng()) + circleManager.add(circle) + Assert.assertEquals(1, circleManager.annotations.size) + circleManager.deleteAll() + Assert.assertEquals(0, circleManager.annotations.size) + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/SymbolManagerTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/SymbolManagerTest.kt index ed293c42b76..14bbbbe9833 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/SymbolManagerTest.kt +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/SymbolManagerTest.kt @@ -226,98 +226,9 @@ class SymbolManagerTest { ) val symbol = KSymbol(LatLng()) symbolManager.add(symbol) - Assert.assertEquals(symbolManager.annotations.values.first(), symbol) + Assert.assertSame(symbol, symbolManager.annotations.values.first()) } - /*@Test - fun addSymbolFromFeatureCollection() { - symbolManager = SymbolManager( - mapView, - maplibreMap, - style, - coreElementProvider, - null, - null, - null, - draggableAnnotationController - ) - val geometry: Geometry = Point.fromLngLat(10.0, 10.0) - val feature = Feature.fromGeometry(geometry).apply { - addNumberProperty("symbol-sort-key", 2.0f) - addNumberProperty("icon-size", 2.0f) - addStringProperty("icon-image", "undefined") - addNumberProperty("icon-rotate", 2.0f) - addProperty("icon-offset", convertArray(arrayOf(0f, 0f))) - addStringProperty("icon-anchor", ICON_ANCHOR_CENTER) - addStringProperty("text-field", "") - addProperty( - "text-font", - convertArray(arrayOf("Open Sans Regular", "Arial Unicode MS Regular")) - ) - addNumberProperty("text-size", 2.0f) - addNumberProperty("text-max-width", 2.0f) - addNumberProperty("text-letter-spacing", 2.0f) - addStringProperty("text-justify", TEXT_JUSTIFY_AUTO) - addNumberProperty("text-radial-offset", 2.0f) - addStringProperty("text-anchor", TEXT_ANCHOR_CENTER) - addNumberProperty("text-rotate", 2.0f) - addStringProperty("text-transform", TEXT_TRANSFORM_NONE) - addProperty("text-offset", convertArray(arrayOf(0f, 0f))) - addNumberProperty("icon-opacity", 2.0f) - addStringProperty("icon-color", "rgba(0, 0, 0, 1)") - addStringProperty("icon-halo-color", "rgba(0, 0, 0, 1)") - addNumberProperty("icon-halo-width", 2.0f) - addNumberProperty("icon-halo-blur", 2.0f) - addNumberProperty("text-opacity", 2.0f) - addStringProperty("text-color", "rgba(0, 0, 0, 1)") - addStringProperty("text-halo-color", "rgba(0, 0, 0, 1)") - addNumberProperty("text-halo-width", 2.0f) - addNumberProperty("text-halo-blur", 2.0f) - addBooleanProperty("is-draggable", true) - } - - val symbols: List = symbolManager.create(FeatureCollection.fromFeature(feature)) - val symbol = symbols[0] - Assert.assertEquals(symbol.geometry, geometry) - Assert.assertEquals(symbol.symbolSortKey, 2.0f) - Assert.assertEquals(symbol.iconSize, 2.0f) - Assert.assertEquals(symbol.iconImage, "undefined") - Assert.assertEquals(symbol.iconRotate, 2.0f) - val iconOffsetExpected = PointF(0f, 0f) - Assert.assertEquals(iconOffsetExpected.x, symbol.iconOffset.x) - Assert.assertEquals(iconOffsetExpected.y, symbol.iconOffset.y) - Assert.assertEquals(symbol.iconAnchor, ICON_ANCHOR_CENTER) - Assert.assertEquals(symbol.textField, "") - Assert.assertTrue( - Arrays.equals( - symbol.textFont, - arrayOf("Open Sans Regular", "Arial Unicode MS Regular") - ) - ) - Assert.assertEquals(symbol.textSize, 2.0f) - Assert.assertEquals(symbol.textMaxWidth, 2.0f) - Assert.assertEquals(symbol.textLetterSpacing, 2.0f) - Assert.assertEquals(symbol.textJustify, TEXT_JUSTIFY_AUTO) - Assert.assertEquals(symbol.textRadialOffset, 2.0f) - Assert.assertEquals(symbol.textAnchor, TEXT_ANCHOR_CENTER) - Assert.assertEquals(symbol.textRotate, 2.0f) - Assert.assertEquals(symbol.textTransform, TEXT_TRANSFORM_NONE) - val textOffsetExpected = PointF(0f, 0f) - Assert.assertEquals(textOffsetExpected.x, symbol.textOffset.x) - Assert.assertEquals(textOffsetExpected.y, symbol.textOffset.y) - Assert.assertEquals(symbol.iconOpacity, 2.0f) - Assert.assertEquals(symbol.iconColor, "rgba(0, 0, 0, 1)") - Assert.assertEquals(symbol.iconHaloColor, "rgba(0, 0, 0, 1)") - Assert.assertEquals(symbol.iconHaloWidth, 2.0f) - Assert.assertEquals(symbol.iconHaloBlur, 2.0f) - Assert.assertEquals(symbol.textOpacity, 2.0f) - Assert.assertEquals(symbol.textColor, "rgba(0, 0, 0, 1)") - Assert.assertEquals(symbol.textHaloColor, "rgba(0, 0, 0, 1)") - Assert.assertEquals(symbol.textHaloWidth, 2.0f) - Assert.assertEquals(symbol.textHaloBlur, 2.0f) - Assert.assertTrue(symbol.isDraggable) - }*/ - @Test fun addSymbols() { symbolManager = SymbolManager( From 04686d68e76ef8fd69f25710870bb7d937130bb7 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Mon, 2 Oct 2023 13:19:12 +0200 Subject: [PATCH 02/10] Rename KCircle to Circle and KSymbol to Symbol --- .../annotations/AnnotationContainer.kt | 16 +- .../maplibre/android/annotations/Circle.java | 356 ----------------- .../annotations/{KCircle.kt => Circle.kt} | 4 +- .../android/annotations/CircleManager.kt | 30 +- .../android/annotations/CircleOptions.java | 364 ------------------ .../annotations/OnAnnotationClickListener.kt | 4 +- .../annotations/OnAnnotationDragListener.kt | 4 +- .../OnAnnotationLongClickListener.kt | 4 +- .../annotations/{KSymbol.kt => Symbol.kt} | 2 +- .../android/annotations/SymbolManager.kt | 110 +++--- .../android/annotations/data/Defaults.kt | 4 +- .../maplibre/android/annotations/data/Icon.kt | 22 +- .../maplibre/android/annotations/data/Text.kt | 34 +- .../maplibre/android/maps/MapLibreMap.java | 4 +- .../android/annotations/CircleManagerTest.kt | 38 +- .../android/annotations/SymbolManagerTest.kt | 80 ++-- .../activity/annotation/BulkMarkerActivity.kt | 7 +- 17 files changed, 180 insertions(+), 903 deletions(-) delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Circle.java rename platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/{KCircle.kt => Circle.kt} (98%) delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/CircleOptions.java rename platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/{KSymbol.kt => Symbol.kt} (99%) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/AnnotationContainer.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/AnnotationContainer.kt index 7ac34906569..5dae5108677 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/AnnotationContainer.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/AnnotationContainer.kt @@ -28,7 +28,7 @@ class KAnnotationContainer( fun add(annotation: KAnnotation<*>) { annotationList.add(annotation) addToManager(annotation) - if (annotation is KSymbol) annotation.icon?.let { style?.addImage(it.image.toString(), it.image) } + if (annotation is Symbol) annotation.icon?.let { style?.addImage(it.image.toString(), it.image) } } @UiThread @@ -44,14 +44,14 @@ class KAnnotationContainer( fun update(annotation: KAnnotation<*>) { managers[annotation.key()]?.updateSource() - if (annotation is KSymbol) annotation.icon?.let { style?.addImage(it.image.toString(), it.image) } + if (annotation is Symbol) annotation.icon?.let { style?.addImage(it.image.toString(), it.image) } } private fun addToManager(annotation: KAnnotation<*>) = managers.getOrCreate(annotation.key())?.let { manager -> when (annotation) { - is KSymbol -> (manager as SymbolManager).add(annotation) - is KCircle -> (manager as CircleManager).add(annotation) + is Symbol -> (manager as SymbolManager).add(annotation) + is Circle -> (manager as CircleManager).add(annotation) } } @@ -62,8 +62,8 @@ class KAnnotationContainer( managers[annotation.key()]?.let { manager -> when (annotation) { - is KSymbol -> (manager as SymbolManager).delete(annotation) - is KCircle -> (manager as CircleManager).delete(annotation) + is Symbol -> (manager as SymbolManager).delete(annotation) + is Circle -> (manager as CircleManager).delete(annotation) } } @@ -92,8 +92,8 @@ class KAnnotationContainer( private fun MutableMap>.getOrCreate(key: Key): AnnotationManager<*, *>? = get(key) ?: style?.let { when (key.type) { - KSymbol::class -> SymbolManager(mapView, mapLibreMap, it) - KCircle::class -> CircleManager(mapView, mapLibreMap, it) + Symbol::class -> SymbolManager(mapView, mapLibreMap, it) + Circle::class -> CircleManager(mapView, mapLibreMap, it) else -> throw IllegalArgumentException( "Impossible key! This should never occur because KAnnotation is a sealed class." ) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Circle.java b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Circle.java deleted file mode 100644 index 68bc8525a50..00000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Circle.java +++ /dev/null @@ -1,356 +0,0 @@ -//package org.maplibre.android.annotations; -// -//import static org.maplibre.android.constants.GeometryConstants.MAX_MERCATOR_LATITUDE; -//import static org.maplibre.android.constants.GeometryConstants.MIN_MERCATOR_LATITUDE; -// -//import android.graphics.PointF; -// -//import androidx.annotation.ColorInt; -//import androidx.annotation.NonNull; -//import androidx.annotation.Nullable; -//import androidx.annotation.UiThread; -// -//import com.google.gson.JsonNull; -//import com.google.gson.JsonObject; -//import com.mapbox.android.gestures.MoveDistancesObject; -//import com.mapbox.geojson.Geometry; -//import com.mapbox.geojson.Point; -// -//import org.maplibre.android.geometry.LatLng; -//import org.maplibre.android.maps.Projection; -//import org.maplibre.android.style.layers.PropertyFactory; -//import org.maplibre.android.utils.ColorUtils; -// -//@UiThread -//public class Circle extends AbstractAnnotation { -// -// private final AnnotationManager annotationManager; -// -// /** -// * Create a circle. -// * -// * @param id the id of the circle -// * @param jsonObject the features of the annotation -// * @param geometry the geometry of the annotation -// */ -// Circle(long id, AnnotationManager annotationManager, JsonObject jsonObject, Point geometry) { -// super(id, jsonObject, geometry); -// this.annotationManager = annotationManager; -// } -// -// @Override -// void setUsedDataDrivenProperties() { -// if (!(jsonObject.get(CircleOptions.PROPERTY_CIRCLE_RADIUS) instanceof JsonNull)) { -// annotationManager.enableDataDrivenProperty(CircleOptions.PROPERTY_CIRCLE_RADIUS); -// } -// if (!(jsonObject.get(CircleOptions.PROPERTY_CIRCLE_COLOR) instanceof JsonNull)) { -// annotationManager.enableDataDrivenProperty(CircleOptions.PROPERTY_CIRCLE_COLOR); -// } -// if (!(jsonObject.get(CircleOptions.PROPERTY_CIRCLE_BLUR) instanceof JsonNull)) { -// annotationManager.enableDataDrivenProperty(CircleOptions.PROPERTY_CIRCLE_BLUR); -// } -// if (!(jsonObject.get(CircleOptions.PROPERTY_CIRCLE_OPACITY) instanceof JsonNull)) { -// annotationManager.enableDataDrivenProperty(CircleOptions.PROPERTY_CIRCLE_OPACITY); -// } -// if (!(jsonObject.get(CircleOptions.PROPERTY_CIRCLE_STROKE_WIDTH) instanceof JsonNull)) { -// annotationManager.enableDataDrivenProperty(CircleOptions.PROPERTY_CIRCLE_STROKE_WIDTH); -// } -// if (!(jsonObject.get(CircleOptions.PROPERTY_CIRCLE_STROKE_COLOR) instanceof JsonNull)) { -// annotationManager.enableDataDrivenProperty(CircleOptions.PROPERTY_CIRCLE_STROKE_COLOR); -// } -// if (!(jsonObject.get(CircleOptions.PROPERTY_CIRCLE_STROKE_OPACITY) instanceof JsonNull)) { -// annotationManager.enableDataDrivenProperty(CircleOptions.PROPERTY_CIRCLE_STROKE_OPACITY); -// } -// } -// -// /** -// * Set the LatLng of the circle, which represents the location of the circle on the map -// *

-// * To update the circle on the map use {@link CircleManager#update(AbstractAnnotation)}. -// *

-// * -// * @param latLng the location of the circle in a latitude and longitude pair -// */ -// public void setLatLng(LatLng latLng) { -// geometry = Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude()); -// } -// -// /** -// * Get the LatLng of the circle, which represents the location of the circle on the map -// * -// * @return the location of the circle -// */ -// @NonNull -// public LatLng getLatLng() { -// return new LatLng(geometry.latitude(), geometry.longitude()); -// } -// -// // Property accessors -// -// /** -// * Get the CircleRadius property -// *

-// * Circle radius. -// *

-// * -// * @return property wrapper value around Float -// */ -// public Float getCircleRadius() { -// return jsonObject.get(CircleOptions.PROPERTY_CIRCLE_RADIUS).getAsFloat(); -// } -// -// /** -// * Set the CircleRadius property -// *

-// * Circle radius. -// *

-// *

-// * To update the circle on the map use {@link CircleManager#update(AbstractAnnotation)}. -// *

-// * -// * @param value constant property value for Float -// */ -// public void setCircleRadius(Float value) { -// jsonObject.addProperty(CircleOptions.PROPERTY_CIRCLE_RADIUS, value); -// } -// -// /** -// * Get the CircleColor property -// *

-// * The fill color of the circle. -// *

-// * -// * @return color value for String -// */ -// @ColorInt -// public int getCircleColorAsInt() { -// return ColorUtils.rgbaToColor(jsonObject.get(CircleOptions.PROPERTY_CIRCLE_COLOR).getAsString()); -// } -// -// /** -// * Get the CircleColor property -// *

-// * The fill color of the circle. -// *

-// * -// * @return color value for String -// */ -// public String getCircleColor() { -// return jsonObject.get(CircleOptions.PROPERTY_CIRCLE_COLOR).getAsString(); -// } -// -// /** -// * Set the CircleColor property -// *

-// * The fill color of the circle. -// *

-// *

-// * To update the circle on the map use {@link CircleManager#update(AbstractAnnotation)}. -// *

-// * -// * @param color value for String -// */ -// public void setCircleColor(@ColorInt int color) { -// jsonObject.addProperty(CircleOptions.PROPERTY_CIRCLE_COLOR, ColorUtils.colorToRgbaString(color)); -// } -// -// /** -// * Set the CircleColor property -// *

-// * The fill color of the circle. -// *

-// *

-// * To update the circle on the map use {@link CircleManager#update(AbstractAnnotation)}. -// *

-// * -// * @param color value for String -// */ -// public void setCircleColor(@NonNull String color) { -// jsonObject.addProperty("circle-color", color); -// } -// -// /** -// * Get the CircleBlur property -// *

-// * Amount to blur the circle. 1 blurs the circle such that only the centerpoint is full opacity. -// *

-// * -// * @return property wrapper value around Float -// */ -// public Float getCircleBlur() { -// return jsonObject.get(CircleOptions.PROPERTY_CIRCLE_BLUR).getAsFloat(); -// } -// -// /** -// * Set the CircleBlur property -// *

-// * Amount to blur the circle. 1 blurs the circle such that only the centerpoint is full opacity. -// *

-// *

-// * To update the circle on the map use {@link CircleManager#update(AbstractAnnotation)}. -// *

-// * -// * @param value constant property value for Float -// */ -// public void setCircleBlur(Float value) { -// jsonObject.addProperty(CircleOptions.PROPERTY_CIRCLE_BLUR, value); -// } -// -// /** -// * Get the CircleOpacity property -// *

-// * The opacity at which the circle will be drawn. -// *

-// * -// * @return property wrapper value around Float -// */ -// public Float getCircleOpacity() { -// return jsonObject.get(CircleOptions.PROPERTY_CIRCLE_OPACITY).getAsFloat(); -// } -// -// /** -// * Set the CircleOpacity property -// *

-// * The opacity at which the circle will be drawn. -// *

-// *

-// * To update the circle on the map use {@link CircleManager#update(AbstractAnnotation)}. -// *

-// * -// * @param value constant property value for Float -// */ -// public void setCircleOpacity(Float value) { -// jsonObject.addProperty(CircleOptions.PROPERTY_CIRCLE_OPACITY, value); -// } -// -// /** -// * Get the CircleStrokeWidth property -// *

-// * The width of the circle's stroke. Strokes are placed outside of the {@link PropertyFactory#circleRadius}. -// *

-// * -// * @return property wrapper value around Float -// */ -// public Float getCircleStrokeWidth() { -// return jsonObject.get(CircleOptions.PROPERTY_CIRCLE_STROKE_WIDTH).getAsFloat(); -// } -// -// /** -// * Set the CircleStrokeWidth property -// *

-// * The width of the circle's stroke. Strokes are placed outside of the {@link PropertyFactory#circleRadius}. -// *

-// *

-// * To update the circle on the map use {@link CircleManager#update(AbstractAnnotation)}. -// *

-// * -// * @param value constant property value for Float -// */ -// public void setCircleStrokeWidth(Float value) { -// jsonObject.addProperty(CircleOptions.PROPERTY_CIRCLE_STROKE_WIDTH, value); -// } -// -// /** -// * Get the CircleStrokeColor property -// *

-// * The stroke color of the circle. -// *

-// * -// * @return color value for String -// */ -// @ColorInt -// public int getCircleStrokeColorAsInt() { -// return ColorUtils.rgbaToColor(jsonObject.get(CircleOptions.PROPERTY_CIRCLE_STROKE_COLOR).getAsString()); -// } -// -// /** -// * Get the CircleStrokeColor property -// *

-// * The stroke color of the circle. -// *

-// * -// * @return color value for String -// */ -// public String getCircleStrokeColor() { -// return jsonObject.get(CircleOptions.PROPERTY_CIRCLE_STROKE_COLOR).getAsString(); -// } -// -// /** -// * Set the CircleStrokeColor property -// *

-// * The stroke color of the circle. -// *

-// *

-// * To update the circle on the map use {@link CircleManager#update(AbstractAnnotation)}. -// *

-// * -// * @param color value for String -// */ -// public void setCircleStrokeColor(@ColorInt int color) { -// jsonObject.addProperty(CircleOptions.PROPERTY_CIRCLE_STROKE_COLOR, ColorUtils.colorToRgbaString(color)); -// } -// -// /** -// * Set the CircleStrokeColor property -// *

-// * The stroke color of the circle. -// *

-// *

-// * To update the circle on the map use {@link CircleManager#update(AbstractAnnotation)}. -// *

-// * -// * @param color value for String -// */ -// public void setCircleStrokeColor(@NonNull String color) { -// jsonObject.addProperty("circle-stroke-color", color); -// } -// -// /** -// * Get the CircleStrokeOpacity property -// *

-// * The opacity of the circle's stroke. -// *

-// * -// * @return property wrapper value around Float -// */ -// public Float getCircleStrokeOpacity() { -// return jsonObject.get(CircleOptions.PROPERTY_CIRCLE_STROKE_OPACITY).getAsFloat(); -// } -// -// /** -// * Set the CircleStrokeOpacity property -// *

-// * The opacity of the circle's stroke. -// *

-// *

-// * To update the circle on the map use {@link CircleManager#update(AbstractAnnotation)}. -// *

-// * -// * @param value constant property value for Float -// */ -// public void setCircleStrokeOpacity(Float value) { -// jsonObject.addProperty(CircleOptions.PROPERTY_CIRCLE_STROKE_OPACITY, value); -// } -// -// @Override -// @Nullable -// Geometry getOffsetGeometry(@NonNull Projection projection, @NonNull MoveDistancesObject moveDistancesObject, -// float touchAreaShiftX, float touchAreaShiftY) { -// PointF pointF = new PointF( -// moveDistancesObject.getCurrentX() - touchAreaShiftX, -// moveDistancesObject.getCurrentY() - touchAreaShiftY -// ); -// -// LatLng latLng = projection.fromScreenLocation(pointF); -// if (latLng.getLatitude() > MAX_MERCATOR_LATITUDE || latLng.getLatitude() < MIN_MERCATOR_LATITUDE) { -// return null; -// } -// -// return Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude()); -// } -// -// @Override -// String getName() { -// return "Circle"; -// } -//} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/KCircle.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Circle.kt similarity index 98% rename from platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/KCircle.kt rename to platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Circle.kt index a5fbf2db7e6..98acde1c658 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/KCircle.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Circle.kt @@ -10,7 +10,7 @@ import org.maplibre.android.constants.GeometryConstants import org.maplibre.android.geometry.LatLng import org.maplibre.android.maps.Projection -class KCircle( +class Circle( center: LatLng, radius: Float = Defaults.CIRCLE_RADIUS, @ColorInt color: Int = Defaults.CIRCLE_COLOR, @@ -102,7 +102,7 @@ class KCircle( val flattenedValues: List get() = listOf( - PROPERTY_CIRCLE_STROKE_WIDTH to width default null, + PROPERTY_CIRCLE_STROKE_WIDTH to width default Unit, PROPERTY_CIRCLE_STROKE_COLOR to color.asColorString() default Defaults.CIRCLE_STROKE_COLOR.asColorString(), PROPERTY_CIRCLE_STROKE_OPACITY to opacity default Defaults.CIRCLE_STROKE_OPACITY diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/CircleManager.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/CircleManager.kt index 50610ac532e..46263ba6269 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/CircleManager.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/CircleManager.kt @@ -31,7 +31,7 @@ class CircleManager @UiThread internal constructor( mapView, maplibreMap ) -) : AnnotationManager( +) : AnnotationManager( mapView, maplibreMap, style, @@ -61,33 +61,33 @@ class CircleManager @UiThread internal constructor( ) override fun generateDataDrivenPropertyExpression(property: String): PropertyValue = when (property) { - KCircle.PROPERTY_CIRCLE_RADIUS -> PropertyFactory.circleRadius( - Expression.get(KCircle.PROPERTY_CIRCLE_RADIUS) + Circle.PROPERTY_CIRCLE_RADIUS -> PropertyFactory.circleRadius( + Expression.get(Circle.PROPERTY_CIRCLE_RADIUS) ) - KCircle.PROPERTY_CIRCLE_COLOR -> PropertyFactory.circleColor( - Expression.get(KCircle.PROPERTY_CIRCLE_COLOR) + Circle.PROPERTY_CIRCLE_COLOR -> PropertyFactory.circleColor( + Expression.get(Circle.PROPERTY_CIRCLE_COLOR) ) - KCircle.PROPERTY_CIRCLE_BLUR -> PropertyFactory.circleBlur( - Expression.get(KCircle.PROPERTY_CIRCLE_BLUR) + Circle.PROPERTY_CIRCLE_BLUR -> PropertyFactory.circleBlur( + Expression.get(Circle.PROPERTY_CIRCLE_BLUR) ) - KCircle.PROPERTY_CIRCLE_OPACITY -> PropertyFactory.circleOpacity( - Expression.get(KCircle.PROPERTY_CIRCLE_OPACITY) + Circle.PROPERTY_CIRCLE_OPACITY -> PropertyFactory.circleOpacity( + Expression.get(Circle.PROPERTY_CIRCLE_OPACITY) ) - KCircle.PROPERTY_CIRCLE_STROKE_WIDTH -> PropertyFactory.circleStrokeWidth( - Expression.get(KCircle.PROPERTY_CIRCLE_STROKE_WIDTH) + Circle.PROPERTY_CIRCLE_STROKE_WIDTH -> PropertyFactory.circleStrokeWidth( + Expression.get(Circle.PROPERTY_CIRCLE_STROKE_WIDTH) ) - KCircle.PROPERTY_CIRCLE_STROKE_COLOR -> PropertyFactory.circleStrokeColor( - Expression.get(KCircle.PROPERTY_CIRCLE_STROKE_COLOR) + Circle.PROPERTY_CIRCLE_STROKE_COLOR -> PropertyFactory.circleStrokeColor( + Expression.get(Circle.PROPERTY_CIRCLE_STROKE_COLOR) ) - KCircle.PROPERTY_CIRCLE_STROKE_OPACITY -> PropertyFactory.circleStrokeOpacity( - Expression.get(KCircle.PROPERTY_CIRCLE_STROKE_OPACITY) + Circle.PROPERTY_CIRCLE_STROKE_OPACITY -> PropertyFactory.circleStrokeOpacity( + Expression.get(Circle.PROPERTY_CIRCLE_STROKE_OPACITY) ) else -> throw IllegalArgumentException( diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/CircleOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/CircleOptions.java deleted file mode 100644 index 7bf9b2291cc..00000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/CircleOptions.java +++ /dev/null @@ -1,364 +0,0 @@ -//package org.maplibre.android.annotations; -// -//import androidx.annotation.NonNull; -//import androidx.annotation.Nullable; -// -//import com.google.gson.JsonElement; -//import com.google.gson.JsonObject; -//import com.mapbox.geojson.Feature; -//import com.mapbox.geojson.Point; -// -//import org.maplibre.android.geometry.LatLng; -//import org.maplibre.android.style.layers.PropertyFactory; -// -///** -// * Builder class from which a circle is created. -// */ -//public class CircleOptions extends Options { -// -// private boolean isDraggable; -// private JsonElement data; -// private Point geometry; -// private Float circleRadius; -// private String circleColor; -// private Float circleBlur; -// private Float circleOpacity; -// private Float circleStrokeWidth; -// private String circleStrokeColor; -// private Float circleStrokeOpacity; -// -// static final String PROPERTY_CIRCLE_RADIUS = "circle-radius"; -// static final String PROPERTY_CIRCLE_COLOR = "circle-color"; -// static final String PROPERTY_CIRCLE_BLUR = "circle-blur"; -// static final String PROPERTY_CIRCLE_OPACITY = "circle-opacity"; -// static final String PROPERTY_CIRCLE_STROKE_WIDTH = "circle-stroke-width"; -// static final String PROPERTY_CIRCLE_STROKE_COLOR = "circle-stroke-color"; -// static final String PROPERTY_CIRCLE_STROKE_OPACITY = "circle-stroke-opacity"; -// private static final String PROPERTY_IS_DRAGGABLE = "is-draggable"; -// -// /** -// * Set circle-radius to initialise the circle with. -// *

-// * Circle radius. -// *

-// * -// * @param circleRadius the circle-radius value -// * @return this -// */ -// public CircleOptions withCircleRadius(Float circleRadius) { -// this.circleRadius = circleRadius; -// return this; -// } -// -// /** -// * Get the current configured circle-radius for the circle -// *

-// * Circle radius. -// *

-// * -// * @return circleRadius value -// */ -// public Float getCircleRadius() { -// return circleRadius; -// } -// -// /** -// * Set circle-color to initialise the circle with. -// *

-// * The fill color of the circle. -// *

-// * -// * @param circleColor the circle-color value -// * @return this -// */ -// public CircleOptions withCircleColor(String circleColor) { -// this.circleColor = circleColor; -// return this; -// } -// -// /** -// * Get the current configured circle-color for the circle -// *

-// * The fill color of the circle. -// *

-// * -// * @return circleColor value -// */ -// public String getCircleColor() { -// return circleColor; -// } -// -// /** -// * Set circle-blur to initialise the circle with. -// *

-// * Amount to blur the circle. 1 blurs the circle such that only the centerpoint is full opacity. -// *

-// * -// * @param circleBlur the circle-blur value -// * @return this -// */ -// public CircleOptions withCircleBlur(Float circleBlur) { -// this.circleBlur = circleBlur; -// return this; -// } -// -// /** -// * Get the current configured circle-blur for the circle -// *

-// * Amount to blur the circle. 1 blurs the circle such that only the centerpoint is full opacity. -// *

-// * -// * @return circleBlur value -// */ -// public Float getCircleBlur() { -// return circleBlur; -// } -// -// /** -// * Set circle-opacity to initialise the circle with. -// *

-// * The opacity at which the circle will be drawn. -// *

-// * -// * @param circleOpacity the circle-opacity value -// * @return this -// */ -// public CircleOptions withCircleOpacity(Float circleOpacity) { -// this.circleOpacity = circleOpacity; -// return this; -// } -// -// /** -// * Get the current configured circle-opacity for the circle -// *

-// * The opacity at which the circle will be drawn. -// *

-// * -// * @return circleOpacity value -// */ -// public Float getCircleOpacity() { -// return circleOpacity; -// } -// -// /** -// * Set circle-stroke-width to initialise the circle with. -// *

-// * The width of the circle's stroke. Strokes are placed outside of the {@link PropertyFactory#circleRadius}. -// *

-// * -// * @param circleStrokeWidth the circle-stroke-width value -// * @return this -// */ -// public CircleOptions withCircleStrokeWidth(Float circleStrokeWidth) { -// this.circleStrokeWidth = circleStrokeWidth; -// return this; -// } -// -// /** -// * Get the current configured circle-stroke-width for the circle -// *

-// * The width of the circle's stroke. Strokes are placed outside of the {@link PropertyFactory#circleRadius}. -// *

-// * -// * @return circleStrokeWidth value -// */ -// public Float getCircleStrokeWidth() { -// return circleStrokeWidth; -// } -// -// /** -// * Set circle-stroke-color to initialise the circle with. -// *

-// * The stroke color of the circle. -// *

-// * -// * @param circleStrokeColor the circle-stroke-color value -// * @return this -// */ -// public CircleOptions withCircleStrokeColor(String circleStrokeColor) { -// this.circleStrokeColor = circleStrokeColor; -// return this; -// } -// -// /** -// * Get the current configured circle-stroke-color for the circle -// *

-// * The stroke color of the circle. -// *

-// * -// * @return circleStrokeColor value -// */ -// public String getCircleStrokeColor() { -// return circleStrokeColor; -// } -// -// /** -// * Set circle-stroke-opacity to initialise the circle with. -// *

-// * The opacity of the circle's stroke. -// *

-// * -// * @param circleStrokeOpacity the circle-stroke-opacity value -// * @return this -// */ -// public CircleOptions withCircleStrokeOpacity(Float circleStrokeOpacity) { -// this.circleStrokeOpacity = circleStrokeOpacity; -// return this; -// } -// -// /** -// * Get the current configured circle-stroke-opacity for the circle -// *

-// * The opacity of the circle's stroke. -// *

-// * -// * @return circleStrokeOpacity value -// */ -// public Float getCircleStrokeOpacity() { -// return circleStrokeOpacity; -// } -// -// /** -// * Set the LatLng of the circle, which represents the location of the circle on the map -// * -// * @param latLng the location of the circle in a longitude and latitude pair -// * @return this -// */ -// public CircleOptions withLatLng(LatLng latLng) { -// geometry = Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude()); -// return this; -// } -// -// /** -// * Get the LatLng of the circle, which represents the location of the circle on the map -// * -// * @return the location of the circle in a longitude and latitude pair -// */ -// public LatLng getLatLng() { -// if (geometry == null) { -// return null; -// } -// return new LatLng(geometry.latitude(), geometry.longitude()); -// } -// -// /** -// * Set the geometry of the circle, which represents the location of the circle on the map -// * -// * @param geometry the location of the circle -// * @return this -// */ -// public CircleOptions withGeometry(Point geometry) { -// this.geometry = geometry; -// return this; -// } -// -// /** -// * Get the geometry of the circle, which represents the location of the circle on the map -// * -// * @return the location of the circle -// */ -// public Point getGeometry() { -// return geometry; -// } -// -// /** -// * Returns whether this circle is draggable, meaning it can be dragged across the screen when touched and moved. -// * -// * @return draggable when touched -// */ -// public boolean getDraggable() { -// return isDraggable; -// } -// -// /** -// * Set whether this circle should be draggable, meaning it can be dragged across the screen when touched and moved. -// * -// * @param draggable should be draggable -// */ -// public CircleOptions withDraggable(boolean draggable) { -// isDraggable = draggable; -// return this; -// } -// -// /** -// * Set the arbitrary json data of the annotation. -// * -// * @param jsonElement the arbitrary json element data -// */ -// public CircleOptions withData(@Nullable JsonElement jsonElement) { -// this.data = jsonElement; -// return this; -// } -// -// /** -// * Get the arbitrary json data of the annotation. -// * -// * @return the arbitrary json object data if set, else null -// */ -// @Nullable -// public JsonElement getData() { -// return data; -// } -// -// @Override -// Circle build(long id, AnnotationManager annotationManager) { -// if (geometry == null) { -// throw new RuntimeException("geometry field is required"); -// } -// JsonObject jsonObject = new JsonObject(); -// jsonObject.addProperty(PROPERTY_CIRCLE_RADIUS, circleRadius); -// jsonObject.addProperty(PROPERTY_CIRCLE_COLOR, circleColor); -// jsonObject.addProperty(PROPERTY_CIRCLE_BLUR, circleBlur); -// jsonObject.addProperty(PROPERTY_CIRCLE_OPACITY, circleOpacity); -// jsonObject.addProperty(PROPERTY_CIRCLE_STROKE_WIDTH, circleStrokeWidth); -// jsonObject.addProperty(PROPERTY_CIRCLE_STROKE_COLOR, circleStrokeColor); -// jsonObject.addProperty(PROPERTY_CIRCLE_STROKE_OPACITY, circleStrokeOpacity); -// Circle circle = new Circle(id, annotationManager, jsonObject, geometry); -// circle.setDraggable(isDraggable); -// circle.setData(data); -// return circle; -// } -// -// /** -// * Creates CircleOptions out of a Feature. -// * -// * @param feature feature to be converted -// */ -// @Nullable -// static CircleOptions fromFeature(@NonNull Feature feature) { -// if (feature.geometry() == null) { -// throw new RuntimeException("geometry field is required"); -// } -// if (!(feature.geometry() instanceof Point)) { -// return null; -// } -// -// CircleOptions options = new CircleOptions(); -// options.geometry = (Point) feature.geometry(); -// if (feature.hasProperty(PROPERTY_CIRCLE_RADIUS)) { -// options.circleRadius = feature.getProperty(PROPERTY_CIRCLE_RADIUS).getAsFloat(); -// } -// if (feature.hasProperty(PROPERTY_CIRCLE_COLOR)) { -// options.circleColor = feature.getProperty(PROPERTY_CIRCLE_COLOR).getAsString(); -// } -// if (feature.hasProperty(PROPERTY_CIRCLE_BLUR)) { -// options.circleBlur = feature.getProperty(PROPERTY_CIRCLE_BLUR).getAsFloat(); -// } -// if (feature.hasProperty(PROPERTY_CIRCLE_OPACITY)) { -// options.circleOpacity = feature.getProperty(PROPERTY_CIRCLE_OPACITY).getAsFloat(); -// } -// if (feature.hasProperty(PROPERTY_CIRCLE_STROKE_WIDTH)) { -// options.circleStrokeWidth = feature.getProperty(PROPERTY_CIRCLE_STROKE_WIDTH).getAsFloat(); -// } -// if (feature.hasProperty(PROPERTY_CIRCLE_STROKE_COLOR)) { -// options.circleStrokeColor = feature.getProperty(PROPERTY_CIRCLE_STROKE_COLOR).getAsString(); -// } -// if (feature.hasProperty(PROPERTY_CIRCLE_STROKE_OPACITY)) { -// options.circleStrokeOpacity = feature.getProperty(PROPERTY_CIRCLE_STROKE_OPACITY).getAsFloat(); -// } -// if (feature.hasProperty(PROPERTY_IS_DRAGGABLE)) { -// options.isDraggable = feature.getProperty(PROPERTY_IS_DRAGGABLE).getAsBoolean(); -// } -// return options; -// } -//} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationClickListener.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationClickListener.kt index 400ca0f9af8..ecc81414708 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationClickListener.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationClickListener.kt @@ -19,9 +19,9 @@ interface OnAnnotationClickListener> { /** * Interface definition for a callback to be invoked when a symbol has been clicked. */ -typealias OnSymbolClickListener = OnAnnotationClickListener +typealias OnSymbolClickListener = OnAnnotationClickListener /** * Interface definition for a callback to be invoked when a circle has been clicked. */ -typealias OnCircleClickListener = OnAnnotationClickListener \ No newline at end of file +typealias OnCircleClickListener = OnAnnotationClickListener \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationDragListener.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationDragListener.kt index 5351042261e..84e3584f2a1 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationDragListener.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationDragListener.kt @@ -31,9 +31,9 @@ interface OnAnnotationDragListener> { /** * Interface definition for a callback to be invoked when a symbol is dragged. */ -typealias OnSymbolDragListener = OnAnnotationDragListener +typealias OnSymbolDragListener = OnAnnotationDragListener /** * Interface definition for a callback to be invoked when a circle is dragged. */ -typealias OnCircleDragListener = OnAnnotationDragListener \ No newline at end of file +typealias OnCircleDragListener = OnAnnotationDragListener \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationLongClickListener.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationLongClickListener.kt index 5357027a2f1..26f80d4edba 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationLongClickListener.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationLongClickListener.kt @@ -19,9 +19,9 @@ interface OnAnnotationLongClickListener> { /** * Interface definition for a callback to be invoked when a symbol has been long clicked. */ -typealias OnSymbolLongClickListener = OnAnnotationLongClickListener +typealias OnSymbolLongClickListener = OnAnnotationLongClickListener /** * Interface definition for a callback to be invoked when a circle has been long clicked. */ -typealias OnCircleLongClickListener = OnAnnotationLongClickListener \ No newline at end of file +typealias OnCircleLongClickListener = OnAnnotationLongClickListener \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/KSymbol.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Symbol.kt similarity index 99% rename from platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/KSymbol.kt rename to platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Symbol.kt index ab163fa0d9c..9a2ecef692c 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/KSymbol.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Symbol.kt @@ -11,7 +11,7 @@ import org.maplibre.android.constants.GeometryConstants import org.maplibre.android.geometry.LatLng import org.maplibre.android.maps.Projection -class KSymbol( +class Symbol( position: LatLng, icon: Icon? = Defaults.SYMBOL_ICON, text: Text? = Defaults.SYMBOL_TEXT diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/SymbolManager.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/SymbolManager.kt index ca84394ce5b..6d8ea8b30f4 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/SymbolManager.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/SymbolManager.kt @@ -32,7 +32,7 @@ class SymbolManager @UiThread internal constructor( mapView, maplibreMap ) -) : AnnotationManager( +) : AnnotationManager( mapView, maplibreMap, style, @@ -100,113 +100,113 @@ class SymbolManager @UiThread internal constructor( } override fun generateDataDrivenPropertyExpression(property: String): PropertyValue = when (property) { - KSymbol.PROPERTY_SYMBOL_SORT_KEY -> PropertyFactory.symbolSortKey( - Expression.get(KSymbol.PROPERTY_SYMBOL_SORT_KEY) + Symbol.PROPERTY_SYMBOL_SORT_KEY -> PropertyFactory.symbolSortKey( + Expression.get(Symbol.PROPERTY_SYMBOL_SORT_KEY) ) - KSymbol.PROPERTY_ICON_SIZE -> PropertyFactory.iconSize( - Expression.get(KSymbol.PROPERTY_ICON_SIZE) + Symbol.PROPERTY_ICON_SIZE -> PropertyFactory.iconSize( + Expression.get(Symbol.PROPERTY_ICON_SIZE) ) - KSymbol.PROPERTY_ICON_IMAGE -> PropertyFactory.iconImage( - Expression.get(KSymbol.PROPERTY_ICON_IMAGE) + Symbol.PROPERTY_ICON_IMAGE -> PropertyFactory.iconImage( + Expression.get(Symbol.PROPERTY_ICON_IMAGE) ) - KSymbol.PROPERTY_ICON_ROTATE -> PropertyFactory.iconRotate( - Expression.get(KSymbol.PROPERTY_ICON_ROTATE) + Symbol.PROPERTY_ICON_ROTATE -> PropertyFactory.iconRotate( + Expression.get(Symbol.PROPERTY_ICON_ROTATE) ) - KSymbol.PROPERTY_ICON_OFFSET -> PropertyFactory.iconOffset( - Expression.get(KSymbol.PROPERTY_ICON_OFFSET) + Symbol.PROPERTY_ICON_OFFSET -> PropertyFactory.iconOffset( + Expression.get(Symbol.PROPERTY_ICON_OFFSET) ) - KSymbol.PROPERTY_ICON_ANCHOR -> PropertyFactory.iconAnchor( - Expression.get(KSymbol.PROPERTY_ICON_ANCHOR) + Symbol.PROPERTY_ICON_ANCHOR -> PropertyFactory.iconAnchor( + Expression.get(Symbol.PROPERTY_ICON_ANCHOR) ) - KSymbol.PROPERTY_TEXT_FIELD -> PropertyFactory.textField( - Expression.get(KSymbol.PROPERTY_TEXT_FIELD) + Symbol.PROPERTY_TEXT_FIELD -> PropertyFactory.textField( + Expression.get(Symbol.PROPERTY_TEXT_FIELD) ) - KSymbol.PROPERTY_TEXT_FONT -> PropertyFactory.textFont( - Expression.get(KSymbol.PROPERTY_TEXT_FONT) + Symbol.PROPERTY_TEXT_FONT -> PropertyFactory.textFont( + Expression.get(Symbol.PROPERTY_TEXT_FONT) ) - KSymbol.PROPERTY_TEXT_SIZE -> PropertyFactory.textSize( - Expression.get(KSymbol.PROPERTY_TEXT_SIZE) + Symbol.PROPERTY_TEXT_SIZE -> PropertyFactory.textSize( + Expression.get(Symbol.PROPERTY_TEXT_SIZE) ) - KSymbol.PROPERTY_TEXT_MAX_WIDTH -> PropertyFactory.textMaxWidth( - Expression.get(KSymbol.PROPERTY_TEXT_MAX_WIDTH) + Symbol.PROPERTY_TEXT_MAX_WIDTH -> PropertyFactory.textMaxWidth( + Expression.get(Symbol.PROPERTY_TEXT_MAX_WIDTH) ) - KSymbol.PROPERTY_TEXT_LETTER_SPACING -> PropertyFactory.textLetterSpacing( - Expression.get(KSymbol.PROPERTY_TEXT_LETTER_SPACING) + Symbol.PROPERTY_TEXT_LETTER_SPACING -> PropertyFactory.textLetterSpacing( + Expression.get(Symbol.PROPERTY_TEXT_LETTER_SPACING) ) - KSymbol.PROPERTY_TEXT_JUSTIFY -> PropertyFactory.textJustify( - Expression.get(KSymbol.PROPERTY_TEXT_JUSTIFY) + Symbol.PROPERTY_TEXT_JUSTIFY -> PropertyFactory.textJustify( + Expression.get(Symbol.PROPERTY_TEXT_JUSTIFY) ) - KSymbol.PROPERTY_TEXT_RADIAL_OFFSET -> PropertyFactory.textRadialOffset( - Expression.get(KSymbol.PROPERTY_TEXT_RADIAL_OFFSET) + Symbol.PROPERTY_TEXT_RADIAL_OFFSET -> PropertyFactory.textRadialOffset( + Expression.get(Symbol.PROPERTY_TEXT_RADIAL_OFFSET) ) - KSymbol.PROPERTY_TEXT_ANCHOR -> PropertyFactory.textAnchor( - Expression.get(KSymbol.PROPERTY_TEXT_ANCHOR) + Symbol.PROPERTY_TEXT_ANCHOR -> PropertyFactory.textAnchor( + Expression.get(Symbol.PROPERTY_TEXT_ANCHOR) ) - KSymbol.PROPERTY_TEXT_ROTATE -> PropertyFactory.textRotate( - Expression.get(KSymbol.PROPERTY_TEXT_ROTATE) + Symbol.PROPERTY_TEXT_ROTATE -> PropertyFactory.textRotate( + Expression.get(Symbol.PROPERTY_TEXT_ROTATE) ) - KSymbol.PROPERTY_TEXT_TRANSFORM -> PropertyFactory.textTransform( - Expression.get(KSymbol.PROPERTY_TEXT_TRANSFORM) + Symbol.PROPERTY_TEXT_TRANSFORM -> PropertyFactory.textTransform( + Expression.get(Symbol.PROPERTY_TEXT_TRANSFORM) ) - KSymbol.PROPERTY_TEXT_OFFSET -> PropertyFactory.textOffset( - Expression.get(KSymbol.PROPERTY_TEXT_OFFSET) + Symbol.PROPERTY_TEXT_OFFSET -> PropertyFactory.textOffset( + Expression.get(Symbol.PROPERTY_TEXT_OFFSET) ) - KSymbol.PROPERTY_ICON_OPACITY -> PropertyFactory.iconOpacity( - Expression.get(KSymbol.PROPERTY_ICON_OPACITY) + Symbol.PROPERTY_ICON_OPACITY -> PropertyFactory.iconOpacity( + Expression.get(Symbol.PROPERTY_ICON_OPACITY) ) - KSymbol.PROPERTY_ICON_COLOR -> PropertyFactory.iconColor( - Expression.get(KSymbol.PROPERTY_ICON_COLOR) + Symbol.PROPERTY_ICON_COLOR -> PropertyFactory.iconColor( + Expression.get(Symbol.PROPERTY_ICON_COLOR) ) - KSymbol.PROPERTY_ICON_HALO_COLOR -> PropertyFactory.iconHaloColor( - Expression.get(KSymbol.PROPERTY_ICON_HALO_COLOR) + Symbol.PROPERTY_ICON_HALO_COLOR -> PropertyFactory.iconHaloColor( + Expression.get(Symbol.PROPERTY_ICON_HALO_COLOR) ) - KSymbol.PROPERTY_ICON_HALO_WIDTH -> PropertyFactory.iconHaloWidth( - Expression.get(KSymbol.PROPERTY_ICON_HALO_WIDTH) + Symbol.PROPERTY_ICON_HALO_WIDTH -> PropertyFactory.iconHaloWidth( + Expression.get(Symbol.PROPERTY_ICON_HALO_WIDTH) ) - KSymbol.PROPERTY_ICON_HALO_BLUR -> PropertyFactory.iconHaloBlur( - Expression.get(KSymbol.PROPERTY_ICON_HALO_BLUR) + Symbol.PROPERTY_ICON_HALO_BLUR -> PropertyFactory.iconHaloBlur( + Expression.get(Symbol.PROPERTY_ICON_HALO_BLUR) ) - KSymbol.PROPERTY_TEXT_OPACITY -> PropertyFactory.textOpacity( - Expression.get(KSymbol.PROPERTY_TEXT_OPACITY) + Symbol.PROPERTY_TEXT_OPACITY -> PropertyFactory.textOpacity( + Expression.get(Symbol.PROPERTY_TEXT_OPACITY) ) - KSymbol.PROPERTY_TEXT_COLOR -> PropertyFactory.textColor( - Expression.get(KSymbol.PROPERTY_TEXT_COLOR) + Symbol.PROPERTY_TEXT_COLOR -> PropertyFactory.textColor( + Expression.get(Symbol.PROPERTY_TEXT_COLOR) ) - KSymbol.PROPERTY_TEXT_HALO_COLOR -> PropertyFactory.textHaloColor( - Expression.get(KSymbol.PROPERTY_TEXT_HALO_COLOR) + Symbol.PROPERTY_TEXT_HALO_COLOR -> PropertyFactory.textHaloColor( + Expression.get(Symbol.PROPERTY_TEXT_HALO_COLOR) ) - KSymbol.PROPERTY_TEXT_HALO_WIDTH -> PropertyFactory.textHaloWidth( - Expression.get(KSymbol.PROPERTY_TEXT_HALO_WIDTH) + Symbol.PROPERTY_TEXT_HALO_WIDTH -> PropertyFactory.textHaloWidth( + Expression.get(Symbol.PROPERTY_TEXT_HALO_WIDTH) ) - KSymbol.PROPERTY_TEXT_HALO_BLUR -> PropertyFactory.textHaloBlur( - Expression.get(KSymbol.PROPERTY_TEXT_HALO_BLUR) + Symbol.PROPERTY_TEXT_HALO_BLUR -> PropertyFactory.textHaloBlur( + Expression.get(Symbol.PROPERTY_TEXT_HALO_BLUR) ) else -> throw IllegalArgumentException( diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/data/Defaults.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/data/Defaults.kt index 9d6fab9a00a..93fca919fb2 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/data/Defaults.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/data/Defaults.kt @@ -4,7 +4,7 @@ import android.graphics.Color import android.graphics.PointF import androidx.annotation.ColorInt import com.google.gson.JsonElement -import org.maplibre.android.annotations.KCircle +import org.maplibre.android.annotations.Circle class Defaults { companion object { @@ -53,7 +53,7 @@ class Defaults { val CIRCLE_COLOR: Int = Color.BLACK val CIRCLE_BLUR: Float? = null val CIRCLE_OPACITY: Float = 1f - val CIRCLE_STROKE: KCircle.Stroke? = null + val CIRCLE_STROKE: Circle.Stroke? = null val CIRCLE_STROKE_COLOR: Int = Color.BLACK val CIRCLE_STROKE_OPACITY: Float = 1f diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/data/Icon.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/data/Icon.kt index 50983e4a7f8..2b6d733852d 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/data/Icon.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/data/Icon.kt @@ -6,7 +6,7 @@ import android.graphics.drawable.Drawable import androidx.annotation.ColorInt import androidx.annotation.FloatRange import androidx.core.graphics.drawable.toBitmap -import org.maplibre.android.annotations.KSymbol +import org.maplibre.android.annotations.Symbol import org.maplibre.android.annotations.PairWithDefault import org.maplibre.android.annotations.asColorString import org.maplibre.android.annotations.default @@ -66,12 +66,12 @@ open class Icon( open val flattenedValues: List get() = listOf( - KSymbol.PROPERTY_ICON_SIZE to size default Defaults.ICON_SIZE, - KSymbol.PROPERTY_ICON_IMAGE to image default Unit, // todo string representation - KSymbol.PROPERTY_ICON_ROTATE to rotate default Defaults.ICON_ROTATE, - KSymbol.PROPERTY_ICON_OFFSET to offset.toArray() default Defaults.ICON_OFFSET, - KSymbol.PROPERTY_ICON_ANCHOR to anchor.toString() default Defaults.ICON_ANCHOR.toString(), - KSymbol.PROPERTY_ICON_OPACITY to opacity default Defaults.ICON_OPACITY + Symbol.PROPERTY_ICON_SIZE to size default Defaults.ICON_SIZE, + Symbol.PROPERTY_ICON_IMAGE to image default Unit, // todo string representation + Symbol.PROPERTY_ICON_ROTATE to rotate default Defaults.ICON_ROTATE, + Symbol.PROPERTY_ICON_OFFSET to offset.toArray() default Defaults.ICON_OFFSET, + Symbol.PROPERTY_ICON_ANCHOR to anchor.toString() default Defaults.ICON_ANCHOR.toString(), + Symbol.PROPERTY_ICON_OPACITY to opacity default Defaults.ICON_OPACITY ) } @@ -87,10 +87,10 @@ class SdfIcon( ) : Icon(image, size, rotate, offset, anchor, opacity) { override val flattenedValues: List get() = super.flattenedValues + listOf( - KSymbol.PROPERTY_ICON_COLOR to color.asColorString() default Unit, - KSymbol.PROPERTY_ICON_HALO_COLOR to halo?.color default Defaults.ICON_HALO?.color, - KSymbol.PROPERTY_ICON_HALO_WIDTH to halo?.width default Defaults.ICON_HALO?.width, - KSymbol.PROPERTY_ICON_HALO_BLUR to halo?.blur default Defaults.ICON_HALO?.blur + Symbol.PROPERTY_ICON_COLOR to color.asColorString() default Unit, + Symbol.PROPERTY_ICON_HALO_COLOR to halo?.color default Defaults.ICON_HALO?.color, + Symbol.PROPERTY_ICON_HALO_WIDTH to halo?.width default Defaults.ICON_HALO?.width, + Symbol.PROPERTY_ICON_HALO_BLUR to halo?.blur default Defaults.ICON_HALO?.blur ) } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/data/Text.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/data/Text.kt index 02064b49fbd..e656963fee2 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/data/Text.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/data/Text.kt @@ -3,7 +3,7 @@ package org.maplibre.android.annotations.data import android.graphics.PointF import androidx.annotation.ColorInt import androidx.annotation.FloatRange -import org.maplibre.android.annotations.KSymbol +import org.maplibre.android.annotations.Symbol import org.maplibre.android.annotations.PairWithDefault import org.maplibre.android.annotations.asColorString import org.maplibre.android.annotations.default @@ -62,33 +62,33 @@ class Text( val flattenedValues: List get() = listOf( - KSymbol.PROPERTY_TEXT_FIELD to string default Unit, - KSymbol.PROPERTY_TEXT_FONT to font default Defaults.TEXT_FONT, - KSymbol.PROPERTY_TEXT_SIZE to size default Defaults.TEXT_SIZE, - KSymbol.PROPERTY_TEXT_MAX_WIDTH to maxWidth default Defaults.TEXT_MAX_WIDTH, - KSymbol.PROPERTY_TEXT_LETTER_SPACING to letterSpacing default Defaults.TEXT_LETTER_SPACING, - KSymbol.PROPERTY_TEXT_JUSTIFY to justify.toString() default Defaults.TEXT_JUSTIFY.toString(), - KSymbol.PROPERTY_TEXT_RADIAL_OFFSET to offset?.let { + Symbol.PROPERTY_TEXT_FIELD to string default Unit, + Symbol.PROPERTY_TEXT_FONT to font default Defaults.TEXT_FONT, + Symbol.PROPERTY_TEXT_SIZE to size default Defaults.TEXT_SIZE, + Symbol.PROPERTY_TEXT_MAX_WIDTH to maxWidth default Defaults.TEXT_MAX_WIDTH, + Symbol.PROPERTY_TEXT_LETTER_SPACING to letterSpacing default Defaults.TEXT_LETTER_SPACING, + Symbol.PROPERTY_TEXT_JUSTIFY to justify.toString() default Defaults.TEXT_JUSTIFY.toString(), + Symbol.PROPERTY_TEXT_RADIAL_OFFSET to offset?.let { if (it is RadialOffset) { it.offset } else { null } } default Unit, - KSymbol.PROPERTY_TEXT_ANCHOR to anchor.toString() default Defaults.TEXT_ANCHOR.toString(), - KSymbol.PROPERTY_TEXT_ROTATE to rotate default Defaults.TEXT_ROTATE, - KSymbol.PROPERTY_TEXT_TRANSFORM to transform.toString() default Defaults.TEXT_TRANSFORM.toString(), - KSymbol.PROPERTY_TEXT_OFFSET to offset?.let { + Symbol.PROPERTY_TEXT_ANCHOR to anchor.toString() default Defaults.TEXT_ANCHOR.toString(), + Symbol.PROPERTY_TEXT_ROTATE to rotate default Defaults.TEXT_ROTATE, + Symbol.PROPERTY_TEXT_TRANSFORM to transform.toString() default Defaults.TEXT_TRANSFORM.toString(), + Symbol.PROPERTY_TEXT_OFFSET to offset?.let { if (it is AbsoluteOffset) { it.offset.toArray() } else { null } } default Unit, - KSymbol.PROPERTY_TEXT_OPACITY to opacity default Defaults.TEXT_OPACITY, - KSymbol.PROPERTY_TEXT_COLOR to color.asColorString() default Defaults.TEXT_COLOR.asColorString(), - KSymbol.PROPERTY_TEXT_HALO_COLOR to halo?.color default Defaults.TEXT_HALO?.color, - KSymbol.PROPERTY_TEXT_HALO_WIDTH to halo?.width default Defaults.TEXT_HALO?.width, - KSymbol.PROPERTY_TEXT_HALO_BLUR to halo?.blur default Defaults.TEXT_HALO?.blur + Symbol.PROPERTY_TEXT_OPACITY to opacity default Defaults.TEXT_OPACITY, + Symbol.PROPERTY_TEXT_COLOR to color.asColorString() default Defaults.TEXT_COLOR.asColorString(), + Symbol.PROPERTY_TEXT_HALO_COLOR to halo?.color default Defaults.TEXT_HALO?.color, + Symbol.PROPERTY_TEXT_HALO_WIDTH to halo?.width default Defaults.TEXT_HALO?.width, + Symbol.PROPERTY_TEXT_HALO_BLUR to halo?.blur default Defaults.TEXT_HALO?.blur ) } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/maps/MapLibreMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/maps/MapLibreMap.java index 52adc51f7f3..2e01a0f7abd 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/maps/MapLibreMap.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/maps/MapLibreMap.java @@ -26,7 +26,7 @@ import org.maplibre.android.annotations.Annotation; import org.maplibre.android.annotations.BaseMarkerOptions; import org.maplibre.android.annotations.KAnnotation; -import org.maplibre.android.annotations.KSymbol; +import org.maplibre.android.annotations.Symbol; import org.maplibre.android.annotations.Marker; import org.maplibre.android.annotations.MarkerOptions; import org.maplibre.android.annotations.Polygon; @@ -996,7 +996,7 @@ void notifyStyleLoaded() { // Annotations // - public void addSymbol(@NonNull KSymbol symbol) { + public void addSymbol(@NonNull Symbol symbol) { symbol.attach(this, nextId++); annotationContainer.add(symbol); } diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/CircleManagerTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/CircleManagerTest.kt index 43354207f32..ef17703e304 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/CircleManagerTest.kt +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/CircleManagerTest.kt @@ -187,7 +187,7 @@ class CircleManagerTest { null, draggableAnnotationController ) - val circle = KCircle(LatLng()) + val circle = Circle(LatLng()) circleManager.add(circle) Assert.assertSame(circle, circleManager.annotations.values.first()) } @@ -207,7 +207,7 @@ class CircleManagerTest { listOf( LatLng(), LatLng(1.0, 1.0) - ).map { KCircle(it) }.forEach { circleManager.add(it) } + ).map { Circle(it) }.forEach { circleManager.add(it) } Assert.assertTrue("Amount of annotations should match", circleManager.annotations.size == 2) } @@ -223,7 +223,7 @@ class CircleManagerTest { null, draggableAnnotationController ) - val circle = KCircle(LatLng()) + val circle = Circle(LatLng()) circleManager.add(circle) Assert.assertEquals( "After adding a circle, one circle should be present", @@ -251,7 +251,7 @@ class CircleManagerTest { draggableAnnotationController ) val latLng = LatLng(12.0, 34.0) - val circle = KCircle(latLng) + val circle = Circle(latLng) circleManager.add(circle) Assert.assertEquals(circle.center, latLng) Assert.assertEquals(circle.geometry, Point.fromLngLat(34.0, 12.0)) @@ -269,7 +269,7 @@ class CircleManagerTest { null, draggableAnnotationController ) - val circle = KCircle(LatLng()) + val circle = Circle(LatLng()) circleManager.add(circle) Assert.assertFalse(circle.draggable) circle.draggable = true @@ -298,7 +298,7 @@ class CircleManagerTest { ) ) for (i in 0 until 2) { - val circle = KCircle(LatLng()).apply { + val circle = Circle(LatLng()).apply { radius = 2f } circleManager.add(circle) @@ -333,7 +333,7 @@ class CircleManagerTest { ) ) for (i in 0 until 2) { - val circle = KCircle(LatLng()).apply { + val circle = Circle(LatLng()).apply { color = Color.RED } circleManager.add(circle) @@ -368,7 +368,7 @@ class CircleManagerTest { ) ) for (i in 0 until 2) { - val circle = KCircle(LatLng()).apply { + val circle = Circle(LatLng()).apply { blur = 2f } circleManager.add(circle) @@ -403,7 +403,7 @@ class CircleManagerTest { ) ) for (i in 0 until 2) { - val circle = KCircle(LatLng()).apply { + val circle = Circle(LatLng()).apply { opacity = 0.5f } circleManager.add(circle) @@ -438,7 +438,7 @@ class CircleManagerTest { ) ) for (i in 0 until 2) { - val circle = KCircle(LatLng(), stroke = KCircle.Stroke(width = 2f)) + val circle = Circle(LatLng(), stroke = Circle.Stroke(width = 2f)) circleManager.add(circle) circleManager.updateSourceNow() Mockito.verify(circleLayer, Mockito.times(1)).setProperties( @@ -471,7 +471,7 @@ class CircleManagerTest { ) ) for (i in 0 until 2) { - val circle = KCircle(LatLng(), stroke = KCircle.Stroke(width = 2f, color = Color.CYAN)) + val circle = Circle(LatLng(), stroke = Circle.Stroke(width = 2f, color = Color.CYAN)) circleManager.add(circle) circleManager.updateSourceNow() Mockito.verify(circleLayer, Mockito.times(1)).setProperties( @@ -504,7 +504,7 @@ class CircleManagerTest { ) ) for (i in 0 until 2) { - val circle = KCircle(LatLng(), stroke = KCircle.Stroke(width = 2f, opacity = 0.5f)) + val circle = Circle(LatLng(), stroke = Circle.Stroke(width = 2f, opacity = 0.5f)) circleManager.add(circle) circleManager.updateSourceNow() Mockito.verify(circleLayer, Mockito.times(1)).setProperties( @@ -541,7 +541,7 @@ class CircleManagerTest { @Test fun testClickListener() { val listener = object : OnCircleClickListener { - override fun onAnnotationClick(t: KCircle): Boolean = false + override fun onAnnotationClick(t: Circle): Boolean = false } circleManager = CircleManager( @@ -564,7 +564,7 @@ class CircleManagerTest { @Test fun testLongClickListener() { val listener = object : OnCircleLongClickListener { - override fun onAnnotationLongClick(t: KCircle): Boolean = false + override fun onAnnotationLongClick(t: Circle): Boolean = false } circleManager = CircleManager( mapView, @@ -586,9 +586,9 @@ class CircleManagerTest { @Test fun testDragListener() { val listener = object : OnCircleDragListener { - override fun onAnnotationDragStarted(annotation: KCircle) = Unit - override fun onAnnotationDrag(annotation: KCircle) = Unit - override fun onAnnotationDragFinished(annotation: KCircle) = Unit + override fun onAnnotationDragStarted(annotation: Circle) = Unit + override fun onAnnotationDrag(annotation: Circle) = Unit + override fun onAnnotationDragFinished(annotation: Circle) = Unit } circleManager = CircleManager( mapView, @@ -619,7 +619,7 @@ class CircleManagerTest { null, draggableAnnotationController ) - val circle = KCircle(LatLng()).apply { + val circle = Circle(LatLng()).apply { data = JsonPrimitive("hello") } circleManager.add(circle) @@ -638,7 +638,7 @@ class CircleManagerTest { null, draggableAnnotationController ) - val circle = KCircle(LatLng()) + val circle = Circle(LatLng()) circleManager.add(circle) Assert.assertEquals(1, circleManager.annotations.size) circleManager.deleteAll() diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/SymbolManagerTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/SymbolManagerTest.kt index 14bbbbe9833..70367f5c38f 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/SymbolManagerTest.kt +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/SymbolManagerTest.kt @@ -224,7 +224,7 @@ class SymbolManagerTest { null, draggableAnnotationController ) - val symbol = KSymbol(LatLng()) + val symbol = Symbol(LatLng()) symbolManager.add(symbol) Assert.assertSame(symbol, symbolManager.annotations.values.first()) } @@ -244,7 +244,7 @@ class SymbolManagerTest { listOf( LatLng(), LatLng(1.0, 1.0) - ).map { KSymbol(it) }.forEach { symbolManager.add(it) } + ).map { Symbol(it) }.forEach { symbolManager.add(it) } Assert.assertTrue("Amount of annotations should match", symbolManager.annotations.size == 2) } @@ -260,7 +260,7 @@ class SymbolManagerTest { null, draggableAnnotationController ) - val symbol = KSymbol(LatLng()) + val symbol = Symbol(LatLng()) symbolManager.add(symbol) Assert.assertEquals( "After adding a symbol, one symbol should be present", @@ -288,7 +288,7 @@ class SymbolManagerTest { draggableAnnotationController ) val latLng = LatLng(12.0, 34.0) - val symbol = KSymbol(latLng) + val symbol = Symbol(latLng) Assert.assertEquals(symbol.position, latLng) Assert.assertEquals(symbol.geometry, Point.fromLngLat(34.0, 12.0)) } @@ -306,7 +306,7 @@ class SymbolManagerTest { draggableAnnotationController ) (0 until 10).map { - KSymbol(LatLng()).also { symbolManager.add(it) } + Symbol(LatLng()).also { symbolManager.add(it) } }.forEachIndexed { index, symbol -> Assert.assertEquals( "Symbol ID should be generated and assigned automatically, starting with -1", @@ -328,7 +328,7 @@ class SymbolManagerTest { null, draggableAnnotationController ) - val symbol = KSymbol(LatLng()) + val symbol = Symbol(LatLng()) symbolManager.add(symbol) Assert.assertFalse(symbol.draggable) symbol.draggable = true @@ -357,7 +357,7 @@ class SymbolManagerTest { ) ) for (i in 0 until 2) { - val symbol = KSymbol(LatLng()).apply { + val symbol = Symbol(LatLng()).apply { zLayer = 2 } symbolManager.add(symbol) @@ -386,7 +386,7 @@ class SymbolManagerTest { ArgumentMatchers.argThat(PropertyValueMatcher(iconSize(get("icon-size")))) ) for (i in 0 until 2) { - val symbol = KSymbol( + val symbol = Symbol( LatLng(), Icon(Mockito.mock(Bitmap::class.java), size = 2f) ) @@ -414,7 +414,7 @@ class SymbolManagerTest { ArgumentMatchers.argThat(PropertyValueMatcher(iconImage(get("icon-image")))) ) for (i in 0 until 2) { - val symbol = KSymbol( + val symbol = Symbol( LatLng(), Icon(Mockito.mock(Bitmap::class.java)) ) @@ -442,7 +442,7 @@ class SymbolManagerTest { ArgumentMatchers.argThat(PropertyValueMatcher(iconRotate(get("icon-rotate")))) ) for (i in 0 until 2) { - val symbol = KSymbol( + val symbol = Symbol( LatLng(), Icon(Mockito.mock(Bitmap::class.java), rotate = 2f) ) @@ -471,7 +471,7 @@ class SymbolManagerTest { ) for (i in 0 until 2) { val symbol = - KSymbol( + Symbol( LatLng(), Icon( Mockito.mock(Bitmap::class.java), @@ -502,7 +502,7 @@ class SymbolManagerTest { ArgumentMatchers.argThat(PropertyValueMatcher(iconAnchor(get("icon-anchor")))) ) for (i in 0 until 2) { - val symbol = KSymbol( + val symbol = Symbol( LatLng(), Icon(Mockito.mock(Bitmap::class.java), anchor = Anchor.LEFT) ) @@ -530,7 +530,7 @@ class SymbolManagerTest { ArgumentMatchers.argThat(PropertyValueMatcher(textField(get("text-field")))) ) for (i in 0 until 2) { - val symbol = KSymbol(LatLng(), text = Text("text")) + val symbol = Symbol(LatLng(), text = Text("text")) symbolManager.add(symbol) symbolManager.updateSourceNow() Mockito.verify(symbolLayer, Mockito.times(1)).setProperties( @@ -555,7 +555,7 @@ class SymbolManagerTest { ArgumentMatchers.argThat(PropertyValueMatcher(textFont(get("text-font")))) ) for (i in 0 until 2) { - val symbol = KSymbol(LatLng(), text = Text("text", font = listOf("Open Sans Regular"))) + val symbol = Symbol(LatLng(), text = Text("text", font = listOf("Open Sans Regular"))) symbolManager.add(symbol) symbolManager.updateSourceNow() Mockito.verify(symbolLayer, Mockito.times(1)).setProperties( @@ -580,7 +580,7 @@ class SymbolManagerTest { ArgumentMatchers.argThat(PropertyValueMatcher(textSize(get("text-size")))) ) for (i in 0 until 2) { - val symbol = KSymbol(LatLng(), text = Text("text", size = 2f)) + val symbol = Symbol(LatLng(), text = Text("text", size = 2f)) symbolManager.add(symbol) symbolManager.updateSourceNow() Mockito.verify(symbolLayer, Mockito.times(1)).setProperties( @@ -605,7 +605,7 @@ class SymbolManagerTest { ArgumentMatchers.argThat(PropertyValueMatcher(textMaxWidth(get("text-max-width")))) ) for (i in 0 until 2) { - val symbol = KSymbol(LatLng(), text = Text("text", maxWidth = 2f)) + val symbol = Symbol(LatLng(), text = Text("text", maxWidth = 2f)) symbolManager.add(symbol) symbolManager.updateSourceNow() Mockito.verify(symbolLayer, Mockito.times(1)).setProperties( @@ -632,7 +632,7 @@ class SymbolManagerTest { ) ) for (i in 0 until 2) { - val symbol = KSymbol(LatLng(), text = Text("text", letterSpacing = 2f)) + val symbol = Symbol(LatLng(), text = Text("text", letterSpacing = 2f)) symbolManager.add(symbol) symbolManager.updateSourceNow() Mockito.verify(symbolLayer, Mockito.times(1)).setProperties( @@ -659,7 +659,7 @@ class SymbolManagerTest { ArgumentMatchers.argThat(PropertyValueMatcher(textJustify(get("text-justify")))) ) for (i in 0 until 2) { - val symbol = KSymbol(LatLng(), text = Text("text", justify = Text.Justify.AUTO)) + val symbol = Symbol(LatLng(), text = Text("text", justify = Text.Justify.AUTO)) symbolManager.add(symbol) symbolManager.updateSourceNow() Mockito.verify(symbolLayer, Mockito.times(1)).setProperties( @@ -686,7 +686,7 @@ class SymbolManagerTest { ) ) for (i in 0 until 2) { - val symbol = KSymbol(LatLng(), text = Text("text", offset = Text.RadialOffset(2f))) + val symbol = Symbol(LatLng(), text = Text("text", offset = Text.RadialOffset(2f))) symbolManager.add(symbol) symbolManager.updateSourceNow() Mockito.verify(symbolLayer, Mockito.times(1)).setProperties( @@ -713,7 +713,7 @@ class SymbolManagerTest { ArgumentMatchers.argThat(PropertyValueMatcher(textAnchor(get("text-anchor")))) ) for (i in 0 until 2) { - val symbol = KSymbol(LatLng(), text = Text("text", anchor = Anchor.LEFT)) + val symbol = Symbol(LatLng(), text = Text("text", anchor = Anchor.LEFT)) symbolManager.add(symbol) symbolManager.updateSourceNow() Mockito.verify(symbolLayer, Mockito.times(1)).setProperties( @@ -738,7 +738,7 @@ class SymbolManagerTest { ArgumentMatchers.argThat(PropertyValueMatcher(textRotate(get("text-rotate")))) ) for (i in 0 until 2) { - val symbol = KSymbol(LatLng(), text = Text("text", rotate = 2f)) + val symbol = Symbol(LatLng(), text = Text("text", rotate = 2f)) symbolManager.add(symbol) symbolManager.updateSourceNow() Mockito.verify(symbolLayer, Mockito.times(1)).setProperties( @@ -763,7 +763,7 @@ class SymbolManagerTest { ArgumentMatchers.argThat(PropertyValueMatcher(textTransform(get("text-transform")))) ) for (i in 0 until 2) { - val symbol = KSymbol( + val symbol = Symbol( LatLng(), text = Text("text", transform = Text.Transform.LOWERCASE) ) @@ -791,7 +791,7 @@ class SymbolManagerTest { ArgumentMatchers.argThat(PropertyValueMatcher(textOffset(get("text-offset")))) ) for (i in 0 until 2) { - val symbol = KSymbol( + val symbol = Symbol( LatLng(), text = Text("text", offset = Text.AbsoluteOffset(PointF(1f, 0f))) ) @@ -819,7 +819,7 @@ class SymbolManagerTest { ArgumentMatchers.argThat(PropertyValueMatcher(iconOpacity(get("icon-opacity")))) ) for (i in 0 until 2) { - val symbol = KSymbol(LatLng(), Icon(Mockito.mock(Bitmap::class.java), opacity = 0.5f)) + val symbol = Symbol(LatLng(), Icon(Mockito.mock(Bitmap::class.java), opacity = 0.5f)) symbolManager.add(symbol) symbolManager.updateSourceNow() Mockito.verify(symbolLayer, Mockito.times(1)).setProperties( @@ -845,7 +845,7 @@ class SymbolManagerTest { ) for (i in 0 until 2) { val symbol = - KSymbol( + Symbol( LatLng(), SdfIcon( Mockito.mock(Bitmap::class.java), @@ -878,7 +878,7 @@ class SymbolManagerTest { ) ) for (i in 0 until 2) { - val symbol = KSymbol( + val symbol = Symbol( LatLng(), SdfIcon( Mockito.mock(Bitmap::class.java), @@ -914,7 +914,7 @@ class SymbolManagerTest { ) ) for (i in 0 until 2) { - val symbol = KSymbol( + val symbol = Symbol( LatLng(), SdfIcon( Mockito.mock(Bitmap::class.java), @@ -948,7 +948,7 @@ class SymbolManagerTest { ArgumentMatchers.argThat(PropertyValueMatcher(iconHaloBlur(get("icon-halo-blur")))) ) for (i in 0 until 2) { - val symbol = KSymbol( + val symbol = Symbol( LatLng(), SdfIcon( Mockito.mock(Bitmap::class.java), @@ -980,7 +980,7 @@ class SymbolManagerTest { ArgumentMatchers.argThat(PropertyValueMatcher(textOpacity(get("text-opacity")))) ) for (i in 0 until 2) { - val symbol = KSymbol(LatLng(), text = Text("text", opacity = 0.5f)) + val symbol = Symbol(LatLng(), text = Text("text", opacity = 0.5f)) symbolManager.add(symbol) symbolManager.updateSourceNow() Mockito.verify(symbolLayer, Mockito.times(1)).setProperties( @@ -1005,7 +1005,7 @@ class SymbolManagerTest { ArgumentMatchers.argThat(PropertyValueMatcher(textColor(get("text-color")))) ) for (i in 0 until 2) { - val symbol = KSymbol(LatLng(), text = Text("text", color = Color.CYAN)) + val symbol = Symbol(LatLng(), text = Text("text", color = Color.CYAN)) symbolManager.add(symbol) symbolManager.updateSourceNow() Mockito.verify(symbolLayer, Mockito.times(1)).setProperties( @@ -1032,7 +1032,7 @@ class SymbolManagerTest { ) ) for (i in 0 until 2) { - val symbol = KSymbol(LatLng(), text = Text("text", halo = Halo(2f, Color.GREEN))) + val symbol = Symbol(LatLng(), text = Text("text", halo = Halo(2f, Color.GREEN))) symbolManager.add(symbol) symbolManager.updateSourceNow() Mockito.verify(symbolLayer, Mockito.times(1)).setProperties( @@ -1061,7 +1061,7 @@ class SymbolManagerTest { ) ) for (i in 0 until 2) { - val symbol = KSymbol(LatLng(), text = Text("text", halo = Halo(2f, Color.GREEN))) + val symbol = Symbol(LatLng(), text = Text("text", halo = Halo(2f, Color.GREEN))) symbolManager.add(symbol) symbolManager.updateSourceNow() Mockito.verify(symbolLayer, Mockito.times(1)).setProperties( @@ -1088,7 +1088,7 @@ class SymbolManagerTest { ArgumentMatchers.argThat(PropertyValueMatcher(textHaloBlur(get("text-halo-blur")))) ) for (i in 0 until 2) { - val symbol = KSymbol(LatLng(), text = Text("text", halo = Halo(2f, Color.GREEN, blur = 0.5f))) + val symbol = Symbol(LatLng(), text = Text("text", halo = Halo(2f, Color.GREEN, blur = 0.5f))) symbolManager.add(symbol) symbolManager.updateSourceNow() Mockito.verify(symbolLayer, Mockito.times(1)).setProperties( @@ -1121,7 +1121,7 @@ class SymbolManagerTest { @Test fun testClickListener() { val listener = object : OnSymbolClickListener { - override fun onAnnotationClick(t: KSymbol) = false + override fun onAnnotationClick(t: Symbol) = false } symbolManager = SymbolManager( mapView, @@ -1143,7 +1143,7 @@ class SymbolManagerTest { @Test fun testLongClickListener() { val listener = object : OnSymbolLongClickListener { - override fun onAnnotationLongClick(t: KSymbol) = false + override fun onAnnotationLongClick(t: Symbol) = false } symbolManager = SymbolManager( mapView, @@ -1165,9 +1165,9 @@ class SymbolManagerTest { @Test fun testDragListener() { val listener = object : OnSymbolDragListener { - override fun onAnnotationDragStarted(annotation: KSymbol) = Unit - override fun onAnnotationDrag(annotation: KSymbol) = Unit - override fun onAnnotationDragFinished(annotation: KSymbol) = Unit + override fun onAnnotationDragStarted(annotation: Symbol) = Unit + override fun onAnnotationDrag(annotation: Symbol) = Unit + override fun onAnnotationDragFinished(annotation: Symbol) = Unit } symbolManager = SymbolManager( mapView, @@ -1198,7 +1198,7 @@ class SymbolManagerTest { null, draggableAnnotationController ) - val symbol = KSymbol(LatLng()).apply { + val symbol = Symbol(LatLng()).apply { data = JsonPrimitive("hello") } symbolManager.add(symbol) @@ -1217,7 +1217,7 @@ class SymbolManagerTest { null, draggableAnnotationController ) - val symbol = KSymbol(LatLng()) + val symbol = Symbol(LatLng()) symbolManager.add(symbol) Assert.assertEquals(1, symbolManager.annotations.size) symbolManager.deleteAll() diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/activity/annotation/BulkMarkerActivity.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/activity/annotation/BulkMarkerActivity.kt index 52a8fb79b64..1a8bb36ea87 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/activity/annotation/BulkMarkerActivity.kt +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/activity/annotation/BulkMarkerActivity.kt @@ -13,9 +13,7 @@ import android.widget.Spinner import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.content.res.AppCompatResources import androidx.core.view.MenuItemCompat -import androidx.core.view.postDelayed -import org.maplibre.android.annotations.KSymbol -import org.maplibre.android.annotations.MarkerOptions +import org.maplibre.android.annotations.Symbol import org.maplibre.android.annotations.data.Icon import org.maplibre.android.annotations.data.Text import org.maplibre.android.geometry.LatLng @@ -28,7 +26,6 @@ import org.maplibre.android.testapp.utils.GeoParseUtil import timber.log.Timber import java.io.IOException import java.lang.ref.WeakReference -import java.text.DecimalFormat import java.util.* /** @@ -103,7 +100,7 @@ class BulkMarkerActivity : AppCompatActivity(), OnItemSelectedListener { randomIndex = random.nextInt(locations!!.size) val latLng = locations!![randomIndex] maplibreMap.addSymbol( - KSymbol( + Symbol( position = latLng, text = Text(i.toString(), color = Color.WHITE), icon = icon From aff81742b4b75182ce7e93cd44a7ff37b65c27ea Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Tue, 3 Oct 2023 11:09:50 +0200 Subject: [PATCH 03/10] Implement new Line annotation class --- .../annotations/AnnotationContainer.kt | 6 + .../android/annotations/AnnotationManager.kt | 21 +- .../maplibre/android/annotations/Circle.kt | 3 +- .../android/annotations/KAnnotation.kt | 2 +- .../maplibre/android/annotations/Line.java | 382 ---- .../org/maplibre/android/annotations/Line.kt | 142 ++ .../android/annotations/LineManager.kt | 489 +++--- .../android/annotations/LineOptions.java | 413 ----- .../annotations/OnAnnotationClickListener.kt | 8 +- .../annotations/OnAnnotationDragListener.kt | 7 +- .../OnAnnotationLongClickListener.kt | 7 +- .../annotations/OnLineClickListener.kt | 6 - .../android/annotations/OnLineDragListener.kt | 6 - .../annotations/OnLineLongClickListener.kt | 6 - .../maplibre/android/annotations/Symbol.kt | 1 + .../android/annotations/data/Defaults.kt | 12 + .../android/annotations/LineManagerTest.kt | 1552 ++++++++--------- 17 files changed, 1138 insertions(+), 1925 deletions(-) delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Line.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Line.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/LineOptions.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnLineClickListener.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnLineDragListener.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnLineLongClickListener.kt diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/AnnotationContainer.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/AnnotationContainer.kt index 5dae5108677..774512a3ca3 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/AnnotationContainer.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/AnnotationContainer.kt @@ -29,6 +29,7 @@ class KAnnotationContainer( annotationList.add(annotation) addToManager(annotation) if (annotation is Symbol) annotation.icon?.let { style?.addImage(it.image.toString(), it.image) } + if (annotation is Line) annotation.pattern?.let { style?.addImage(it.toString(), it) } } @UiThread @@ -42,9 +43,11 @@ class KAnnotationContainer( } } + @UiThread fun update(annotation: KAnnotation<*>) { managers[annotation.key()]?.updateSource() if (annotation is Symbol) annotation.icon?.let { style?.addImage(it.image.toString(), it.image) } + if (annotation is Line) annotation.pattern?.let { style?.addImage(it.toString(), it) } } private fun addToManager(annotation: KAnnotation<*>) = @@ -52,6 +55,7 @@ class KAnnotationContainer( when (annotation) { is Symbol -> (manager as SymbolManager).add(annotation) is Circle -> (manager as CircleManager).add(annotation) + is Line -> (manager as LineManager).add(annotation) } } @@ -64,6 +68,7 @@ class KAnnotationContainer( when (annotation) { is Symbol -> (manager as SymbolManager).delete(annotation) is Circle -> (manager as CircleManager).delete(annotation) + is Line -> (manager as LineManager).delete(annotation) } } @@ -94,6 +99,7 @@ class KAnnotationContainer( when (key.type) { Symbol::class -> SymbolManager(mapView, mapLibreMap, it) Circle::class -> CircleManager(mapView, mapLibreMap, it) + Line::class -> LineManager(mapView, mapLibreMap, it) else -> throw IllegalArgumentException( "Impossible key! This should never occur because KAnnotation is a sealed class." ) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/AnnotationManager.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/AnnotationManager.kt index c47d22d39cc..a6fd84f0848 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/AnnotationManager.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/AnnotationManager.kt @@ -7,7 +7,6 @@ import com.mapbox.geojson.Feature import com.mapbox.geojson.FeatureCollection import java.util.concurrent.atomic.AtomicBoolean import org.maplibre.android.geometry.LatLng -import org.maplibre.android.log.Logger import org.maplibre.android.maps.MapLibreMap import org.maplibre.android.maps.MapLibreMap.OnMapClickListener import org.maplibre.android.maps.MapLibreMap.OnMapLongClickListener @@ -97,15 +96,19 @@ abstract class AnnotationManager> @UiThread intern */ @UiThread fun add(annotation: T) { - if (annotation.id == 0L) { - // At this point, the annotation has no ID iff the user has added it directly to the manager. - annotation.attach(this, nextId--) - } - annotations[annotation.id]?.let { - throw IllegalStateException("An ID was generated twice.") + if (annotations[annotation.id] === annotation) { + Timber.w("The same annotation was added twice. Ignoring.") + } else { + if (annotation.id == 0L) { + // At this point, the annotation has no ID iff the user has added it directly to the manager. + annotation.attach(this, nextId--) + } + annotations[annotation.id]?.let { + throw IllegalStateException("An ID was generated twice.") + } + annotations[annotation.id] = annotation + updateSource() } - annotations[annotation.id] = annotation - updateSource() } /** diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Circle.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Circle.kt index 98acde1c658..d89c6ba8ce4 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Circle.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Circle.kt @@ -22,6 +22,7 @@ class Circle( var center: LatLng = center set(value) { field = value + geometry = Point.fromLngLat(value.longitude, value.latitude) updateThis() } var radius: Float = radius @@ -65,7 +66,7 @@ class Circle( init { if (blur != null && blur <= 0) { throw IllegalArgumentException( - "A blur of $blur has been provided for a Halo object. This means that no blur is to be used. " + + "A blur of $blur has been provided for a Circle object. This means that no blur is to be used. " + "Please use `null` to indicate that `blur` is not used." ) } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/KAnnotation.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/KAnnotation.kt index 2ede3bb4b5f..be1712a963e 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/KAnnotation.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/KAnnotation.kt @@ -43,7 +43,7 @@ sealed class KAnnotation( private set internal abstract var geometry: T - internal abstract val dataDrivenProperties: List> + internal abstract val dataDrivenProperties: List internal open fun updateThis() { attachedToMap?.updateAnnotation(this) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Line.java b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Line.java deleted file mode 100644 index f290882df30..00000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Line.java +++ /dev/null @@ -1,382 +0,0 @@ -//package org.maplibre.android.annotations; -// -//import static org.maplibre.android.constants.GeometryConstants.MAX_MERCATOR_LATITUDE; -//import static org.maplibre.android.constants.GeometryConstants.MIN_MERCATOR_LATITUDE; -// -//import android.graphics.PointF; -// -//import androidx.annotation.ColorInt; -//import androidx.annotation.NonNull; -//import androidx.annotation.Nullable; -//import androidx.annotation.UiThread; -// -//import com.google.gson.JsonNull; -//import com.google.gson.JsonObject; -//import com.mapbox.android.gestures.MoveDistancesObject; -//import com.mapbox.geojson.Geometry; -//import com.mapbox.geojson.LineString; -//import com.mapbox.geojson.Point; -// -//import org.maplibre.android.geometry.LatLng; -//import org.maplibre.android.maps.Projection; -//import org.maplibre.android.style.layers.Property; -//import org.maplibre.android.utils.ColorUtils; -// -//import java.util.ArrayList; -//import java.util.List; -// -//@UiThread -//public class Line extends AbstractAnnotation { -// -// private final AnnotationManager annotationManager; -// -// /** -// * Create a line. -// * -// * @param id the id of the line -// * @param jsonObject the features of the annotation -// * @param geometry the geometry of the annotation -// */ -// Line(long id, AnnotationManager annotationManager, JsonObject jsonObject, LineString geometry) -// { -// super(id, jsonObject, geometry); -// this.annotationManager = annotationManager; -// } -// -// @Override -// void setUsedDataDrivenProperties() { -// if (!(jsonObject.get(LineOptions.PROPERTY_LINE_JOIN) instanceof JsonNull)) { -// annotationManager.enableDataDrivenProperty(LineOptions.PROPERTY_LINE_JOIN); -// } -// if (!(jsonObject.get(LineOptions.PROPERTY_LINE_OPACITY) instanceof JsonNull)) { -// annotationManager.enableDataDrivenProperty(LineOptions.PROPERTY_LINE_OPACITY); -// } -// if (!(jsonObject.get(LineOptions.PROPERTY_LINE_COLOR) instanceof JsonNull)) { -// annotationManager.enableDataDrivenProperty(LineOptions.PROPERTY_LINE_COLOR); -// } -// if (!(jsonObject.get(LineOptions.PROPERTY_LINE_WIDTH) instanceof JsonNull)) { -// annotationManager.enableDataDrivenProperty(LineOptions.PROPERTY_LINE_WIDTH); -// } -// if (!(jsonObject.get(LineOptions.PROPERTY_LINE_GAP_WIDTH) instanceof JsonNull)) { -// annotationManager.enableDataDrivenProperty(LineOptions.PROPERTY_LINE_GAP_WIDTH); -// } -// if (!(jsonObject.get(LineOptions.PROPERTY_LINE_OFFSET) instanceof JsonNull)) { -// annotationManager.enableDataDrivenProperty(LineOptions.PROPERTY_LINE_OFFSET); -// } -// if (!(jsonObject.get(LineOptions.PROPERTY_LINE_BLUR) instanceof JsonNull)) { -// annotationManager.enableDataDrivenProperty(LineOptions.PROPERTY_LINE_BLUR); -// } -// if (!(jsonObject.get(LineOptions.PROPERTY_LINE_PATTERN) instanceof JsonNull)) { -// annotationManager.enableDataDrivenProperty(LineOptions.PROPERTY_LINE_PATTERN); -// } -// } -// -// /** -// * Set a list of LatLng for the line, which represents the locations of the line on the map -// *

-// * To update the line on the map use {@link LineManager#update(AbstractAnnotation)}. -// *

-// * -// * @param latLngs a list of the locations of the line in a longitude and latitude pairs -// */ -// public void setLatLngs(List latLngs) { -// List points = new ArrayList<>(); -// for (LatLng latLng : latLngs) { -// points.add(Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude())); -// } -// geometry = LineString.fromLngLats(points); -// } -// -// /** -// * Get a list of LatLng for the line, which represents the locations of the line on the map -// * -// * @return a list of the locations of the line in a latitude and longitude pairs -// */ -// @NonNull -// public List getLatLngs() { -// LineString lineString = (LineString) geometry; -// List latLngs = new ArrayList<>(); -// for (Point point : lineString.coordinates()) { -// latLngs.add(new LatLng(point.latitude(), point.longitude())); -// } -// return latLngs; -// } -// -// // Property accessors -// -// /** -// * Get the LineJoin property -// *

-// * The display of lines when joining. -// *

-// * -// * @return property wrapper value around String -// */ -// public String getLineJoin() { -// return jsonObject.get(LineOptions.PROPERTY_LINE_JOIN).getAsString(); -// } -// -// /** -// * Set the LineJoin property -// *

-// * The display of lines when joining. -// *

-// *

-// * To update the line on the map use {@link LineManager#update(AbstractAnnotation)}. -// *

-// * -// * @param value constant property value for String -// */ -// public void setLineJoin(@Property.LINE_JOIN String value) { -// jsonObject.addProperty(LineOptions.PROPERTY_LINE_JOIN, value); -// } -// -// /** -// * Get the LineOpacity property -// *

-// * The opacity at which the line will be drawn. -// *

-// * -// * @return property wrapper value around Float -// */ -// public Float getLineOpacity() { -// return jsonObject.get(LineOptions.PROPERTY_LINE_OPACITY).getAsFloat(); -// } -// -// /** -// * Set the LineOpacity property -// *

-// * The opacity at which the line will be drawn. -// *

-// *

-// * To update the line on the map use {@link LineManager#update(AbstractAnnotation)}. -// *

-// * -// * @param value constant property value for Float -// */ -// public void setLineOpacity(Float value) { -// jsonObject.addProperty(LineOptions.PROPERTY_LINE_OPACITY, value); -// } -// -// /** -// * Get the LineColor property -// *

-// * The color with which the line will be drawn. -// *

-// * -// * @return color value for String -// */ -// @ColorInt -// public int getLineColorAsInt() { -// return ColorUtils.rgbaToColor(jsonObject.get(LineOptions.PROPERTY_LINE_COLOR).getAsString()); -// } -// -// /** -// * Get the LineColor property -// *

-// * The color with which the line will be drawn. -// *

-// * -// * @return color value for String -// */ -// public String getLineColor() { -// return jsonObject.get(LineOptions.PROPERTY_LINE_COLOR).getAsString(); -// } -// -// /** -// * Set the LineColor property -// *

-// * The color with which the line will be drawn. -// *

-// *

-// * To update the line on the map use {@link LineManager#update(AbstractAnnotation)}. -// *

-// * -// * @param color value for String -// */ -// public void setLineColor(@ColorInt int color) { -// jsonObject.addProperty(LineOptions.PROPERTY_LINE_COLOR, ColorUtils.colorToRgbaString(color)); -// } -// -// /** -// * Set the LineColor property -// *

-// * The color with which the line will be drawn. -// *

-// *

-// * To update the line on the map use {@link LineManager#update(AbstractAnnotation)}. -// *

-// * -// * @param color value for String -// */ -// public void setLineColor(@NonNull String color) { -// jsonObject.addProperty("line-color", color); -// } -// -// /** -// * Get the LineWidth property -// *

-// * Stroke thickness. -// *

-// * -// * @return property wrapper value around Float -// */ -// public Float getLineWidth() { -// return jsonObject.get(LineOptions.PROPERTY_LINE_WIDTH).getAsFloat(); -// } -// -// /** -// * Set the LineWidth property -// *

-// * Stroke thickness. -// *

-// *

-// * To update the line on the map use {@link LineManager#update(AbstractAnnotation)}. -// *

-// * -// * @param value constant property value for Float -// */ -// public void setLineWidth(Float value) { -// jsonObject.addProperty(LineOptions.PROPERTY_LINE_WIDTH, value); -// } -// -// /** -// * Get the LineGapWidth property -// *

-// * Draws a line casing outside of a line's actual path. Value indicates the width of the inner gap. -// *

-// * -// * @return property wrapper value around Float -// */ -// public Float getLineGapWidth() { -// return jsonObject.get(LineOptions.PROPERTY_LINE_GAP_WIDTH).getAsFloat(); -// } -// -// /** -// * Set the LineGapWidth property -// *

-// * Draws a line casing outside of a line's actual path. Value indicates the width of the inner gap. -// *

-// *

-// * To update the line on the map use {@link LineManager#update(AbstractAnnotation)}. -// *

-// * -// * @param value constant property value for Float -// */ -// public void setLineGapWidth(Float value) { -// jsonObject.addProperty(LineOptions.PROPERTY_LINE_GAP_WIDTH, value); -// } -// -// /** -// * Get the LineOffset property -// *

-// * The line's offset. For linear features, a positive value offsets the line to the right, relative to the direction -// * of the line, and a negative value to the left. For polygon features, a positive value results in an inset, and a -// * negative value results in an outset. -// *

-// * -// * @return property wrapper value around Float -// */ -// public Float getLineOffset() { -// return jsonObject.get(LineOptions.PROPERTY_LINE_OFFSET).getAsFloat(); -// } -// -// /** -// * Set the LineOffset property -// *

-// * The line's offset. For linear features, a positive value offsets the line to the right, relative to the direction -// * of the line, and a negative value to the left. For polygon features, a positive value results in an inset, and a -// * negative value results in an outset. -// *

-// *

-// * To update the line on the map use {@link LineManager#update(AbstractAnnotation)}. -// *

-// * -// * @param value constant property value for Float -// */ -// public void setLineOffset(Float value) { -// jsonObject.addProperty(LineOptions.PROPERTY_LINE_OFFSET, value); -// } -// -// /** -// * Get the LineBlur property -// *

-// * Blur applied to the line, in density-independent pixels. -// *

-// * -// * @return property wrapper value around Float -// */ -// public Float getLineBlur() { -// return jsonObject.get(LineOptions.PROPERTY_LINE_BLUR).getAsFloat(); -// } -// -// /** -// * Set the LineBlur property -// *

-// * Blur applied to the line, in density-independent pixels. -// *

-// *

-// * To update the line on the map use {@link LineManager#update(AbstractAnnotation)}. -// *

-// * -// * @param value constant property value for Float -// */ -// public void setLineBlur(Float value) { -// jsonObject.addProperty(LineOptions.PROPERTY_LINE_BLUR, value); -// } -// -// /** -// * Get the LinePattern property -// *

-// * Name of image in sprite to use for drawing image lines. For seamless patterns, image width must be a factor of -// * two (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be evaluated only at integer zoom levels. -// *

-// * -// * @return property wrapper value around String -// */ -// public String getLinePattern() { -// return jsonObject.get(LineOptions.PROPERTY_LINE_PATTERN).getAsString(); -// } -// -// /** -// * Set the LinePattern property -// *

-// * Name of image in sprite to use for drawing image lines. For seamless patterns, image width must be a factor of -// * two (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be evaluated only at integer zoom levels. -// *

-// *

-// * To update the line on the map use {@link LineManager#update(AbstractAnnotation)}. -// *

-// * -// * @param value constant property value for String -// */ -// public void setLinePattern(String value) { -// jsonObject.addProperty(LineOptions.PROPERTY_LINE_PATTERN, value); -// } -// -// @Override -// @Nullable -// Geometry getOffsetGeometry(@NonNull Projection projection, @NonNull MoveDistancesObject moveDistancesObject, -// float touchAreaShiftX, float touchAreaShiftY) { -// List originalPoints = geometry.coordinates(); -// List resultingPoints = new ArrayList<>(originalPoints.size()); -// for (Point jsonPoint : originalPoints) { -// PointF pointF = projection.toScreenLocation(new LatLng(jsonPoint.latitude(), jsonPoint.longitude())); -// pointF.x -= moveDistancesObject.getDistanceXSinceLast(); -// pointF.y -= moveDistancesObject.getDistanceYSinceLast(); -// -// LatLng latLng = projection.fromScreenLocation(pointF); -// if (latLng.getLatitude() > MAX_MERCATOR_LATITUDE || latLng.getLatitude() < MIN_MERCATOR_LATITUDE) { -// return null; -// } -// resultingPoints.add(Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude())); -// } -// -// return LineString.fromLngLats(resultingPoints); -// } -// -// @Override -// String getName() { -// return "Line"; -// } -//} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Line.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Line.kt new file mode 100644 index 00000000000..6f22c83e533 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Line.kt @@ -0,0 +1,142 @@ +package org.maplibre.android.annotations + +import android.graphics.Bitmap +import androidx.annotation.ColorInt +import com.mapbox.android.gestures.MoveDistancesObject +import com.mapbox.geojson.Geometry +import com.mapbox.geojson.LineString +import com.mapbox.geojson.Point +import org.maplibre.android.annotations.data.Defaults +import org.maplibre.android.constants.GeometryConstants.MAX_MERCATOR_LATITUDE +import org.maplibre.android.constants.GeometryConstants.MIN_MERCATOR_LATITUDE +import org.maplibre.android.geometry.LatLng +import org.maplibre.android.maps.Projection + +class Line( + path: List, + join: Join = Defaults.LINE_JOIN, + opacity: Float = Defaults.LINE_OPACITY, + @ColorInt color: Int = Defaults.LINE_COLOR, + width: Float = Defaults.LINE_WIDTH, + gap: Float? = Defaults.LINE_GAP, + offset: Float = Defaults.LINE_OFFSET, + blur: Float? = Defaults.LINE_BLUR, + pattern: Bitmap? = Defaults.LINE_PATTERN, + // TODO: NDD properties cap, translate, dashArray +) : KAnnotation() { + + var path: List = path + set(value) { + field = value + geometry = LineString.fromLngLats( + path.map { Point.fromLngLat(it.longitude, it.latitude) } + ) + updateThis() + } + var join: Join = join + set(value) { + field = value + updateThis() + } + var opacity: Float = opacity + set(value) { + field = value + updateThis() + } + @ColorInt var color: Int = color + set(value) { + field = value + updateThis() + } + var width: Float = width + set(value) { + field = value + updateThis() + } + var gap: Float? = gap + set(value) { + field = value + updateThis() + } + var offset: Float = offset + set(value) { + field = value + updateThis() + } + var blur: Float? = blur + set(value) { + field = value + updateThis() + } + var pattern: Bitmap? = pattern + set(value) { + field = value + updateThis() + } + + override var geometry: LineString = LineString.fromLngLats( + path.map { Point.fromLngLat(it.longitude, it.latitude) } + ) + + override val dataDrivenProperties: List + get() = listOf( + PROPERTY_LINE_JOIN to join.toString().lowercase() default Defaults.LINE_JOIN.toString().lowercase(), + PROPERTY_LINE_OPACITY to opacity default Defaults.LINE_OPACITY, + PROPERTY_LINE_COLOR to color.asColorString() default Defaults.LINE_COLOR, + PROPERTY_LINE_WIDTH to width default Defaults.LINE_WIDTH, + PROPERTY_LINE_GAP_WIDTH to gap default Defaults.LINE_GAP, + PROPERTY_LINE_OFFSET to offset default Defaults.LINE_OFFSET, + PROPERTY_LINE_BLUR to blur default Defaults.LINE_BLUR, + PROPERTY_LINE_PATTERN to pattern default Defaults.LINE_PATTERN + ) + + override fun getOffsetGeometry( + projection: Projection, moveDistancesObject: MoveDistancesObject, touchAreaShiftX: Float, touchAreaShiftY: Float + ): Geometry? = + geometry.coordinates().map { + val pointF = projection.toScreenLocation(LatLng(it.latitude(), it.longitude())).apply { + x -= moveDistancesObject.distanceXSinceLast + y -= moveDistancesObject.distanceYSinceLast + } + + val latLng = projection.fromScreenLocation(pointF) + if (latLng.latitude > MAX_MERCATOR_LATITUDE || latLng.latitude < MIN_MERCATOR_LATITUDE) { + return null + } + Point.fromLngLat(latLng.longitude, latLng.latitude) + }.let { LineString.fromLngLats(it) } + + init { + if (gap != null && gap <= 0) { + throw IllegalArgumentException( + "A gap of $gap has been provided for a Line object. This means that no line gap is to be used. " + + "Please use `null` to indicate that `gap` is not used." + ) + } + if (blur != null && blur <= 0) { + throw IllegalArgumentException( + "A blur of $blur has been provided for a Line object. This means that no blur is to be used. " + + "Please use `null` to indicate that `blur` is not used." + ) + } + if (opacity > 1f || opacity < 0f) { + throw IllegalArgumentException("Opacity must be between 0 and 1 (inclusive)") + } + + } + + enum class Join { + BEVEL, ROUND, MITER + } + + companion object { + const val PROPERTY_LINE_JOIN = "line-join" + const val PROPERTY_LINE_OPACITY = "line-opacity" + const val PROPERTY_LINE_COLOR = "line-color" + const val PROPERTY_LINE_WIDTH = "line-width" + const val PROPERTY_LINE_GAP_WIDTH = "line-gap-width" + const val PROPERTY_LINE_OFFSET = "line-offset" + const val PROPERTY_LINE_BLUR = "line-blur" + const val PROPERTY_LINE_PATTERN = "line-pattern" + } +} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/LineManager.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/LineManager.kt index 2cfe8410386..31dbbfb5ae9 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/LineManager.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/LineManager.kt @@ -1,278 +1,211 @@ -//package org.maplibre.android.annotations -// -//import androidx.annotation.UiThread -//import com.mapbox.geojson.FeatureCollection -//import org.maplibre.android.maps.MapLibreMap -//import org.maplibre.android.maps.MapView -//import org.maplibre.android.maps.Style -//import org.maplibre.android.style.expressions.Expression -//import org.maplibre.android.style.layers.LineLayer -//import org.maplibre.android.style.layers.PropertyFactory -//import org.maplibre.android.style.layers.PropertyValue -//import org.maplibre.android.style.sources.GeoJsonOptions -// -///** -// * The line manager allows to add lines to a map. -// * -// * @param maplibreMap the map object to add lines to -// * @param style a valid a fully loaded style object -// * @param belowLayerId the id of the layer above the line layer -// * @param aboveLayerId the id of the layer below the line layer -// * @param geoJsonOptions options for the internal source -// */ -//class LineManager @UiThread internal constructor( -// mapView: MapView, -// maplibreMap: MapLibreMap, -// style: Style, -// coreElementProvider: CoreElementProvider = LineElementProvider(), -// belowLayerId: String? = null, -// aboveLayerId: String? = null, -// geoJsonOptions: GeoJsonOptions? = null, -// draggableAnnotationController: DraggableAnnotationController = -// DraggableAnnotationController.getInstance(mapView, maplibreMap) -//) : AnnotationManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// draggableAnnotationController, -// belowLayerId, -// aboveLayerId, -// geoJsonOptions -//) { -// -// constructor( -// mapView: MapView, -// maplibreMap: MapLibreMap, -// style: Style, -// belowLayerId: String? = null, -// aboveLayerId: String? = null, -// geoJsonOptions: GeoJsonOptions? = null -// ) : this( -// mapView, -// maplibreMap, -// style, -// LineElementProvider(), -// belowLayerId, -// aboveLayerId, -// geoJsonOptions -// ) -// -// override fun initializeDataDrivenPropertyMap() = -// listOf( -// LineOptions.PROPERTY_LINE_JOIN, -// LineOptions.PROPERTY_LINE_OPACITY, -// LineOptions.PROPERTY_LINE_COLOR, -// LineOptions.PROPERTY_LINE_WIDTH, -// LineOptions.PROPERTY_LINE_GAP_WIDTH, -// LineOptions.PROPERTY_LINE_OFFSET, -// LineOptions.PROPERTY_LINE_BLUR, -// LineOptions.PROPERTY_LINE_PATTERN -// ).associateWith { false }.let { dataDrivenPropertyUsageMap.putAll(it) } -// -// override fun setDataDrivenPropertyIsUsed(property: String) { -// when (property) { -// LineOptions.PROPERTY_LINE_JOIN -> layer.setProperties( -// PropertyFactory.lineJoin( -// Expression.get(LineOptions.PROPERTY_LINE_JOIN) -// ) -// ) -// -// LineOptions.PROPERTY_LINE_OPACITY -> layer.setProperties( -// PropertyFactory.lineOpacity( -// Expression.get(LineOptions.PROPERTY_LINE_OPACITY) -// ) -// ) -// -// LineOptions.PROPERTY_LINE_COLOR -> layer.setProperties( -// PropertyFactory.lineColor( -// Expression.get(LineOptions.PROPERTY_LINE_COLOR) -// ) -// ) -// -// LineOptions.PROPERTY_LINE_WIDTH -> layer.setProperties( -// PropertyFactory.lineWidth( -// Expression.get(LineOptions.PROPERTY_LINE_WIDTH) -// ) -// ) -// -// LineOptions.PROPERTY_LINE_GAP_WIDTH -> layer.setProperties( -// PropertyFactory.lineGapWidth( -// Expression.get(LineOptions.PROPERTY_LINE_GAP_WIDTH) -// ) -// ) -// -// LineOptions.PROPERTY_LINE_OFFSET -> layer.setProperties( -// PropertyFactory.lineOffset( -// Expression.get(LineOptions.PROPERTY_LINE_OFFSET) -// ) -// ) -// -// LineOptions.PROPERTY_LINE_BLUR -> layer.setProperties( -// PropertyFactory.lineBlur( -// Expression.get(LineOptions.PROPERTY_LINE_BLUR) -// ) -// ) -// -// LineOptions.PROPERTY_LINE_PATTERN -> layer.setProperties( -// PropertyFactory.linePattern( -// Expression.get(LineOptions.PROPERTY_LINE_PATTERN) -// ) -// ) -// } -// } -// -// /** -// * Create a list of lines on the map. -// * -// * -// * Lines are going to be created only for features with a matching geometry. -// * -// * -// * All supported properties are:

-// * LineOptions.PROPERTY_LINE_JOIN - String

-// * LineOptions.PROPERTY_LINE_OPACITY - Float

-// * LineOptions.PROPERTY_LINE_COLOR - String

-// * LineOptions.PROPERTY_LINE_WIDTH - Float

-// * LineOptions.PROPERTY_LINE_GAP_WIDTH - Float

-// * LineOptions.PROPERTY_LINE_OFFSET - Float

-// * LineOptions.PROPERTY_LINE_BLUR - Float

-// * LineOptions.PROPERTY_LINE_PATTERN - String

-// * Learn more about above properties in the [Style specification](https://maplibre.org/maplibre-style-spec/). -// * -// * -// * Out of spec properties:

-// * "is-draggable" - Boolean, true if the line should be draggable, false otherwise -// * -// * @param json the GeoJSON defining the list of lines to build -// * @return the list of built lines -// */ -// @UiThread -// fun create(json: String): List = create(FeatureCollection.fromJson(json)) -// -// /** -// * Create a list of lines on the map. -// * -// * -// * Lines are going to be created only for features with a matching geometry. -// * -// * -// * All supported properties are:

-// * LineOptions.PROPERTY_LINE_JOIN - String

-// * LineOptions.PROPERTY_LINE_OPACITY - Float

-// * LineOptions.PROPERTY_LINE_COLOR - String

-// * LineOptions.PROPERTY_LINE_WIDTH - Float

-// * LineOptions.PROPERTY_LINE_GAP_WIDTH - Float

-// * LineOptions.PROPERTY_LINE_OFFSET - Float

-// * LineOptions.PROPERTY_LINE_BLUR - Float

-// * LineOptions.PROPERTY_LINE_PATTERN - String

-// * Learn more about above properties in the [Style specification](https://maplibre.org/maplibre-style-spec/). -// * -// * -// * Out of spec properties:

-// * "is-draggable" - Boolean, true if the line should be draggable, false otherwise -// * -// * @param featureCollection the featureCollection defining the list of lines to build -// * @return the list of built lines -// */ -// @UiThread -// fun create(featureCollection: FeatureCollection): List = -// featureCollection.features()?.mapNotNull { LineOptions.fromFeature(it) } -// .let { create(it ?: emptyList()) } -// -// /** -// * Key of the id of the annotation -// */ -// override val annotationIdKey: String -// get() = Line.ID_KEY -// -// // Property accessors -// /** -// * The display of line endings. -// */ -// var lineCap: String? -// get() = layer.lineCap.value -// set(value) { -// val propertyValue: PropertyValue<*> = PropertyFactory.lineCap(value) -// constantPropertyUsageMap[PROPERTY_LINE_CAP] = propertyValue -// layer.setProperties(propertyValue) -// } -// -// /** -// * Used to automatically convert miter joins to bevel joins for sharp angles. -// */ -// var lineMiterLimit: Float? -// get() = layer.lineMiterLimit.value -// set(value) { -// val propertyValue: PropertyValue<*> = PropertyFactory.lineMiterLimit(value) -// constantPropertyUsageMap[PROPERTY_LINE_MITER_LIMIT] = propertyValue -// layer.setProperties(propertyValue) -// } -// -// /** -// * Used to automatically convert round joins to miter joins for shallow angles. -// */ -// var lineRoundLimit: Float? -// get() = layer.lineRoundLimit.value -// set(value) { -// val propertyValue: PropertyValue<*> = PropertyFactory.lineRoundLimit(value) -// constantPropertyUsageMap[PROPERTY_LINE_ROUND_LIMIT] = propertyValue -// layer.setProperties(propertyValue) -// } -// -// /** -// * The geometry's offset. Values are [x, y] where negatives indicate left and up, respectively. -// */ -// var lineTranslate: Array? -// get() = layer.lineTranslate.value -// set(value) { -// val propertyValue: PropertyValue<*> = PropertyFactory.lineTranslate(value) -// constantPropertyUsageMap[PROPERTY_LINE_TRANSLATE] = propertyValue -// layer.setProperties(propertyValue) -// } -// -// /** -// * Controls the frame of reference for [PropertyFactory.lineTranslate]. -// */ -// var lineTranslateAnchor: String? -// get() = layer.lineTranslateAnchor.value -// set(value) { -// val propertyValue: PropertyValue<*> = PropertyFactory.lineTranslateAnchor(value) -// constantPropertyUsageMap[PROPERTY_LINE_TRANSLATE_ANCHOR] = propertyValue -// layer.setProperties(propertyValue) -// } -// -// /** -// * Specifies the lengths of the alternating dashes and gaps that form the dash pattern. The lengths are later scaled by the line width. To convert a dash length to density-independent pixels, multiply the length by the current line width. Note that GeoJSON sources with `lineMetrics: true` specified won't render dashed lines to the expected scale. Also note that zoom-dependent expressions will be evaluated only at integer zoom levels. -// */ -// var lineDasharray: Array? -// get() = layer.lineDasharray.value -// set(value) { -// val propertyValue: PropertyValue<*> = PropertyFactory.lineDasharray(value) -// constantPropertyUsageMap[PROPERTY_LINE_DASHARRAY] = propertyValue -// layer.setProperties(propertyValue) -// } -// -// /** -// * Set filter on the managed lines. -// */ -// override fun setFilter(expression: Expression) { -// layerFilter = expression -// layer.setFilter(expression) -// } -// -// val filter: Expression? -// /** -// * Get filter of the managed lines. -// */ -// get() = layer.filter -// -// companion object { -// private const val PROPERTY_LINE_CAP: String = "line-cap" -// private const val PROPERTY_LINE_MITER_LIMIT: String = "line-miter-limit" -// private const val PROPERTY_LINE_ROUND_LIMIT: String = "line-round-limit" -// private const val PROPERTY_LINE_TRANSLATE: String = "line-translate" -// private const val PROPERTY_LINE_TRANSLATE_ANCHOR: String = "line-translate-anchor" -// private const val PROPERTY_LINE_DASHARRAY: String = "line-dasharray" -// } -//} +package org.maplibre.android.annotations + +import androidx.annotation.UiThread +import org.maplibre.android.maps.MapLibreMap +import org.maplibre.android.maps.MapView +import org.maplibre.android.maps.Style +import org.maplibre.android.style.expressions.Expression +import org.maplibre.android.style.layers.LineLayer +import org.maplibre.android.style.layers.PropertyFactory +import org.maplibre.android.style.layers.PropertyValue +import org.maplibre.android.style.sources.GeoJsonOptions + +/** + * The line manager allows to add lines to a map. + * + * @param maplibreMap the map object to add lines to + * @param style a valid a fully loaded style object + * @param belowLayerId the id of the layer above the line layer + * @param aboveLayerId the id of the layer below the line layer + * @param geoJsonOptions options for the internal source + */ +class LineManager @UiThread internal constructor( + mapView: MapView, + maplibreMap: MapLibreMap, + style: Style, + coreElementProvider: CoreElementProvider = LineElementProvider(), + belowLayerId: String? = null, + aboveLayerId: String? = null, + geoJsonOptions: GeoJsonOptions? = null, + draggableAnnotationController: DraggableAnnotationController = + DraggableAnnotationController.getInstance(mapView, maplibreMap) +) : AnnotationManager( + mapView, + maplibreMap, + style, + coreElementProvider, + draggableAnnotationController, + belowLayerId, + aboveLayerId, + geoJsonOptions +) { + + constructor( + mapView: MapView, + maplibreMap: MapLibreMap, + style: Style, + belowLayerId: String? = null, + aboveLayerId: String? = null, + geoJsonOptions: GeoJsonOptions? = null + ) : this( + mapView, + maplibreMap, + style, + LineElementProvider(), + belowLayerId, + aboveLayerId, + geoJsonOptions + ) + + override fun generateDataDrivenPropertyExpression(property: String): PropertyValue = when (property) { + Line.PROPERTY_LINE_JOIN -> PropertyFactory.lineJoin( + Expression.get(Line.PROPERTY_LINE_JOIN) + ) + + Line.PROPERTY_LINE_OPACITY -> PropertyFactory.lineOpacity( + Expression.get(Line.PROPERTY_LINE_OPACITY) + ) + + Line.PROPERTY_LINE_COLOR -> PropertyFactory.lineColor( + Expression.get(Line.PROPERTY_LINE_COLOR) + ) + + Line.PROPERTY_LINE_WIDTH -> PropertyFactory.lineWidth( + Expression.get(Line.PROPERTY_LINE_WIDTH) + ) + + Line.PROPERTY_LINE_GAP_WIDTH -> PropertyFactory.lineGapWidth( + Expression.get(Line.PROPERTY_LINE_GAP_WIDTH) + ) + + Line.PROPERTY_LINE_OFFSET -> PropertyFactory.lineOffset( + Expression.get(Line.PROPERTY_LINE_OFFSET) + ) + + Line.PROPERTY_LINE_BLUR -> PropertyFactory.lineBlur( + Expression.get(Line.PROPERTY_LINE_BLUR) + ) + + Line.PROPERTY_LINE_PATTERN -> PropertyFactory.linePattern( + Expression.get(Line.PROPERTY_LINE_PATTERN) + ) + + else -> throw IllegalArgumentException( + "$property is not a valid data-driven property for a line." + ) + } + + override fun addDragListener(d: OnLineDragListener) { + super.addDragListener(d) + } + + override fun removeDragListener(d: OnLineDragListener) { + super.removeDragListener(d) + } + + override fun addClickListener(u: OnLineClickListener) { + super.addClickListener(u) + } + + override fun removeClickListener(u: OnLineClickListener) { + super.removeClickListener(u) + } + + override fun addLongClickListener(v: OnLineLongClickListener) { + super.addLongClickListener(v) + } + + override fun removeLongClickListener(v: OnLineLongClickListener) { + super.removeLongClickListener(v) + } + + // Property accessors + /** + * The display of line endings. + */ + var lineCap: String? + get() = layer.lineCap.value + set(value) { + val propertyValue: PropertyValue<*> = PropertyFactory.lineCap(value) + constantPropertyUsageMap[PROPERTY_LINE_CAP] = propertyValue + layer.setProperties(propertyValue) + } + + /** + * Used to automatically convert miter joins to bevel joins for sharp angles. + */ + var lineMiterLimit: Float? + get() = layer.lineMiterLimit.value + set(value) { + val propertyValue: PropertyValue<*> = PropertyFactory.lineMiterLimit(value) + constantPropertyUsageMap[PROPERTY_LINE_MITER_LIMIT] = propertyValue + layer.setProperties(propertyValue) + } + + /** + * Used to automatically convert round joins to miter joins for shallow angles. + */ + var lineRoundLimit: Float? + get() = layer.lineRoundLimit.value + set(value) { + val propertyValue: PropertyValue<*> = PropertyFactory.lineRoundLimit(value) + constantPropertyUsageMap[PROPERTY_LINE_ROUND_LIMIT] = propertyValue + layer.setProperties(propertyValue) + } + + /** + * The geometry's offset. Values are [x, y] where negatives indicate left and up, respectively. + */ + var lineTranslate: Array? + get() = layer.lineTranslate.value + set(value) { + val propertyValue: PropertyValue<*> = PropertyFactory.lineTranslate(value) + constantPropertyUsageMap[PROPERTY_LINE_TRANSLATE] = propertyValue + layer.setProperties(propertyValue) + } + + /** + * Controls the frame of reference for [PropertyFactory.lineTranslate]. + */ + var lineTranslateAnchor: String? + get() = layer.lineTranslateAnchor.value + set(value) { + val propertyValue: PropertyValue<*> = PropertyFactory.lineTranslateAnchor(value) + constantPropertyUsageMap[PROPERTY_LINE_TRANSLATE_ANCHOR] = propertyValue + layer.setProperties(propertyValue) + } + + /** + * Specifies the lengths of the alternating dashes and gaps that form the dash pattern. The lengths are later scaled by the line width. To convert a dash length to density-independent pixels, multiply the length by the current line width. Note that GeoJSON sources with `lineMetrics: true` specified won't render dashed lines to the expected scale. Also note that zoom-dependent expressions will be evaluated only at integer zoom levels. + */ + var lineDasharray: Array? + get() = layer.lineDasharray.value + set(value) { + val propertyValue: PropertyValue<*> = PropertyFactory.lineDasharray(value) + constantPropertyUsageMap[PROPERTY_LINE_DASHARRAY] = propertyValue + layer.setProperties(propertyValue) + } + + /** + * Set filter on the managed lines. + */ + override fun setFilter(expression: Expression) { + layerFilter = expression + layer.setFilter(expression) + } + + val filter: Expression? + /** + * Get filter of the managed lines. + */ + get() = layer.filter + + companion object { + private const val PROPERTY_LINE_CAP: String = "line-cap" + private const val PROPERTY_LINE_MITER_LIMIT: String = "line-miter-limit" + private const val PROPERTY_LINE_ROUND_LIMIT: String = "line-round-limit" + private const val PROPERTY_LINE_TRANSLATE: String = "line-translate" + private const val PROPERTY_LINE_TRANSLATE_ANCHOR: String = "line-translate-anchor" + private const val PROPERTY_LINE_DASHARRAY: String = "line-dasharray" + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/LineOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/LineOptions.java deleted file mode 100644 index 2d98806d305..00000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/LineOptions.java +++ /dev/null @@ -1,413 +0,0 @@ -//package org.maplibre.android.annotations; -// -//import androidx.annotation.NonNull; -//import androidx.annotation.Nullable; -// -//import com.google.gson.JsonElement; -//import com.google.gson.JsonObject; -//import com.mapbox.geojson.Feature; -//import com.mapbox.geojson.LineString; -//import com.mapbox.geojson.Point; -// -//import org.maplibre.android.geometry.LatLng; -//import org.maplibre.android.style.layers.Property; -// -//import java.util.ArrayList; -//import java.util.List; -// -///** -// * Builder class from which a line is created. -// */ -//public class LineOptions extends Options { -// -// private boolean isDraggable; -// private JsonElement data; -// private LineString geometry; -// private String lineJoin; -// private Float lineOpacity; -// private String lineColor; -// private Float lineWidth; -// private Float lineGapWidth; -// private Float lineOffset; -// private Float lineBlur; -// private String linePattern; -// -// static final String PROPERTY_LINE_JOIN = "line-join"; -// static final String PROPERTY_LINE_OPACITY = "line-opacity"; -// static final String PROPERTY_LINE_COLOR = "line-color"; -// static final String PROPERTY_LINE_WIDTH = "line-width"; -// static final String PROPERTY_LINE_GAP_WIDTH = "line-gap-width"; -// static final String PROPERTY_LINE_OFFSET = "line-offset"; -// static final String PROPERTY_LINE_BLUR = "line-blur"; -// static final String PROPERTY_LINE_PATTERN = "line-pattern"; -// private static final String PROPERTY_IS_DRAGGABLE = "is-draggable"; -// -// /** -// * Set line-join to initialise the line with. -// *

-// * The display of lines when joining. -// *

-// * -// * @param lineJoin the line-join value -// * @return this -// */ -// public LineOptions withLineJoin(@Property.LINE_JOIN String lineJoin) { -// this.lineJoin = lineJoin; -// return this; -// } -// -// /** -// * Get the current configured line-join for the line -// *

-// * The display of lines when joining. -// *

-// * -// * @return lineJoin value -// */ -// public String getLineJoin() { -// return lineJoin; -// } -// -// /** -// * Set line-opacity to initialise the line with. -// *

-// * The opacity at which the line will be drawn. -// *

-// * -// * @param lineOpacity the line-opacity value -// * @return this -// */ -// public LineOptions withLineOpacity(Float lineOpacity) { -// this.lineOpacity = lineOpacity; -// return this; -// } -// -// /** -// * Get the current configured line-opacity for the line -// *

-// * The opacity at which the line will be drawn. -// *

-// * -// * @return lineOpacity value -// */ -// public Float getLineOpacity() { -// return lineOpacity; -// } -// -// /** -// * Set line-color to initialise the line with. -// *

-// * The color with which the line will be drawn. -// *

-// * -// * @param lineColor the line-color value -// * @return this -// */ -// public LineOptions withLineColor(String lineColor) { -// this.lineColor = lineColor; -// return this; -// } -// -// /** -// * Get the current configured line-color for the line -// *

-// * The color with which the line will be drawn. -// *

-// * -// * @return lineColor value -// */ -// public String getLineColor() { -// return lineColor; -// } -// -// /** -// * Set line-width to initialise the line with. -// *

-// * Stroke thickness. -// *

-// * -// * @param lineWidth the line-width value -// * @return this -// */ -// public LineOptions withLineWidth(Float lineWidth) { -// this.lineWidth = lineWidth; -// return this; -// } -// -// /** -// * Get the current configured line-width for the line -// *

-// * Stroke thickness. -// *

-// * -// * @return lineWidth value -// */ -// public Float getLineWidth() { -// return lineWidth; -// } -// -// /** -// * Set line-gap-width to initialise the line with. -// *

-// * Draws a line casing outside of a line's actual path. Value indicates the width of the inner gap. -// *

-// * -// * @param lineGapWidth the line-gap-width value -// * @return this -// */ -// public LineOptions withLineGapWidth(Float lineGapWidth) { -// this.lineGapWidth = lineGapWidth; -// return this; -// } -// -// /** -// * Get the current configured line-gap-width for the line -// *

-// * Draws a line casing outside of a line's actual path. Value indicates the width of the inner gap. -// *

-// * -// * @return lineGapWidth value -// */ -// public Float getLineGapWidth() { -// return lineGapWidth; -// } -// -// /** -// * Set line-offset to initialise the line with. -// *

-// * The line's offset. For linear features, a positive value offsets the line to the right, relative to the direction -// * of the line, and a negative value to the left. For polygon features, a positive value results in an inset, and a -// * negative value results in an outset. -// *

-// * -// * @param lineOffset the line-offset value -// * @return this -// */ -// public LineOptions withLineOffset(Float lineOffset) { -// this.lineOffset = lineOffset; -// return this; -// } -// -// /** -// * Get the current configured line-offset for the line -// *

-// * The line's offset. For linear features, a positive value offsets the line to the right, relative to the direction -// * of the line, and a negative value to the left. For polygon features, a positive value results in an inset, and a -// * negative value results in an outset. -// *

-// * -// * @return lineOffset value -// */ -// public Float getLineOffset() { -// return lineOffset; -// } -// -// /** -// * Set line-blur to initialise the line with. -// *

-// * Blur applied to the line, in density-independent pixels. -// *

-// * -// * @param lineBlur the line-blur value -// * @return this -// */ -// public LineOptions withLineBlur(Float lineBlur) { -// this.lineBlur = lineBlur; -// return this; -// } -// -// /** -// * Get the current configured line-blur for the line -// *

-// * Blur applied to the line, in density-independent pixels. -// *

-// * -// * @return lineBlur value -// */ -// public Float getLineBlur() { -// return lineBlur; -// } -// -// /** -// * Set line-pattern to initialise the line with. -// *

-// * Name of image in sprite to use for drawing image lines. For seamless patterns, image width must be a factor of -// * two (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be evaluated only at integer zoom levels. -// *

-// * -// * @param linePattern the line-pattern value -// * @return this -// */ -// public LineOptions withLinePattern(String linePattern) { -// this.linePattern = linePattern; -// return this; -// } -// -// /** -// * Get the current configured line-pattern for the line -// *

-// * Name of image in sprite to use for drawing image lines. For seamless patterns, image width must be a factor of -// * two (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be evaluated only at integer zoom levels. -// *

-// * -// * @return linePattern value -// */ -// public String getLinePattern() { -// return linePattern; -// } -// -// /** -// * Set a list of LatLng for the line, which represents the locations of the line on the map -// * -// * @param latLngs a list of the locations of the line in a longitude and latitude pairs -// * @return this -// */ -// public LineOptions withLatLngs(List latLngs) { -// List points = new ArrayList<>(); -// for (LatLng latLng : latLngs) { -// points.add(Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude())); -// } -// geometry = LineString.fromLngLats(points); -// return this; -// } -// -// /** -// * Get a list of LatLng for the line, which represents the locations of the line on the map -// * -// * @return a list of the locations of the line in a longitude and latitude pairs -// */ -// public List getLatLngs() { -// List latLngs = new ArrayList<>(); -// if (geometry != null) { -// for (Point coordinate : geometry.coordinates()) { -// latLngs.add(new LatLng(coordinate.latitude(), coordinate.longitude())); -// } -// } -// return latLngs; -// } -// -// /** -// * Set the geometry of the line, which represents the location of the line on the map -// * -// * @param geometry the location of the line -// * @return this -// */ -// public LineOptions withGeometry(LineString geometry) { -// this.geometry = geometry; -// return this; -// } -// -// /** -// * Get the geometry of the line, which represents the location of the line on the map -// * -// * @return the location of the line -// */ -// public LineString getGeometry() { -// return geometry; -// } -// -// /** -// * Returns whether this line is draggable, meaning it can be dragged across the screen when touched and moved. -// * -// * @return draggable when touched -// */ -// public boolean getDraggable() { -// return isDraggable; -// } -// -// /** -// * Set whether this line should be draggable, meaning it can be dragged across the screen when touched and moved. -// * -// * @param draggable should be draggable -// */ -// public LineOptions withDraggable(boolean draggable) { -// isDraggable = draggable; -// return this; -// } -// -// /** -// * Set the arbitrary json data of the annotation. -// * -// * @param jsonElement the arbitrary json element data -// */ -// public LineOptions withData(@Nullable JsonElement jsonElement) { -// this.data = jsonElement; -// return this; -// } -// -// /** -// * Get the arbitrary json data of the annotation. -// * -// * @return the arbitrary json object data if set, else null -// */ -// @Nullable -// public JsonElement getData() { -// return data; -// } -// -// @Override -// Line build(long id, AnnotationManager annotationManager) { -// if (geometry == null) { -// throw new RuntimeException("geometry field is required"); -// } -// JsonObject jsonObject = new JsonObject(); -// jsonObject.addProperty(PROPERTY_LINE_JOIN, lineJoin); -// jsonObject.addProperty(PROPERTY_LINE_OPACITY, lineOpacity); -// jsonObject.addProperty(PROPERTY_LINE_COLOR, lineColor); -// jsonObject.addProperty(PROPERTY_LINE_WIDTH, lineWidth); -// jsonObject.addProperty(PROPERTY_LINE_GAP_WIDTH, lineGapWidth); -// jsonObject.addProperty(PROPERTY_LINE_OFFSET, lineOffset); -// jsonObject.addProperty(PROPERTY_LINE_BLUR, lineBlur); -// jsonObject.addProperty(PROPERTY_LINE_PATTERN, linePattern); -// Line line = new Line(id, annotationManager, jsonObject, geometry); -// line.setDraggable(isDraggable); -// line.setData(data); -// return line; -// } -// -// /** -// * Creates LineOptions out of a Feature. -// * -// * @param feature feature to be converted -// */ -// @Nullable -// static LineOptions fromFeature(@NonNull Feature feature) { -// if (feature.geometry() == null) { -// throw new RuntimeException("geometry field is required"); -// } -// if (!(feature.geometry() instanceof LineString)) { -// return null; -// } -// -// LineOptions options = new LineOptions(); -// options.geometry = (LineString) feature.geometry(); -// if (feature.hasProperty(PROPERTY_LINE_JOIN)) { -// options.lineJoin = feature.getProperty(PROPERTY_LINE_JOIN).getAsString(); -// } -// if (feature.hasProperty(PROPERTY_LINE_OPACITY)) { -// options.lineOpacity = feature.getProperty(PROPERTY_LINE_OPACITY).getAsFloat(); -// } -// if (feature.hasProperty(PROPERTY_LINE_COLOR)) { -// options.lineColor = feature.getProperty(PROPERTY_LINE_COLOR).getAsString(); -// } -// if (feature.hasProperty(PROPERTY_LINE_WIDTH)) { -// options.lineWidth = feature.getProperty(PROPERTY_LINE_WIDTH).getAsFloat(); -// } -// if (feature.hasProperty(PROPERTY_LINE_GAP_WIDTH)) { -// options.lineGapWidth = feature.getProperty(PROPERTY_LINE_GAP_WIDTH).getAsFloat(); -// } -// if (feature.hasProperty(PROPERTY_LINE_OFFSET)) { -// options.lineOffset = feature.getProperty(PROPERTY_LINE_OFFSET).getAsFloat(); -// } -// if (feature.hasProperty(PROPERTY_LINE_BLUR)) { -// options.lineBlur = feature.getProperty(PROPERTY_LINE_BLUR).getAsFloat(); -// } -// if (feature.hasProperty(PROPERTY_LINE_PATTERN)) { -// options.linePattern = feature.getProperty(PROPERTY_LINE_PATTERN).getAsString(); -// } -// if (feature.hasProperty(PROPERTY_IS_DRAGGABLE)) { -// options.isDraggable = feature.getProperty(PROPERTY_IS_DRAGGABLE).getAsBoolean(); -// } -// return options; -// } -//} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationClickListener.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationClickListener.kt index ecc81414708..dee35ac8f82 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationClickListener.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationClickListener.kt @@ -24,4 +24,10 @@ typealias OnSymbolClickListener = OnAnnotationClickListener /** * Interface definition for a callback to be invoked when a circle has been clicked. */ -typealias OnCircleClickListener = OnAnnotationClickListener \ No newline at end of file +typealias OnCircleClickListener = OnAnnotationClickListener + +/** + * Interface definition for a callback to be invoked when a line has been clicked. + */ +typealias OnLineClickListener = OnAnnotationClickListener + diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationDragListener.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationDragListener.kt index 84e3584f2a1..5d114a37858 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationDragListener.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationDragListener.kt @@ -36,4 +36,9 @@ typealias OnSymbolDragListener = OnAnnotationDragListener /** * Interface definition for a callback to be invoked when a circle is dragged. */ -typealias OnCircleDragListener = OnAnnotationDragListener \ No newline at end of file +typealias OnCircleDragListener = OnAnnotationDragListener + +/** + * Interface definition for a callback to be invoked when a line is dragged. + */ +typealias OnLineDragListener = OnAnnotationDragListener diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationLongClickListener.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationLongClickListener.kt index 26f80d4edba..4a009bd7382 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationLongClickListener.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationLongClickListener.kt @@ -24,4 +24,9 @@ typealias OnSymbolLongClickListener = OnAnnotationLongClickListener /** * Interface definition for a callback to be invoked when a circle has been long clicked. */ -typealias OnCircleLongClickListener = OnAnnotationLongClickListener \ No newline at end of file +typealias OnCircleLongClickListener = OnAnnotationLongClickListener + +/** + * Interface definition for a callback to be invoked when a line has been long clicked. + */ +typealias OnLineLongClickListener = OnAnnotationLongClickListener diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnLineClickListener.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnLineClickListener.kt deleted file mode 100644 index b2e327fab99..00000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnLineClickListener.kt +++ /dev/null @@ -1,6 +0,0 @@ -package org.maplibre.android.annotations - -/** - * Interface definition for a callback to be invoked when a line has been clicked. - */ -//interface OnLineClickListener : OnAnnotationClickListener diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnLineDragListener.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnLineDragListener.kt deleted file mode 100644 index 6ac31e1d2ac..00000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnLineDragListener.kt +++ /dev/null @@ -1,6 +0,0 @@ -package org.maplibre.android.annotations - -/** - * Interface definition for a callback to be invoked when a line is dragged. - */ -//interface OnLineDragListener : OnAnnotationDragListener diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnLineLongClickListener.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnLineLongClickListener.kt deleted file mode 100644 index a30c565631a..00000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnLineLongClickListener.kt +++ /dev/null @@ -1,6 +0,0 @@ -package org.maplibre.android.annotations - -/** - * Interface definition for a callback to be invoked when a line has been long clicked. - */ -//interface OnLineLongClickListener : OnAnnotationLongClickListener diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Symbol.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Symbol.kt index 9a2ecef692c..3ada75870c1 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Symbol.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Symbol.kt @@ -19,6 +19,7 @@ class Symbol( var position: LatLng = position set(value) { field = value + geometry = Point.fromLngLat(value.longitude, value.latitude) updateThis() } var icon: Icon? = icon diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/data/Defaults.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/data/Defaults.kt index 93fca919fb2..e2fd2f012d9 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/data/Defaults.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/data/Defaults.kt @@ -1,10 +1,12 @@ package org.maplibre.android.annotations.data +import android.graphics.Bitmap import android.graphics.Color import android.graphics.PointF import androidx.annotation.ColorInt import com.google.gson.JsonElement import org.maplibre.android.annotations.Circle +import org.maplibre.android.annotations.Line class Defaults { companion object { @@ -57,5 +59,15 @@ class Defaults { val CIRCLE_STROKE_COLOR: Int = Color.BLACK val CIRCLE_STROKE_OPACITY: Float = 1f + + val LINE_JOIN: Line.Join = Line.Join.MITER + val LINE_OPACITY: Float = 1f + @ColorInt + val LINE_COLOR: Int = Color.BLACK + val LINE_WIDTH: Float = 1f + val LINE_GAP: Float? = null + val LINE_OFFSET: Float = 0f + val LINE_BLUR: Float? = null + val LINE_PATTERN: Bitmap? = null } } diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/LineManagerTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/LineManagerTest.kt index b840fe7ca21..aa313f4a9df 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/LineManagerTest.kt +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/LineManagerTest.kt @@ -1,820 +1,732 @@ -//package org.maplibre.android.annotations -// -//import com.google.gson.JsonPrimitive -//import com.mapbox.geojson.Feature -//import com.mapbox.geojson.FeatureCollection -//import com.mapbox.geojson.Geometry -//import com.mapbox.geojson.LineString -//import com.mapbox.geojson.Point -//import junit.framework.Assert -//import org.junit.Before -//import org.junit.Test -//import org.maplibre.android.geometry.LatLng -//import org.maplibre.android.maps.MapLibreMap -//import org.maplibre.android.maps.MapView -//import org.maplibre.android.maps.Style -//import org.maplibre.android.maps.Style.OnStyleLoaded -//import org.maplibre.android.style.expressions.Expression -//import org.maplibre.android.style.expressions.Expression.get -//import org.maplibre.android.style.layers.LineLayer -//import org.maplibre.android.style.layers.Property.LINE_JOIN_BEVEL -//import org.maplibre.android.style.layers.PropertyFactory -//import org.maplibre.android.style.layers.PropertyFactory.lineBlur -//import org.maplibre.android.style.layers.PropertyFactory.lineColor -//import org.maplibre.android.style.layers.PropertyFactory.lineGapWidth -//import org.maplibre.android.style.layers.PropertyFactory.lineJoin -//import org.maplibre.android.style.layers.PropertyFactory.lineOffset -//import org.maplibre.android.style.layers.PropertyFactory.lineOpacity -//import org.maplibre.android.style.layers.PropertyFactory.linePattern -//import org.maplibre.android.style.layers.PropertyFactory.lineWidth -//import org.maplibre.android.style.sources.GeoJsonOptions -//import org.maplibre.android.style.sources.GeoJsonSource -//import org.mockito.ArgumentCaptor -//import org.mockito.ArgumentMatchers -//import org.mockito.Mockito -// -//class LineManagerTest { -// private val draggableAnnotationController = Mockito.mock( -// DraggableAnnotationController::class.java -// ) -// private val mapView: MapView = Mockito.mock(MapView::class.java) -// private val maplibreMap: MapLibreMap = Mockito.mock( -// MapLibreMap::class.java -// ) -// private val style: Style = Mockito.mock(Style::class.java) -// private val geoJsonSource: GeoJsonSource = Mockito.mock( -// GeoJsonSource::class.java -// ) -// private val optionedGeoJsonSource: GeoJsonSource = Mockito.mock( -// GeoJsonSource::class.java -// ) -// private val layerId = "annotation_layer" -// private val lineLayer: LineLayer = Mockito.mock( -// LineLayer::class.java -// ) -// private lateinit var lineManager: LineManager -// private val coreElementProvider: CoreElementProvider = Mockito.mock( -// LineElementProvider::class.java -// ) -// private val geoJsonOptions: GeoJsonOptions = Mockito.mock( -// GeoJsonOptions::class.java -// ) -// -// @Before -// fun beforeTest() { -// Mockito.`when`(lineLayer.id).thenReturn(layerId) -// Mockito.`when`(coreElementProvider.layer).thenReturn(lineLayer) -// Mockito.`when`(coreElementProvider.getSource(null)).thenReturn(geoJsonSource) -// Mockito.`when`(coreElementProvider.getSource(geoJsonOptions)) -// .thenReturn(optionedGeoJsonSource) -// Mockito.`when`(style.isFullyLoaded).thenReturn(true) -// } -// -// @Test -// fun testInitialization() { -// lineManager = LineManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Mockito.verify(style).addSource(geoJsonSource) -// Mockito.verify(style).addLayer(lineLayer) -// Assert.assertTrue(lineManager.dataDrivenPropertyUsageMap.isNotEmpty()) -// for (value in lineManager.dataDrivenPropertyUsageMap.values) { -// Assert.assertFalse(value) -// } -// Mockito.verify(lineLayer).setProperties( -// *lineManager.constantPropertyUsageMap.values.toTypedArray() -// ) -// Mockito.verify(lineLayer, Mockito.times(0)).setFilter( -// ArgumentMatchers.any( -// Expression::class.java -// ) -// ) -// } -// -// @Test -// fun testInitializationOnStyleReload() { -// lineManager = LineManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Mockito.verify(style).addSource(geoJsonSource) -// Mockito.verify(style).addLayer(lineLayer) -// Assert.assertTrue(lineManager.dataDrivenPropertyUsageMap.isNotEmpty()) -// for (value in lineManager.dataDrivenPropertyUsageMap.values) { -// Assert.assertFalse(value) -// } -// Mockito.verify(lineLayer).setProperties( -// *lineManager.constantPropertyUsageMap.values.toTypedArray() -// ) -// val filter: Expression = Expression.literal(false) -// lineManager.setFilter(filter) -// val loadingArgumentCaptor: ArgumentCaptor = -// ArgumentCaptor.forClass( -// MapView.OnDidFinishLoadingStyleListener::class.java -// ) -// Mockito.verify(mapView).addOnDidFinishLoadingStyleListener(loadingArgumentCaptor.capture()) -// loadingArgumentCaptor.value.onDidFinishLoadingStyle() -// val styleLoadedArgumentCaptor: ArgumentCaptor = ArgumentCaptor.forClass( -// OnStyleLoaded::class.java -// ) -// Mockito.verify(maplibreMap).getStyle(styleLoadedArgumentCaptor.capture()) -// val newStyle: Style = Mockito.mock(Style::class.java) -// Mockito.`when`(newStyle.isFullyLoaded).thenReturn(true) -// val newSource: GeoJsonSource = Mockito.mock( -// GeoJsonSource::class.java -// ) -// Mockito.`when`(coreElementProvider.getSource(null)).thenReturn(newSource) -// val newLayer: LineLayer = Mockito.mock(LineLayer::class.java) -// Mockito.`when`(coreElementProvider.layer).thenReturn(newLayer) -// styleLoadedArgumentCaptor.value.onStyleLoaded(newStyle) -// Mockito.verify(newStyle).addSource(newSource) -// Mockito.verify(newStyle).addLayer(newLayer) -// Assert.assertTrue(lineManager.dataDrivenPropertyUsageMap.isNotEmpty()) -// for (value in lineManager.dataDrivenPropertyUsageMap.values) { -// Assert.assertFalse(value) -// } -// Mockito.verify(newLayer).setProperties( -// *lineManager.constantPropertyUsageMap.values.toTypedArray() -// ) -// Mockito.verify(lineLayer).setFilter(filter) -// } -// -// @Test -// fun testLayerBelowInitialization() { -// lineManager = LineManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// "test_layer", -// null, -// null, -// draggableAnnotationController -// ) -// Mockito.verify(style).addSource(geoJsonSource) -// Mockito.verify(style).addLayerBelow(lineLayer, "test_layer") -// Assert.assertTrue(lineManager.dataDrivenPropertyUsageMap.isNotEmpty()) -// for (value in lineManager.dataDrivenPropertyUsageMap.values) { -// Assert.assertFalse(value) -// } -// Mockito.verify(lineLayer).setProperties( -// *lineManager.constantPropertyUsageMap.values.toTypedArray() -// ) -// } -// -// @Test -// fun testGeoJsonOptionsInitialization() { -// lineManager = LineManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// geoJsonOptions, -// draggableAnnotationController -// ) -// Mockito.verify(style).addSource(optionedGeoJsonSource) -// Mockito.verify(style).addLayer(lineLayer) -// Assert.assertTrue(lineManager.dataDrivenPropertyUsageMap.isNotEmpty()) -// for (value in lineManager.dataDrivenPropertyUsageMap.values) { -// Assert.assertFalse(value) -// } -// Mockito.verify(lineLayer).setProperties( -// *lineManager.constantPropertyUsageMap.values.toTypedArray() -// ) -// Mockito.verify(lineLayer, Mockito.times(0)).setFilter( -// ArgumentMatchers.any( -// Expression::class.java -// ) -// ) -// } -// -// @Test -// fun testLayerId() { -// lineManager = LineManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Assert.assertEquals(layerId, lineManager.layerId) -// } -// -// @Test -// fun testAddLine() { -// lineManager = LineManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val latLngs = listOf(LatLng(), LatLng(1.0, 1.0)) -// val line = lineManager.create(LineOptions().withLatLngs(latLngs)) -// Assert.assertEquals(lineManager.annotations[0], line) -// } -// -// @Test -// fun addLineFromFeatureCollection() { -// lineManager = LineManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val points = listOf( -// Point.fromLngLat(0.0, 0.0), -// Point.fromLngLat(1.0, 1.0) -// ) -// val geometry: Geometry = LineString.fromLngLats(points) -// val feature = Feature.fromGeometry(geometry) -// feature.addStringProperty("line-join", LINE_JOIN_BEVEL) -// feature.addNumberProperty("line-opacity", 2.0f) -// feature.addStringProperty("line-color", "rgba(0, 0, 0, 1)") -// feature.addNumberProperty("line-width", 2.0f) -// feature.addNumberProperty("line-gap-width", 2.0f) -// feature.addNumberProperty("line-offset", 2.0f) -// feature.addNumberProperty("line-blur", 2.0f) -// feature.addStringProperty("line-pattern", "pedestrian-polygon") -// feature.addBooleanProperty("is-draggable", true) -// val lines: List = lineManager.create(FeatureCollection.fromFeature(feature)) -// val line = lines[0] -// Assert.assertEquals(line.geometry, geometry) -// Assert.assertEquals(line.lineJoin, LINE_JOIN_BEVEL) -// Assert.assertEquals(line.lineOpacity, 2.0f) -// Assert.assertEquals(line.lineColor, "rgba(0, 0, 0, 1)") -// Assert.assertEquals(line.lineWidth, 2.0f) -// Assert.assertEquals(line.lineGapWidth, 2.0f) -// Assert.assertEquals(line.lineOffset, 2.0f) -// Assert.assertEquals(line.lineBlur, 2.0f) -// Assert.assertEquals(line.linePattern, "pedestrian-polygon") -// Assert.assertTrue(line.isDraggable) -// } -// -// @Test -// fun addLines() { -// lineManager = LineManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val lines = listOf( -// listOf( -// LatLng(2.0, 2.0), -// LatLng(2.0, 3.0) -// ), -// listOf( -// LatLng(1.0, 1.0), -// LatLng(2.0, 3.0) -// ) -// ).map { LineOptions().withLatLngs(it) } -// .let { lineManager.create(it) } -// Assert.assertTrue("Returned value size should match", lines.size == 2) -// Assert.assertTrue("Annotations size should match", lineManager.annotations.size() == 2) -// } -// -// @Test -// fun testDeleteLine() { -// lineManager = LineManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val latLngs = listOf( -// LatLng(), -// LatLng(1.0, 1.0) -// ) -// val line = lineManager.create(LineOptions().withLatLngs(latLngs)) -// lineManager.delete(line) -// Assert.assertTrue(lineManager.annotations.size() == 0) -// } -// -// @Test -// fun testGeometryLine() { -// lineManager = LineManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val latLngs = listOf( -// LatLng(), -// LatLng(1.0, 1.0) -// ) -// val options = LineOptions().withLatLngs(latLngs) -// val line = lineManager.create(options) -// Assert.assertEquals(options.latLngs, latLngs) -// Assert.assertEquals(line.latLngs, latLngs) -// Assert.assertEquals( -// options.geometry as Any, -// LineString.fromLngLats( -// listOf( -// Point.fromLngLat(0.0, 0.0), -// Point.fromLngLat(1.0, 1.0) -// ) -// ) -// ) -// Assert.assertEquals( -// line.getGeometry(), -// LineString.fromLngLats( -// listOf( -// Point.fromLngLat(0.0, 0.0), -// Point.fromLngLat(1.0, 1.0) -// ) -// ) -// ) -// } -// -// @Test -// fun testFeatureIdLine() { -// lineManager = LineManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val latLngs = listOf( -// LatLng(), -// LatLng(1.0, 1.0) -// ) -// val lineZero = lineManager.create(LineOptions().withLatLngs(latLngs)) -// val lineOne = lineManager.create(LineOptions().withLatLngs(latLngs)) -// Assert.assertEquals(lineZero.feature[Line.ID_KEY].asLong, 0) -// Assert.assertEquals(lineOne.feature[Line.ID_KEY].asLong, 1) -// } -// -// @Test -// fun testLineDraggableFlag() { -// lineManager = LineManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val latLngs = listOf( -// LatLng(), -// LatLng(1.0, 1.0) -// ) -// val lineZero = lineManager.create(LineOptions().withLatLngs(latLngs)) -// Assert.assertFalse(lineZero.isDraggable) -// lineZero.isDraggable = true -// Assert.assertTrue(lineZero.isDraggable) -// lineZero.isDraggable = false -// Assert.assertFalse(lineZero.isDraggable) -// } -// -// @Test -// fun testLineJoinLayerProperty() { -// lineManager = LineManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Mockito.verify(lineLayer, Mockito.times(0)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(lineJoin(get("line-join")))) -// ) -// val latLngs = listOf( -// LatLng(), -// LatLng(1.0, 1.0) -// ) -// val options = LineOptions().withLatLngs(latLngs).withLineJoin(LINE_JOIN_BEVEL) -// lineManager.create(options) -// lineManager.updateSourceNow() -// Mockito.verify(lineLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(lineJoin(get("line-join")))) -// ) -// lineManager.create(options) -// Mockito.verify(lineLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(lineJoin(get("line-join")))) -// ) -// } -// -// @Test -// fun testLineOpacityLayerProperty() { -// lineManager = LineManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Mockito.verify(lineLayer, Mockito.times(0)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(lineOpacity(get("line-opacity")))) -// ) -// val latLngs = listOf( -// LatLng(), -// LatLng(1.0, 1.0) -// ) -// val options = LineOptions().withLatLngs(latLngs).withLineOpacity(2.0f) -// lineManager.create(options) -// lineManager.updateSourceNow() -// Mockito.verify(lineLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(lineOpacity(get("line-opacity")))) -// ) -// lineManager.create(options) -// Mockito.verify(lineLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(lineOpacity(get("line-opacity")))) -// ) -// } -// -// @Test -// fun testLineColorLayerProperty() { -// lineManager = LineManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Mockito.verify(lineLayer, Mockito.times(0)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(lineColor(get("line-color")))) -// ) -// val latLngs = listOf( -// LatLng(), -// LatLng(1.0, 1.0) -// ) -// val options = LineOptions().withLatLngs(latLngs).withLineColor("rgba(0, 0, 0, 1)") -// lineManager.create(options) -// lineManager.updateSourceNow() -// Mockito.verify(lineLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(lineColor(get("line-color")))) -// ) -// lineManager.create(options) -// Mockito.verify(lineLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(lineColor(get("line-color")))) -// ) -// } -// -// @Test -// fun testLineWidthLayerProperty() { -// lineManager = LineManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Mockito.verify(lineLayer, Mockito.times(0)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(lineWidth(get("line-width")))) -// ) -// val latLngs = listOf( -// LatLng(), -// LatLng(1.0, 1.0) -// ) -// val options = LineOptions().withLatLngs(latLngs).withLineWidth(2.0f) -// lineManager.create(options) -// lineManager.updateSourceNow() -// Mockito.verify(lineLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(lineWidth(get("line-width")))) -// ) -// lineManager.create(options) -// Mockito.verify(lineLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(lineWidth(get("line-width")))) -// ) -// } -// -// @Test -// fun testLineGapWidthLayerProperty() { -// lineManager = LineManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Mockito.verify(lineLayer, Mockito.times(0)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(lineGapWidth(get("line-gap-width")))) -// ) -// val latLngs = listOf( -// LatLng(), -// LatLng(1.0, 1.0) -// ) -// val options = LineOptions().withLatLngs(latLngs).withLineGapWidth(2.0f) -// lineManager.create(options) -// lineManager.updateSourceNow() -// Mockito.verify(lineLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(lineGapWidth(get("line-gap-width")))) -// ) -// lineManager.create(options) -// Mockito.verify(lineLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(lineGapWidth(get("line-gap-width")))) -// ) -// } -// -// @Test -// fun testLineOffsetLayerProperty() { -// lineManager = LineManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Mockito.verify(lineLayer, Mockito.times(0)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(lineOffset(get("line-offset")))) -// ) -// val latLngs = listOf( -// LatLng(), -// LatLng(1.0, 1.0) -// ) -// val options = LineOptions().withLatLngs(latLngs).withLineOffset(2.0f) -// lineManager.create(options) -// lineManager.updateSourceNow() -// Mockito.verify(lineLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(lineOffset(get("line-offset")))) -// ) -// lineManager.create(options) -// Mockito.verify(lineLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(lineOffset(get("line-offset")))) -// ) -// } -// -// @Test -// fun testLineBlurLayerProperty() { -// lineManager = LineManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Mockito.verify(lineLayer, Mockito.times(0)).setProperties( -// ArgumentMatchers.argThat( -// PropertyValueMatcher(PropertyFactory.lineBlur(get("line-blur"))) -// ) -// ) -// val latLngs = listOf( -// LatLng(), -// LatLng(1.0, 1.0) -// ) -// val options = LineOptions().withLatLngs(latLngs).withLineBlur(2.0f) -// lineManager.create(options) -// lineManager.updateSourceNow() -// Mockito.verify(lineLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(lineBlur(get("line-blur")))) -// ) -// lineManager.create(options) -// Mockito.verify(lineLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(lineBlur(get("line-blur")))) -// ) -// } -// -// @Test -// fun testLinePatternLayerProperty() { -// lineManager = LineManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Mockito.verify(lineLayer, Mockito.times(0)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(linePattern(get("line-pattern")))) -// ) -// val latLngs = listOf( -// LatLng(), -// LatLng(1.0, 1.0) -// ) -// val options = LineOptions().withLatLngs(latLngs).withLinePattern("pedestrian-polygon") -// lineManager.create(options) -// lineManager.updateSourceNow() -// Mockito.verify(lineLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(linePattern(get("line-pattern")))) -// ) -// lineManager.create(options) -// Mockito.verify(lineLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(linePattern(get("line-pattern")))) -// ) -// } -// -// @Test -// fun testLineLayerFilter() { -// lineManager = LineManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val expression: Expression = Expression.eq(Expression.get("test"), "selected") -// Mockito.verify(lineLayer, Mockito.times(0)).setFilter(expression) -// lineManager.setFilter(expression) -// Mockito.verify(lineLayer, Mockito.times(1)).setFilter(expression) -// Mockito.`when`(lineLayer.filter).thenReturn(expression) -// Assert.assertEquals(expression, lineManager.filter) -// Assert.assertEquals(expression, lineManager.layerFilter) -// } -// -// @Test -// fun testClickListener() { -// val listener = Mockito.mock( -// OnLineClickListener::class.java -// ) -// lineManager = LineManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Assert.assertTrue(lineManager.getClickListeners().isEmpty()) -// lineManager.addClickListener(listener) -// Assert.assertTrue(lineManager.getClickListeners().contains(listener)) -// lineManager.removeClickListener(listener) -// Assert.assertTrue(lineManager.getClickListeners().isEmpty()) -// } -// -// @Test -// fun testLongClickListener() { -// val listener = Mockito.mock( -// OnLineLongClickListener::class.java -// ) -// lineManager = LineManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Assert.assertTrue(lineManager.getLongClickListeners().isEmpty()) -// lineManager.addLongClickListener(listener) -// Assert.assertTrue(lineManager.getLongClickListeners().contains(listener)) -// lineManager.removeLongClickListener(listener) -// Assert.assertTrue(lineManager.getLongClickListeners().isEmpty()) -// } -// -// @Test -// fun testDragListener() { -// val listener = Mockito.mock( -// OnLineDragListener::class.java -// ) -// lineManager = LineManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Assert.assertTrue(lineManager.getDragListeners().isEmpty()) -// lineManager.addDragListener(listener) -// Assert.assertTrue(lineManager.getDragListeners().contains(listener)) -// lineManager.removeDragListener(listener) -// Assert.assertTrue(lineManager.getDragListeners().isEmpty()) -// } -// -// @Test -// fun testCustomData() { -// lineManager = LineManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val latLngs = listOf( -// LatLng(), -// LatLng(1.0, 1.0) -// ) -// val options = LineOptions().withLatLngs(latLngs) -// options.withData(JsonPrimitive("hello")) -// val line = lineManager.create(options) -// Assert.assertEquals(JsonPrimitive("hello"), line.data) -// } -// -// @Test -// fun testClearAll() { -// lineManager = LineManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val latLngs = listOf( -// LatLng(), -// LatLng(1.0, 1.0) -// ) -// val options = LineOptions().withLatLngs(latLngs) -// lineManager.create(options) -// Assert.assertEquals(1, lineManager.annotations.size()) -// lineManager.deleteAll() -// Assert.assertEquals(0, lineManager.annotations.size()) -// } -// -// @Test -// fun testIgnoreClearedAnnotations() { -// lineManager = LineManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val latLngs = listOf( -// LatLng(), -// LatLng(1.0, 1.0) -// ) -// val options = LineOptions().withLatLngs(latLngs) -// val line = lineManager.create(options) -// Assert.assertEquals(1, lineManager.annotations.size()) -// lineManager.annotations.clear() -// lineManager.updateSource() -// Assert.assertTrue(lineManager.annotations.isEmpty) -// lineManager.update(line) -// Assert.assertTrue(lineManager.annotations.isEmpty) -// } -//} +package org.maplibre.android.annotations + +import android.graphics.Bitmap +import android.graphics.Color +import com.google.gson.JsonPrimitive +import com.mapbox.geojson.Feature +import com.mapbox.geojson.FeatureCollection +import com.mapbox.geojson.Geometry +import com.mapbox.geojson.LineString +import com.mapbox.geojson.Point +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.maplibre.android.geometry.LatLng +import org.maplibre.android.maps.MapLibreMap +import org.maplibre.android.maps.MapView +import org.maplibre.android.maps.Style +import org.maplibre.android.maps.Style.OnStyleLoaded +import org.maplibre.android.style.expressions.Expression +import org.maplibre.android.style.expressions.Expression.get +import org.maplibre.android.style.layers.LineLayer +import org.maplibre.android.style.layers.Property.LINE_JOIN_BEVEL +import org.maplibre.android.style.layers.PropertyFactory +import org.maplibre.android.style.layers.PropertyFactory.lineBlur +import org.maplibre.android.style.layers.PropertyFactory.lineColor +import org.maplibre.android.style.layers.PropertyFactory.lineGapWidth +import org.maplibre.android.style.layers.PropertyFactory.lineJoin +import org.maplibre.android.style.layers.PropertyFactory.lineOffset +import org.maplibre.android.style.layers.PropertyFactory.lineOpacity +import org.maplibre.android.style.layers.PropertyFactory.linePattern +import org.maplibre.android.style.layers.PropertyFactory.lineWidth +import org.maplibre.android.style.sources.GeoJsonOptions +import org.maplibre.android.style.sources.GeoJsonSource +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers +import org.mockito.Mockito + +class LineManagerTest { + private val draggableAnnotationController = Mockito.mock( + DraggableAnnotationController::class.java + ) + private val mapView: MapView = Mockito.mock(MapView::class.java) + private val maplibreMap: MapLibreMap = Mockito.mock( + MapLibreMap::class.java + ) + private val style: Style = Mockito.mock(Style::class.java) + private val geoJsonSource: GeoJsonSource = Mockito.mock( + GeoJsonSource::class.java + ) + private val optionedGeoJsonSource: GeoJsonSource = Mockito.mock( + GeoJsonSource::class.java + ) + private val layerId = "annotation_layer" + private val lineLayer: LineLayer = Mockito.mock( + LineLayer::class.java + ) + private lateinit var lineManager: LineManager + private val coreElementProvider: CoreElementProvider = Mockito.mock( + LineElementProvider::class.java + ) + private val geoJsonOptions: GeoJsonOptions = Mockito.mock( + GeoJsonOptions::class.java + ) + + @Before + fun beforeTest() { + Mockito.`when`(lineLayer.id).thenReturn(layerId) + Mockito.`when`(coreElementProvider.layer).thenReturn(lineLayer) + Mockito.`when`(coreElementProvider.getSource(null)).thenReturn(geoJsonSource) + Mockito.`when`(coreElementProvider.getSource(geoJsonOptions)) + .thenReturn(optionedGeoJsonSource) + Mockito.`when`(style.isFullyLoaded).thenReturn(true) + } + + @Test + fun testInitialization() { + lineManager = LineManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Mockito.verify(style).addSource(geoJsonSource) + Mockito.verify(style).addLayer(lineLayer) + Assert.assertTrue(lineManager.usedDataDrivenProperties.isEmpty()) + Mockito.verify(lineLayer).setProperties( + *lineManager.constantPropertyUsageMap.values.toTypedArray() + ) + Mockito.verify(lineLayer, Mockito.times(0)).setFilter( + ArgumentMatchers.any( + Expression::class.java + ) + ) + } + + @Test + fun testInitializationOnStyleReload() { + lineManager = LineManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Mockito.verify(style).addSource(geoJsonSource) + Mockito.verify(style).addLayer(lineLayer) + Assert.assertTrue(lineManager.usedDataDrivenProperties.isEmpty()) + Mockito.verify(lineLayer).setProperties( + *lineManager.constantPropertyUsageMap.values.toTypedArray() + ) + val filter: Expression = Expression.literal(false) + lineManager.setFilter(filter) + val loadingArgumentCaptor: ArgumentCaptor = + ArgumentCaptor.forClass( + MapView.OnDidFinishLoadingStyleListener::class.java + ) + Mockito.verify(mapView).addOnDidFinishLoadingStyleListener(loadingArgumentCaptor.capture()) + loadingArgumentCaptor.value.onDidFinishLoadingStyle() + val styleLoadedArgumentCaptor: ArgumentCaptor = ArgumentCaptor.forClass( + OnStyleLoaded::class.java + ) + Mockito.verify(maplibreMap).getStyle(styleLoadedArgumentCaptor.capture()) + val newStyle: Style = Mockito.mock(Style::class.java) + Mockito.`when`(newStyle.isFullyLoaded).thenReturn(true) + val newSource: GeoJsonSource = Mockito.mock( + GeoJsonSource::class.java + ) + Mockito.`when`(coreElementProvider.getSource(null)).thenReturn(newSource) + val newLayer: LineLayer = Mockito.mock(LineLayer::class.java) + Mockito.`when`(coreElementProvider.layer).thenReturn(newLayer) + styleLoadedArgumentCaptor.value.onStyleLoaded(newStyle) + Mockito.verify(newStyle).addSource(newSource) + Mockito.verify(newStyle).addLayer(newLayer) + Assert.assertTrue(lineManager.usedDataDrivenProperties.isEmpty()) + Mockito.verify(newLayer).setProperties( + *lineManager.constantPropertyUsageMap.values.toTypedArray() + ) + Mockito.verify(lineLayer).setFilter(filter) + } + + @Test + fun testLayerBelowInitialization() { + lineManager = LineManager( + mapView, + maplibreMap, + style, + coreElementProvider, + "test_layer", + null, + null, + draggableAnnotationController + ) + Mockito.verify(style).addSource(geoJsonSource) + Mockito.verify(style).addLayerBelow(lineLayer, "test_layer") + Assert.assertTrue(lineManager.usedDataDrivenProperties.isEmpty()) + Mockito.verify(lineLayer).setProperties( + *lineManager.constantPropertyUsageMap.values.toTypedArray() + ) + } + + @Test + fun testGeoJsonOptionsInitialization() { + lineManager = LineManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + geoJsonOptions, + draggableAnnotationController + ) + Mockito.verify(style).addSource(optionedGeoJsonSource) + Mockito.verify(style).addLayer(lineLayer) + Assert.assertTrue(lineManager.usedDataDrivenProperties.isEmpty()) + Mockito.verify(lineLayer).setProperties( + *lineManager.constantPropertyUsageMap.values.toTypedArray() + ) + Mockito.verify(lineLayer, Mockito.times(0)).setFilter( + ArgumentMatchers.any( + Expression::class.java + ) + ) + } + + @Test + fun testLayerId() { + lineManager = LineManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Assert.assertEquals(layerId, lineManager.layerId) + } + + @Test + fun testAddLine() { + lineManager = LineManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + val latLngs = listOf(LatLng(), LatLng(1.0, 1.0)) + val line = Line(latLngs) + lineManager.add(line) + Assert.assertEquals(line, lineManager.annotations.values.first()) + } + + @Test + fun addLines() { + lineManager = LineManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + listOf( + listOf( + LatLng(2.0, 2.0), + LatLng(2.0, 3.0) + ), + listOf( + LatLng(1.0, 1.0), + LatLng(2.0, 3.0) + ) + ) + .map { Line(it) } + .forEach { lineManager.add(it) } + Assert.assertTrue("Annotations size should match", lineManager.annotations.size == 2) + } + + @Test + fun testDeleteLine() { + lineManager = LineManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + val latLngs = listOf( + LatLng(), + LatLng(1.0, 1.0) + ) + val line = Line(latLngs) + lineManager.add(line) + Assert.assertEquals( + "After adding a line, one line should be present", + 1, + lineManager.annotations.size + ) + lineManager.delete(line) + Assert.assertEquals( + "After removing the only line, no lines should be present anymore", + 0, + lineManager.annotations.size + ) + } + + @Test + fun testGeometryLine() { + lineManager = LineManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + val latLngs = listOf( + LatLng(), + LatLng(1.0, 1.0) + ) + val line = Line(latLngs) + lineManager.add(line) + Assert.assertEquals(line.path, latLngs) + Assert.assertEquals( + line.geometry, + LineString.fromLngLats( + listOf( + Point.fromLngLat(0.0, 0.0), + Point.fromLngLat(1.0, 1.0) + ) + ) + ) + } + + @Test + fun testFeatureIdLine() { + lineManager = LineManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + val latLngs = listOf( + LatLng(), + LatLng(1.0, 1.0) + ) + val lineZero = Line(latLngs) + lineManager.add(lineZero) + val lineOne = Line(latLngs) + lineManager.add(lineOne) + Assert.assertEquals(lineZero.id, -1) + Assert.assertEquals(lineOne.id, -2) + } + + @Test + fun testLineDraggableFlag() { + lineManager = LineManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + val latLngs = listOf( + LatLng(), + LatLng(1.0, 1.0) + ) + val line = Line(latLngs) + lineManager.add(line) + Assert.assertFalse(line.draggable) + line.draggable = true + Assert.assertTrue(line.draggable) + line.draggable = false + Assert.assertFalse(line.draggable) + } + + @Test + fun testLineJoinLayerProperty() { + lineManager = LineManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Mockito.verify(lineLayer, Mockito.times(0)).setProperties( + ArgumentMatchers.argThat(PropertyValueMatcher(lineJoin(get("line-join")))) + ) + val latLngs = listOf( + LatLng(), + LatLng(1.0, 1.0) + ) + for (i in 0 until 2) { + val line = Line(latLngs, join = Line.Join.BEVEL) + lineManager.add(line) + lineManager.updateSourceNow() + Mockito.verify(lineLayer, Mockito.times(1)).setProperties( + ArgumentMatchers.argThat(PropertyValueMatcher(lineJoin(get("line-join")))) + ) + } + } + + @Test + fun testLineOpacityLayerProperty() { + lineManager = LineManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Mockito.verify(lineLayer, Mockito.times(0)).setProperties( + ArgumentMatchers.argThat(PropertyValueMatcher(lineOpacity(get("line-opacity")))) + ) + val latLngs = listOf( + LatLng(), + LatLng(1.0, 1.0) + ) + for (i in 0 until 2) { + val line = Line(latLngs, opacity = 0.5f) + lineManager.add(line) + lineManager.updateSourceNow() + Mockito.verify(lineLayer, Mockito.times(1)).setProperties( + ArgumentMatchers.argThat(PropertyValueMatcher(lineOpacity(get("line-opacity")))) + ) + } + } + + @Test + fun testLineColorLayerProperty() { + lineManager = LineManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Mockito.verify(lineLayer, Mockito.times(0)).setProperties( + ArgumentMatchers.argThat(PropertyValueMatcher(lineColor(get("line-color")))) + ) + val latLngs = listOf( + LatLng(), + LatLng(1.0, 1.0) + ) + for (i in 0 until 2) { + val line = Line(latLngs, color = Color.GREEN) + lineManager.add(line) + lineManager.updateSourceNow() + Mockito.verify(lineLayer, Mockito.times(1)).setProperties( + ArgumentMatchers.argThat(PropertyValueMatcher(lineColor(get("line-color")))) + ) + } + } + + @Test + fun testLineWidthLayerProperty() { + lineManager = LineManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Mockito.verify(lineLayer, Mockito.times(0)).setProperties( + ArgumentMatchers.argThat(PropertyValueMatcher(lineWidth(get("line-width")))) + ) + val latLngs = listOf( + LatLng(), + LatLng(1.0, 1.0) + ) + for (i in 0 until 2) { + val line = Line(latLngs, width = 2f) + lineManager.add(line) + lineManager.updateSourceNow() + Mockito.verify(lineLayer, Mockito.times(1)).setProperties( + ArgumentMatchers.argThat(PropertyValueMatcher(lineWidth(get("line-width")))) + ) + } + } + + @Test + fun testLineGapWidthLayerProperty() { + lineManager = LineManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Mockito.verify(lineLayer, Mockito.times(0)).setProperties( + ArgumentMatchers.argThat(PropertyValueMatcher(lineGapWidth(get("line-gap-width")))) + ) + val latLngs = listOf( + LatLng(), + LatLng(1.0, 1.0) + ) + for (i in 0 until 2) { + val line = Line(latLngs, gap = 2f) + lineManager.add(line) + lineManager.updateSourceNow() + Mockito.verify(lineLayer, Mockito.times(1)).setProperties( + ArgumentMatchers.argThat(PropertyValueMatcher(lineGapWidth(get("line-gap-width")))) + ) + } + } + + @Test + fun testLineOffsetLayerProperty() { + lineManager = LineManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Mockito.verify(lineLayer, Mockito.times(0)).setProperties( + ArgumentMatchers.argThat(PropertyValueMatcher(lineOffset(get("line-offset")))) + ) + val latLngs = listOf( + LatLng(), + LatLng(1.0, 1.0) + ) + for (i in 0 until 2) { + val line = Line(latLngs, offset = 2f) + lineManager.add(line) + lineManager.updateSourceNow() + Mockito.verify(lineLayer, Mockito.times(1)).setProperties( + ArgumentMatchers.argThat(PropertyValueMatcher(lineOffset(get("line-offset")))) + ) + } + } + + @Test + fun testLineBlurLayerProperty() { + lineManager = LineManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Mockito.verify(lineLayer, Mockito.times(0)).setProperties( + ArgumentMatchers.argThat( + PropertyValueMatcher(PropertyFactory.lineBlur(get("line-blur"))) + ) + ) + val latLngs = listOf( + LatLng(), + LatLng(1.0, 1.0) + ) + for (i in 0 until 2) { + val line = Line(latLngs, blur = 2f) + lineManager.add(line) + + lineManager.updateSourceNow() + Mockito.verify(lineLayer, Mockito.times(1)).setProperties( + ArgumentMatchers.argThat(PropertyValueMatcher(lineBlur(get("line-blur")))) + ) + } + } + + @Test + fun testLinePatternLayerProperty() { + lineManager = LineManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Mockito.verify(lineLayer, Mockito.times(0)).setProperties( + ArgumentMatchers.argThat(PropertyValueMatcher(linePattern(get("line-pattern")))) + ) + val latLngs = listOf( + LatLng(), + LatLng(1.0, 1.0) + ) + for (i in 0 until 2) { + val line = Line(latLngs, pattern = Mockito.mock(Bitmap::class.java)) + lineManager.add(line) + lineManager.updateSourceNow() + Mockito.verify(lineLayer, Mockito.times(1)).setProperties( + ArgumentMatchers.argThat(PropertyValueMatcher(linePattern(get("line-pattern")))) + ) + } + } + + @Test + fun testLineLayerFilter() { + lineManager = LineManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + val expression: Expression = Expression.eq(Expression.get("test"), "selected") + Mockito.verify(lineLayer, Mockito.times(0)).setFilter(expression) + lineManager.setFilter(expression) + Mockito.verify(lineLayer, Mockito.times(1)).setFilter(expression) + Mockito.`when`(lineLayer.filter).thenReturn(expression) + Assert.assertEquals(expression, lineManager.filter) + Assert.assertEquals(expression, lineManager.layerFilter) + } + + @Test + fun testClickListener() { + val listener = object : OnLineClickListener { + override fun onAnnotationClick(t: Line): Boolean = false + } + lineManager = LineManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Assert.assertTrue(lineManager.getClickListeners().isEmpty()) + lineManager.addClickListener(listener) + Assert.assertTrue(lineManager.getClickListeners().contains(listener)) + lineManager.removeClickListener(listener) + Assert.assertTrue(lineManager.getClickListeners().isEmpty()) + } + + @Test + fun testLongClickListener() { + val listener = object : OnLineLongClickListener { + override fun onAnnotationLongClick(t: Line): Boolean = false + } + lineManager = LineManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Assert.assertTrue(lineManager.getLongClickListeners().isEmpty()) + lineManager.addLongClickListener(listener) + Assert.assertTrue(lineManager.getLongClickListeners().contains(listener)) + lineManager.removeLongClickListener(listener) + Assert.assertTrue(lineManager.getLongClickListeners().isEmpty()) + } + + @Test + fun testDragListener() { + val listener = object : OnLineDragListener { + override fun onAnnotationDragStarted(annotation: Line) = Unit + override fun onAnnotationDrag(annotation: Line) = Unit + override fun onAnnotationDragFinished(annotation: Line) = Unit + } + lineManager = LineManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Assert.assertTrue(lineManager.getDragListeners().isEmpty()) + lineManager.addDragListener(listener) + Assert.assertTrue(lineManager.getDragListeners().contains(listener)) + lineManager.removeDragListener(listener) + Assert.assertTrue(lineManager.getDragListeners().isEmpty()) + } + + @Test + fun testCustomData() { + lineManager = LineManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + val latLngs = listOf( + LatLng(), + LatLng(1.0, 1.0) + ) + val line = Line(latLngs).apply { + data = JsonPrimitive("hello") + } + lineManager.add(line) + Assert.assertEquals(JsonPrimitive("hello"), line.data) + } + + @Test + fun testClearAll() { + lineManager = LineManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + val latLngs = listOf( + LatLng(), + LatLng(1.0, 1.0) + ) + val line = Line(latLngs) + lineManager.add(line) + Assert.assertEquals(1, lineManager.annotations.size) + lineManager.deleteAll() + Assert.assertEquals(0, lineManager.annotations.size) + } +} From d5e61c13f32d8801db78b72fb5c3a4a22d156a46 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Tue, 3 Oct 2023 18:20:25 +0200 Subject: [PATCH 04/10] Implement new Fill class --- .../annotations/AnnotationContainer.kt | 5 + .../maplibre/android/annotations/Circle.kt | 1 + .../maplibre/android/annotations/Fill.java | 312 ---- .../org/maplibre/android/annotations/Fill.kt | 95 ++ .../android/annotations/FillManager.kt | 383 ++--- .../android/annotations/FillOptions.java | 295 ---- .../org/maplibre/android/annotations/Line.kt | 1 + .../annotations/OnAnnotationClickListener.kt | 4 + .../annotations/OnAnnotationDragListener.kt | 5 + .../OnAnnotationLongClickListener.kt | 5 + .../annotations/OnFillClickListener.kt | 6 - .../android/annotations/OnFillDragListener.kt | 6 - .../annotations/OnFillLongClickListener.kt | 6 - .../android/annotations/data/Defaults.kt | 6 + .../android/annotations/CircleManagerTest.kt | 1 - .../android/annotations/FillManagerTest.kt | 1385 ++++++++--------- 16 files changed, 935 insertions(+), 1581 deletions(-) delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Fill.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Fill.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/FillOptions.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnFillClickListener.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnFillDragListener.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnFillLongClickListener.kt diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/AnnotationContainer.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/AnnotationContainer.kt index 774512a3ca3..eefd4510199 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/AnnotationContainer.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/AnnotationContainer.kt @@ -30,6 +30,7 @@ class KAnnotationContainer( addToManager(annotation) if (annotation is Symbol) annotation.icon?.let { style?.addImage(it.image.toString(), it.image) } if (annotation is Line) annotation.pattern?.let { style?.addImage(it.toString(), it) } + if (annotation is Fill) annotation.pattern?.let { style?.addImage(it.toString(), it) } } @UiThread @@ -48,6 +49,7 @@ class KAnnotationContainer( managers[annotation.key()]?.updateSource() if (annotation is Symbol) annotation.icon?.let { style?.addImage(it.image.toString(), it.image) } if (annotation is Line) annotation.pattern?.let { style?.addImage(it.toString(), it) } + if (annotation is Fill) annotation.pattern?.let { style?.addImage(it.toString(), it) } } private fun addToManager(annotation: KAnnotation<*>) = @@ -56,6 +58,7 @@ class KAnnotationContainer( is Symbol -> (manager as SymbolManager).add(annotation) is Circle -> (manager as CircleManager).add(annotation) is Line -> (manager as LineManager).add(annotation) + is Fill -> (manager as FillManager).add(annotation) } } @@ -69,6 +72,7 @@ class KAnnotationContainer( is Symbol -> (manager as SymbolManager).delete(annotation) is Circle -> (manager as CircleManager).delete(annotation) is Line -> (manager as LineManager).delete(annotation) + is Fill -> (manager as FillManager).delete(annotation) } } @@ -100,6 +104,7 @@ class KAnnotationContainer( Symbol::class -> SymbolManager(mapView, mapLibreMap, it) Circle::class -> CircleManager(mapView, mapLibreMap, it) Line::class -> LineManager(mapView, mapLibreMap, it) + Fill::class -> FillManager(mapView, mapLibreMap, it) else -> throw IllegalArgumentException( "Impossible key! This should never occur because KAnnotation is a sealed class." ) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Circle.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Circle.kt index d89c6ba8ce4..5fea5e6bbf7 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Circle.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Circle.kt @@ -55,6 +55,7 @@ class Circle( override val dataDrivenProperties: List get() = listOf( + PROPERTY_IS_DRAGGABLE to draggable default Defaults.DRAGGABLE, PROPERTY_CIRCLE_SORT_KEY to zLayer default Defaults.Z_LAYER, PROPERTY_IS_DRAGGABLE to draggable default Defaults.DRAGGABLE, PROPERTY_CIRCLE_RADIUS to radius default Defaults.CIRCLE_RADIUS, diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Fill.java b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Fill.java deleted file mode 100644 index 101f24bba66..00000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Fill.java +++ /dev/null @@ -1,312 +0,0 @@ -//package org.maplibre.android.annotations; -// -//import static org.maplibre.android.constants.GeometryConstants.MAX_MERCATOR_LATITUDE; -//import static org.maplibre.android.constants.GeometryConstants.MIN_MERCATOR_LATITUDE; -// -//import android.graphics.PointF; -// -//import androidx.annotation.ColorInt; -//import androidx.annotation.NonNull; -//import androidx.annotation.Nullable; -//import androidx.annotation.UiThread; -// -//import com.google.gson.JsonNull; -//import com.google.gson.JsonObject; -//import com.mapbox.android.gestures.MoveDistancesObject; -//import com.mapbox.geojson.Geometry; -//import com.mapbox.geojson.Point; -//import com.mapbox.geojson.Polygon; -// -//import org.maplibre.android.geometry.LatLng; -//import org.maplibre.android.maps.Projection; -//import org.maplibre.android.style.layers.PropertyFactory; -//import org.maplibre.android.utils.ColorUtils; -// -//import java.util.ArrayList; -//import java.util.List; -// -//@UiThread -//public class Fill extends AbstractAnnotation { -// -// private final AnnotationManager annotationManager; -// -// /** -// * Create a fill. -// * -// * @param id the id of the fill -// * @param jsonObject the features of the annotation -// * @param geometry the geometry of the annotation -// */ -// Fill(long id, AnnotationManager annotationManager, JsonObject jsonObject, Polygon geometry) { -// super(id, jsonObject, geometry); -// this.annotationManager = annotationManager; -// } -// -// @Override -// void setUsedDataDrivenProperties() { -// if (!(jsonObject.get(FillOptions.PROPERTY_FILL_OPACITY) instanceof JsonNull)) { -// annotationManager.enableDataDrivenProperty(FillOptions.PROPERTY_FILL_OPACITY); -// } -// if (!(jsonObject.get(FillOptions.PROPERTY_FILL_COLOR) instanceof JsonNull)) { -// annotationManager.enableDataDrivenProperty(FillOptions.PROPERTY_FILL_COLOR); -// } -// if (!(jsonObject.get(FillOptions.PROPERTY_FILL_OUTLINE_COLOR) instanceof JsonNull)) { -// annotationManager.enableDataDrivenProperty(FillOptions.PROPERTY_FILL_OUTLINE_COLOR); -// } -// if (!(jsonObject.get(FillOptions.PROPERTY_FILL_PATTERN) instanceof JsonNull)) { -// annotationManager.enableDataDrivenProperty(FillOptions.PROPERTY_FILL_PATTERN); -// } -// } -// -// /** -// * Set a list of lists of LatLng for the fill, which represents the locations of the fill on the map -// *

-// * To update the fill on the map use {@link FillManager#update(AbstractAnnotation)}. -// *

-// * -// * @param latLngs a list of a lists of the locations of the polygon in a latitude and longitude pairs -// */ -// public void setLatLngs(List> latLngs) { -// List> points = new ArrayList<>(); -// for (List innerLatLngs : latLngs) { -// List innerList = new ArrayList<>(); -// for (LatLng latLng : innerLatLngs) { -// innerList.add(Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude())); -// } -// points.add(innerList); -// } -// geometry = Polygon.fromLngLats(points); -// } -// -// /** -// * Get a list of lists of LatLng for the fill, which represents the locations of the fill on the map -// * -// * @return a list of a lists of the locations of the polygon in a latitude and longitude pairs -// */ -// @NonNull -// public List> getLatLngs() { -// Polygon polygon = (Polygon) geometry; -// List> latLngs = new ArrayList<>(); -// List> coordinates = polygon.coordinates(); -// if (coordinates != null) { -// for (List innerPoints : coordinates) { -// List innerList = new ArrayList<>(); -// for (Point point : innerPoints) { -// innerList.add(new LatLng(point.latitude(), point.longitude())); -// } -// latLngs.add(innerList); -// } -// } -// return latLngs; -// } -// -// // Property accessors -// -// /** -// * Get the FillOpacity property -// *

-// * The opacity of the entire fill layer. In contrast to the {@link PropertyFactory#fillColor}, this value will also -// * affect the 1px stroke around the fill, if the stroke is used. -// *

-// * -// * @return property wrapper value around Float -// */ -// public Float getFillOpacity() { -// return jsonObject.get(FillOptions.PROPERTY_FILL_OPACITY).getAsFloat(); -// } -// -// /** -// * Set the FillOpacity property -// *

-// * The opacity of the entire fill layer. In contrast to the {@link PropertyFactory#fillColor}, this value will also -// * affect the 1px stroke around the fill, if the stroke is used. -// *

-// *

-// * To update the fill on the map use {@link FillManager#update(AbstractAnnotation)}. -// *

-// * -// * @param value constant property value for Float -// */ -// public void setFillOpacity(Float value) { -// jsonObject.addProperty(FillOptions.PROPERTY_FILL_OPACITY, value); -// } -// -// /** -// * Get the FillColor property -// *

-// * The color of the filled part of this layer. This color can be specified as `rgba` with an alpha component and the -// * color's opacity will not affect the opacity of the 1px stroke, if it is used. -// *

-// * -// * @return color value for String -// */ -// @ColorInt -// public int getFillColorAsInt() { -// return ColorUtils.rgbaToColor(jsonObject.get(FillOptions.PROPERTY_FILL_COLOR).getAsString()); -// } -// -// /** -// * Get the FillColor property -// *

-// * The color of the filled part of this layer. This color can be specified as `rgba` with an alpha component and the -// * color's opacity will not affect the opacity of the 1px stroke, if it is used. -// *

-// * -// * @return color value for String -// */ -// public String getFillColor() { -// return jsonObject.get(FillOptions.PROPERTY_FILL_COLOR).getAsString(); -// } -// -// /** -// * Set the FillColor property -// *

-// * The color of the filled part of this layer. This color can be specified as `rgba` with an alpha component and the -// * color's opacity will not affect the opacity of the 1px stroke, if it is used. -// *

-// *

-// * To update the fill on the map use {@link FillManager#update(AbstractAnnotation)}. -// *

-// * -// * @param color value for String -// */ -// public void setFillColor(@ColorInt int color) { -// jsonObject.addProperty(FillOptions.PROPERTY_FILL_COLOR, ColorUtils.colorToRgbaString(color)); -// } -// -// /** -// * Set the FillColor property -// *

-// * The color of the filled part of this layer. This color can be specified as `rgba` with an alpha component and the -// * color's opacity will not affect the opacity of the 1px stroke, if it is used. -// *

-// *

-// * To update the fill on the map use {@link FillManager#update(AbstractAnnotation)}. -// *

-// * -// * @param color value for String -// */ -// public void setFillColor(@NonNull String color) { -// jsonObject.addProperty("fill-color", color); -// } -// -// /** -// * Get the FillOutlineColor property -// *

-// * The outline color of the fill. Matches the value of {@link PropertyFactory#fillColor} if unspecified. -// *

-// * -// * @return color value for String -// */ -// @ColorInt -// public int getFillOutlineColorAsInt() { -// return ColorUtils.rgbaToColor(jsonObject.get(FillOptions.PROPERTY_FILL_OUTLINE_COLOR).getAsString()); -// } -// -// /** -// * Get the FillOutlineColor property -// *

-// * The outline color of the fill. Matches the value of {@link PropertyFactory#fillColor} if unspecified. -// *

-// * -// * @return color value for String -// */ -// public String getFillOutlineColor() { -// return jsonObject.get(FillOptions.PROPERTY_FILL_OUTLINE_COLOR).getAsString(); -// } -// -// /** -// * Set the FillOutlineColor property -// *

-// * The outline color of the fill. Matches the value of {@link PropertyFactory#fillColor} if unspecified. -// *

-// *

-// * To update the fill on the map use {@link FillManager#update(AbstractAnnotation)}. -// *

-// * -// * @param color value for String -// */ -// public void setFillOutlineColor(@ColorInt int color) { -// jsonObject.addProperty(FillOptions.PROPERTY_FILL_OUTLINE_COLOR, ColorUtils.colorToRgbaString(color)); -// } -// -// /** -// * Set the FillOutlineColor property -// *

-// * The outline color of the fill. Matches the value of {@link PropertyFactory#fillColor} if unspecified. -// *

-// *

-// * To update the fill on the map use {@link FillManager#update(AbstractAnnotation)}. -// *

-// * -// * @param color value for String -// */ -// public void setFillOutlineColor(@NonNull String color) { -// jsonObject.addProperty("fill-outline-color", color); -// } -// -// /** -// * Get the FillPattern property -// *

-// * Name of image in sprite to use for drawing image fills. For seamless patterns, image width and height must be a -// * factor of two (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be evaluated only at integer zoom -// * levels. -// *

-// * -// * @return property wrapper value around String -// */ -// public String getFillPattern() { -// return jsonObject.get(FillOptions.PROPERTY_FILL_PATTERN).getAsString(); -// } -// -// /** -// * Set the FillPattern property -// *

-// * Name of image in sprite to use for drawing image fills. For seamless patterns, image width and height must be a -// * factor of two (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be evaluated only at integer zoom -// * levels. -// *

-// *

-// * To update the fill on the map use {@link FillManager#update(AbstractAnnotation)}. -// *

-// * -// * @param value constant property value for String -// */ -// public void setFillPattern(String value) { -// jsonObject.addProperty(FillOptions.PROPERTY_FILL_PATTERN, value); -// } -// -// @Override -// @Nullable -// Geometry getOffsetGeometry(@NonNull Projection projection, @NonNull MoveDistancesObject moveDistancesObject, -// float touchAreaShiftX, float touchAreaShiftY) { -// List> originalPoints = geometry.coordinates(); -// if (originalPoints != null) { -// List> resultingPoints = new ArrayList<>(originalPoints.size()); -// for (List points : originalPoints) { -// List innerPoints = new ArrayList<>(); -// for (Point jsonPoint : points) { -// PointF pointF = projection.toScreenLocation(new LatLng(jsonPoint.latitude(), jsonPoint.longitude())); -// pointF.x -= moveDistancesObject.getDistanceXSinceLast(); -// pointF.y -= moveDistancesObject.getDistanceYSinceLast(); -// -// LatLng latLng = projection.fromScreenLocation(pointF); -// if (latLng.getLatitude() > MAX_MERCATOR_LATITUDE || latLng.getLatitude() < MIN_MERCATOR_LATITUDE) { -// return null; -// } -// innerPoints.add(Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude())); -// } -// resultingPoints.add(innerPoints); -// } -// -// return Polygon.fromLngLats(resultingPoints); -// } -// -// return null; -// } -// -// @Override -// String getName() { -// return "Fill"; -// } -//} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Fill.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Fill.kt new file mode 100644 index 00000000000..d4ffe41cd7c --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Fill.kt @@ -0,0 +1,95 @@ +package org.maplibre.android.annotations + +import android.graphics.Bitmap +import androidx.annotation.ColorInt +import com.mapbox.android.gestures.MoveDistancesObject +import com.mapbox.geojson.Geometry +import com.mapbox.geojson.Point +import com.mapbox.geojson.Polygon +import org.maplibre.android.annotations.data.Defaults +import org.maplibre.android.constants.GeometryConstants.MAX_MERCATOR_LATITUDE +import org.maplibre.android.constants.GeometryConstants.MIN_MERCATOR_LATITUDE +import org.maplibre.android.geometry.LatLng +import org.maplibre.android.maps.Projection + +class Fill( + paths: List>, + opacity: Float = Defaults.FILL_OPACITY, + @ColorInt color: Int = Defaults.FILL_COLOR, + @ColorInt outlineColor: Int? = Defaults.FILL_OUTLINE_COLOR, + pattern: Bitmap? = Defaults.FILL_PATTERN, + // TODO: NDD properties antialias and translate +) : KAnnotation() { + + var paths: List> = paths + set(value) { + field = value + Polygon.fromLngLats(value.map { it.map { Point.fromLngLat(it.longitude, it.latitude) } }) + updateThis() + } + var opacity: Float = opacity + set(value) { + field = value + updateThis() + } + @ColorInt var color: Int = color + set(value) { + field = value + updateThis() + } + @ColorInt var outlineColor: Int? = outlineColor + set(value) { + field = value + updateThis() + } + var pattern: Bitmap? = pattern + set(value) { + field = value + updateThis() + } + + override var geometry: Polygon = Polygon.fromLngLats(paths.map { it.map { Point.fromLngLat(it.longitude, it.latitude) } }) + override val dataDrivenProperties: List + get() = listOf( + PROPERTY_IS_DRAGGABLE to draggable default Defaults.DRAGGABLE, + PROPERTY_FILL_OPACITY to opacity default Defaults.FILL_OPACITY, + PROPERTY_FILL_COLOR to color.asColorString() default Defaults.FILL_COLOR.asColorString(), + PROPERTY_FILL_OUTLINE_COLOR to outlineColor?.asColorString() + default Defaults.FILL_OUTLINE_COLOR?.asColorString(), + PROPERTY_FILL_PATTERN to pattern default Defaults.FILL_PATTERN + ) + + override fun getOffsetGeometry( + projection: Projection, + moveDistancesObject: MoveDistancesObject, + touchAreaShiftX: Float, + touchAreaShiftY: Float + ): Geometry? = + geometry.coordinates().map { innerList -> + innerList.map { + val pointF = projection.toScreenLocation(LatLng(it.latitude(), it.longitude())).apply { + x -= moveDistancesObject.distanceXSinceLast + y -= moveDistancesObject.distanceYSinceLast + } + + val latLng = projection.fromScreenLocation(pointF) + if (latLng.latitude > MAX_MERCATOR_LATITUDE || latLng.latitude < MIN_MERCATOR_LATITUDE) { + return null + } + Point.fromLngLat(latLng.longitude, latLng.latitude) + } + }.let { Polygon.fromLngLats(it) } + + init { + if (opacity > 1f || opacity < 0f) { + throw IllegalArgumentException("Opacity must be between 0 and 1 (inclusive)") + } + } + + companion object { + const val PROPERTY_FILL_OPACITY = "fill-opacity" + const val PROPERTY_FILL_COLOR = "fill-color" + const val PROPERTY_FILL_OUTLINE_COLOR = "fill-outline-color" + const val PROPERTY_FILL_PATTERN = "fill-pattern" + } +} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/FillManager.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/FillManager.kt index 6623b3c293a..0e1833cbdb2 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/FillManager.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/FillManager.kt @@ -1,217 +1,166 @@ -//package org.maplibre.android.annotations -// -//import androidx.annotation.UiThread -//import com.mapbox.geojson.FeatureCollection -//import org.maplibre.android.maps.MapLibreMap -//import org.maplibre.android.maps.MapView -//import org.maplibre.android.maps.Style -//import org.maplibre.android.style.expressions.Expression -//import org.maplibre.android.style.layers.FillLayer -//import org.maplibre.android.style.layers.PropertyFactory -//import org.maplibre.android.style.layers.PropertyValue -//import org.maplibre.android.style.sources.GeoJsonOptions -// -///** -// * The fill manager allows to add fills to a map. -// * -// * @param maplibreMap the map object to add fills to -// * @param style a valid a fully loaded style object -// * @param belowLayerId the id of the layer above the fill layer -// * @param aboveLayerId the id of the layer below the fill layer -// * @param geoJsonOptions options for the internal source -// */ -//class FillManager @UiThread internal constructor( -// mapView: MapView, -// maplibreMap: MapLibreMap, -// style: Style, -// coreElementProvider: CoreElementProvider = FillElementProvider(), -// belowLayerId: String? = null, -// aboveLayerId: String? = null, -// geoJsonOptions: GeoJsonOptions? = null, -// draggableAnnotationController: DraggableAnnotationController = DraggableAnnotationController.getInstance( -// mapView, -// maplibreMap -// ) -//) : AnnotationManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// draggableAnnotationController, -// belowLayerId, -// aboveLayerId, -// geoJsonOptions -//) { -// -// @JvmOverloads -// constructor( -// mapView: MapView, -// maplibreMap: MapLibreMap, -// style: Style, -// belowLayerId: String? = null, -// aboveLayerId: String? = null, -// geoJsonOptions: GeoJsonOptions? = null -// ) : this( -// mapView, -// maplibreMap, -// style, -// FillElementProvider(), -// belowLayerId, -// aboveLayerId, -// geoJsonOptions -// ) -// -// override fun initializeDataDrivenPropertyMap() = listOf( -// FillOptions.PROPERTY_FILL_OPACITY, -// FillOptions.PROPERTY_FILL_COLOR, -// FillOptions.PROPERTY_FILL_OUTLINE_COLOR, -// FillOptions.PROPERTY_FILL_PATTERN -// ).associateWith { false }.let { dataDrivenPropertyUsageMap.putAll(it) } -// -// override fun setDataDrivenPropertyIsUsed(property: String) { -// when (property) { -// FillOptions.PROPERTY_FILL_OPACITY -> layer.setProperties( -// PropertyFactory.fillOpacity( -// Expression.get(FillOptions.PROPERTY_FILL_OPACITY) -// ) -// ) -// -// FillOptions.PROPERTY_FILL_COLOR -> layer.setProperties( -// PropertyFactory.fillColor( -// Expression.get(FillOptions.PROPERTY_FILL_COLOR) -// ) -// ) -// -// FillOptions.PROPERTY_FILL_OUTLINE_COLOR -> layer.setProperties( -// PropertyFactory.fillOutlineColor( -// Expression.get(FillOptions.PROPERTY_FILL_OUTLINE_COLOR) -// ) -// ) -// -// FillOptions.PROPERTY_FILL_PATTERN -> layer.setProperties( -// PropertyFactory.fillPattern( -// Expression.get(FillOptions.PROPERTY_FILL_PATTERN) -// ) -// ) -// } -// } -// -// /** -// * Create a list of fills on the map. -// * -// * -// * Fills are going to be created only for features with a matching geometry. -// * -// * -// * All supported properties are:

-// * FillOptions.PROPERTY_FILL_OPACITY - Float

-// * FillOptions.PROPERTY_FILL_COLOR - String

-// * FillOptions.PROPERTY_FILL_OUTLINE_COLOR - String

-// * FillOptions.PROPERTY_FILL_PATTERN - String

-// * Learn more about above properties in the [Style specification](https://maplibre.org/maplibre-style-spec/). -// * -// * -// * Out of spec properties:

-// * "is-draggable" - Boolean, true if the fill should be draggable, false otherwise -// * -// * @param json the GeoJSON defining the list of fills to build -// * @return the list of built fills -// */ -// @UiThread -// fun create(json: String): List = create(FeatureCollection.fromJson(json)) -// -// /** -// * Create a list of fills on the map. -// * -// * -// * Fills are going to be created only for features with a matching geometry. -// * -// * -// * All supported properties are:

-// * FillOptions.PROPERTY_FILL_OPACITY - Float

-// * FillOptions.PROPERTY_FILL_COLOR - String

-// * FillOptions.PROPERTY_FILL_OUTLINE_COLOR - String

-// * FillOptions.PROPERTY_FILL_PATTERN - String

-// * Learn more about above properties in the [Style specification](https://maplibre.org/maplibre-style-spec/). -// * -// * -// * Out of spec properties:

-// * "is-draggable" - Boolean, true if the fill should be draggable, false otherwise -// * -// * @param featureCollection the featureCollection defining the list of fills to build -// * @return the list of built fills -// */ -// @UiThread -// fun create(featureCollection: FeatureCollection): List = -// featureCollection.features()?.mapNotNull { FillOptions.fromFeature(it) } -// .let { create(it ?: emptyList()) } -// -// /** -// * Key of the id of the annotation -// */ -// override val annotationIdKey: String -// /** -// * Get the of the annotation. -// * -// * @return the key of the id of the annotation -// */ -// get() = Fill.ID_KEY -// -// // Property accessors -// /** -// * Whether or not the fill should be antialiased. -// */ -// var fillAntialias: Boolean? -// get() = layer.fillAntialias.value -// set(value) { -// val propertyValue: PropertyValue<*> = PropertyFactory.fillAntialias(value) -// constantPropertyUsageMap[PROPERTY_FILL_ANTIALIAS] = propertyValue -// layer.setProperties(propertyValue) -// } -// -// /** -// * The geometry's offset. Values are [x, y] where negatives indicate left and up, respectively. -// */ -// var fillTranslate: Array? -// get() = layer.fillTranslate.value -// set(value) { -// val propertyValue: PropertyValue<*> = PropertyFactory.fillTranslate(value) -// constantPropertyUsageMap[PROPERTY_FILL_TRANSLATE] = propertyValue -// layer.setProperties(propertyValue) -// } -// -// /** -// * Controls the frame of reference for [PropertyFactory.fillTranslate]. -// */ -// var fillTranslateAnchor: String? -// get() = layer.fillTranslateAnchor.value -// set(value) { -// val propertyValue: PropertyValue<*> = PropertyFactory.fillTranslateAnchor(value) -// constantPropertyUsageMap[PROPERTY_FILL_TRANSLATE_ANCHOR] = propertyValue -// layer.setProperties(propertyValue) -// } -// -// /** -// * Set filter on the managed fills. -// * -// * @param expression expression -// */ -// override fun setFilter(expression: Expression) { -// layerFilter = expression -// layer.setFilter(expression) -// } -// -// val filter: Expression? -// /** -// * Get filter of the managed fills. -// */ -// get() { -// return layer.filter -// } -// -// companion object { -// private const val PROPERTY_FILL_ANTIALIAS: String = "fill-antialias" -// private const val PROPERTY_FILL_TRANSLATE: String = "fill-translate" -// private const val PROPERTY_FILL_TRANSLATE_ANCHOR: String = "fill-translate-anchor" -// } -//} +package org.maplibre.android.annotations + +import androidx.annotation.UiThread +import org.maplibre.android.maps.MapLibreMap +import org.maplibre.android.maps.MapView +import org.maplibre.android.maps.Style +import org.maplibre.android.style.expressions.Expression +import org.maplibre.android.style.layers.FillLayer +import org.maplibre.android.style.layers.PropertyFactory +import org.maplibre.android.style.layers.PropertyValue +import org.maplibre.android.style.sources.GeoJsonOptions + +/** + * The fill manager allows to add fills to a map. + * + * @param maplibreMap the map object to add fills to + * @param style a valid a fully loaded style object + * @param belowLayerId the id of the layer above the fill layer + * @param aboveLayerId the id of the layer below the fill layer + * @param geoJsonOptions options for the internal source + */ +class FillManager @UiThread internal constructor( + mapView: MapView, + maplibreMap: MapLibreMap, + style: Style, + coreElementProvider: CoreElementProvider = FillElementProvider(), + belowLayerId: String? = null, + aboveLayerId: String? = null, + geoJsonOptions: GeoJsonOptions? = null, + draggableAnnotationController: DraggableAnnotationController = DraggableAnnotationController.getInstance( + mapView, + maplibreMap + ) +) : AnnotationManager( + mapView, + maplibreMap, + style, + coreElementProvider, + draggableAnnotationController, + belowLayerId, + aboveLayerId, + geoJsonOptions +) { + + override fun generateDataDrivenPropertyExpression(property: String): PropertyValue = when (property) { + Fill.PROPERTY_FILL_OPACITY -> PropertyFactory.fillOpacity( + Expression.get(Fill.PROPERTY_FILL_OPACITY) + ) + + Fill.PROPERTY_FILL_COLOR -> PropertyFactory.fillColor( + Expression.get(Fill.PROPERTY_FILL_COLOR) + ) + + Fill.PROPERTY_FILL_OUTLINE_COLOR -> PropertyFactory.fillOutlineColor( + Expression.get(Fill.PROPERTY_FILL_OUTLINE_COLOR) + ) + + Fill.PROPERTY_FILL_PATTERN -> PropertyFactory.fillPattern( + Expression.get(Fill.PROPERTY_FILL_PATTERN) + ) + + else -> throw IllegalArgumentException( + "$property is not a valid data-driven property for a fill." + ) + } + + @JvmOverloads + constructor( + mapView: MapView, + maplibreMap: MapLibreMap, + style: Style, + belowLayerId: String? = null, + aboveLayerId: String? = null, + geoJsonOptions: GeoJsonOptions? = null + ) : this( + mapView, + maplibreMap, + style, + FillElementProvider(), + belowLayerId, + aboveLayerId, + geoJsonOptions + ) + + override fun addDragListener(d: OnFillDragListener) { + super.addDragListener(d) + } + + override fun removeDragListener(d: OnFillDragListener) { + super.removeDragListener(d) + } + + override fun addClickListener(u: OnFillClickListener) { + super.addClickListener(u) + } + + override fun removeClickListener(u: OnFillClickListener) { + super.removeClickListener(u) + } + + override fun addLongClickListener(v: OnFillLongClickListener) { + super.addLongClickListener(v) + } + + override fun removeLongClickListener(v: OnFillLongClickListener) { + super.removeLongClickListener(v) + } + + // Property accessors + /** + * Whether or not the fill should be antialiased. + */ + var fillAntialias: Boolean? + get() = layer.fillAntialias.value + set(value) { + val propertyValue: PropertyValue<*> = PropertyFactory.fillAntialias(value) + constantPropertyUsageMap[PROPERTY_FILL_ANTIALIAS] = propertyValue + layer.setProperties(propertyValue) + } + + /** + * The geometry's offset. Values are [x, y] where negatives indicate left and up, respectively. + */ + var fillTranslate: Array? + get() = layer.fillTranslate.value + set(value) { + val propertyValue: PropertyValue<*> = PropertyFactory.fillTranslate(value) + constantPropertyUsageMap[PROPERTY_FILL_TRANSLATE] = propertyValue + layer.setProperties(propertyValue) + } + + /** + * Controls the frame of reference for [PropertyFactory.fillTranslate]. + */ + var fillTranslateAnchor: String? + get() = layer.fillTranslateAnchor.value + set(value) { + val propertyValue: PropertyValue<*> = PropertyFactory.fillTranslateAnchor(value) + constantPropertyUsageMap[PROPERTY_FILL_TRANSLATE_ANCHOR] = propertyValue + layer.setProperties(propertyValue) + } + + /** + * Set filter on the managed fills. + * + * @param expression expression + */ + override fun setFilter(expression: Expression) { + layerFilter = expression + layer.setFilter(expression) + } + + val filter: Expression? + /** + * Get filter of the managed fills. + */ + get() { + return layer.filter + } + + companion object { + private const val PROPERTY_FILL_ANTIALIAS: String = "fill-antialias" + private const val PROPERTY_FILL_TRANSLATE: String = "fill-translate" + private const val PROPERTY_FILL_TRANSLATE_ANCHOR: String = "fill-translate-anchor" + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/FillOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/FillOptions.java deleted file mode 100644 index 83549e95dd1..00000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/FillOptions.java +++ /dev/null @@ -1,295 +0,0 @@ -//package org.maplibre.android.annotations; -// -//import androidx.annotation.NonNull; -//import androidx.annotation.Nullable; -// -//import com.google.gson.JsonElement; -//import com.google.gson.JsonObject; -//import com.mapbox.geojson.Feature; -//import com.mapbox.geojson.Point; -//import com.mapbox.geojson.Polygon; -// -//import org.maplibre.android.geometry.LatLng; -//import org.maplibre.android.style.layers.PropertyFactory; -// -//import java.util.ArrayList; -//import java.util.List; -// -///** -// * Builder class from which a fill is created. -// */ -//public class FillOptions extends Options { -// -// private boolean isDraggable; -// private JsonElement data; -// private Polygon geometry; -// private Float fillOpacity; -// private String fillColor; -// private String fillOutlineColor; -// private String fillPattern; -// -// static final String PROPERTY_FILL_OPACITY = "fill-opacity"; -// static final String PROPERTY_FILL_COLOR = "fill-color"; -// static final String PROPERTY_FILL_OUTLINE_COLOR = "fill-outline-color"; -// static final String PROPERTY_FILL_PATTERN = "fill-pattern"; -// private static final String PROPERTY_IS_DRAGGABLE = "is-draggable"; -// -// /** -// * Set fill-opacity to initialise the fill with. -// *

-// * The opacity of the entire fill layer. In contrast to the {@link PropertyFactory#fillColor}, this value will also -// * affect the 1px stroke around the fill, if the stroke is used. -// *

-// * -// * @param fillOpacity the fill-opacity value -// * @return this -// */ -// public FillOptions withFillOpacity(Float fillOpacity) { -// this.fillOpacity = fillOpacity; -// return this; -// } -// -// /** -// * Get the current configured fill-opacity for the fill -// *

-// * The opacity of the entire fill layer. In contrast to the {@link PropertyFactory#fillColor}, this value will also -// * affect the 1px stroke around the fill, if the stroke is used. -// *

-// * -// * @return fillOpacity value -// */ -// public Float getFillOpacity() { -// return fillOpacity; -// } -// -// /** -// * Set fill-color to initialise the fill with. -// *

-// * The color of the filled part of this layer. This color can be specified as `rgba` with an alpha component and the -// * color's opacity will not affect the opacity of the 1px stroke, if it is used. -// *

-// * -// * @param fillColor the fill-color value -// * @return this -// */ -// public FillOptions withFillColor(String fillColor) { -// this.fillColor = fillColor; -// return this; -// } -// -// /** -// * Get the current configured fill-color for the fill -// *

-// * The color of the filled part of this layer. This color can be specified as `rgba` with an alpha component and the -// * color's opacity will not affect the opacity of the 1px stroke, if it is used. -// *

-// * -// * @return fillColor value -// */ -// public String getFillColor() { -// return fillColor; -// } -// -// /** -// * Set fill-outline-color to initialise the fill with. -// *

-// * The outline color of the fill. Matches the value of {@link PropertyFactory#fillColor} if unspecified. -// *

-// * -// * @param fillOutlineColor the fill-outline-color value -// * @return this -// */ -// public FillOptions withFillOutlineColor(String fillOutlineColor) { -// this.fillOutlineColor = fillOutlineColor; -// return this; -// } -// -// /** -// * Get the current configured fill-outline-color for the fill -// *

-// * The outline color of the fill. Matches the value of {@link PropertyFactory#fillColor} if unspecified. -// *

-// * -// * @return fillOutlineColor value -// */ -// public String getFillOutlineColor() { -// return fillOutlineColor; -// } -// -// /** -// * Set fill-pattern to initialise the fill with. -// *

-// * Name of image in sprite to use for drawing image fills. For seamless patterns, image width and height must be a -// * factor of two (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be evaluated only at integer zoom -// * levels. -// *

-// * -// * @param fillPattern the fill-pattern value -// * @return this -// */ -// public FillOptions withFillPattern(String fillPattern) { -// this.fillPattern = fillPattern; -// return this; -// } -// -// /** -// * Get the current configured fill-pattern for the fill -// *

-// * Name of image in sprite to use for drawing image fills. For seamless patterns, image width and height must be a -// * factor of two (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be evaluated only at integer zoom -// * levels. -// *

-// * -// * @return fillPattern value -// */ -// public String getFillPattern() { -// return fillPattern; -// } -// -// /** -// * Set a list of lists of LatLng for the fill, which represents the locations of the fill on the map -// * -// * @param latLngs a list of a lists of the locations of the line in a longitude and latitude pairs -// * @return this -// */ -// public FillOptions withLatLngs(List> latLngs) { -// List> points = new ArrayList<>(); -// for (List innerLatLngs : latLngs) { -// List innerList = new ArrayList<>(); -// for (LatLng latLng : innerLatLngs) { -// innerList.add(Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude())); -// } -// points.add(innerList); -// } -// geometry = Polygon.fromLngLats(points); -// return this; -// } -// -// /** -// * Get a list of lists of LatLng for the fill, which represents the locations of the fill on the map -// * -// * @return a list of a lists of the locations of the line in a longitude and latitude pairs -// */ -// public List> getLatLngs() { -// List> points = new ArrayList<>(); -// if (geometry != null) { -// for (List coordinates : geometry.coordinates()) { -// List innerList = new ArrayList<>(); -// for (Point point : coordinates) { -// innerList.add(new LatLng(point.latitude(), point.longitude())); -// } -// points.add(innerList); -// } -// } -// return points; -// } -// -// /** -// * Set the geometry of the fill, which represents the location of the fill on the map -// * -// * @param geometry the location of the fill -// * @return this -// */ -// public FillOptions withGeometry(Polygon geometry) { -// this.geometry = geometry; -// return this; -// } -// -// /** -// * Get the geometry of the fill, which represents the location of the fill on the map -// * -// * @return the location of the fill -// */ -// public Polygon getGeometry() { -// return geometry; -// } -// -// /** -// * Returns whether this fill is draggable, meaning it can be dragged across the screen when touched and moved. -// * -// * @return draggable when touched -// */ -// public boolean getDraggable() { -// return isDraggable; -// } -// -// /** -// * Set whether this fill should be draggable, meaning it can be dragged across the screen when touched and moved. -// * -// * @param draggable should be draggable -// */ -// public FillOptions withDraggable(boolean draggable) { -// isDraggable = draggable; -// return this; -// } -// -// /** -// * Set the arbitrary json data of the annotation. -// * -// * @param jsonElement the arbitrary json element data -// */ -// public FillOptions withData(@Nullable JsonElement jsonElement) { -// this.data = jsonElement; -// return this; -// } -// -// /** -// * Get the arbitrary json data of the annotation. -// * -// * @return the arbitrary json object data if set, else null -// */ -// @Nullable -// public JsonElement getData() { -// return data; -// } -// -// @Override -// Fill build(long id, AnnotationManager annotationManager) { -// if (geometry == null) { -// throw new RuntimeException("geometry field is required"); -// } -// JsonObject jsonObject = new JsonObject(); -// jsonObject.addProperty(PROPERTY_FILL_OPACITY, fillOpacity); -// jsonObject.addProperty(PROPERTY_FILL_COLOR, fillColor); -// jsonObject.addProperty(PROPERTY_FILL_OUTLINE_COLOR, fillOutlineColor); -// jsonObject.addProperty(PROPERTY_FILL_PATTERN, fillPattern); -// Fill fill = new Fill(id, annotationManager, jsonObject, geometry); -// fill.setDraggable(isDraggable); -// fill.setData(data); -// return fill; -// } -// -// /** -// * Creates FillOptions out of a Feature. -// * -// * @param feature feature to be converted -// */ -// @Nullable -// static FillOptions fromFeature(@NonNull Feature feature) { -// if (feature.geometry() == null) { -// throw new RuntimeException("geometry field is required"); -// } -// if (!(feature.geometry() instanceof Polygon)) { -// return null; -// } -// -// FillOptions options = new FillOptions(); -// options.geometry = (Polygon) feature.geometry(); -// if (feature.hasProperty(PROPERTY_FILL_OPACITY)) { -// options.fillOpacity = feature.getProperty(PROPERTY_FILL_OPACITY).getAsFloat(); -// } -// if (feature.hasProperty(PROPERTY_FILL_COLOR)) { -// options.fillColor = feature.getProperty(PROPERTY_FILL_COLOR).getAsString(); -// } -// if (feature.hasProperty(PROPERTY_FILL_OUTLINE_COLOR)) { -// options.fillOutlineColor = feature.getProperty(PROPERTY_FILL_OUTLINE_COLOR).getAsString(); -// } -// if (feature.hasProperty(PROPERTY_FILL_PATTERN)) { -// options.fillPattern = feature.getProperty(PROPERTY_FILL_PATTERN).getAsString(); -// } -// if (feature.hasProperty(PROPERTY_IS_DRAGGABLE)) { -// options.isDraggable = feature.getProperty(PROPERTY_IS_DRAGGABLE).getAsBoolean(); -// } -// return options; -// } -//} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Line.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Line.kt index 6f22c83e533..865aa33d5be 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Line.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Line.kt @@ -80,6 +80,7 @@ class Line( override val dataDrivenProperties: List get() = listOf( + PROPERTY_IS_DRAGGABLE to draggable default Defaults.DRAGGABLE, PROPERTY_LINE_JOIN to join.toString().lowercase() default Defaults.LINE_JOIN.toString().lowercase(), PROPERTY_LINE_OPACITY to opacity default Defaults.LINE_OPACITY, PROPERTY_LINE_COLOR to color.asColorString() default Defaults.LINE_COLOR, diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationClickListener.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationClickListener.kt index dee35ac8f82..0d78c90063f 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationClickListener.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationClickListener.kt @@ -31,3 +31,7 @@ typealias OnCircleClickListener = OnAnnotationClickListener */ typealias OnLineClickListener = OnAnnotationClickListener +/** + * Interface definition for a callback to be invoked when a fill has been clicked. + */ +typealias OnFillClickListener = OnAnnotationClickListener \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationDragListener.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationDragListener.kt index 5d114a37858..f6cc46df135 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationDragListener.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationDragListener.kt @@ -42,3 +42,8 @@ typealias OnCircleDragListener = OnAnnotationDragListener * Interface definition for a callback to be invoked when a line is dragged. */ typealias OnLineDragListener = OnAnnotationDragListener + +/** + * Interface definition for a callback to be invoked when a fill is dragged. + */ +typealias OnFillDragListener = OnAnnotationDragListener \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationLongClickListener.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationLongClickListener.kt index 4a009bd7382..2a2ef60aa7d 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationLongClickListener.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnAnnotationLongClickListener.kt @@ -30,3 +30,8 @@ typealias OnCircleLongClickListener = OnAnnotationLongClickListener * Interface definition for a callback to be invoked when a line has been long clicked. */ typealias OnLineLongClickListener = OnAnnotationLongClickListener + +/** + * Interface definition for a callback to be invoked when a fill has been long clicked. + */ +typealias OnFillLongClickListener = OnAnnotationLongClickListener \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnFillClickListener.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnFillClickListener.kt deleted file mode 100644 index b759569ed13..00000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnFillClickListener.kt +++ /dev/null @@ -1,6 +0,0 @@ -package org.maplibre.android.annotations - -/** - * Interface definition for a callback to be invoked when a fill has been clicked. - */ -//interface OnFillClickListener : OnAnnotationClickListener diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnFillDragListener.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnFillDragListener.kt deleted file mode 100644 index cdfc944d615..00000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnFillDragListener.kt +++ /dev/null @@ -1,6 +0,0 @@ -package org.maplibre.android.annotations - -/** - * Interface definition for a callback to be invoked when a fill is dragged. - */ -//interface OnFillDragListener : OnAnnotationDragListener diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnFillLongClickListener.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnFillLongClickListener.kt deleted file mode 100644 index d196c67e2f4..00000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/OnFillLongClickListener.kt +++ /dev/null @@ -1,6 +0,0 @@ -package org.maplibre.android.annotations - -/** - * Interface definition for a callback to be invoked when a fill has been long clicked. - */ -//interface OnFillLongClickListener : OnAnnotationLongClickListener diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/data/Defaults.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/data/Defaults.kt index e2fd2f012d9..feafacfc207 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/data/Defaults.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/data/Defaults.kt @@ -69,5 +69,11 @@ class Defaults { val LINE_OFFSET: Float = 0f val LINE_BLUR: Float? = null val LINE_PATTERN: Bitmap? = null + + val FILL_OPACITY: Float = 1f + @ColorInt + val FILL_COLOR: Int = Color.BLACK + val FILL_OUTLINE_COLOR: Int? = null + val FILL_PATTERN: Bitmap? = null } } diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/CircleManagerTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/CircleManagerTest.kt index ef17703e304..dbbd4e58125 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/CircleManagerTest.kt +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/CircleManagerTest.kt @@ -542,7 +542,6 @@ class CircleManagerTest { fun testClickListener() { val listener = object : OnCircleClickListener { override fun onAnnotationClick(t: Circle): Boolean = false - } circleManager = CircleManager( mapView, diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/FillManagerTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/FillManagerTest.kt index 422171cfc65..53126dc916f 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/FillManagerTest.kt +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/FillManagerTest.kt @@ -1,738 +1,647 @@ -//package org.maplibre.android.annotations -// -//import com.google.gson.JsonPrimitive -//import com.mapbox.geojson.Feature -//import com.mapbox.geojson.FeatureCollection -//import com.mapbox.geojson.Geometry -//import com.mapbox.geojson.Point -//import com.mapbox.geojson.Polygon -//import junit.framework.Assert -//import org.junit.Before -//import org.junit.Test -//import org.maplibre.android.geometry.LatLng -//import org.maplibre.android.maps.MapLibreMap -//import org.maplibre.android.maps.MapView -//import org.maplibre.android.maps.Style -//import org.maplibre.android.style.expressions.Expression -//import org.maplibre.android.style.expressions.Expression.get -//import org.maplibre.android.style.layers.FillLayer -//import org.maplibre.android.style.layers.PropertyFactory.fillColor -//import org.maplibre.android.style.layers.PropertyFactory.fillOpacity -//import org.maplibre.android.style.layers.PropertyFactory.fillOutlineColor -//import org.maplibre.android.style.layers.PropertyFactory.fillPattern -//import org.maplibre.android.style.sources.GeoJsonOptions -//import org.maplibre.android.style.sources.GeoJsonSource -//import org.mockito.ArgumentCaptor -//import org.mockito.ArgumentMatchers -//import org.mockito.Mockito -// -//class FillManagerTest { -// private val draggableAnnotationController = Mockito.mock( -// DraggableAnnotationController::class.java -// ) -// private val mapView: MapView = Mockito.mock(MapView::class.java) -// private val maplibreMap: MapLibreMap = Mockito.mock(MapLibreMap::class.java) -// private val style: Style = Mockito.mock(Style::class.java) -// private val geoJsonSource: GeoJsonSource = Mockito.mock(GeoJsonSource::class.java) -// private val optionedGeoJsonSource: GeoJsonSource = Mockito.mock(GeoJsonSource::class.java) -// private val layerId = "annotation_layer" -// private val fillLayer: FillLayer = Mockito.mock(FillLayer::class.java) -// private lateinit var fillManager: FillManager -// private val coreElementProvider: CoreElementProvider = Mockito.mock( -// FillElementProvider::class.java -// ) -// private val geoJsonOptions: GeoJsonOptions = Mockito.mock(GeoJsonOptions::class.java) -// -// @Before -// fun beforeTest() { -// Mockito.`when`(fillLayer.id).thenReturn(layerId) -// Mockito.`when`(coreElementProvider.layer).thenReturn(fillLayer) -// Mockito.`when`(coreElementProvider.getSource(null)).thenReturn(geoJsonSource) -// Mockito.`when`(coreElementProvider.getSource(geoJsonOptions)) -// .thenReturn(optionedGeoJsonSource) -// Mockito.`when`(style.isFullyLoaded).thenReturn(true) -// } -// -// @Test -// fun testInitialization() { -// fillManager = FillManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Mockito.verify(style).addSource(geoJsonSource) -// Mockito.verify(style).addLayer(fillLayer) -// Assert.assertTrue(fillManager!!.dataDrivenPropertyUsageMap.size > 0) -// for (value in fillManager!!.dataDrivenPropertyUsageMap.values) { -// Assert.assertFalse(value) -// } -// Mockito.verify(fillLayer).setProperties( -// *fillManager!!.constantPropertyUsageMap.values.toTypedArray() -// ) -// Mockito.verify(fillLayer, Mockito.times(0)).setFilter( -// ArgumentMatchers.any( -// Expression::class.java -// ) -// ) -// } -// -// @Test -// fun testInitializationOnStyleReload() { -// fillManager = FillManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Mockito.verify(style).addSource(geoJsonSource) -// Mockito.verify(style).addLayer(fillLayer) -// Assert.assertTrue(fillManager.dataDrivenPropertyUsageMap.isNotEmpty()) -// for (value in fillManager.dataDrivenPropertyUsageMap.values) { -// Assert.assertFalse(value) -// } -// Mockito.verify(fillLayer).setProperties( -// *fillManager.constantPropertyUsageMap.values.toTypedArray() -// ) -// val filter: Expression = Expression.literal(false) -// fillManager.setFilter(filter) -// val loadingArgumentCaptor: ArgumentCaptor = -// ArgumentCaptor.forClass( -// MapView.OnDidFinishLoadingStyleListener::class.java -// ) -// Mockito.verify(mapView).addOnDidFinishLoadingStyleListener(loadingArgumentCaptor.capture()) -// loadingArgumentCaptor.value.onDidFinishLoadingStyle() -// val styleLoadedArgumentCaptor: ArgumentCaptor = -// ArgumentCaptor.forClass( -// Style.OnStyleLoaded::class.java -// ) -// Mockito.verify(maplibreMap).getStyle(styleLoadedArgumentCaptor.capture()) -// val newStyle: Style = Mockito.mock(Style::class.java) -// Mockito.`when`(newStyle.isFullyLoaded).thenReturn(true) -// -// val newSource: GeoJsonSource = Mockito.mock(GeoJsonSource::class.java) -// Mockito.`when`(coreElementProvider.getSource(null)).thenReturn(newSource) -// -// val newLayer: FillLayer = Mockito.mock(FillLayer::class.java) -// Mockito.`when`(coreElementProvider.layer).thenReturn(newLayer) -// -// styleLoadedArgumentCaptor.value.onStyleLoaded(newStyle) -// Mockito.verify(newStyle).addSource(newSource) -// Mockito.verify(newStyle).addLayer(newLayer) -// Assert.assertTrue(fillManager.dataDrivenPropertyUsageMap.isNotEmpty()) -// for (value in fillManager.dataDrivenPropertyUsageMap.values) { -// Assert.assertFalse(value) -// } -// Mockito.verify(newLayer).setProperties( -// *fillManager.constantPropertyUsageMap.values.toTypedArray() -// ) -// Mockito.verify(fillLayer).setFilter(filter) -// } -// -// @Test -// fun testLayerBelowInitialization() { -// fillManager = FillManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// "test_layer", -// null, -// null, -// draggableAnnotationController -// ) -// Mockito.verify(style).addSource(geoJsonSource) -// Mockito.verify(style).addLayerBelow(fillLayer, "test_layer") -// Assert.assertTrue(fillManager.dataDrivenPropertyUsageMap.isNotEmpty()) -// for (value in fillManager.dataDrivenPropertyUsageMap.values) { -// Assert.assertFalse(value) -// } -// Mockito.verify(fillLayer).setProperties( -// *fillManager.constantPropertyUsageMap.values.toTypedArray() -// ) -// } -// -// @Test -// fun testGeoJsonOptionsInitialization() { -// fillManager = FillManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// geoJsonOptions, -// draggableAnnotationController -// ) -// Mockito.verify(style).addSource(optionedGeoJsonSource) -// Mockito.verify(style).addLayer(fillLayer) -// Assert.assertTrue(fillManager.dataDrivenPropertyUsageMap.isNotEmpty()) -// for (value in fillManager.dataDrivenPropertyUsageMap.values) { -// Assert.assertFalse(value) -// } -// Mockito.verify(fillLayer).setProperties( -// *fillManager.constantPropertyUsageMap.values.toTypedArray() -// ) -// Mockito.verify(fillLayer, Mockito.times(0)).setFilter( -// ArgumentMatchers.any( -// Expression::class.java -// ) -// ) -// } -// -// @Test -// fun testLayerId() { -// fillManager = FillManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Assert.assertEquals(layerId, fillManager.layerId) -// } -// -// @Test -// fun testAddFill() { -// fillManager = FillManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val innerLatLngs: List = listOf( -// LatLng(), -// LatLng(1.0, 1.0), -// LatLng(-1.0, -1.0), -// LatLng() -// ) -// val latLngs: List> = listOf(innerLatLngs) -// val fill = fillManager.create(FillOptions().withLatLngs(latLngs)) -// Assert.assertEquals(fillManager.annotations[0], fill) -// } -// -// @Test -// fun addFillFromFeatureCollection() { -// fillManager = FillManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// -// val innerPoints: List = listOf( -// Point.fromLngLat(0.0, 0.0), -// Point.fromLngLat(1.0, 1.0), -// Point.fromLngLat(-1.0, -1.0), -// Point.fromLngLat(0.0, 0.0) -// ) -// val points: List> = listOf(innerPoints) -// -// val geometry: Geometry = Polygon.fromLngLats(points) -// val feature = Feature.fromGeometry(geometry) -// feature.addNumberProperty("fill-opacity", 2.0f) -// feature.addStringProperty("fill-color", "rgba(0, 0, 0, 1)") -// feature.addStringProperty("fill-outline-color", "rgba(0, 0, 0, 1)") -// feature.addStringProperty("fill-pattern", "pedestrian-polygon") -// feature.addBooleanProperty("is-draggable", true) -// -// val fills: List = fillManager.create(FeatureCollection.fromFeature(feature)) -// val fill = fills[0] -// -// Assert.assertEquals(fill.geometry, geometry) -// Assert.assertEquals(fill.fillOpacity, 2.0f) -// Assert.assertEquals(fill.fillColor, "rgba(0, 0, 0, 1)") -// Assert.assertEquals(fill.fillOutlineColor, "rgba(0, 0, 0, 1)") -// Assert.assertEquals(fill.fillPattern, "pedestrian-polygon") -// Assert.assertTrue(fill.isDraggable) -// } -// -// @Test -// fun addFills() { -// fillManager = FillManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val latLngListOne: List> = listOf( -// listOf( -// LatLng(2.0, 2.0), -// LatLng(2.0, 3.0) -// ), -// listOf( -// LatLng(1.0, 1.0), -// LatLng(2.0, 3.0) -// ) -// ) -// val latLngListTwo: List> = listOf( -// listOf( -// LatLng(5.0, 7.0), -// LatLng(2.0, 3.0) -// ), -// listOf( -// LatLng(1.0, 1.0), -// LatLng(3.0, 9.0) -// ) -// ) -// -// val latLngList: List>> = listOf( -// latLngListOne, -// latLngListTwo -// ) -// -// val options: MutableList = ArrayList() -// for (lists in latLngList) { -// options.add(FillOptions().withLatLngs(lists)) -// } -// val fills = fillManager.create(options) -// Assert.assertTrue("Returned value size should match", fills.size == 2) -// Assert.assertTrue("Annotations size should match", fillManager.annotations.size() == 2) -// } -// -// @Test -// fun testDeleteFill() { -// fillManager = FillManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val innerLatLngs: MutableList = ArrayList() -// innerLatLngs.add(LatLng()) -// innerLatLngs.add(LatLng(1.0, 1.0)) -// innerLatLngs.add(LatLng(-1.0, -1.0)) -// val latLngs: MutableList> = ArrayList() -// latLngs.add(innerLatLngs) -// val fill = fillManager.create(FillOptions().withLatLngs(latLngs)) -// fillManager.delete(fill) -// Assert.assertTrue(fillManager.annotations.size() == 0) -// } -// -// @Test -// fun testGeometryFill() { -// fillManager = FillManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val innerLatLngs: List = listOf( -// LatLng(), -// LatLng(1.0, 1.0), -// LatLng(-1.0, -1.0) -// ) -// val latLngs: List> = listOf(innerLatLngs) -// val options = FillOptions().withLatLngs(latLngs) -// val fill = fillManager.create(options) -// Assert.assertEquals(options.latLngs, latLngs) -// Assert.assertEquals(fill.latLngs, latLngs) -// Assert.assertEquals( -// options.geometry, -// Polygon.fromLngLats( -// listOf( -// listOf( -// Point.fromLngLat(0.0, 0.0), -// Point.fromLngLat(1.0, 1.0), -// Point.fromLngLat(-1.0, -1.0) -// ) -// ) -// ) -// ) -// Assert.assertEquals( -// fill.getGeometry(), -// Polygon.fromLngLats( -// listOf( -// listOf( -// Point.fromLngLat(0.0, 0.0), -// Point.fromLngLat(1.0, 1.0), -// Point.fromLngLat(-1.0, -1.0) -// ) -// ) -// ) -// ) -// } -// -// @Test -// fun testFeatureIdFill() { -// fillManager = FillManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val innerLatLngs: List = listOf( -// LatLng(), -// LatLng(1.0, 1.0), -// LatLng(-1.0, -1.0) -// ) -// val latLngs: List> = listOf(innerLatLngs) -// val fillZero = fillManager.create(FillOptions().withLatLngs(latLngs)) -// val fillOne = fillManager.create(FillOptions().withLatLngs(latLngs)) -// Assert.assertEquals(fillZero.feature[Fill.ID_KEY].asLong, 0) -// Assert.assertEquals(fillOne.feature[Fill.ID_KEY].asLong, 1) -// } -// -// @Test -// fun testFillDraggableFlag() { -// fillManager = FillManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val innerLatLngs: List = listOf( -// LatLng(), -// LatLng(1.0, 1.0), -// LatLng(-1.0, -1.0) -// ) -// val latLngs: List> = listOf(innerLatLngs) -// val fillZero = fillManager.create(FillOptions().withLatLngs(latLngs)) -// Assert.assertFalse(fillZero.isDraggable) -// fillZero.isDraggable = true -// Assert.assertTrue(fillZero.isDraggable) -// fillZero.isDraggable = false -// Assert.assertFalse(fillZero.isDraggable) -// } -// -// @Test -// fun testFillOpacityLayerProperty() { -// fillManager = FillManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Mockito.verify(fillLayer, Mockito.times(0)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(fillOpacity(get("fill-opacity")))) -// ) -// val innerLatLngs: List = listOf( -// LatLng(), -// LatLng(1.0, 1.0), -// LatLng(-1.0, -1.0) -// ) -// val latLngs: List> = listOf(innerLatLngs) -// val options = FillOptions().withLatLngs(latLngs).withFillOpacity(2.0f) -// fillManager.create(options) -// fillManager.updateSourceNow() -// Mockito.verify(fillLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(fillOpacity(get("fill-opacity")))) -// ) -// fillManager.create(options) -// Mockito.verify(fillLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(fillOpacity(get("fill-opacity")))) -// ) -// } -// -// @Test -// fun testFillColorLayerProperty() { -// fillManager = FillManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Mockito.verify(fillLayer, Mockito.times(0)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(fillColor(get("fill-color")))) -// ) -// val innerLatLngs: List = listOf( -// LatLng(), -// LatLng(1.0, 1.0), -// LatLng(-1.0, -1.0) -// ) -// val latLngs: List> = listOf(innerLatLngs) -// val options = FillOptions().withLatLngs(latLngs).withFillColor("rgba(0, 0, 0, 1)") -// fillManager.create(options) -// fillManager.updateSourceNow() -// Mockito.verify(fillLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(fillColor(get("fill-color")))) -// ) -// fillManager.create(options) -// Mockito.verify(fillLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(fillColor(get("fill-color")))) -// ) -// } -// -// @Test -// fun testFillOutlineColorLayerProperty() { -// fillManager = FillManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Mockito.verify(fillLayer, Mockito.times(0)).setProperties( -// ArgumentMatchers.argThat( -// PropertyValueMatcher(fillOutlineColor(get("fill-outline-color"))) -// ) -// ) -// val innerLatLngs: List = listOf( -// LatLng(), -// LatLng(1.0, 1.0), -// LatLng(-1.0, -1.0) -// ) -// val latLngs: List> = listOf(innerLatLngs) -// val options = FillOptions().withLatLngs(latLngs).withFillOutlineColor("rgba(0, 0, 0, 1)") -// fillManager.create(options) -// fillManager.updateSourceNow() -// Mockito.verify(fillLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat( -// PropertyValueMatcher(fillOutlineColor(get("fill-outline-color"))) -// ) -// ) -// fillManager.create(options) -// Mockito.verify(fillLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat( -// PropertyValueMatcher(fillOutlineColor(get("fill-outline-color"))) -// ) -// ) -// } -// -// @Test -// fun testFillPatternLayerProperty() { -// fillManager = FillManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Mockito.verify(fillLayer, Mockito.times(0)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(fillPattern(get("fill-pattern")))) -// ) -// val innerLatLngs: List = listOf( -// LatLng(), -// LatLng(1.0, 1.0), -// LatLng(-1.0, -1.0) -// ) -// val latLngs: List> = listOf(innerLatLngs) -// val options = FillOptions().withLatLngs(latLngs).withFillPattern("pedestrian-polygon") -// fillManager.create(options) -// fillManager.updateSourceNow() -// Mockito.verify(fillLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(fillPattern(get("fill-pattern")))) -// ) -// fillManager.create(options) -// Mockito.verify(fillLayer, Mockito.times(1)).setProperties( -// ArgumentMatchers.argThat(PropertyValueMatcher(fillPattern(get("fill-pattern")))) -// ) -// } -// -// @Test -// fun testFillLayerFilter() { -// fillManager = FillManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val expression: Expression = Expression.eq(Expression.get("test"), "selected") -// Mockito.verify(fillLayer, Mockito.times(0)).setFilter(expression) -// fillManager.setFilter(expression) -// Mockito.verify(fillLayer, Mockito.times(1)).setFilter(expression) -// Mockito.`when`(fillLayer.filter).thenReturn(expression) -// Assert.assertEquals(expression, fillManager.filter) -// Assert.assertEquals(expression, fillManager.layerFilter) -// } -// -// @Test -// fun testClickListener() { -// val listener = Mockito.mock( -// OnFillClickListener::class.java -// ) -// fillManager = FillManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Assert.assertTrue(fillManager.getClickListeners().isEmpty()) -// fillManager.addClickListener(listener) -// Assert.assertTrue(fillManager.getClickListeners().contains(listener)) -// fillManager.removeClickListener(listener) -// Assert.assertTrue(fillManager.getClickListeners().isEmpty()) -// } -// -// @Test -// fun testLongClickListener() { -// val listener = Mockito.mock( -// OnFillLongClickListener::class.java -// ) -// fillManager = FillManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Assert.assertTrue(fillManager.getLongClickListeners().isEmpty()) -// fillManager.addLongClickListener(listener) -// Assert.assertTrue(fillManager.getLongClickListeners().contains(listener)) -// fillManager.removeLongClickListener(listener) -// Assert.assertTrue(fillManager.getLongClickListeners().isEmpty()) -// } -// -// @Test -// fun testDragListener() { -// val listener = Mockito.mock( -// OnFillDragListener::class.java -// ) -// fillManager = FillManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// Assert.assertTrue(fillManager.getDragListeners().isEmpty()) -// fillManager.addDragListener(listener) -// Assert.assertTrue(fillManager.getDragListeners().contains(listener)) -// fillManager.removeDragListener(listener) -// Assert.assertTrue(fillManager.getDragListeners().isEmpty()) -// } -// -// @Test -// fun testCustomData() { -// fillManager = FillManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val innerLatLngs: List = listOf( -// LatLng(), -// LatLng(1.0, 1.0), -// LatLng(-1.0, -1.0) -// ) -// val latLngs: List> = listOf(innerLatLngs) -// val options = FillOptions().withLatLngs(latLngs) -// options.withData(JsonPrimitive("hello")) -// val fill = fillManager.create(options) -// Assert.assertEquals(JsonPrimitive("hello"), fill.data) -// } -// -// @Test -// fun testClearAll() { -// fillManager = FillManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val innerLatLngs: List = listOf( -// LatLng(), -// LatLng(1.0, 1.0), -// LatLng(-1.0, -1.0) -// ) -// val latLngs: List> = listOf(innerLatLngs) -// val options = FillOptions().withLatLngs(latLngs) -// fillManager.create(options) -// Assert.assertEquals(1, fillManager.annotations.size()) -// fillManager.deleteAll() -// Assert.assertEquals(0, fillManager.annotations.size()) -// } -// -// @Test -// fun testIgnoreClearedAnnotations() { -// fillManager = FillManager( -// mapView, -// maplibreMap, -// style, -// coreElementProvider, -// null, -// null, -// null, -// draggableAnnotationController -// ) -// val innerLatLngs: List = listOf( -// LatLng(), -// LatLng(1.0, 1.0), -// LatLng(-1.0, -1.0) -// ) -// val latLngs: List> = listOf(innerLatLngs) -// val options = FillOptions().withLatLngs(latLngs) -// val fill = fillManager.create(options) -// Assert.assertEquals(1, fillManager.annotations.size()) -// fillManager.annotations.clear() -// fillManager.updateSource() -// Assert.assertTrue(fillManager.annotations.isEmpty) -// fillManager.update(fill) -// Assert.assertTrue(fillManager.annotations.isEmpty) -// } -//} +package org.maplibre.android.annotations + +import android.graphics.Bitmap +import android.graphics.Color +import com.google.gson.JsonPrimitive +import com.mapbox.geojson.Feature +import com.mapbox.geojson.FeatureCollection +import com.mapbox.geojson.Geometry +import com.mapbox.geojson.Point +import com.mapbox.geojson.Polygon +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.maplibre.android.geometry.LatLng +import org.maplibre.android.maps.MapLibreMap +import org.maplibre.android.maps.MapView +import org.maplibre.android.maps.Style +import org.maplibre.android.style.expressions.Expression +import org.maplibre.android.style.expressions.Expression.get +import org.maplibre.android.style.layers.FillLayer +import org.maplibre.android.style.layers.PropertyFactory.fillColor +import org.maplibre.android.style.layers.PropertyFactory.fillOpacity +import org.maplibre.android.style.layers.PropertyFactory.fillOutlineColor +import org.maplibre.android.style.layers.PropertyFactory.fillPattern +import org.maplibre.android.style.sources.GeoJsonOptions +import org.maplibre.android.style.sources.GeoJsonSource +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers +import org.mockito.Mockito + +class FillManagerTest { + private val draggableAnnotationController = Mockito.mock( + DraggableAnnotationController::class.java + ) + private val mapView: MapView = Mockito.mock(MapView::class.java) + private val maplibreMap: MapLibreMap = Mockito.mock(MapLibreMap::class.java) + private val style: Style = Mockito.mock(Style::class.java) + private val geoJsonSource: GeoJsonSource = Mockito.mock(GeoJsonSource::class.java) + private val optionedGeoJsonSource: GeoJsonSource = Mockito.mock(GeoJsonSource::class.java) + private val layerId = "annotation_layer" + private val fillLayer: FillLayer = Mockito.mock(FillLayer::class.java) + private lateinit var fillManager: FillManager + private val coreElementProvider: CoreElementProvider = Mockito.mock( + FillElementProvider::class.java + ) + private val geoJsonOptions: GeoJsonOptions = Mockito.mock(GeoJsonOptions::class.java) + + @Before + fun beforeTest() { + Mockito.`when`(fillLayer.id).thenReturn(layerId) + Mockito.`when`(coreElementProvider.layer).thenReturn(fillLayer) + Mockito.`when`(coreElementProvider.getSource(null)).thenReturn(geoJsonSource) + Mockito.`when`(coreElementProvider.getSource(geoJsonOptions)) + .thenReturn(optionedGeoJsonSource) + Mockito.`when`(style.isFullyLoaded).thenReturn(true) + } + + @Test + fun testInitialization() { + fillManager = FillManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Mockito.verify(style).addSource(geoJsonSource) + Mockito.verify(style).addLayer(fillLayer) + Assert.assertTrue(fillManager.usedDataDrivenProperties.isEmpty()) + Mockito.verify(fillLayer).setProperties( + *fillManager.constantPropertyUsageMap.values.toTypedArray() + ) + Mockito.verify(fillLayer, Mockito.times(0)).setFilter( + ArgumentMatchers.any( + Expression::class.java + ) + ) + } + + @Test + fun testInitializationOnStyleReload() { + fillManager = FillManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Mockito.verify(style).addSource(geoJsonSource) + Mockito.verify(style).addLayer(fillLayer) + Assert.assertTrue(fillManager.usedDataDrivenProperties.isEmpty()) + Mockito.verify(fillLayer).setProperties( + *fillManager.constantPropertyUsageMap.values.toTypedArray() + ) + val filter: Expression = Expression.literal(false) + fillManager.setFilter(filter) + val loadingArgumentCaptor: ArgumentCaptor = + ArgumentCaptor.forClass( + MapView.OnDidFinishLoadingStyleListener::class.java + ) + Mockito.verify(mapView).addOnDidFinishLoadingStyleListener(loadingArgumentCaptor.capture()) + loadingArgumentCaptor.value.onDidFinishLoadingStyle() + val styleLoadedArgumentCaptor: ArgumentCaptor = + ArgumentCaptor.forClass( + Style.OnStyleLoaded::class.java + ) + Mockito.verify(maplibreMap).getStyle(styleLoadedArgumentCaptor.capture()) + val newStyle: Style = Mockito.mock(Style::class.java) + Mockito.`when`(newStyle.isFullyLoaded).thenReturn(true) + + val newSource: GeoJsonSource = Mockito.mock(GeoJsonSource::class.java) + Mockito.`when`(coreElementProvider.getSource(null)).thenReturn(newSource) + + val newLayer: FillLayer = Mockito.mock(FillLayer::class.java) + Mockito.`when`(coreElementProvider.layer).thenReturn(newLayer) + + styleLoadedArgumentCaptor.value.onStyleLoaded(newStyle) + Mockito.verify(newStyle).addSource(newSource) + Mockito.verify(newStyle).addLayer(newLayer) + Assert.assertTrue(fillManager.usedDataDrivenProperties.isEmpty()) + Mockito.verify(newLayer).setProperties( + *fillManager.constantPropertyUsageMap.values.toTypedArray() + ) + Mockito.verify(fillLayer).setFilter(filter) + } + + @Test + fun testLayerBelowInitialization() { + fillManager = FillManager( + mapView, + maplibreMap, + style, + coreElementProvider, + "test_layer", + null, + null, + draggableAnnotationController + ) + Mockito.verify(style).addSource(geoJsonSource) + Mockito.verify(style).addLayerBelow(fillLayer, "test_layer") + Assert.assertTrue(fillManager.usedDataDrivenProperties.isEmpty()) + Mockito.verify(fillLayer).setProperties( + *fillManager.constantPropertyUsageMap.values.toTypedArray() + ) + } + + @Test + fun testGeoJsonOptionsInitialization() { + fillManager = FillManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + geoJsonOptions, + draggableAnnotationController + ) + Mockito.verify(style).addSource(optionedGeoJsonSource) + Mockito.verify(style).addLayer(fillLayer) + Assert.assertTrue(fillManager.usedDataDrivenProperties.isEmpty()) + Mockito.verify(fillLayer).setProperties( + *fillManager.constantPropertyUsageMap.values.toTypedArray() + ) + Mockito.verify(fillLayer, Mockito.times(0)).setFilter( + ArgumentMatchers.any( + Expression::class.java + ) + ) + } + + @Test + fun testLayerId() { + fillManager = FillManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Assert.assertEquals(layerId, fillManager.layerId) + } + + @Test + fun testAddFill() { + fillManager = FillManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + val innerLatLngs: List = listOf( + LatLng(), + LatLng(1.0, 1.0), + LatLng(-1.0, -1.0), + LatLng() + ) + val latLng: List> = listOf(innerLatLngs) + val fill = Fill(latLng) + fillManager.add(fill) + Assert.assertEquals(fill, fillManager.annotations.values.first()) + } + + @Test + fun addFills() { + fillManager = FillManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + val latLngListOne: List> = listOf( + listOf( + LatLng(2.0, 2.0), + LatLng(2.0, 3.0) + ), + listOf( + LatLng(1.0, 1.0), + LatLng(2.0, 3.0) + ) + ) + val latLngListTwo: List> = listOf( + listOf( + LatLng(5.0, 7.0), + LatLng(2.0, 3.0) + ), + listOf( + LatLng(1.0, 1.0), + LatLng(3.0, 9.0) + ) + ) + + listOf( + latLngListOne, + latLngListTwo + ) + .map { Fill(it) } + .forEach { fillManager.add(it) } + + Assert.assertTrue("Annotations size should match", fillManager.annotations.size == 2) + } + + @Test + fun testDeleteFill() { + fillManager = FillManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + val innerLatLngs: List = listOf( + LatLng(), + LatLng(1.0, 1.0), + LatLng(-1.0, -1.0) + ) + val latLngs: List> = listOf(innerLatLngs) + val fill = Fill(latLngs) + fillManager.add(fill) + Assert.assertEquals( + "After adding a fill, one fill should be present", + 1, + fillManager.annotations.size + ) + fillManager.delete(fill) + Assert.assertEquals( + "After removing the only fill, no fills should be present anymore", + 0, + fillManager.annotations.size + ) + } + + @Test + fun testGeometryFill() { + fillManager = FillManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + val innerLatLngs: List = listOf( + LatLng(), + LatLng(1.0, 1.0), + LatLng(-1.0, -1.0) + ) + val latLngs: List> = listOf(innerLatLngs) + val fill = Fill(latLngs) + fillManager.add(fill) + Assert.assertEquals(fill.paths, latLngs) + Assert.assertEquals( + fill.geometry, + Polygon.fromLngLats( + listOf( + listOf( + Point.fromLngLat(0.0, 0.0), + Point.fromLngLat(1.0, 1.0), + Point.fromLngLat(-1.0, -1.0) + ) + ) + ) + ) + } + + @Test + fun testFeatureIdFill() { + fillManager = FillManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + val innerLatLngs: List = listOf( + LatLng(), + LatLng(1.0, 1.0), + LatLng(-1.0, -1.0) + ) + val latLngs: List> = listOf(innerLatLngs) + val fillZero = Fill(latLngs) + fillManager.add(fillZero) + val fillOne = Fill(latLngs) + fillManager.add(fillOne) + Assert.assertEquals(fillZero.id, -1) + Assert.assertEquals(fillOne.id, -2) + } + + @Test + fun testFillDraggableFlag() { + fillManager = FillManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + val innerLatLngs: List = listOf( + LatLng(), + LatLng(1.0, 1.0), + LatLng(-1.0, -1.0) + ) + val latLngs: List> = listOf(innerLatLngs) + val fill = Fill(latLngs) + fillManager.add(fill) + Assert.assertFalse(fill.draggable) + fill.draggable = true + Assert.assertTrue(fill.draggable) + fill.draggable = false + Assert.assertFalse(fill.draggable) + } + + @Test + fun testFillOpacityLayerProperty() { + fillManager = FillManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Mockito.verify(fillLayer, Mockito.times(0)).setProperties( + ArgumentMatchers.argThat(PropertyValueMatcher(fillOpacity(get("fill-opacity")))) + ) + val innerLatLngs: List = listOf( + LatLng(), + LatLng(1.0, 1.0), + LatLng(-1.0, -1.0) + ) + val latLngs: List> = listOf(innerLatLngs) + for (i in 0 until 2) { + val fill = Fill(latLngs, opacity = 0.5f) + fillManager.add(fill) + fillManager.updateSourceNow() + Mockito.verify(fillLayer, Mockito.times(1)).setProperties( + ArgumentMatchers.argThat(PropertyValueMatcher(fillOpacity(get("fill-opacity")))) + ) + } + } + + @Test + fun testFillColorLayerProperty() { + fillManager = FillManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Mockito.verify(fillLayer, Mockito.times(0)).setProperties( + ArgumentMatchers.argThat(PropertyValueMatcher(fillColor(get("fill-color")))) + ) + val innerLatLngs: List = listOf( + LatLng(), + LatLng(1.0, 1.0), + LatLng(-1.0, -1.0) + ) + val latLngs: List> = listOf(innerLatLngs) + for (i in 0 until 2) { + val fill = Fill(latLngs, color = Color.BLUE) + fillManager.add(fill) + fillManager.updateSourceNow() + Mockito.verify(fillLayer, Mockito.times(1)).setProperties( + ArgumentMatchers.argThat(PropertyValueMatcher(fillColor(get("fill-color")))) + ) + } + } + + @Test + fun testFillOutlineColorLayerProperty() { + fillManager = FillManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Mockito.verify(fillLayer, Mockito.times(0)).setProperties( + ArgumentMatchers.argThat( + PropertyValueMatcher(fillOutlineColor(get("fill-outline-color"))) + ) + ) + val innerLatLngs: List = listOf( + LatLng(), + LatLng(1.0, 1.0), + LatLng(-1.0, -1.0) + ) + val latLngs: List> = listOf(innerLatLngs) + for (i in 0 until 2) { + val fill = Fill(latLngs, outlineColor = Color.MAGENTA) + fillManager.add(fill) + fillManager.updateSourceNow() + Mockito.verify(fillLayer, Mockito.times(1)).setProperties( + ArgumentMatchers.argThat( + PropertyValueMatcher(fillOutlineColor(get("fill-outline-color"))) + ) + ) + } + } + + @Test + fun testFillPatternLayerProperty() { + fillManager = FillManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Mockito.verify(fillLayer, Mockito.times(0)).setProperties( + ArgumentMatchers.argThat(PropertyValueMatcher(fillPattern(get("fill-pattern")))) + ) + val innerLatLngs: List = listOf( + LatLng(), + LatLng(1.0, 1.0), + LatLng(-1.0, -1.0) + ) + val latLngs: List> = listOf(innerLatLngs) + for (i in 0 until 2) { + val fill = Fill(latLngs, pattern = Mockito.mock(Bitmap::class.java)) + fillManager.add(fill) + fillManager.updateSourceNow() + Mockito.verify(fillLayer, Mockito.times(1)).setProperties( + ArgumentMatchers.argThat(PropertyValueMatcher(fillPattern(get("fill-pattern")))) + ) + } + } + + @Test + fun testFillLayerFilter() { + fillManager = FillManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + val expression: Expression = Expression.eq(Expression.get("test"), "selected") + Mockito.verify(fillLayer, Mockito.times(0)).setFilter(expression) + fillManager.setFilter(expression) + Mockito.verify(fillLayer, Mockito.times(1)).setFilter(expression) + Mockito.`when`(fillLayer.filter).thenReturn(expression) + Assert.assertEquals(expression, fillManager.filter) + Assert.assertEquals(expression, fillManager.layerFilter) + } + + @Test + fun testClickListener() { + val listener = object : OnFillClickListener { + override fun onAnnotationClick(t: Fill): Boolean = false + } + fillManager = FillManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Assert.assertTrue(fillManager.getClickListeners().isEmpty()) + fillManager.addClickListener(listener) + Assert.assertTrue(fillManager.getClickListeners().contains(listener)) + fillManager.removeClickListener(listener) + Assert.assertTrue(fillManager.getClickListeners().isEmpty()) + } + + @Test + fun testLongClickListener() { + val listener = object : OnFillLongClickListener { + override fun onAnnotationLongClick(t: Fill): Boolean = false + } + fillManager = FillManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Assert.assertTrue(fillManager.getLongClickListeners().isEmpty()) + fillManager.addLongClickListener(listener) + Assert.assertTrue(fillManager.getLongClickListeners().contains(listener)) + fillManager.removeLongClickListener(listener) + Assert.assertTrue(fillManager.getLongClickListeners().isEmpty()) + } + + @Test + fun testDragListener() { + val listener = object : OnFillDragListener { + override fun onAnnotationDragStarted(annotation: Fill) = Unit + override fun onAnnotationDrag(annotation: Fill) = Unit + override fun onAnnotationDragFinished(annotation: Fill) = Unit + } + fillManager = FillManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Assert.assertTrue(fillManager.getDragListeners().isEmpty()) + fillManager.addDragListener(listener) + Assert.assertTrue(fillManager.getDragListeners().contains(listener)) + fillManager.removeDragListener(listener) + Assert.assertTrue(fillManager.getDragListeners().isEmpty()) + } + + @Test + fun testCustomData() { + fillManager = FillManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + val innerLatLngs: List = listOf( + LatLng(), + LatLng(1.0, 1.0), + LatLng(-1.0, -1.0) + ) + val latLngs: List> = listOf(innerLatLngs) + val fill = Fill(latLngs).apply { + data = JsonPrimitive("hello") + } + fillManager.add(fill) + Assert.assertEquals(JsonPrimitive("hello"), fill.data) + } + + @Test + fun testClearAll() { + fillManager = FillManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + val innerLatLngs: List = listOf( + LatLng(), + LatLng(1.0, 1.0), + LatLng(-1.0, -1.0) + ) + val latLngs: List> = listOf(innerLatLngs) + val fill = Fill(latLngs) + fillManager.add(fill) + Assert.assertEquals(1, fillManager.annotations.size) + fillManager.deleteAll() + Assert.assertEquals(0, fillManager.annotations.size) + } +} From 03b36408b3c4f098926d529e207b81cded06d5f1 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Tue, 3 Oct 2023 18:35:22 +0200 Subject: [PATCH 05/10] User-facing interface for adding and removing annotations --- .../maplibre/android/maps/MapLibreMap.java | 42 +++++++++++++++++-- .../activity/annotation/BulkMarkerActivity.kt | 2 +- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/maps/MapLibreMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/maps/MapLibreMap.java index 2e01a0f7abd..2956ae61c9e 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/maps/MapLibreMap.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/maps/MapLibreMap.java @@ -25,8 +25,10 @@ import org.maplibre.android.MapStrictMode; import org.maplibre.android.annotations.Annotation; import org.maplibre.android.annotations.BaseMarkerOptions; +import org.maplibre.android.annotations.Circle; +import org.maplibre.android.annotations.Fill; import org.maplibre.android.annotations.KAnnotation; -import org.maplibre.android.annotations.Symbol; +import org.maplibre.android.annotations.Line; import org.maplibre.android.annotations.Marker; import org.maplibre.android.annotations.MarkerOptions; import org.maplibre.android.annotations.Polygon; @@ -34,6 +36,7 @@ import org.maplibre.android.annotations.Polyline; import org.maplibre.android.annotations.PolylineOptions; import org.maplibre.android.annotations.KAnnotationContainer; +import org.maplibre.android.annotations.Symbol; import org.maplibre.android.camera.CameraPosition; import org.maplibre.android.camera.CameraUpdate; import org.maplibre.android.camera.CameraUpdateFactory; @@ -996,9 +999,35 @@ void notifyStyleLoaded() { // Annotations // - public void addSymbol(@NonNull Symbol symbol) { - symbol.attach(this, nextId++); - annotationContainer.add(symbol); + /* To make the types of annotations more discoverable, we have implemented the same method + * four times for all of the subtypes of Annotation. + */ + + public void addAnnotation(@NonNull Symbol annotation) { + annotation.attach(this, nextId++); + annotationContainer.add(annotation); + } + + public void addAnnotation(@NonNull Circle annotation) { + annotation.attach(this, nextId++); + annotationContainer.add(annotation); + } + + public void addAnnotation(@NonNull Line annotation) { + annotation.attach(this, nextId++); + annotationContainer.add(annotation); + } + + public void addAnnotation(@NonNull Fill annotation) { + annotation.attach(this, nextId++); + annotationContainer.add(annotation); + } + + public void addAnnotations(@NonNull KAnnotation... annotations) { + for (KAnnotation annotation : annotations) { + annotation.attach(this, nextId++); + annotationContainer.add(annotation); + } } public void updateAnnotations() { @@ -1013,6 +1042,10 @@ public void updateAnnotation(@NonNull KAnnotation annotation) { annotationContainer.update(annotation); } + public void removeAnnotation(@NonNull KAnnotation annotation) { + annotationContainer.remove(annotation); + } + /** *

* Adds a marker to this map. @@ -1269,6 +1302,7 @@ public void removeAnnotations(@NonNull List annotationList @Deprecated public void removeAnnotations() { annotationManager.removeAnnotations(); + annotationContainer.clear(); } /** diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/activity/annotation/BulkMarkerActivity.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/activity/annotation/BulkMarkerActivity.kt index 1a8bb36ea87..68ccfc9199c 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/activity/annotation/BulkMarkerActivity.kt +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/activity/annotation/BulkMarkerActivity.kt @@ -99,7 +99,7 @@ class BulkMarkerActivity : AppCompatActivity(), OnItemSelectedListener { for (i in 0 until amount) { randomIndex = random.nextInt(locations!!.size) val latLng = locations!![randomIndex] - maplibreMap.addSymbol( + maplibreMap.addAnnotation( Symbol( position = latLng, text = Text(i.toString(), color = Color.WHITE), From f48eb9b69850b5387ca67cdd9b09948b861fe637 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Tue, 3 Oct 2023 18:48:48 +0200 Subject: [PATCH 06/10] JvmOverloads for new class constructors --- .../src/main/java/org/maplibre/android/annotations/Circle.kt | 2 +- .../src/main/java/org/maplibre/android/annotations/Fill.kt | 2 +- .../src/main/java/org/maplibre/android/annotations/Line.kt | 2 +- .../src/main/java/org/maplibre/android/annotations/Symbol.kt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Circle.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Circle.kt index 5fea5e6bbf7..681e44d4d35 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Circle.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Circle.kt @@ -10,7 +10,7 @@ import org.maplibre.android.constants.GeometryConstants import org.maplibre.android.geometry.LatLng import org.maplibre.android.maps.Projection -class Circle( +class Circle @JvmOverloads constructor( center: LatLng, radius: Float = Defaults.CIRCLE_RADIUS, @ColorInt color: Int = Defaults.CIRCLE_COLOR, diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Fill.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Fill.kt index d4ffe41cd7c..701377432a5 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Fill.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Fill.kt @@ -12,7 +12,7 @@ import org.maplibre.android.constants.GeometryConstants.MIN_MERCATOR_LATITUDE import org.maplibre.android.geometry.LatLng import org.maplibre.android.maps.Projection -class Fill( +class Fill @JvmOverloads constructor( paths: List>, opacity: Float = Defaults.FILL_OPACITY, @ColorInt color: Int = Defaults.FILL_COLOR, diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Line.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Line.kt index 865aa33d5be..9fb845d732c 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Line.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Line.kt @@ -12,7 +12,7 @@ import org.maplibre.android.constants.GeometryConstants.MIN_MERCATOR_LATITUDE import org.maplibre.android.geometry.LatLng import org.maplibre.android.maps.Projection -class Line( +class Line @JvmOverloads constructor( path: List, join: Join = Defaults.LINE_JOIN, opacity: Float = Defaults.LINE_OPACITY, diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Symbol.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Symbol.kt index 3ada75870c1..f8eb531f9ab 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Symbol.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Symbol.kt @@ -11,7 +11,7 @@ import org.maplibre.android.constants.GeometryConstants import org.maplibre.android.geometry.LatLng import org.maplibre.android.maps.Projection -class Symbol( +class Symbol @JvmOverloads constructor( position: LatLng, icon: Icon? = Defaults.SYMBOL_ICON, text: Text? = Defaults.SYMBOL_TEXT From f3f043da9b31264087be688348abb6ee29f566ca Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Tue, 3 Oct 2023 18:52:20 +0200 Subject: [PATCH 07/10] Allow manual testing of Fill class Also fixes a bug in Fill class where geometry updates are not applied. --- .../org/maplibre/android/annotations/Fill.kt | 2 +- .../activity/annotation/PolygonActivity.kt | 36 +++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Fill.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Fill.kt index 701377432a5..026ea9d9d44 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Fill.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Fill.kt @@ -24,7 +24,7 @@ class Fill @JvmOverloads constructor( var paths: List> = paths set(value) { field = value - Polygon.fromLngLats(value.map { it.map { Point.fromLngLat(it.longitude, it.latitude) } }) + geometry = Polygon.fromLngLats(value.map { it.map { Point.fromLngLat(it.longitude, it.latitude) } }) updateThis() } var opacity: Float = opacity diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/activity/annotation/PolygonActivity.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/activity/annotation/PolygonActivity.kt index e08a74a13e4..f1807cfeafd 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/activity/annotation/PolygonActivity.kt +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/activity/annotation/PolygonActivity.kt @@ -6,8 +6,7 @@ import android.view.Menu import android.view.MenuItem import android.widget.Toast import androidx.appcompat.app.AppCompatActivity -import org.maplibre.android.annotations.Polygon -import org.maplibre.android.annotations.PolygonOptions +import org.maplibre.android.annotations.Fill import org.maplibre.android.camera.CameraPosition import org.maplibre.android.geometry.LatLng import org.maplibre.android.maps.* // ktlint-disable no-wildcard-imports @@ -24,7 +23,7 @@ import java.util.ArrayList class PolygonActivity : AppCompatActivity(), OnMapReadyCallback { private lateinit var mapView: MapView private lateinit var maplibreMap: MapLibreMap - private var polygon: Polygon? = null + private var polygon: Fill? = null private var fullAlpha = true private var polygonIsVisible = true private var color = true @@ -56,18 +55,16 @@ class PolygonActivity : AppCompatActivity(), OnMapReadyCallback { override fun onMapReady(map: MapLibreMap) { maplibreMap = map map.setStyle(Style.getPredefinedStyle("Streets")) - map.setOnPolygonClickListener { polygon: Polygon -> + /*map.setOnPolygonClickListener { polygon: Fill -> Toast.makeText( this@PolygonActivity, "You clicked on polygon with id = " + polygon.id, Toast.LENGTH_SHORT ).show() + }*/ + polygon = Fill(listOf(Config.STAR_SHAPE_POINTS), color = Config.BLUE_COLOR).also { + maplibreMap.addAnnotation(it) } - polygon = maplibreMap.addPolygon( - PolygonOptions() - .addAll(Config.STAR_SHAPE_POINTS) - .fillColor(Config.BLUE_COLOR) - ) } override fun onStart() { @@ -109,32 +106,35 @@ class PolygonActivity : AppCompatActivity(), OnMapReadyCallback { return when (item.itemId) { R.id.action_id_alpha -> { fullAlpha = !fullAlpha - polygon!!.alpha = + polygon!!.opacity = if (fullAlpha) Config.FULL_ALPHA else Config.PARTIAL_ALPHA true } R.id.action_id_visible -> { polygonIsVisible = !polygonIsVisible - polygon!!.alpha = + polygon!!.opacity = if (polygonIsVisible) if (fullAlpha) Config.FULL_ALPHA else Config.PARTIAL_ALPHA else Config.NO_ALPHA true } R.id.action_id_points -> { allPoints = !allPoints - polygon!!.points = - if (allPoints) Config.STAR_SHAPE_POINTS else Config.BROKEN_SHAPE_POINTS + polygon!!.paths = + if (allPoints) listOf(Config.STAR_SHAPE_POINTS) else listOf(Config.BROKEN_SHAPE_POINTS) + holes = false true } R.id.action_id_color -> { color = !color - polygon!!.fillColor = + polygon!!.color = if (color) Config.BLUE_COLOR else Config.RED_COLOR true } R.id.action_id_holes -> { holes = !holes - polygon!!.holes = - if (holes) Config.STAR_SHAPE_HOLES else emptyList() + val holesList = if (holes) Config.STAR_SHAPE_HOLES else emptyList() + val allList = if (allPoints) Config.STAR_SHAPE_POINTS else Config.BROKEN_SHAPE_POINTS + + polygon!!.paths = listOf(allList) + holesList true } else -> super.onOptionsItemSelected(item) @@ -152,7 +152,7 @@ class PolygonActivity : AppCompatActivity(), OnMapReadyCallback { const val FULL_ALPHA = 1.0f const val PARTIAL_ALPHA = 0.5f const val NO_ALPHA = 0.0f - val STAR_SHAPE_POINTS: ArrayList = object : ArrayList() { + val STAR_SHAPE_POINTS: ArrayList = object : ArrayList() { init { add(LatLng(45.522585, -122.685699)) add(LatLng(45.534611, -122.708873)) @@ -170,7 +170,7 @@ class PolygonActivity : AppCompatActivity(), OnMapReadyCallback { } } val BROKEN_SHAPE_POINTS = STAR_SHAPE_POINTS.subList(0, STAR_SHAPE_POINTS.size - 3) - val STAR_SHAPE_HOLES: ArrayList?> = object : ArrayList?>() { + val STAR_SHAPE_HOLES: ArrayList> = object : ArrayList>() { init { add( ArrayList(object : ArrayList() { From 0b3a29a7b956f76f11e90df412acc0f4b2a38351 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Tue, 3 Oct 2023 19:05:00 +0200 Subject: [PATCH 08/10] Allow manual testing of Lines --- .../maplibre/android/maps/MapLibreMap.java | 6 ++ .../activity/annotation/PolylineActivity.kt | 94 +++++++------------ 2 files changed, 41 insertions(+), 59 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/maps/MapLibreMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/maps/MapLibreMap.java index 2956ae61c9e..1a0ba0df383 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/maps/MapLibreMap.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/maps/MapLibreMap.java @@ -1046,6 +1046,12 @@ public void removeAnnotation(@NonNull KAnnotation annotation) { annotationContainer.remove(annotation); } + public void removeAnnotations(@NonNull KAnnotation... annotations) { + for (KAnnotation annotation : annotations) { + annotationContainer.remove(annotation); + } + } + /** *

* Adds a marker to this map. diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/activity/annotation/PolylineActivity.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/activity/annotation/PolylineActivity.kt index 1ce15877495..f0caff9d8ef 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/activity/annotation/PolylineActivity.kt +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/org/maplibre/android/testapp/activity/annotation/PolylineActivity.kt @@ -7,15 +7,13 @@ import android.view.MenuItem import android.view.View import android.widget.Toast import androidx.appcompat.app.AppCompatActivity -import org.maplibre.android.annotations.Polyline -import org.maplibre.android.annotations.PolylineOptions +import org.maplibre.android.annotations.Line import org.maplibre.android.geometry.LatLng import org.maplibre.android.maps.MapView import org.maplibre.android.maps.MapLibreMap import org.maplibre.android.maps.OnMapReadyCallback import org.maplibre.android.maps.Style import org.maplibre.android.testapp.R -import java.util.* /** * Test activity showcasing the Polyline annotations API. @@ -25,8 +23,7 @@ import java.util.* * */ class PolylineActivity : AppCompatActivity() { - private var polylines: MutableList? = null - private var polylineOptions: ArrayList? = ArrayList() + private var polylines: MutableList = mutableListOf() private lateinit var mapView: MapView private lateinit var maplibreMap: MapLibreMap private var fullAlpha = true @@ -36,76 +33,58 @@ class PolylineActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_polyline) - if (savedInstanceState != null) { - polylineOptions = savedInstanceState.getParcelableArrayList(STATE_POLYLINE_OPTIONS) - } else { - polylineOptions!!.addAll(allPolylines) - } mapView = findViewById(R.id.mapView) mapView.onCreate(savedInstanceState) mapView.getMapAsync( OnMapReadyCallback { maplibreMap: MapLibreMap -> this@PolylineActivity.maplibreMap = maplibreMap maplibreMap.setStyle(Style.getPredefinedStyle("Satellite Hybrid")) - maplibreMap.setOnPolylineClickListener { polyline: Polyline -> + /*maplibreMap.setOnPolylineClickListener { polyline: Polyline -> Toast.makeText( this@PolylineActivity, "You clicked on polyline with id = " + polyline.id, Toast.LENGTH_SHORT ).show() - } - polylines = maplibreMap.addPolylines(polylineOptions!!) + }*/ + maplibreMap.addAnnotations(*polylines.toTypedArray()) } ) val fab = findViewById(R.id.fab) fab?.setOnClickListener { view: View? -> if (maplibreMap != null) { - if (polylines != null && polylines!!.size > 0) { - if (polylines!!.size == 1) { + if (polylines != null && polylines.size > 0) { + if (polylines.size == 1) { // test for removing annotation - maplibreMap.removeAnnotation(polylines!![0]) + maplibreMap.removeAnnotation(polylines[0]) } else { // test for removing annotations - maplibreMap.removeAnnotations(polylines!!) + maplibreMap.removeAnnotations(*polylines.toTypedArray()) } } - polylineOptions!!.clear() - polylineOptions!!.addAll(randomLine) - polylines = maplibreMap.addPolylines(polylineOptions!!) + polylines.clear() + randomLine.let { + polylines.add(it) + maplibreMap.addAnnotation(it) + } } } } - private val allPolylines: List - private get() { - val options: MutableList = ArrayList() - options.add(generatePolyline(ANDORRA, LUXEMBOURG, "#F44336")) - options.add(generatePolyline(ANDORRA, MONACO, "#FF5722")) - options.add(generatePolyline(MONACO, VATICAN_CITY, "#673AB7")) - options.add(generatePolyline(VATICAN_CITY, SAN_MARINO, "#009688")) - options.add(generatePolyline(SAN_MARINO, LIECHTENSTEIN, "#795548")) - options.add(generatePolyline(LIECHTENSTEIN, LUXEMBOURG, "#3F51B5")) - return options - } + private val allPolylines: List + private get() = listOf( + generatePolyline(ANDORRA, LUXEMBOURG, "#F44336"), + generatePolyline(ANDORRA, MONACO, "#FF5722"), + generatePolyline(MONACO, VATICAN_CITY, "#673AB7"), + generatePolyline(VATICAN_CITY, SAN_MARINO, "#009688"), + generatePolyline(SAN_MARINO, LIECHTENSTEIN, "#795548"), + generatePolyline(LIECHTENSTEIN, LUXEMBOURG, "#3F51B5") + ) - private fun generatePolyline(start: LatLng, end: LatLng, color: String): PolylineOptions { - val line = PolylineOptions() - line.add(start) - line.add(end) - line.color(Color.parseColor(color)) - return line - } + private fun generatePolyline(start: LatLng, end: LatLng, color: String): Line = + Line(listOf(start, end), color = Color.parseColor(color)) - val randomLine: List - get() { - val randomLines = allPolylines - Collections.shuffle(randomLines) - return object : ArrayList() { - init { - add(randomLines[0]) - } - } - } + val randomLine: Line + get() = allPolylines.random() override fun onStart() { super.onStart() @@ -130,7 +109,6 @@ class PolylineActivity : AppCompatActivity() { override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) mapView.onSaveInstanceState(outState) - outState.putParcelableArrayList(STATE_POLYLINE_OPTIONS, polylineOptions) } override fun onDestroy() { @@ -149,43 +127,42 @@ class PolylineActivity : AppCompatActivity() { } override fun onOptionsItemSelected(item: MenuItem): Boolean { - if (polylines!!.size <= 0) { + if (polylines.size <= 0) { Toast.makeText(this@PolylineActivity, "No polylines on map", Toast.LENGTH_LONG).show() return super.onOptionsItemSelected(item) } return when (item.itemId) { R.id.action_id_remove -> { // test to remove all annotations - polylineOptions!!.clear() maplibreMap.clear() - polylines!!.clear() + polylines.clear() true } R.id.action_id_alpha -> { fullAlpha = !fullAlpha - for (p in polylines!!) { - p.alpha = if (fullAlpha) FULL_ALPHA else PARTIAL_ALPHA + for (p in polylines) { + p.opacity = if (fullAlpha) FULL_ALPHA else PARTIAL_ALPHA } true } R.id.action_id_color -> { color = !color - for (p in polylines!!) { + for (p in polylines) { p.color = if (color) Color.RED else Color.BLUE } true } R.id.action_id_width -> { width = !width - for (p in polylines!!) { + for (p in polylines) { p.width = if (width) 3.0f else 5.0f } true } R.id.action_id_visible -> { showPolylines = !showPolylines - for (p in polylines!!) { - p.alpha = + for (p in polylines) { + p.opacity = if (showPolylines) (if (fullAlpha) FULL_ALPHA else PARTIAL_ALPHA) else NO_ALPHA } true @@ -195,7 +172,6 @@ class PolylineActivity : AppCompatActivity() { } companion object { - private const val STATE_POLYLINE_OPTIONS = "polylineOptions" private val ANDORRA = LatLng(42.505777, 1.52529) private val LUXEMBOURG = LatLng(49.815273, 6.129583) private val MONACO = LatLng(43.738418, 7.424616) From d445c269902ee2e65131e79966c6a4cedc69ecb0 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Tue, 3 Oct 2023 19:32:24 +0200 Subject: [PATCH 09/10] Add Z layer as properties Had not existed in annotations plugin previously. --- .../android/annotations/CircleManager.kt | 4 +++ .../org/maplibre/android/annotations/Fill.kt | 10 +++--- .../android/annotations/FillManager.kt | 4 +++ .../org/maplibre/android/annotations/Line.kt | 18 +++++----- .../android/annotations/LineManager.kt | 4 +++ .../android/annotations/CircleManagerTest.kt | 35 +++++++++++++++++++ .../android/annotations/FillManagerTest.kt | 34 ++++++++++++++++++ .../android/annotations/LineManagerTest.kt | 32 +++++++++++++++++ 8 files changed, 129 insertions(+), 12 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/CircleManager.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/CircleManager.kt index 46263ba6269..b777841bf9b 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/CircleManager.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/CircleManager.kt @@ -61,6 +61,10 @@ class CircleManager @UiThread internal constructor( ) override fun generateDataDrivenPropertyExpression(property: String): PropertyValue = when (property) { + Circle.PROPERTY_CIRCLE_SORT_KEY -> PropertyFactory.circleSortKey( + Expression.get(Circle.PROPERTY_CIRCLE_SORT_KEY) + ) + Circle.PROPERTY_CIRCLE_RADIUS -> PropertyFactory.circleRadius( Expression.get(Circle.PROPERTY_CIRCLE_RADIUS) ) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Fill.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Fill.kt index 026ea9d9d44..6f0afcb94dd 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Fill.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Fill.kt @@ -52,6 +52,7 @@ class Fill @JvmOverloads constructor( override val dataDrivenProperties: List get() = listOf( PROPERTY_IS_DRAGGABLE to draggable default Defaults.DRAGGABLE, + PROPERTY_FILL_SORT_KEY to zLayer default Defaults.Z_LAYER, PROPERTY_FILL_OPACITY to opacity default Defaults.FILL_OPACITY, PROPERTY_FILL_COLOR to color.asColorString() default Defaults.FILL_COLOR.asColorString(), PROPERTY_FILL_OUTLINE_COLOR to outlineColor?.asColorString() @@ -87,9 +88,10 @@ class Fill @JvmOverloads constructor( } companion object { - const val PROPERTY_FILL_OPACITY = "fill-opacity" - const val PROPERTY_FILL_COLOR = "fill-color" - const val PROPERTY_FILL_OUTLINE_COLOR = "fill-outline-color" - const val PROPERTY_FILL_PATTERN = "fill-pattern" + internal const val PROPERTY_FILL_SORT_KEY = "fill-sort-key" + internal const val PROPERTY_FILL_OPACITY = "fill-opacity" + internal const val PROPERTY_FILL_COLOR = "fill-color" + internal const val PROPERTY_FILL_OUTLINE_COLOR = "fill-outline-color" + internal const val PROPERTY_FILL_PATTERN = "fill-pattern" } } \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/FillManager.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/FillManager.kt index 0e1833cbdb2..60d0c196e9c 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/FillManager.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/FillManager.kt @@ -43,6 +43,10 @@ class FillManager @UiThread internal constructor( ) { override fun generateDataDrivenPropertyExpression(property: String): PropertyValue = when (property) { + Fill.PROPERTY_FILL_SORT_KEY -> PropertyFactory.fillSortKey( + Expression.get(Fill.PROPERTY_FILL_SORT_KEY) + ) + Fill.PROPERTY_FILL_OPACITY -> PropertyFactory.fillOpacity( Expression.get(Fill.PROPERTY_FILL_OPACITY) ) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Line.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Line.kt index 9fb845d732c..efd31bd9984 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Line.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/Line.kt @@ -81,6 +81,7 @@ class Line @JvmOverloads constructor( override val dataDrivenProperties: List get() = listOf( PROPERTY_IS_DRAGGABLE to draggable default Defaults.DRAGGABLE, + PROPERTY_LINE_SORT_KEY to zLayer default Defaults.Z_LAYER, PROPERTY_LINE_JOIN to join.toString().lowercase() default Defaults.LINE_JOIN.toString().lowercase(), PROPERTY_LINE_OPACITY to opacity default Defaults.LINE_OPACITY, PROPERTY_LINE_COLOR to color.asColorString() default Defaults.LINE_COLOR, @@ -131,13 +132,14 @@ class Line @JvmOverloads constructor( } companion object { - const val PROPERTY_LINE_JOIN = "line-join" - const val PROPERTY_LINE_OPACITY = "line-opacity" - const val PROPERTY_LINE_COLOR = "line-color" - const val PROPERTY_LINE_WIDTH = "line-width" - const val PROPERTY_LINE_GAP_WIDTH = "line-gap-width" - const val PROPERTY_LINE_OFFSET = "line-offset" - const val PROPERTY_LINE_BLUR = "line-blur" - const val PROPERTY_LINE_PATTERN = "line-pattern" + internal const val PROPERTY_LINE_SORT_KEY = "line-sort-key" + internal const val PROPERTY_LINE_JOIN = "line-join" + internal const val PROPERTY_LINE_OPACITY = "line-opacity" + internal const val PROPERTY_LINE_COLOR = "line-color" + internal const val PROPERTY_LINE_WIDTH = "line-width" + internal const val PROPERTY_LINE_GAP_WIDTH = "line-gap-width" + internal const val PROPERTY_LINE_OFFSET = "line-offset" + internal const val PROPERTY_LINE_BLUR = "line-blur" + internal const val PROPERTY_LINE_PATTERN = "line-pattern" } } \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/LineManager.kt b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/LineManager.kt index 31dbbfb5ae9..e72e416ec00 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/LineManager.kt +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/annotations/LineManager.kt @@ -58,6 +58,10 @@ class LineManager @UiThread internal constructor( ) override fun generateDataDrivenPropertyExpression(property: String): PropertyValue = when (property) { + Line.PROPERTY_LINE_SORT_KEY -> PropertyFactory.lineSortKey( + Expression.get(Line.PROPERTY_LINE_SORT_KEY) + ) + Line.PROPERTY_LINE_JOIN -> PropertyFactory.lineJoin( Expression.get(Line.PROPERTY_LINE_JOIN) ) diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/CircleManagerTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/CircleManagerTest.kt index dbbd4e58125..898acf6ccca 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/CircleManagerTest.kt +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/CircleManagerTest.kt @@ -278,6 +278,41 @@ class CircleManagerTest { Assert.assertFalse(circle.draggable) } + @Test + fun testCircleSortKeyLayerProperty() { + circleManager = CircleManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Mockito.verify(circleLayer, Mockito.times(0)).setProperties( + ArgumentMatchers.argThat( + PropertyValueMatcher( + PropertyFactory.circleSortKey(Expression.get("circle-sort-key")) + ) + ) + ) + for (i in 0 until 2) { + val circle = Circle(LatLng()).apply { + zLayer = 2 + } + circleManager.add(circle) + circleManager.updateSourceNow() + Mockito.verify(circleLayer, Mockito.times(1)).setProperties( + ArgumentMatchers.argThat( + PropertyValueMatcher( + PropertyFactory.circleSortKey(Expression.get("circle-sort-key")) + ) + ) + ) + } + } + @Test fun testCircleRadiusLayerProperty() { circleManager = CircleManager( diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/FillManagerTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/FillManagerTest.kt index 53126dc916f..ab0560aa3a0 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/FillManagerTest.kt +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/FillManagerTest.kt @@ -22,6 +22,7 @@ import org.maplibre.android.style.layers.PropertyFactory.fillColor import org.maplibre.android.style.layers.PropertyFactory.fillOpacity import org.maplibre.android.style.layers.PropertyFactory.fillOutlineColor import org.maplibre.android.style.layers.PropertyFactory.fillPattern +import org.maplibre.android.style.layers.PropertyFactory.fillSortKey import org.maplibre.android.style.sources.GeoJsonOptions import org.maplibre.android.style.sources.GeoJsonSource import org.mockito.ArgumentCaptor @@ -378,6 +379,39 @@ class FillManagerTest { Assert.assertFalse(fill.draggable) } + @Test + fun testFillSortKeyLayerProperty() { + fillManager = FillManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Mockito.verify(fillLayer, Mockito.times(0)).setProperties( + ArgumentMatchers.argThat(PropertyValueMatcher(fillSortKey(get("fill-sort-key")))) + ) + val innerLatLngs: List = listOf( + LatLng(), + LatLng(1.0, 1.0), + LatLng(-1.0, -1.0) + ) + val latLngs: List> = listOf(innerLatLngs) + for (i in 0 until 2) { + val fill = Fill(latLngs).apply { + zLayer = 2 + } + fillManager.add(fill) + fillManager.updateSourceNow() + Mockito.verify(fillLayer, Mockito.times(1)).setProperties( + ArgumentMatchers.argThat(PropertyValueMatcher(fillSortKey(get("fill-sort-key")))) + ) + } + } + @Test fun testFillOpacityLayerProperty() { fillManager = FillManager( diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/LineManagerTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/LineManagerTest.kt index aa313f4a9df..353a9dc43cb 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/LineManagerTest.kt +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/org/maplibre/android/annotations/LineManagerTest.kt @@ -28,6 +28,7 @@ import org.maplibre.android.style.layers.PropertyFactory.lineJoin import org.maplibre.android.style.layers.PropertyFactory.lineOffset import org.maplibre.android.style.layers.PropertyFactory.lineOpacity import org.maplibre.android.style.layers.PropertyFactory.linePattern +import org.maplibre.android.style.layers.PropertyFactory.lineSortKey import org.maplibre.android.style.layers.PropertyFactory.lineWidth import org.maplibre.android.style.sources.GeoJsonOptions import org.maplibre.android.style.sources.GeoJsonSource @@ -360,6 +361,37 @@ class LineManagerTest { Assert.assertFalse(line.draggable) } + @Test + fun testLineSortKeyLayerProperty() { + lineManager = LineManager( + mapView, + maplibreMap, + style, + coreElementProvider, + null, + null, + null, + draggableAnnotationController + ) + Mockito.verify(lineLayer, Mockito.times(0)).setProperties( + ArgumentMatchers.argThat(PropertyValueMatcher(lineSortKey(get("line-sort-key")))) + ) + val latLngs = listOf( + LatLng(), + LatLng(1.0, 1.0) + ) + for (i in 0 until 2) { + val line = Line(latLngs).apply { + zLayer = 2 + } + lineManager.add(line) + lineManager.updateSourceNow() + Mockito.verify(lineLayer, Mockito.times(1)).setProperties( + ArgumentMatchers.argThat(PropertyValueMatcher(lineSortKey(get("line-sort-key")))) + ) + } + } + @Test fun testLineJoinLayerProperty() { lineManager = LineManager( From 9297a9cb6e7f19f2095663510905163f2352dada Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Fri, 13 Oct 2023 13:15:34 +0200 Subject: [PATCH 10/10] Add documentation to new MapLibreMap methods --- .../maplibre/android/maps/MapLibreMap.java | 45 +++++++++++++++---- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/maps/MapLibreMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/maps/MapLibreMap.java index 1a0ba0df383..bdfe45c2885 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/maps/MapLibreMap.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/org/maplibre/android/maps/MapLibreMap.java @@ -1003,26 +1003,46 @@ void notifyStyleLoaded() { * four times for all of the subtypes of Annotation. */ + /** + * Adds the provided symbol to the map, to be rendered at its position using its text and icon. + * @param annotation symbol to be added + */ public void addAnnotation(@NonNull Symbol annotation) { annotation.attach(this, nextId++); annotationContainer.add(annotation); } + /** + * Adds the provided circle to the map, to be rendered at its position using its radius and colors. + * @param annotation circle to be added + */ public void addAnnotation(@NonNull Circle annotation) { annotation.attach(this, nextId++); annotationContainer.add(annotation); } + /** + * Adds the provided line to the map, to be rendered according to its path. + * @param annotation line to be added + */ public void addAnnotation(@NonNull Line annotation) { annotation.attach(this, nextId++); annotationContainer.add(annotation); } + /** + * Adds the provided fill to the map, to be rendered according to its paths. + * @param annotation fill to be added + */ public void addAnnotation(@NonNull Fill annotation) { annotation.attach(this, nextId++); annotationContainer.add(annotation); } + /** + * Adds all provided annotations to the map. + * @param annotations list of annotations to be added + */ public void addAnnotations(@NonNull KAnnotation... annotations) { for (KAnnotation annotation : annotations) { annotation.attach(this, nextId++); @@ -1030,11 +1050,17 @@ public void addAnnotations(@NonNull KAnnotation... annotations) { } } + /** + * Forces redraw of annotations. Annotations are automatically redrawn if any of their properties + * are changed, or if a new map style is applied. + */ public void updateAnnotations() { annotationContainer.updateAll(); } /** + * Propagate updates of a single annotation. Called automatically if annotation properties are changed. + *

* Only to be used when the association of annotations to managers has not changed. * Otherwise, use {@link #updateAnnotations()}. */ @@ -1042,10 +1068,18 @@ public void updateAnnotation(@NonNull KAnnotation annotation) { annotationContainer.update(annotation); } + /** + * Removes an annotation from the map. + * @param annotation the annotation to be removed + */ public void removeAnnotation(@NonNull KAnnotation annotation) { annotationContainer.remove(annotation); } + /** + * Removes a series of annotations from the map. + * @param annotations the annotations to be removed + */ public void removeAnnotations(@NonNull KAnnotation... annotations) { for (KAnnotation annotation : annotations) { annotationContainer.remove(annotation); @@ -1300,23 +1334,16 @@ public void removeAnnotations(@NonNull List annotationList /** * Removes all annotations from the map. - * - * @deprecated As of 7.0.0, - * use - * MapLibre Annotation Plugin instead */ - @Deprecated public void removeAnnotations() { annotationManager.removeAnnotations(); annotationContainer.clear(); } /** - * Removes all markers, polylines, polygons, overlays, etc from the map. + * Removes all annotations from the map. * - * @deprecated As of 7.0.0, - * use - * MapLibre Annotation Plugin instead + * @deprecated use {@link #removeAnnotations()} instead. */ @Deprecated public void clear() {