Spaces:
Sleeping
Sleeping
| import Foundation | |
| extension Array where Element == Float { | |
| /// bounds: Apply min and max after interpolation | |
| func interpolate(type: ReaderInterpolation, timeOld: TimerangeDt, timeNew: TimerangeDt, latitude: Float, longitude: Float, scalefactor: Float) -> [Float] { | |
| switch type { | |
| case .linear: | |
| return interpolateLinear(timeOld: timeOld, timeNew: timeNew, scalefactor: scalefactor) | |
| case .linearDegrees: | |
| return interpolateLinearDegrees(timeOld: timeOld, timeNew: timeNew, scalefactor: scalefactor) | |
| case .hermite(let bounds): | |
| return interpolateHermite(timeOld: timeOld, timeNew: timeNew, scalefactor: scalefactor, bounds: bounds) | |
| case .solar_backwards_averaged, .solar_backwards_missing_not_averaged: | |
| return interpolateSolarBackwards(timeOld: timeOld, timeNew: timeNew, latitude: latitude, longitude: longitude, scalefactor: scalefactor) | |
| case .backwards_sum: | |
| return backwardsSum(timeOld: timeOld, timeNew: timeNew, scalefactor: scalefactor) | |
| case .backwards: | |
| return backwards(timeOld: timeOld, timeNew: timeNew, scalefactor: scalefactor) | |
| } | |
| } | |
| func interpolateSolarBackwards(timeOld timeLow: TimerangeDt, timeNew time: TimerangeDt, latitude: Float, longitude: Float, scalefactor: Float) -> [Float] { | |
| /// Like regular hermite, but interpolated via clearsky index kt derived with solar factor | |
| let position = RegularGrid(nx: 1, ny: 1, latMin: latitude, lonMin: longitude, dx: 1, dy: 1) | |
| let solarLow = Zensun.calculateRadiationBackwardsAveraged(grid: position, locationRange: 0..<1, timerange: timeLow).data | |
| let solar = Zensun.calculateRadiationBackwardsAveraged(grid: position, locationRange: 0..<1, timerange: time).data | |
| let dt = time.dtSeconds | |
| let dtOld = timeLow.dtSeconds | |
| let tStart = timeLow.range.lowerBound.timeIntervalSince1970 | |
| return time.enumerated().map { (i, t) in | |
| // time need to be shifted by dtOld/2 because those are averages over time | |
| let (index, fraction) = (t.timeIntervalSince1970 - tStart + dtOld - dt - dtOld/2 + dt/2).moduloFraction(dtOld) | |
| if index < 0 { | |
| return .nan | |
| } | |
| let indexB = Swift.max(index, 0) | |
| let indexA = Swift.max(index-1, 0) | |
| let indexC = Swift.min(index+1, self.count-1) | |
| let indexD = Swift.min(index+2, self.count-1) | |
| if self[indexB].isNaN { | |
| return .nan | |
| } | |
| if solar[i] == 0 { | |
| return 0 // Night | |
| } | |
| let A = self[indexA] | |
| let B = self[indexB] | |
| let C = self[indexC] | |
| let D = self[indexD] | |
| let solA = solarLow[indexA] | |
| let solB = solarLow[indexB] | |
| let solC = solarLow[indexC] | |
| let solD = solarLow[indexD] | |
| var ktA = solA <= 0.005 ? .nan : Swift.min(A / solA, 1100) | |
| var ktB = solB <= 0.005 ? .nan : Swift.min(B / solB, 1100) | |
| var ktC = solC <= 0.005 ? .nan : Swift.min(C / solC, 1100) | |
| var ktD = solD <= 0.005 ? .nan : Swift.min(D / solD, 1100) | |
| if ktA.isNaN { | |
| ktA = !ktB.isNaN ? ktB : !ktC.isNaN ? ktC : ktD | |
| } | |
| if ktB.isNaN { | |
| ktB = !ktA.isNaN ? ktA : !ktC.isNaN ? ktC : ktD | |
| } | |
| if ktC.isNaN { | |
| ktC = !ktB.isNaN ? ktB : !ktD.isNaN ? ktD : ktA | |
| } | |
| if ktD.isNaN { | |
| ktD = !ktC.isNaN ? ktC : !ktB.isNaN ? ktB : ktA | |
| } | |
| // no interpolation | |
| //return (fraction < 0.5 ? ktB : ktC) * solar[i] | |
| // linear interpolation | |
| //return (ktB * (1-fraction) + ktC * fraction) * solar[i] | |
| let a = -ktA/2.0 + (3.0*ktB)/2.0 - (3.0*ktC)/2.0 + ktD/2.0 | |
| let b = ktA - (5.0*ktB)/2.0 + 2.0*ktC - ktD / 2.0 | |
| let c = -ktA/2.0 + ktC/2.0 | |
| let d = ktB | |
| let h = (a*fraction*fraction*fraction + b*fraction*fraction + c*fraction + d) * solar[i] | |
| /// adjust it to scalefactor, otherwise interpolated values show more level of detail | |
| return roundf(h * scalefactor) / scalefactor | |
| } | |
| } | |
| /// Take the next value and devide it by dt. Used for precipitation, snow, etc | |
| func backwardsSum(timeOld timeLow: TimerangeDt, timeNew time: TimerangeDt, scalefactor: Float) -> [Float] { | |
| let multiply = Float(time.dtSeconds) / Float(timeLow.dtSeconds) | |
| return time.map { t in | |
| /// Take the next array element, except it it is the same timestamp | |
| let index = Swift.min((t.timeIntervalSince1970 - time.dtSeconds) / timeLow.dtSeconds + 1 - timeLow.range.lowerBound.timeIntervalSince1970 / timeLow.dtSeconds, self.count-1) | |
| /// adjust it to scalefactor, otherwise interpolated values show more level of detail | |
| return roundf(self[index] * multiply * scalefactor) / scalefactor | |
| } | |
| } | |
| func backwards(timeOld timeLow: TimerangeDt, timeNew time: TimerangeDt, scalefactor: Float) -> [Float] { | |
| return time.map { t in | |
| /// Take the next array element, except it it is the same timestamp | |
| let index = Swift.min((t.timeIntervalSince1970 - time.dtSeconds) / timeLow.dtSeconds + 1 - timeLow.range.lowerBound.timeIntervalSince1970 / timeLow.dtSeconds, self.count-1) | |
| /// adjust it to scalefactor, otherwise interpolated values show more level of detail | |
| return roundf(self[index] * scalefactor) / scalefactor | |
| } | |
| } | |
| /// Interpolate degree values from 0-360° | |
| func interpolateLinearDegrees(timeOld timeLow: TimerangeDt, timeNew time: TimerangeDt, scalefactor: Float) -> [Float] { | |
| return time.map { t in | |
| let index = t.timeIntervalSince1970 / timeLow.dtSeconds - timeLow.range.lowerBound.timeIntervalSince1970 / timeLow.dtSeconds | |
| let fraction = Float(t.timeIntervalSince1970 % timeLow.dtSeconds) / Float(timeLow.dtSeconds) | |
| let A = self[index] | |
| let B = index+1 >= self.count ? A : self[index+1] | |
| let A2 = (abs(B-A) > 180 && A < B) ? A + 360 : A | |
| let B2 = (abs(B-A) > 180 && A > B) ? B + 360 : B | |
| let h = A2 * (1-fraction) + B2 * fraction | |
| let h2 = h.truncatingRemainder(dividingBy: 360) | |
| /// adjust it to scalefactor, otherwise interpolated values show more level of detail | |
| return roundf(h2 * scalefactor) / scalefactor | |
| } | |
| } | |
| func interpolateLinear(timeOld timeLow: TimerangeDt, timeNew time: TimerangeDt, scalefactor: Float) -> [Float] { | |
| return time.map { t in | |
| let index = t.timeIntervalSince1970 / timeLow.dtSeconds - timeLow.range.lowerBound.timeIntervalSince1970 / timeLow.dtSeconds | |
| let fraction = Float(t.timeIntervalSince1970 % timeLow.dtSeconds) / Float(timeLow.dtSeconds) | |
| let A = self[index] | |
| let B = index+1 >= self.count ? A : self[index+1] | |
| let h = A * (1-fraction) + B * fraction | |
| /// adjust it to scalefactor, otherwise interpolated values show more level of detail | |
| return roundf(h * scalefactor) / scalefactor | |
| } | |
| } | |
| func interpolateHermite(timeOld timeLow: TimerangeDt, timeNew time: TimerangeDt, scalefactor: Float, bounds: ClosedRange<Float>?) -> [Float] { | |
| return time.map { t in | |
| let index = t.timeIntervalSince1970 / timeLow.dtSeconds - timeLow.range.lowerBound.timeIntervalSince1970 / timeLow.dtSeconds | |
| let fraction = Float(t.timeIntervalSince1970 % timeLow.dtSeconds) / Float(timeLow.dtSeconds) | |
| let B = self[index] | |
| let A = index-1 < 0 ? B : self[index-1].isNaN ? B : self[index-1] | |
| let C = index+1 >= self.count ? B : self[index+1].isNaN ? B : self[index+1] | |
| let D = index+2 >= self.count ? C : self[index+2].isNaN ? B : self[index+2] | |
| let a = -A/2.0 + (3.0*B)/2.0 - (3.0*C)/2.0 + D/2.0 | |
| let b = A - (5.0*B)/2.0 + 2.0*C - D / 2.0 | |
| let c = -A/2.0 + C/2.0 | |
| let d = B | |
| let h = a*fraction*fraction*fraction + b*fraction*fraction + c*fraction + d | |
| /// adjust it to scalefactor, otherwise interpolated values show more level of detail | |
| let hScaled = roundf(h * scalefactor) / scalefactor | |
| if let bounds = bounds { | |
| return Swift.min(Swift.max(hScaled, bounds.lowerBound), bounds.upperBound) | |
| } | |
| return hScaled | |
| } | |
| } | |
| } | |