Просмотр исходного кода

wxGUI/animation: vector rendering (patch by Soeren Gebbert)

git-svn-id: https://svn.osgeo.org/grass/grass/trunk@55829 15284696-431f-4ddb-bdfa-cd5b030d7da7
Anna Petrášová 12 лет назад
Родитель
Сommit
527bc2b466

+ 20 - 15
gui/wxpython/animation/controller.py

@@ -252,28 +252,32 @@ class AnimationController(wx.EvtHandler):
         self.animationData.append(animData)
         self.animationData.append(animData)
         self._setAnimations()
         self._setAnimations()
 
 
-    def SetAnimations(self, raster = None, strds = None):
+    def SetAnimations(self, inputs=None, dataType=None):
         """!Set animation data directly.
         """!Set animation data directly.
 
 
         @param raster list of lists of raster maps or None
         @param raster list of lists of raster maps or None
         @param strds list of strds or None
         @param strds list of strds or None
+        @param inputs list of lists of raster maps or vector maps, 
+               or a space time raster or vector dataset
+        @param dataType The type of the input data must be one of 'rast', 'vect', 'strds' or 'strds'
         """
         """
         try:
         try:
             animationData = []
             animationData = []
             for i in range(len(self.animations)):
             for i in range(len(self.animations)):
-                if raster is not None and type(raster[i]) == list and raster[i]:
-                    anim = AnimationData()
-                    anim.SetDefaultValues(i, i)
-                    anim.inputMapType = 'rast'
-                    anim.inputData = ','.join(raster[i])
-                    animationData.append(anim)
-
-                elif strds is not None and strds[i]:
-                    anim = AnimationData()
-                    anim.SetDefaultValues(i, i)
-                    anim.inputMapType = 'strds'
-                    anim.inputData = strds[i]
-                    animationData.append(anim)
+                if inputs is not None and inputs[i]:
+                    if dataType == 'rast' or dataType == 'vect':
+                        if type(inputs[i]) == list:
+                            anim = AnimationData()
+                            anim.SetDefaultValues(i, i)
+                            anim.inputMapType = dataType
+                            anim.inputData = ','.join(inputs[i])
+                            animationData.append(anim)
+                    elif dataType == 'strds' or dataType == 'stvds':
+                        anim = AnimationData()
+                        anim.SetDefaultValues(i, i)
+                        anim.inputMapType = dataType
+                        anim.inputData = inputs[i]
+                        animationData.append(anim)
 
 
         except (GException, ValueError, IOError) as e:
         except (GException, ValueError, IOError) as e:
             GError(parent = self.frame, message = str(e),
             GError(parent = self.frame, message = str(e),
@@ -301,6 +305,7 @@ class AnimationController(wx.EvtHandler):
 
 
         self._updateSlider(timeLabels = timeLabels)
         self._updateSlider(timeLabels = timeLabels)
         self._updateAnimations(activeIndices = indices, mapNamesDict = mapNamesDict)
         self._updateAnimations(activeIndices = indices, mapNamesDict = mapNamesDict)
+        wx.Yield()
         self._updateBitmapData()
         self._updateBitmapData()
         # if running:
         # if running:
         #     self.PauseAnimation(False)
         #     self.PauseAnimation(False)
@@ -371,7 +376,7 @@ class AnimationController(wx.EvtHandler):
 
 
     def _load2DData(self, animationData):
     def _load2DData(self, animationData):
         prov = self.bitmapProviders[animationData.windowIndex]
         prov = self.bitmapProviders[animationData.windowIndex]
-        prov.SetData(datasource = animationData.mapData)
+        prov.SetData(datasource = animationData.mapData, dataType=animationData.inputMapType)
 
 
         self.bitmapProviders[animationData.windowIndex].Load()
         self.bitmapProviders[animationData.windowIndex].Load()
 
 

+ 7 - 6
gui/wxpython/animation/dialogs.py

@@ -416,10 +416,8 @@ class InputDialog(wx.Dialog):
 
 
     def _setMapTypes(self, view2d = True):
     def _setMapTypes(self, view2d = True):
         index = 0
         index = 0
-        if view2d:
-            inputTypes = self.animationData.inputMapTypes[::2]
-        else:
-            inputTypes = self.animationData.inputMapTypes
+
+        inputTypes = self.animationData.inputMapTypes
         self.dataChoice.Clear()
         self.dataChoice.Clear()
         for i, (itype, itypeName) in enumerate(inputTypes):
         for i, (itype, itypeName) in enumerate(inputTypes):
             self.dataChoice.Append(itypeName, clientData = itype)
             self.dataChoice.Append(itypeName, clientData = itype)
@@ -696,8 +694,11 @@ class AnimationData(object):
             if self.inputMapType == 'strds':
             if self.inputMapType == 'strds':
                 sp = tgis.SpaceTimeRasterDataset(ident = timeseries)
                 sp = tgis.SpaceTimeRasterDataset(ident = timeseries)
             elif self.inputMapType == 'stvds':
             elif self.inputMapType == 'stvds':
-                sp = tgis.SpaceTimeRasterDataset(ident = timeseries)
-            # else ?
+                sp = tgis.SpaceTimeVectorDataset(ident = timeseries)
+                
+            if sp.is_in_db() == False:
+                raise GException(_("Space time dataset <%s> not found.") % timeseries)
+        
             sp.select()
             sp.select()
             rows = sp.get_registered_maps(columns = "id", where = None, order = "start_time", dbif = None)
             rows = sp.get_registered_maps(columns = "id", where = None, order = "start_time", dbif = None)
             timeseriesMaps = []
             timeseriesMaps = []

+ 10 - 6
gui/wxpython/animation/frame.py

@@ -143,8 +143,14 @@ class AnimationFrame(wx.Frame):
                               BestSize((self.toolbars['miscToolbar'].GetBestSize())))
                               BestSize((self.toolbars['miscToolbar'].GetBestSize())))
             self.controller.SetAnimationToolbar(self.toolbars['miscToolbar'])
             self.controller.SetAnimationToolbar(self.toolbars['miscToolbar'])
 
 
-    def SetAnimations(self, raster = None, strds = None):
-        self.controller.SetAnimations(raster, strds)
+    def SetAnimations(self, inputs=None, dataType=None):
+        """!Set animation data
+        
+        @param inputs list of lists of raster maps or vector maps,  
+               or a space time raster or vector dataset 
+  	  @param dataType The type of the input data must be one of 'rast', 'vect', 'strds' or 'strds' 
+        """ 
+        self.controller.SetAnimations(inputs, dataType) 
 
 
     def OnAddAnimation(self, event):
     def OnAddAnimation(self, event):
         self.controller.AddAnimation()
         self.controller.AddAnimation()
@@ -291,8 +297,6 @@ class AnimationsPanel(wx.Panel):
         return self.mainSizer.IsShown(self.windows[index])
         return self.mainSizer.IsShown(self.windows[index])
 
 
 
 
-
-
 class AnimationSliderBase(wx.Panel):
 class AnimationSliderBase(wx.Panel):
     def __init__(self, parent):
     def __init__(self, parent):
         wx.Panel.__init__(self, parent = parent, id = wx.ID_ANY)
         wx.Panel.__init__(self, parent = parent, id = wx.ID_ANY)
@@ -517,8 +521,8 @@ def test():
     app = wx.PySimpleApp()
     app = wx.PySimpleApp()
     wx.InitAllImageHandlers()
     wx.InitAllImageHandlers()
 
 
-    frame = AnimationFrame(parent = None)
-    frame.SetAnimations(raster = None, strds = None)
+    frame = AnimationFrame(parent=None)
+    frame.SetAnimations(inputs=None, dataType=None)
 
 
     frame.Show()
     frame.Show()
     app.MainLoop()
     app.MainLoop()

+ 42 - 14
gui/wxpython/animation/g.gui.animation.py

@@ -3,7 +3,7 @@
 #
 #
 # MODULE:    Animation
 # MODULE:    Animation
 # AUTHOR(S): Anna Kratochvilova
 # AUTHOR(S): Anna Kratochvilova
-# PURPOSE:   Tool for animating a series of GRASS raster maps
+# PURPOSE:   Tool for animating a series of GRASS raster and vector maps
 #            or a space time raster dataset
 #            or a space time raster dataset
 # COPYRIGHT: (C) 2012 by Anna Kratochvilova, and the GRASS Development Team
 # COPYRIGHT: (C) 2012 by Anna Kratochvilova, and the GRASS Development Team
 #
 #
@@ -20,7 +20,7 @@
 ############################################################################
 ############################################################################
 
 
 #%module
 #%module
-#% description: Tool for animating a series of raster maps or a space time raster dataset.
+#% description: Tool for animating a series of raster and vector maps or a space time raster ot vector dataset.
 #% keywords: general
 #% keywords: general
 #% keywords: gui
 #% keywords: gui
 #% keywords: display
 #% keywords: display
@@ -31,13 +31,24 @@
 #% required: no
 #% required: no
 #% guisection: Input
 #% guisection: Input
 #%end
 #%end
+#%option G_OPT_V_INPUTS
+#% key: vect
+#% label: Vector maps to animate
+#% required: no
+#% guisection: Input
+#%end
 #%option G_OPT_STRDS_INPUT
 #%option G_OPT_STRDS_INPUT
 #% key: strds
 #% key: strds
 #% description: Space time raster dataset to animate
 #% description: Space time raster dataset to animate
 #% required: no
 #% required: no
 #% guisection: Input
 #% guisection: Input
 #%end
 #%end
-
+#%option G_OPT_STVDS_INPUT
+#% key: stvds
+#% description: Space time vector dataset to animate
+#% required: no
+#% guisection: Input
+#%end
 
 
 import os
 import os
 import sys
 import sys
@@ -61,20 +72,38 @@ def main():
     options, flags = grass.parser()
     options, flags = grass.parser()
 
 
     rast = options['rast']
     rast = options['rast']
+    vect = options['vect']
     strds = options['strds']
     strds = options['strds']
+    stvds = options['stvds']
+    
+    dataType=None
+    inputs=None
+    numInputs=0
+    
+    if rast:
+        numInputs += 1
+    if vect:
+        numInputs += 1
+    if strds:
+        numInputs += 1
+    if stvds:
+        numInputs += 1
 
 
-    if rast and strds:
-        grass.fatal(_("Options 'rast' and 'strds' are mutually exclusive."))
+    if  numInputs > 1:
+        grass.fatal(_("Options 'rast', 'vect', 'strds' and 'stvds' are mutually exclusive."))
 
 
     if rast:
     if rast:
-        rast = [rast.split(',')] + [None] * (MAX_COUNT - 1)
-    else:
-        rast = None
-
+        inputs = [rast.split(',')] + [None] * (MAX_COUNT - 1)
+        dataType='rast'
+    if vect:
+        inputs = [vect.split(',')] + [None] * (MAX_COUNT - 1)
+        dataType='vect'
     if strds:
     if strds:
-        strds = [strds] + [None] * (MAX_COUNT - 1)
-    else:
-        strds = None
+        inputs = [strds] + [None] * (MAX_COUNT - 1)
+        dataType='strds'
+    if stvds:
+        inputs = [stvds] + [None] * (MAX_COUNT - 1)
+        dataType='stvds'
 
 
     app = wx.PySimpleApp()
     app = wx.PySimpleApp()
     if not CheckWxVersion([2, 9]):
     if not CheckWxVersion([2, 9]):
@@ -83,10 +112,9 @@ def main():
     frame = AnimationFrame(parent = None)
     frame = AnimationFrame(parent = None)
     frame.CentreOnScreen()
     frame.CentreOnScreen()
     frame.Show()
     frame.Show()
-    frame.SetAnimations(raster = rast, strds = strds)
+    frame.SetAnimations(inputs = inputs, dataType = dataType)
     app.MainLoop()
     app.MainLoop()
 
 
-
 if __name__ == '__main__':
 if __name__ == '__main__':
     main()
     main()
 
 

+ 37 - 21
gui/wxpython/animation/mapwindow.py

@@ -23,6 +23,7 @@ import tempfile
 import grass.script as grass
 import grass.script as grass
 from core.gcmd import RunCommand
 from core.gcmd import RunCommand
 from core.debug import Debug
 from core.debug import Debug
+from core.settings import UserSettings
 
 
 from grass.pydispatch.signal import Signal
 from grass.pydispatch.signal import Signal
 
 
@@ -170,9 +171,9 @@ class BitmapProvider(object):
                 suffix = '', nvizRegion = None):
                 suffix = '', nvizRegion = None):
         """!Sets data.
         """!Sets data.
 
 
-        @param datasource data to load (raster maps, m.nviz.image commands)
+        @param datasource data to load (raster maps, vector maps, m.nviz.image commands)
         @param dataNames data labels (keys)
         @param dataNames data labels (keys)
-        @param dataType 'rast', 'nviz'
+        @param dataType 'rast', 'vect', 'nviz'
         @param nvizRegion region which must be set for m.nviz.image
         @param nvizRegion region which must be set for m.nviz.image
         """
         """
         self.datasource = datasource
         self.datasource = datasource
@@ -223,17 +224,23 @@ class BitmapProvider(object):
         dc.SelectObject(wx.NullBitmap)
         dc.SelectObject(wx.NullBitmap)
         return bitmap
         return bitmap
 
 
-    def Load(self, force = False):
+    def Load(self, force = False, nprocs=4):
         """!Loads data.
         """!Loads data.
 
 
         Shows progress dialog.
         Shows progress dialog.
 
 
         @param force if True reload all data, otherwise only missing data
         @param force if True reload all data, otherwise only missing data
+        @param imageWidth width of the image to render with d.rast or d.vect
+        @param imageHeight height of the image to render with d.rast or d.vect
+        @param nprocs number of procs to be used for rendering
         """
         """
+        if nprocs <= 0:
+            nprocs = 1
+
         count, maxLength = self._dryLoad(rasters = self.datasource,
         count, maxLength = self._dryLoad(rasters = self.datasource,
                                          names = self.dataNames, force = force)
                                          names = self.dataNames, force = force)
         progress = None
         progress = None
-        if self.dataType == 'rast' and count > 5 or \
+        if self.dataType in ('rast', 'vect', 'strds', 'stvds') and count > 5 or \
             self.dataType == 'nviz':
             self.dataType == 'nviz':
             progress = wx.ProgressDialog(title = "Loading data",
             progress = wx.ProgressDialog(title = "Loading data",
                                          message = " " * (maxLength + 20), # ?
                                          message = " " * (maxLength + 20), # ?
@@ -245,9 +252,10 @@ class BitmapProvider(object):
         else:
         else:
             updateFunction = None
             updateFunction = None
 
 
-        if self.dataType == 'rast' or self.dataType == 'vect':
+        if self.dataType in ('rast', 'vect', 'strds', 'stvds'):
             self._loadMaps(mapType=self.dataType, maps = self.datasource, names = self.dataNames,
             self._loadMaps(mapType=self.dataType, maps = self.datasource, names = self.dataNames,
-                             force = force, updateFunction = updateFunction)
+                           force = force, updateFunction = updateFunction,
+                           imageWidth=self.imageWidth, imageHeight=self.imageHeight, nprocs=nprocs)
         elif self.dataType == 'nviz':
         elif self.dataType == 'nviz':
             self._load3D(commands = self.datasource, region = self.nvizRegion, names = self.dataNames,
             self._load3D(commands = self.datasource, region = self.nvizRegion, names = self.dataNames,
                          force = force, updateFunction = updateFunction)
                          force = force, updateFunction = updateFunction)
@@ -280,7 +288,8 @@ class BitmapProvider(object):
         return count, maxLength
         return count, maxLength
 
 
     
     
-    def _loadMaps(self, mapType, maps, names, force, updateFunction):
+    def _loadMaps(self, mapType, maps, names, force, updateFunction,
+                  imageWidth, imageHeight, nprocs):
         """!Loads rasters/vectors (also from temporal dataset).
         """!Loads rasters/vectors (also from temporal dataset).
 
 
         Uses d.rast/d.vect and multiprocessing for parallel rendering
         Uses d.rast/d.vect and multiprocessing for parallel rendering
@@ -290,6 +299,9 @@ class BitmapProvider(object):
         @param names names used as keys for bitmaps
         @param names names used as keys for bitmaps
         @param force load everything even though it is already there
         @param force load everything even though it is already there
         @param updateFunction function called for updating progress dialog
         @param updateFunction function called for updating progress dialog
+        @param imageWidth width of the image to render with d.rast or d.vect
+        @param imageHeight height of the image to render with d.rast or d.vect
+        @param nprocs number of procs to be used for rendering
         """
         """
 
 
         count = 0
         count = 0
@@ -304,7 +316,7 @@ class BitmapProvider(object):
 
 
         # create no data bitmap
         # create no data bitmap
         if None not in self.bitmapPool or force:
         if None not in self.bitmapPool or force:
-            self.bitmapPool[None] = self._createNoDataBitmap(self.imageWidth, self.imageHeight)
+            self.bitmapPool[None] = self._createNoDataBitmap(imageWidth, imageHeight)
 
 
         for mapname, name in zip(maps, names):
         for mapname, name in zip(maps, names):
             count += 1
             count += 1
@@ -315,7 +327,7 @@ class BitmapProvider(object):
             # Queue object for interprocess communication
             # Queue object for interprocess communication
             q = Queue()
             q = Queue()
             # The separate render process
             # The separate render process
-            p = Process(target=mapRenderProcess, args=(mapType, mapname, self.imageWidth, self.imageHeight, q))
+            p = Process(target=mapRenderProcess, args=(mapType, mapname, imageWidth, imageHeight, q))
             p.start()
             p.start()
 
 
             queue_list.append(q)
             queue_list.append(q)
@@ -325,7 +337,7 @@ class BitmapProvider(object):
             proc_count += 1
             proc_count += 1
 
 
             # Wait for all running processes and read/store the created images
             # Wait for all running processes and read/store the created images
-            if proc_count == self.nprocs or count == mapNum:
+            if proc_count == nprocs or count == mapNum:
                 for i in range(len(name_list)):
                 for i in range(len(name_list)):
                     proc_list[i].join()
                     proc_list[i].join()
                     filename = queue_list[i].get()
                     filename = queue_list[i].get()
@@ -333,7 +345,7 @@ class BitmapProvider(object):
                     # Unfortunately the png files must be read here, 
                     # Unfortunately the png files must be read here, 
                     # since the swig wx objects can not be serialized by the Queue object :(
                     # since the swig wx objects can not be serialized by the Queue object :(
                     if filename == None:
                     if filename == None:
-                        self.bitmapPool[name_list[i]] = wx.EmptyBitmap(self.imageWidth, self.imageHeight)
+                        self.bitmapPool[name_list[i]] = wx.EmptyBitmap(imageWidth, imageHeight)
                     else:
                     else:
                         self.bitmapPool[name_list[i]] = wx.BitmapFromImage(wx.Image(filename))
                         self.bitmapPool[name_list[i]] = wx.BitmapFromImage(wx.Image(filename))
                         os.remove(filename)
                         os.remove(filename)
@@ -396,11 +408,11 @@ def mapRenderProcess(mapType, mapname, width, height, fileQueue):
     """!Render raster or vector files as png image and write the 
     """!Render raster or vector files as png image and write the 
        resulting png filename in the provided file queue
        resulting png filename in the provided file queue
     
     
-        @param mapType Must be "rast" or "vect"
-        @param mapname raster or vector map name to be rendered
-        @param width Width of the resulting image
-        @param height Height of the resulting image
-        @param fileQueue The inter process communication queue storing the file name of the image
+    @param mapType Must be "rast" or "vect"
+    @param mapname raster or vector map name to be rendered
+    @param width Width of the resulting image
+    @param height Height of the resulting image
+    @param fileQueue The inter process communication queue storing the file name of the image
     """
     """
     
     
     # temporary file, we use python here to avoid calling g.tempfile for each render process
     # temporary file, we use python here to avoid calling g.tempfile for each render process
@@ -410,17 +422,21 @@ def mapRenderProcess(mapType, mapname, width, height, fileQueue):
     # Set the environment variables for this process
     # Set the environment variables for this process
     os.environ['GRASS_WIDTH'] = str(width) 
     os.environ['GRASS_WIDTH'] = str(width) 
     os.environ['GRASS_HEIGHT'] = str(height)
     os.environ['GRASS_HEIGHT'] = str(height)
-    os.environ['GRASS_RENDER_IMMEDIATE'] = "png"
+    driver = UserSettings.Get(group = 'display', key = 'driver', subkey = 'type')
+    os.environ['GRASS_RENDER_IMMEDIATE'] = driver
     os.environ['GRASS_TRUECOLOR'] = "1" 
     os.environ['GRASS_TRUECOLOR'] = "1" 
     os.environ['GRASS_TRANSPARENT'] = "1"
     os.environ['GRASS_TRANSPARENT'] = "1"
     os.environ['GRASS_PNGFILE'] = str(filename)
     os.environ['GRASS_PNGFILE'] = str(filename)
-    
-    if mapType == "rast":
+
+    if mapType in ('rast', 'strds'):
         Debug.msg(1, "Render raster image " + str(filename))
         Debug.msg(1, "Render raster image " + str(filename))
         returncode, stdout, messages = read2_command('d.rast', map = mapname)
         returncode, stdout, messages = read2_command('d.rast', map = mapname)
-    else:
+    elif mapType in ('vect', 'stvds'):
         Debug.msg(1, "Render vector image " + str(filename))
         Debug.msg(1, "Render vector image " + str(filename))
         returncode, stdout, messages = read2_command('d.vect', map = mapname)
         returncode, stdout, messages = read2_command('d.vect', map = mapname)
+    else:
+        returncode = 1
+        return
 
 
     if returncode != 0:
     if returncode != 0:
         fileQueue.put(None)
         fileQueue.put(None)
@@ -444,7 +460,7 @@ class BitmapPool():
         return key in self.bitmaps
         return key in self.bitmaps
 
 
     def Clear(self, usedKeys):
     def Clear(self, usedKeys):
-        """!Removes all bitmaps which are currentlu not used.
+        """!Removes all bitmaps which are currently not used.
 
 
         @param usedKeys keys which are currently used
         @param usedKeys keys which are currently used
         """
         """

+ 1 - 1
gui/wxpython/animation/temporal_manager.py

@@ -72,7 +72,6 @@ class TemporalManager(object):
         """
         """
         self._gatherInformation(timeseries, etype, self.timeseriesList, self.timeseriesInfo)
         self._gatherInformation(timeseries, etype, self.timeseriesList, self.timeseriesInfo)
 
 
-
     def EvaluateInputData(self):
     def EvaluateInputData(self):
         """!Checks if all timeseries are compatible (raises GException).
         """!Checks if all timeseries are compatible (raises GException).
 
 
@@ -311,6 +310,7 @@ class TemporalManager(object):
         sp.select()
         sp.select()
         # Get ordered map list
         # Get ordered map list
         maps = sp.get_registered_maps_as_objects()
         maps = sp.get_registered_maps_as_objects()
+        
         if not sp.check_temporal_topology(maps):
         if not sp.check_temporal_topology(maps):
             raise GException(_("Topology of Space time dataset %s is invalid." % id))
             raise GException(_("Topology of Space time dataset %s is invalid." % id))