Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGary Martin <gary@garycmartin.com>2008-03-15 02:28:17 (GMT)
committer Gary Martin <gary@garycmartin.com>2008-03-15 02:28:17 (GMT)
commit9dedbff8a29b7757782379b98891eb7600e33c9a (patch)
tree449453d7a7f9315b6d0dcf6457132bb5f7043641
Initial import
-rw-r--r--MANIFEST16
-rw-r--r--Moon.xobin0 -> 95669 bytes
-rw-r--r--activity/activity.info7
-rw-r--r--activity/moon-icon.svg28
-rw-r--r--icons/grid-icon.svg11
-rw-r--r--icons/hemi-icon.svg23
-rw-r--r--moon.jpgbin0 -> 68425 bytes
-rwxr-xr-xmoon.py600
-rw-r--r--moon.pycbin0 -> 34361 bytes
-rw-r--r--po/POTFILES.in3
-rw-r--r--po/moon.pot183
-rwxr-xr-xsetup.py12
12 files changed, 883 insertions, 0 deletions
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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
+ <!ENTITY fill_color "#FFFFFF">
+ <!ENTITY stroke_color "#000000">
+]>
+<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50">
+ <defs>
+ <mask id="Mask" maskUnits="userSpaceOnUse"
+ x="0" y="0" width="50" height="50">
+ <path d="M33 5 C15,0 15,50 33,45 C-5,55 -5,-5 33 5 z" style="fill:#FFFFFF;;stroke:none;;stroke-width:3.5"/>
+ <circle cx="20" cy="33" r="4" style="fill:none;;stroke:#000000;;stroke-width:1.5"/>
+ <circle cx="20" cy="19" r="3" style="fill:none;;stroke:#000000;;stroke-width:1.5"/>
+ </mask>
+ </defs>
+ <g transform="translate(-9.2,15.8)">
+ <g transform="rotate(-30)">
+ <circle cx="25" cy="25" r="21" style="fill:&fill_color;;stroke:&stroke_color;;stroke-width:3.0"/>
+
+ <path d="M33 5 C15,0 15,50 33,45 C-5,55 -5,-5 33 5 z" style="fill:&stroke_color;;stroke:none;;stroke-width:3.5" mask="url(#Mask)"/>
+ <circle cx="20" cy="33" r="4" style="fill:none;;stroke:&fill_color;;stroke-width:1.5"/>
+ <circle cx="20" cy="19" r="3" style="fill:none;;stroke:&fill_color;;stroke-width:1.5"/>
+
+ <circle cx="27" cy="12" r="2" style="fill:none;;stroke:&stroke_color;;stroke-width:1"/>
+ <circle cx="26" cy="26" r="2.5" style="fill:none;;stroke:&stroke_color;;stroke-width:1.25"/>
+ <circle cx="28" cy="38" r="1.5" style="fill:none;;stroke:&stroke_color;;stroke-width:.75"/>
+ </g>
+ </g>
+</svg>
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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
+ <!ENTITY fill_color "#000000">
+ <!ENTITY stroke_color "#FFFFFF">
+]>
+<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50">
+ <line x1="15" y1="4" x2="15" y2="46" stroke-width="3.0" stroke="&stroke_color;" />
+ <line x1="35" y1="4" x2="35" y2="46" stroke-width="3.0" stroke="&stroke_color;" />
+ <line x1="4" y1="15" x2="46" y2="15" stroke-width="3.0" stroke="&stroke_color;" />
+ <line x1="4" y1="35" x2="46" y2="35" stroke-width="3.0" stroke="&stroke_color;" />
+</svg>
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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
+ <!ENTITY fill_color "#000000">
+ <!ENTITY stroke_color "#FFFFFF">
+]>
+<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50">
+ <defs>
+ <mask id="Mask" maskUnits="userSpaceOnUse"
+ x="0" y="0" width="50" height="50">
+ <path d="M33 5 C15,0 15,50 33,45 C-5,55 -5,-5 33 5 z" style="fill:#FFFFFF;;stroke:none;;stroke-width:3.5"/>
+ <circle cx="20" cy="33" r="4" style="fill:none;;stroke:#000000;;stroke-width:1.5"/>
+ <circle cx="20" cy="19" r="3" style="fill:none;;stroke:#000000;;stroke-width:1.5"/>
+ </mask>
+ <marker id="Triangle" viewBox="0 0 10 10" refX="0" refY="5" markerUnits="strokeWidth" markerWidth="3.5" markerHeight="3.5" orient="auto" fill="&stroke_color;" stroke="&stroke_color;">
+ <path d="M 0 0 L 10 5 L 0 10 z"/>
+ </marker>
+ </defs>
+ <path d="M33 5 C15,0 15,50 33,45 C-5,55 -5,-5 33 5 z" style="fill:&stroke_color;;stroke:none;;stroke-width:3.5" mask="url(#Mask)"/>
+ <circle cx="27" cy="12" r="2" style="fill:none;;stroke:&stroke_color;;stroke-width:1"/>
+ <circle cx="26" cy="26" r="2.5" style="fill:none;;stroke:&stroke_color;;stroke-width:1.25"/>
+ <path d="M36 10 C52,10 52,40 36,40" fill="none" stroke="&stroke_color;" stroke-width="3.0"/>
+ <path d="M 36 40 l0 3 l-6 -3 l6 -3 z" fill="&stroke_color;" stroke="&stroke_color;" stroke-width="2.5"/>
+</svg>
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 <EMAIL@ADDRESS>, 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 <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\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')