[GRASS-SVN] r53766 - in grass-addons/grass7/gui/wxpython: . timeline
svn_grass at osgeo.org
svn_grass at osgeo.org
Sat Nov 10 11:11:17 PST 2012
Author: annakrat
Date: 2012-11-10 11:11:16 -0800 (Sat, 10 Nov 2012)
New Revision: 53766
Added:
grass-addons/grass7/gui/wxpython/timeline/
grass-addons/grass7/gui/wxpython/timeline/frame.py
grass-addons/grass7/gui/wxpython/timeline/g.gui.timeline.html
grass-addons/grass7/gui/wxpython/timeline/g.gui.timeline.py
grass-addons/grass7/gui/wxpython/timeline/timeline_2D.jpg
Log:
added matplotlib-based tool for visualizing temporal extents of space time datasets
Added: grass-addons/grass7/gui/wxpython/timeline/frame.py
===================================================================
--- grass-addons/grass7/gui/wxpython/timeline/frame.py (rev 0)
+++ grass-addons/grass7/gui/wxpython/timeline/frame.py 2012-11-10 19:11:16 UTC (rev 53766)
@@ -0,0 +1,571 @@
+"""!
+ at package timeline.frame
+
+ at brief Timeline Tool
+
+Classes:
+ - frame::DataCursor
+ - frame::TimelineFrame
+ - frame::LookUp
+
+(C) 2012 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Anna Kratochvilova <kratochanna gmail.com>
+"""
+import os
+import sys
+import pprint
+import random
+import wx
+# The recommended way to use wx with mpl is with the WXAgg
+# backend.
+#
+from math import ceil
+from itertools import cycle
+import numpy as np
+
+import grass.script as grass
+
+try:
+ import matplotlib
+except ImportError:
+ grass.fatal(_("Matplotlib is not installed"))
+
+matplotlib.use('WXAgg')
+from matplotlib.figure import Figure
+import matplotlib.pyplot as plt
+from matplotlib.backends.backend_wxagg import \
+ FigureCanvasWxAgg as FigCanvas, \
+ NavigationToolbar2WxAgg as NavigationToolbar
+
+import matplotlib.dates as mdates
+from matplotlib import cbook
+from mpl_toolkits.mplot3d import Axes3D
+
+if __name__ == '__main__':
+ sys.path.append(os.path.join(os.environ['GISBASE'], "etc", "gui", "wxpython"))
+
+import grass.temporal as tgis
+from core.gcmd import RunCommand, GError, GException
+from gui_core import gselect
+from core import globalvar
+
+ALPHA = 0.5
+COLORS = ['b', 'g', 'r', 'c', 'm', 'y', 'k']
+
+def check_version(*version):
+ versionInstalled = []
+ for i in matplotlib.__version__.split('.'):
+ try:
+ v = int(i)
+ versionInstalled.append(v)
+ except ValueError:
+ versionInstalled.append(0)
+ if versionInstalled < list(version):
+ return False
+ else:
+ return True
+
+class TimelineFrame(wx.Frame):
+ """!The main frame of the application"""
+ def __init__(self, parent):
+ wx.Frame.__init__(self, parent, id = wx.ID_ANY, title = _("Timeline Tool"))
+
+ tgis.init()
+ self.datasets = []
+ self.timeData = {}
+ self._layout()
+ self.temporalType = None
+ self.unit = None
+
+
+ def _layout(self):
+ """!Creates the main panel with all the controls on it:
+ * mpl canvas
+ * mpl navigation toolbar
+ * Control panel for interaction
+ """
+ self.panel = wx.Panel(self)
+
+ # Create the mpl Figure and FigCanvas objects.
+ # 5x4 inches, 100 dots-per-inch
+ #
+ # color = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
+ self.fig = Figure((5.0, 4.0), facecolor = (1, 1, 1))
+ self.canvas = FigCanvas(self.panel, wx.ID_ANY, self.fig)
+ # axes are initialized later
+ self.axes2d = None
+ self.axes3d = None
+
+ # Create the navigation toolbar, tied to the canvas
+ #
+ self.toolbar = NavigationToolbar(self.canvas)
+
+ #
+ # Layout
+ #
+
+ self.vbox = wx.BoxSizer(wx.VERTICAL)
+ self.vbox.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.EXPAND)
+ self.vbox.Add(self.toolbar, 0, wx.EXPAND)
+ self.vbox.AddSpacer(10)
+
+ gridSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+
+ self.datasetSelect = gselect.Select(parent = self.panel, id = wx.ID_ANY,
+ size = globalvar.DIALOG_GSELECT_SIZE,
+ type = 'stds', multiple = True)
+ self.drawButton = wx.Button(self.panel, id = wx.ID_ANY, label = _("Draw"))
+ self.drawButton.Bind(wx.EVT_BUTTON, self.OnRedraw)
+ self.view3dCheck = wx.CheckBox(self.panel, id = wx.ID_ANY, label = _("3D plot of spatio-temporal extents"))
+ self.view3dCheck.Bind(wx.EVT_CHECKBOX, self.OnRedraw)
+ if not check_version(1, 0, 0):
+ self.view3dCheck.SetLabel(_("3D plot of spatio-temporal extents (matplotlib >= 1.0.0)"))
+ self.view3dCheck.Disable()
+
+ gridSizer.Add(wx.StaticText(self.panel, id = wx.ID_ANY, label = _("Select space time dataset(s):")),
+ pos = (0, 0), flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
+ gridSizer.Add(self.datasetSelect, pos = (1, 0), flag = wx.EXPAND)
+ gridSizer.Add(self.drawButton, pos = (1, 1), flag = wx.EXPAND)
+ gridSizer.Add(self.view3dCheck, pos = (2, 0), flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
+
+
+ self.vbox.Add(gridSizer, proportion = 0, flag = wx.EXPAND | wx.ALL, border = 10)
+
+ self.panel.SetSizer(self.vbox)
+ self.vbox.Fit(self)
+
+ def _getData(self, timeseries):
+ """!Load data and read properties"""
+ self.timeData = {}
+ mode = None
+ unit = None
+ for series in timeseries:
+ name = series[0] + '@' + series[1]
+ etype = series[2]
+ sp = tgis.dataset_factory(etype, name)
+ if not sp.is_in_db():
+ GError(self, message = _("Dataset <%s> not found in temporal database") % (name))
+ return
+
+ sp.select()
+
+ self.timeData[name] = {}
+ self.timeData[name]['elementType'] = series[2]
+ self.timeData[name]['temporalType'] = sp.get_temporal_type() # abs/rel
+
+ extent = dict(zip(['north', 'south', 'west', 'east', 'top', 'bottom'], sp.get_spatial_extent()))
+ self.timeData[name]['spatialExtent'] = extent
+ if mode is None:
+ mode = self.timeData[name]['temporalType']
+ elif self.timeData[name]['temporalType'] != mode:
+ GError(parent = self, message = _("Datasets have different temporal type (absolute x relative), which is not allowed."))
+ return
+
+ # check topology
+ maps = sp.get_registered_maps_as_objects()
+ self.timeData[name]['validTopology'] = sp.check_temporal_topology(maps)
+
+ self.timeData[name]['temporalMapType'] = sp.get_map_time() # point/interval
+ self.timeData[name]['unit'] = None # only with relative
+ if self.timeData[name]['temporalType'] == 'relative':
+ start, end, self.timeData[name]['unit'] = sp.get_relative_time()
+ if unit is None:
+ unit = self.timeData[name]['unit']
+ elif self.timeData[name]['unit'] != unit:
+ GError(self, _("Datasets have different time unit which is not allowed."))
+ return
+
+ self.timeData[name]['start_datetime'] = []
+ # self.timeData[name]['start_plot'] = []
+ self.timeData[name]['end_datetime'] = []
+ # self.timeData[name]['end_plot'] = []
+ self.timeData[name]['names'] = []
+ self.timeData[name]['north'] = []
+ self.timeData[name]['south'] = []
+ self.timeData[name]['west'] = []
+ self.timeData[name]['east'] = []
+
+
+ columns = ','.join(['name', 'start_time', 'end_time', 'north', 'south', 'west', 'east'])
+
+ rows = sp.get_registered_maps(columns = columns, where = None, order = 'start_time', dbif = None)
+ if rows is None:
+ rows = []
+ for row in rows:
+ mapName, start, end, north, south, west, east = row
+ self.timeData[name]['start_datetime'].append(start)
+ self.timeData[name]['end_datetime'].append(end)
+ self.timeData[name]['names'].append(mapName)
+ self.timeData[name]['north'].append(north)
+ self.timeData[name]['south'].append(south)
+ self.timeData[name]['west'].append(west)
+ self.timeData[name]['east'].append(east)
+
+ self.temporalType = mode
+ self.unit = unit
+
+ def _draw3dFigure(self):
+ """!Draws 3d view (spatio-temporal extents).
+
+
+ Only for matplotlib versions >= 1.0.0.
+ Earlier versions cannot draw time ticks and alpha
+ and it has a slightly different API.
+ """
+ self.axes3d.clear()
+ self.axes3d.grid(False)
+ # self.axes3d.grid(True)
+ if self.temporalType == 'absolute':
+ if check_version(1, 1, 0):
+ self.axes3d.zaxis_date()
+ convert = mdates.date2num
+ else:
+ convert = lambda x: x
+
+ colors = cycle(COLORS)
+ plots = []
+ for i, name in enumerate(self.datasets):
+ name = name[0] + '@' + name[1]
+ startZ = convert(self.timeData[name]['start_datetime'])
+ mapType = self.timeData[name]['temporalMapType']
+ if mapType == 'interval':
+ dZ = convert(self.timeData[name]['end_datetime']) - startZ
+
+ else:
+ dZ = [0] * len(startZ)
+
+ startX = self.timeData[name]['west']
+ dX = self.timeData[name]['east'] - np.array(startX)
+ startY = self.timeData[name]['south']
+ dY = self.timeData[name]['north'] - np.array(startY)
+
+ color = colors.next()
+ plots.append(self.axes3d.bar3d(startX, startY, startZ, dX, dY, dZ, color = color, alpha = ALPHA))
+
+ params = grass.read_command('g.proj', flags = 'g')
+ params = grass.parse_key_val(params)
+ if 'unit' in params:
+ self.axes3d.set_xlabel(_("X [%s]") % params['unit'])
+ self.axes3d.set_ylabel(_("Y [%s]") % params['unit'])
+ else:
+ self.axes3d.set_xlabel(_("X"))
+ self.axes3d.set_ylabel(_("Y"))
+
+ self.axes3d.set_zlabel(_('Time'))
+ self.axes3d.mouse_init()
+ self.canvas.draw()
+
+
+ def _draw2dFigure(self):
+ """!Draws 2D plot (temporal extents)"""
+ self.axes2d.clear()
+ self.axes2d.grid(True)
+ if self.temporalType == 'absolute':
+ self.axes2d.xaxis_date()
+ self.fig.autofmt_xdate()
+ convert = mdates.date2num
+ else:
+ convert = lambda x: x
+
+ colors = cycle(COLORS)
+
+ yticksNames = []
+ yticksPos = []
+ plots = []
+ lookUp = LookUp(self.timeData)
+ for i, name in enumerate(self.datasets):
+ name = name[0] + '@' + name[1]
+ yticksNames.append(name)
+ yticksPos.append(i)
+ barData = []
+ pointData = []
+ mapType = self.timeData[name]['temporalMapType']
+
+ start = convert(self.timeData[name]['start_datetime'])
+ # TODO: mixed
+ if mapType == 'interval':
+ end = convert(self.timeData[name]['end_datetime'])
+ lookUpData = zip(start, end)
+ duration = end - np.array(start)
+ barData = zip(start, duration)
+ yrange = (i - 0.1, 0.2)
+ lookUp.AddDataset(type = 'bar', yrange = (i - 0.1, i + 0.1), xranges = lookUpData, datasetName = name)
+
+ else:
+ # self.timeData[name]['end_plot'] = None
+ pointData = start
+ lookUp.AddDataset(type = 'point', yrange = i, xranges = pointData, datasetName = name)
+ color = colors.next()
+ if mapType == 'interval':
+ plots.append(self.axes2d.broken_barh(xranges = barData , yrange = (i - 0.1, 0.2),
+ facecolors = color, alpha = ALPHA))
+ else:
+ plots.append(self.axes2d.plot(pointData, [i] * len(pointData), marker = 'o', linestyle = 'None', color = color)[0])
+
+ if self.temporalType == 'absolute':
+ pass
+ # self.axes2d.set_xlabel(_("Time"))
+ else:
+ self.axes2d.set_xlabel(_("Time [%s]") % self.unit)
+
+
+ self.axes2d.set_yticks(yticksPos)
+ self.axes2d.set_yticklabels(yticksNames)
+ self.axes2d.set_ylim(min(yticksPos)-1, max(yticksPos) + 1)
+
+ # adjust xlim
+ xlim = self.axes2d.get_xlim()
+ padding = ceil((xlim[1] - xlim[0]) / 20.)
+ self.axes2d.set_xlim(xlim[0] - padding, xlim[1] + padding)
+
+
+ self.canvas.draw()
+ DataCursor(plots, lookUp, InfoFormat)
+
+ def OnRedraw(self, event):
+ """!Required redrawing."""
+ datasets = self.datasetSelect.GetValue().strip().split(',')
+ if not datasets:
+ return
+ try:
+ datasets = self._checkDatasets(datasets)
+ except GException:
+ GError(parent = self, message = _("Invalid input data"))
+ return
+
+ self.datasets = datasets
+ self._redraw()
+
+ def _redraw(self):
+ """!Readraw data.
+
+ Decides if to draw also 3D and adjusts layout if needed.
+ """
+ self._getData(self.datasets)
+
+ # axes3d are physically removed
+ if not self.axes2d:
+ self.axes2d = self.fig.add_subplot(1, 1, 1)
+ self._draw2dFigure()
+ if check_version(1, 0, 0):
+ if self.view3dCheck.IsChecked():
+ self.axes2d.change_geometry(2,1,1)
+ if not self.axes3d:
+ self.axes3d = self.fig.add_subplot(2, 1, 2, projection = '3d')
+
+ self.axes3d.set_visible(True)
+ self._draw3dFigure()
+ else:
+ if self.axes3d:
+ self.fig.delaxes(self.axes3d)
+ self.axes3d = None
+ self.axes2d.change_geometry(1,1,1)
+ self.canvas.draw()
+
+ if check_version(1, 1, 0):
+ # not working, maybe someone is more lucky
+ try:
+ plt.tight_layout()
+ except:
+ pass
+
+ def _checkDatasets(self, datasets):
+ """!Checks and validates datasets.
+
+ Reports also type of dataset (e.g. 'strds').
+
+ @return (mapName, mapset, type)
+ """
+ validated = []
+ tDict = tgis.tlist_grouped('stds', group_type = True)
+ # nested list with '(map, mapset, etype)' items
+ allDatasets = [[[(map, mapset, etype) for map in maps]
+ for etype, maps in etypesDict.iteritems()]
+ for mapset, etypesDict in tDict.iteritems()]
+ # flatten this list
+ allDatasets = reduce(lambda x,y: x+y, reduce(lambda x,y: x+y, allDatasets))
+
+ for dataset in datasets:
+ errorMsg = _("Space time dataset <%s> not found.") % dataset
+ if dataset.find("@") >= 0:
+ nameShort, mapset = dataset.split('@', 1)
+ indices = [n for n, (mapName, mapsetName, etype) in enumerate(allDatasets)
+ if nameShort == mapName and mapsetName == mapset]
+ else:
+ indices = [n for n, (mapName, mapset, etype) in enumerate(allDatasets)
+ if dataset == mapName]
+
+ if len(indices) == 0:
+ raise GException(errorMsg)
+ elif len(indices) >= 2:
+ dlg = wx.SingleChoiceDialog(self,
+ message = _("Please specify the space time dataset <%s>." % dataset),
+ caption = _("Ambiguous dataset name"),
+ choices = [("%(map)s@%(mapset)s: %(etype)s" % {'map': allDatasets[i][0],
+ 'mapset': allDatasets[i][1],
+ 'etype': allDatasets[i][2]}) for i in indices],
+ style = wx.CHOICEDLG_STYLE | wx.OK)
+ if dlg.ShowModal() == wx.ID_OK:
+ index = dlg.GetSelection()
+ validated.append(allDatasets[indices[index]])
+ else:
+ validated.append(allDatasets[indices[0]])
+
+ return validated
+
+# interface
+
+ def SetDatasets(self, datasets):
+ """!Set data"""
+ if not datasets:
+ return
+ try:
+ datasets = self._checkDatasets(datasets)
+ except GException:
+ GError(parent = self, message = _("Invalid input data"))
+ return
+ self.datasets = datasets
+ self.datasetSelect.SetValue(','.join(map(lambda x: x[0] + '@' + x[1], datasets)))
+ self._redraw()
+
+ def Show3D(self, show):
+ """!Show also 3D if possible"""
+ if check_version(1, 0, 0):
+ self.view3dCheck.SetValue(show)
+
+
+class LookUp:
+ """!Helper class for searching info by coordinates"""
+ def __init__(self, timeData):
+ self.data = {}
+ self.timeData = timeData
+
+ def AddDataset(self, type, yrange, xranges, datasetName):
+ if type == 'bar':
+ self.data[yrange] = {'name': datasetName}
+ for i, (start, end) in enumerate(xranges):
+ self.data[yrange][(start, end)] = i
+ elif type == 'point':
+ self.data[(yrange, yrange)] = {'name': datasetName}
+ for i, start in enumerate(xranges):
+ self.data[(yrange, yrange)][(start, start)] = i
+
+ def GetInformation(self, x, y):
+ keys = None
+ for keyY in self.data.keys():
+ if keyY[0] <= y <= keyY[1]:
+ for keyX in self.data[keyY].keys():
+ if keyX != 'name' and keyX[0] <= x <= keyX[1]:
+ keys = keyY, keyX
+ break
+ if keys:
+ break
+ if not keys:
+ return None
+
+ datasetName = self.data[keys[0]]['name']
+ mapIndex = self.data[keys[0]][keys[1]]
+ return self.timeData, datasetName, mapIndex
+
+
+def InfoFormat(timeData, datasetName, mapIndex):
+ """!Formats information about dataset"""
+ text = []
+ etype = timeData[datasetName]['elementType']
+ if etype == 'strds':
+ text.append(_("Space time raster dataset: %s") % datasetName)
+ elif etype == 'stvds':
+ text.append(_("Space time vector dataset: %s") % datasetName)
+ elif etype == 'str3ds':
+ text.append(_("Space time 3D raster dataset: %s") % datasetName)
+
+ text.append(_("Map name: %s") % timeData[datasetName]['names'][mapIndex])
+ text.append(_("Start time: %s") % timeData[datasetName]['start_datetime'][mapIndex])
+ text.append(_("End time: %s") % timeData[datasetName]['end_datetime'][mapIndex])
+
+ if not timeData[datasetName]['validTopology']:
+ text.append(_("WARNING: invalid topology"))
+
+ return '\n'.join(text)
+
+
+class DataCursor(object):
+ """A simple data cursor widget that displays the x,y location of a
+ matplotlib artist when it is selected.
+
+
+ Source: http://stackoverflow.com/questions/4652439/
+ is-there-a-matplotlib-equivalent-of-matlabs-datacursormode/4674445
+ """
+ def __init__(self, artists, lookUp, formatFunction, tolerance=5, offsets=(-30, 30),
+ display_all=False):
+ """Create the data cursor and connect it to the relevant figure.
+ "artists" is the matplotlib artist or sequence of artists that will be
+ selected.
+ "tolerance" is the radius (in points) that the mouse click must be
+ within to select the artist.
+ "offsets" is a tuple of (x,y) offsets in points from the selected
+ point to the displayed annotation box
+ "display_all" controls whether more than one annotation box will
+ be shown if there are multiple axes. Only one will be shown
+ per-axis, regardless.
+ """
+ self.lookUp = lookUp
+ self.formatFunction = formatFunction
+ self.offsets = offsets
+ self.display_all = display_all
+ if not cbook.iterable(artists):
+ artists = [artists]
+ self.artists = artists
+
+ self.axes = tuple(set(art.axes for art in self.artists))
+ self.figures = tuple(set(ax.figure for ax in self.axes))
+
+ self.annotations = {}
+ for ax in self.axes:
+ self.annotations[ax] = self.annotate(ax)
+ for artist in self.artists:
+ artist.set_picker(tolerance)
+ for fig in self.figures:
+ fig.canvas.mpl_connect('pick_event', self)
+
+ def annotate(self, ax):
+ """Draws and hides the annotation box for the given axis "ax"."""
+ annotation = ax.annotate(self.formatFunction, xy=(0, 0), ha = 'center',
+ xytext=self.offsets, textcoords='offset points', va='bottom',
+ bbox=dict(boxstyle='round,pad=0.5', fc='yellow', alpha=0.7),
+ arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0'),
+ annotation_clip = False, multialignment = 'left')
+ annotation.set_visible(False)
+ return annotation
+
+ def __call__(self, event):
+ """Intended to be called through "mpl_connect"."""
+ # Rather than trying to interpolate, just display the clicked coords
+ # This will only be called if it's within "tolerance", anyway.
+ x, y = event.mouseevent.xdata, event.mouseevent.ydata
+ annotation = self.annotations[event.artist.axes]
+ if x is not None:
+ if not self.display_all:
+ # Hide any other annotation boxes...
+ for ann in self.annotations.values():
+ ann.set_visible(False)
+ # Update the annotation in the current axis..
+ annotation.xy = x, y
+
+ if 'Line2D' in str(type(event.artist)):
+ y = event.artist.get_ydata()[0]
+ xData = event.artist.get_xdata()
+ x = xData[np.argmin(abs(xData - x))]
+
+ info = self.lookUp.GetInformation(x, y)
+ if not info:
+ return
+ text = self.formatFunction(*info)
+ annotation.set_text(text)
+ annotation.set_visible(True)
+ event.canvas.draw()
Added: grass-addons/grass7/gui/wxpython/timeline/g.gui.timeline.html
===================================================================
--- grass-addons/grass7/gui/wxpython/timeline/g.gui.timeline.html (rev 0)
+++ grass-addons/grass7/gui/wxpython/timeline/g.gui.timeline.html 2012-11-10 19:11:16 UTC (rev 53766)
@@ -0,0 +1,39 @@
+<!-- meta page description: wxGUI Timeline Tool -->
+<!-- meta page index: wxGUI -->
+<h2>DESCRIPTION</h2>
+
+The <b>Timeline Tool</b> is a <em><a href="wxGUI.html">wxGUI</a></em> component
+which allows the user to compare temporal datasets' extents in a plot.
+It requires python plotting library <a href="http://matplotlib.org/">Matplotlib</a>.
+
+
+<p>
+Features:
+<ul>
+ <li>supports temporal datasets with interval/point and absolute/relative time</li>
+ <li>2D plot - temporal extent</li>
+ <li>3D plot - spatio-temporal extent (matplotlib >= 1.0.0)</li>
+ <li>pop-up annotations with basic metadata</li>
+</ul>
+
+<center>
+<br>
+<img src="timeline_2D.jpg" border="1" alt="Timeline Tool screenshot">
+<br>
+
+</center>
+
+<h2>SEE ALSO</h2>
+
+<em>
+ <a href="wxGUI.html">wxGUI</a><br>
+ <a href="wxGUI.Components.html">wxGUI components</a>
+</em>
+
+<h2>AUTHOR</h2>
+
+Anna Kratochvilova,
+<a href="http://www.cvut.cz">Czech Technical University in Prague</a>, Czech Republic
+
+<p>
+<i>$Date: 2012-03-07 13:21:57 +0100 (Wed, 07 Mar 2012) $</i>
Added: grass-addons/grass7/gui/wxpython/timeline/g.gui.timeline.py
===================================================================
--- grass-addons/grass7/gui/wxpython/timeline/g.gui.timeline.py (rev 0)
+++ grass-addons/grass7/gui/wxpython/timeline/g.gui.timeline.py 2012-11-10 19:11:16 UTC (rev 53766)
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+############################################################################
+#
+# MODULE: g.gui.timeline.py
+# AUTHOR(S): Anna Kratochvilova
+# PURPOSE: Timeline Tool is a wxGUI component (based on matplotlib)
+# which allows the user to compare temporal datasets' extents.
+# COPYRIGHT: (C) 2012 by Anna Kratochvilova, and the GRASS Development Team
+#
+# 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.
+#
+############################################################################
+
+#%module
+#% description: Allows to compare temporal datasets by displaying their temporal extents in a plot.
+#%end
+#%option G_OPT_STDS_INPUTS
+#% required: no
+#%end
+#%flag
+#% key: 3
+#% description: Show also 3D plot of spatio-temporal extents
+#%end
+
+
+import os
+import sys
+
+import wx
+import gettext
+
+import grass.script as grass
+
+if __name__ == '__main__':
+ sys.path.append(os.path.join(os.environ['GISBASE'], "etc", "gui", "wxpython"))
+
+from timeline.frame import TimelineFrame
+
+def main():
+ gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
+
+ options, flags = grass.parser()
+
+ datasets = options['inputs'].strip().split(',')
+ datasets = [data for data in datasets if data]
+ view3d = flags['3']
+
+ app = wx.PySimpleApp()
+ frame = TimelineFrame(None)
+ frame.SetDatasets(datasets)
+ frame.Show3D(view3d)
+ frame.Show()
+ app.MainLoop()
+
+
+if __name__ == '__main__':
+ main()
+
Property changes on: grass-addons/grass7/gui/wxpython/timeline/g.gui.timeline.py
___________________________________________________________________
Added: svn:executable
+ *
Added: grass-addons/grass7/gui/wxpython/timeline/timeline_2D.jpg
===================================================================
(Binary files differ)
Property changes on: grass-addons/grass7/gui/wxpython/timeline/timeline_2D.jpg
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
More information about the grass-commit
mailing list