diff --git a/build.gradle.kts b/build.gradle.kts index 0f0c14a..abaaad7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { group = "center.sciprog" - version = "0.2.0-dev-1" + version = "0.2.1-dev-1" } apiValidation{ diff --git a/gradle.properties b/gradle.properties index 02f2449..2a403eb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,9 +1,10 @@ kotlin.code.style=official compose.version=1.2.2 - -agp.version=4.2.2 +agp.version=7.3.1 android.useAndroidX=true org.jetbrains.compose.experimental.jscanvas.enabled=true +org.gradle.jvmargs=-Xmx4096m + toolsVersion=0.13.3-kotlin-1.7.20 \ No newline at end of file diff --git a/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapView.kt b/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapView.kt index a8ad123..08b64b7 100644 --- a/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapView.kt +++ b/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapView.kt @@ -2,7 +2,6 @@ package center.sciprog.maps.compose import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import center.sciprog.maps.coordinates.Gmc import center.sciprog.maps.features.* @@ -10,8 +9,8 @@ import center.sciprog.maps.features.* @Composable public expect fun MapView( - mapState: MapViewScope, - featuresState: FeatureGroup, + viewScope: MapViewScope, + features: FeatureGroup, modifier: Modifier = Modifier.fillMaxSize(), ) @@ -21,27 +20,21 @@ public expect fun MapView( @Composable public fun MapView( mapTileProvider: MapTileProvider, - initialViewPoint: MapViewPoint? = null, + features: FeatureGroup, + initialViewPoint: ViewPoint? = null, initialRectangle: Rectangle? = null, - featureMap: Map, MapFeature>, config: ViewConfig = ViewConfig(), modifier: Modifier = Modifier.fillMaxSize(), ) { - val featureState = remember(featureMap) { - FeatureGroup.build(WebMercatorSpace) { - featureMap.forEach { feature(it.key.id, it.value) } - } - } - - val mapState: MapViewScope = rememberMapState( + val mapState: MapViewScope = MapViewScope.remember( mapTileProvider, config, 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 public fun MapView( mapTileProvider: MapTileProvider, - initialViewPoint: MapViewPoint? = null, + initialViewPoint: ViewPoint? = null, initialRectangle: Rectangle? = null, config: ViewConfig = ViewConfig(), modifier: Modifier = Modifier.fillMaxSize(), @@ -63,11 +56,14 @@ public fun MapView( val featureState = FeatureGroup.remember(WebMercatorSpace, buildFeatures) - val mapState: MapViewScope = rememberMapState( + val mapState: MapViewScope = MapViewScope.remember( mapTileProvider, config, initialViewPoint = initialViewPoint, - initialRectangle = initialRectangle ?: featureState.features.computeBoundingBox(WebMercatorSpace, Float.MAX_VALUE), + initialRectangle = initialRectangle ?: featureState.features.computeBoundingBox( + WebMercatorSpace, + Float.MAX_VALUE + ), ) MapView(mapState, featureState, modifier) diff --git a/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapViewPoint.kt b/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapViewPoint.kt index 014fe50..9b6487f 100644 --- a/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapViewPoint.kt +++ b/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapViewPoint.kt @@ -1,13 +1,15 @@ 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 kotlin.math.pow /** * 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 zoom: Float, ) : ViewPoint{ @@ -16,29 +18,4 @@ public data class MapViewPoint( public companion object{ 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)) } \ No newline at end of file diff --git a/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapViewScope.kt b/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapViewScope.kt index 7895384..062014f 100644 --- a/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapViewScope.kt +++ b/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapViewScope.kt @@ -60,7 +60,7 @@ public class MapViewScope internal constructor( canvasSize.height.value / rectangle.latitudeDelta.radians.value ) * PI / mapTileProvider.tileSize ) - return MapViewPoint(rectangle.center, zoom.toFloat()) + return space.ViewPoint(rectangle.center, zoom.toFloat()) } override fun ViewPoint.moveBy(x: Dp, y: Dp): ViewPoint { @@ -73,22 +73,24 @@ public class MapViewScope internal constructor( ), focus.longitude + (deltaX / scaleFactor).radians ) - return MapViewPoint(newCoordinates, zoom) + return space.ViewPoint(newCoordinates, zoom) } -} -@Composable -internal fun rememberMapState( - mapTileProvider: MapTileProvider, - config: ViewConfig, - initialViewPoint: MapViewPoint? = null, - initialRectangle: Rectangle? = null, -): MapViewScope = remember { - MapViewScope(mapTileProvider, config).also { mapState-> - if (initialViewPoint != null) { - mapState.viewPoint = initialViewPoint - } else if (initialRectangle != null) { - mapState.viewPoint = mapState.computeViewPoint(initialRectangle) + public companion object { + @Composable + public fun remember( + mapTileProvider: MapTileProvider, + config: ViewConfig = ViewConfig(), + initialViewPoint: ViewPoint? = null, + initialRectangle: Rectangle? = null, + ): MapViewScope = remember { + MapViewScope(mapTileProvider, config).also { mapState -> + if (initialViewPoint != null) { + mapState.viewPoint = initialViewPoint + } else if (initialRectangle != null) { + mapState.viewPoint = mapState.computeViewPoint(initialRectangle) + } + } } } } \ No newline at end of file diff --git a/maps-kt-compose/src/jvmMain/kotlin/center/sciprog/maps/compose/mapViewJvm.kt b/maps-kt-compose/src/jvmMain/kotlin/center/sciprog/maps/compose/mapViewJvm.kt index 3384e26..362612b 100644 --- a/maps-kt-compose/src/jvmMain/kotlin/center/sciprog/maps/compose/mapViewJvm.kt +++ b/maps-kt-compose/src/jvmMain/kotlin/center/sciprog/maps/compose/mapViewJvm.kt @@ -1,7 +1,6 @@ package center.sciprog.maps.compose import androidx.compose.foundation.Canvas -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -44,11 +43,10 @@ private val logger = KotlinLogging.logger("MapView") */ @Composable public actual fun MapView( - mapState: MapViewScope, - featuresState: FeatureGroup, + viewScope: MapViewScope, + features: FeatureGroup, modifier: Modifier, -): Unit = with(mapState) { - +): Unit = with(viewScope) { val mapTiles = remember(mapTileProvider) { mutableStateListOf() } // Load tiles asynchronously @@ -89,57 +87,56 @@ public actual fun MapView( } } } + key(viewScope, features) { + val painterCache: Map, Painter> = + features.features.filterIsInstance>().associateWith { it.getPainter() } - val painterCache: Map, Painter> = key(featuresState) { - featuresState.features.filterIsInstance>().associateWith { it.getPainter() } - } + Canvas(modifier = modifier.mapControls(viewScope, features)) { - - Canvas(modifier = modifier.mapControls(mapState, featuresState).fillMaxSize()) { - - if (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 - ) + if (canvasSize != size.toDpSize()) { + logger.debug { "Recalculate canvas. Size: $size" } + config.onCanvasSizeChange(canvasSize) + canvasSize = size.toDpSize() } - featuresState.featureMap.values.sortedBy { it.z } - .filter { viewPoint.zoom in it.zoomRange } - .forEach { feature -> - drawFeature(mapState, 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) + 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 + ) + } + + 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) + ) + ) + } } } } diff --git a/maps-kt-features/src/commonMain/kotlin/center.sciprog.attributes/Attributes.kt b/maps-kt-features/src/commonMain/kotlin/center.sciprog.attributes/Attributes.kt index 5e8ecc3..3421521 100644 --- a/maps-kt-features/src/commonMain/kotlin/center.sciprog.attributes/Attributes.kt +++ b/maps-kt-features/src/commonMain/kotlin/center.sciprog.attributes/Attributes.kt @@ -1,11 +1,9 @@ package center.sciprog.attributes -import androidx.compose.runtime.Stable import center.sciprog.maps.features.Feature import center.sciprog.maps.features.ZAttribute import kotlin.jvm.JvmInline -@Stable @JvmInline public value class Attributes internal constructor(internal val map: Map, Any>) { @Suppress("UNCHECKED_CAST") diff --git a/maps-kt-scheme/src/jvmMain/kotlin/center/sciprog/maps/scheme/SchemeView.kt b/maps-kt-scheme/src/jvmMain/kotlin/center/sciprog/maps/scheme/SchemeView.kt index 1aad880..e05582f 100644 --- a/maps-kt-scheme/src/jvmMain/kotlin/center/sciprog/maps/scheme/SchemeView.kt +++ b/maps-kt-scheme/src/jvmMain/kotlin/center/sciprog/maps/scheme/SchemeView.kt @@ -23,15 +23,15 @@ private val logger = KotlinLogging.logger("SchemeView") @Composable public fun SchemeView( state: XYViewScope, - featuresState: FeatureGroup, - modifier: Modifier = Modifier, -) = key(state, featuresState) { + features: FeatureGroup, + modifier: Modifier = Modifier.fillMaxSize(), +) = key(state, features) { with(state) { //Can't do that inside canvas val painterCache: Map, Painter> = - featuresState.features.filterIsInstance>().associateWith { it.getPainter() } + features.features.filterIsInstance>().associateWith { it.getPainter() } - Canvas(modifier = modifier.mapControls(state, featuresState).fillMaxSize()) { + Canvas(modifier = modifier.mapControls(state, features)) { if (canvasSize != size.toDpSize()) { canvasSize = size.toDpSize() @@ -39,7 +39,7 @@ public fun SchemeView( } clipRect { - featuresState.featureMap.values.sortedBy { it.z } + features.featureMap.values.sortedBy { it.z } .filter { viewPoint.zoom in it.zoomRange } .forEach { feature -> drawFeature(state, painterCache, feature) @@ -80,30 +80,20 @@ public fun Rectangle.computeViewPoint( */ @Composable public fun SchemeView( + features: FeatureGroup, initialViewPoint: ViewPoint? = null, initialRectangle: Rectangle? = null, - featureMap: Map, Feature>, config: ViewConfig = ViewConfig(), modifier: Modifier = Modifier.fillMaxSize(), ) { - - val featureState = key(featureMap) { - FeatureGroup.build(XYCoordinateSpace) { - featureMap.forEach { feature(it.key.id, it.value) } - } - } - val state = XYViewScope.remember( config, initialViewPoint = initialViewPoint, - initialRectangle = initialRectangle ?: featureState.features.computeBoundingBox( - XYCoordinateSpace, - Float.MAX_VALUE - ), + initialRectangle = initialRectangle ?: features.getBoundingBox(Float.MAX_VALUE), ) - SchemeView(state, featureState, modifier) + SchemeView(state, features, modifier) } /**