Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package io.data2viz.geo
import io.data2viz.geom.Extent
import io.data2viz.math.range
import io.data2viz.geojson.LineString
import io.data2viz.geojson.MultiLineString
import io.data2viz.geojson.Polygon
import io.data2viz.math.EPSILON
import kotlin.math.abs
import kotlin.math.ceil
// TODO : use Extent
fun geoGraticule() = geoGraticule {}
fun geoGraticule(init: Graticule.() -> Unit): Graticule {
val g = Graticule()
g.extentMajor = Extent(-180.0, -90.0 + EPSILON, 180.0, 90.0 - EPSILON)
g.extentMinor = Extent(-180.0, -80.0 - EPSILON, 180.0, 80.0 + EPSILON)
g.init()
return g
}
private fun reorderExtent(extent: Extent) {
if (extent.x0 > extent.x1) {
val t = extent.x0
extent.x0 = extent.x1
extent.x1 = t
}
if (extent.y0 > extent.y1) {
val t = extent.y0
extent.y0 = extent.y1
extent.y1 = t
}
}
/**
* Constructs a geometry generator for creating graticules: a uniform grid of meridians and parallels for showing
* projection distortion.
* The default graticule has meridians and parallels every 10° between ±80° latitude;
* for the polar regions, there are meridians every 90°.
*/
class Graticule {
private var minorExtent = Extent(Double.NaN, Double.NaN, Double.NaN, Double.NaN)
private var majorExtent = Extent(Double.NaN, Double.NaN, Double.NaN, Double.NaN)
private var minorStepX = 10.0
private var minorStepY = 10.0
private var majorStepX = 90.0
private var majorStepY = 360.0
private lateinit var minorX: (Double) -> List
private lateinit var minorY: (Double) -> List
private lateinit var majorX: (Double) -> List
private lateinit var majorY: (Double) -> List
// TODO fun geoGraticule10() {
/**
* The precision for this graticule, in degrees which defaults to 2.5°.
*/
var precision = 2.5
set(value) {
field = value
minorX = graticuleX(minorExtent.y0, minorExtent.y1, 90.0)
minorY = graticuleY(minorExtent.x0, minorExtent.x1, precision)
majorX = graticuleX(majorExtent.y0, majorExtent.y1, 90.0)
majorY = graticuleY(majorExtent.x0, majorExtent.x1, precision)
}
/**
* Set: sets the major and minor extents of this graticule.
* Get: returns the current minor extent, which defaults to ⟨⟨-180°, -80° - ε⟩, ⟨180°, 80° + ε⟩⟩.
*/
var extent: Extent
get() = minorExtent
set(value) {
extentMajor = value
extentMinor = value
}
/**
* Set: sets the major extent of this graticule.
* Get: returns the current major extent, which defaults to ⟨⟨-180°, -90° + ε⟩, ⟨180°, 90° - ε⟩⟩.
*/
var extentMajor: Extent
get() = majorExtent
set(value) {
majorExtent = value
reorderExtent(majorExtent)
precision = precision
}
/**
* Set: sets the minor extent of this graticule.
* Get: returns the current minor extent, which defaults to ⟨⟨-180°, -80° - ε⟩, ⟨180°, 80° + ε⟩⟩.
*/
var extentMinor: Extent
get() = minorExtent
set(value) {
minorExtent = value
reorderExtent(minorExtent)
precision = precision
}
/**
* Set: sets the major step for this graticule.
* Get: returns the current major step, which defaults to ⟨90°, 360°⟩.
*/
var stepMajor: DoubleArray
get() = doubleArrayOf(majorStepX, majorStepY)
set(value) {
majorStepX = value[0]
majorStepY = value[1]
}
/**
* Set: sets the minor step for this graticule.
* Get: returns the current minor step, which defaults to ⟨10°, 10°⟩.
*/
var stepMinor: DoubleArray
get() = doubleArrayOf(minorStepX, minorStepY)
set(value) {
minorStepX = value[0]
minorStepY = value[1]
}
/**
* Set: sets the major and minor step for this graticule.
* Get: returns the current minor step, which defaults to ⟨10°, 10°⟩.
*/
var step: DoubleArray
get() = stepMinor
set(value) {
stepMajor = value
stepMinor = value
}
/**
* Returns a GeoJSON MultiLineString geometry object representing all meridians and parallels for this graticule.
*/
fun graticule() = MultiLineString(buildLines().map { it.map { arrayOf(it[0], it[1]) }.toTypedArray() }.toTypedArray())
/**
* Returns an array of GeoJSON LineString geometry objects, one for each meridian or parallel for this graticule.
*/
fun lines() = buildLines().map { LineString(it.map { arrayOf(it[0], it[1]) }.toTypedArray()) }
/**
* Returns a GeoJSON Polygon geometry object representing the outline of this graticule, i.e. along the
* meridians and parallels defining its extent.
*/
fun outline(): Polygon {
val coords = majorX(majorExtent.x0).toMutableList()
coords += majorY(majorExtent.y1).subList(1, majorY(majorExtent.y1).lastIndex)
coords += majorX(majorExtent.x1).asReversed().subList(1, majorX(majorExtent.x1).lastIndex)
coords += majorY(majorExtent.y0).asReversed().subList(1, majorY(majorExtent.y0).lastIndex)
return Polygon(arrayOf(coords.map { arrayOf(it[0], it[1]) }.toTypedArray()))
}
private fun buildLines(): List> {
val lines = range(ceil(majorExtent.x0 / majorStepX) * majorStepX, majorExtent.x1, majorStepX).map(majorX)
.toMutableList()
lines += range(ceil(majorExtent.y0 / majorStepY) * majorStepY, majorExtent.y1, majorStepY).map(majorY)
lines += range(
ceil(minorExtent.x0 / minorStepX) * minorStepX,
minorExtent.x1,
minorStepX
).filter { abs(it % majorStepX) > EPSILON }.map(
minorX
)
lines += range(
ceil(minorExtent.y0 / minorStepY) * minorStepY,
minorExtent.y1,
minorStepY
).filter { abs(it % majorStepY) > EPSILON }.map(
minorY
)
return lines
}
private fun graticuleX(y0: Double, y1: Double, dy: Double): (Double) -> List {
val y = range(y0, y1 - EPSILON, dy).toMutableList()
y += y1
return { x: Double -> y.map { doubleArrayOf(x, it) } }
}
private fun graticuleY(x0: Double, x1: Double, dx: Double): (Double) -> List {
val x = range(x0, x1 - EPSILON, dx).toMutableList()
x += x1
return { y: Double -> x.map { doubleArrayOf(it, y) } }
}
}