126 lines
4.1 KiB
Kotlin
Raw Normal View History

2022-07-23 10:58:16 +03:00
package center.sciprog.maps.compose
2022-07-11 09:36:43 +03:00
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.key
import androidx.compose.runtime.remember
2022-07-11 09:36:43 +03:00
import androidx.compose.ui.Modifier
2022-08-30 10:29:56 +03:00
import androidx.compose.ui.input.pointer.PointerEvent
2022-07-14 20:19:57 +03:00
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
2022-07-23 11:24:37 +03:00
import center.sciprog.maps.coordinates.*
2022-07-14 20:19:57 +03:00
import kotlin.math.PI
import kotlin.math.log2
import kotlin.math.min
2022-07-11 09:36:43 +03:00
2022-07-13 11:36:02 +03:00
2022-07-19 12:31:09 +03:00
//TODO consider replacing by modifier
/**
*/
2022-07-23 13:49:47 +03:00
public data class MapViewConfig(
2022-07-13 11:36:02 +03:00
val zoomSpeed: Double = 1.0 / 3.0,
2022-08-30 10:29:56 +03:00
val onClick: MapViewPoint.(PointerEvent) -> Unit = {},
val dragHandle: DragHandle = DragHandle.BYPASS,
2022-07-19 12:31:09 +03:00
val onViewChange: MapViewPoint.() -> Unit = {},
2022-08-29 13:38:46 +03:00
val onSelect: (GmcRectangle) -> Unit = {},
val zoomOnSelect: Boolean = true,
val onCanvasSizeChange: (DpSize) -> Unit = {},
2022-07-13 11:36:02 +03:00
)
2022-07-11 09:36:43 +03:00
@Composable
2022-07-23 13:49:47 +03:00
public expect fun MapView(
2022-07-11 09:36:43 +03:00
mapTileProvider: MapTileProvider,
initialViewPoint: MapViewPoint,
featuresState: MapFeaturesState,
2022-07-13 11:36:02 +03:00
config: MapViewConfig = MapViewConfig(),
modifier: Modifier = Modifier.fillMaxSize(),
)
internal val defaultCanvasSize = DpSize(512.dp, 512.dp)
public fun GmcRectangle.computeViewPoint(
mapTileProvider: MapTileProvider,
canvasSize: DpSize = defaultCanvasSize,
): MapViewPoint {
val zoom = log2(
min(
canvasSize.width.value / longitudeDelta.radians.value,
canvasSize.height.value / latitudeDelta.radians.value
) * PI / mapTileProvider.tileSize
)
return MapViewPoint(center, zoom)
}
/**
* A builder for a Map with static features.
*/
@Composable
public fun MapView(
mapTileProvider: MapTileProvider,
initialViewPoint: MapViewPoint? = null,
initialRectangle: GmcRectangle? = null,
2022-12-09 22:21:24 +03:00
featureMap: Map<FeatureId<*>, MapFeature>,
config: MapViewConfig = MapViewConfig(),
modifier: Modifier = Modifier.fillMaxSize(),
) {
val featuresState = key(featureMap) {
MapFeaturesState.build {
2022-12-09 22:21:24 +03:00
featureMap.forEach { feature(it.key.id, it.value) }
}
}
val viewPointOverride: MapViewPoint = remember(initialViewPoint, initialRectangle) {
initialViewPoint
?: initialRectangle?.computeViewPoint(mapTileProvider)
?: featureMap.values.computeBoundingBox(1.0)?.computeViewPoint(mapTileProvider)
?: MapViewPoint.globe
}
MapView(mapTileProvider, viewPointOverride, featuresState, config, modifier)
}
/**
* Draw a map using convenient parameters. If neither [initialViewPoint], noe [initialRectangle] is defined,
* use map features to infer view region.
* @param initialViewPoint The view point of the map using center and zoom. Is used if provided
* @param initialRectangle The rectangle to be used for view point computation. Used if [initialViewPoint] is not defined.
* @param buildFeatures - a builder for features
*/
@Composable
2022-07-23 13:49:47 +03:00
public fun MapView(
2022-07-14 20:19:57 +03:00
mapTileProvider: MapTileProvider,
initialViewPoint: MapViewPoint? = null,
initialRectangle: GmcRectangle? = null,
2022-07-14 20:19:57 +03:00
config: MapViewConfig = MapViewConfig(),
modifier: Modifier = Modifier.fillMaxSize(),
2022-09-14 12:11:15 +03:00
buildFeatures: MapFeaturesState.() -> Unit = {},
) {
val featureState = MapFeaturesState.remember(buildFeatures)
2022-09-14 12:11:15 +03:00
val viewPointOverride: MapViewPoint = remember(initialViewPoint, initialRectangle) {
initialViewPoint
?: initialRectangle?.computeViewPoint(mapTileProvider)
2022-12-17 21:36:05 +03:00
?: featureState.features.values.computeBoundingBox(1.0)?.computeViewPoint(mapTileProvider)
?: MapViewPoint.globe
}
val featureDrag: DragHandle = DragHandle.withPrimaryButton { event, start, end ->
featureState.forEachWithAttribute(DraggableAttribute) { _, handle ->
2022-12-09 22:21:24 +03:00
if (!handle.handle(event, start, end)) return@withPrimaryButton false
}
true
2022-08-31 22:48:52 +03:00
}
val newConfig = config.copy(
dragHandle = DragHandle.combine(featureDrag, config.dragHandle)
)
MapView(
mapTileProvider = mapTileProvider,
initialViewPoint = viewPointOverride,
featuresState = featureState,
config = newConfig,
modifier = modifier,
)
}