Spaces:
Sleeping
Sleeping
File size: 8,276 Bytes
6ee917b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | import Foundation
enum WeatherCode: Int {
case clearSky = 0
case mainlyClear = 1
case partlyCloudy = 2
case overcast = 3
case fog = 45
case depositingRimeFog = 48
case lightDrizzle = 51
case moderateDrizzle = 53
case denseDrizzle = 55
case lightFreezingDrizzle = 56
case moderateOrDenseFreezingDrizzle = 57
case lightRain = 61
case moderateRain = 63
case heavyRain = 65
case lightFreezingRain = 66
case moderateOrHeavyFreezingRain = 67
case slightSnowfall = 71
case moderateSnowfall = 73
case heavySnowfall = 75
case snowGrains = 77
case slightRainShowers = 80
case moderateRainShowers = 81
case heavyRainShowers = 82
case slightSnowShowers = 85
case heavySnowShowers = 86
case thunderstormSlightOrModerate = 95
case thunderstormStrong = 96
case thunderstormHeavy = 99
/// Calculate weather interpretation code
/// http://www.cosmo-model.org/content/model/documentation/newsLetters/newsLetter06/cnl6_hoffmann.pdf
/// https://www.dwd.de/DE/leistungen/pbfb_verlag_promet/pdf_promethefte/28_1_2_pdf.pdf?__blob=publicationFile&v=8
public static func calculate(cloudcover: Float, precipitation: Float, convectivePrecipitation: Float?, snowfallCentimeters: Float, gusts: Float?, cape: Float?, liftedIndex: Float?, visibilityMeters: Float?, categoricalFreezingRain: Float?, modelDtSeconds: Int) -> WeatherCode? {
guard cloudcover.isFinite, precipitation.isFinite, snowfallCentimeters.isFinite else {
return nil
}
let modelDtHours = Float(modelDtSeconds) / 3600
//let thunderstromStrength: WeatherCode = ((gusts ?? 0) >= 18/3.6 || (precipitation / modelDtHours) >= 10) ? .thunderstormStrong : ((gusts ?? 0 >= 29/3.6) || (precipitation / modelDtHours) >= 25) ? .thunderstormStrong : .thunderstormSlightOrModerate
if let cape, cape >= 3000 {
if let liftedIndex {
if liftedIndex <= -5 {
return .thunderstormSlightOrModerate
}
} else {
return .thunderstormSlightOrModerate
}
}
if let categoricalFreezingRain, categoricalFreezingRain >= 1 {
switch precipitation / modelDtHours {
case 0.01..<0.5: return .lightFreezingDrizzle
case 0.5..<1.0: return .moderateOrDenseFreezingDrizzle
case 1.0..<1.3: return .moderateOrDenseFreezingDrizzle
case 1.3..<2.5: return .lightFreezingRain
case 2.5..<7.6: return .moderateOrHeavyFreezingRain
case 7.6...: return .moderateOrHeavyFreezingRain
default: break
}
}
if (convectivePrecipitation ?? 0) > 0 || (cape ?? 0) >= 800 {
switch snowfallCentimeters / modelDtHours {
case 0.01..<0.2: return .slightSnowShowers
case 0.2..<0.8: return .slightSnowShowers
case 0.8...: return .heavySnowShowers
default: break
}
switch precipitation / modelDtHours {
case 1.3..<2.5: return .slightRainShowers
case 2.5..<7.6: return .moderateRainShowers
case 7.6...: return .moderateRainShowers
default: break
}
}
switch snowfallCentimeters / modelDtHours {
case 0.01..<0.2: return .slightSnowfall
case 0.2..<0.8: return .moderateSnowfall
case 0.8...: return .heavySnowfall
default: break
}
switch precipitation / modelDtHours {
case 0.01..<0.5: return .lightDrizzle
case 0.5..<1.0: return .moderateDrizzle
case 1.0..<1.3: return .denseDrizzle
case 1.3..<2.5: return .lightRain
case 2.5..<7.6: return .moderateRain
case 7.6...: return .heavyRain
default: break
}
if let visibilityMeters, visibilityMeters <= 1000 {
return .fog
}
switch cloudcover {
case 0..<20: return .clearSky
case 20..<50: return .mainlyClear
case 50..<80: return .partlyCloudy
case 80...: return .overcast
default: break
}
return nil
}
public static func calculate(cloudcover: [Float], precipitation: [Float], convectivePrecipitation: [Float]?, snowfallCentimeters: [Float], gusts: [Float]?, cape: [Float]?, liftedIndex: [Float]?, visibilityMeters: [Float]?, categoricalFreezingRain: [Float]?, modelDtSeconds: Int) -> [Float] {
return cloudcover.indices.map { i in
return calculate(
cloudcover: cloudcover[i],
precipitation: precipitation[i],
convectivePrecipitation: convectivePrecipitation?[i],
snowfallCentimeters: snowfallCentimeters[i],
gusts: gusts?[i],
cape: cape?[i],
liftedIndex: liftedIndex?[i],
visibilityMeters: visibilityMeters?[i],
categoricalFreezingRain: categoricalFreezingRain?[i],
modelDtSeconds: modelDtSeconds
).map({Float($0.rawValue)}) ?? .nan
}
}
/// True if weather code is an precipitation event. Thunderstorm, return false as they may only indicate potential
var isPrecipitationEvent: Bool {
switch self {
case .lightDrizzle, .moderateDrizzle, .denseDrizzle:
fallthrough
case .lightFreezingDrizzle, .moderateOrDenseFreezingDrizzle:
fallthrough
case .lightRain, .moderateRain, .heavyRain:
fallthrough
case .lightFreezingRain, .moderateOrHeavyFreezingRain:
fallthrough
case .slightSnowfall, .moderateSnowfall, .heavySnowfall, .snowGrains:
fallthrough
case .slightRainShowers, .moderateRainShowers,.heavyRainShowers:
fallthrough
case .slightSnowShowers, .heavySnowShowers:
return true
default:
return false
}
}
/// DWD ICON weather codes show rain although precipitation is 0
/// Similar for snow at +2°C or more
func correctDwdIconWeatherCode(temperature_2m: Float, precipitation: Float, snowfallHeightAboveGrid: Bool) -> WeatherCode {
if precipitation <= 0 && self.isPrecipitationEvent {
// Weather code shows drizzle, but no precipitation, demote to overcast
return .overcast
}
if temperature_2m >= 2 || snowfallHeightAboveGrid {
// Weather code may show snow, although temperature is high
switch self {
case .slightSnowfall:
return .lightRain
case .moderateSnowfall:
return .moderateRain
case .heavySnowfall:
return .heavyRain
default:
break
}
}
if temperature_2m < -1 {
switch self {
case .lightRain:
return .slightSnowfall
case .moderateRain:
return .moderateSnowfall
case .heavyRain:
return .heavySnowfall
default:
break
}
}
return self
}
// If temperature smaller or greated 0°C, set or unset snow
func correctSnowRainHardCutOff(temperature_2m: Float) -> Self {
if temperature_2m > 0 {
// Weather code may show snow, although temperature is high
switch self {
case .slightSnowfall:
return .lightRain
case .moderateSnowfall:
return .moderateRain
case .heavySnowfall:
return .heavyRain
default:
break
}
}
if temperature_2m < 0 {
switch self {
case .lightRain:
return .slightSnowfall
case .moderateRain:
return .moderateSnowfall
case .heavyRain:
return .heavySnowfall
default:
break
}
}
return self
}
}
|