From 9dedbff8a29b7757782379b98891eb7600e33c9a Mon Sep 17 00:00:00 2001 From: Gary Martin Date: Sat, 15 Mar 2008 02:28:17 +0000 Subject: Initial import --- diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..2370fd4 --- /dev/null +++ b/MANIFEST @@ -0,0 +1,16 @@ +Moon.activity/ +Moon.activity//activity +Moon.activity//activity/activity.info +Moon.activity//activity/moon-icon.svg +Moon.activity//icons +Moon.activity//icons/grid-icon.svg +Moon.activity//icons/hemi-icon.svg +Moon.activity//MANIFEST +Moon.activity//moon.jpg +Moon.activity//moon.py +Moon.activity//moon.pyc +Moon.activity//Moon.xo +Moon.activity//po +Moon.activity//po/moon.pot +Moon.activity//po/POTFILES.in +Moon.activity//setup.py diff --git a/Moon.xo b/Moon.xo new file mode 100644 index 0000000..26ac115 --- /dev/null +++ b/Moon.xo Binary files differ diff --git a/activity/activity.info b/activity/activity.info new file mode 100644 index 0000000..d2d10c8 --- /dev/null +++ b/activity/activity.info @@ -0,0 +1,7 @@ +[Activity] +name = Moon +activity_version = 4 +service_name = com.garycmartin.Moon +icon = moon-icon +class = moon.MoonActivity +show_launcher = yes diff --git a/activity/moon-icon.svg b/activity/moon-icon.svg new file mode 100644 index 0000000..7706465 --- /dev/null +++ b/activity/moon-icon.svg @@ -0,0 +1,28 @@ + + + +]> + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/grid-icon.svg b/icons/grid-icon.svg new file mode 100644 index 0000000..75c269f --- /dev/null +++ b/icons/grid-icon.svg @@ -0,0 +1,11 @@ + + + +]> + + + + + + diff --git a/icons/hemi-icon.svg b/icons/hemi-icon.svg new file mode 100644 index 0000000..478bae4 --- /dev/null +++ b/icons/hemi-icon.svg @@ -0,0 +1,23 @@ + + + +]> + + + + + + + + + + + + + + + + + diff --git a/moon.jpg b/moon.jpg new file mode 100644 index 0000000..e2ee075 --- /dev/null +++ b/moon.jpg Binary files differ diff --git a/moon.py b/moon.py new file mode 100755 index 0000000..54798fa --- /dev/null +++ b/moon.py @@ -0,0 +1,600 @@ +#!/usr/bin/python +# coding=UTF-8 + +# Copyright 2008 Gary C. Martin +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +# Activity web site: http://wiki.laptop.org/go/Moon +# Created: Febuary 2008 +# Author: gary@garycmartin.com +# Home page: http://www.garycmartin.com/ + +"""\ +Moon phase information XO activity. + +TODO: +- Mouse over text hints for major Moon features +- Reduce amount of decimal places shown + - remove seconds and/or minutes? + - make basic text info (phase) more central/larger (show mini-graphic)? + - make complex text info less prominent? + - allow hiding of different information blocks? + - make info area scrollable so more could be included? +- When showing grid, draw terminator in green? +- Would a move to Cairo allow me better alpha control? +- Show daylight view menu option? + - fades out moon during the (approx) day with pastel blue + - eye candy cloud effect? + - solar eclipse effect +- Show interesting markers view menu option? + - lunar probes + - man missions +- Structure code into a better View, Model, Controller +- Reduce CPU and memory footprint + - stop updates when the activity is not visible + - free up (mainly image) data between updates or when not visible? +- Wrap 'Next' dates in round rect bezel style with mini Moon graphic? +- New feature, mini-month calendar view in bottom of info area? +- Dark adapted view for night gazing usage? +""" + +import gtk +import gobject +from sugar.activity import activity +from sugar.graphics.toolbutton import ToolButton +from gettext import gettext as _ +import math +import time +import cPickle +import os + +IMAGE_SIZE = 726 +HALF_SIZE = IMAGE_SIZE / 2 + +class MoonActivity(activity.Activity): + """Moon phase activity.""" + def __init__(self, handle): + activity.Activity.__init__(self, handle) + self._name = handle + self.set_title(_("Moon")) + + # Defaults (Journal resume priority, default persistent file secondary, fall-back hardcoded values) + self.hemisphereView = 'north' + self.showGrid = False + self.activityState = {} + self.activityState['hemisphereView'] = self.hemisphereView + self.activityState['showGrid'] = self.showGrid + self.parse_preferences(os.environ['SUGAR_ACTIVITY_ROOT'] + '/data/defaults') + + # Toolbox + toolbox = activity.ActivityToolbox(self) + viewToolBar = gtk.Toolbar() + self.toggleGridButton = ToolButton('grid-icon') + self.toggleGridButton.set_tooltip(_("Toggle Grid View")) + self.toggleGridHandlerId = self.toggleGridButton.connect('clicked', self.toggleGridClicked) + viewToolBar.insert(self.toggleGridButton, -1) + self.toggleGridButton.show() + self.toggleHemisphereButton = ToolButton('hemi-icon') + self.toggleHemisphereButton.set_tooltip(_("Toggle Hemisphere View")) + self.toggleHemisphereHandlerId = self.toggleHemisphereButton.connect('clicked', self.toggleHemisphereClicked) + viewToolBar.insert(self.toggleHemisphereButton, -1) + self.toggleHemisphereButton.show() + viewToolBar.show() + toolbox.add_toolbar(_('View'), viewToolBar) + self.set_toolbox(toolbox) + toolbox.show() + + # Create the main activity container + self.mainView = gtk.HBox() + + # Create an event box to hold the Moon image (mainly so I can set background color) + self.event_box = gtk.EventBox() + colormap = gtk.gdk.colormap_get_system() + self.blackAllocColor = colormap.alloc_color('black') + self.whiteAllocColor = colormap.alloc_color('white') + self.blueGreenMaskAllocColor = colormap.alloc_color('#F00') + self.redAllocColor = colormap.alloc_color('#F20') + self.blueAllocColor = colormap.alloc_color('#04F') + self.event_box.modify_bg(gtk.STATE_NORMAL, self.blackAllocColor) + + # Create the Moon image widget + self.image = gtk.Image() + self.imagePixmap = gtk.gdk.Pixmap(self.window, IMAGE_SIZE, IMAGE_SIZE) + self.gc = self.imagePixmap.new_gc(foreground=self.blackAllocColor) + self.image.set_from_pixmap(self.imagePixmap, None) + self.event_box.add(self.image) + self.mainView.pack_end(self.event_box) + + # Create Moon information panel + self.infoPanel = gtk.VBox() + self.infoPanel.set_border_width(17) + self.info = gtk.Label() + self.info.set_justify(gtk.JUSTIFY_LEFT) + self.infoPanel.pack_start(self.info, False, False, 0) + self.mainView.pack_start(self.infoPanel, False, False, 0) + + # Create Moon data model + self.dataModel = DataModel() + + # Generate first set of views for display and kick off their timers + self.toggleGridButton.handler_block(self.toggleGridHandlerId) + self.toggleHemisphereButton.handler_block(self.toggleHemisphereHandlerId) + self.updateTextInformationView() + self.updateMoonImageView() + + # Display everything + self.info.show() + self.infoPanel.show() + self.image.show() + self.event_box.show() + self.mainView.show() + self.set_canvas(self.mainView) + self.show_all() + + def parse_preferences(self, file_path): + """Parse and set preference data from a given file.""" + try: + f = file(file_path, 'r') + self.activityState = cPickle.load(f) + f.close() + if 'hemisphereView' in self.activityState.keys(): + self.hemisphereView = self.activityState['hemisphereView'] + if 'showGrid' in self.activityState.keys(): + self.showGrid = self.activityState['showGrid'] + except: + print "preference data %s not available" % (file_path) + + def read_file(self, file_path): + """Read state from datastore.""" + self.parse_preferences(file_path) + print "Reading previous state from datastore." + self.blockViewButtonsDuringUpdate() + + def write_file(self, file_path): + """Write state to journal datastore and to persistent file system.""" + self.activityState['hemisphereView'] = self.hemisphereView + self.activityState['showGrid'] = self.showGrid + toJournal = file(file_path, 'w') + cPickle.dump(self.activityState, toJournal) + toJournal.close() + toPersistentFs = file(os.environ['SUGAR_ACTIVITY_ROOT'] + '/data/defaults', 'w') + cPickle.dump(self.activityState, toPersistentFs) + toPersistentFs.close() + + def toggleGridClicked(self, widget): + """Respond to toolbar button to hide/show grid lines.""" + if self.showGrid == True: + self.showGrid = False + else: + self.showGrid = True + self.blockViewButtonsDuringUpdate() + + def toggleHemisphereClicked(self, widget): + """Respond to toolbar button to change viewing hemisphere.""" + if self.hemisphereView == 'north': + self.hemisphereView = 'south' + else: + self.hemisphereView = 'north' + self.blockViewButtonsDuringUpdate() + + def blockViewButtonsDuringUpdate(self): + """Disable view buttons while updating image to prevent multi-clicks.""" + self.toggleGridButton.handler_block(self.toggleGridHandlerId) + self.toggleHemisphereButton.handler_block(self.toggleHemisphereHandlerId) + gobject.source_remove(self.updateMoonImageTimeout) + self.updateMoonImageView() + + def unblockViewUpdateButtons(self): + """Reactivate view button after updating image, stops multi-clicks.""" + self.toggleGridButton.handler_unblock(self.toggleGridHandlerId) + self.toggleHemisphereButton.handler_unblock(self.toggleHemisphereHandlerId) + + def updateTextInformationView(self): + """Generate Moon data and update text based information view.""" + # Generate Moon data + self.dataModel.updateMoonCalculations(time.time()) + + # Update text information + informationString = "" + informationString += _("Today's Moon Information\n\n") + informationString += _("Phase:\n%s\n\n") % (self.dataModel.moonPhaseName(self.dataModel.phaseOfMoon)) + informationString += _("Julian Date:\n%.2f (astronomical)\n\n") % (self.dataModel.julianDate) + informationString += _("Age:\n%(days).0f days, %(hours).0f hours, %(minutes).0f minutes\n\n") % {'days':self.dataModel.daysOld, 'hours':self.dataModel.hoursOld, 'minutes':self.dataModel.minutesOld} + informationString += _("Lunation:\n%(phase).2f%% through lunation %(lunation)d\n\n") % {'phase':self.dataModel.phaseOfMoon * 100, 'lunation':self.dataModel.lunation} + informationString += _("Surface Visibility:\n%.0f%% (estimated)\n\n") % (self.dataModel.percentOfFullMoon * 100) + informationString += _("Selenographic Terminator Longitude:\n%(deg).1f˚%(westOrEast)s (%(riseOrSet)s)\n\n") % {'deg':self.dataModel.selenographicDeg, 'westOrEast':self.dataModel.westOrEast, 'riseOrSet':self.dataModel.riseOrSet} + informationString += _("Next Full Moon:\n%(date)s in %(days).0f days\n\n") % {'date':time.ctime(self.dataModel.nextFullMoonDate), 'days':self.dataModel.daysUntilFullMoon} + informationString += _("Next New Moon:\n%(date)s in %(days).0f days\n\n") % {'date':time.ctime(self.dataModel.nextNewMoonDate), 'days':self.dataModel.daysUntilNewMoon} + informationString += _("Next Lunar eclipse:\n%(date)s in %(days).0f days\n\n") % {'date':time.ctime(self.dataModel.nextLunarEclipseDate), 'days':self.dataModel.daysUntilLunarEclipse} + informationString += _("Next Solar eclipse:\n%(date)s in %(days).0f days\n\n") % {'date':time.ctime(self.dataModel.nextSolarEclipseDate), 'days':self.dataModel.daysUntilSolarEclipse} + self.info.set_markup(informationString) + + # Calculate time to next minute cusp and set a new timer + msToNextMinCusp = (60 - time.gmtime()[5]) * 1000 + gobject.timeout_add(msToNextMinCusp, self.updateTextInformationView) + + # Stop this timer running + return False + + def updateMoonImageView(self): + """Update Moon image view using last cached Moon data.""" + # Erase last Moon rendering + self.imagePixmap.draw_rectangle(self.gc, True, 0, 0, IMAGE_SIZE, IMAGE_SIZE) + + # Create a 1bit shadow mask + maskPixmap = gtk.gdk.Pixmap(None, IMAGE_SIZE, IMAGE_SIZE, depth=1) + kgc = maskPixmap.new_gc(foreground=self.blackAllocColor) + wgc = maskPixmap.new_gc(foreground=self.whiteAllocColor) + maskPixmap.draw_rectangle(kgc, True, 0, 0, IMAGE_SIZE, IMAGE_SIZE) + if self.dataModel.phaseOfMoon <= .25: + # New Moon to First Quarter + phaseShadowAdjust = self.dataModel.phaseOfMoon - abs(math.sin(self.dataModel.phaseOfMoon * math.pi * 4) / 18.0) + arcScale = int(IMAGE_SIZE * (1 - (phaseShadowAdjust * 4))) + maskPixmap.draw_rectangle(wgc, True, HALF_SIZE + 1, 0, HALF_SIZE, IMAGE_SIZE - 1) + maskPixmap.draw_arc(kgc, True, HALF_SIZE - int(arcScale / 2), 0, arcScale, IMAGE_SIZE, 17280, 11520) + elif self.dataModel.phaseOfMoon <= .5: + # First Quarter to Full Moon + phaseShadowAdjust = self.dataModel.phaseOfMoon + abs(math.sin(self.dataModel.phaseOfMoon * math.pi * 4) / 18.0) + arcScale = int(IMAGE_SIZE * ((phaseShadowAdjust - .25) * 4)) + maskPixmap.draw_rectangle(wgc, True, HALF_SIZE, 0, HALF_SIZE, IMAGE_SIZE) + maskPixmap.draw_arc(wgc, True, HALF_SIZE - int(arcScale / 2), 0, arcScale, IMAGE_SIZE, 5760, 11520) + elif self.dataModel.phaseOfMoon <= .75: + # Full Moon to Last Quarter + phaseShadowAdjust = self.dataModel.phaseOfMoon - abs(math.sin(self.dataModel.phaseOfMoon * math.pi * 4) / 18.0) + arcScale = int(IMAGE_SIZE * (1 - ((phaseShadowAdjust - .5) * 4))) + maskPixmap.draw_rectangle(wgc, True, 0, 0, HALF_SIZE + 1, IMAGE_SIZE) + maskPixmap.draw_arc(wgc, True, HALF_SIZE - int(arcScale / 2), 0, arcScale, IMAGE_SIZE, 17280, 11520) + else: + # Last Quarter to New Moon + phaseShadowAdjust = self.dataModel.phaseOfMoon + abs(math.sin(self.dataModel.phaseOfMoon * math.pi * 4) / 18.0) + arcScale = int(IMAGE_SIZE * ((phaseShadowAdjust - .75) * 4)) + maskPixmap.draw_rectangle(wgc, True, 0, 0, HALF_SIZE, IMAGE_SIZE) + maskPixmap.draw_arc(kgc, True, HALF_SIZE - int(arcScale / 2), 0, arcScale, IMAGE_SIZE, 5760, 11520) + maskgc = self.imagePixmap.new_gc(clip_mask=maskPixmap) + + # Modified image based on public domain photo by John MacCooey + moonPixbuf = gtk.gdk.pixbuf_new_from_file("moon.jpg") + + # Composite bright Moon image and semi-transparant Moon for shadow detail + darkPixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, IMAGE_SIZE, IMAGE_SIZE) + if (self.dataModel.nextLunarEclipseSeconds == -1 and self.dataModel.lastLunarEclipseSeconds > 7200) or (self.dataModel.nextLunarEclipseSeconds > 7200 and self.dataModel.lastLunarEclipseSeconds == -1) or min(self.dataModel.nextLunarEclipseSeconds, self.dataModel.lastLunarEclipseSeconds) > 7200: + # Normal Moon phase render + moonPixbuf.composite(darkPixbuf, 0, 0, IMAGE_SIZE, IMAGE_SIZE, 0, 0, 1, 1, gtk.gdk.INTERP_BILINEAR, 127) + self.imagePixmap.draw_pixbuf(self.gc, darkPixbuf, 0, 0, 0, 0) + self.imagePixmap.draw_pixbuf(maskgc, moonPixbuf, 0, 0, 0, 0) + + else: + # Reddening eclipse effect, 2hrs (7200sec) before and after (by masking out green & blue) + if self.dataModel.nextLunarEclipseSeconds == -1: + eclipseAlpha = self.dataModel.lastLunarEclipseSeconds / 7200.0 * 256 + elif self.dataModel.lastLunarEclipseSeconds == -1: + eclipseAlpha = self.dataModel.nextLunarEclipseSeconds / 7200.0 * 256 + else: + eclipseAlpha = min(self.dataModel.nextLunarEclipseSeconds, self.dataModel.lastLunarEclipseSeconds) / 7200.0 * 256 + moonPixbuf.composite(darkPixbuf, 0, 0, IMAGE_SIZE, IMAGE_SIZE, 0, 0, 1, 1, gtk.gdk.INTERP_BILINEAR, 196 - eclipseAlpha / 2) + self.imagePixmap.draw_pixbuf(self.gc, darkPixbuf, 0, 0, 0, 0) + del darkPixbuf + darkPixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, IMAGE_SIZE, IMAGE_SIZE) + moonPixbuf.composite(darkPixbuf, 0, 0, IMAGE_SIZE, IMAGE_SIZE, 0, 0, 1, 1, gtk.gdk.INTERP_BILINEAR, eclipseAlpha) + rgc = self.imagePixmap.new_gc(foreground=self.blueGreenMaskAllocColor, function=gtk.gdk.AND) + self.imagePixmap.draw_rectangle(rgc, True, 0, 0, IMAGE_SIZE, IMAGE_SIZE) + self.imagePixmap.draw_pixbuf(self.gc, darkPixbuf, 0, 0, 0, 0) + + if self.hemisphereView == 'south': + # Rotate final image for a view from north or south hemisphere + rotPixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, IMAGE_SIZE, IMAGE_SIZE) + rotPixbuf.get_from_drawable(self.imagePixmap, self.imagePixmap.get_colormap(), 0, 0, 0, 0, -1, -1) + rotPixbuf = rotPixbuf.rotate_simple(gtk.gdk.PIXBUF_ROTATE_UPSIDEDOWN) + self.imagePixmap.draw_pixbuf(self.gc, rotPixbuf, 0, 0, 0, 0) + if self.showGrid: + # Draw grid rotated for south hemi + self.drawGrid(_("SNWE")) + elif self.showGrid: + # Draw grid for north hemi + self.drawGrid(_("NSEW")) + + self.image.queue_draw() + + # Update the Moon image in another 5min + self.updateMoonImageTimeout = gobject.timeout_add(300000, self.updateMoonImageView) + + # Delay before view buttons can be clicked again (blocked to stop repeat clicks) + gobject.timeout_add(50, self.unblockViewUpdateButtons) + + # Stop this timer running + return False + + def drawGrid(self, compassText): + # Draw Selenographic grid line data + rgc = self.imagePixmap.new_gc(foreground=self.redAllocColor) + bgc = self.imagePixmap.new_gc(foreground=self.blueAllocColor) + wgc = self.imagePixmap.new_gc(foreground=self.whiteAllocColor) + pangoLayout = self.image.create_pango_layout("") + pangoLayout.set_text("0°") + self.imagePixmap.draw_rectangle(bgc, True, HALF_SIZE + 2, HALF_SIZE, 24, 22) + self.imagePixmap.draw_layout(wgc, HALF_SIZE + 2, HALF_SIZE, pangoLayout) + pangoLayout.set_text("30°") + self.imagePixmap.draw_rectangle(bgc, True, HALF_SIZE + 2, int(HALF_SIZE * 0.5), 36, 22) + self.imagePixmap.draw_rectangle(bgc, True, HALF_SIZE + 2, int(HALF_SIZE * 1.5), 36, 22) + self.imagePixmap.draw_layout(wgc, HALF_SIZE + 2, int(HALF_SIZE * 0.5), pangoLayout) + self.imagePixmap.draw_layout(wgc, HALF_SIZE + 2, int(HALF_SIZE * 1.5), pangoLayout) + pangoLayout.set_text("60°") + self.imagePixmap.draw_rectangle(bgc, True, HALF_SIZE + 2, int(HALF_SIZE * 0.15), 36, 22) + self.imagePixmap.draw_rectangle(bgc, True, HALF_SIZE + 2, int(HALF_SIZE * 1.85), 36, 22) + self.imagePixmap.draw_layout(wgc, HALF_SIZE + 2, int(HALF_SIZE * 0.15), pangoLayout) + self.imagePixmap.draw_layout(wgc, HALF_SIZE + 2, int(HALF_SIZE * 1.85), pangoLayout) + pangoLayout.set_text("30°") + self.imagePixmap.draw_rectangle(rgc, True, int(HALF_SIZE * 0.48) + 2, HALF_SIZE, 36, 22) + self.imagePixmap.draw_rectangle(rgc, True, int(HALF_SIZE * 1.52) + 2, HALF_SIZE, 36, 22) + self.imagePixmap.draw_layout(wgc, int(HALF_SIZE * 0.48) + 2, HALF_SIZE, pangoLayout) + self.imagePixmap.draw_layout(wgc, int(HALF_SIZE * 1.52) + 2, HALF_SIZE, pangoLayout) + pangoLayout.set_text("60°") + self.imagePixmap.draw_rectangle(rgc, True, int(HALF_SIZE * 0.15) + 2, HALF_SIZE, 36, 22) + self.imagePixmap.draw_rectangle(rgc, True, int(HALF_SIZE * 1.85) + 2, HALF_SIZE, 36, 22) + self.imagePixmap.draw_layout(wgc, int(HALF_SIZE * 0.15) + 2, HALF_SIZE, pangoLayout) + self.imagePixmap.draw_layout(wgc, int(HALF_SIZE * 1.85) + 2, HALF_SIZE, pangoLayout) + for i in (-1, 0, 1): + self.imagePixmap.draw_line(rgc, HALF_SIZE + i, 0, HALF_SIZE + i, IMAGE_SIZE) + self.imagePixmap.draw_arc(rgc, False, int(HALF_SIZE * 0.15) + i, 0, IMAGE_SIZE - int(IMAGE_SIZE * 0.15), IMAGE_SIZE, 0, 360*64) + self.imagePixmap.draw_arc(rgc, False, int(HALF_SIZE * 0.48) + i, 0, IMAGE_SIZE - int(IMAGE_SIZE * 0.48) , IMAGE_SIZE, 0, 360*64) + self.imagePixmap.draw_line(bgc, 0, HALF_SIZE + i, IMAGE_SIZE, HALF_SIZE + i) + self.imagePixmap.draw_line(bgc, int(HALF_SIZE * 0.15), int(HALF_SIZE * 0.5) + i, IMAGE_SIZE - int(HALF_SIZE * 0.15), int(HALF_SIZE * 0.5) + i) + self.imagePixmap.draw_line(bgc, int(HALF_SIZE * 0.15), int(HALF_SIZE * 1.5) + i, IMAGE_SIZE - int(HALF_SIZE * 0.15), int(HALF_SIZE * 1.5) + i) + self.imagePixmap.draw_line(bgc, int(HALF_SIZE * 0.5), int(HALF_SIZE * 0.15) + i, IMAGE_SIZE - int(HALF_SIZE * 0.5), int(HALF_SIZE * 0.15) + i) + self.imagePixmap.draw_line(bgc, int(HALF_SIZE * 0.5), int(HALF_SIZE * 1.85) + i, IMAGE_SIZE - int(HALF_SIZE * 0.5), int(HALF_SIZE * 1.85) + i) + + # Key text + pangoLayout.set_text(_("Latitude")) + self.imagePixmap.draw_layout(bgc, 0, IMAGE_SIZE - 48, pangoLayout) + pangoLayout.set_text(_("Longitude")) + self.imagePixmap.draw_layout(rgc, 0, IMAGE_SIZE - 24, pangoLayout) + + # Compass + self.imagePixmap.draw_line(bgc, 45, 24, 45, 68) + self.imagePixmap.draw_line(rgc, 22, 48, 68, 48) + pangoLayout.set_text(compassText[0]) + self.imagePixmap.draw_layout(bgc, 38, 0, pangoLayout) + pangoLayout.set_text(compassText[1]) + self.imagePixmap.draw_layout(bgc, 38, 72, pangoLayout) + pangoLayout.set_text(compassText[2]) + self.imagePixmap.draw_layout(rgc, 72, 36, pangoLayout) + pangoLayout.set_text(compassText[3]) + self.imagePixmap.draw_layout(rgc, 0, 36, pangoLayout) + + +class DataModel(): + """Moon phase data model and various utility methods.""" + + def __init__(self): + """Init simple, hard coded, tupules for New, First Quarter, Full Last Quarter Moon UTC data. + + 2008 to 2018 data from http://sunearth.gsfc.nasa.gov/eclipse/phase/phasecat.html + algorithms used in predicting the phases of the Moon and eclipses are based + on Jean Meeus' Astronomical Algorithms (Willmann-Bell, Inc., 1998). All + calculations are by Fred Espenak, and he assumes full responsibility for + their accuracy. Permission is freely granted to reproduce this data when + accompanied by an acknowledgment. + + Data is all UTC and in YYYY-MM-DD HH:MM format, with New and Full Moon + arrays with an extra end character for eclipse types T=Total (Solar), + A=Annular (Solar), H=Hybrid (Solar Annular/Total), P=Partial (Solar), + t=Total (Lunar Umbral), p=Partial (Lunar Umbral), n=Penumbral (Lunar), + _=none.""" + + self.dateFormat = "%Y-%m-%d %H:%M" + self.newMoonArray = ("2008-01-08 11:37_", "2008-02-07 03:44A", "2008-03-07 17:14_", "2008-04-06 03:55_", "2008-05-05 12:18_", "2008-06-03 19:23_", "2008-07-03 02:19_", "2008-08-01 10:13T", "2008-08-30 19:58_", "2008-09-29 08:12_", "2008-10-28 23:14_", "2008-11-27 16:55_", "2008-12-27 12:23_", "2009-01-26 07:55A", "2009-02-25 01:35_", "2009-03-26 16:06_", "2009-04-25 03:23_", "2009-05-24 12:11_", "2009-06-22 19:35_", "2009-07-22 02:35T", "2009-08-20 10:01_", "2009-09-18 18:44_", "2009-10-18 05:33_", "2009-11-16 19:14_", "2009-12-16 12:02_", "2010-01-15 07:11A", "2010-02-14 02:51_", "2010-03-15 21:01_", "2010-04-14 12:29_", "2010-05-14 01:04_", "2010-06-12 11:15_", "2010-07-11 19:40T", "2010-08-10 03:08_", "2010-09-08 10:30_", "2010-10-07 18:44_", "2010-11-06 04:52_", "2010-12-05 17:36_", "2011-01-04 09:03P", "2011-02-03 02:31_", "2011-03-04 20:46_", "2011-04-03 14:32_", "2011-05-03 06:51_", "2011-06-01 21:03P", "2011-07-01 08:54P", "2011-07-30 18:40_", "2011-08-29 03:04_", "2011-09-27 11:09_", "2011-10-26 19:56_", "2011-11-25 06:10P", "2011-12-24 18:06_", "2012-01-23 07:39_", "2012-02-21 22:35_", "2012-03-22 14:37_", "2012-04-21 07:18_", "2012-05-20 23:47A", "2012-06-19 15:02_", "2012-07-19 04:24_", "2012-08-17 15:54_", "2012-09-16 02:11_", "2012-10-15 12:02_", "2012-11-13 22:08T", "2012-12-13 08:42_", "2013-01-11 19:44_", "2013-02-10 07:20_", "2013-03-11 19:51_", "2013-04-10 09:35_", "2013-05-10 00:29A", "2013-06-08 15:56_", "2013-07-08 07:14_", "2013-08-06 21:51_", "2013-09-05 11:36_", "2013-10-05 00:35_", "2013-11-03 12:50H", "2013-12-03 00:22_", "2014-01-01 11:14_", "2014-01-30 21:39_", "2014-03-01 08:00_", "2014-03-30 18:45_", "2014-04-29 06:14A", "2014-05-28 18:40_", "2014-06-27 08:09_", "2014-07-26 22:42_", "2014-08-25 14:13_", "2014-09-24 06:14_", "2014-10-23 21:57P", "2014-11-22 12:32_", "2014-12-22 01:36_", "2015-01-20 13:14_", "2015-02-18 23:47_", "2015-03-20 09:36T", "2015-04-18 18:57_", "2015-05-18 04:13_", "2015-06-16 14:05_", "2015-07-16 01:24_", "2015-08-14 14:54_", "2015-09-13 06:41P", "2015-10-13 00:06_", "2015-11-11 17:47_", "2015-12-11 10:29_", "2016-01-10 01:30_", "2016-02-08 14:39_", "2016-03-09 01:54T", "2016-04-07 11:24_", "2016-05-06 19:30_", "2016-06-05 03:00_", "2016-07-04 11:01_", "2016-08-02 20:45_", "2016-09-01 09:03A", "2016-10-01 00:12_", "2016-10-30 17:38_", "2016-11-29 12:18_", "2016-12-29 06:53_", "2017-01-28 00:07_", "2017-02-26 14:58A", "2017-03-28 02:57_", "2017-04-26 12:16_", "2017-05-25 19:44_", "2017-06-24 02:31_", "2017-07-23 09:46_", "2017-08-21 18:30T", "2017-09-20 05:30_", "2017-10-19 19:12_", "2017-11-18 11:42_", "2017-12-18 06:31_", "2018-01-17 02:17_", "2018-02-15 21:05P", "2018-03-17 13:12_", "2018-04-16 01:57_", "2018-05-15 11:48_", "2018-06-13 19:43_", "2018-07-13 02:48P", "2018-08-11 09:58P", "2018-09-09 18:01_", "2018-10-09 03:47_", "2018-11-07 16:02_", "2018-12-07 07:20_") + self.fullMoonArray = ("2008-01-22 13:35_", "2008-02-21 03:31t", "2008-03-21 18:40_", "2008-04-20 10:25_", "2008-05-20 02:11_", "2008-06-18 17:30_", "2008-07-18 07:59_", "2008-08-16 21:16p", "2008-09-15 09:13_", "2008-10-14 20:03_", "2008-11-13 06:17_", "2008-12-12 16:37_", "2009-01-11 03:27_", "2009-02-09 14:49n", "2009-03-11 02:38_", "2009-04-09 14:56_", "2009-05-09 04:01_", "2009-06-07 18:12_", "2009-07-07 09:21n", "2009-08-06 00:55n", "2009-09-04 16:03_", "2009-10-04 06:10_", "2009-11-02 19:14_", "2009-12-02 07:30_", "2009-12-31 19:13p", "2010-01-30 06:18_", "2010-02-28 16:38_", "2010-03-30 02:25_", "2010-04-28 12:18_", "2010-05-27 23:07_", "2010-06-26 11:30p", "2010-07-26 01:37_", "2010-08-24 17:05_", "2010-09-23 09:17_", "2010-10-23 01:36_", "2010-11-21 17:27_", "2010-12-21 08:13t", "2011-01-19 21:21_", "2011-02-18 08:36_", "2011-03-19 18:10_", "2011-04-18 02:44_", "2011-05-17 11:09_", "2011-06-15 20:13t", "2011-07-15 06:40_", "2011-08-13 18:58_", "2011-09-12 09:27_", "2011-10-12 02:06_", "2011-11-10 20:16_", "2011-12-10 14:36t", "2012-01-09 07:30_", "2012-02-07 21:54_", "2012-03-08 09:40_", "2012-04-06 19:19_", "2012-05-06 03:35_", "2012-06-04 11:12p", "2012-07-03 18:52_", "2012-08-02 03:27_", "2012-08-31 13:58_", "2012-09-30 03:19_", "2012-10-29 19:50_", "2012-11-28 14:46n", "2012-12-28 10:21_", "2013-01-27 04:38_", "2013-02-25 20:26_", "2013-03-27 09:27_", "2013-04-25 19:57p", "2013-05-25 04:25n", "2013-06-23 11:32_", "2013-07-22 18:15_", "2013-08-21 01:45_", "2013-09-19 11:13_", "2013-10-18 23:38n", "2013-11-17 15:16_", "2013-12-17 09:28_", "2014-01-16 04:52_", "2014-02-14 23:53_", "2014-03-16 17:09_", "2014-04-15 07:42t", "2014-05-14 19:16_", "2014-06-13 04:11_", "2014-07-12 11:25_", "2014-08-10 18:09_", "2014-09-09 01:38_", "2014-10-08 10:51t", "2014-11-06 22:23_", "2014-12-06 12:27_", "2015-01-05 04:53_", "2015-02-03 23:09_", "2015-03-05 18:06_", "2015-04-04 12:06p", "2015-05-04 03:42_", "2015-06-02 16:19_", "2015-07-02 02:20_", "2015-07-31 10:43_", "2015-08-29 18:35_", "2015-09-28 02:50t", "2015-10-27 12:05_", "2015-11-25 22:44_", "2015-12-25 11:11_", "2016-01-24 01:46_", "2016-02-22 18:20_", "2016-03-23 12:01n", "2016-04-22 05:24_", "2016-05-21 21:15_", "2016-06-20 11:02_", "2016-07-19 22:57_", "2016-08-18 09:27_", "2016-09-16 19:05n", "2016-10-16 04:23_", "2016-11-14 13:52_", "2016-12-14 00:06_", "2017-01-12 11:34_", "2017-02-11 00:33n", "2017-03-12 14:54_", "2017-04-11 06:08_", "2017-05-10 21:43_", "2017-06-09 13:10_", "2017-07-09 04:07_", "2017-08-07 18:11p", "2017-09-06 07:03_", "2017-10-05 18:40_", "2017-11-04 05:23_", "2017-12-03 15:47_", "2018-01-02 02:24_", "2018-01-31 13:27t", "2018-03-02 00:51_", "2018-03-31 12:37_", "2018-04-30 00:58_", "2018-05-29 14:20_", "2018-06-28 04:53_", "2018-07-27 20:20t", "2018-08-26 11:56_", "2018-09-25 02:53_", "2018-10-24 16:45_", "2018-11-23 05:39_", "2018-12-22 17:49_") + self.firstQuarterArray = ("2008-01-15 19:46", "2008-02-14 03:34", "2008-03-14 10:46", "2008-04-12 18:32", "2008-05-12 03:47", "2008-06-10 15:04", "2008-07-10 04:35", "2008-08-08 20:20", "2008-09-07 14:04", "2008-10-07 09:04", "2008-11-06 04:04", "2008-12-05 21:26", "2009-01-04 11:56", "2009-02-02 23:13", "2009-03-04 07:46", "2009-04-02 14:34", "2009-05-01 20:44", "2009-05-31 03:22", "2009-06-29 11:28", "2009-07-28 22:00", "2009-08-27 11:42", "2009-09-26 04:50", "2009-10-26 00:42", "2009-11-24 21:39", "2009-12-24 17:36", "2010-01-23 10:53", "2010-02-22 00:42", "2010-03-23 11:00", "2010-04-21 18:20", "2010-05-20 23:43", "2010-06-19 04:30", "2010-07-18 10:11", "2010-08-16 18:14", "2010-09-15 05:50", "2010-10-14 21:27", "2010-11-13 16:39", "2010-12-13 13:59", "2011-01-12 11:31", "2011-02-11 07:18", "2011-03-12 23:45", "2011-04-11 12:05", "2011-05-10 20:33", "2011-06-09 02:11", "2011-07-08 06:29", "2011-08-06 11:08", "2011-09-04 17:39", "2011-10-04 03:15", "2011-11-02 16:38", "2011-12-02 09:52", "2012-01-01 06:15", "2012-01-31 04:10", "2012-03-01 01:22", "2012-03-30 19:41", "2012-04-29 09:58", "2012-05-28 20:16", "2012-06-27 03:30", "2012-07-26 08:56", "2012-08-24 13:54", "2012-09-22 19:41", "2012-10-22 03:32", "2012-11-20 14:31", "2012-12-20 05:19", "2013-01-18 23:45", "2013-02-17 20:31", "2013-03-19 17:27", "2013-04-18 12:31", "2013-05-18 04:35", "2013-06-16 17:24", "2013-07-16 03:18", "2013-08-14 10:56", "2013-09-12 17:08", "2013-10-11 23:02", "2013-11-10 05:57", "2013-12-09 15:12", "2014-01-08 03:39", "2014-02-06 19:22", "2014-03-08 13:27", "2014-04-07 08:31", "2014-05-07 03:15", "2014-06-05 20:39", "2014-07-05 11:59", "2014-08-04 00:50", "2014-09-02 11:11", "2014-10-01 19:33", "2014-10-31 02:48", "2014-11-29 10:06", "2014-12-28 18:31", "2015-01-27 04:48", "2015-02-25 17:14", "2015-03-27 07:43", "2015-04-25 23:55", "2015-05-25 17:19", "2015-06-24 11:03", "2015-07-24 04:04", "2015-08-22 19:31", "2015-09-21 08:59", "2015-10-20 20:31", "2015-11-19 06:27", "2015-12-18 15:14", "2016-01-16 23:26", "2016-02-15 07:46", "2016-03-15 17:03", "2016-04-14 03:59", "2016-05-13 17:02", "2016-06-12 08:10", "2016-07-12 00:52", "2016-08-10 18:21", "2016-09-09 11:49", "2016-10-09 04:33", "2016-11-07 19:51", "2016-12-07 09:03", "2017-01-05 19:47", "2017-02-04 04:19", "2017-03-05 11:32", "2017-04-03 18:39", "2017-05-03 02:47", "2017-06-01 12:42", "2017-07-01 00:51", "2017-07-30 15:23", "2017-08-29 08:13", "2017-09-28 02:54", "2017-10-27 22:22", "2017-11-26 17:03", "2017-12-26 09:20", "2018-01-24 22:20", "2018-02-23 08:09", "2018-03-24 15:35", "2018-04-22 21:46", "2018-05-22 03:49", "2018-06-20 10:51", "2018-07-19 19:52", "2018-08-18 07:49", "2018-09-16 23:15", "2018-10-16 18:02", "2018-11-15 14:54", "2018-12-15 11:49") + self.lastQuarterArray = ("2008-01-30 05:03", "2008-02-29 02:18", "2008-03-29 21:47", "2008-04-28 14:12", "2008-05-28 02:57", "2008-06-26 12:10", "2008-07-25 18:42", "2008-08-23 23:50", "2008-09-22 05:04", "2008-10-21 11:55", "2008-11-19 21:31", "2008-12-19 10:29", "2009-01-18 02:46", "2009-02-16 21:37", "2009-03-18 17:47", "2009-04-17 13:36", "2009-05-17 07:26", "2009-06-15 22:15", "2009-07-15 09:53", "2009-08-13 18:55", "2009-09-12 02:16", "2009-10-11 08:56", "2009-11-09 15:56", "2009-12-09 00:13", "2010-01-07 10:40", "2010-02-05 23:49", "2010-03-07 15:42", "2010-04-06 09:37", "2010-05-06 04:15", "2010-06-04 22:13", "2010-07-04 14:35", "2010-08-03 04:59", "2010-09-01 17:22", "2010-10-01 03:52", "2010-10-30 12:46", "2010-11-28 20:36", "2010-12-28 04:18", "2011-01-26 12:57", "2011-02-24 23:26", "2011-03-26 12:07", "2011-04-25 02:47", "2011-05-24 18:52", "2011-06-23 11:48", "2011-07-23 05:02", "2011-08-21 21:55", "2011-09-20 13:39", "2011-10-20 03:30", "2011-11-18 15:09", "2011-12-18 00:48", "2012-01-16 09:08", "2012-02-14 17:04", "2012-03-15 01:25", "2012-04-13 10:50", "2012-05-12 21:47", "2012-06-11 10:41", "2012-07-11 01:48", "2012-08-09 18:55", "2012-09-08 13:15", "2012-10-08 07:33", "2012-11-07 00:36", "2012-12-06 15:32", "2013-01-05 03:58", "2013-02-03 13:56", "2013-03-04 21:53", "2013-04-03 04:37", "2013-05-02 11:14", "2013-05-31 18:58", "2013-06-30 04:54", "2013-07-29 17:43", "2013-08-28 09:35", "2013-09-27 03:56", "2013-10-26 23:41", "2013-11-25 19:28", "2013-12-25 13:48", "2014-01-24 05:19", "2014-02-22 17:15", "2014-03-24 01:46", "2014-04-22 07:52", "2014-05-21 12:59", "2014-06-19 18:39", "2014-07-19 02:08", "2014-08-17 12:26", "2014-09-16 02:05", "2014-10-15 19:12", "2014-11-14 15:16", "2014-12-14 12:51", "2015-01-13 09:47", "2015-02-12 03:50", "2015-03-13 17:48", "2015-04-12 03:44", "2015-05-11 10:36", "2015-06-09 15:42", "2015-07-08 20:24", "2015-08-07 02:03", "2015-09-05 09:54", "2015-10-04 21:06", "2015-11-03 12:24", "2015-12-03 07:40", "2016-01-02 05:30", "2016-02-01 03:28", "2016-03-01 23:11", "2016-03-31 15:17", "2016-04-30 03:29", "2016-05-29 12:12", "2016-06-27 18:19", "2016-07-26 23:00", "2016-08-25 03:41", "2016-09-23 09:56", "2016-10-22 19:14", "2016-11-21 08:33", "2016-12-21 01:56", "2017-01-19 22:14", "2017-02-18 19:33", "2017-03-20 15:58", "2017-04-19 09:57", "2017-05-19 00:33", "2017-06-17 11:33", "2017-07-16 19:26", "2017-08-15 01:15", "2017-09-13 06:25", "2017-10-12 12:25", "2017-11-10 20:37", "2017-12-10 07:51", "2018-01-08 22:25", "2018-02-07 15:54", "2018-03-09 11:20", "2018-04-08 07:18", "2018-05-08 02:09", "2018-06-06 18:32", "2018-07-06 07:51", "2018-08-04 18:18", "2018-09-03 02:37", "2018-10-02 09:45", "2018-10-31 16:40", "2018-11-30 00:19", "2018-12-29 09:34") + + def updateMoonCalculations(self, theDate): + """Generate all Moon data ready for display.""" + SECONDS_PER_DAY = 86400.0 + lastNewMoonSeconds = self.lastNewMoonSecondsAtTime(theDate) + nextNewMoonSeconds = self.nextNewMoonSecondsAtTime(theDate) + lastFullMoonSeconds = self.lastFullMoonSecondsAtTime(theDate) + nextFullMoonSeconds = self.nextFullMoonSecondsAtTime(theDate) + lastQuarterMoonSeconds = self.lastQuarterMoonSecondsAtTime(theDate) + nextQuarterMoonSeconds = self.nextQuarterMoonSecondsAtTime(theDate) + + # Calculate phase percent of Moon based on nearest two data values + if nextFullMoonSeconds <= nextNewMoonSeconds: + if nextQuarterMoonSeconds <= nextFullMoonSeconds: + self.phaseOfMoon = (lastNewMoonSeconds / (lastNewMoonSeconds + nextQuarterMoonSeconds)) * 0.25 + else: + self.phaseOfMoon = (lastQuarterMoonSeconds / (lastQuarterMoonSeconds + nextFullMoonSeconds)) * 0.25 + 0.25 + else: + if nextQuarterMoonSeconds <= nextNewMoonSeconds: + self.phaseOfMoon = (lastFullMoonSeconds / (lastFullMoonSeconds + nextQuarterMoonSeconds)) * 0.25 + 0.5 + else: + self.phaseOfMoon = (lastQuarterMoonSeconds / (lastQuarterMoonSeconds + nextNewMoonSeconds)) * 0.25 + 0.75 + + # Generate interesting human readable values + self.percentOfFullMoon = (math.cos(((self.phaseOfMoon + .5) / .5 * math.pi)) + 1) * .5 + self.julianDate = 2452135 + ((theDate - 997700400.0) / SECONDS_PER_DAY) + self.lunation = self.lunationAtTime(theDate) + dayWithFraction = lastNewMoonSeconds / SECONDS_PER_DAY + self.daysOld = math.floor(dayWithFraction) + self.hoursOld = math.floor((dayWithFraction - self.daysOld) * 24) + self.minutesOld = math.floor((((dayWithFraction - self.daysOld) * 24) - self.hoursOld) * 60) + self.daysUntilNewMoon = nextNewMoonSeconds / SECONDS_PER_DAY + self.nextNewMoonDate = theDate + nextNewMoonSeconds - self.correctForTzAndDst(theDate + nextNewMoonSeconds) + self.daysUntilFullMoon = nextFullMoonSeconds / SECONDS_PER_DAY + self.nextFullMoonDate = theDate + nextFullMoonSeconds - self.correctForTzAndDst(theDate + nextFullMoonSeconds) + + # Eclipse information + self.nextLunarEclipseSeconds = self.nextLunarEclipseSecondsAtTime(theDate) + self.nextSolarEclipseSeconds = self.nextSolarEclipseSecondsAtTime(theDate) + self.lastLunarEclipseSeconds = self.lastLunarEclipseSecondsAtTime(theDate) + self.daysUntilLunarEclipse = self.nextLunarEclipseSeconds / SECONDS_PER_DAY + self.nextLunarEclipseDate = theDate + self.nextLunarEclipseSeconds - self.correctForTzAndDst(theDate + self.nextLunarEclipseSeconds) + self.daysUntilSolarEclipse = self.nextSolarEclipseSeconds / SECONDS_PER_DAY + self.nextSolarEclipseDate = theDate + self.nextSolarEclipseSeconds - self.correctForTzAndDst(theDate + self.nextSolarEclipseSeconds) + + # Selenographic terminator longitude + selenographicTmp = 270 + (self.phaseOfMoon * 360) + if selenographicTmp >= 360: + selenographicTmp -= 360 + if selenographicTmp >= 270: + selenographicTmp -= 360 + elif selenographicTmp >= 180: + selenographicTmp -= 180 + elif selenographicTmp >= 90: + selenographicTmp -= 180 + selenographicTmp = -selenographicTmp + if selenographicTmp < 0: + self.westOrEast = _("west") + else: + self.westOrEast = _("east") + self.selenographicDeg = abs(selenographicTmp) + if self.phaseOfMoon >= .5: + self.riseOrSet = _("Sunset") + else: + self.riseOrSet = _("Sunrise") + + def correctForTzAndDst(self, dateSecOfEvent): + """Time-zone and/or daylight-saving correction for a displayed event (internal data all UTC).""" + if time.daylight == 0 or time.localtime(dateSecOfEvent)[8] == 0: + # Time-zone correction + return time.timezone + else: + # Time-zone & daylight saving correction + return time.altzone + + def moonPhaseName(self, phaseOfMoon): + """Return the moon image name for a given phase value.""" + if phaseOfMoon >= 0 and phaseOfMoon < 0.025: + return _("New Moon") + elif phaseOfMoon >= 0.025 and phaseOfMoon < 0.225: + return _("Waxing Crescent") + elif phaseOfMoon >= 0.225 and phaseOfMoon < 0.275: + return _("First Quarter") + elif phaseOfMoon >= 0.275 and phaseOfMoon < 0.475: + return _("Waxing Gibbous") + elif phaseOfMoon >= 0.475 and phaseOfMoon < 0.525: + return _("Full Moon") + elif phaseOfMoon >= 0.525 and phaseOfMoon < 0.735: + return _("Waning Gibbous") + elif phaseOfMoon >= 0.735 and phaseOfMoon < 0.775: + return _("Last Quarter") + elif phaseOfMoon >= 0.775 and phaseOfMoon < 0.975: + return _("Waning Crescent") + else: + return _("New Moon") + + def nextFullMoonSecondsAtTime(self, now): + """Return seconds to the next Full Moon.""" + for dateString in self.fullMoonArray: + next = time.mktime(time.strptime(dateString[:-1], self.dateFormat)) + if next >= now: + break + return next - now + + def nextNewMoonSecondsAtTime(self, now): + """Return seconds to the next New Moon.""" + for dateString in self.newMoonArray: + next = time.mktime(time.strptime(dateString[:-1], self.dateFormat)) + if next >= now: + break + return next - now + + def nextQuarterMoonSecondsAtTime(self, now): + """Return seconds to the next Quater Moon phase (could be First or Last).""" + for dateString in self.firstQuarterArray: + next1 = time.mktime(time.strptime(dateString, self.dateFormat)) + if next1 >= now: + break + for dateString in self.lastQuarterArray: + next2 = time.mktime(time.strptime(dateString, self.dateFormat)) + if next2 >= now: + break + next = min(next1, next2) + return next - now + + def lastFullMoonSecondsAtTime(self, now): + """Return (positive) seconds since the last Full Moon.""" + for dateString in self.fullMoonArray: + then = time.mktime(time.strptime(dateString[:-1], self.dateFormat)) + if then >= now: + break + last = then + return now - last + + def lastNewMoonSecondsAtTime(self, now): + """Return (positive) seconds since the last New Moon.""" + for dateString in self.newMoonArray: + then = time.mktime(time.strptime(dateString[:-1], self.dateFormat)) + if then >= now: + break + last = then + return now - last + + def lastQuarterMoonSecondsAtTime(self, now): + """Return (positive) seconds to the last Quater Moon phase (could be First or Last).""" + for dateString in self.firstQuarterArray: + then = time.mktime(time.strptime(dateString, self.dateFormat)) + if then >= now: + break + last1 = then + for dateString in self.lastQuarterArray: + then = time.mktime(time.strptime(dateString, self.dateFormat)) + if then >= now: + break + last2 = then + last = max(last1, last2) + return now - last + + def lunationAtTime(self, now): + """Return lunation number, 0 started on Dec 18, 1922, current data set starts as 2008""" + lunation = 1051 + for dateString in self.newMoonArray: + next = time.mktime(time.strptime(dateString[:-1], self.dateFormat)) + if next >= now: + break + lunation += 1 + return lunation + + def nextLunarEclipseSecondsAtTime(self, now): + """Return (positive) seconds to the next Lunar eclipe or -1.""" + for dateString in self.fullMoonArray: + if dateString[-1:] != "_": + next = time.mktime(time.strptime(dateString[:-1], self.dateFormat)) + if next >= now: + return next - now + return -1 + + def lastLunarEclipseSecondsAtTime(self, now): + """Return (positive) seconds to the last Lunar eclipe or -1.""" + last = -1 + for dateString in self.fullMoonArray: + if dateString[-1:] != "_": + then = time.mktime(time.strptime(dateString[:-1], self.dateFormat)) + if then >= now: + break + last = then + if last == -1: + return -1 + else: + return now - last + + def nextSolarEclipseSecondsAtTime(self, now): + """Return (positive) seconds to the next Solar eclipe or -1.""" + for dateString in self.newMoonArray: + if dateString[-1:] != "_": + next = time.mktime(time.strptime(dateString[:-1], self.dateFormat)) + if next >= now: + return next - now + return -1 diff --git a/moon.pyc b/moon.pyc new file mode 100644 index 0000000..0f77dc1 --- /dev/null +++ b/moon.pyc Binary files differ diff --git a/po/POTFILES.in b/po/POTFILES.in new file mode 100644 index 0000000..a7347ef --- /dev/null +++ b/po/POTFILES.in @@ -0,0 +1,3 @@ +encoding: UTF-8 +moon.py + diff --git a/po/moon.pot b/po/moon.pot new file mode 100644 index 0000000..3ba174d --- /dev/null +++ b/po/moon.pot @@ -0,0 +1,183 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2008-03-14 16:02+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: ../moon.py:71 +msgid "Moon" +msgstr "" + +#: ../moon.py:83 +msgid "Toggle Grid View" +msgstr "" + +#: ../moon.py:88 +msgid "Toggle Hemisphere View" +msgstr "" + +#: ../moon.py:93 +msgid "View" +msgstr "" + +#: ../moon.py:190 +msgid "" +"Today's Moon Information\n" +"\n" +msgstr "" + +#: ../moon.py:191 +#, python-format +msgid "" +"Phase:\n" +"%s\n" +"\n" +msgstr "" + +#: ../moon.py:192 +#, python-format +msgid "" +"Julian Date:\n" +"%.2f (astronomical)\n" +"\n" +msgstr "" + +#: ../moon.py:193 +#, python-format +msgid "" +"Age:\n" +"%(days).0f days, %(hours).0f hours, %(minutes).0f minutes\n" +"\n" +msgstr "" + +#: ../moon.py:194 +#, python-format +msgid "" +"Lunation:\n" +"%(phase).2f%% through lunation %(lunation)d\n" +"\n" +msgstr "" + +#: ../moon.py:195 +#, python-format +msgid "" +"Surface Visibility:\n" +"%.0f%% (estimated)\n" +"\n" +msgstr "" + +#: ../moon.py:196 +#, python-format +msgid "" +"Selenographic Terminator Longitude:\n" +"%(deg).1f˚%(westOrEast)s (%(riseOrSet)s)\n" +"\n" +msgstr "" + +#: ../moon.py:197 +#, python-format +msgid "" +"Next Full Moon:\n" +"%(date)s in %(days).0f days\n" +"\n" +msgstr "" + +#: ../moon.py:198 +#, python-format +msgid "" +"Next New Moon:\n" +"%(date)s in %(days).0f days\n" +"\n" +msgstr "" + +#: ../moon.py:199 +#, python-format +msgid "" +"Next Lunar eclipse:\n" +"%(date)s in %(days).0f days\n" +"\n" +msgstr "" + +#: ../moon.py:200 +#, python-format +msgid "" +"Next Solar eclipse:\n" +"%(date)s in %(days).0f days\n" +"\n" +msgstr "" + +#: ../moon.py:282 +msgid "SNWE" +msgstr "" + +#: ../moon.py:285 +msgid "NSEW" +msgstr "" + +#: ../moon.py:338 +msgid "Latitude" +msgstr "" + +#: ../moon.py:340 +msgid "Longitude" +msgstr "" + +#: ../moon.py:437 +msgid "west" +msgstr "" + +#: ../moon.py:439 +msgid "east" +msgstr "" + +#: ../moon.py:442 +msgid "Sunset" +msgstr "" + +#: ../moon.py:444 +msgid "Sunrise" +msgstr "" + +#: ../moon.py:458 ../moon.py:474 +msgid "New Moon" +msgstr "" + +#: ../moon.py:460 +msgid "Waxing Crescent" +msgstr "" + +#: ../moon.py:462 +msgid "First Quarter" +msgstr "" + +#: ../moon.py:464 +msgid "Waxing Gibbous" +msgstr "" + +#: ../moon.py:466 +msgid "Full Moon" +msgstr "" + +#: ../moon.py:468 +msgid "Waning Gibbous" +msgstr "" + +#: ../moon.py:470 +msgid "Last Quarter" +msgstr "" + +#: ../moon.py:472 +msgid "Waning Crescent" +msgstr "" diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..8119ac1 --- /dev/null +++ b/setup.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python +try: + from sugar.activity import bundlebuilder + bundlebuilder.start("moon") +except ImportError: + import os + os.system("find ./ | sed 's,^./,Moon.activity/,g' > MANIFEST") + os.system('rm Moon.xo') + os.chdir('..') + os.system('zip -r Moon.xo Moon.activity') + os.system('mv Moon.xo ./Moon.activity') + os.chdir('Moon.activity') -- cgit v0.9.1