Change state propagation to better support dynamic features. Drag not working for now
This commit is contained in:
parent
fa51fc03db
commit
4c71b8e7bb
@ -28,21 +28,16 @@ public interface MapFeatureBuilder {
|
|||||||
|
|
||||||
public fun <T> setAttribute(id: FeatureId, key: MapFeatureAttributeKey<T>, value: T)
|
public fun <T> setAttribute(id: FeatureId, key: MapFeatureAttributeKey<T>, value: T)
|
||||||
|
|
||||||
public val features: MutableMap<FeatureId, MapFeature>
|
|
||||||
|
|
||||||
public val attributes: Map<FeatureId, MapFeatureAttributeSet>
|
|
||||||
|
|
||||||
//TODO use context receiver for that
|
//TODO use context receiver for that
|
||||||
public fun FeatureId.draggable(enabled: Boolean = true) {
|
public fun FeatureId.draggable(enabled: Boolean = true) {
|
||||||
setAttribute(this, DraggableAttribute, enabled)
|
setAttribute(this, DraggableAttribute, enabled)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class MapFeatureBuilderImpl(
|
internal class MapFeatureBuilderImpl: MapFeatureBuilder {
|
||||||
override val features: SnapshotStateMap<FeatureId, MapFeature>,
|
|
||||||
) : MapFeatureBuilder {
|
|
||||||
|
|
||||||
private val _attributes = SnapshotStateMap<FeatureId, SnapshotStateMap<MapFeatureAttributeKey<out Any?>, in Any?>>()
|
internal val features = mutableStateMapOf<FeatureId, MapFeature>()
|
||||||
|
internal val attributes = SnapshotStateMap<FeatureId, SnapshotStateMap<MapFeatureAttributeKey<out Any?>, in Any?>>()
|
||||||
|
|
||||||
|
|
||||||
private fun generateID(feature: MapFeature): FeatureId = "@feature[${feature.hashCode().toUInt()}]"
|
private fun generateID(feature: MapFeature): FeatureId = "@feature[${feature.hashCode().toUInt()}]"
|
||||||
@ -54,14 +49,13 @@ internal class MapFeatureBuilderImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun <T> setAttribute(id: FeatureId, key: MapFeatureAttributeKey<T>, value: T) {
|
override fun <T> setAttribute(id: FeatureId, key: MapFeatureAttributeKey<T>, value: T) {
|
||||||
_attributes.getOrPut(id) { SnapshotStateMap() }[key] = value
|
attributes.getOrPut(id) { SnapshotStateMap() }[key] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
override val attributes: Map<FeatureId, MapFeatureAttributeSet>
|
|
||||||
get() = _attributes.mapValues { MapFeatureAttributeSet(it.value) }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public fun MapFeatureBuilder.circle(
|
public fun MapFeatureBuilder.circle(
|
||||||
center: GeodeticMapCoordinates,
|
center: GeodeticMapCoordinates,
|
||||||
zoomRange: IntRange = defaultZoomRange,
|
zoomRange: IntRange = defaultZoomRange,
|
||||||
@ -195,7 +189,7 @@ public fun MapFeatureBuilder.group(
|
|||||||
id: FeatureId? = null,
|
id: FeatureId? = null,
|
||||||
builder: MapFeatureBuilder.() -> Unit,
|
builder: MapFeatureBuilder.() -> Unit,
|
||||||
): FeatureId {
|
): FeatureId {
|
||||||
val map = MapFeatureBuilderImpl(mutableStateMapOf()).apply(builder).features
|
val map = MapFeatureBuilderImpl().apply(builder).features
|
||||||
val feature = MapFeatureGroup(map, zoomRange)
|
val feature = MapFeatureGroup(map, zoomRange)
|
||||||
return addFeature(id, feature)
|
return addFeature(id, feature)
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,11 @@ package center.sciprog.maps.compose
|
|||||||
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.snapshots.SnapshotStateMap
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.input.pointer.PointerEvent
|
import androidx.compose.ui.input.pointer.PointerEvent
|
||||||
import androidx.compose.ui.input.pointer.isPrimaryPressed
|
import androidx.compose.ui.input.pointer.isPrimaryPressed
|
||||||
import androidx.compose.ui.unit.DpSize
|
import androidx.compose.ui.unit.DpSize
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import center.sciprog.maps.coordinates.*
|
import center.sciprog.maps.coordinates.*
|
||||||
import kotlin.math.PI
|
import kotlin.math.PI
|
||||||
import kotlin.math.log2
|
import kotlin.math.log2
|
||||||
@ -87,6 +87,8 @@ internal fun GmcRectangle.computeViewPoint(
|
|||||||
return MapViewPoint(center, zoom)
|
return MapViewPoint(center, zoom)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val defaultCanvasSize = DpSize(512.dp, 512.dp)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draw a map using convenient parameters. If neither [initialViewPoint], noe [initialRectangle] is defined,
|
* Draw a map using convenient parameters. If neither [initialViewPoint], noe [initialRectangle] is defined,
|
||||||
* use map features to infer view region.
|
* use map features to infer view region.
|
||||||
@ -102,46 +104,48 @@ public fun MapView(
|
|||||||
config: MapViewConfig = MapViewConfig(),
|
config: MapViewConfig = MapViewConfig(),
|
||||||
modifier: Modifier = Modifier.fillMaxSize(),
|
modifier: Modifier = Modifier.fillMaxSize(),
|
||||||
buildFeatures: @Composable (MapFeatureBuilder.() -> Unit) = {},
|
buildFeatures: @Composable (MapFeatureBuilder.() -> Unit) = {},
|
||||||
) {
|
): Unit = key(buildFeatures) {
|
||||||
|
|
||||||
var viewPointOverride by remember(initialViewPoint, initialRectangle) { mutableStateOf(initialViewPoint ?: MapViewPoint.globe) }
|
val featuresBuilder = MapFeatureBuilderImpl().apply { buildFeatures() }
|
||||||
|
|
||||||
val featuresBuilder = MapFeatureBuilderImpl(mutableStateMapOf()).apply { buildFeatures() }
|
val features = featuresBuilder.features
|
||||||
|
val attributes = featuresBuilder.attributes
|
||||||
|
|
||||||
val features: SnapshotStateMap<FeatureId, MapFeature> = remember(buildFeatures) { featuresBuilder.features }
|
var cachedCanvasSize: DpSize by remember { mutableStateOf(defaultCanvasSize) }
|
||||||
|
|
||||||
val attributes = remember(buildFeatures) { featuresBuilder.attributes }
|
val viewPointOverride: MapViewPoint = remember(initialViewPoint, initialRectangle, cachedCanvasSize) {
|
||||||
|
initialViewPoint
|
||||||
val featureDrag by derivedStateOf {
|
?: initialRectangle?.computeViewPoint(mapTileProvider, cachedCanvasSize)
|
||||||
DragHandle.withPrimaryButton { _, start, end ->
|
?: features.values.computeBoundingBox(1.0)?.computeViewPoint(mapTileProvider, cachedCanvasSize)
|
||||||
val zoom = start.zoom
|
?: MapViewPoint.globe
|
||||||
attributes.filterValues {
|
|
||||||
it[DraggableAttribute] ?: false
|
|
||||||
}.keys.forEach { id ->
|
|
||||||
val feature = features[id] as? DraggableMapFeature ?: return@forEach
|
|
||||||
//val border = WebMercatorProjection.scaleFactor(zoom)
|
|
||||||
val boundingBox = feature.getBoundingBox(zoom) ?: return@forEach
|
|
||||||
if (start.focus in boundingBox) {
|
|
||||||
features[id] = feature.withCoordinates(end.focus)
|
|
||||||
return@withPrimaryButton false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return@withPrimaryButton true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val featureDrag = DragHandle.withPrimaryButton { _, start, end ->
|
||||||
|
val zoom = start.zoom
|
||||||
|
attributes.filterValues {
|
||||||
|
it[DraggableAttribute] as? Boolean ?: false
|
||||||
|
}.keys.forEach { id ->
|
||||||
|
val feature = features[id] as? DraggableMapFeature ?: return@forEach
|
||||||
|
//val border = WebMercatorProjection.scaleFactor(zoom)
|
||||||
|
val boundingBox = feature.getBoundingBox(zoom) ?: return@forEach
|
||||||
|
if (start.focus in boundingBox) {
|
||||||
|
features[id] = feature.withCoordinates(end.focus)
|
||||||
|
return@withPrimaryButton false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return@withPrimaryButton true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
val newConfig = config.copy(
|
val newConfig = config.copy(
|
||||||
dragHandle = DragHandle.combine(featureDrag, config.dragHandle),
|
dragHandle = DragHandle.combine(featureDrag, config.dragHandle),
|
||||||
onCanvasSizeChange = { canvasSize ->
|
onCanvasSizeChange = { canvasSize ->
|
||||||
viewPointOverride = initialViewPoint
|
|
||||||
?: initialRectangle?.computeViewPoint(mapTileProvider, canvasSize)
|
|
||||||
?: features.values.computeBoundingBox(1.0)?.computeViewPoint(mapTileProvider, canvasSize)
|
|
||||||
?: MapViewPoint.globe
|
|
||||||
|
|
||||||
config.onCanvasSizeChange(canvasSize)
|
config.onCanvasSizeChange(canvasSize)
|
||||||
|
cachedCanvasSize = canvasSize
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
MapView(
|
MapView(
|
||||||
mapTileProvider = mapTileProvider,
|
mapTileProvider = mapTileProvider,
|
||||||
initialViewPoint = viewPointOverride,
|
initialViewPoint = viewPointOverride,
|
||||||
|
@ -53,17 +53,11 @@ public actual fun MapView(
|
|||||||
features: Map<FeatureId, MapFeature>,
|
features: Map<FeatureId, MapFeature>,
|
||||||
config: MapViewConfig,
|
config: MapViewConfig,
|
||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
) {
|
): Unit = key(initialViewPoint) {
|
||||||
var canvasSize by remember { mutableStateOf(DpSize(512.dp, 512.dp)) }
|
var canvasSize by remember { mutableStateOf(DpSize(512.dp, 512.dp)) }
|
||||||
|
|
||||||
var viewPointOverride by remember { mutableStateOf(initialViewPoint) }
|
|
||||||
var viewPoint by remember { mutableStateOf(initialViewPoint) }
|
var viewPoint by remember { mutableStateOf(initialViewPoint) }
|
||||||
|
|
||||||
if (viewPointOverride != initialViewPoint) {
|
|
||||||
viewPoint = initialViewPoint
|
|
||||||
viewPointOverride = initialViewPoint
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setViewPoint(newViewPoint: MapViewPoint) {
|
fun setViewPoint(newViewPoint: MapViewPoint) {
|
||||||
config.onViewChange(newViewPoint)
|
config.onViewChange(newViewPoint)
|
||||||
viewPoint = newViewPoint
|
viewPoint = newViewPoint
|
||||||
@ -78,7 +72,7 @@ public actual fun MapView(
|
|||||||
2.0.pow(viewPoint.zoom - zoom)
|
2.0.pow(viewPoint.zoom - zoom)
|
||||||
}
|
}
|
||||||
|
|
||||||
val mapTiles = remember { mutableStateListOf<MapTile>() }
|
val mapTiles = remember(mapTileProvider) { mutableStateListOf<MapTile>() }
|
||||||
|
|
||||||
val centerCoordinates by derivedStateOf { WebMercatorProjection.toMercator(viewPoint.focus, zoom) }
|
val centerCoordinates by derivedStateOf { WebMercatorProjection.toMercator(viewPoint.focus, zoom) }
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user