Created
July 31, 2017 20:23
-
-
Save Souf-R/24b8e1024985563cceaf570914f27145 to your computer and use it in GitHub Desktop.
PrayerTimes API for iOS, swift 3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
+// | |
+// Thsi class is based on this repo: https://github.com/ashikahmad/PrayerTimes-Swift | |
+// SRPrayerTime.swift | |
+// PrayerKit | |
+// | |
+// Created by Soufiane ROCHDI on 07/31/17. | |
+// | |
+import UIKit | |
+ | |
+class SRPrayerTime { | |
+ | |
+ enum CalculationMethod { | |
+ /// Muslim World League | |
+ case MWL | |
+ /// Islamic Society of North America | |
+ case ISNA | |
+ /// Egyptian General Authority of Survey | |
+ case Egypt | |
+ /// Umm al-Qura University, Makkah | |
+ case Makkah | |
+ /// University of Islamic Science, Karachi | |
+ case Karachi | |
+ /// Institute of Geophysics, University of Tehran | |
+ case Tehran | |
+ /// Shia Ithna Ashari, Leva Research Institute, Qum | |
+ case Jafari | |
+ /// Custom, these can be changed as user sets. | |
+ case Custom | |
+ } | |
+ | |
+ enum JuristicMethod { | |
+ /// Shafi'i, Maliki, Ja'fari, and Hanbali | |
+ case Shafii | |
+ /// Hanafi | |
+ case Hanafi | |
+ | |
+ func toInt()->Int { | |
+ switch self { | |
+ case .Shafii: return 0 | |
+ case .Hanafi: return 1 | |
+ } | |
+ } | |
+ } | |
+ | |
+ enum HigherLatutudeAdjustment { | |
+ case None | |
+ case MidNight | |
+ case OneSeventh | |
+ case AngleBased | |
+ } | |
+ | |
+ enum OutputTimeFormat { | |
+ case Time24 | |
+ case Time12 | |
+ case Time12NoSuffix | |
+ case Float | |
+ case Date | |
+ } | |
+ | |
+ enum TimeNames : Int { | |
+ case Fajr = 0 | |
+ case Sunrise = 1 | |
+ case Dhuhr = 2 | |
+ case Asr = 3 | |
+ case Sunset = 4 | |
+ case Maghrib = 5 | |
+ case Isha = 6 | |
+ | |
+ func toString()->String { | |
+ switch(self) { | |
+ case .Fajr : return "Fajr" | |
+ case .Sunrise : return "Sunrise" | |
+ case .Dhuhr : return "Dhuhr" | |
+ case .Asr : return "Asr" | |
+ case .Sunset : return "Sunset" | |
+ case .Maghrib : return "Maghrib" | |
+ case .Isha : return "Isha" | |
+ } | |
+ } | |
+ } | |
+ | |
+ struct Coordinate { | |
+ var latitude:Double | |
+ var longitude:Double | |
+ | |
+ init(lat:Double, lng:Double){ | |
+ latitude = lat | |
+ longitude = lng | |
+ } | |
+ } | |
+ | |
+ private static let GregorianCalendar = NSCalendar(calendarIdentifier: NSCalendar.Identifier.gregorian)! | |
+ private static let DefaultDayTimes:[TimeNames: Double] = [ | |
+ TimeNames.Fajr : 5.0, | |
+ TimeNames.Sunrise : 6.0, | |
+ TimeNames.Dhuhr : 12.0, | |
+ TimeNames.Asr : 13.0, | |
+ TimeNames.Sunset : 18.0, | |
+ TimeNames.Maghrib : 18.0, | |
+ TimeNames.Isha : 18.0 | |
+ ] | |
+ | |
+ //------------------------------------------------------ | |
+ // MARK: - Technical Settings | |
+ //------------------------------------------------------ | |
+ | |
+ /// number of iterations needed to compute times | |
+ var numIterations:Int = 1 | |
+ | |
+ /** | |
+ Required parameters for calculation methods. | |
+ None but the `.Custom` parameters should be changed where appropriate. | |
+ Mostly, you should not be touching is directly. | |
+ | |
+ **Note:** | |
+ Parameters are five-element arrays with following values: | |
+ | |
+ methodParams[method] = @[fa, ms, mv, is, iv]; | |
+ ------------------------------------------------------ | |
+ fa: fajr angle | |
+ ms: maghrib selector (0 = angle; 1 = minutes after sunset) | |
+ mv: maghrib parameter value (in angle or minutes) | |
+ is: isha selector (0 = angle; 1 = minutes after maghrib) | |
+ iv: isha parameter value (in angle or minutes) | |
+ */ | |
+ var methodParams:[CalculationMethod: [Float]] = [ | |
+ .MWL : [18 , 1, 0 , 0, 17 ], | |
+ .ISNA : [15 , 1, 0 , 0, 15 ], | |
+ .Egypt : [19.5, 1, 0 , 0, 17.5], | |
+ .Makkah : [18.5, 1, 0 , 1, 90 ], | |
+ .Karachi : [18 , 1, 0 , 0, 18 ], | |
+ .Tehran : [17.7, 0, 4.5, 0, 14 ], | |
+ .Jafari : [16 , 0, 4 , 0, 14 ], | |
+ .Custom : [18 , 1, 0 , 0, 17 ] | |
+ ]; | |
+ | |
+ //------------------------------------------------------ | |
+ // MARK: - Properties | |
+ //------------------------------------------------------ | |
+ | |
+ var offsets:[TimeNames: Double] = [ | |
+ .Fajr : 0, | |
+ .Sunrise : 0, | |
+ .Dhuhr : 0, | |
+ .Asr : 0, | |
+ .Sunset : 0, | |
+ .Maghrib : 0, | |
+ .Isha : 0 | |
+ ]; | |
+ | |
+ | |
+ /// Once 'computePrayerTimes' is called, | |
+ /// computed values are stored here for reuse | |
+ var currentPrayerTimes:[TimeNames: Double]? | |
+ | |
+ /// Prayer calculation methods. | |
+ /// See `CalculationMethod` enums for more details | |
+ var calculationMethod = CalculationMethod.MWL | |
+ /// Asr method, `Shafii` or `Hanafii` | |
+ var asrJuristic = JuristicMethod.Shafii | |
+ /// Adjustment options for Higher Latitude | |
+ var highLatitudeAdjustment = HigherLatutudeAdjustment.MidNight | |
+ /// Prayer time output format. | |
+ var outputFormat = OutputTimeFormat.Time24 | |
+ | |
+ // Not sure if it should be replaced by offsets[.Dhuhr] | |
+ var dhuhrMinutes:Float = 0 | |
+ | |
+ /// Coordinate of the place, times will be calculated for. | |
+ var coordinate:Coordinate! { | |
+ didSet { | |
+ calculateJulianDate() | |
+ } | |
+ } | |
+ | |
+ /// Timezone of the place, times will be calculated for. | |
+ var timeZone:Float = SRPrayerTime.systemTimeZone() | |
+ | |
+ /// Date for which prayer times will be calculated. | |
+ /// Defaults to today, when not set. | |
+ var calcDate:NSDate! { | |
+ didSet { | |
+ calculateJulianDate() | |
+ } | |
+ } | |
+ | |
+ private lazy var jDate:Double = SRPrayerTime.julianDateFromDate(date: NSDate()) | |
+ | |
+ //------------------------------------------------------ | |
+ // MARK: - Constructor | |
+ //------------------------------------------------------ | |
+ | |
+ init(lat:Double, lng:Double){ | |
+ coordinate = Coordinate(lat: lat, lng: lng) | |
+ calcDate = NSDate() | |
+ } | |
+ | |
+ //------------------------------------------------------ | |
+ // MARK: - Utility Methods (Type Methods) | |
+ //------------------------------------------------------ | |
+ | |
+ class func systemTimeZone()->Float { | |
+ let timeZone = NSTimeZone.local | |
+ return Float(timeZone.secondsFromGMT())/3600.0 | |
+ } | |
+ | |
+ class func dayLightSavingOffset()->Double { | |
+ let timeZone = NSTimeZone.local | |
+ return Double(timeZone.daylightSavingTimeOffset(for: NSDate() as Date)) | |
+ } | |
+ | |
+ //------------------------------------------------------ | |
+ // MARK: - Public Methods: Get prayer times | |
+ //------------------------------------------------------ | |
+ | |
+ /// Return prayer times for a given date, latitude, longitude and timeZone | |
+ func getDatePrayerTimes(year:Int, month:Int, day:Int, latitude:Double, longitude:Double, tZone:Float)->[TimeNames: AnyObject] { | |
+ coordinate = Coordinate(lat: latitude, lng: longitude) | |
+ | |
+ let comp = NSDateComponents() | |
+ comp.year = year | |
+ comp.month = month | |
+ comp.day = day | |
+ calcDate = SRPrayerTime.GregorianCalendar.date(from: comp as DateComponents) as NSDate! | |
+ | |
+ timeZone = tZone | |
+ | |
+ jDate = SRPrayerTime.julianDate(year: year, month: month, day: day) | |
+ | |
+ let lonDiff = longitude / (15.0 * 24.0) | |
+ jDate = jDate - lonDiff; | |
+ return computeDayTimes() | |
+ } | |
+ | |
+ /// Returns prayer times for a date(or today) when everything is set | |
+ func getPrayerTimes()->[TimeNames: AnyObject]? { | |
+ // If coordinate is not set, cannot obtain prayer times | |
+ if coordinate == nil { | |
+ return nil | |
+ } | |
+ | |
+ // If date is not set, set today as calcDate | |
+ if calcDate == nil { | |
+ calcDate = NSDate() | |
+ } | |
+ | |
+ // jDate should be autometically set already | |
+ return computeDayTimes() | |
+ } | |
+ | |
+ //------------------------------------------------------ | |
+ // MARK: - Public Methods: Configurations | |
+ //------------------------------------------------------ | |
+ | |
+ /// Set custom values for calculation parameters | |
+ func setCustomParams(params:[Float]) { | |
+ var cust = methodParams[CalculationMethod.Custom]! | |
+ var curr = methodParams[calculationMethod]! | |
+ for i in 0 ..< 5 { | |
+ let j:Float = params[i]; | |
+ if j == -1 { | |
+ cust[i] = curr[i] | |
+ } else { | |
+ cust[i] = j | |
+ } | |
+ } | |
+ methodParams[CalculationMethod.Custom] = cust | |
+ calculationMethod = CalculationMethod.Custom | |
+ } | |
+ | |
+ /// Set the angle for calculating Fajr | |
+ func setFajrAngle(angle:Float) { | |
+ setCustomParams(params: [angle, -1.0, -1.0, -1.0, -1.0]) | |
+ } | |
+ | |
+ /// Set the angle for calculating Maghrib | |
+ func setMaghribAngle(angle:Float) { | |
+ setCustomParams(params: [-1.0, 0.0, angle, -1.0, -1.0]) | |
+ } | |
+ | |
+ /// Set the angle for calculating Isha | |
+ func setIshaAngle(angle:Float) { | |
+ setCustomParams(params: [-1.0, -1.0, -1.0, 0.0, angle]) | |
+ } | |
+ | |
+ /// Set the minutes after Sunset for calculating Maghrib | |
+ func setMaghribMinutes(minutes:Float) { | |
+ setCustomParams(params: [-1.0, 1.0, minutes, -1.0, -1.0]) | |
+ } | |
+ | |
+ /// Set the minutes after Maghrib for calculating Isha | |
+ func setIshaMinutes(minutes:Float) { | |
+ setCustomParams(params: [-1.0, -1.0, -1.0, 1.0, minutes]) | |
+ } | |
+ | |
+ //------------------------------------------------------ | |
+ // MARK: - Public Methods: Format Conversion | |
+ //------------------------------------------------------ | |
+ | |
+ /// Convert float hours to (hours, minutes) | |
+ func floatToHourMinute(time:Double)->(hours:Int, minutes:Int)? { | |
+ if time.isNaN { | |
+ return nil | |
+ } | |
+ | |
+ let ttime = fixHour(a: time + 0.5 / 60.0) // add 0.5 minutes to round | |
+ let hours = Int(floor(time)) | |
+ let minutes = Int(floor((ttime - Double(hours)) * 60.0)) | |
+ | |
+ return (hours: hours, minutes: minutes) | |
+ } | |
+ | |
+ /// Convert float hours to 24h format | |
+ func floatToTime24(time:Double)->String { | |
+ if let (hours, minutes) = floatToHourMinute(time: time) { | |
+ return NSString(format: "%02d:%02d", hours, minutes) as String | |
+ } else { | |
+ return "---" | |
+ } | |
+ } | |
+ | |
+ /// Convert float hours to 12h format | |
+ func floatToTime12(time:Double, noSuffix:Bool)->String { | |
+ if let (hours, minutes) = floatToHourMinute(time: time) { | |
+ return NSString(format: "%02d:%02d%@", (hours % 12), minutes, (noSuffix ? "" : ((hours > 12) ? " pm" : " am")) ) as String | |
+ } else { | |
+ return "---" | |
+ } | |
+ } | |
+ | |
+ /// Convert float hours to 12h format with no suffix | |
+ func floatToTime12NS(time:Double)->String { | |
+ return floatToTime12(time: time, noSuffix: true) | |
+ } | |
+ | |
+ /// Convert float hours to NSDate | |
+ func floatToNSDate(time:Double)->NSDate? { | |
+ if let (hours, minutes) = floatToHourMinute(time: time) { | |
+ var components = SRPrayerTime.GregorianCalendar.components([NSCalendar.Unit.year, NSCalendar.Unit.month, NSCalendar.Unit.day], from: calcDate as Date) | |
+ | |
+ components.hour = hours | |
+ components.minute = minutes | |
+ return SRPrayerTime.GregorianCalendar.date(from: components) as NSDate? | |
+ } else { | |
+ return nil | |
+ } | |
+ } | |
+ | |
+ //------------------------------------------------------ | |
+ // MARK: - Julian Date Calculation | |
+ //------------------------------------------------------ | |
+ | |
+ private func calculateJulianDate() { | |
+ if let date = calcDate { | |
+ if let latlng = coordinate { | |
+ jDate = SRPrayerTime.julianDateFromDate(date: date) | |
+ jDate = jDate - (latlng.longitude / (15.0 * 24.0)) | |
+ } | |
+ } | |
+ } | |
+ | |
+ private class func julianDateFromDate(date:NSDate)->Double { | |
+ let components = GregorianCalendar.components([NSCalendar.Unit.year, NSCalendar.Unit.month, NSCalendar.Unit.day], from: Date()) | |
+ return julianDate(year: components.year!, month: components.month!, day: components.day!) | |
+ } | |
+ | |
+ private class func julianDate(year:Int, month:Int, day:Int)->Double { | |
+ var yyear = year, mmonth = month, dday = day | |
+ if mmonth < 2 { | |
+ yyear -= 1 | |
+ mmonth += 12 | |
+ } | |
+ | |
+ let A = floor(Double(yyear)/100.0) | |
+ let B = 2.0 - A + floor(A/4.0) | |
+ | |
+ return floor(365.25 * (Double(yyear) + 4716.0)) | |
+ + floor(30.6001 * (Double(mmonth) + 1.0)) | |
+ + Double(dday) + B - 1524.5 | |
+ } | |
+ | |
+ //------------------------------------------------------ | |
+ // MARK: - Calculation Functions | |
+ //------------------------------------------------------ | |
+ | |
+ // References: | |
+ // http://praytimes.org/calculation/ | |
+ | |
+ // compute declination angle of sun and equation of time | |
+ private func sunPosition(jd:Double)->(Double, Double) { | |
+ let D = jd - 2451545.0; | |
+ let g = DMath.fixAngle(a: 357.529 + 0.98560028 * D) | |
+ let q = DMath.fixAngle(a: 280.459 + 0.98564736 * D) | |
+ let L = DMath.fixAngle(a: q + (1.915 * DMath.dSin(d: g)) + (0.020 * DMath.dSin(d: 2 * g))) | |
+ | |
+ //double R = 1.00014 - 0.01671 * [self dcos:g] - 0.00014 * [self dcos: (2*g)]; | |
+ let e = 23.439 - (0.00000036 * D) | |
+ var RA = DMath.dArcTan2(y: DMath.dCos(d: e) * DMath.dSin(d: L), x: DMath.dCos(d: L)) / 15.0 | |
+ RA = fixHour(a: RA); | |
+ | |
+ let d = DMath.dArcSin(x: DMath.dSin(d: e) * DMath.dSin(d: L)) | |
+ let EqT = q / 15.0 - RA; | |
+ | |
+ return (d, EqT); | |
+ } | |
+ | |
+ // compute equation of time | |
+ private func equationOfTime(jd:Double)->Double { | |
+ let (_, EqT) = sunPosition(jd: jd) | |
+ return EqT | |
+ } | |
+ | |
+ // compute declination angle of sun | |
+ private func sunDeclination(jd:Double)->Double { | |
+ let (d, _) = sunPosition(jd: jd) | |
+ return d | |
+ } | |
+ | |
+ // compute mid-day (Dhuhr, Zawal) time | |
+ private func computeMidDay(t:Double)->Double { | |
+ let T = equationOfTime(jd: jDate + t) | |
+ return fixHour(a: 12 - T) | |
+ } | |
+ | |
+ // compute time for a given angle G | |
+ private func computeTime(G:Double, t:Double)->Double { | |
+ let D:Double = sunDeclination(jd: jDate + t) | |
+ let Z:Double = computeMidDay(t: t) | |
+ let V:Double = DMath.dArcCos(x: (-DMath.dSin(d: G) - (DMath.dSin(d: D) * DMath.dSin(d: coordinate!.latitude))) / (DMath.dCos(d: D) * DMath.dCos(d: coordinate!.latitude))) / 15.0 | |
+ | |
+ if G > 90 { | |
+ return Z - V | |
+ } else { | |
+ return Z + V | |
+ } | |
+ } | |
+ | |
+ // compute the time of Asr | |
+ // Shafii: step=1, Hanafi: step=2 | |
+ private func computeAsr(step:Double, t:Double)->Double { | |
+ let d = sunDeclination(jd: jDate + t) | |
+ let g = -DMath.dArcCot(x: step + DMath.dTan(d: abs(coordinate!.latitude - d))) | |
+ return computeTime(G: g, t: t) | |
+ } | |
+ | |
+ //------------------------------------------------------ | |
+ // MARK: - Misc Functions | |
+ //------------------------------------------------------ | |
+ | |
+ // compute the difference between two times | |
+ private func timeDiff(time1:Double, time2:Double)->Double { | |
+ return fixHour(a: time2 - time1) | |
+ } | |
+ | |
+ //------------------------------------------------------ | |
+ // MARK: - Compute Prayer Times | |
+ //------------------------------------------------------ | |
+ | |
+ // compute prayer times at given julian date | |
+ private func computeTimes(times:[TimeNames: Double])->[TimeNames: Double] { | |
+ var t = dayPortion(times: times) | |
+ var params = methodParams[calculationMethod]! | |
+ | |
+ let idk = params[0] | |
+ let fajr:Double = computeTime(G: (180.0 - Double(idk)), t: t[.Fajr]!) | |
+ let sunrise:Double = computeTime(G: (180.0 - 0.833), t: t[.Sunrise]!) | |
+ let dhuhr:Double = computeMidDay(t: t[.Dhuhr]!) | |
+ let asr:Double = computeAsr(step: Double(1 + asrJuristic.toInt()), t: t[.Asr]!) | |
+ let sunset:Double = computeTime(G: 0.833, t: t[.Sunset]!) | |
+ let maghrib:Double = computeTime(G: Double(params[2]), t: t[.Maghrib]!) | |
+ let isha:Double = computeTime(G: Double(params[4]), t: t[.Isha]!) | |
+ | |
+ let cTimes = [ | |
+ TimeNames.Fajr : fajr, | |
+ TimeNames.Sunrise : sunrise, | |
+ TimeNames.Dhuhr : dhuhr, | |
+ TimeNames.Asr : asr, | |
+ TimeNames.Sunset : sunset, | |
+ TimeNames.Maghrib : maghrib, | |
+ TimeNames.Isha : isha | |
+ ] | |
+ | |
+ //Tune times here | |
+ //Ctimes = [self tuneTimes:Ctimes]; | |
+ | |
+ return cTimes; | |
+ } | |
+ | |
+ // compute prayer times at given julian date | |
+ private func computeDayTimes()->[TimeNames: AnyObject] { | |
+ //default times | |
+ let times = SRPrayerTime.DefaultDayTimes; | |
+ | |
+ // Compute minimum once | |
+ var t1 = computeTimes(times: times) | |
+ | |
+ // If need more iterations... | |
+ if numIterations > 1 { | |
+ for _ in 2...numIterations { | |
+ t1 = computeTimes(times: times) | |
+ } | |
+ } | |
+ | |
+ var t2 = adjustTimes(times: t1) | |
+ t2 = tuneTimes(times: t2) | |
+ | |
+ //Set prayerTimesCurrent here!! | |
+ currentPrayerTimes = t2 | |
+ | |
+ let t3 = adjustTimesFormat(times: t2) | |
+ | |
+ return t3 | |
+ } | |
+ | |
+ // Tune timings for adjustments | |
+ // Set time offsets | |
+ private func tune(offsetTimes:[TimeNames: Double]) { | |
+ offsets = offsetTimes; | |
+ } | |
+ | |
+ private func tuneTimes(times:[TimeNames: Double])->[TimeNames: Double] { | |
+ var ttimes = times | |
+ for (pName, time) in times { | |
+ //if(i==5) | |
+ //NSLog(@"Normal: %d - %@", i, [times objectAtIndex:i]); | |
+ let off = offsets[pName]! / 60.0 | |
+ let oTime = time + off | |
+ ttimes[pName] = oTime | |
+ //if(i==5) | |
+ //NSLog(@"Modified: %d - %@", i, [times objectAtIndex:i]); | |
+ } | |
+ | |
+ return ttimes; | |
+ } | |
+ | |
+ // range reduce hours to 0..23 | |
+ private func fixHour(a:Double)->Double { | |
+ return DMath.wrap(a: a, min: 0, max: 24) | |
+ } | |
+ | |
+ // adjust times in a prayer time array | |
+ private func adjustTimes(times:[TimeNames: Double])->[TimeNames: Double] { | |
+ var ttimes = times | |
+ var dTime1:Double | |
+ var dTime2:Double | |
+ | |
+ for (timeName, time) in ttimes { | |
+ ttimes[timeName] = time + (Double(timeZone) - coordinate!.longitude / 15.0); | |
+ } | |
+ | |
+ ttimes[TimeNames.Dhuhr] = ttimes[TimeNames.Dhuhr]! + (Double(dhuhrMinutes) / 60.0); //Dhuhr | |
+ | |
+ var params = methodParams[calculationMethod]! | |
+ let val = params[1] | |
+ | |
+ if (val == 1.0) { // Maghrib | |
+ dTime1 = ttimes[TimeNames.Sunset]! + Double(params[2] / 60.0) | |
+ ttimes[TimeNames.Maghrib] = dTime1 | |
+ } | |
+ | |
+ if params[3] == 1 { // Isha | |
+ dTime2 = ttimes[TimeNames.Maghrib]! + Double(params[4] / 60.0) | |
+ ttimes[TimeNames.Isha] = dTime2 | |
+ } | |
+ | |
+ if (highLatitudeAdjustment != HigherLatutudeAdjustment.None){ | |
+ ttimes = adjustHighLatTimes(times: ttimes) | |
+ } | |
+ return ttimes; | |
+ } | |
+ | |
+ // convert times array to given time format | |
+ private func adjustTimesFormat(times:[TimeNames: Double])->[TimeNames: AnyObject] { | |
+ var ttimes:[TimeNames: AnyObject] = [TimeNames: AnyObject]() | |
+ | |
+ for (timeName, time) in times { | |
+ if (outputFormat == OutputTimeFormat.Float) { | |
+ ttimes[timeName] = time as AnyObject | |
+ } else if (outputFormat == OutputTimeFormat.Time12) { | |
+ ttimes[timeName] = floatToTime12(time: time, noSuffix: false) as AnyObject? | |
+ } else if (outputFormat == OutputTimeFormat.Time12NoSuffix) { | |
+ ttimes[timeName] = floatToTime12(time: time, noSuffix:true) as AnyObject? | |
+ } else if (outputFormat == OutputTimeFormat.Time24){ | |
+ ttimes[timeName] = floatToTime24(time: time) as AnyObject? | |
+ } else { | |
+ // floatToNSDate can return nil, if time is invalid | |
+ ttimes[timeName] = floatToNSDate(time: time) | |
+ } | |
+ } | |
+ return ttimes; | |
+ } | |
+ | |
+ // adjust Fajr, Isha and Maghrib for locations in higher latitudes | |
+ private func adjustHighLatTimes(times:[TimeNames: Double])->[TimeNames: Double] { | |
+ var ttimes = times | |
+ let params = methodParams[calculationMethod]! | |
+ | |
+ let nightTime = timeDiff(time1: ttimes[TimeNames.Sunset]!, time2:ttimes[TimeNames.Sunrise]!) // sunset to sunrise | |
+ | |
+ // Adjust Fajr | |
+ let fajrDiff = nightPortion(angle: Double(params[0])) * nightTime; | |
+ if (ttimes[TimeNames.Fajr]!.isNaN || timeDiff(time1: ttimes[TimeNames.Fajr]!, time2: ttimes[TimeNames.Sunrise]!) > fajrDiff) { | |
+ ttimes[TimeNames.Fajr] = ttimes[TimeNames.Sunrise]! - fajrDiff | |
+ } | |
+ | |
+ // Adjust Isha | |
+ let ishaAngle:Double = (params[3] == 0.0) ? Double(params[4]) : 18.0 | |
+ let ishaDiff:Double = nightPortion(angle: ishaAngle) * nightTime | |
+ if (ttimes[TimeNames.Isha]!.isNaN || timeDiff(time1: ttimes[TimeNames.Sunset]!, time2: ttimes[TimeNames.Isha]!) > ishaDiff) { | |
+ ttimes[TimeNames.Isha] = ttimes[TimeNames.Sunset]! + ishaDiff | |
+ } | |
+ | |
+ // Adjust Maghrib | |
+ let maghribAngle:Double = (params[1] == 0.0) ? Double(params[2]) : 4.0 | |
+ let maghribDiff:Double = nightPortion(angle: maghribAngle) * nightTime | |
+ if (ttimes[TimeNames.Maghrib]!.isNaN || timeDiff(time1: ttimes[TimeNames.Sunset]!, time2: ttimes[TimeNames.Maghrib]!) > maghribDiff) { | |
+ ttimes[TimeNames.Maghrib] = ttimes[TimeNames.Sunset]! + maghribDiff | |
+ } | |
+ | |
+ return ttimes; | |
+ } | |
+ | |
+ // the night portion used for adjusting times in higher latitudes | |
+ private func nightPortion(angle:Double)->Double { | |
+ var calc:Double | |
+ | |
+ switch highLatitudeAdjustment { | |
+ case .None : calc = 0.0 | |
+ case .AngleBased : calc = angle / 60.0 | |
+ case .MidNight : calc = 0.5 | |
+ case .OneSeventh : calc = 0.14286 | |
+ } | |
+ | |
+ return calc; | |
+ } | |
+ | |
+ // convert hours to day portions | |
+ private func dayPortion(times:[TimeNames: Double])->[TimeNames: Double] { | |
+ var ttimes = [TimeNames: Double]() | |
+ for (pName, time) in times { | |
+ let timeH = time / 24.0 | |
+ ttimes[pName] = timeH | |
+ } | |
+ return ttimes | |
+ } | |
+ | |
+} | |
+ | |
+// ------------------------------------------------------ | |
+// MARK: - Trigonometric Functions | |
+// ------------------------------------------------------ | |
+class DMath { | |
+ class func wrap(a:Double, min:Double, max:Double)->Double { | |
+ var aa = a | |
+ let range = max - min | |
+ aa = aa.truncatingRemainder(dividingBy: range) | |
+ if aa < min { aa += range } | |
+ if aa > max { aa -= range } | |
+ return aa | |
+ } | |
+ | |
+ // range reduce angle in degrees. | |
+ class func fixAngle(a:Double)->Double { | |
+ return wrap(a: a, min: 0, max: 360) | |
+ } | |
+ | |
+ // radian to degree | |
+ class func radiansToDegrees(alpha:Double) ->Double{ | |
+ return ((alpha*180.0)/M_PI); | |
+ } | |
+ | |
+ // deree to radian | |
+ class func degreesToRadians(alpha:Double)->Double { | |
+ return ((alpha*M_PI)/180.0); | |
+ } | |
+ | |
+ // degree sin | |
+ class func dSin(d:Double)->Double { | |
+ return sin(degreesToRadians(alpha: d)) | |
+ } | |
+ | |
+ // degree cos | |
+ class func dCos(d:Double)->Double { | |
+ return cos(degreesToRadians(alpha: d)) | |
+ } | |
+ | |
+ // degree tan | |
+ class func dTan(d:Double)->Double { | |
+ return tan(degreesToRadians(alpha: d)) | |
+ } | |
+ | |
+ // degree arcsin | |
+ class func dArcSin(x:Double)->Double { | |
+ let val = asin(x) | |
+ return radiansToDegrees(alpha: val) | |
+ } | |
+ | |
+ // degree arccos | |
+ class func dArcCos(x:Double)->Double { | |
+ let val = acos(x); | |
+ return radiansToDegrees(alpha: val) | |
+ } | |
+ | |
+ // degree arctan | |
+ class func dArcTan(x:Double)->Double { | |
+ let val = atan(x); | |
+ return radiansToDegrees(alpha: val) | |
+ } | |
+ | |
+ // degree arctan2 | |
+ class func dArcTan2(y:Double, x:Double)->Double { | |
+ let val = atan2(y, x); | |
+ return radiansToDegrees(alpha: val) | |
+ } | |
+ | |
+ // degree arccot | |
+ class func dArcCot(x:Double)->Double { | |
+ let val = atan2(1.0, x); | |
+ return radiansToDegrees(alpha: val) | |
+ } | |
+} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment