Merge lp:~vthompson/ubuntu-weather-app/reboot-sunrise-sunset into lp:ubuntu-weather-app

Proposed by Victor Thompson
Status: Merged
Approved by: Andrew Hayzen
Approved revision: 64
Merged at revision: 65
Proposed branch: lp:~vthompson/ubuntu-weather-app/reboot-sunrise-sunset
Merge into: lp:ubuntu-weather-app
Diff against target: 469 lines (+348/-8)
7 files modified
app/components/DayDelegate.qml (+2/-2)
app/components/ForecastDetailsDelegate.qml (+1/-0)
app/data/WeatherApi.js (+6/-0)
app/data/suncalc.js (+300/-0)
app/ui/HomePage.qml (+9/-4)
app/ui/LocationPane.qml (+5/-2)
debian/copyright (+25/-0)
To merge this branch: bzr merge lp:~vthompson/ubuntu-weather-app/reboot-sunrise-sunset
Reviewer Review Type Date Requested Status
Andrew Hayzen Approve
Ubuntu Phone Apps Jenkins Bot continuous-integration Approve
Review via email: mp+263171@code.launchpad.net

Commit message

Use sunrise/sunset from API if available, otherwise use calculated times

Description of the change

Use sunrise/sunset from API if available, otherwise use calculated times

To post a comment you must log in.
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
58. By Victor Thompson

get each date in forecast

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
59. By Victor Thompson

Fix spelling mistake: mooon

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Andrew Hayzen (ahayzen) wrote :

Some inline code comments/questions to start off with :-)

review: Needs Information
60. By Victor Thompson

Update date getter and formatting functions

Revision history for this message
Victor Thompson (vthompson) wrote :

Added responses and updated mp.

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Nekhelesh Ramananthan (nik90) wrote :

> return Qt.formatDate(getDate(dateData), i18n.tr(format))

We should *never* be exposing the format as a translatable string and should instead be using Qt.locale to display the time/date as per the locale format. Considering that this is something that is also present in trunk, it doesn't block this MP.

61. By Victor Thompson

Update copyright file

62. By Victor Thompson

Add dots

63. By Victor Thompson

One more try

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
64. By Victor Thompson

Change to BSD 2-clause

Revision history for this message
Andrew Hayzen (ahayzen) wrote :

Thanks for the extra changes, LGTM :-)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'app/components/DayDelegate.qml'
2--- app/components/DayDelegate.qml 2015-06-21 14:02:58 +0000
3+++ app/components/DayDelegate.qml 2015-07-25 21:36:51 +0000
4@@ -224,13 +224,13 @@
5 ForecastDetailsDelegate {
6 id: sunriseForecast
7 forecast: i18n.tr("Sunrise")
8- // FIXME: need icon
9+ imageName: "info"
10 }
11
12 ForecastDetailsDelegate {
13 id: sunsetForecast
14 forecast: i18n.tr("Sunset")
15- // FIXME: need icon
16+ imageName: "info"
17 }
18 }
19 }
20
21=== modified file 'app/components/ForecastDetailsDelegate.qml'
22--- app/components/ForecastDetailsDelegate.qml 2015-06-21 15:02:05 +0000
23+++ app/components/ForecastDetailsDelegate.qml 2015-07-25 21:36:51 +0000
24@@ -24,6 +24,7 @@
25 spacing: units.gu(2)
26 visible: value !== ""
27
28+ property alias imageName: icon.name
29 property alias imageSource: icon.source
30 property alias forecast: forecastLabel.text
31 property alias value: forecastValue.text
32
33=== modified file 'app/data/WeatherApi.js'
34--- app/data/WeatherApi.js 2015-04-27 00:57:35 +0000
35+++ app/data/WeatherApi.js 2015-07-25 21:36:51 +0000
36@@ -512,6 +512,7 @@
37 "daily": combinedData[0]["DailyForecasts"],
38 "forecast": combinedData[0]["HourlyForecasts"],
39 "current": combinedData[0]["StandardObservation"],
40+ "sunRiseSet": combinedData[0]["SunRiseSet"],
41 };
42 print("["+location.name+"] "+JSON.stringify(localNow));
43 // add openweathermap id for faster responses
44@@ -522,6 +523,7 @@
45 for(var x=0;x<5;x++) {
46 var dayData = data["daily"][x],
47 date = getLocationTime(((dayData.validDate*1000)-1000)+offset); // minus 1 sec to handle +/-12 TZ
48+ var sunRiseSet = data["sunRiseSet"][x];
49 day = date.year+"-"+date.month+"-"+date.date;
50 if(!todayDate) {
51 if(localNow.year+"-"+localNow.month+"-"+localNow.date > day) {
52@@ -531,6 +533,10 @@
53 todayDate = date;
54 }
55 tmpResult[day] = _buildDayFormat(date, dayData, nowMs);
56+ var sunrise = new Date(sunRiseSet.rise*1000);
57+ var sunset = new Date(sunRiseSet.set*1000);
58+ tmpResult[day].sunrise = sunrise.toLocaleTimeString();
59+ tmpResult[day].sunset = sunset.toLocaleTimeString();
60 }
61 //
62 if(data["forecast"] !== undefined) {
63
64=== added file 'app/data/suncalc.js'
65--- app/data/suncalc.js 1970-01-01 00:00:00 +0000
66+++ app/data/suncalc.js 2015-07-25 21:36:51 +0000
67@@ -0,0 +1,300 @@
68+/*
69+ (c) 2011-2015, Vladimir Agafonkin
70+ SunCalc is a JavaScript library for calculating sun/moon position and light phases.
71+ https://github.com/mourner/suncalc
72+*/
73+
74+// shortcuts for easier to read formulas
75+
76+var PI = Math.PI,
77+ sin = Math.sin,
78+ cos = Math.cos,
79+ tan = Math.tan,
80+ asin = Math.asin,
81+ atan = Math.atan2,
82+ acos = Math.acos,
83+ rad = PI / 180;
84+
85+// sun calculations are based on http://aa.quae.nl/en/reken/zonpositie.html formulas
86+
87+
88+// date/time constants and conversions
89+
90+var dayMs = 1000 * 60 * 60 * 24,
91+ J1970 = 2440588,
92+ J2000 = 2451545;
93+
94+function toJulian(date) { return date.valueOf() / dayMs - 0.5 + J1970; }
95+function fromJulian(j) { return new Date((j + 0.5 - J1970) * dayMs); }
96+function toDays(date) { return toJulian(date) - J2000; }
97+
98+
99+// general calculations for position
100+
101+var e = rad * 23.4397; // obliquity of the Earth
102+
103+function rightAscension(l, b) { return atan(sin(l) * cos(e) - tan(b) * sin(e), cos(l)); }
104+function declination(l, b) { return asin(sin(b) * cos(e) + cos(b) * sin(e) * sin(l)); }
105+
106+function azimuth(H, phi, dec) { return atan(sin(H), cos(H) * sin(phi) - tan(dec) * cos(phi)); }
107+function altitude(H, phi, dec) { return asin(sin(phi) * sin(dec) + cos(phi) * cos(dec) * cos(H)); }
108+
109+function siderealTime(d, lw) { return rad * (280.16 + 360.9856235 * d) - lw; }
110+
111+
112+// general sun calculations
113+
114+function solarMeanAnomaly(d) { return rad * (357.5291 + 0.98560028 * d); }
115+
116+function eclipticLongitude(M) {
117+
118+ var C = rad * (1.9148 * sin(M) + 0.02 * sin(2 * M) + 0.0003 * sin(3 * M)), // equation of center
119+ P = rad * 102.9372; // perihelion of the Earth
120+
121+ return M + C + P + PI;
122+}
123+
124+function sunCoords(d) {
125+
126+ var M = solarMeanAnomaly(d),
127+ L = eclipticLongitude(M);
128+
129+ return {
130+ dec: declination(L, 0),
131+ ra: rightAscension(L, 0)
132+ };
133+}
134+
135+
136+var SunCalc = {};
137+
138+
139+// calculates sun position for a given date and latitude/longitude
140+
141+SunCalc.getPosition = function (date, lat, lng) {
142+
143+ var lw = rad * -lng,
144+ phi = rad * lat,
145+ d = toDays(date),
146+
147+ c = sunCoords(d),
148+ H = siderealTime(d, lw) - c.ra;
149+
150+ return {
151+ azimuth: azimuth(H, phi, c.dec),
152+ altitude: altitude(H, phi, c.dec)
153+ };
154+};
155+
156+
157+// sun times configuration (angle, morning name, evening name)
158+
159+var times = SunCalc.times = [
160+ [-0.833, 'sunrise', 'sunset' ],
161+ [ -0.3, 'sunriseEnd', 'sunsetStart' ],
162+ [ -6, 'dawn', 'dusk' ],
163+ [ -12, 'nauticalDawn', 'nauticalDusk'],
164+ [ -18, 'nightEnd', 'night' ],
165+ [ 6, 'goldenHourEnd', 'goldenHour' ]
166+];
167+
168+// adds a custom time to the times config
169+
170+SunCalc.addTime = function (angle, riseName, setName) {
171+ times.push([angle, riseName, setName]);
172+};
173+
174+
175+// calculations for sun times
176+
177+var J0 = 0.0009;
178+
179+function julianCycle(d, lw) { return Math.round(d - J0 - lw / (2 * PI)); }
180+
181+function approxTransit(Ht, lw, n) { return J0 + (Ht + lw) / (2 * PI) + n; }
182+function solarTransitJ(ds, M, L) { return J2000 + ds + 0.0053 * sin(M) - 0.0069 * sin(2 * L); }
183+
184+function hourAngle(h, phi, d) { return acos((sin(h) - sin(phi) * sin(d)) / (cos(phi) * cos(d))); }
185+
186+// returns set time for the given sun altitude
187+function getSetJ(h, lw, phi, dec, n, M, L) {
188+
189+ var w = hourAngle(h, phi, dec),
190+ a = approxTransit(w, lw, n);
191+ return solarTransitJ(a, M, L);
192+}
193+
194+
195+// calculates sun times for a given date and latitude/longitude
196+
197+SunCalc.getTimes = function (date, lat, lng) {
198+
199+ var lw = rad * -lng,
200+ phi = rad * lat,
201+
202+ d = toDays(date),
203+ n = julianCycle(d, lw),
204+ ds = approxTransit(0, lw, n),
205+
206+ M = solarMeanAnomaly(ds),
207+ L = eclipticLongitude(M),
208+ dec = declination(L, 0),
209+
210+ Jnoon = solarTransitJ(ds, M, L),
211+
212+ i, len, time, Jset, Jrise;
213+
214+
215+ var result = {
216+ solarNoon: fromJulian(Jnoon),
217+ nadir: fromJulian(Jnoon - 0.5)
218+ };
219+
220+ for (i = 0, len = times.length; i < len; i += 1) {
221+ time = times[i];
222+
223+ Jset = getSetJ(time[0] * rad, lw, phi, dec, n, M, L);
224+ Jrise = Jnoon - (Jset - Jnoon);
225+
226+ result[time[1]] = fromJulian(Jrise);
227+ result[time[2]] = fromJulian(Jset);
228+ }
229+
230+ return result;
231+};
232+
233+
234+// moon calculations, based on http://aa.quae.nl/en/reken/hemelpositie.html formulas
235+
236+function moonCoords(d) { // geocentric ecliptic coordinates of the moon
237+
238+ var L = rad * (218.316 + 13.176396 * d), // ecliptic longitude
239+ M = rad * (134.963 + 13.064993 * d), // mean anomaly
240+ F = rad * (93.272 + 13.229350 * d), // mean distance
241+
242+ l = L + rad * 6.289 * sin(M), // longitude
243+ b = rad * 5.128 * sin(F), // latitude
244+ dt = 385001 - 20905 * cos(M); // distance to the moon in km
245+
246+ return {
247+ ra: rightAscension(l, b),
248+ dec: declination(l, b),
249+ dist: dt
250+ };
251+}
252+
253+SunCalc.getMoonPosition = function (date, lat, lng) {
254+
255+ var lw = rad * -lng,
256+ phi = rad * lat,
257+ d = toDays(date),
258+
259+ c = moonCoords(d),
260+ H = siderealTime(d, lw) - c.ra,
261+ h = altitude(H, phi, c.dec);
262+
263+ // altitude correction for refraction
264+ h = h + rad * 0.017 / tan(h + rad * 10.26 / (h + rad * 5.10));
265+
266+ return {
267+ azimuth: azimuth(H, phi, c.dec),
268+ altitude: h,
269+ distance: c.dist
270+ };
271+};
272+
273+
274+// calculations for illumination parameters of the moon,
275+// based on http://idlastro.gsfc.nasa.gov/ftp/pro/astro/mphase.pro formulas and
276+// Chapter 48 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998.
277+
278+SunCalc.getMoonIllumination = function (date) {
279+
280+ var d = toDays(date),
281+ s = sunCoords(d),
282+ m = moonCoords(d),
283+
284+ sdist = 149598000, // distance from Earth to Sun in km
285+
286+ phi = acos(sin(s.dec) * sin(m.dec) + cos(s.dec) * cos(m.dec) * cos(s.ra - m.ra)),
287+ inc = atan(sdist * sin(phi), m.dist - sdist * cos(phi)),
288+ angle = atan(cos(s.dec) * sin(s.ra - m.ra), sin(s.dec) * cos(m.dec) -
289+ cos(s.dec) * sin(m.dec) * cos(s.ra - m.ra));
290+
291+ return {
292+ fraction: (1 + cos(inc)) / 2,
293+ phase: 0.5 + 0.5 * inc * (angle < 0 ? -1 : 1) / Math.PI,
294+ angle: angle
295+ };
296+};
297+
298+
299+function hoursLater(date, h) {
300+ return new Date(date.valueOf() + h * dayMs / 24);
301+}
302+
303+// calculations for moon rise/set times are based on http://www.stargazing.net/kepler/moonrise.html article
304+
305+SunCalc.getMoonTimes = function (date, lat, lng) {
306+ var t = new Date(date);
307+ t.setHours(0);
308+ t.setMinutes(0);
309+ t.setSeconds(0);
310+ t.setMilliseconds(0);
311+
312+ var hc = 0.133 * rad,
313+ h0 = SunCalc.getMoonPosition(t, lat, lng).altitude - hc,
314+ h1, h2, rise, set, a, b, xe, ye, d, roots, x1, x2, dx;
315+
316+ // go in 2-hour chunks, each time seeing if a 3-point quadratic curve crosses zero (which means rise or set)
317+ for (var i = 1; i <= 24; i += 2) {
318+ h1 = SunCalc.getMoonPosition(hoursLater(t, i), lat, lng).altitude - hc;
319+ h2 = SunCalc.getMoonPosition(hoursLater(t, i + 1), lat, lng).altitude - hc;
320+
321+ a = (h0 + h2) / 2 - h1;
322+ b = (h2 - h0) / 2;
323+ xe = -b / (2 * a);
324+ ye = (a * xe + b) * xe + h1;
325+ d = b * b - 4 * a * h1;
326+ roots = 0;
327+
328+ if (d >= 0) {
329+ dx = Math.sqrt(d) / (Math.abs(a) * 2);
330+ x1 = xe - dx;
331+ x2 = xe + dx;
332+ if (Math.abs(x1) <= 1) roots++;
333+ if (Math.abs(x2) <= 1) roots++;
334+ if (x1 < -1) x1 = x2;
335+ }
336+
337+ if (roots === 1) {
338+ if (h0 < 0) rise = i + x1;
339+ else set = i + x1;
340+
341+ } else if (roots === 2) {
342+ rise = i + (ye < 0 ? x2 : x1);
343+ set = i + (ye < 0 ? x1 : x2);
344+ }
345+
346+ if (rise && set) break;
347+
348+ h0 = h2;
349+ }
350+
351+ var result = {};
352+
353+ if (rise) result.rise = hoursLater(t, rise);
354+ if (set) result.set = hoursLater(t, set);
355+
356+ if (!rise && !set) result[ye > 0 ? 'alwaysUp' : 'alwaysDown'] = true;
357+
358+ return result;
359+};
360+
361+
362+// export as AMD module / Node module / browser variable
363+//if (typeof define === 'function' && define.amd) define(SunCalc);
364+//else if (typeof module !== 'undefined') module.exports = SunCalc;
365+//else window.SunCalc = SunCalc;
366+
367+//}());
368
369=== modified file 'app/ui/HomePage.qml'
370--- app/ui/HomePage.qml 2015-07-18 19:08:21 +0000
371+++ app/ui/HomePage.qml 2015-07-25 21:36:51 +0000
372@@ -66,16 +66,21 @@
373 Format date object by given format.
374 */
375 function formatTimestamp(dateData, format) {
376- var date = new Date(dateData.year, dateData.month, dateData.date, dateData.hours, dateData.minutes)
377- return Qt.formatDate(date, i18n.tr(format))
378+ return Qt.formatDate(getDate(dateData), i18n.tr(format))
379 }
380
381 /*
382 Format time object by given format.
383 */
384 function formatTime(dateData, format) {
385- var date = new Date(dateData.year, dateData.month, dateData.date, dateData.hours, dateData.minutes)
386- return Qt.formatTime(date, i18n.tr(format))
387+ return Qt.formatTime(getDate(dateData), i18n.tr(format))
388+ }
389+
390+ /*
391+ Get Date object from dateData.
392+ */
393+ function getDate(dateData) {
394+ return new Date(dateData.year, dateData.month, dateData.date, dateData.hours, dateData.minutes)
395 }
396
397 /*
398
399=== modified file 'app/ui/LocationPane.qml'
400--- app/ui/LocationPane.qml 2015-06-21 14:02:58 +0000
401+++ app/ui/LocationPane.qml 2015-07-25 21:36:51 +0000
402@@ -20,6 +20,7 @@
403 import Ubuntu.Components 1.2
404 import Ubuntu.Components.ListItems 0.1 as ListItem
405 import "../components"
406+import "../data/suncalc.js" as SunCalc
407
408 Item {
409 id: locationItem
410@@ -99,6 +100,8 @@
411 image: (forecasts[x].icon !== undefined && iconMap[forecasts[x].icon] !== undefined) ? iconMap[forecasts[x].icon] : "",
412 chanceOfRain: forecasts[x].propPrecip === undefined ? -1 : forecasts[x].propPrecip,
413 humidity: emptyIfUndefined(forecasts[x].humidity, "%"),
414+ sunrise: forecasts[x].sunrise || SunCalc.SunCalc.getTimes(getDate(forecasts[x].date), data.location.coord.lat, data.location.coord.lon).sunrise.toLocaleTimeString(),
415+ sunset: forecasts[x].sunset || SunCalc.SunCalc.getTimes(getDate(forecasts[x].date), data.location.coord.lat, data.location.coord.lon).sunset.toLocaleTimeString(),
416 uvIndex: emptyIfUndefined(forecasts[x].uv),
417 wind: forecasts[x][tempUnits].windSpeed === undefined || forecasts[x].windDir === undefined
418 ? "" : Math.round(forecasts[x][tempUnits].windSpeed) + settings.windUnits + " " + forecasts[x].windDir
419@@ -185,8 +188,8 @@
420 humidity: model.humidity
421 // TODO: extra from API
422 //pollen: model.pollen
423- //sunrise: model.sunrise
424- //sunset: model.sunset
425+ sunrise: model.sunrise
426+ sunset: model.sunset
427 wind: model.wind
428 uvIndex: model.uvIndex
429 }
430
431=== modified file 'debian/copyright'
432--- debian/copyright 2015-02-02 15:25:53 +0000
433+++ debian/copyright 2015-07-25 21:36:51 +0000
434@@ -15,10 +15,35 @@
435 2015 Victor Thompson <victor.thompson@gmail.com>
436 License: GPL-3
437
438+Files: app/data/suncalc.js
439+Copyright: 2014 Vladimir Agafonkin
440+License: BSD-2-clause
441+
442 Files: debian/*
443 Copyright: 2013 Canonical Ltd.
444 License: LGPL-3
445
446+License: BSD-2-clause
447+ Redistribution and use in source and binary forms, with or without modification, are
448+ permitted provided that the following conditions are met:
449+ .
450+ 1. Redistributions of source code must retain the above copyright notice, this list of
451+ conditions and the following disclaimer.
452+ .
453+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
454+ of conditions and the following disclaimer in the documentation and/or other materials
455+ provided with the distribution.
456+ .
457+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
458+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
459+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
460+ COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
461+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
462+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
463+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
464+ TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
465+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
466+
467 License: GPL-3
468 This package is free software; you can redistribute it and/or
469 modify it under the terms of the GNU General Public

Subscribers

People subscribed via source and target branches

to all changes: