I have two data sets and I want to show them in the same chart.
Both data sets are time based (x axis) and with same time range. They have different units of measure and scaling (y axis).
Is it possible to show both data sets in the same chart, but with two different y axis definitions?
I have tried to overlay two independent charts but then the two charts are not aligned as I configure one with Y axis to the left and the other with Y axis to the right.
Any ideas how to make this happen?
Here is an example showing two different data sets on the same axis. You need to scale the data yourself though, so in this example I've done so manually with the adjustments to the pressure values, but you could also do this programatically.
Example here at github.com/jknlsn/MultipleDataSetSwiftChartsExample with lollipop detail popover, and slightly simplified example below.
import Charts
import SwiftUI
import WeatherKit
struct HourWeatherStruct {
var date: Date
var pressure: Double
var temperature: Double
var windSpeed: Double
}
let hours: [HourWeatherStruct] = [
HourWeatherStruct(date: Date(timeIntervalSinceNow: 3600),
pressure: 1015.0,
temperature: 18.2,
windSpeed: 6.1),
HourWeatherStruct(date: Date(timeIntervalSinceNow: 3600 * 2),
pressure: 1015.3,
temperature: 18.2,
windSpeed: 8.1),
HourWeatherStruct(date: Date(timeIntervalSinceNow: 3600 * 3),
pressure: 1015.9,
temperature: 18.2,
windSpeed: 9.4),
HourWeatherStruct(date: Date(timeIntervalSinceNow: 3600 * 4),
pressure: 1016.3,
temperature: 18.2,
windSpeed: 5.2),
HourWeatherStruct(date: Date(timeIntervalSinceNow: 3600 * 5),
pressure: 1016.3,
temperature: 18.2,
windSpeed: 12.1),
HourWeatherStruct(date: Date(timeIntervalSinceNow: 3600 * 6),
pressure: 1016.3,
temperature: 18.2,
windSpeed: 11.1),
HourWeatherStruct(date: Date(timeIntervalSinceNow: 3600 * 7),
pressure: 1017.3,
temperature: 18.2,
windSpeed: 10.1),
HourWeatherStruct(date: Date(timeIntervalSinceNow: 3600 * 8),
pressure: 1018.3,
temperature: 18.2,
windSpeed: 11.1),
HourWeatherStruct(date: Date(timeIntervalSinceNow: 3600 * 9),
pressure: 1018.3,
temperature: 18.2,
windSpeed: 9.1),
HourWeatherStruct(date: Date(timeIntervalSinceNow: 3600 * 10),
pressure: 1018.3,
temperature: 18.2,
windSpeed: 8.1),
HourWeatherStruct(date: Date(timeIntervalSinceNow: 3600 * 11),
pressure: 1017.3,
temperature: 18.2,
windSpeed: 19.9),
HourWeatherStruct(date: Date(timeIntervalSinceNow: 3600 * 12),
pressure: 1018.3,
temperature: 18.2,
windSpeed: 7.1),
]
struct InteractiveLollipopChartMinimal: View {
var body: some View {
Chart {
ForEach(hours, id: \.date) {
LineMark(
x: .value("Date", $0.date, unit: .hour),
y: .value("Wind Speed", $0.windSpeed)
)
.foregroundStyle(by: .value("Value", "Wind"))
LineMark(
x: .value("Date", $0.date, unit: .hour),
y: .value("Pressure", ($0.pressure - 1014) * 4)
)
.foregroundStyle(by: .value("Value", "Pressure"))
}
.lineStyle(StrokeStyle(lineWidth: 4.0))
.interpolationMethod(.catmullRom)
}
.chartForegroundStyleScale([
"Pressure": .purple,
"Wind": .teal
])
.chartXAxis {
AxisMarks(position: .bottom, values: .stride(by: .hour, count: 2)) {
_ in
AxisTick()
AxisGridLine()
AxisValueLabel(format: .dateTime.hour(), centered: true)
}
}
.chartYAxis {
AxisMarks(position: .leading, values: Array(stride(from: 0, through: 24, by: 4))){
axis in
AxisTick()
AxisGridLine()
AxisValueLabel("\(1014 + (axis.index * 1))", centered: false)
}
AxisMarks(position: .trailing, values: Array(stride(from: 0, through: 24, by: 4))){
axis in
AxisTick()
AxisGridLine()
AxisValueLabel("\(axis.index * 4)", centered: false)
}
}
}
}
struct InteractiveLollipopMinimal: View {
var body: some View {
List {
VStack(alignment: .leading) {
VStack(alignment: .leading) {
Text("Windspeed and Pressure")
.font(.callout)
.foregroundStyle(.secondary)
Text("\(hours.first?.date ?? Date(), format: .dateTime)")
.font(.title2.bold())
}
InteractiveLollipopChartMinimal()
.frame(height: 200)
}
.listRowSeparator(.hidden)
}
.listStyle(.plain)
.navigationBarTitle("Interactive Lollipop", displayMode: .inline)
}
}