[GRASS-SVN] r58611 - in grass/trunk/gui: icons/grass wxpython/mapdisp wxpython/mapwin

svn_grass at osgeo.org svn_grass at osgeo.org
Sat Jan 4 20:29:07 PST 2014


Author: annakrat
Date: 2014-01-04 20:29:07 -0800 (Sat, 04 Jan 2014)
New Revision: 58611

Added:
   grass/trunk/gui/icons/grass/area-measure.png
Modified:
   grass/trunk/gui/wxpython/mapdisp/frame.py
   grass/trunk/gui/wxpython/mapdisp/toolbars.py
   grass/trunk/gui/wxpython/mapwin/analysis.py
   grass/trunk/gui/wxpython/mapwin/buffered.py
   grass/trunk/gui/wxpython/mapwin/graphics.py
Log:
wxGUI: adds area measurement tool

Added: grass/trunk/gui/icons/grass/area-measure.png
===================================================================
(Binary files differ)


Property changes on: grass/trunk/gui/icons/grass/area-measure.png
___________________________________________________________________
Added: svn:mime-type
   + image/png

Modified: grass/trunk/gui/wxpython/mapdisp/frame.py
===================================================================
--- grass/trunk/gui/wxpython/mapdisp/frame.py	2014-01-05 02:43:00 UTC (rev 58610)
+++ grass/trunk/gui/wxpython/mapdisp/frame.py	2014-01-05 04:29:07 UTC (rev 58611)
@@ -56,7 +56,8 @@
 from wxplot.histogram   import HistogramPlotFrame
 from wxplot.profile     import ProfileFrame
 from wxplot.scatter     import ScatterFrame
-from mapwin.analysis import ProfileController, MeasureDistanceController
+from mapwin.analysis import ProfileController, MeasureDistanceController, \
+    MeasureAreaController
 from gui_core.forms import GUI
 from core.giface import Notification
 
@@ -236,7 +237,7 @@
         self.dialogs['vnet'] = None
         self.dialogs['query'] = None
 
-        self.measureDistController = None
+        self.measureController = None
 
     def GetMapWindow(self):
         return self.MapWindow
@@ -854,13 +855,22 @@
         else:
             return cmd
 
-    def OnMeasure(self, event):
-        if not self.measureDistController:
-            self.measureDistController = MeasureDistanceController(self._giface,
-                                                                   mapWindow=self.GetMapWindow())
-            self._toolSwitcher.toggleToolChanged.connect(lambda: self.measureDistController.Stop())
-        self.measureDistController.Start()
+    def OnMeasureDistance(self, event):
+        self._onMeasure(MeasureDistanceController)
 
+    def OnMeasureArea(self, event):
+        self._onMeasure(MeasureAreaController)
+
+    def _onMeasure(self, controller):
+        """!Starts measurement mode.
+
+        @param controller measurement class (MeasureDistanceController, MeasureAreaController)
+        """
+        self.measureController = controller(self._giface, mapWindow=self.GetMapWindow())
+        # assure that the mode is ended and lines are cleared whenever other tool is selected
+        self._toolSwitcher.toggleToolChanged.connect(lambda: self.measureController.Stop())
+        self.measureController.Start()
+
     def OnProfile(self, event):
         """!Launch profile tool
         """

Modified: grass/trunk/gui/wxpython/mapdisp/toolbars.py
===================================================================
--- grass/trunk/gui/wxpython/mapdisp/toolbars.py	2014-01-05 02:43:00 UTC (rev 58610)
+++ grass/trunk/gui/wxpython/mapdisp/toolbars.py	2014-01-05 04:29:07 UTC (rev 58611)
@@ -37,8 +37,10 @@
     'analyze'    : MetaIcon(img = 'layer-raster-analyze',
                             label = _('Analyze map'),
                             desc = _('Measuring, profiling, histogramming, ...')),
-    'measure'    : MetaIcon(img = 'measure-length',
-                            label = _('Measure distance')),
+    'measureDistance': MetaIcon(img='measure-length',
+                                label=_('Measure distance')),
+    'measureArea' : MetaIcon(img='area-measure',
+                             label=_('Measure area')),
     'profile'    : MetaIcon(img = 'layer-raster-profile',
                             label = _('Profile surface map')),
     'scatter'    : MetaIcon(img = 'layer-raster-profile',
@@ -236,7 +238,8 @@
     def OnAnalyze(self, event):
         """!Analysis tools menu
         """
-        self._onMenu(((MapIcons["measure"],     self.parent.OnMeasure),
+        self._onMenu(((MapIcons["measureDistance"], self.parent.OnMeasureDistance),
+                      (MapIcons["measureArea"], self.parent.OnMeasureArea),
                       (MapIcons["profile"],     self.parent.OnProfile),
                       (MapIcons["scatter"],     self.parent.OnScatterplot),
                       (MapIcons["histogram"],   self.parent.OnHistogramPyPlot),

Modified: grass/trunk/gui/wxpython/mapwin/analysis.py
===================================================================
--- grass/trunk/gui/wxpython/mapwin/analysis.py	2014-01-05 02:43:00 UTC (rev 58610)
+++ grass/trunk/gui/wxpython/mapwin/analysis.py	2014-01-05 04:29:07 UTC (rev 58611)
@@ -23,9 +23,11 @@
 
 from core.utils import _
 import core.units as units
+from core.gcmd import RunCommand
 from core.giface import Notification
 
 from grass.pydispatch.signal import Signal
+import grass.script.core as gcore
 
 
 class AnalysisControllerBase:
@@ -40,6 +42,7 @@
         self._mapWindow = mapWindow
 
         self._registeredGraphics = None
+        self._graphicsType = None
 
         self._oldMouseUse = None
         self._oldCursor = None
@@ -74,6 +77,7 @@
         # draw
         self._mapWindow.ClearLines()
         self._registeredGraphics.Draw(pdc=self._mapWindow.pdcTmp)
+        self._mapWindow.Refresh()
         wx.Yield()
 
         self._doAnalysis(coords)
@@ -111,7 +115,7 @@
         # unregister
         self._mapWindow.UnregisterGraphicsToDraw(self._registeredGraphics)
         self._registeredGraphics = None
-        self._mapWindow.Refresh()
+        self._mapWindow.UpdateMap(render=False)
 
         if restore:
             # restore mouse['use'] and cursor to the state before measuring starts
@@ -125,8 +129,8 @@
         self._oldMouseUse = self._mapWindow.mouse['use']
         self._oldCursor = self._mapWindow.GetNamedCursor()
 
-        self._registeredGraphics = self._mapWindow.RegisterGraphicsToDraw(graphicsType='line',
-                                                                          mapCoords=False)
+        self._registeredGraphics = self._mapWindow.RegisterGraphicsToDraw(graphicsType=self._graphicsType,
+                                                                          mapCoords=True)
 
         self._connectAll()
 
@@ -150,6 +154,7 @@
         AnalysisControllerBase.__init__(self, giface=giface, mapWindow=mapWindow)
 
         self.transectChanged = Signal('ProfileController.transectChanged')
+        self._graphicsType = 'line'
 
     def _doAnalysis(self, coords):
         """!Informs profile dialog that profile changed.
@@ -183,6 +188,7 @@
         self._projInfo = self._mapWindow.Map.projinfo
         self._totaldist = 0.0  # total measured distance
         self._useCtypes = False
+        self._graphicsType = 'line'
 
     def _doAnalysis(self, coords):
         """!New point added.
@@ -226,12 +232,12 @@
         # 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))
+                                    'Double click with left button to clear.') %
+                                   (os.linesep))
         if self._projInfo['proj'] != 'xy':
             mapunits = self._projInfo['units']
             self._giface.WriteCmdLog(_('Measuring distance') + ' ('
-                                      + mapunits + '):')
+                                     + mapunits + '):')
         else:
             self._giface.WriteCmdLog(_('Measuring distance:'))
 
@@ -288,3 +294,72 @@
         self._giface.WriteLog(mstring, notification=Notification.MAKE_VISIBLE)
 
         return dist
+
+
+class MeasureAreaController(AnalysisControllerBase):
+    """!Class controls measuring area in map display."""
+    def __init__(self, giface, mapWindow):
+        AnalysisControllerBase.__init__(self, giface=giface, mapWindow=mapWindow)
+        self._graphicsType = 'polygon'
+
+    def _doAnalysis(self, coords):
+        """!New point added.
+
+        @param coords east north coordinates as a list
+        """
+        self.MeasureArea(coords)
+
+    def _disconnectAll(self):
+        self._mapWindow.mouseLeftDown.disconnect(self._start)
+        self._mapWindow.mouseLeftUp.disconnect(self._addPoint)
+        self._mapWindow.mouseDClick.disconnect(self.Stop)
+
+    def _connectAll(self):
+        self._mapWindow.mouseLeftDown.connect(self._start)
+        self._mapWindow.mouseLeftUp.connect(self._addPoint)
+        self._mapWindow.mouseDClick.connect(self.Stop)
+
+    def _getPen(self):
+        return wx.Pen(colour='green', width=2, style=wx.SOLID)
+
+    def Stop(self, restore=True):
+        if not self.IsActive():
+            return
+        AnalysisControllerBase.Stop(self, restore=restore)
+
+        self._giface.WriteCmdLog(_('Measuring finished'))
+
+    def Start(self):
+        """!Init measurement routine that calculates area of polygon
+        drawn on map display.
+        """
+        if self.IsActive():
+            return
+        AnalysisControllerBase.Start(self)
+
+        self._giface.WriteWarning(_('Click and drag with left mouse button '
+                                    'to measure.%s'
+                                    'Double click with left button to clear.') %
+                                   (os.linesep))
+        self._giface.WriteCmdLog(_('Measuring area:'))
+
+    def MeasureArea(self, coords):
+        """!Calculate area and print to output window.
+
+        @param coords list of E, N coordinates
+        """
+        # TODO: make sure appending first point is needed for m.measure
+        coordinates = coords + [coords[0]]
+        coordinates = ','.join([str(item) for sublist in coordinates for item in sublist])
+        result = RunCommand('m.measure', flags='g', coordinates=coordinates, read=True).strip()
+        result = gcore.parse_key_val(result)
+        if 'units' not in result:
+            self._giface.WriteWarning(_("Units not recognized, measurement failed."))
+            unit = ''
+        else:
+            unit = result['units'].split(',')[1]
+        if 'area' not in result:
+            text = _("Area: {area} {unit}\n").format(area=0, unit=unit)
+        else:
+            text = _("Area: {area} {unit}\n").format(area=result['area'], unit=unit)
+        self._giface.WriteLog(text, notification=Notification.MAKE_VISIBLE)

Modified: grass/trunk/gui/wxpython/mapwin/buffered.py
===================================================================
--- grass/trunk/gui/wxpython/mapwin/buffered.py	2014-01-05 02:43:00 UTC (rev 58610)
+++ grass/trunk/gui/wxpython/mapwin/buffered.py	2014-01-05 04:29:07 UTC (rev 58611)
@@ -323,6 +323,17 @@
                     pdc.SetIdBounds(drawid, wx.Rect(x1,y1,x2,y2))
                     # self.ovlcoords[drawid] = [x1,y1,x2,y2]
 
+        elif pdctype == 'polygon':
+            if pen:
+                pdc.SetPen(pen)
+                pdc.SetBrush(wx.TRANSPARENT_BRUSH)
+                pdc.DrawPolygon(points=coords)
+                x = min(coords, key=lambda x: x[0])[0]
+                y = min(coords, key=lambda x: x[1])[1]
+                w = max(coords, key=lambda x: x[0])[0] - x
+                h = max(coords, key=lambda x: x[1])[1] - y
+                pdc.SetIdBounds(drawid, wx.Rect(x, y, w, h))
+
         elif pdctype == 'circle': # draw circle
             if pen:
                 pdc.SetPen(pen)
@@ -944,8 +955,24 @@
         
         return -1
 
+    def DrawPolylines(self, pdc, coords, pen, drawid=None):
+        """!Draw polyline in PseudoDC.
+        
+        This is similar to DrawLines but this is used with GraphicsSet,
+        coordinates should be always in pixels.
+        
+        @param pdc PseudoDC
+        @param coords list of coordinates (pixel coordinates)
+        @param pen pen to be used
+        @param drawid id of the drawn object (used by PseudoDC)
+        """
+        Debug.msg (4, "BufferedWindow.DrawPolylines(): coords=%s" % coords)
+        self.lineId = self.Draw(pdc, drawid=None, pdctype='polyline', coords=coords, pen=pen)
+
+        return self.lineid
+
     def DrawCross(self, pdc, coords, size, rotation = 0, pen = None,
-                  text = None, textAlign = 'lr', textOffset = (5, 5)):
+                  text = None, textAlign = 'lr', textOffset = (5, 5), drawid=None):
         """!Draw cross in PseudoDC
 
         @todo implement rotation
@@ -956,19 +983,19 @@
         @param text draw also text (text, font, color, rotation)
         @param textAlign alignment (default 'lower-right')
         @param textOffset offset for text (from center point)
+        @param drawid id of the drawn object (used by PseudoDC)
         """
         Debug.msg(4, "BufferedWindow.DrawCross(): pdc=%s, coords=%s, size=%d" % \
                   (pdc, coords, size))
         coordsCross = ((coords[0] - size, coords[1], coords[0] + size, coords[1]),
                        (coords[0], coords[1] - size, coords[0], coords[1] + size))
 
-        self.lineid = wx.NewId()
         for lineCoords in coordsCross:
-            self.Draw(pdc, drawid = self.lineid, pdctype = 'line', coords = lineCoords, pen = pen)
-        
+            self.lineid = self.Draw(pdc, drawid=drawid, pdctype='line', coords=lineCoords, pen=pen)
+
         if not text:
             return self.lineid
-        
+
         if textAlign == 'ul':
             coord = [coords[0] - textOffset[0], coords[1] - textOffset[1], 0, 0]
         elif textAlign == 'ur':
@@ -983,35 +1010,49 @@
         
         return self.lineid
 
-    def DrawRectangle(self, pdc, point1, point2, pen=None):
+    def DrawRectangle(self, pdc, point1, point2, pen, drawid=None):
         """!Draw rectangle (not filled) in PseudoDC
 
         @param pdc PseudoDC
         @param point1 top left corner (pixel coordinates)
         @param point2 bottom right corner (pixel coordinates)
         @param pen pen
+        @param drawid id of the drawn object (used by PseudoDC)
         """
         Debug.msg(4, "BufferedWindow.DrawRectangle(): pdc=%s, point1=%s, point2=%s" % \
                   (pdc, point1, point2))
         coords = [point1[0], point1[1], point2[0], point2[1]]
-        self.lineid = self.Draw(pdc, drawid=None, pdctype='box', coords=coords, pen=pen)
+        self.lineid = self.Draw(pdc, drawid=drawid, pdctype='box', coords=coords, pen=pen)
         return self.lineid
 
-    def DrawCircle(self, pdc, coords, radius, pen=None):
+    def DrawCircle(self, pdc, coords, radius, pen, drawid=None):
         """!Draw circle (not filled) in PseudoDC
 
         @param pdc PseudoDC
         @param coords center (pixel coordinates)
         @param radius radius
         @param pen pen
+        @param drawid id of the drawn object (used by PseudoDC)
         """
         Debug.msg(4, "BufferedWindow.DrawCircle(): pdc=%s, coords=%s, radius=%s" %
                   (pdc, coords, radius))
         newcoords = [coords[0] - radius, coords[1] - radius,
                      coords[0] + radius, coords[1] + radius]
-        self.lineid = self.Draw(pdc, drawid=None, pdctype='circle', coords=newcoords, pen=pen)
+        self.lineid = self.Draw(pdc, drawid=drawid, pdctype='circle', coords=newcoords, pen=pen)
         return self.lineid
 
+    def DrawPolygon(self, pdc, coords, pen, drawid=None):
+        """!Draws polygon from a list of points (do not append the first point)
+
+        @param pdc PseudoDC
+        @param coords list of coordinates (pixel coordinates)
+        @param pen pen
+        @param drawid id of the drawn object (used by PseudoDC)
+        """
+        self.lineid = self.Draw(pdc, drawid=drawid, pdctype='polygon',
+                                coords=coords, pen=pen)
+        return self.lineid
+
     def _computeZoomToPointAndRecenter(self, position, zoomtype):
         """!Computes zoom parameters for recenter mode.
 

Modified: grass/trunk/gui/wxpython/mapwin/graphics.py
===================================================================
--- grass/trunk/gui/wxpython/mapwin/graphics.py	2014-01-05 02:43:00 UTC (rev 58610)
+++ grass/trunk/gui/wxpython/mapwin/graphics.py	2014-01-05 04:29:07 UTC (rev 58611)
@@ -63,11 +63,14 @@
             self.drawFunc = self.parentMapWin.DrawCross
 
         elif self.graphicsType == "line":
-            self.drawFunc = self.parentMapWin.DrawLines
+            self.drawFunc = self.parentMapWin.DrawPolylines
 
         elif self.graphicsType == "rectangle":
             self.drawFunc = self.parentMapWin.DrawRectangle
 
+        elif self.graphicsType == "polygon":
+            self.drawFunc = self.parentMapWin.DrawPolygon
+
     def Draw(self, pdc):
         """!Draws all containing items.
 
@@ -75,6 +78,7 @@
         """
         itemOrderNum = 0
         for item in self.itemsList:
+            self._clearId(pdc, item.GetId())
             if self.setStatusFunc is not None:
                 self.setStatusFunc(item, itemOrderNum)
 
@@ -98,24 +102,24 @@
                 self.properties["text"]['color'] = self.parentMapWin.pen.GetColour()
                 self.properties["text"]['text'] = item.GetPropertyVal("label")
 
-                self.drawFunc(pdc=pdc,
+                self.drawFunc(pdc=pdc, drawid=item.GetId(),
                               coords=coords,
                               text=self.properties["text"],
                               size=self.properties["size"])
 
             elif self.graphicsType == "line":
                 if item.GetPropertyVal("penName"):
-                    self.parentMapWin.polypen = self.pens[item.GetPropertyVal("penName")]
+                    pen = self.pens[item.GetPropertyVal("penName")]
                 else:
-                    self.parentMapWin.polypen = self.pens["default"]
+                    pen = self.pens["default"]
 
                 if self.mapCoords:
                     coords = [self.parentMapWin.Cell2Pixel(coords) for coords in item.GetCoords()]
                 else:
                     coords = item.GetCoords()
 
-                self.drawFunc(pdc=pdc,
-                              polycoords=coords)
+                self.drawFunc(pdc=pdc, pen=pen,
+                              coords=coords, drawid=item.GetId())
              
             elif self.graphicsType == "rectangle":
                 if item.GetPropertyVal("penName"):
@@ -127,9 +131,22 @@
                 else:
                     coords = item.GetCoords()
 
-                self.drawFunc(pdc=pdc, pen=pen, 
+                self.drawFunc(pdc=pdc, pen=pen, drawid=item.GetId(),
                               point1=coords[0],
                               point2=coords[1])
+
+            elif self.graphicsType == "polygon":
+                if item.GetPropertyVal("penName"):
+                    pen = self.pens[item.GetPropertyVal("penName")]
+                else:
+                    pen = self.pens["default"]
+                if self.mapCoords:
+                    coords = [self.parentMapWin.Cell2Pixel(coords) for coords in item.GetCoords()]
+                else:
+                    coords = item.GetCoords()
+
+                self.drawFunc(pdc=pdc, pen=pen, 
+                              coords=coords, drawid=item.GetId())
             itemOrderNum += 1
 
     def AddItem(self, coords, penName=None, label=None, hide=False):
@@ -282,7 +299,14 @@
         except ValueError:
             return None
 
+    def _clearId(self, pdc, drawid):
+        """!Clears old object before drawing new object."""
+        try:
+            pdc.ClearId(drawid)
+        except:
+            pass
 
+
 class GraphicsSetItem:
 
     def __init__(self, coords, penName=None, label=None, hide=False):
@@ -305,6 +329,7 @@
         self.properties = {"penName": penName,
                            "hide": hide,
                            "label": label}
+        self.id = wx.NewId()
 
     def SetPropertyVal(self, propName, propVal):
         """!Set property value
@@ -354,3 +379,8 @@
         @returns coordinates
         """
         return self.coords
+
+    def GetId(self):
+        """!Get item id (drawing id).
+        """
+        return self.id



More information about the grass-commit mailing list