Added dewPoint to Environment Metrics (#1377)

Co-authored-by: Jake-B <jake-b@users.noreply.github.com>
This commit is contained in:
jake-b 2025-09-07 19:03:49 -04:00 committed by GitHub
parent 7a065277ea
commit aaeecba532
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 90 additions and 21 deletions

View file

@ -1086,6 +1086,9 @@
}
}
}
},
"%f%%" : {
},
"%lf" : {
"localizations" : {

View file

@ -52,4 +52,10 @@ public class TelemetryEntity: NSManagedObject, Identifiable {
@ManagedAttribute<Float>(attributeName: "soilTemperature") public var soilTemperature: Float?
@ManagedAttribute<UInt32>(attributeName: "soilMoisture") public var soilMoisture: UInt32?
public var dewPoint: Float? {
guard let temp = self.temperature, let rh = self.relativeHumidity else {
return nil
}
return Float(calculateDewPoint(temp: temp, relativeHumidity: rh, convertToLocale: false))
}
}

View file

@ -30,6 +30,9 @@ class MetricsChartSeries: ObservableObject {
// A closure that will provide the foreground style given the data set and overall chart range
let foregroundStyle: (ClosedRange<Float>?) -> AnyShapeStyle?
// For drawing the lines in the chart
let strokeStyle: StrokeStyle
// A closure that will provide the Chart Content for this series
let chartBodyClosure:
(MetricsChartSeries, ClosedRange<Float>?, TelemetryEntity) -> AnyChartContent? // Closure to render the chart
@ -51,6 +54,7 @@ class MetricsChartSeries: ObservableObject {
minumumYAxisSpan: Float? = nil,
conversion: ((Value) -> Value)? = nil,
visible: Bool = true,
strokeStyle: StrokeStyle = StrokeStyle(lineWidth: 4),
foregroundStyle: @escaping ((ClosedRange<Float>?) -> ForegroundStyle?) = { _ in nil },
@ChartContentBuilder chartBody: @escaping (MetricsChartSeries, ClosedRange<Float>?, Date, Value) -> ChartBody?
) {
@ -62,7 +66,8 @@ class MetricsChartSeries: ObservableObject {
self.initialYAxisRange = initialYAxisRange
self.minumumYAxisSpan = minumumYAxisSpan
self.visible = visible
self.strokeStyle = strokeStyle
// By saving these closures, MetricsChartSeries can be type agnostic
// This is a less elegant form of type erasure, but doesn't require a new Any-type
self.foregroundStyle = { range in foregroundStyle(range).map({ AnyShapeStyle($0) }) }

View file

@ -90,12 +90,18 @@ struct LocalWeatherConditions: View {
}
/// Magnus Formula
func calculateDewPoint(temp: Float, relativeHumidity: Float) -> Double {
func calculateDewPoint(temp: Float, relativeHumidity: Float, convertToLocale: Bool = true) -> Double {
let a: Float = 17.27
let b: Float = 237.7
let alpha = ((a * temp) / (b + temp)) + log(relativeHumidity / 100.0)
let dewPoint = (b * alpha) / (a - alpha)
let dewPointUnit = Measurement<UnitTemperature>(value: Double(dewPoint), unit: .celsius)
if !convertToLocale {
return Double(dewPoint)
}
// Otherwise convert to locale units, default behavior
let locale = NSLocale.current as NSLocale
let localeUnit = locale.object(forKey: NSLocale.Key(rawValue: "kCFLocaleTemperatureUnitKey"))
var format: UnitTemperature = .celsius

View file

@ -39,6 +39,19 @@ extension MetricsColumnList {
} ?? Text(verbatim: Constants.nilValueIndicator)
}),
// Relative Humidity Series Configuration
MetricsTableColumn(
id: "dewPoint",
keyPath: \.dewPoint,
name: "Dew Point",
abbreviatedName: "Dew",
minWidth: 30, maxWidth: 45,
tableBody: { _, dewPoint in
dewPoint.map {
Text("\($0.formattedTemperature())")
} ?? Text(verbatim: Constants.nilValueIndicator)
}),
// Barometric Pressure Series Configuration
MetricsTableColumn(
id: "barometricPressure",

View file

@ -50,7 +50,7 @@ extension MetricsSeriesList {
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.lineStyle(series.strokeStyle)
.alignsMarkStylesWithPlotArea()
}
}),
@ -76,11 +76,42 @@ extension MetricsSeriesList {
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.lineStyle(series.strokeStyle)
.alignsMarkStylesWithPlotArea()
}
}),
MetricsChartSeries(
id: "dewPoint",
keyPath: \.dewPoint,
name: "Dew Point",
abbreviatedName: "Dew",
minumumYAxisSpan: 50.0,
conversion: { t in t.map { Float($0.localeTemperature()) } },
strokeStyle: StrokeStyle(lineWidth: 4, dash: [2, 2]),
foregroundStyle: { chartRange in
let locale = NSLocale.current as NSLocale
let localeUnit = locale.object(forKey: NSLocale.Key(rawValue: "kCFLocaleTemperatureUnitKey"))
let format: UnitTemperature = localeUnit as? String ?? "Celsius" == "Fahrenheit" ? .fahrenheit : .celsius
let lowerBound = chartRange.map { Double($0.lowerBound) } ?? 0.0
let upperBound = chartRange.map { Double($0.upperBound) } ?? 100.0
let stops: [Gradient.Stop] = generateStops(minTemp: lowerBound, maxTemp: upperBound, tempUnit: format, opacity: 1.0)
return LinearGradient(stops: stops, startPoint: .bottom, endPoint: .top)
},
chartBody: { series, chartRange, time, dewPoint in
if let dewPoint {
LineMark(
x: .value("Time", time),
y: .value(
series.abbreviatedName, dewPoint.localeTemperature())
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(series.strokeStyle)
.alignsMarkStylesWithPlotArea()
}
}),
// Barometric Pressure Series Configuration
MetricsChartSeries(
id: "barometricPressure",
@ -102,7 +133,7 @@ extension MetricsSeriesList {
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.lineStyle(series.strokeStyle)
.alignsMarkStylesWithPlotArea()
}
}),
@ -130,7 +161,7 @@ extension MetricsSeriesList {
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.lineStyle(series.strokeStyle)
.alignsMarkStylesWithPlotArea()
}
}),
@ -156,7 +187,7 @@ extension MetricsSeriesList {
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.lineStyle(series.strokeStyle)
.alignsMarkStylesWithPlotArea()
}
}),
@ -182,7 +213,7 @@ extension MetricsSeriesList {
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.lineStyle(series.strokeStyle)
.alignsMarkStylesWithPlotArea()
}
}),
@ -208,7 +239,7 @@ extension MetricsSeriesList {
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.lineStyle(series.strokeStyle)
.alignsMarkStylesWithPlotArea()
}
}),
@ -234,7 +265,7 @@ extension MetricsSeriesList {
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.lineStyle(series.strokeStyle)
.alignsMarkStylesWithPlotArea()
}
}),
@ -261,7 +292,7 @@ extension MetricsSeriesList {
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.lineStyle(series.strokeStyle)
.alignsMarkStylesWithPlotArea()
}
}),
@ -288,7 +319,7 @@ extension MetricsSeriesList {
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.lineStyle(series.strokeStyle)
.alignsMarkStylesWithPlotArea()
PointMark(
x: .value("Time", time),
@ -327,7 +358,7 @@ extension MetricsSeriesList {
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.lineStyle(series.strokeStyle)
.alignsMarkStylesWithPlotArea()
}
}),
@ -353,7 +384,7 @@ extension MetricsSeriesList {
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.lineStyle(series.strokeStyle)
.alignsMarkStylesWithPlotArea()
}
}),
@ -379,7 +410,7 @@ extension MetricsSeriesList {
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.lineStyle(series.strokeStyle)
.alignsMarkStylesWithPlotArea()
}
}),
@ -405,7 +436,7 @@ extension MetricsSeriesList {
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.lineStyle(series.strokeStyle)
.alignsMarkStylesWithPlotArea()
}
}),
@ -431,7 +462,7 @@ extension MetricsSeriesList {
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.lineStyle(series.strokeStyle)
.alignsMarkStylesWithPlotArea()
}
}),
@ -457,7 +488,7 @@ extension MetricsSeriesList {
)
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("Series", series.abbreviatedName))
.lineStyle(StrokeStyle(lineWidth: 4))
.lineStyle(series.strokeStyle)
.alignsMarkStylesWithPlotArea()
}
})

View file

@ -21,9 +21,14 @@ struct MetricsColumnDetail: View {
Section("Chart") {
ForEach(seriesList) { series in
HStack {
Circle()
.fill(series.foregroundStyle(0.0...100.0) ?? AnyShapeStyle(.clear))
.frame(width: 20.0, height: 20.0)
Path { path in
path.move(to: CGPoint(x: 10, y: 0))
path.addLine(to: CGPoint(x: 10, y: 20))
}
.stroke(series.foregroundStyle(0.0...100.0) ?? AnyShapeStyle(.clear),
style: series.strokeStyle)
.frame(width: 20.0, height: 20.0)
.rotationEffect(.degrees(90.0))
Text(series.name)
Spacer()
if series.visible {