Reorder some API
This commit is contained in:
parent
4c71b8e7bb
commit
693f608b14
@ -2,6 +2,7 @@ package center.sciprog.maps.compose
|
|||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.mutableStateMapOf
|
import androidx.compose.runtime.mutableStateMapOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.snapshots.SnapshotStateMap
|
import androidx.compose.runtime.snapshots.SnapshotStateMap
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.PointMode
|
import androidx.compose.ui.graphics.PointMode
|
||||||
@ -13,50 +14,57 @@ import center.sciprog.maps.coordinates.*
|
|||||||
|
|
||||||
public typealias FeatureId = String
|
public typealias FeatureId = String
|
||||||
|
|
||||||
public interface MapFeatureAttributeKey<T>
|
public object DraggableAttribute : MapFeaturesState.Attribute<Boolean>
|
||||||
|
|
||||||
|
public class MapFeaturesState internal constructor(
|
||||||
public class MapFeatureAttributeSet(private val map: Map<MapFeatureAttributeKey<*>, *>) {
|
private val features: MutableMap<FeatureId, MapFeature>,
|
||||||
public operator fun <T> get(key: MapFeatureAttributeKey<*>): T? = map[key]?.let {
|
private val attributes: MutableMap<FeatureId, SnapshotStateMap<Attribute<out Any?>, in Any?>>,
|
||||||
@Suppress("UNCHECKED_CAST")
|
) {
|
||||||
it as T
|
public interface Attribute<T>
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface MapFeatureBuilder {
|
|
||||||
public fun addFeature(id: FeatureId?, feature: MapFeature): FeatureId
|
|
||||||
|
|
||||||
public fun <T> setAttribute(id: FeatureId, key: MapFeatureAttributeKey<T>, value: T)
|
|
||||||
|
|
||||||
//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: MapFeatureBuilder {
|
public fun features(): Map<FeatureId, MapFeature> = features
|
||||||
|
|
||||||
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()}]"
|
||||||
|
|
||||||
override fun addFeature(id: FeatureId?, feature: MapFeature): FeatureId {
|
public fun addFeature(id: FeatureId?, feature: MapFeature): FeatureId {
|
||||||
val safeId = id ?: generateID(feature)
|
val safeId = id ?: generateID(feature)
|
||||||
features[id ?: generateID(feature)] = feature
|
features[id ?: generateID(feature)] = feature
|
||||||
return safeId
|
return safeId
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun <T> setAttribute(id: FeatureId, key: MapFeatureAttributeKey<T>, value: T) {
|
public fun <T> setAttribute(id: FeatureId, key: MapFeaturesState.Attribute<T>, value: T) {
|
||||||
attributes.getOrPut(id) { SnapshotStateMap() }[key] = value
|
attributes.getOrPut(id) { mutableStateMapOf() }[key] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
public fun <T> getAttribute(id: FeatureId, key: MapFeaturesState.Attribute<T>): T? =
|
||||||
|
attributes[id]?.get(key)?.let { it as T }
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
public fun <T> findAllWithAttribute(key: MapFeaturesState.Attribute<T>, condition: (T) -> Boolean): Set<FeatureId> {
|
||||||
|
return attributes.filterValues {
|
||||||
|
condition(it[key] as T)
|
||||||
|
}.keys
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
public fun rememberMapFeatureState(
|
||||||
|
builder: @Composable MapFeaturesState.() -> Unit = {},
|
||||||
|
): MapFeaturesState = remember(builder) {
|
||||||
|
MapFeaturesState(
|
||||||
|
mutableStateMapOf(),
|
||||||
|
mutableStateMapOf()
|
||||||
|
)
|
||||||
|
}.apply { builder() }
|
||||||
|
|
||||||
public fun MapFeatureBuilder.circle(
|
public fun MapFeaturesState.circle(
|
||||||
center: GeodeticMapCoordinates,
|
center: GeodeticMapCoordinates,
|
||||||
zoomRange: IntRange = defaultZoomRange,
|
zoomRange: IntRange = defaultZoomRange,
|
||||||
size: Float = 5f,
|
size: Float = 5f,
|
||||||
@ -66,7 +74,7 @@ public fun MapFeatureBuilder.circle(
|
|||||||
id, MapCircleFeature(center, zoomRange, size, color)
|
id, MapCircleFeature(center, zoomRange, size, color)
|
||||||
)
|
)
|
||||||
|
|
||||||
public fun MapFeatureBuilder.circle(
|
public fun MapFeaturesState.circle(
|
||||||
centerCoordinates: Pair<Double, Double>,
|
centerCoordinates: Pair<Double, Double>,
|
||||||
zoomRange: IntRange = defaultZoomRange,
|
zoomRange: IntRange = defaultZoomRange,
|
||||||
size: Float = 5f,
|
size: Float = 5f,
|
||||||
@ -76,7 +84,7 @@ public fun MapFeatureBuilder.circle(
|
|||||||
id, MapCircleFeature(centerCoordinates.toCoordinates(), zoomRange, size, color)
|
id, MapCircleFeature(centerCoordinates.toCoordinates(), zoomRange, size, color)
|
||||||
)
|
)
|
||||||
|
|
||||||
public fun MapFeatureBuilder.rectangle(
|
public fun MapFeaturesState.rectangle(
|
||||||
centerCoordinates: Pair<Double, Double>,
|
centerCoordinates: Pair<Double, Double>,
|
||||||
zoomRange: IntRange = defaultZoomRange,
|
zoomRange: IntRange = defaultZoomRange,
|
||||||
size: DpSize = DpSize(5.dp, 5.dp),
|
size: DpSize = DpSize(5.dp, 5.dp),
|
||||||
@ -86,14 +94,14 @@ public fun MapFeatureBuilder.rectangle(
|
|||||||
id, MapRectangleFeature(centerCoordinates.toCoordinates(), zoomRange, size, color)
|
id, MapRectangleFeature(centerCoordinates.toCoordinates(), zoomRange, size, color)
|
||||||
)
|
)
|
||||||
|
|
||||||
public fun MapFeatureBuilder.draw(
|
public fun MapFeaturesState.draw(
|
||||||
position: Pair<Double, Double>,
|
position: Pair<Double, Double>,
|
||||||
zoomRange: IntRange = defaultZoomRange,
|
zoomRange: IntRange = defaultZoomRange,
|
||||||
id: FeatureId? = null,
|
id: FeatureId? = null,
|
||||||
drawFeature: DrawScope.() -> Unit,
|
drawFeature: DrawScope.() -> Unit,
|
||||||
): FeatureId = addFeature(id, MapDrawFeature(position.toCoordinates(), zoomRange, drawFeature))
|
): FeatureId = addFeature(id, MapDrawFeature(position.toCoordinates(), zoomRange, drawFeature))
|
||||||
|
|
||||||
public fun MapFeatureBuilder.line(
|
public fun MapFeaturesState.line(
|
||||||
aCoordinates: Gmc,
|
aCoordinates: Gmc,
|
||||||
bCoordinates: Gmc,
|
bCoordinates: Gmc,
|
||||||
zoomRange: IntRange = defaultZoomRange,
|
zoomRange: IntRange = defaultZoomRange,
|
||||||
@ -104,7 +112,7 @@ public fun MapFeatureBuilder.line(
|
|||||||
MapLineFeature(aCoordinates, bCoordinates, zoomRange, color)
|
MapLineFeature(aCoordinates, bCoordinates, zoomRange, color)
|
||||||
)
|
)
|
||||||
|
|
||||||
public fun MapFeatureBuilder.line(
|
public fun MapFeaturesState.line(
|
||||||
curve: GmcCurve,
|
curve: GmcCurve,
|
||||||
zoomRange: IntRange = defaultZoomRange,
|
zoomRange: IntRange = defaultZoomRange,
|
||||||
color: Color = Color.Red,
|
color: Color = Color.Red,
|
||||||
@ -114,7 +122,7 @@ public fun MapFeatureBuilder.line(
|
|||||||
MapLineFeature(curve.forward.coordinates, curve.backward.coordinates, zoomRange, color)
|
MapLineFeature(curve.forward.coordinates, curve.backward.coordinates, zoomRange, color)
|
||||||
)
|
)
|
||||||
|
|
||||||
public fun MapFeatureBuilder.line(
|
public fun MapFeaturesState.line(
|
||||||
aCoordinates: Pair<Double, Double>,
|
aCoordinates: Pair<Double, Double>,
|
||||||
bCoordinates: Pair<Double, Double>,
|
bCoordinates: Pair<Double, Double>,
|
||||||
zoomRange: IntRange = defaultZoomRange,
|
zoomRange: IntRange = defaultZoomRange,
|
||||||
@ -125,7 +133,7 @@ public fun MapFeatureBuilder.line(
|
|||||||
MapLineFeature(aCoordinates.toCoordinates(), bCoordinates.toCoordinates(), zoomRange, color)
|
MapLineFeature(aCoordinates.toCoordinates(), bCoordinates.toCoordinates(), zoomRange, color)
|
||||||
)
|
)
|
||||||
|
|
||||||
public fun MapFeatureBuilder.arc(
|
public fun MapFeaturesState.arc(
|
||||||
oval: GmcRectangle,
|
oval: GmcRectangle,
|
||||||
startAngle: Angle,
|
startAngle: Angle,
|
||||||
arcLength: Angle,
|
arcLength: Angle,
|
||||||
@ -137,7 +145,7 @@ public fun MapFeatureBuilder.arc(
|
|||||||
MapArcFeature(oval, startAngle, arcLength, zoomRange, color)
|
MapArcFeature(oval, startAngle, arcLength, zoomRange, color)
|
||||||
)
|
)
|
||||||
|
|
||||||
public fun MapFeatureBuilder.arc(
|
public fun MapFeaturesState.arc(
|
||||||
center: Pair<Double, Double>,
|
center: Pair<Double, Double>,
|
||||||
radius: Distance,
|
radius: Distance,
|
||||||
startAngle: Angle,
|
startAngle: Angle,
|
||||||
@ -156,7 +164,7 @@ public fun MapFeatureBuilder.arc(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
public fun MapFeatureBuilder.points(
|
public fun MapFeaturesState.points(
|
||||||
points: List<Gmc>,
|
points: List<Gmc>,
|
||||||
zoomRange: IntRange = defaultZoomRange,
|
zoomRange: IntRange = defaultZoomRange,
|
||||||
stroke: Float = 2f,
|
stroke: Float = 2f,
|
||||||
@ -166,7 +174,7 @@ public fun MapFeatureBuilder.points(
|
|||||||
): FeatureId = addFeature(id, MapPointsFeature(points, zoomRange, stroke, color, pointMode))
|
): FeatureId = addFeature(id, MapPointsFeature(points, zoomRange, stroke, color, pointMode))
|
||||||
|
|
||||||
@JvmName("pointsFromPairs")
|
@JvmName("pointsFromPairs")
|
||||||
public fun MapFeatureBuilder.points(
|
public fun MapFeaturesState.points(
|
||||||
points: List<Pair<Double, Double>>,
|
points: List<Pair<Double, Double>>,
|
||||||
zoomRange: IntRange = defaultZoomRange,
|
zoomRange: IntRange = defaultZoomRange,
|
||||||
stroke: Float = 2f,
|
stroke: Float = 2f,
|
||||||
@ -176,7 +184,7 @@ public fun MapFeatureBuilder.points(
|
|||||||
): FeatureId = addFeature(id, MapPointsFeature(points.map { it.toCoordinates() }, zoomRange, stroke, color, pointMode))
|
): FeatureId = addFeature(id, MapPointsFeature(points.map { it.toCoordinates() }, zoomRange, stroke, color, pointMode))
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
public fun MapFeatureBuilder.image(
|
public fun MapFeaturesState.image(
|
||||||
position: Pair<Double, Double>,
|
position: Pair<Double, Double>,
|
||||||
image: ImageVector,
|
image: ImageVector,
|
||||||
size: DpSize = DpSize(20.dp, 20.dp),
|
size: DpSize = DpSize(20.dp, 20.dp),
|
||||||
@ -184,17 +192,18 @@ public fun MapFeatureBuilder.image(
|
|||||||
id: FeatureId? = null,
|
id: FeatureId? = null,
|
||||||
): FeatureId = addFeature(id, MapVectorImageFeature(position.toCoordinates(), image, size, zoomRange))
|
): FeatureId = addFeature(id, MapVectorImageFeature(position.toCoordinates(), image, size, zoomRange))
|
||||||
|
|
||||||
public fun MapFeatureBuilder.group(
|
@Composable
|
||||||
|
public fun MapFeaturesState.group(
|
||||||
zoomRange: IntRange = defaultZoomRange,
|
zoomRange: IntRange = defaultZoomRange,
|
||||||
id: FeatureId? = null,
|
id: FeatureId? = null,
|
||||||
builder: MapFeatureBuilder.() -> Unit,
|
builder: @Composable MapFeaturesState.() -> Unit,
|
||||||
): FeatureId {
|
): FeatureId {
|
||||||
val map = MapFeatureBuilderImpl().apply(builder).features
|
val map = rememberMapFeatureState(builder).features()
|
||||||
val feature = MapFeatureGroup(map, zoomRange)
|
val feature = MapFeatureGroup(map, zoomRange)
|
||||||
return addFeature(id, feature)
|
return addFeature(id, feature)
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun MapFeatureBuilder.text(
|
public fun MapFeaturesState.text(
|
||||||
position: GeodeticMapCoordinates,
|
position: GeodeticMapCoordinates,
|
||||||
text: String,
|
text: String,
|
||||||
zoomRange: IntRange = defaultZoomRange,
|
zoomRange: IntRange = defaultZoomRange,
|
||||||
@ -203,7 +212,7 @@ public fun MapFeatureBuilder.text(
|
|||||||
id: FeatureId? = null,
|
id: FeatureId? = null,
|
||||||
): FeatureId = addFeature(id, MapTextFeature(position, text, zoomRange, color, font))
|
): FeatureId = addFeature(id, MapTextFeature(position, text, zoomRange, color, font))
|
||||||
|
|
||||||
public fun MapFeatureBuilder.text(
|
public fun MapFeaturesState.text(
|
||||||
position: Pair<Double, Double>,
|
position: Pair<Double, Double>,
|
||||||
text: String,
|
text: String,
|
||||||
zoomRange: IntRange = defaultZoomRange,
|
zoomRange: IntRange = defaultZoomRange,
|
@ -68,7 +68,7 @@ public data class MapViewConfig(
|
|||||||
public expect fun MapView(
|
public expect fun MapView(
|
||||||
mapTileProvider: MapTileProvider,
|
mapTileProvider: MapTileProvider,
|
||||||
initialViewPoint: MapViewPoint,
|
initialViewPoint: MapViewPoint,
|
||||||
features: Map<FeatureId, MapFeature>,
|
features: MapFeaturesState,
|
||||||
config: MapViewConfig = MapViewConfig(),
|
config: MapViewConfig = MapViewConfig(),
|
||||||
modifier: Modifier = Modifier.fillMaxSize(),
|
modifier: Modifier = Modifier.fillMaxSize(),
|
||||||
)
|
)
|
||||||
@ -103,13 +103,12 @@ public fun MapView(
|
|||||||
initialRectangle: GmcRectangle? = null,
|
initialRectangle: GmcRectangle? = null,
|
||||||
config: MapViewConfig = MapViewConfig(),
|
config: MapViewConfig = MapViewConfig(),
|
||||||
modifier: Modifier = Modifier.fillMaxSize(),
|
modifier: Modifier = Modifier.fillMaxSize(),
|
||||||
buildFeatures: @Composable (MapFeatureBuilder.() -> Unit) = {},
|
buildFeatures: @Composable (MapFeaturesState.() -> Unit) = {},
|
||||||
): Unit = key(buildFeatures) {
|
): Unit = key(buildFeatures) {
|
||||||
|
|
||||||
val featuresBuilder = MapFeatureBuilderImpl().apply { buildFeatures() }
|
val featureState = rememberMapFeatureState(buildFeatures)
|
||||||
|
|
||||||
val features = featuresBuilder.features
|
val features = featureState.features()
|
||||||
val attributes = featuresBuilder.attributes
|
|
||||||
|
|
||||||
var cachedCanvasSize: DpSize by remember { mutableStateOf(defaultCanvasSize) }
|
var cachedCanvasSize: DpSize by remember { mutableStateOf(defaultCanvasSize) }
|
||||||
|
|
||||||
@ -120,36 +119,37 @@ public fun MapView(
|
|||||||
?: MapViewPoint.globe
|
?: MapViewPoint.globe
|
||||||
}
|
}
|
||||||
|
|
||||||
val featureDrag = DragHandle.withPrimaryButton { _, start, end ->
|
val featureDrag by derivedStateOf {
|
||||||
val zoom = start.zoom
|
DragHandle.withPrimaryButton { _, start, end ->
|
||||||
attributes.filterValues {
|
val zoom = start.zoom
|
||||||
it[DraggableAttribute] as? Boolean ?: false
|
featureState.findAllWithAttribute(DraggableAttribute) { it }.forEach { id ->
|
||||||
}.keys.forEach { id ->
|
val feature = features[id] as? DraggableMapFeature ?: return@forEach
|
||||||
val feature = features[id] as? DraggableMapFeature ?: return@forEach
|
val boundingBox = feature.getBoundingBox(zoom) ?: return@forEach
|
||||||
//val border = WebMercatorProjection.scaleFactor(zoom)
|
if (start.focus in boundingBox) {
|
||||||
val boundingBox = feature.getBoundingBox(zoom) ?: return@forEach
|
featureState.addFeature(id, feature.withCoordinates(end.focus))
|
||||||
if (start.focus in boundingBox) {
|
return@withPrimaryButton false
|
||||||
features[id] = feature.withCoordinates(end.focus)
|
}
|
||||||
return@withPrimaryButton false
|
|
||||||
}
|
}
|
||||||
|
return@withPrimaryButton true
|
||||||
}
|
}
|
||||||
return@withPrimaryButton true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
val newConfig = config.copy(
|
val newConfig by derivedStateOf {
|
||||||
dragHandle = DragHandle.combine(featureDrag, config.dragHandle),
|
config.copy(
|
||||||
onCanvasSizeChange = { canvasSize ->
|
dragHandle = DragHandle.combine(featureDrag, config.dragHandle),
|
||||||
config.onCanvasSizeChange(canvasSize)
|
onCanvasSizeChange = { canvasSize ->
|
||||||
cachedCanvasSize = canvasSize
|
config.onCanvasSizeChange(canvasSize)
|
||||||
}
|
cachedCanvasSize = canvasSize
|
||||||
)
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
MapView(
|
MapView(
|
||||||
mapTileProvider = mapTileProvider,
|
mapTileProvider = mapTileProvider,
|
||||||
initialViewPoint = viewPointOverride,
|
initialViewPoint = viewPointOverride,
|
||||||
features = features,
|
features = featureState,
|
||||||
config = newConfig,
|
config = newConfig,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
package center.sciprog.maps.compose
|
|
||||||
|
|
||||||
public object DraggableAttribute: MapFeatureAttributeKey<Boolean>
|
|
@ -50,7 +50,7 @@ private val logger = KotlinLogging.logger("MapView")
|
|||||||
public actual fun MapView(
|
public actual fun MapView(
|
||||||
mapTileProvider: MapTileProvider,
|
mapTileProvider: MapTileProvider,
|
||||||
initialViewPoint: MapViewPoint,
|
initialViewPoint: MapViewPoint,
|
||||||
features: Map<FeatureId, MapFeature>,
|
features: MapFeaturesState,
|
||||||
config: MapViewConfig,
|
config: MapViewConfig,
|
||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
): Unit = key(initialViewPoint) {
|
): Unit = key(initialViewPoint) {
|
||||||
@ -334,7 +334,7 @@ public actual fun MapView(
|
|||||||
dstSize = tileSize
|
dstSize = tileSize
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
features.values.filter { zoom in it.zoomRange }.forEach { feature ->
|
features.features().values.filter { zoom in it.zoomRange }.forEach { feature ->
|
||||||
drawFeature(zoom, feature)
|
drawFeature(zoom, feature)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user