All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.org.jetbrains.letsPlot.livemap.config.MapProjectionBuilder.kt Maven / Gradle / Ivy

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]
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy