All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.ovh.plrapps.mapcompose.api.LayerApi.kt Maven / Gradle / Ivy

Go to download

A Compose Multiplatform library to display tiled maps, with support for markers, paths, and rotation

The newest version!
@file:Suppress("unused")

package ovh.plrapps.mapcompose.api

import ovh.plrapps.mapcompose.core.*
import ovh.plrapps.mapcompose.ui.state.MapState
import ovh.plrapps.mapcompose.utils.swap


/**
 * Add a layer. By default, the layer is added on top of the layer stack (see [AboveAll]).
 * Optionally, the layer can be added at the bottom of the stack, or above / below an existing layer.
 *
 * Note that [initialOpacity] is taken into account _only_ if the layer being added isn't the lowest
 * one, or the only one. However, if later on another layer is added below this layer, the
 * [initialOpacity] will be taken into account.
 *
 * @return The id of the created layer
 */
fun MapState.addLayer(
    tileStreamProvider: TileStreamProvider,
    initialOpacity: Float = 1f,
    placement: LayerPlacement = AboveAll
): String {
    val layers = tileCanvasState.layerFlow.value.toMutableList()
    val id = makeLayerId()
    val layer = Layer(id, tileStreamProvider, initialOpacity)

    val newLayers = when (placement) {
        AboveAll -> {
            layers + layer
        }
        is AboveLayer -> {
            val existingLayerIndex = layers.indexOfFirst { it.id == placement.layerId }
            if (existingLayerIndex != -1 && existingLayerIndex < layers.lastIndex) {
                layers.add(existingLayerIndex + 1, layer)
            }
            layers
        }
        BelowAll -> {
            layers.add(0, layer)
            layers
        }
        is BelowLayer -> {
            val existingLayerIndex = layers.indexOfFirst { it.id == placement.layerId }
            if (existingLayerIndex != -1) {
                layers.add(existingLayerIndex, layer)
            }
            layers
        }
    }

    setLayers(newLayers)

    return id
}

/**
 * Replaces a layer. If the layer doesn't exist, no layer is added.
 *
 * @return The id of the added layer, or null if [layerId] doesn't match with any existing layer
 */
fun MapState.replaceLayer(
    layerId: String,
    tileStreamProvider: TileStreamProvider,
    initialOpacity: Float = 1f
): String? {
    val layers = tileCanvasState.layerFlow.value.toMutableList()

    val index = layers.indexOfFirst {
        it.id == layerId
    }

    val id = makeLayerId()

    return if (index != -1) {
        layers[index] = Layer(id, tileStreamProvider, initialOpacity)
        setLayers(layers)
        id
    } else null
}

/**
 * Moves a layer up in the layer stack, making it drawn on top of the layer which was previously
 * above it.
 */
fun MapState.moveLayerUp(layerId: String) {
    val layers = tileCanvasState.layerFlow.value.toMutableList()

    val index = layers.indexOfFirst {
        it.id == layerId
    }

    if (index < layers.lastIndex) {
        layers.swap(index + 1, index)
        setLayers(layers)
    }
}

/**
 * Moves a layer down in the layer stack, making it drawn below the layer which was previously
 * below it.
 */
fun MapState.moveLayerDown(layerId: String) {
    val layers = tileCanvasState.layerFlow.value.toMutableList()

    val index = layers.indexOfFirst {
        it.id == layerId
    }

    if (index > 0) {
        layers.swap(index - 1, index)
        setLayers(layers)
    }
}

/**
 * Reorder layers in the order of the provided list of ids. Layers listed first will be drawn before
 * subsequent layers (so the later will be above).
 * Existing layers not included in the provided list will be removed
 */
fun MapState.reorderLayers(layerIds: List) {
    val layerForId = tileCanvasState.layerFlow.value.associateBy { it.id }
    val layers = layerIds.mapNotNull { layerForId[it] }

    setLayers(layers)
}

/**
 * Remove all layers.
 */
fun MapState.removeAllLayers() {
    setLayers(emptyList())
}

/**
 * Remove some layers.
 */
fun MapState.removeLayers(layerIds: List) {
    val remainingLayers = tileCanvasState.layerFlow.value.filterNot {
        it.id in layerIds
    }
    setLayers(remainingLayers)
}

/**
 * Remove a layer.
 */
fun MapState.removeLayer(layerId: String) {
    val remainingLayers = tileCanvasState.layerFlow.value.filterNot {
        it.id == layerId
    }
    setLayers(remainingLayers)
}

/**
 * Dynamically update the opacity of a layer. If the layer is the lowest one or the only one, the
 * new opacity won't have effect until a layer is added below it.
 */
fun MapState.setLayerOpacity(layerId: String, opacity: Float) {
    val newLayers = tileCanvasState.layerFlow.value.map {
        if (it.id == layerId) {
            it.copy(alpha = opacity.coerceIn(0f..1f))
        } else it
    }
    setLayers(newLayers)
}

/**
 * Utility function to automatically refresh tiles after a change of layers.
 */
private fun MapState.setLayers(layers: List) {
    tileCanvasState.setLayers(layers)
    renderVisibleTilesThrottled()
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy