iwmlib/lib/pixi/maps/projections/robinson.js

157 lines
4.3 KiB
JavaScript

import Projection from './projection'
/* */
export default class Robinson extends Projection {
constructor(lng = 0) {
super()
this.lng0 = lng
this.lengthOfParallel = [
1.0,
0.9986,
0.9954,
0.99,
0.9822,
0.973,
0.96,
0.9427,
0.9216,
0.8962,
0.8679,
0.835,
0.7986,
0.7597,
0.7186,
0.6732,
0.6213,
0.5722,
0.5322
]
this.distancesFromEquator = [
0.0,
0.062,
0.124,
0.186,
0.248,
0.31,
0.372,
0.434,
0.4958,
0.5571,
0.6176,
0.6769,
0.7346,
0.7903,
0.8435,
0.8936,
0.9394,
0.9761,
1.0
]
}
forward(coords) {
let { x: lat, y: lng } = coords
lng = this._adjustLng(lng)
// Get the required indices, the remainder in between low and hight as ratio
// and the sign of the found indices, as the tables are only in positive direction.
let { low, high, ratio, sign } = this._getInterpolationValues(lat, 90)
// Values that lie inbetween two indices are interpolated.
let y = this._interpolate(this.distancesFromEquator[low], this.distancesFromEquator[high], ratio)
// Reapply the sign to the vertical position.
y *= sign
// The center of the projection is in the center of the map. Therefore we shift the
// center to the top left corner.
y = 1 - (y + 1) / 2
// The lengthOfParallel table provides us with the corresponding scaling factor
// for a specific latitude. Inbetween values are interpolated as before.
let proportionalLength = this._interpolate(this.lengthOfParallel[low], this.lengthOfParallel[high], ratio)
//To normalize the value to a range from -1 to 1.
let x = (proportionalLength * lng) / 180
x = (x + 1) / 2
return { x, y }
}
backward(position) {
let { x, y } = position
y = 1 - 2 * y
let sign = Math.sign(y)
y = Math.abs(y)
let low = 0
let high = 0
for (let i = 0; i < this.distancesFromEquator.length - 1 && y > this.distancesFromEquator[i]; i++) {
low = i
high = i + 1
}
let lowDist = this.distancesFromEquator[low]
let highDist = this.distancesFromEquator[high]
let ratio = highDist - lowDist == 0 ? 0 : (y - lowDist) / (highDist - lowDist)
let lat = low * 5 + ratio * 5
let parallelLengthMin = this.lengthOfParallel[low]
let parallelLengthMax = this.lengthOfParallel[high]
let completeLength = parallelLengthMin + (parallelLengthMax - parallelLengthMin) * ratio
x = x * 2 - 1
let normalizedLength = x / completeLength
let lng = normalizedLength * 180
return { x: lat * sign, y: this._adjustLng(lng, true) }
}
_adjustLng(lng, inv = false) {
let moved = inv ? lng + this.lng0 : lng - this.lng0
// if (moved < -180) moved += 360
// if (moved > 180) moved -= 360
return moved
}
_interpolate(a, b, ratio) {
return a * (1 - ratio) + b * ratio
}
_getInterpolationValues(value, max) {
let sign = Math.sign(value)
value = Math.min(Math.abs(value), max)
// Note that min and max can be the same. Which is true
// when lat is dividable by 5. This also covers the edge cases 0 and 90.
let minIndex = Math.floor(value / 5)
let maxIndex = Math.ceil(value / 5)
let ratio = (value % 5) / 5
// console.log({ value, minIndex, maxIndex, ratio })
// console.log(this.lengthOfParallel.length)
return { low: minIndex, high: maxIndex, ratio, sign }
}
get name() {
return 'Robinson Projection'
}
get maxViewport() {
let min = new PIXI.Point(-90, -180)
let max = new PIXI.Point(90, 180)
max.x += this.lng0
min.x += this.lng0
console.log({ min, max })
return { min, max }
}
}