Swift class to convert pollutant concentration values to AQI
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

104 linhas
4.0 KiB

import Foundation
// https://archive.epa.gov/ttn/ozone/web/pdf/rg701.pdf
// https://www.airnow.gov/sites/default/files/2020-05/aqi-technical-assistance-document-sept2018.pdf
class AQIUtils {
private static let index_low : [Double] = [ 0, 51, 101, 151, 201, 301, 401 ]
private static let index_high : [Double] = [ 50, 100, 150, 200, 300, 400, 500 ]
public enum Pollutant : String {
public typealias RawValue = String
case unknown = "Unknown"
case pm2p5 = "PM2.5"
case pm10 = "PM10"
case co = "Carbon Monoxide"
case so2 = "Sulfur Dioxide"
}
public enum AQICategory : String {
public typealias RawValue = String
case invalid = "Invalid"
case good = "Good"
case moderate = "Moderate"
case unhealthySensitive = "Unhealthy for Sensitive Groups"
case unhealthy = "Unhealthy"
case veryUnhealthy = "Very Unhealthy"
case hazardous = "Hazardous"
}
public struct AQIResult : CustomDebugStringConvertible {
public var debugDescription: String {
return "AQIResult(aqi: \(aqi), category: \(category.rawValue), pollutant: \(pollutant.rawValue))"
}
var aqi : Int
var category : AQICategory
var pollutant : Pollutant
}
private static func c2aqi(pollutant : Pollutant, concentration : Double, breakpoints_low : [Double], breakpoints_high : [Double]) -> AQIResult {
if (concentration > breakpoints_high.last!) { return(AQIResult(aqi: -1, category: .invalid, pollutant: pollutant)) }
let rank = breakpoints_high.map({ concentration <= $0 }).firstIndex(where: { $0 == true })!
let result = ceil(
(index_high[rank] - index_low[rank]) /
(breakpoints_high[rank] - breakpoints_low[rank]) * (concentration - breakpoints_low[rank]) + index_low[rank]
)
if (result <= 50) {
return(AQIResult(aqi: Int(result), category: .good, pollutant: pollutant))
} else if (result <= 100) {
return(AQIResult(aqi: Int(result), category: .moderate, pollutant: pollutant))
} else if (result <= 150) {
return(AQIResult(aqi: Int(result), category: .unhealthySensitive, pollutant: pollutant))
} else if (result <= 200) {
return(AQIResult(aqi: Int(result), category: .unhealthy, pollutant: pollutant))
} else if (result <= 300) {
return(AQIResult(aqi: Int(result), category: .veryUnhealthy, pollutant: pollutant))
} else if (result <= 400) {
return(AQIResult(aqi: Int(result), category: .hazardous, pollutant: pollutant))
} else if (result <= 500) {
return(AQIResult(aqi: Int(result), category: .hazardous, pollutant: pollutant))
} else {
return(AQIResult(aqi: Int(result), category: .invalid, pollutant: pollutant))
}
}
public static func pm2p5_aqi(concentration: Double) -> AQIResult {
let pm2p5_bpl : [Double] = [ 0, 12.1, 35.5, 55.5, 150.5, 250.5, 350.5 ]
let pm2p5_bph : [Double] = [ 12.0, 35.4, 55.4, 150.4, 250.4, 350.4, 500.4 ]
return(c2aqi(pollutant: .pm2p5, concentration: concentration, breakpoints_low: pm2p5_bpl, breakpoints_high: pm2p5_bph))
}
public static func pm10_aqi(concentration: Double) -> AQIResult {
let pm10_bpl : [Double] = [ 0, 55, 155, 255, 355, 425, 505 ]
let pm10_bph : [Double] = [ 54, 154, 254, 354, 424, 504, 604 ]
return(c2aqi(pollutant: .pm10, concentration: concentration, breakpoints_low: pm10_bpl, breakpoints_high: pm10_bph))
}
public static func co_aqi(concentration: Double) -> AQIResult {
let co_bpl : [Double] = [ 0, 4.5, 9.5, 12.5, 15.5, 30.5, 40.5 ]
let cp_bph : [Double] = [ 4.4, 9.4, 12.4, 15.4, 30.4, 40.4, 50.4 ]
return(c2aqi(pollutant: .co, concentration: concentration, breakpoints_low: co_bpl, breakpoints_high: cp_bph))
}
public static func so2_aqi(concentration: Double) -> AQIResult {
let so2_bpl : [Double] = [ 0, 36, 76, 186, 305, 605, 805 ]
let so2_bph : [Double] = [ 35, 75, 185, 304, 604, 804, 1004 ]
return(c2aqi(pollutant: .so2, concentration: concentration*1000, breakpoints_low: so2_bpl, breakpoints_high: so2_bph))
}
}