Display Calculated Data in SwiftUI Charts ...

I am trying to discover how to display my application’s calculated Solar Information values in a chart.

  1. My application identifies a selected location in MapKit.
  2. The application identifies the location’s longitude, latitude, and current time of day.
  3. The application calculates the selected location’s NOAA [SOLAR ELEVATION], and the [SOLAR AZIMUTH] for the time of day.
  4. The application calculates the data, then stores the calculated values as a [Plist] file within my application’s Document Directory.

For the moment, complete with repeated scouring of the Internet, I am not sure how to properly convert, transfer, or create a Structure, required by the chart to display the calculated values. I would like to create the chart once the calculations are complete, but I introduced a Plist to store the calculations for future use, too.

The calculated values coincide with the NOAA Solar Calculations, complete to the displayed [h : m : s], whereas I also designed the application to create the [Array of Dictionary Objects] to store the calculated values for each subsequent six minute interval, until the end of the selected location’s day. The calculated values are properly appended to the [Array of Dictionary Objects] after each completed calculation, with data transfer constants. There are 240 calculations per day from [00:06:00 to 23:54:00], presented as a [STRING], complete with the [Elevation] presented as a [DOUBLE].

For example :: The application generates the following [Calculated Array of Dictionary Objects], then recreates, and appends a new Plist in the Document Directory.

mySolarElevationDataArrayOfDictionaries :: [(theRequiredTimeOfDay: "00:06:00", theCalculatedElevation: -62.60301082991259), (theRequiredTimeOfDay: "00:12:00", theCalculatedElevation: -62.94818095051292), (theRequiredTimeOfDay: "00:18:00", theCalculatedElevation: -63.245198186807215), (theRequiredTimeOfDay: "00:24:00", theCalculatedElevation: -63.49236786176319), (theRequiredTimeOfDay: "00:30:00", theCalculatedElevation: -63.688223890934175), (theRequiredTimeOfDay: "00:36:00", theCalculatedElevation: -63.831564163806945), (theRequiredTimeOfDay: "00:42:00", theCalculatedElevation: -63.921486675739004), (theRequiredTimeOfDay: "00:48:00", theCalculatedElevation: -63.95741610687708), to the end of the data :: ===> (theRequiredTimeOfDay: "23:54:00", theCalculatedElevation: -60.69355458181633)]

The application presents the initial data as follows ::

Then presents a compass view to illustrate the results ::

I modified the Chart’s [MOCK DATA] from the calculated values to test the Chart’s display in a [SwiftUI Hosting Controller].

For example :: The following Chart Mock Data in a [HourlySunElevation_MockChartData.swift] file is called by the application’s [Content View].

import Foundation

struct Value {

    let theRequiredTimeOfDay: String
    let theCalculatedElevation: Double
    
    static func theSunElevationMockData() -> [Value] {
        
        return [Value(theRequiredTimeOfDay: "00:06:00", theCalculatedElevation: -62.60301082991259), Value(theRequiredTimeOfDay: "00:12:00", theCalculatedElevation: -62.94818095051292), Value(theRequiredTimeOfDay: "00:18:00", theCalculatedElevation: -63.245198186807215), Value(theRequiredTimeOfDay: "00:24:00", theCalculatedElevation: -63.49236786176319), Value(theRequiredTimeOfDay: "00:30:00", theCalculatedElevation: -63.688223890934175), Value(theRequiredTimeOfDay: "00:36:00", theCalculatedElevation: -63.831564163806945), Value(theRequiredTimeOfDay: "00:42:00", theCalculatedElevation: -63.921486675739004), Value(theRequiredTimeOfDay: "00:48:00", theCalculatedElevation: -63.95741610687708), to the end of the data :: ===> Value(theRequiredTimeOfDay: "23:54:00", theCalculatedElevation: -60.69355458181633)]

The Chart illustrates the Mock Data as follows ::

I also created a Struct within the [MySunElevationChart_ViewController] to try to append the calculated data, using the same logic with the Plist data transfer constants, as employed by the [Array of Dictionary Objects] ::

struct ChartSolarElevationValues {
    
    var theRequiredTimeOfDay: String
    var theCalculatedElevation: Double
    
    //  Structs have an implicit [init]. This is here for reference.
    init(theRequiredTimeOfDay: String, theCalculatedElevation: Double) {
        
        self.theRequiredTimeOfDay = theRequiredTimeOfDay
        self.theCalculatedElevation = theCalculatedElevation
        //mySolarElevationChartData.append(self)
    
    }   //  End of [init(theRequiredTimeOfDay: String, theCalculatedElevation: Double)]

}   //  End of [struct ChartSolarElevationValues]

Unfortunately, the result did not append each subsequent calculation, but continued to create the same calculation as a new distinct object ::

NOTE :: I only called three calculations with the Struct test.


//  NOTE :: To prevent an [ERROR] at [var mySolarElevationChartData = [ChartSolarElevationValues]] since it has an init.
        //  Therefore you must add () at the end of [var mySolarElevationChartData = [ChartSolarElevationValues]]
        let theData = [ChartSolarElevationValues]()
        
        //print("theData :: \(theData)\n")
        
        let someData = ChartSolarElevationValues(theRequiredTimeOfDay: TheTimeForDaySunElevation.theTheTimeForDaySunElevation, theCalculatedElevation:VerifyCityLocationSearchRequestCorrectedSolarElevation.theVerifyCityLocationSearchRequestCorrectedSolarElevation)
        
        var theData_New = theData
        
        theData_New.append(someData)
        
        print("theData_New :: \(theData_New)\n")

	  //	Prints :: theData_New :: [My_Map.ChartSolarElevationValues(theRequiredTimeOfDay: "00:06:00", theCalculatedElevation: -61.11000735370401)]]

		//	Prints :: [theData_New :: [My_Map.ChartSolarElevationValues(theRequiredTimeOfDay: "00:12:00", theCalculatedElevation: -61.315092082911875)]]

		//	Prints :: [theData_New :: [My_Map.ChartSolarElevationValues(theRequiredTimeOfDay: "00:18:00", theCalculatedElevation: -61.47403413313205)]]

So, I am misintepreting the required coding structure to properly append the Elevation Chart, and the Azimuth Chart with the calculated data.

I know something is amiss, but for the moment, I do not know how to address this issue.

Your suggestions would be welcome ... :]

jim_k

I previously mentioned, my application creates a [Solar Elevation] and a [Solar Azimuth] for an identifiable map location, based upon the [NOAA] calculations. The results happen to be accurate to the [Hour:Minute:Second], compared to NOAA calculations. The [Compass] view illustrates that success, at the selected location with the requested [Time of Day], and [Location].

I focused on the [Solar Elevation] first to determine how I could translate the calculated data to be displayed in a SwiftUI Chart. My previous attempts to create an [Appended Struct] to display the calculated data in the [Chart] failed miserably, although I managed to correct my error to properly append the [Struct]. As noted, every SwiftUI Chart example I discovered always engaged the [Chart Data] with [Mock Data] to interact with the [Chart]. The examples did not solve my question, but the accumulated information generated ideas for me to try.

NOTE :: For the moment, I have not discovered a solution with the following method to display the chart’s calculated [Y-Axis] data with a [Loli-pop], but still exploring.

So to possibly resolve my issue, I did the following to display the calculated data in a SwiftUI Chart with my very obscure, but somewhat tedious effective method. Please read my comment as too many incremental steps, and possibly TL:TR … :]

I prepared the [Calculated Data] ::

1.	  The application’s [MySunElevationChart_ViewController] manually identified each six minute time interval.
2.	Converted each six minute interval into a 24 hour fraction.
3.	Converted the fraction into a formatted date string.
4.	Appended each formatted date string to the [theTimeOfDayArray].
5.	Assigned the [theTimeOfDayArray] to a [Struct] in the [Constants Folder].
6.	The application’s [MySunElevationChart_ViewController] calculated the [Solar Elevation] with each six minute interval date fraction.
7.	  Appended each calculated [Solar Elevation] to the [theCalculatedSunElevationArray].
8.	Assigned the [theCalculatedSunElevationArray] to a [Struct] in the [Constants Folder].

I decided to modify my application’s original static value [MOCK DATA STRUCT] with the embedded [Calculated Data] ::

1.	  The application’s [MySunElevationChart_ViewController] retrieved the [theTimeOfDayArray].
2.	Isolated and extracted each [Formatted String] at a specified index.
3.	Assigned each extracted string value to a [Struct] in the [Constants Folder].
4.	The application’s [MySunElevationChart_ViewController] retrieved the [theCalculatedSunElevationArray].
5.	Isolated and extracted each [Solar Elevation] at a specified index.
6.	Assigned each extracted [Solar Elevation] value to a [Struct] in the [Constants Folder].
7.	  The application refactored the static value [Mock Chart Data] with each [Formatted String Struct Value], and each [Solar Elevation Struct Value].
8.	The application transfered the [Refactored Mock Data] to the [Chart Content View].

NOTE :: I created several [Structs] to prepare the application to receive and transfer the [Calculated Solar Elevation Values] to the [SwiftUI Chart].

I created [Structs] for the [Solar Time Of Day] and the [Solar Elevation] in the [Constants Folder]. I created [Structs] for the [Solar Time Of Day Array] and the [Solar Elevation Array] in the [Constants Folder]. I created [Structs] for each extracted [Solar Elevation Time] and each extracted [Solar Elevation] in the [Constants Folder] for the refactored [MOCK DATA STRUCT].

//   [For example the Time Structs]

struct SolarElevationTime_0 {
    static var theSolarElevationTime_0: String = ""
}   //  End of [struct SolarElevationTime_0]

//   to the end of the data …

struct SolarElevationTime_238 {
    static var theSolarElevationTime_238: String = ""
}   //  End of [struct SolarElevationTime_238]

//   [For example the Solar Elevation Structs]

struct SolarElevation_0 {
    static var theSolarElevation_0: Double = 0
}   //  End of [struct SolarElevation_0]

//   to the end of the data …

struct SolarElevation_238 {
    static var theSolarElevation_238: Double = 0
}   //  End of [struct SolarElevation_238]

I created a separate function, within the [MySunElevationChart_ViewController] to isolate, and extract each [Time of Day] from the [theTimeOfDayArray] with the following code ::

//  Identify the [TIME OF DAY ARRAY] from the [CONSTANTS FOLDER]
let someRequiredTimeOfDayArray = SolarElevationRequiredTimeOfDayDataArray.theSolarElevationRequiredTimeOfDayDataArray

let theTimeOfDayValueAtIndex_0 = (someRequiredTimeOfDayArray[0])
//   Transfer the constant to the [Constants Folder Struct]
SolarElevationTime_0.theSolarElevationTime_0 = theTimeOfDayValueAtIndex_0

//   to the end of the data …

let theTimeOfDayValueAtIndex_238 = (someRequiredTimeOfDayArray[238])
//   Transfer the constant to the [Constants Folder Struct]
SolarElevationTime_238.theSolarElevationTime_238 = theTimeOfDayValueAtIndex_238

I created a separate function, within the [MySunElevationChart_ViewController] to isolate, and extract each [Solar Elevation] from the [theCalculatedSunElevationArray] with the following code ::

//  Identify the [SOLAR ELEVATION ARRAY] from the [CONSTANTS FOLDER]
let someRequiredSolarElevationArray = SolarElevationRequiredDataArray.theSolarElevationRequiredDataArray

let theSolarElevationValueAtIndex_0 = (someRequiredSolarElevationArray[0])
//   Transfer the constant to the [Constants Folder Struct]
SolarElevation_0.theSolarElevation_0 = theSolarElevationValueAtIndex_0

//   to the end of the data …

let theSolarElevationValueAtIndex_238 = (someRequiredSolarElevationArray[238])
//   Transfer the constant to the [Constants Folder Struct]
SolarElevation_0.theSolarElevation_238 = theSolarElevationValueAtIndex_238

The application transfers and inserts the [Calculated Chart Data] to the application’s [Mock Data Struct]. I refactored the application’s original static value [Mock Data Struct] in the [HourlySunElevation_MockChartData] Swift File to receive the calculated [Struct] constants.

struct Value: Identifiable, Equatable, Hashable {

    let id = UUID()
    let theRequiredTimeOfDay: String
    let theCalculatedElevation: Double
    
    static func theSunElevationMockData() -> [Value] {
        return [Value(theRequiredTimeOfDay: SolarElevationTime_0.theSolarElevationTime_0, theCalculatedElevation: SolarElevation_0.theSolarElevation_0), Value(theRequiredTimeOfDay: SolarElevationTime_1.theSolarElevationTime_1, theCalculatedElevation: SolarElevation_1.theSolarElevation_1),
//	:: To the end of the data ::
Value(theRequiredTimeOfDay: SolarElevationTime_238.theSolarElevationTime_238, theCalculatedElevation: SolarElevation_238.theSolarElevation_238)]
    }   //  End of [static func theSunElevationMockData() -> [Value]]
}   //  End of [struct Value: Identifiable, Equatable, Hashable]

The application’s [Content View] receives and presents the calculated [Chart Data] ::

Continued ::

NOTE :: I am able to engage two different methods to display the calculated [Chart Data].

NOTE :: Both methods properly illustrate the [Chart Data].

NOTE :: As to which method would be best to engage the chart’s [Loli-pop], I do not know at this moment.

The [First Method ::

The application’s [Content View] calls the [MOCK Data] to display the [Chart] with the following code ::

struct SunElevationChartContentView: View {
    //  Identify the refactored [SWIFT CHARTS MOCK DATA]
    @State private var overallData = Value.theSunElevationMockData()
    var body: some View {
        //  Create the [CHART]
        VStack {
            Chart(overallData) {
                //  Display the [SOLAR ELEVATION DATA]
                LineMark(
                    x: .value("h:m:s", $0.theRequiredTimeOfDay),
                    y: .value("Elevation", $0.theCalculatedElevation)
                )
                //  A parameter for the Catmull-Rom spline. Use [0] for a [Uniform Spline].
                .interpolationMethod(.catmullRom(alpha: 0))
                .foregroundStyle(.yellow)
                .lineStyle(StrokeStyle(lineWidth: 10))
                .opacity(1.00)
            }   //  End of [Chart(overallData)]
        }   //  End of [VStack]
    }   //  End of [var body: some View]
}   //  End of [struct SunElevationChartContentView: View]

The SwiftUI Chart displays the [Calculated Solar Elevation Values] for each [Time of Day].

The [Second Method ::

The application’s [Content View] calls the [MOCK Data] to display the [Chart] with the following code ::

struct SunElevationChartContentView: View {
    //  Identify the refactored [SWIFT CHARTS MOCK DATA]
    @State private var overallData = Value.theSunElevationMockData()
    var body: some View {
        //  Create the [CHART]
        VStack {
            Chart {
                ForEach(overallData) { dataPoint in
                    //  Display the [SOLAR ELEVATION DATA]
                    LineMark(
                        x: .value("h:m:s", dataPoint.theRequiredTimeOfDay),
                        y: .value("Elevation", dataPoint.theCalculatedElevation)
                    )
                    //  A parameter for the Catmull-Rom spline. Use [0] for a [Uniform Spline].
                    .interpolationMethod(.catmullRom(alpha: 0))
                    .foregroundStyle(.yellow)
                    .lineStyle(StrokeStyle(lineWidth: 10))
                    .opacity(1.00)
                }   //  End of [ForEach(overallData) { dataPoint in]
            }   //  End of [Chart]
        }   //  End of [VStack]
    }   //  End of [var body: some View]
}   //  End of [struct SunElevationChartContentView: View]

The SwiftUI Chart displays the [Calculated Solar Elevation Values] for each [Time of Day].

As a side note, the above [Content View Code] methods do not show the incremental [Structs] to configure the [Chart] to show the [Labels], the [CGRect Vertical Lines], and the [CGRect Horizontal Lines].

For the moment, complete with my last coding issue, I am still trying to determine how to properly engage a [Chart Loli-pop] to display the individual [Solar Elevation] values. I am sure I shall discover a method to present the chart’s [Calculated Data], by employing the [Chart Loli-pop] in the future, using the [First Chart Content View Method], or the [Second Chart Content View Method]. This solution is still a mystery to me, where I did not completely resolve my question … :]

As always, thank you for your time reviewing my [Chart Data] issue … :]

Best regards, jim_k

The resultant calculated Chart Images ::

Paris ::

Suva Fiji ::

Queenstown NZ ::

Cairo ::

Melbourne ::

London ::

Display Calculated Data in SwiftUI Charts ...
 
 
Q