GRASS Programmer's Manual  6.5.svn(2012)-r51648
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
mapdisp.py
Go to the documentation of this file.
00001 """!
00002 @package gui_core.mapdisp
00003 
00004 @brief Base classes for Map display window
00005 
00006 Classes:
00007  - mapdisp::MapFrameBase
00008 
00009 (C) 2009-2011 by the GRASS Development Team
00010 
00011 This program is free software under the GNU General Public License
00012 (>=v2). Read the file COPYING that comes with GRASS for details.
00013 
00014 @author Martin Landa <landa.martin gmail.com>
00015 @author Michael Barton <michael.barton@asu.edu>
00016 """
00017 
00018 import os
00019 import sys
00020 
00021 import wx
00022 
00023 from core       import globalvar
00024 from core.debug import Debug
00025 
00026 from grass.script import core as grass
00027 
00028 class MapFrameBase(wx.Frame):
00029     """!Base class for map display window
00030     
00031     Derived class must use statusbarManager or override
00032     GetProperty, SetProperty and HasProperty methods.
00033     If derived class enables and disables auto-rendering,
00034     it should override IsAutoRendered method.
00035     """
00036     def __init__(self, parent = None, id = wx.ID_ANY, title = None,
00037                  style = wx.DEFAULT_FRAME_STYLE, toolbars = None,
00038                  Map = None, auimgr = None, name = None, **kwargs):
00039         """!
00040         @param toolbars array of activated toolbars, e.g. ['map', 'digit']
00041         @param Map instance of render.Map
00042         @param auimgs AUI manager
00043         @param name frame name
00044         @param kwargs wx.Frame attributes
00045         """
00046         
00047 
00048         self.Map        = Map       # instance of render.Map
00049         self.parent     = parent
00050         
00051         wx.Frame.__init__(self, parent, id, title, style = style, name = name, **kwargs)
00052         
00053         # available cursors
00054         self.cursors = {
00055             # default: cross
00056             # "default" : wx.StockCursor(wx.CURSOR_DEFAULT),
00057             "default" : wx.StockCursor(wx.CURSOR_ARROW),
00058             "cross"   : wx.StockCursor(wx.CURSOR_CROSS),
00059             "hand"    : wx.StockCursor(wx.CURSOR_HAND),
00060             "pencil"  : wx.StockCursor(wx.CURSOR_PENCIL),
00061             "sizenwse": wx.StockCursor(wx.CURSOR_SIZENWSE)
00062             }
00063                 
00064         #
00065         # set the size & system icon
00066         #
00067         self.SetClientSize(self.GetSize())
00068         self.iconsize = (16, 16)
00069 
00070         self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_map.ico'), wx.BITMAP_TYPE_ICO))
00071         
00072         # toolbars
00073         self.toolbars = {}
00074         
00075         #
00076         # Fancy gui
00077         #
00078         self._mgr = wx.aui.AuiManager(self)
00079         
00080     def _initMap(self, map):
00081         """!Initialize map display, set dimensions and map region
00082         """
00083         if not grass.find_program('g.region', ['--help']):
00084             sys.exit(_("GRASS module '%s' not found. Unable to start map "
00085                        "display window.") % 'g.region')
00086         
00087         self.width, self.height = self.GetClientSize()
00088         
00089         Debug.msg(2, "MapFrame._initMap():")
00090         map.ChangeMapSize(self.GetClientSize())
00091         map.region = map.GetRegion() # g.region -upgc
00092         # self.Map.SetRegion() # adjust region to match display window
00093         
00094     def SetProperty(self, name, value):
00095         """!Sets property"""
00096         self.statusbarManager.SetProperty(name, value)
00097         
00098     def GetProperty(self, name):
00099         """!Returns property"""
00100         return self.statusbarManager.GetProperty(name)
00101         
00102     def HasProperty(self, name):
00103         """!Checks whether object has property"""
00104         return self.statusbarManager.HasProperty(name)
00105     
00106     def GetPPM(self):
00107         """! Get pixel per meter
00108         
00109         @todo now computed every time, is it necessary?
00110         @todo enable user to specify ppm (and store it in UserSettings)
00111         """
00112         # TODO: need to be fixed...
00113         ### screen X region problem
00114         ### user should specify ppm
00115         dc = wx.ScreenDC()
00116         dpSizePx = wx.DisplaySize()   # display size in pixels
00117         dpSizeMM = wx.DisplaySizeMM() # display size in mm (system)
00118         dpSizeIn = (dpSizeMM[0] / 25.4, dpSizeMM[1] / 25.4) # inches
00119         sysPpi  = dc.GetPPI()
00120         comPpi = (dpSizePx[0] / dpSizeIn[0],
00121                   dpSizePx[1] / dpSizeIn[1])
00122 
00123         ppi = comPpi                  # pixel per inch
00124         ppm = ((ppi[0] / 2.54) * 100, # pixel per meter
00125                     (ppi[1] / 2.54) * 100)
00126         
00127         Debug.msg(4, "MapFrameBase.GetPPM(): size: px=%d,%d mm=%f,%f "
00128                   "in=%f,%f ppi: sys=%d,%d com=%d,%d; ppm=%f,%f" % \
00129                   (dpSizePx[0], dpSizePx[1], dpSizeMM[0], dpSizeMM[1],
00130                    dpSizeIn[0], dpSizeIn[1],
00131                    sysPpi[0], sysPpi[1], comPpi[0], comPpi[1],
00132                    ppm[0], ppm[1]))
00133         
00134         return ppm
00135     
00136     def SetMapScale(self, value, map = None):
00137         """! Set current map scale
00138         
00139         @param value scale value (n if scale is 1:n)
00140         @param map Map instance (if none self.Map is used)
00141         """
00142         if not map:
00143             map = self.Map
00144         
00145         region = self.Map.region
00146         dEW = value * (region['cols'] / self.GetPPM()[0])
00147         dNS = value * (region['rows'] / self.GetPPM()[1])
00148         region['n'] = region['center_northing'] + dNS / 2.
00149         region['s'] = region['center_northing'] - dNS / 2.
00150         region['w'] = region['center_easting']  - dEW / 2.
00151         region['e'] = region['center_easting']  + dEW / 2.
00152         
00153         # add to zoom history
00154         self.GetWindow().ZoomHistory(region['n'], region['s'],
00155                                    region['e'], region['w'])
00156     
00157     def GetMapScale(self, map = None):
00158         """! Get current map scale
00159         
00160         @param map Map instance (if none self.Map is used)
00161         """
00162         if not map:
00163             map = self.Map
00164         
00165         region = map.region
00166         ppm = self.GetPPM()
00167 
00168         heightCm = region['rows'] / ppm[1] * 100
00169         widthCm  = region['cols'] / ppm[0] * 100
00170 
00171         Debug.msg(4, "MapFrame.GetMapScale(): width_cm=%f, height_cm=%f" %
00172                   (widthCm, heightCm))
00173 
00174         xscale = (region['e'] - region['w']) / (region['cols'] / ppm[0])
00175         yscale = (region['n'] - region['s']) / (region['rows'] / ppm[1])
00176         scale = (xscale + yscale) / 2.
00177         
00178         Debug.msg(3, "MapFrame.GetMapScale(): xscale=%f, yscale=%f -> scale=%f" % \
00179                       (xscale, yscale, scale))
00180         
00181         return scale
00182         
00183     def GetProgressBar(self):
00184         """!Returns progress bar
00185         
00186         Progress bar can be used by other classes.
00187         """
00188         return self.statusbarManager.GetProgressBar()
00189         
00190     def GetMap(self):
00191         """!Returns current Map instance
00192         """
00193         return self.Map
00194 
00195     def GetWindow(self):
00196         """!Get map window"""
00197         return self.MapWindow
00198         
00199     def GetMapToolbar(self):
00200        """!Returns toolbar with zooming tools"""
00201        raise NotImplementedError()
00202        
00203     def GetToolbar(self, name):
00204         """!Returns toolbar if exists else None.
00205         
00206         Toolbars dictionary contains currently used toolbars only.
00207         """
00208         if name in self.toolbars:
00209             return self.toolbars[name]
00210         
00211         return None
00212        
00213     def StatusbarUpdate(self):
00214         """!Update statusbar content"""
00215         self.statusbarManager.Update()
00216         
00217     def IsAutoRendered(self):
00218         """!Check if auto-rendering is enabled"""
00219         return self.GetProperty('render')
00220         
00221     def CoordinatesChanged(self):
00222         """!Shows current coordinates on statusbar.
00223         
00224         Used in BufferedWindow to report change of map coordinates (under mouse cursor).
00225         """
00226         self.statusbarManager.ShowItem('coordinates')
00227         
00228     def StatusbarReposition(self):
00229         """!Reposition items in statusbar"""
00230         self.statusbarManager.Reposition()
00231         
00232     def StatusbarEnableLongHelp(self, enable = True):
00233         """!Enable/disable toolbars long help"""
00234         for toolbar in self.toolbars.itervalues():
00235             toolbar.EnableLongHelp(enable)
00236         
00237     def IsStandalone(self):
00238         """!Check if Map display is standalone"""
00239         raise NotImplementedError("IsStandalone")
00240    
00241     def OnRender(self, event):
00242         """!Re-render map composition (each map layer)
00243         """
00244         raise NotImplementedError("OnRender")
00245         
00246     def OnDraw(self, event):
00247         """!Re-display current map composition
00248         """
00249         self.MapWindow.UpdateMap(render = False)
00250         
00251     def OnErase(self, event):
00252         """!Erase the canvas
00253         """
00254         self.MapWindow.EraseMap()
00255         
00256     def OnZoomIn(self, event):
00257         """!Zoom in the map.
00258         Set mouse cursor, zoombox attributes, and zoom direction
00259         """
00260         toolbar = self.GetMapToolbar()
00261         self._switchTool(toolbar, event)
00262         
00263         win = self.GetWindow()
00264         self._prepareZoom(mapWindow = win, zoomType = 1)
00265         
00266     def OnZoomOut(self, event):
00267         """!Zoom out the map.
00268         Set mouse cursor, zoombox attributes, and zoom direction
00269         """
00270         toolbar = self.GetMapToolbar()
00271         self._switchTool(toolbar, event)
00272         
00273         win = self.GetWindow()
00274         self._prepareZoom(mapWindow = win, zoomType = -1)
00275         
00276     def _prepareZoom(self, mapWindow, zoomType):
00277         """!Prepares MapWindow for zoom, toggles toolbar
00278         
00279         @param mapWindow MapWindow to prepare
00280         @param zoomType 1 for zoom in, -1 for zoom out
00281         """
00282         mapWindow.mouse['use'] = "zoom"
00283         mapWindow.mouse['box'] = "box"
00284         mapWindow.zoomtype = zoomType
00285         mapWindow.pen = wx.Pen(colour = 'Red', width = 2, style = wx.SHORT_DASH)
00286         
00287         # change the cursor
00288         mapWindow.SetCursor(self.cursors["cross"])
00289     
00290     def _switchTool(self, toolbar, event):
00291         """!Helper function to switch tools"""
00292         if toolbar:
00293             toolbar.OnTool(event)
00294             toolbar.action['desc'] = ''
00295             
00296     def OnPan(self, event):
00297         """!Panning, set mouse to drag
00298         """
00299         toolbar = self.GetMapToolbar()
00300         self._switchTool(toolbar, event)
00301         
00302         win = self.GetWindow()
00303         self._preparePan(mapWindow = win)
00304     
00305     def _preparePan(self, mapWindow):
00306         """!Prepares MapWindow for pan, toggles toolbar
00307         
00308         @param mapWindow MapWindow to prepare
00309         """
00310         mapWindow.mouse['use'] = "pan"
00311         mapWindow.mouse['box'] = "pan"
00312         mapWindow.zoomtype = 0
00313         
00314         # change the cursor
00315         mapWindow.SetCursor(self.cursors["hand"])
00316         
00317     def OnZoomBack(self, event):
00318         """!Zoom last (previously stored position)
00319         """
00320         self.MapWindow.ZoomBack()
00321         
00322     def OnZoomToMap(self, event):
00323         """!
00324         Set display extents to match selected raster (including NULLs)
00325         or vector map.
00326         """
00327         self.MapWindow.ZoomToMap(layers = self.Map.GetListOfLayers())
00328     
00329     def OnZoomToWind(self, event):
00330         """!Set display geometry to match computational region
00331         settings (set with g.region)
00332         """
00333         self.MapWindow.ZoomToWind()
00334         
00335     def OnZoomToDefault(self, event):
00336         """!Set display geometry to match default region settings
00337         """
00338         self.MapWindow.ZoomToDefault()