Fast scroll bug fix
This commit is contained in:
parent
18b7b91b6f
commit
6258e25669
@ -1,24 +1,25 @@
|
|||||||
package centre.sciprog.maps.compose
|
package centre.sciprog.maps.compose
|
||||||
|
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
|
|
||||||
//TODO replace zoom range with zoom-based representation change
|
//TODO replace zoom range with zoom-based representation change
|
||||||
sealed class MapFeature(val zoomRange: ClosedFloatingPointRange<Double>)
|
sealed class MapFeature(val zoomRange: ClosedFloatingPointRange<Double>)
|
||||||
|
|
||||||
private val defaultRange = 0.0..18.0
|
private val defaultZoomRange = 1.0..18.0
|
||||||
|
|
||||||
private fun Pair<Double, Double>.toCoordinates() = GeodeticMapCoordinates.ofDegrees(first, second)
|
private fun Pair<Double, Double>.toCoordinates() = GeodeticMapCoordinates.ofDegrees(first, second)
|
||||||
|
|
||||||
class MapCircleFeature(
|
class MapCircleFeature(
|
||||||
val center: GeodeticMapCoordinates,
|
val center: GeodeticMapCoordinates,
|
||||||
zoomRange: ClosedFloatingPointRange<Double> = defaultRange,
|
zoomRange: ClosedFloatingPointRange<Double> = defaultZoomRange,
|
||||||
val size: Float = 5f,
|
val size: Float = 5f,
|
||||||
val color: Color = Color.Red,
|
val color: Color = Color.Red,
|
||||||
) : MapFeature(zoomRange)
|
) : MapFeature(zoomRange)
|
||||||
|
|
||||||
fun MapCircleFeature(
|
fun MapCircleFeature(
|
||||||
centerCoordinates: Pair<Double, Double>,
|
centerCoordinates: Pair<Double, Double>,
|
||||||
zoomRange: ClosedFloatingPointRange<Double> = defaultRange,
|
zoomRange: ClosedFloatingPointRange<Double> = defaultZoomRange,
|
||||||
size: Float = 5f,
|
size: Float = 5f,
|
||||||
color: Color = Color.Red,
|
color: Color = Color.Red,
|
||||||
) = MapCircleFeature(
|
) = MapCircleFeature(
|
||||||
@ -28,17 +29,30 @@ fun MapCircleFeature(
|
|||||||
color
|
color
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class MapLineFeature(
|
class MapLineFeature(
|
||||||
val a: GeodeticMapCoordinates,
|
val a: GeodeticMapCoordinates,
|
||||||
val b: GeodeticMapCoordinates,
|
val b: GeodeticMapCoordinates,
|
||||||
zoomRange: ClosedFloatingPointRange<Double> = defaultRange,
|
zoomRange: ClosedFloatingPointRange<Double> = defaultZoomRange,
|
||||||
val color: Color = Color.Red,
|
val color: Color = Color.Red,
|
||||||
) : MapFeature(zoomRange)
|
) : MapFeature(zoomRange)
|
||||||
|
|
||||||
fun MapLineFeature(
|
fun MapLineFeature(
|
||||||
aCoordinates: Pair<Double, Double>,
|
aCoordinates: Pair<Double, Double>,
|
||||||
bCoordinates: Pair<Double, Double>,
|
bCoordinates: Pair<Double, Double>,
|
||||||
zoomRange: ClosedFloatingPointRange<Double> = defaultRange,
|
zoomRange: ClosedFloatingPointRange<Double> = defaultZoomRange,
|
||||||
color: Color = Color.Red,
|
color: Color = Color.Red,
|
||||||
) = MapLineFeature(aCoordinates.toCoordinates(), bCoordinates.toCoordinates(), zoomRange, color)
|
) = MapLineFeature(aCoordinates.toCoordinates(), bCoordinates.toCoordinates(), zoomRange, color)
|
||||||
|
|
||||||
|
class MapTextFeature(
|
||||||
|
val position: GeodeticMapCoordinates,
|
||||||
|
val text: String,
|
||||||
|
zoomRange: ClosedFloatingPointRange<Double> = defaultZoomRange,
|
||||||
|
val color: Color = Color.Red,
|
||||||
|
): MapFeature(zoomRange)
|
||||||
|
|
||||||
|
class MapImageFeature(
|
||||||
|
val position: GeodeticMapCoordinates,
|
||||||
|
val image: ImageBitmap,
|
||||||
|
zoomRange: ClosedFloatingPointRange<Double> = defaultZoomRange,
|
||||||
|
val color: Color = Color.Red,
|
||||||
|
): MapFeature(zoomRange)
|
@ -9,8 +9,11 @@ import androidx.compose.ui.ExperimentalComposeUiApi
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.geometry.Offset
|
import androidx.compose.ui.geometry.Offset
|
||||||
import androidx.compose.ui.geometry.Size
|
import androidx.compose.ui.geometry.Size
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.ImageBitmap
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
import androidx.compose.ui.graphics.drawscope.clipRect
|
import androidx.compose.ui.graphics.drawscope.clipRect
|
||||||
|
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
|
||||||
|
import androidx.compose.ui.graphics.nativeCanvas
|
||||||
import androidx.compose.ui.graphics.toComposeImageBitmap
|
import androidx.compose.ui.graphics.toComposeImageBitmap
|
||||||
import androidx.compose.ui.input.pointer.PointerEventType
|
import androidx.compose.ui.input.pointer.PointerEventType
|
||||||
import androidx.compose.ui.input.pointer.PointerInputChange
|
import androidx.compose.ui.input.pointer.PointerInputChange
|
||||||
@ -30,6 +33,7 @@ import java.net.URL
|
|||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlin.io.path.*
|
import kotlin.io.path.*
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
|
||||||
private const val TILE_SIZE = 256
|
private const val TILE_SIZE = 256
|
||||||
@ -89,6 +93,14 @@ private class OsMapCache(val scope: CoroutineScope, val client: HttpClient, priv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun Color.toPaint(): org.jetbrains.skia.Paint = org.jetbrains.skia.Paint().setARGB(
|
||||||
|
(alpha * 256).toInt(),
|
||||||
|
(red * 256).toInt(),
|
||||||
|
(green * 256).toInt(),
|
||||||
|
(blue * 256).toInt()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
private fun Double.toIndex(): Int = floor(this / TILE_SIZE).toInt()
|
private fun Double.toIndex(): Int = floor(this / TILE_SIZE).toInt()
|
||||||
private fun Int.toCoordinate(): Double = (this * TILE_SIZE).toDouble()
|
private fun Int.toCoordinate(): Double = (this * TILE_SIZE).toDouble()
|
||||||
|
|
||||||
@ -115,6 +127,7 @@ fun MapView(
|
|||||||
val centerCoordinates by derivedStateOf { viewPoint.toMercator() }
|
val centerCoordinates by derivedStateOf { viewPoint.toMercator() }
|
||||||
|
|
||||||
LaunchedEffect(viewPoint, canvasSize) {
|
LaunchedEffect(viewPoint, canvasSize) {
|
||||||
|
val z = viewPoint.zoom.toInt()
|
||||||
val left = centerCoordinates.x - canvasSize.width / 2
|
val left = centerCoordinates.x - canvasSize.width / 2
|
||||||
val right = centerCoordinates.x + canvasSize.width / 2
|
val right = centerCoordinates.x + canvasSize.width / 2
|
||||||
val horizontalIndices = left.toIndex()..right.toIndex()
|
val horizontalIndices = left.toIndex()..right.toIndex()
|
||||||
@ -125,11 +138,15 @@ fun MapView(
|
|||||||
|
|
||||||
mapTiles.clear()
|
mapTiles.clear()
|
||||||
|
|
||||||
|
val indexRange = 0 until 2.0.pow(z).toInt()
|
||||||
|
|
||||||
for (j in verticalIndices) {
|
for (j in verticalIndices) {
|
||||||
for (i in horizontalIndices) {
|
for (i in horizontalIndices) {
|
||||||
val tileId = OsMapTileId(viewPoint.zoom.toInt(), i, j)
|
if(z == viewPoint.zoom.toInt() && i in indexRange && j in indexRange) {
|
||||||
val tile = mapCache.loadTile(tileId)
|
val tileId = OsMapTileId(z, i, j)
|
||||||
mapTiles.add(tile)
|
val tile = mapCache.loadTile(tileId)
|
||||||
|
mapTiles.add(tile)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,10 +203,20 @@ fun MapView(
|
|||||||
topLeft = offset
|
topLeft = offset
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
features.filter { viewPoint.zoom in it.zoomRange }.forEach {
|
features.filter { viewPoint.zoom in it.zoomRange }.forEach { feature ->
|
||||||
when (it) {
|
when (feature) {
|
||||||
is MapCircleFeature -> drawCircle(it.color, it.size, center = it.center.toOffset())
|
is MapCircleFeature -> drawCircle(
|
||||||
is MapLineFeature -> drawLine(it.color, it.a.toOffset(), it.b.toOffset())
|
feature.color,
|
||||||
|
feature.size,
|
||||||
|
center = feature.center.toOffset()
|
||||||
|
)
|
||||||
|
is MapLineFeature -> drawLine(feature.color, feature.a.toOffset(), feature.b.toOffset())
|
||||||
|
is MapImageFeature -> drawImage(feature.image, feature.position.toOffset())
|
||||||
|
is MapTextFeature -> drawIntoCanvas {
|
||||||
|
val offset = feature.position.toOffset()
|
||||||
|
it.nativeCanvas.drawString(feature.text, offset.x, offset.y, null, feature.color.toPaint())
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ fun MapViewPoint.move(delta: GeodeticMapCoordinates): MapViewPoint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun MapViewPoint.zoom(zoomDelta: Double): MapViewPoint {
|
fun MapViewPoint.zoom(zoomDelta: Double): MapViewPoint {
|
||||||
return copy(zoom = (zoom + zoomDelta).coerceIn(1.0, 18.0))
|
return copy(zoom = (zoom + zoomDelta).coerceIn(2.0, 18.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun MapViewPoint.toMercator() = WebMercatorProjection.toMercator(focus, zoom)
|
fun MapViewPoint.toMercator() = WebMercatorProjection.toMercator(focus, zoom)
|
Loading…
Reference in New Issue
Block a user