package refactoring
This commit is contained in:
parent
8867388e85
commit
4c3aefcfae
@ -15,11 +15,12 @@ import center.sciprog.maps.coordinates.GmcBox
|
|||||||
import center.sciprog.maps.coordinates.wrapAll
|
import center.sciprog.maps.coordinates.wrapAll
|
||||||
|
|
||||||
//TODO replace zoom range with zoom-based representation change
|
//TODO replace zoom range with zoom-based representation change
|
||||||
sealed class MapFeature(val zoomRange: IntRange) {
|
public sealed interface MapFeature {
|
||||||
abstract fun getBoundingBox(zoom: Int): GmcBox?
|
public val zoomRange: IntRange
|
||||||
|
public fun getBoundingBox(zoom: Int): GmcBox?
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Iterable<MapFeature>.computeBoundingBox(zoom: Int): GmcBox? =
|
public fun Iterable<MapFeature>.computeBoundingBox(zoom: Int): GmcBox? =
|
||||||
mapNotNull { it.getBoundingBox(zoom) }.wrapAll()
|
mapNotNull { it.getBoundingBox(zoom) }.wrapAll()
|
||||||
|
|
||||||
internal fun Pair<Double, Double>.toCoordinates() = GeodeticMapCoordinates.ofDegrees(first, second)
|
internal fun Pair<Double, Double>.toCoordinates() = GeodeticMapCoordinates.ofDegrees(first, second)
|
||||||
@ -29,68 +30,72 @@ internal val defaultZoomRange = 1..18
|
|||||||
/**
|
/**
|
||||||
* A feature that decides what to show depending on the zoom value (it could change size of shape)
|
* A feature that decides what to show depending on the zoom value (it could change size of shape)
|
||||||
*/
|
*/
|
||||||
class MapFeatureSelector(val selector: (zoom: Int) -> MapFeature) : MapFeature(defaultZoomRange) {
|
public class MapFeatureSelector(
|
||||||
|
public val selector: (zoom: Int) -> MapFeature,
|
||||||
|
) : MapFeature {
|
||||||
|
override val zoomRange: IntRange get() = defaultZoomRange
|
||||||
|
|
||||||
override fun getBoundingBox(zoom: Int): GmcBox? = selector(zoom).getBoundingBox(zoom)
|
override fun getBoundingBox(zoom: Int): GmcBox? = selector(zoom).getBoundingBox(zoom)
|
||||||
}
|
}
|
||||||
|
|
||||||
class MapDrawFeature(
|
public class MapDrawFeature(
|
||||||
val position: GeodeticMapCoordinates,
|
public val position: GeodeticMapCoordinates,
|
||||||
zoomRange: IntRange = defaultZoomRange,
|
override val zoomRange: IntRange = defaultZoomRange,
|
||||||
val drawFeature: DrawScope.() -> Unit,
|
public val drawFeature: DrawScope.() -> Unit,
|
||||||
) : MapFeature(zoomRange) {
|
) : MapFeature{
|
||||||
override fun getBoundingBox(zoom: Int): GmcBox {
|
override fun getBoundingBox(zoom: Int): GmcBox {
|
||||||
//TODO add box computation
|
//TODO add box computation
|
||||||
return GmcBox(position, position)
|
return GmcBox(position, position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MapCircleFeature(
|
public class MapCircleFeature(
|
||||||
val center: GeodeticMapCoordinates,
|
public val center: GeodeticMapCoordinates,
|
||||||
zoomRange: IntRange = defaultZoomRange,
|
override val zoomRange: IntRange = defaultZoomRange,
|
||||||
val size: Float = 5f,
|
public val size: Float = 5f,
|
||||||
val color: Color = Color.Red,
|
public val color: Color = Color.Red,
|
||||||
) : MapFeature(zoomRange) {
|
) : MapFeature {
|
||||||
override fun getBoundingBox(zoom: Int): GmcBox = GmcBox(center, center)
|
override fun getBoundingBox(zoom: Int): GmcBox = GmcBox(center, center)
|
||||||
}
|
}
|
||||||
|
|
||||||
class MapLineFeature(
|
public class MapLineFeature(
|
||||||
val a: GeodeticMapCoordinates,
|
public val a: GeodeticMapCoordinates,
|
||||||
val b: GeodeticMapCoordinates,
|
public val b: GeodeticMapCoordinates,
|
||||||
zoomRange: IntRange = defaultZoomRange,
|
override val zoomRange: IntRange = defaultZoomRange,
|
||||||
val color: Color = Color.Red,
|
public val color: Color = Color.Red,
|
||||||
) : MapFeature(zoomRange) {
|
) : MapFeature {
|
||||||
override fun getBoundingBox(zoom: Int): GmcBox = GmcBox(a, b)
|
override fun getBoundingBox(zoom: Int): GmcBox = GmcBox(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
class MapTextFeature(
|
public class MapTextFeature(
|
||||||
val position: GeodeticMapCoordinates,
|
public val position: GeodeticMapCoordinates,
|
||||||
val text: String,
|
public val text: String,
|
||||||
zoomRange: IntRange = defaultZoomRange,
|
override val zoomRange: IntRange = defaultZoomRange,
|
||||||
val color: Color = Color.Red,
|
public val color: Color = Color.Red,
|
||||||
) : MapFeature(zoomRange) {
|
) : MapFeature {
|
||||||
override fun getBoundingBox(zoom: Int): GmcBox = GmcBox(position, position)
|
override fun getBoundingBox(zoom: Int): GmcBox = GmcBox(position, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
class MapBitmapImageFeature(
|
public class MapBitmapImageFeature(
|
||||||
val position: GeodeticMapCoordinates,
|
public val position: GeodeticMapCoordinates,
|
||||||
val image: ImageBitmap,
|
public val image: ImageBitmap,
|
||||||
val size: IntSize = IntSize(15, 15),
|
public val size: IntSize = IntSize(15, 15),
|
||||||
zoomRange: IntRange = defaultZoomRange,
|
override val zoomRange: IntRange = defaultZoomRange,
|
||||||
) : MapFeature(zoomRange) {
|
) : MapFeature{
|
||||||
override fun getBoundingBox(zoom: Int): GmcBox = GmcBox(position, position)
|
override fun getBoundingBox(zoom: Int): GmcBox = GmcBox(position, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
class MapVectorImageFeature(
|
public class MapVectorImageFeature(
|
||||||
val position: GeodeticMapCoordinates,
|
public val position: GeodeticMapCoordinates,
|
||||||
val painter: Painter,
|
public val painter: Painter,
|
||||||
val size: DpSize,
|
public val size: DpSize,
|
||||||
zoomRange: IntRange = defaultZoomRange,
|
override val zoomRange: IntRange = defaultZoomRange,
|
||||||
) : MapFeature(zoomRange) {
|
) : MapFeature {
|
||||||
override fun getBoundingBox(zoom: Int): GmcBox = GmcBox(position, position)
|
override fun getBoundingBox(zoom: Int): GmcBox = GmcBox(position, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MapVectorImageFeature(
|
public fun MapVectorImageFeature(
|
||||||
position: GeodeticMapCoordinates,
|
position: GeodeticMapCoordinates,
|
||||||
image: ImageVector,
|
image: ImageVector,
|
||||||
size: DpSize = DpSize(20.dp, 20.dp),
|
size: DpSize = DpSize(20.dp, 20.dp),
|
||||||
@ -100,9 +105,9 @@ fun MapVectorImageFeature(
|
|||||||
/**
|
/**
|
||||||
* A group of other features
|
* A group of other features
|
||||||
*/
|
*/
|
||||||
class MapFeatureGroup(
|
public class MapFeatureGroup(
|
||||||
val children: Map<FeatureId, MapFeature>,
|
public val children: Map<FeatureId, MapFeature>,
|
||||||
zoomRange: IntRange = defaultZoomRange,
|
override val zoomRange: IntRange = defaultZoomRange,
|
||||||
) : MapFeature(zoomRange) {
|
) : MapFeature{
|
||||||
override fun getBoundingBox(zoom: Int): GmcBox? = children.values.mapNotNull { it.getBoundingBox(zoom) }.wrapAll()
|
override fun getBoundingBox(zoom: Int): GmcBox? = children.values.mapNotNull { it.getBoundingBox(zoom) }.wrapAll()
|
||||||
}
|
}
|
||||||
|
@ -10,15 +10,15 @@ import androidx.compose.ui.unit.DpSize
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import center.sciprog.maps.coordinates.GeodeticMapCoordinates
|
import center.sciprog.maps.coordinates.GeodeticMapCoordinates
|
||||||
|
|
||||||
typealias FeatureId = String
|
public typealias FeatureId = String
|
||||||
|
|
||||||
interface FeatureBuilder {
|
public interface MapFeatureBuilder {
|
||||||
fun addFeature(id: FeatureId?, feature: MapFeature): FeatureId
|
public fun addFeature(id: FeatureId?, feature: MapFeature): FeatureId
|
||||||
|
|
||||||
fun build(): SnapshotStateMap<FeatureId, MapFeature>
|
public fun build(): SnapshotStateMap<FeatureId, MapFeature>
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class MapFeatureBuilder(initialFeatures: Map<FeatureId, MapFeature>) : FeatureBuilder {
|
internal class MapFeatureBuilderImpl(initialFeatures: Map<FeatureId, MapFeature>) : MapFeatureBuilder {
|
||||||
|
|
||||||
private val content: SnapshotStateMap<FeatureId, MapFeature> = mutableStateMapOf<FeatureId, MapFeature>().apply {
|
private val content: SnapshotStateMap<FeatureId, MapFeature> = mutableStateMapOf<FeatureId, MapFeature>().apply {
|
||||||
putAll(initialFeatures)
|
putAll(initialFeatures)
|
||||||
@ -35,72 +35,72 @@ internal class MapFeatureBuilder(initialFeatures: Map<FeatureId, MapFeature>) :
|
|||||||
override fun build(): SnapshotStateMap<FeatureId, MapFeature> = content
|
override fun build(): SnapshotStateMap<FeatureId, MapFeature> = content
|
||||||
}
|
}
|
||||||
|
|
||||||
fun FeatureBuilder.circle(
|
public fun MapFeatureBuilder.circle(
|
||||||
center: GeodeticMapCoordinates,
|
center: GeodeticMapCoordinates,
|
||||||
zoomRange: IntRange = defaultZoomRange,
|
zoomRange: IntRange = defaultZoomRange,
|
||||||
size: Float = 5f,
|
size: Float = 5f,
|
||||||
color: Color = Color.Red,
|
color: Color = Color.Red,
|
||||||
id: FeatureId? = null,
|
id: FeatureId? = null,
|
||||||
) = addFeature(
|
): FeatureId = addFeature(
|
||||||
id, MapCircleFeature(center, zoomRange, size, color)
|
id, MapCircleFeature(center, zoomRange, size, color)
|
||||||
)
|
)
|
||||||
|
|
||||||
fun FeatureBuilder.circle(
|
public fun MapFeatureBuilder.circle(
|
||||||
centerCoordinates: Pair<Double, Double>,
|
centerCoordinates: Pair<Double, Double>,
|
||||||
zoomRange: IntRange = defaultZoomRange,
|
zoomRange: IntRange = defaultZoomRange,
|
||||||
size: Float = 5f,
|
size: Float = 5f,
|
||||||
color: Color = Color.Red,
|
color: Color = Color.Red,
|
||||||
id: FeatureId? = null,
|
id: FeatureId? = null,
|
||||||
) = addFeature(
|
): FeatureId = addFeature(
|
||||||
id, MapCircleFeature(centerCoordinates.toCoordinates(), zoomRange, size, color)
|
id, MapCircleFeature(centerCoordinates.toCoordinates(), zoomRange, size, color)
|
||||||
)
|
)
|
||||||
|
|
||||||
fun FeatureBuilder.custom(
|
public fun MapFeatureBuilder.custom(
|
||||||
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,
|
||||||
) = addFeature(id, MapDrawFeature(position.toCoordinates(), zoomRange, drawFeature))
|
): FeatureId = addFeature(id, MapDrawFeature(position.toCoordinates(), zoomRange, drawFeature))
|
||||||
|
|
||||||
fun FeatureBuilder.line(
|
public fun MapFeatureBuilder.line(
|
||||||
aCoordinates: Pair<Double, Double>,
|
aCoordinates: Pair<Double, Double>,
|
||||||
bCoordinates: Pair<Double, Double>,
|
bCoordinates: Pair<Double, Double>,
|
||||||
zoomRange: IntRange = defaultZoomRange,
|
zoomRange: IntRange = defaultZoomRange,
|
||||||
color: Color = Color.Red,
|
color: Color = Color.Red,
|
||||||
id: FeatureId? = null,
|
id: FeatureId? = null,
|
||||||
) = addFeature(id, MapLineFeature(aCoordinates.toCoordinates(), bCoordinates.toCoordinates(), zoomRange, color))
|
): FeatureId = addFeature(id, MapLineFeature(aCoordinates.toCoordinates(), bCoordinates.toCoordinates(), zoomRange, color))
|
||||||
|
|
||||||
fun FeatureBuilder.text(
|
public fun MapFeatureBuilder.text(
|
||||||
position: GeodeticMapCoordinates,
|
position: GeodeticMapCoordinates,
|
||||||
text: String,
|
text: String,
|
||||||
zoomRange: IntRange = defaultZoomRange,
|
zoomRange: IntRange = defaultZoomRange,
|
||||||
color: Color = Color.Red,
|
color: Color = Color.Red,
|
||||||
id: FeatureId? = null,
|
id: FeatureId? = null,
|
||||||
) = addFeature(id, MapTextFeature(position, text, zoomRange, color))
|
): FeatureId = addFeature(id, MapTextFeature(position, text, zoomRange, color))
|
||||||
|
|
||||||
fun FeatureBuilder.text(
|
public fun MapFeatureBuilder.text(
|
||||||
position: Pair<Double, Double>,
|
position: Pair<Double, Double>,
|
||||||
text: String,
|
text: String,
|
||||||
zoomRange: IntRange = defaultZoomRange,
|
zoomRange: IntRange = defaultZoomRange,
|
||||||
color: Color = Color.Red,
|
color: Color = Color.Red,
|
||||||
id: FeatureId? = null,
|
id: FeatureId? = null,
|
||||||
) = addFeature(id, MapTextFeature(position.toCoordinates(), text, zoomRange, color))
|
): FeatureId = addFeature(id, MapTextFeature(position.toCoordinates(), text, zoomRange, color))
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun FeatureBuilder.image(
|
public fun MapFeatureBuilder.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),
|
||||||
zoomRange: IntRange = defaultZoomRange,
|
zoomRange: IntRange = defaultZoomRange,
|
||||||
id: FeatureId? = null,
|
id: FeatureId? = null,
|
||||||
) = addFeature(id, MapVectorImageFeature(position.toCoordinates(), image, size, zoomRange))
|
): FeatureId = addFeature(id, MapVectorImageFeature(position.toCoordinates(), image, size, zoomRange))
|
||||||
|
|
||||||
fun FeatureBuilder.group(
|
public fun MapFeatureBuilder.group(
|
||||||
zoomRange: IntRange = defaultZoomRange,
|
zoomRange: IntRange = defaultZoomRange,
|
||||||
id: FeatureId? = null,
|
id: FeatureId? = null,
|
||||||
builder: FeatureBuilder.() -> Unit,
|
builder: MapFeatureBuilder.() -> Unit,
|
||||||
): FeatureId {
|
): FeatureId {
|
||||||
val map = MapFeatureBuilder(emptyMap()).apply(builder).build()
|
val map = MapFeatureBuilderImpl(emptyMap()).apply(builder).build()
|
||||||
val feature = MapFeatureGroup(map, zoomRange)
|
val feature = MapFeatureGroup(map, zoomRange)
|
||||||
return addFeature(id, feature)
|
return addFeature(id, feature)
|
||||||
}
|
}
|
@ -5,27 +5,27 @@ import kotlinx.coroutines.CoroutineScope
|
|||||||
import kotlinx.coroutines.Deferred
|
import kotlinx.coroutines.Deferred
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
|
|
||||||
data class TileId(
|
public data class TileId(
|
||||||
val zoom: Int,
|
val zoom: Int,
|
||||||
val i: Int,
|
val i: Int,
|
||||||
val j: Int,
|
val j: Int,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class MapTile(
|
public data class MapTile(
|
||||||
val id: TileId,
|
val id: TileId,
|
||||||
val image: ImageBitmap,
|
val image: ImageBitmap,
|
||||||
)
|
)
|
||||||
|
|
||||||
interface MapTileProvider {
|
public interface MapTileProvider {
|
||||||
fun CoroutineScope.loadTileAsync(tileId: TileId): Deferred<MapTile>
|
public fun CoroutineScope.loadTileAsync(tileId: TileId): Deferred<MapTile>
|
||||||
|
|
||||||
val tileSize: Int get() = DEFAULT_TILE_SIZE
|
public val tileSize: Int get() = DEFAULT_TILE_SIZE
|
||||||
|
|
||||||
fun toIndex(d: Double): Int = floor(d / tileSize).toInt()
|
public fun toIndex(d: Double): Int = floor(d / tileSize).toInt()
|
||||||
|
|
||||||
fun toCoordinate(i: Int): Double = (i * tileSize).toDouble()
|
public fun toCoordinate(i: Int): Double = (i * tileSize).toDouble()
|
||||||
|
|
||||||
companion object {
|
public companion object {
|
||||||
const val DEFAULT_TILE_SIZE = 256
|
public const val DEFAULT_TILE_SIZE: Int = 256
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ import kotlin.math.min
|
|||||||
|
|
||||||
|
|
||||||
//TODO consider replacing by modifier
|
//TODO consider replacing by modifier
|
||||||
data class MapViewConfig(
|
public data class MapViewConfig(
|
||||||
val zoomSpeed: Double = 1.0 / 3.0,
|
val zoomSpeed: Double = 1.0 / 3.0,
|
||||||
val inferViewBoxFromFeatures: Boolean = false,
|
val inferViewBoxFromFeatures: Boolean = false,
|
||||||
val onClick: MapViewPoint.() -> Unit = {},
|
val onClick: MapViewPoint.() -> Unit = {},
|
||||||
@ -21,7 +21,7 @@ data class MapViewConfig(
|
|||||||
)
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
expect fun MapView(
|
public expect fun MapView(
|
||||||
mapTileProvider: MapTileProvider,
|
mapTileProvider: MapTileProvider,
|
||||||
computeViewPoint: (canvasSize: DpSize) -> MapViewPoint,
|
computeViewPoint: (canvasSize: DpSize) -> MapViewPoint,
|
||||||
features: Map<FeatureId, MapFeature>,
|
features: Map<FeatureId, MapFeature>,
|
||||||
@ -30,15 +30,15 @@ expect fun MapView(
|
|||||||
)
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MapView(
|
public fun MapView(
|
||||||
mapTileProvider: MapTileProvider,
|
mapTileProvider: MapTileProvider,
|
||||||
initialViewPoint: MapViewPoint,
|
initialViewPoint: MapViewPoint,
|
||||||
features: Map<FeatureId, MapFeature> = emptyMap(),
|
features: Map<FeatureId, MapFeature> = emptyMap(),
|
||||||
config: MapViewConfig = MapViewConfig(),
|
config: MapViewConfig = MapViewConfig(),
|
||||||
modifier: Modifier = Modifier.fillMaxSize(),
|
modifier: Modifier = Modifier.fillMaxSize(),
|
||||||
buildFeatures: @Composable (FeatureBuilder.() -> Unit) = {},
|
buildFeatures: @Composable (MapFeatureBuilder.() -> Unit) = {},
|
||||||
) {
|
) {
|
||||||
val featuresBuilder = MapFeatureBuilder(features)
|
val featuresBuilder = MapFeatureBuilderImpl(features)
|
||||||
featuresBuilder.buildFeatures()
|
featuresBuilder.buildFeatures()
|
||||||
MapView(
|
MapView(
|
||||||
mapTileProvider,
|
mapTileProvider,
|
||||||
@ -66,9 +66,9 @@ public fun MapView(
|
|||||||
features: Map<FeatureId, MapFeature> = emptyMap(),
|
features: Map<FeatureId, MapFeature> = emptyMap(),
|
||||||
config: MapViewConfig = MapViewConfig(),
|
config: MapViewConfig = MapViewConfig(),
|
||||||
modifier: Modifier = Modifier.fillMaxSize(),
|
modifier: Modifier = Modifier.fillMaxSize(),
|
||||||
buildFeatures: @Composable (FeatureBuilder.() -> Unit) = {},
|
buildFeatures: @Composable (MapFeatureBuilder.() -> Unit) = {},
|
||||||
) {
|
) {
|
||||||
val featuresBuilder = MapFeatureBuilder(features)
|
val featuresBuilder = MapFeatureBuilderImpl(features)
|
||||||
featuresBuilder.buildFeatures()
|
featuresBuilder.buildFeatures()
|
||||||
MapView(
|
MapView(
|
||||||
mapTileProvider,
|
mapTileProvider,
|
||||||
|
@ -33,7 +33,7 @@ private fun Color.toPaint(): Paint = Paint().apply {
|
|||||||
private fun IntRange.intersect(other: IntRange) = max(first, other.first)..min(last, other.last)
|
private fun IntRange.intersect(other: IntRange) = max(first, other.first)..min(last, other.last)
|
||||||
|
|
||||||
internal fun MapViewPoint.move(deltaX: Double, deltaY: Double): MapViewPoint {
|
internal fun MapViewPoint.move(deltaX: Double, deltaY: Double): MapViewPoint {
|
||||||
val newCoordinates = center.sciprog.maps.coordinates.GeodeticMapCoordinates.ofRadians(
|
val newCoordinates = GeodeticMapCoordinates.ofRadians(
|
||||||
(focus.latitude + deltaY / scaleFactor).coerceIn(
|
(focus.latitude + deltaY / scaleFactor).coerceIn(
|
||||||
-MercatorProjection.MAXIMUM_LATITUDE,
|
-MercatorProjection.MAXIMUM_LATITUDE,
|
||||||
MercatorProjection.MAXIMUM_LATITUDE
|
MercatorProjection.MAXIMUM_LATITUDE
|
||||||
@ -50,7 +50,7 @@ private val logger = KotlinLogging.logger("MapView")
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
actual fun MapView(
|
public actual fun MapView(
|
||||||
mapTileProvider: MapTileProvider,
|
mapTileProvider: MapTileProvider,
|
||||||
computeViewPoint: (canvasSize: DpSize) -> MapViewPoint,
|
computeViewPoint: (canvasSize: DpSize) -> MapViewPoint,
|
||||||
features: Map<FeatureId, MapFeature>,
|
features: Map<FeatureId, MapFeature>,
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
package center.sciprog.maps.coordinates
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A marker interface for flat coordinates
|
|
||||||
*/
|
|
||||||
public interface Coordinates2D
|
|
||||||
|
|
||||||
public interface CoordinateBox<T: Coordinates2D>{
|
|
||||||
public val a: T
|
|
||||||
public val b :T
|
|
||||||
}
|
|
@ -8,7 +8,7 @@ import kotlin.math.PI
|
|||||||
public class GeodeticMapCoordinates private constructor(
|
public class GeodeticMapCoordinates private constructor(
|
||||||
public val latitude: Double,
|
public val latitude: Double,
|
||||||
public val longitude: Double,
|
public val longitude: Double,
|
||||||
): Coordinates2D {
|
){
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
|
@ -5,9 +5,9 @@ import kotlin.math.max
|
|||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
public data class GmcBox(
|
public data class GmcBox(
|
||||||
public override val a: GeodeticMapCoordinates,
|
public val a: GeodeticMapCoordinates,
|
||||||
public override val b: GeodeticMapCoordinates,
|
public val b: GeodeticMapCoordinates,
|
||||||
) : CoordinateBox<GeodeticMapCoordinates>
|
)
|
||||||
|
|
||||||
public fun GmcBox(
|
public fun GmcBox(
|
||||||
latitudes: ClosedFloatingPointRange<Double>,
|
latitudes: ClosedFloatingPointRange<Double>,
|
||||||
|
@ -25,7 +25,6 @@ kotlin {
|
|||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain {
|
commonMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
api(projects.mapsKtCore)
|
|
||||||
api("io.github.microutils:kotlin-logging:2.1.23")
|
api("io.github.microutils:kotlin-logging:2.1.23")
|
||||||
api(compose.foundation)
|
api(compose.foundation)
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,15 @@
|
|||||||
package center.sciprog.maps.scheme
|
package center.sciprog.maps.scheme
|
||||||
|
|
||||||
import center.sciprog.maps.coordinates.CoordinateBox
|
|
||||||
import center.sciprog.maps.coordinates.Coordinates2D
|
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
data class SchemeCoordinates(val x: Float, val y: Float) : Coordinates2D
|
data class SchemeCoordinates(val x: Float, val y: Float)
|
||||||
|
|
||||||
data class SchemeCoordinateBox(
|
data class SchemeCoordinateBox(
|
||||||
override val a: SchemeCoordinates,
|
val a: SchemeCoordinates,
|
||||||
override val b: SchemeCoordinates,
|
val b: SchemeCoordinates,
|
||||||
) : CoordinateBox<SchemeCoordinates>
|
)
|
||||||
|
|
||||||
val SchemeCoordinateBox.top get() = max(a.y, b.y)
|
val SchemeCoordinateBox.top get() = max(a.y, b.y)
|
||||||
val SchemeCoordinateBox.bottom get() = min(a.y, b.y)
|
val SchemeCoordinateBox.bottom get() = min(a.y, b.y)
|
||||||
|
@ -14,13 +14,15 @@ import center.sciprog.maps.scheme.SchemeFeature.Companion.defaultScaleRange
|
|||||||
|
|
||||||
typealias FeatureId = String
|
typealias FeatureId = String
|
||||||
|
|
||||||
interface FeatureBuilder {
|
interface SchemeFeatureBuilder {
|
||||||
fun addFeature(id: FeatureId?, feature: SchemeFeature): FeatureId
|
fun addFeature(id: FeatureId?, feature: SchemeFeature): FeatureId
|
||||||
|
|
||||||
fun build(): SnapshotStateMap<FeatureId, SchemeFeature>
|
fun build(): SnapshotStateMap<FeatureId, SchemeFeature>
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class SchemeFeatureBuilder(initialFeatures: Map<FeatureId, SchemeFeature>) : FeatureBuilder {
|
internal class SchemeFeatureBuilderImpl(
|
||||||
|
initialFeatures: Map<FeatureId, SchemeFeature>,
|
||||||
|
) : SchemeFeatureBuilder {
|
||||||
|
|
||||||
private val content: SnapshotStateMap<FeatureId, SchemeFeature> =
|
private val content: SnapshotStateMap<FeatureId, SchemeFeature> =
|
||||||
mutableStateMapOf<FeatureId, SchemeFeature>().apply {
|
mutableStateMapOf<FeatureId, SchemeFeature>().apply {
|
||||||
@ -38,7 +40,7 @@ internal class SchemeFeatureBuilder(initialFeatures: Map<FeatureId, SchemeFeatur
|
|||||||
override fun build(): SnapshotStateMap<FeatureId, SchemeFeature> = content
|
override fun build(): SnapshotStateMap<FeatureId, SchemeFeature> = content
|
||||||
}
|
}
|
||||||
|
|
||||||
fun FeatureBuilder.background(
|
fun SchemeFeatureBuilder.background(
|
||||||
painter: Painter,
|
painter: Painter,
|
||||||
box: SchemeCoordinateBox,
|
box: SchemeCoordinateBox,
|
||||||
id: FeatureId? = null,
|
id: FeatureId? = null,
|
||||||
@ -47,7 +49,7 @@ fun FeatureBuilder.background(
|
|||||||
SchemeBackgroundFeature(box, painter)
|
SchemeBackgroundFeature(box, painter)
|
||||||
)
|
)
|
||||||
|
|
||||||
fun FeatureBuilder.background(
|
fun SchemeFeatureBuilder.background(
|
||||||
painter: Painter,
|
painter: Painter,
|
||||||
size: Size = painter.intrinsicSize,
|
size: Size = painter.intrinsicSize,
|
||||||
offset: SchemeCoordinates = SchemeCoordinates(0f, 0f),
|
offset: SchemeCoordinates = SchemeCoordinates(0f, 0f),
|
||||||
@ -60,7 +62,7 @@ fun FeatureBuilder.background(
|
|||||||
return background(painter, box, id)
|
return background(painter, box, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun FeatureBuilder.circle(
|
fun SchemeFeatureBuilder.circle(
|
||||||
center: SchemeCoordinates,
|
center: SchemeCoordinates,
|
||||||
scaleRange: FloatRange = defaultScaleRange,
|
scaleRange: FloatRange = defaultScaleRange,
|
||||||
size: Float = 5f,
|
size: Float = 5f,
|
||||||
@ -70,7 +72,7 @@ fun FeatureBuilder.circle(
|
|||||||
id, SchemeCircleFeature(center, scaleRange, size, color)
|
id, SchemeCircleFeature(center, scaleRange, size, color)
|
||||||
)
|
)
|
||||||
|
|
||||||
fun FeatureBuilder.circle(
|
fun SchemeFeatureBuilder.circle(
|
||||||
centerCoordinates: Pair<Number, Number>,
|
centerCoordinates: Pair<Number, Number>,
|
||||||
scaleRange: FloatRange = defaultScaleRange,
|
scaleRange: FloatRange = defaultScaleRange,
|
||||||
size: Float = 5f,
|
size: Float = 5f,
|
||||||
@ -80,14 +82,14 @@ fun FeatureBuilder.circle(
|
|||||||
id, SchemeCircleFeature(centerCoordinates.toCoordinates(), scaleRange, size, color)
|
id, SchemeCircleFeature(centerCoordinates.toCoordinates(), scaleRange, size, color)
|
||||||
)
|
)
|
||||||
|
|
||||||
fun FeatureBuilder.custom(
|
fun SchemeFeatureBuilder.custom(
|
||||||
position: Pair<Number, Number>,
|
position: Pair<Number, Number>,
|
||||||
scaleRange: FloatRange = defaultScaleRange,
|
scaleRange: FloatRange = defaultScaleRange,
|
||||||
id: FeatureId? = null,
|
id: FeatureId? = null,
|
||||||
drawFeature: DrawScope.() -> Unit,
|
drawFeature: DrawScope.() -> Unit,
|
||||||
) = addFeature(id, SchemeDrawFeature(position.toCoordinates(), scaleRange, drawFeature))
|
) = addFeature(id, SchemeDrawFeature(position.toCoordinates(), scaleRange, drawFeature))
|
||||||
|
|
||||||
fun FeatureBuilder.line(
|
fun SchemeFeatureBuilder.line(
|
||||||
aCoordinates: Pair<Number, Number>,
|
aCoordinates: Pair<Number, Number>,
|
||||||
bCoordinates: Pair<Number, Number>,
|
bCoordinates: Pair<Number, Number>,
|
||||||
scaleRange: FloatRange = defaultScaleRange,
|
scaleRange: FloatRange = defaultScaleRange,
|
||||||
@ -95,7 +97,7 @@ fun FeatureBuilder.line(
|
|||||||
id: FeatureId? = null,
|
id: FeatureId? = null,
|
||||||
) = addFeature(id, SchemeLineFeature(aCoordinates.toCoordinates(), bCoordinates.toCoordinates(), scaleRange, color))
|
) = addFeature(id, SchemeLineFeature(aCoordinates.toCoordinates(), bCoordinates.toCoordinates(), scaleRange, color))
|
||||||
|
|
||||||
fun FeatureBuilder.text(
|
fun SchemeFeatureBuilder.text(
|
||||||
position: SchemeCoordinates,
|
position: SchemeCoordinates,
|
||||||
text: String,
|
text: String,
|
||||||
scaleRange: FloatRange = defaultScaleRange,
|
scaleRange: FloatRange = defaultScaleRange,
|
||||||
@ -103,7 +105,7 @@ fun FeatureBuilder.text(
|
|||||||
id: FeatureId? = null,
|
id: FeatureId? = null,
|
||||||
) = addFeature(id, SchemeTextFeature(position, text, scaleRange, color))
|
) = addFeature(id, SchemeTextFeature(position, text, scaleRange, color))
|
||||||
|
|
||||||
fun FeatureBuilder.text(
|
fun SchemeFeatureBuilder.text(
|
||||||
position: Pair<Number, Number>,
|
position: Pair<Number, Number>,
|
||||||
text: String,
|
text: String,
|
||||||
scaleRange: FloatRange = defaultScaleRange,
|
scaleRange: FloatRange = defaultScaleRange,
|
||||||
@ -112,7 +114,7 @@ fun FeatureBuilder.text(
|
|||||||
) = addFeature(id, SchemeTextFeature(position.toCoordinates(), text, scaleRange, color))
|
) = addFeature(id, SchemeTextFeature(position.toCoordinates(), text, scaleRange, color))
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun FeatureBuilder.image(
|
fun SchemeFeatureBuilder.image(
|
||||||
position: Pair<Number, Number>,
|
position: Pair<Number, Number>,
|
||||||
image: ImageVector,
|
image: ImageVector,
|
||||||
size: DpSize = DpSize(20.dp, 20.dp),
|
size: DpSize = DpSize(20.dp, 20.dp),
|
||||||
@ -120,12 +122,12 @@ fun FeatureBuilder.image(
|
|||||||
id: FeatureId? = null,
|
id: FeatureId? = null,
|
||||||
) = addFeature(id, SchemeVectorImageFeature(position.toCoordinates(), image, size, scaleRange))
|
) = addFeature(id, SchemeVectorImageFeature(position.toCoordinates(), image, size, scaleRange))
|
||||||
|
|
||||||
fun FeatureBuilder.group(
|
fun SchemeFeatureBuilder.group(
|
||||||
scaleRange: FloatRange = defaultScaleRange,
|
scaleRange: FloatRange = defaultScaleRange,
|
||||||
id: FeatureId? = null,
|
id: FeatureId? = null,
|
||||||
builder: FeatureBuilder.() -> Unit,
|
builder: SchemeFeatureBuilder.() -> Unit,
|
||||||
): FeatureId {
|
): FeatureId {
|
||||||
val map = SchemeFeatureBuilder(emptyMap()).apply(builder).build()
|
val map = SchemeFeatureBuilderImpl(emptyMap()).apply(builder).build()
|
||||||
val feature = SchemeFeatureGroup(map, scaleRange)
|
val feature = SchemeFeatureGroup(map, scaleRange)
|
||||||
return addFeature(id, feature)
|
return addFeature(id, feature)
|
||||||
}
|
}
|
@ -249,9 +249,9 @@ fun SchemeView(
|
|||||||
features: Map<FeatureId, SchemeFeature> = emptyMap(),
|
features: Map<FeatureId, SchemeFeature> = emptyMap(),
|
||||||
config: SchemeViewConfig = SchemeViewConfig(),
|
config: SchemeViewConfig = SchemeViewConfig(),
|
||||||
modifier: Modifier = Modifier.fillMaxSize(),
|
modifier: Modifier = Modifier.fillMaxSize(),
|
||||||
buildFeatures: @Composable (FeatureBuilder.() -> Unit) = {},
|
buildFeatures: @Composable (SchemeFeatureBuilder.() -> Unit) = {},
|
||||||
) {
|
) {
|
||||||
val featuresBuilder = SchemeFeatureBuilder(features)
|
val featuresBuilder = SchemeFeatureBuilderImpl(features)
|
||||||
featuresBuilder.buildFeatures()
|
featuresBuilder.buildFeatures()
|
||||||
SchemeView(
|
SchemeView(
|
||||||
{ initialViewPoint },
|
{ initialViewPoint },
|
||||||
|
Loading…
Reference in New Issue
Block a user