[GRASS-SVN] r57398 - grass/trunk/gui/wxpython/mapdisp

svn_grass at osgeo.org svn_grass at osgeo.org
Sun Aug 4 09:47:23 PDT 2013


Author: annakrat
Date: 2013-08-04 09:47:23 -0700 (Sun, 04 Aug 2013)
New Revision: 57398

Modified:
   grass/trunk/gui/wxpython/mapdisp/frame.py
   grass/trunk/gui/wxpython/mapdisp/mapwindow.py
Log:
wxGUI: encapsulate measure functionality

Modified: grass/trunk/gui/wxpython/mapdisp/frame.py
===================================================================
--- grass/trunk/gui/wxpython/mapdisp/frame.py	2013-08-04 16:37:56 UTC (rev 57397)
+++ grass/trunk/gui/wxpython/mapdisp/frame.py	2013-08-04 16:47:23 UTC (rev 57398)
@@ -63,7 +63,6 @@
 
 import grass.script as grass
 
-haveCtypes = False
 
 class MapFrame(SingleMapFrame):
     """!Main frame for map display window. Drawing takes place in
@@ -218,6 +217,8 @@
         self.dialogs['query'] = None
 
         self.decorationDialog = None # decoration/overlays
+        
+        self.measureController = None
 
     def GetMapWindow(self):
         return self.MapWindow
@@ -864,90 +865,10 @@
             return cmd
 
     def OnMeasure(self, event):
-        """!Init measurement routine that calculates map distance
-        along transect drawn on map display
-        """
-        self.totaldist = 0.0 # total measured distance
-        
-        self.SwitchTool(self.toolbars['map'], event)
+        if not self.measureController:
+            self.measureController = MeasureController(self._giface)
+        self.measureController.StartMeasurement()
 
-        # change mouse to draw line for measurement
-        self.MapWindow.mouse['use'] = "measure"
-        self.MapWindow.mouse['box'] = "line"
-        self.MapWindow.zoomtype = 0
-        self.MapWindow.pen     = wx.Pen(colour = 'red', width = 2, style = wx.SHORT_DASH)
-        self.MapWindow.polypen = wx.Pen(colour = 'green', width = 2, style = wx.SHORT_DASH)
-        
-        # change the cursor
-        self.MapWindow.SetNamedCursor('pencil')
-        
-        # initiating output (and write a message)
-        # e.g., in Layer Manager switch to output console
-        # TODO: this should be something like: write important message or write tip
-        # TODO: mixed 'switching' and message? no, measuring handles 'swithing' on its own
-        self._giface.WriteWarning(_('Click and drag with left mouse button '
-                                              'to measure.%s'
-                                              'Double click with left button to clear.') % \
-                                                (os.linesep))
-        if self.Map.projinfo['proj'] != 'xy':
-            units = self.Map.projinfo['units']
-            self._giface.WriteCmdLog(_('Measuring distance') + ' ('
-                                      + units + '):')
-        else:
-            self._giface.WriteCmdLog(_('Measuring distance:'))
-        
-        if self.Map.projinfo['proj'] == 'll':
-            try:
-                import grass.lib.gis as gislib
-                global haveCtypes
-                haveCtypes = True
-
-                gislib.G_begin_distance_calculations()
-            except ImportError, e:
-                self._giface.WriteWarning(_('Geodesic distance is not yet '
-                                                          'supported by this tool.\n'
-                                                          'Reason: %s' % e))
-        
-    def MeasureDist(self, beginpt, endpt):
-        """!Calculate map distance from screen distance
-        and print to output window
-        """
-        
-        dist, (north, east) = self.MapWindow.Distance(beginpt, endpt)
-        
-        dist = round(dist, 3)
-        d, dunits = units.formatDist(dist, self.Map.projinfo['units'])
-
-        self.totaldist += dist
-        td, tdunits = units.formatDist(self.totaldist,
-                                       self.Map.projinfo['units'])
-
-        strdist = str(d)
-        strtotdist = str(td)
-        
-        if self.Map.projinfo['proj'] == 'xy' or 'degree' not in self.Map.projinfo['unit']:
-            angle = int(math.degrees(math.atan2(north,east)) + 0.5)
-            # uncomment below (or flip order of atan2(y,x) above) to use
-            #   the mathematical theta convention (CCW from +x axis)
-            #angle = 90 - angle
-            if angle < 0:
-                angle = 360 + angle
-            
-            mstring = '%s = %s %s\n%s = %s %s\n%s = %d %s\n%s' \
-                % (_('segment'), strdist, dunits,
-                   _('total distance'), strtotdist, tdunits,
-                   _('bearing'), angle, _('degrees (clockwise from grid-north)'),
-                   '-' * 60)
-        else:
-            mstring = '%s = %s %s\n%s = %s %s\n%s' \
-                % (_('segment'), strdist, dunits,
-                   _('total distance'), strtotdist, tdunits,
-                   '-' * 60)
-        
-        self._giface.WriteLog(mstring, priority=2)
-        
-        return dist
-
     def OnProfile(self, event):
         """!Launch profile tool
         """
@@ -1281,6 +1202,8 @@
             if btn.GetValue():
                 btn.SetValue(0)
                 self.dialogs['legend'].DisconnectResizing()
+        if self.measureController and self.measureController.IsMeasuring():
+            self.measureController.StopMeasurement(restore=False)
 
     def ResetPointer(self):
         """Sets pointer mode.
@@ -1294,3 +1217,160 @@
         toolbar.action['id'] = vars(toolbar)["pointer"]
         toolbar.OnTool(None)
         self.OnPointer(event=None)
+
+
+class MeasureController:
+    """!Class controls measuring in map display."""
+    def __init__(self, giface):
+        self._giface = giface
+        self._mapWindow = self._giface.GetMapWindow()
+        self._projInfo = self._mapWindow.Map.projinfo
+        self._measureGraphics = None
+        
+        self._totaldist = 0.0 # total measured distance
+        
+        self._oldMouseUse = None
+        self._oldCursor = None
+        
+    def IsMeasuring(self):
+        """!Returns True if measuring mode is enabled, otherwise False"""
+        return bool(self._measureGraphics)
+
+    def _startMeasurement(self, x, y):
+        """!Handles the actual start of measuring
+        and adding each new point.
+        
+        @param x,y east north coordinates
+        """
+        if not self._measureGraphics.GetAllItems():
+            item = self._measureGraphics.AddItem(coords=[[x, y]])
+            item.SetPropertyVal('penName', 'measure')
+        else:
+            # needed to switch mouse begin and end to draw intermediate line properly
+            coords = self._measureGraphics.GetItem(0).GetCoords()[-1]
+            self._mapWindow.mouse['begin'] = self._mapWindow.Cell2Pixel(coords)
+
+    def _addMeasurement(self, x, y):
+        """!New point added.
+
+        @param x,y east north coordinates
+        """
+        # add new point and calculate distance
+        item = self._measureGraphics.GetItem(0)
+        coords = item.GetCoords() + [[x, y]]
+        item.SetCoords(coords)
+        self.MeasureDist(coords[-2], coords[-1])
+        # draw
+        self._mapWindow.ClearLines()
+        self._measureGraphics.Draw(pdc=self._mapWindow.pdcTmp)
+        
+    def StopMeasurement(self, restore=True):
+        """!Measure mode is stopped."""
+        self._mapWindow.ClearLines(pdc=self._mapWindow.pdcTmp)
+        self._mapWindow.mouse['end'] = self._mapWindow.mouse['begin']
+        # disconnect mouse events
+        self._mapWindow.mouseLeftUp.disconnect(self._addMeasurement)
+        self._mapWindow.mouseDClick.disconnect(self.StopMeasurement)
+        self._mapWindow.mouseLeftDown.disconnect(self._startMeasurement)
+        # unregister
+        self._mapWindow.UnregisterGraphicsToDraw(self._measureGraphics)
+        self._measureGraphics = None
+        self._mapWindow.Refresh()
+
+        if restore:
+            # restore mouse['use'] and cursor to the state before measuring starts
+            self._mapWindow.SetNamedCursor(self._oldCursor)
+            self._mapWindow.mouse['use'] = self._oldMouseUse
+        
+        self._giface.WriteCmdLog(_('Measuring finished'))
+
+    def StartMeasurement(self):
+        """!Init measurement routine that calculates map distance
+        along transect drawn on map display
+        """
+        self._totaldist = 0.0 # total measured distance
+        
+        self._oldMouseUse = self._mapWindow.mouse['use']
+        self._oldCursor = self._mapWindow.GetNamedCursor()
+
+        self._measureGraphics = self._mapWindow.RegisterGraphicsToDraw(graphicsType='line')
+
+        self._mapWindow.mouseLeftDown.connect(self._startMeasurement)
+        self._mapWindow.mouseDClick.connect(self.StopMeasurement)
+        self._mapWindow.mouseLeftUp.connect(self._addMeasurement)
+
+        # change mouse['box'] and pen to draw line during dragging
+        # TODO: better soluyion for drawing this line
+        self._mapWindow.mouse['use'] = None
+        self._mapWindow.mouse['box'] = "line"
+        self._mapWindow.pen = wx.Pen(colour='red', width=2, style=wx.SHORT_DASH)
+        
+        self._measureGraphics.AddPen('measure', wx.Pen(colour='green', width=2, style=wx.SHORT_DASH) )
+        
+        # change the cursor
+        self._mapWindow.SetNamedCursor('pencil')
+        
+        # initiating output (and write a message)
+        # e.g., in Layer Manager switch to output console
+        # TODO: this should be something like: write important message or write tip
+        # TODO: mixed 'switching' and message? no, measuring handles 'swithing' on its own
+        self._giface.WriteWarning(_('Click and drag with left mouse button '
+                                    'to measure.%s'
+                                    'Double click with left button to clear.') % \
+                                    (os.linesep))
+        if self._projInfo['proj'] != 'xy':
+            mapunits = self._projInfo['units']
+            self._giface.WriteCmdLog(_('Measuring distance') + ' ('
+                                      + mapunits + '):')
+        else:
+            self._giface.WriteCmdLog(_('Measuring distance:'))
+        
+        if self._projInfo['proj'] == 'll':
+            try:
+                import grass.lib.gis as gislib
+                gislib.G_begin_distance_calculations()
+            except ImportError, e:
+                self._giface.WriteWarning(_('Geodesic distance is not yet '
+                                                          'supported by this tool.\n'
+                                                          'Reason: %s' % e))
+        
+    def MeasureDist(self, beginpt, endpt):
+        """!Calculate distance and print to output window.
+        
+        @param beginpt,endpt EN coordinates
+        """
+        # move also Distance method?
+        dist, (north, east) = self._mapWindow.Distance(beginpt, endpt, screen=False)
+        
+        dist = round(dist, 3)
+        d, dunits = units.formatDist(dist, self._projInfo['units'])
+        
+        self._totaldist += dist
+        td, tdunits = units.formatDist(self._totaldist,
+                                       self._projInfo['units'])
+        
+        strdist = str(d)
+        strtotdist = str(td)
+        
+        if self._projInfo['proj'] == 'xy' or 'degree' not in self._projInfo['unit']:
+            angle = int(math.degrees(math.atan2(north,east)) + 0.5)
+            # uncomment below (or flip order of atan2(y,x) above) to use
+            #   the mathematical theta convention (CCW from +x axis)
+            #angle = 90 - angle
+            if angle < 0:
+                angle = 360 + angle
+            
+            mstring = '%s = %s %s\n%s = %s %s\n%s = %d %s\n%s' \
+                % (_('segment'), strdist, dunits,
+                   _('total distance'), strtotdist, tdunits,
+                   _('bearing'), angle, _('degrees (clockwise from grid-north)'),
+                   '-' * 60)
+        else:
+            mstring = '%s = %s %s\n%s = %s %s\n%s' \
+                % (_('segment'), strdist, dunits,
+                   _('total distance'), strtotdist, tdunits,
+                   '-' * 60)
+        
+        self._giface.WriteLog(mstring, priority=2)
+        
+        return dist

Modified: grass/trunk/gui/wxpython/mapdisp/mapwindow.py
===================================================================
--- grass/trunk/gui/wxpython/mapdisp/mapwindow.py	2013-08-04 16:37:56 UTC (rev 57397)
+++ grass/trunk/gui/wxpython/mapdisp/mapwindow.py	2013-08-04 16:47:23 UTC (rev 57398)
@@ -112,6 +112,10 @@
         self.mouseLeftUpPointer = Signal('BufferedWindow.mouseLeftUpPointer')
         # Emitted when left mouse button is released
         self.mouseLeftUp = Signal('BufferedWindow.mouseLeftUp')
+        # Emitted when left mouse button was pressed
+        self.mouseLeftDown = Signal('BufferedWindow.mouseLeftDown')
+        # Emitted after double-click
+        self.mouseDClick = Signal('BufferedWindow.mouseDClick')
         # Emitted when mouse us moving (mouse motion event)
         # Parametres are x and y of the mouse position in map (cell) units
         self.mouseMoving = Signal('BufferedWindow.mouseMoving')
@@ -734,17 +738,6 @@
         
         if len(self.polycoords) > 0:
             self.DrawLines(self.pdcTmp)
-        
-        # 
-        # clear measurement
-        #
-        if self.mouse["use"] == "measure":
-            self.ClearLines(pdc = self.pdcTmp)
-            self.polycoords = []
-            self.mouse['use'] = 'pointer'
-            self.mouse['box'] = 'point'
-            self.mouse['end'] = [0, 0]
-            self.SetNamedCursor('default')
             
         stop = time.clock()
         
@@ -1121,8 +1114,7 @@
         
         self.mouse['begin'] = event.GetPositionTuple()[:]
         
-        if self.mouse["use"] in ["measure", "profile"]:
-            # measure or profile
+        if self.mouse["use"] in ["profile"]:
             if len(self.polycoords) == 0:
                 self.mouse['end'] = self.mouse['begin']
                 self.polycoords.append(self.Pixel2Cell(self.mouse['begin']))
@@ -1151,6 +1143,8 @@
                 self.dragid = idlist[0] #drag whatever is on top
         else:
             pass
+        coords = self.Pixel2Cell(self.mouse['begin'])
+        self.mouseLeftDown.emit(x=coords[0], y=coords[1])
         
         event.Skip()
         
@@ -1183,11 +1177,7 @@
         elif self.mouse["use"] == "query":
             self.mapQueried.emit(x=self.mouse['end'][0], y=self.mouse['end'][1])
 
-        elif self.mouse["use"] in ["measure", "profile"]:
-            # measure or profile
-            if self.mouse["use"] == "measure":
-                self.frame.MeasureDist(self.mouse['begin'], self.mouse['end'])
-            
+        elif self.mouse["use"] in ["profile"]:            
             self.polycoords.append(self.Pixel2Cell(self.mouse['end']))
             self.ClearLines(pdc = self.pdcTmp)
             self.DrawLines(pdc = self.pdcTmp)
@@ -1220,35 +1210,28 @@
         Debug.msg (5, "BufferedWindow.OnButtonDClick(): use=%s" % \
                    self.mouse["use"])
         
-        if self.mouse["use"] == "measure":
-            # measure
-            self.ClearLines(pdc=self.pdcTmp)
-            self.polycoords = []
-            self.mouse['use'] = 'pointer'
-            self.mouse['box'] = 'point'
-            self.mouse['end'] = [0, 0]
-            self.Refresh()
-            self.SetNamedCursor('default')
-        
-        elif self.mouse["use"] != "profile" or \
+        screenCoords = event.GetPosition()
+
+        if self.mouse["use"] != "profile" or \
                 (self.mouse['use'] != 'pointer' and \
                      hasattr(self, "digit")):
                # select overlay decoration options dialog
-            clickposition = event.GetPositionTuple()[:]
-            idlist  = self.pdc.FindObjects(clickposition[0], clickposition[1], self.hitradius)
-            if idlist == []:
-                return
-            self.dragid = idlist[0]
+            idlist  = self.pdc.FindObjects(screenCoords[0], screenCoords[1], self.hitradius)
+            if idlist:
+                self.dragid = idlist[0]
+    
+                # self.ovlcoords[self.dragid] = self.pdc.GetIdBounds(self.dragid)
+                if self.dragid > 100:
+                    self.currtxtid = self.dragid
+                    self.frame.OnAddText(None)
+                elif self.dragid == 0:
+                    self.frame.AddBarscale()
+                elif self.dragid == 1:
+                    self.frame.AddLegend()
+                
+        coords = self.Pixel2Cell(screenCoords)
+        self.mouseDClick.emit(x=coords[0], y=coords[1])
 
-            # self.ovlcoords[self.dragid] = self.pdc.GetIdBounds(self.dragid)
-            if self.dragid > 100:
-                self.currtxtid = self.dragid
-                self.frame.OnAddText(None)
-            elif self.dragid == 0:
-                self.frame.AddBarscale()
-            elif self.dragid == 1:
-                self.frame.AddLegend()
-        
     def OnRightDown(self, event):
         """!Right mouse button pressed
         """
@@ -1904,10 +1887,12 @@
                               size = self.properties["size"])
             
             elif self.graphicsType == "line":
-                if item.GetPropertyVal("pen"):
-                    self.parentMapWin.polypen = self.pens[item.GetPropertyVal("pen")]
+                if item.GetPropertyVal("penName"):
+                    self.parentMapWin.polypen = self.pens[item.GetPropertyVal("penName")]
                 else:
                     self.parentMapWin.polypen = self.pens["default"]
+                coords = item.GetCoords()
+                
                 self.drawFunc(pdc = pdc, 
                               polycoords = coords)
             itemOrderNum += 1



More information about the grass-commit mailing list