API fixes
This commit is contained in:
parent
6278235b51
commit
190834634f
@ -8,7 +8,7 @@ plugins {
|
|||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
group = "center.sciprog"
|
group = "center.sciprog"
|
||||||
version = "0.2.0-dev-1"
|
version = "0.2.1-dev-1"
|
||||||
}
|
}
|
||||||
|
|
||||||
apiValidation{
|
apiValidation{
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
|
|
||||||
compose.version=1.2.2
|
compose.version=1.2.2
|
||||||
|
agp.version=7.3.1
|
||||||
agp.version=4.2.2
|
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
org.jetbrains.compose.experimental.jscanvas.enabled=true
|
org.jetbrains.compose.experimental.jscanvas.enabled=true
|
||||||
|
|
||||||
|
org.gradle.jvmargs=-Xmx4096m
|
||||||
|
|
||||||
toolsVersion=0.13.3-kotlin-1.7.20
|
toolsVersion=0.13.3-kotlin-1.7.20
|
@ -2,7 +2,6 @@ package center.sciprog.maps.compose
|
|||||||
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import center.sciprog.maps.coordinates.Gmc
|
import center.sciprog.maps.coordinates.Gmc
|
||||||
import center.sciprog.maps.features.*
|
import center.sciprog.maps.features.*
|
||||||
@ -10,8 +9,8 @@ import center.sciprog.maps.features.*
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
public expect fun MapView(
|
public expect fun MapView(
|
||||||
mapState: MapViewScope,
|
viewScope: MapViewScope,
|
||||||
featuresState: FeatureGroup<Gmc>,
|
features: FeatureGroup<Gmc>,
|
||||||
modifier: Modifier = Modifier.fillMaxSize(),
|
modifier: Modifier = Modifier.fillMaxSize(),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -21,27 +20,21 @@ public expect fun MapView(
|
|||||||
@Composable
|
@Composable
|
||||||
public fun MapView(
|
public fun MapView(
|
||||||
mapTileProvider: MapTileProvider,
|
mapTileProvider: MapTileProvider,
|
||||||
initialViewPoint: MapViewPoint? = null,
|
features: FeatureGroup<Gmc>,
|
||||||
|
initialViewPoint: ViewPoint<Gmc>? = null,
|
||||||
initialRectangle: Rectangle<Gmc>? = null,
|
initialRectangle: Rectangle<Gmc>? = null,
|
||||||
featureMap: Map<FeatureId<*>, MapFeature>,
|
|
||||||
config: ViewConfig<Gmc> = ViewConfig(),
|
config: ViewConfig<Gmc> = ViewConfig(),
|
||||||
modifier: Modifier = Modifier.fillMaxSize(),
|
modifier: Modifier = Modifier.fillMaxSize(),
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val featureState = remember(featureMap) {
|
val mapState: MapViewScope = MapViewScope.remember(
|
||||||
FeatureGroup.build(WebMercatorSpace) {
|
|
||||||
featureMap.forEach { feature(it.key.id, it.value) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val mapState: MapViewScope = rememberMapState(
|
|
||||||
mapTileProvider,
|
mapTileProvider,
|
||||||
config,
|
config,
|
||||||
initialViewPoint = initialViewPoint,
|
initialViewPoint = initialViewPoint,
|
||||||
initialRectangle = initialRectangle ?: featureState.features.computeBoundingBox(WebMercatorSpace, Float.MAX_VALUE),
|
initialRectangle = initialRectangle ?: features.getBoundingBox(Float.MAX_VALUE),
|
||||||
)
|
)
|
||||||
|
|
||||||
MapView(mapState, featureState, modifier)
|
MapView(mapState, features, modifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,7 +47,7 @@ public fun MapView(
|
|||||||
@Composable
|
@Composable
|
||||||
public fun MapView(
|
public fun MapView(
|
||||||
mapTileProvider: MapTileProvider,
|
mapTileProvider: MapTileProvider,
|
||||||
initialViewPoint: MapViewPoint? = null,
|
initialViewPoint: ViewPoint<Gmc>? = null,
|
||||||
initialRectangle: Rectangle<Gmc>? = null,
|
initialRectangle: Rectangle<Gmc>? = null,
|
||||||
config: ViewConfig<Gmc> = ViewConfig(),
|
config: ViewConfig<Gmc> = ViewConfig(),
|
||||||
modifier: Modifier = Modifier.fillMaxSize(),
|
modifier: Modifier = Modifier.fillMaxSize(),
|
||||||
@ -63,11 +56,14 @@ public fun MapView(
|
|||||||
|
|
||||||
val featureState = FeatureGroup.remember(WebMercatorSpace, buildFeatures)
|
val featureState = FeatureGroup.remember(WebMercatorSpace, buildFeatures)
|
||||||
|
|
||||||
val mapState: MapViewScope = rememberMapState(
|
val mapState: MapViewScope = MapViewScope.remember(
|
||||||
mapTileProvider,
|
mapTileProvider,
|
||||||
config,
|
config,
|
||||||
initialViewPoint = initialViewPoint,
|
initialViewPoint = initialViewPoint,
|
||||||
initialRectangle = initialRectangle ?: featureState.features.computeBoundingBox(WebMercatorSpace, Float.MAX_VALUE),
|
initialRectangle = initialRectangle ?: featureState.features.computeBoundingBox(
|
||||||
|
WebMercatorSpace,
|
||||||
|
Float.MAX_VALUE
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
MapView(mapState, featureState, modifier)
|
MapView(mapState, featureState, modifier)
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
package center.sciprog.maps.compose
|
package center.sciprog.maps.compose
|
||||||
|
|
||||||
import center.sciprog.maps.coordinates.*
|
import center.sciprog.maps.coordinates.GeodeticMapCoordinates
|
||||||
|
import center.sciprog.maps.coordinates.Gmc
|
||||||
|
import center.sciprog.maps.coordinates.WebMercatorProjection
|
||||||
|
import center.sciprog.maps.coordinates.radians
|
||||||
import center.sciprog.maps.features.ViewPoint
|
import center.sciprog.maps.features.ViewPoint
|
||||||
import kotlin.math.pow
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Observable position on the map. Includes observation coordinate and [zoom] factor
|
* Observable position on the map. Includes observation coordinate and [zoom] factor
|
||||||
*/
|
*/
|
||||||
public data class MapViewPoint(
|
internal data class MapViewPoint(
|
||||||
override val focus: GeodeticMapCoordinates,
|
override val focus: GeodeticMapCoordinates,
|
||||||
override val zoom: Float,
|
override val zoom: Float,
|
||||||
) : ViewPoint<Gmc>{
|
) : ViewPoint<Gmc>{
|
||||||
@ -16,29 +18,4 @@ public data class MapViewPoint(
|
|||||||
public companion object{
|
public companion object{
|
||||||
public val globe: MapViewPoint = MapViewPoint(GeodeticMapCoordinates(0.0.radians, 0.0.radians), 1f)
|
public val globe: MapViewPoint = MapViewPoint(GeodeticMapCoordinates(0.0.radians, 0.0.radians), 1f)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public fun MapViewPoint.move(delta: GeodeticMapCoordinates): MapViewPoint {
|
|
||||||
val newCoordinates = GeodeticMapCoordinates(
|
|
||||||
(focus.latitude + delta.latitude).coerceIn(
|
|
||||||
-MercatorProjection.MAXIMUM_LATITUDE,
|
|
||||||
MercatorProjection.MAXIMUM_LATITUDE
|
|
||||||
),
|
|
||||||
focus.longitude + delta.longitude
|
|
||||||
)
|
|
||||||
return MapViewPoint(newCoordinates, zoom)
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun MapViewPoint.zoom(
|
|
||||||
zoomDelta: Float,
|
|
||||||
invariant: GeodeticMapCoordinates = focus,
|
|
||||||
): MapViewPoint = if (invariant == focus) {
|
|
||||||
copy(zoom = (zoom + zoomDelta).coerceIn(2f, 18f))
|
|
||||||
} else {
|
|
||||||
val difScale = (1 - 2f.pow(-zoomDelta))
|
|
||||||
val newCenter = GeodeticMapCoordinates(
|
|
||||||
focus.latitude + (invariant.latitude - focus.latitude) * difScale,
|
|
||||||
focus.longitude + (invariant.longitude - focus.longitude) * difScale
|
|
||||||
)
|
|
||||||
MapViewPoint(newCenter, (zoom + zoomDelta).coerceIn(2f, 18f))
|
|
||||||
}
|
}
|
@ -60,7 +60,7 @@ public class MapViewScope internal constructor(
|
|||||||
canvasSize.height.value / rectangle.latitudeDelta.radians.value
|
canvasSize.height.value / rectangle.latitudeDelta.radians.value
|
||||||
) * PI / mapTileProvider.tileSize
|
) * PI / mapTileProvider.tileSize
|
||||||
)
|
)
|
||||||
return MapViewPoint(rectangle.center, zoom.toFloat())
|
return space.ViewPoint(rectangle.center, zoom.toFloat())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun ViewPoint<Gmc>.moveBy(x: Dp, y: Dp): ViewPoint<Gmc> {
|
override fun ViewPoint<Gmc>.moveBy(x: Dp, y: Dp): ViewPoint<Gmc> {
|
||||||
@ -73,22 +73,24 @@ public class MapViewScope internal constructor(
|
|||||||
),
|
),
|
||||||
focus.longitude + (deltaX / scaleFactor).radians
|
focus.longitude + (deltaX / scaleFactor).radians
|
||||||
)
|
)
|
||||||
return MapViewPoint(newCoordinates, zoom)
|
return space.ViewPoint(newCoordinates, zoom)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
public companion object {
|
||||||
internal fun rememberMapState(
|
@Composable
|
||||||
mapTileProvider: MapTileProvider,
|
public fun remember(
|
||||||
config: ViewConfig<Gmc>,
|
mapTileProvider: MapTileProvider,
|
||||||
initialViewPoint: MapViewPoint? = null,
|
config: ViewConfig<Gmc> = ViewConfig(),
|
||||||
initialRectangle: Rectangle<Gmc>? = null,
|
initialViewPoint: ViewPoint<Gmc>? = null,
|
||||||
): MapViewScope = remember {
|
initialRectangle: Rectangle<Gmc>? = null,
|
||||||
MapViewScope(mapTileProvider, config).also { mapState->
|
): MapViewScope = remember {
|
||||||
if (initialViewPoint != null) {
|
MapViewScope(mapTileProvider, config).also { mapState ->
|
||||||
mapState.viewPoint = initialViewPoint
|
if (initialViewPoint != null) {
|
||||||
} else if (initialRectangle != null) {
|
mapState.viewPoint = initialViewPoint
|
||||||
mapState.viewPoint = mapState.computeViewPoint(initialRectangle)
|
} else if (initialRectangle != null) {
|
||||||
|
mapState.viewPoint = mapState.computeViewPoint(initialRectangle)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,6 @@
|
|||||||
package center.sciprog.maps.compose
|
package center.sciprog.maps.compose
|
||||||
|
|
||||||
import androidx.compose.foundation.Canvas
|
import androidx.compose.foundation.Canvas
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
@ -44,11 +43,10 @@ private val logger = KotlinLogging.logger("MapView")
|
|||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
public actual fun MapView(
|
public actual fun MapView(
|
||||||
mapState: MapViewScope,
|
viewScope: MapViewScope,
|
||||||
featuresState: FeatureGroup<Gmc>,
|
features: FeatureGroup<Gmc>,
|
||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
): Unit = with(mapState) {
|
): Unit = with(viewScope) {
|
||||||
|
|
||||||
val mapTiles = remember(mapTileProvider) { mutableStateListOf<MapTile>() }
|
val mapTiles = remember(mapTileProvider) { mutableStateListOf<MapTile>() }
|
||||||
|
|
||||||
// Load tiles asynchronously
|
// Load tiles asynchronously
|
||||||
@ -89,57 +87,56 @@ public actual fun MapView(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
key(viewScope, features) {
|
||||||
|
val painterCache: Map<PainterFeature<Gmc>, Painter> =
|
||||||
|
features.features.filterIsInstance<PainterFeature<Gmc>>().associateWith { it.getPainter() }
|
||||||
|
|
||||||
val painterCache: Map<PainterFeature<Gmc>, Painter> = key(featuresState) {
|
Canvas(modifier = modifier.mapControls(viewScope, features)) {
|
||||||
featuresState.features.filterIsInstance<PainterFeature<Gmc>>().associateWith { it.getPainter() }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (canvasSize != size.toDpSize()) {
|
||||||
Canvas(modifier = modifier.mapControls(mapState, featuresState).fillMaxSize()) {
|
logger.debug { "Recalculate canvas. Size: $size" }
|
||||||
|
config.onCanvasSizeChange(canvasSize)
|
||||||
if (canvasSize != size.toDpSize()) {
|
canvasSize = size.toDpSize()
|
||||||
logger.debug { "Recalculate canvas. Size: $size" }
|
|
||||||
config.onCanvasSizeChange(canvasSize)
|
|
||||||
canvasSize = size.toDpSize()
|
|
||||||
}
|
|
||||||
|
|
||||||
clipRect {
|
|
||||||
val tileSize = IntSize(
|
|
||||||
ceil((mapTileProvider.tileSize.dp * tileScale).toPx()).toInt(),
|
|
||||||
ceil((mapTileProvider.tileSize.dp * tileScale).toPx()).toInt()
|
|
||||||
)
|
|
||||||
mapTiles.forEach { (id, image) ->
|
|
||||||
//converting back from tile index to screen offset
|
|
||||||
val offset = IntOffset(
|
|
||||||
(canvasSize.width / 2 + (mapTileProvider.toCoordinate(id.i).dp - centerCoordinates.x.dp) * tileScale).roundToPx(),
|
|
||||||
(canvasSize.height / 2 + (mapTileProvider.toCoordinate(id.j).dp - centerCoordinates.y.dp) * tileScale).roundToPx()
|
|
||||||
)
|
|
||||||
drawImage(
|
|
||||||
image = image.toComposeImageBitmap(),
|
|
||||||
dstOffset = offset,
|
|
||||||
dstSize = tileSize
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
featuresState.featureMap.values.sortedBy { it.z }
|
clipRect {
|
||||||
.filter { viewPoint.zoom in it.zoomRange }
|
val tileSize = IntSize(
|
||||||
.forEach { feature ->
|
ceil((mapTileProvider.tileSize.dp * tileScale).toPx()).toInt(),
|
||||||
drawFeature(mapState, painterCache, feature)
|
ceil((mapTileProvider.tileSize.dp * tileScale).toPx()).toInt()
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
selectRect?.let { dpRect ->
|
|
||||||
val rect = dpRect.toRect()
|
|
||||||
drawRect(
|
|
||||||
color = Color.Blue,
|
|
||||||
topLeft = rect.topLeft,
|
|
||||||
size = rect.size,
|
|
||||||
alpha = 0.5f,
|
|
||||||
style = Stroke(
|
|
||||||
width = 2f,
|
|
||||||
pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f), 0f)
|
|
||||||
)
|
)
|
||||||
)
|
mapTiles.forEach { (id, image) ->
|
||||||
|
//converting back from tile index to screen offset
|
||||||
|
val offset = IntOffset(
|
||||||
|
(canvasSize.width / 2 + (mapTileProvider.toCoordinate(id.i).dp - centerCoordinates.x.dp) * tileScale).roundToPx(),
|
||||||
|
(canvasSize.height / 2 + (mapTileProvider.toCoordinate(id.j).dp - centerCoordinates.y.dp) * tileScale).roundToPx()
|
||||||
|
)
|
||||||
|
drawImage(
|
||||||
|
image = image.toComposeImageBitmap(),
|
||||||
|
dstOffset = offset,
|
||||||
|
dstSize = tileSize
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
features.featureMap.values.sortedBy { it.z }
|
||||||
|
.filter { viewPoint.zoom in it.zoomRange }
|
||||||
|
.forEach { feature ->
|
||||||
|
drawFeature(viewScope, painterCache, feature)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selectRect?.let { dpRect ->
|
||||||
|
val rect = dpRect.toRect()
|
||||||
|
drawRect(
|
||||||
|
color = Color.Blue,
|
||||||
|
topLeft = rect.topLeft,
|
||||||
|
size = rect.size,
|
||||||
|
alpha = 0.5f,
|
||||||
|
style = Stroke(
|
||||||
|
width = 2f,
|
||||||
|
pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f), 0f)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
package center.sciprog.attributes
|
package center.sciprog.attributes
|
||||||
|
|
||||||
import androidx.compose.runtime.Stable
|
|
||||||
import center.sciprog.maps.features.Feature
|
import center.sciprog.maps.features.Feature
|
||||||
import center.sciprog.maps.features.ZAttribute
|
import center.sciprog.maps.features.ZAttribute
|
||||||
import kotlin.jvm.JvmInline
|
import kotlin.jvm.JvmInline
|
||||||
|
|
||||||
@Stable
|
|
||||||
@JvmInline
|
@JvmInline
|
||||||
public value class Attributes internal constructor(internal val map: Map<Attribute<*>, Any>) {
|
public value class Attributes internal constructor(internal val map: Map<Attribute<*>, Any>) {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
@ -23,15 +23,15 @@ private val logger = KotlinLogging.logger("SchemeView")
|
|||||||
@Composable
|
@Composable
|
||||||
public fun SchemeView(
|
public fun SchemeView(
|
||||||
state: XYViewScope,
|
state: XYViewScope,
|
||||||
featuresState: FeatureGroup<XY>,
|
features: FeatureGroup<XY>,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier.fillMaxSize(),
|
||||||
) = key(state, featuresState) {
|
) = key(state, features) {
|
||||||
with(state) {
|
with(state) {
|
||||||
//Can't do that inside canvas
|
//Can't do that inside canvas
|
||||||
val painterCache: Map<PainterFeature<XY>, Painter> =
|
val painterCache: Map<PainterFeature<XY>, Painter> =
|
||||||
featuresState.features.filterIsInstance<PainterFeature<XY>>().associateWith { it.getPainter() }
|
features.features.filterIsInstance<PainterFeature<XY>>().associateWith { it.getPainter() }
|
||||||
|
|
||||||
Canvas(modifier = modifier.mapControls(state, featuresState).fillMaxSize()) {
|
Canvas(modifier = modifier.mapControls(state, features)) {
|
||||||
|
|
||||||
if (canvasSize != size.toDpSize()) {
|
if (canvasSize != size.toDpSize()) {
|
||||||
canvasSize = size.toDpSize()
|
canvasSize = size.toDpSize()
|
||||||
@ -39,7 +39,7 @@ public fun SchemeView(
|
|||||||
}
|
}
|
||||||
|
|
||||||
clipRect {
|
clipRect {
|
||||||
featuresState.featureMap.values.sortedBy { it.z }
|
features.featureMap.values.sortedBy { it.z }
|
||||||
.filter { viewPoint.zoom in it.zoomRange }
|
.filter { viewPoint.zoom in it.zoomRange }
|
||||||
.forEach { feature ->
|
.forEach { feature ->
|
||||||
drawFeature(state, painterCache, feature)
|
drawFeature(state, painterCache, feature)
|
||||||
@ -80,30 +80,20 @@ public fun Rectangle<XY>.computeViewPoint(
|
|||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
public fun SchemeView(
|
public fun SchemeView(
|
||||||
|
features: FeatureGroup<XY>,
|
||||||
initialViewPoint: ViewPoint<XY>? = null,
|
initialViewPoint: ViewPoint<XY>? = null,
|
||||||
initialRectangle: Rectangle<XY>? = null,
|
initialRectangle: Rectangle<XY>? = null,
|
||||||
featureMap: Map<FeatureId<*>, Feature<XY>>,
|
|
||||||
config: ViewConfig<XY> = ViewConfig(),
|
config: ViewConfig<XY> = ViewConfig(),
|
||||||
modifier: Modifier = Modifier.fillMaxSize(),
|
modifier: Modifier = Modifier.fillMaxSize(),
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
||||||
val featureState = key(featureMap) {
|
|
||||||
FeatureGroup.build(XYCoordinateSpace) {
|
|
||||||
featureMap.forEach { feature(it.key.id, it.value) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val state = XYViewScope.remember(
|
val state = XYViewScope.remember(
|
||||||
config,
|
config,
|
||||||
initialViewPoint = initialViewPoint,
|
initialViewPoint = initialViewPoint,
|
||||||
initialRectangle = initialRectangle ?: featureState.features.computeBoundingBox(
|
initialRectangle = initialRectangle ?: features.getBoundingBox(Float.MAX_VALUE),
|
||||||
XYCoordinateSpace,
|
|
||||||
Float.MAX_VALUE
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
SchemeView(state, featureState, modifier)
|
SchemeView(state, features, modifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user