commonMain.org.jetbrains.letsPlot.livemap.config.MapProjectionBuilder.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of livemap-jvm Show documentation
Show all versions of livemap-jvm Show documentation
A part of the Lets-Plot library.
The newest version!
/*
* Copyright (c) 2023. JetBrains s.r.o.
* Use of this source code is governed by the MIT license that can be found in the LICENSE file.
*/
package org.jetbrains.letsPlot.livemap.config
import org.jetbrains.letsPlot.commons.intern.spatial.LonLatPoint
import org.jetbrains.letsPlot.commons.intern.typedGeometry.*
import org.jetbrains.letsPlot.commons.intern.typedGeometry.Transforms.transform
import org.jetbrains.letsPlot.livemap.World
import org.jetbrains.letsPlot.livemap.WorldPoint
import org.jetbrains.letsPlot.livemap.WorldRectangle
import org.jetbrains.letsPlot.livemap.core.*
import org.jetbrains.letsPlot.livemap.core.Transforms
import org.jetbrains.letsPlot.livemap.mapengine.MapProjection
import kotlin.math.min
internal class MapProjectionBuilder(
private val geoProjection: GeoProjection,
private val mapRect: WorldRectangle
) {
var reverseX = false
var reverseY = false
private fun offset(offset: Double): Transform {
return object : Transform {
override fun apply(v: Double): Double = v - offset
override fun invert(v: Double): Double = v + offset
}
}
private fun composite(
t1: UnsafeTransform,
t2: UnsafeTransform
): UnsafeTransform {
return object : UnsafeTransform {
override fun apply(v: InT): OutT? = v.run(t1::apply)?.run(t2::apply)
override fun invert(v: OutT): InT? = v.run(t2::invert)?.run(t1::invert)
}
}
private fun composite(
t1: UnsafeTransform,
t2: Transform
): UnsafeTransform {
return object : UnsafeTransform {
override fun apply(v: InT): OutT? = v.run(t1::apply)?.run(t2::apply)
override fun invert(v: OutT): InT? = v.run(t2::invert)?.run(t1::invert)
}
}
private fun composite(
t1: Transform,
t2: Transform
): Transform {
return object : Transform {
override fun apply(v: InT): OutT = v.run(t1::apply).run(t2::apply)
override fun invert(v: OutT): InT = v.run(t2::invert).run(t1::invert)
}
}
private fun linear(offset: Double, scale: Double): Transform {
return composite(offset(offset), Transforms.scale { scale })
}
fun create(): MapProjection {
val rect = transform(geoProjection.validRect(), geoProjection::apply)
?: error("Unable to transform projection valid rect")
val scale = min(mapRect.width / rect.width, mapRect.height / rect.height)
@Suppress("UNCHECKED_CAST")
val projSize = (mapRect.dimension * (1.0 / scale)) as Vec
val projRect = Rect(rect.center - projSize * 0.5, projSize)
val offsetX = if (reverseX) projRect.right else projRect.left
val scaleX = if (reverseX) -scale else scale
val offsetY = if (reverseY) projRect.bottom else projRect.top
val scaleY = if (reverseY) -scale else scale
val linearProjection =
Transforms.tuple(
linear(offsetX, scaleX),
linear(offsetY, scaleY)
)
val proj = composite(geoProjection, linearProjection)
return object : MapProjection {
override fun apply(v: LonLatPoint): WorldPoint? = proj.apply(v)
override fun invert(v: WorldPoint): LonLatPoint? = proj.invert(v)
override val mapRect: WorldRectangle
get() = [email protected]
}
}
}