Procházet zdrojové kódy

wxGUI/wxpsmap: reorganize code into more files

git-svn-id: https://svn.osgeo.org/grass/grass/trunk@50798 15284696-431f-4ddb-bdfa-cd5b030d7da7
Anna Petrášová před 13 roky
rodič
revize
1b893d53cf

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 9 - 2145
gui/wxpython/psmap/dialogs.py


+ 12 - 16
gui/wxpython/psmap/frame.py

@@ -7,7 +7,7 @@ Classes:
  - frame::PsMapFrame
  - frame::PsMapBufferedWindow
 
-(C) 2011 by Anna Kratochvilova, and the GRASS Development Team
+(C) 2011-2012 by Anna Kratochvilova, and 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.
 
@@ -20,11 +20,6 @@ import sys
 import textwrap
 import Queue
 from math import sin, cos, pi, sqrt
-try:
-    import Image as PILImage
-    havePILImage = True
-except ImportError:
-    havePILImage = False
 
 if __name__ == "__main__":
     sys.path.append(os.path.join(os.getenv('GISBASE'), 'etc', 'gui', 'wxpython'))
@@ -38,16 +33,17 @@ except ImportError:
 
 import grass.script as grass
 
-from gui_core.menu    import Menu
-from gui_core.goutput import CmdThread, EVT_CMD_DONE
-from psmap.toolbars   import PsMapToolbar
-from core.gcmd        import RunCommand, GError, GMessage
-from core.settings    import UserSettings
-from gui_core.forms   import GUI
-from psmap.menudata   import PsMapData
-
-from psmap.dialogs    import *
-from psmap.utils      import *
+from gui_core.menu      import Menu
+from gui_core.goutput   import CmdThread, EVT_CMD_DONE
+from psmap.toolbars     import PsMapToolbar
+from core.gcmd          import RunCommand, GError, GMessage
+from core.settings      import UserSettings
+from gui_core.forms     import GUI
+from psmap.menudata     import PsMapData
+
+from psmap.dialogs      import *
+from psmap.instructions import *
+from psmap.utils        import *
 
 class PsMapFrame(wx.Frame):
     def __init__(self, parent = None, id = wx.ID_ANY,

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1811 - 0
gui/wxpython/psmap/instructions.py


+ 359 - 1
gui/wxpython/psmap/utils.py

@@ -1,12 +1,13 @@
 """!
 @package psmap.utils
 
-@brief utilities for wxpsmap
+@brief utilities for wxpsmap (classes, functions)
 
 Classes:
  - utils::Rect2D
  - utils::Rect2DPP
  - utils::Rect2DPS
+ - utils::UnitConversion
 
 (C) 2012 by Anna Kratochvilova, and the GRASS Development Team
 This program is free software under the GNU General Public License
@@ -16,6 +17,16 @@ This program is free software under the GNU General Public License
 """
 
 import wx
+from math import ceil, floor, sin, cos, pi
+
+try:
+    import Image as PILImage
+    havePILImage = True
+except ImportError:
+    havePILImage = False
+
+import grass.script as grass
+from core.gcmd          import RunCommand
 
 class Rect2D(wx.Rect2D):
     """!Class representing rectangle with floating point values.
@@ -68,3 +79,350 @@ class Rect2DPS(Rect2D):
     """
     def __init__(self, pos = wx.Point2D(), size = (0, 0)):
         Rect2D.__init__(self, x = pos[0], y = pos[1], width = size[0], height = size[1])
+
+class UnitConversion:
+    """! Class for converting units"""
+    def __init__(self, parent = None):
+        self.parent = parent
+        if self.parent:
+            ppi = wx.ClientDC(self.parent).GetPPI()
+        else: 
+            ppi = (72, 72)
+        self._unitsPage = { 'inch'          : {'val': 1.0, 'tr' : _("inch")},
+                            'point'         : {'val': 72.0, 'tr' : _("point")},
+                            'centimeter'    : {'val': 2.54, 'tr' : _("centimeter")},
+                            'millimeter'    : {'val': 25.4, 'tr' : _("millimeter")}}
+        self._unitsMap = {  'meters'        : {'val': 0.0254, 'tr' : _("meters")},
+                            'kilometers'    : {'val': 2.54e-5, 'tr' : _("kilometers")},
+                            'feet'          : {'val': 1./12, 'tr' : _("feet")},
+                            'miles'         : {'val': 1./63360, 'tr' : _("miles")},
+                            'nautical miles': {'val': 1/72913.386, 'tr' : _("nautical miles")}}
+
+        self._units = { 'pixel'     : {'val': ppi[0], 'tr' : _("pixel")},
+                        'meter'     : {'val': 0.0254, 'tr' : _("meter")},
+                        'nautmiles' : {'val': 1/72913.386, 'tr' :_("nautical miles")},
+                        'degrees'   : {'val': 0.0254 , 'tr' : _("degree")} #like 1 meter, incorrect
+                        }
+        self._units.update(self._unitsPage)
+        self._units.update(self._unitsMap)
+
+    def getPageUnitsNames(self):
+        return sorted(self._unitsPage[unit]['tr'] for unit in self._unitsPage.keys())
+    
+    def getMapUnitsNames(self):
+        return sorted(self._unitsMap[unit]['tr'] for unit in self._unitsMap.keys())
+    
+    def getAllUnits(self):
+        return sorted(self._units.keys())
+    
+    def findUnit(self, name):
+        """!Returns unit by its tr. string"""
+        for unit in self._units.keys():
+            if self._units[unit]['tr'] == name:
+                return unit
+        return None
+    
+    def findName(self, unit):
+        """!Returns tr. string of a unit"""
+        try:
+            return self._units[unit]['tr']
+        except KeyError:
+            return None
+    
+    def convert(self, value, fromUnit = None, toUnit = None):
+        return float(value)/self._units[fromUnit]['val']*self._units[toUnit]['val']
+
+def convertRGB(rgb):
+    """!Converts wx.Colour(r,g,b,a) to string 'r:g:b' or named color,
+            or named color/r:g:b string to wx.Colour, depending on input""" 
+    # transform a wx.Colour tuple into an r:g:b string    
+    if type(rgb) == wx.Colour:
+        for name, color in grass.named_colors.items(): 
+            if  rgb.Red() == int(color[0] * 255) and\
+                rgb.Green() == int(color[1] * 255) and\
+                rgb.Blue() == int(color[2] * 255):
+                return name
+        return str(rgb.Red()) + ':' + str(rgb.Green()) + ':' + str(rgb.Blue())
+    # transform a GRASS named color or an r:g:b string into a wx.Colour tuple
+    else:
+        color = (grass.parse_color(rgb)[0]*255,
+                 grass.parse_color(rgb)[1]*255,
+                 grass.parse_color(rgb)[2]*255)
+        color = wx.Color(*color)
+        if color.IsOk():
+            return color
+        else:  
+            return None
+        
+def PaperMapCoordinates(map, x, y, paperToMap = True):
+    """!Converts paper (inch) coordinates -> map coordinates"""
+    unitConv = UnitConversion()
+    currRegionDict = grass.region()
+    cornerEasting, cornerNorthing = currRegionDict['w'], currRegionDict['n']
+    xMap = map['rect'][0]
+    yMap = map['rect'][1]
+    widthMap = map['rect'][2] * 0.0254 # to meter
+    heightMap = map['rect'][3] * 0.0254
+    xScale = widthMap / abs(currRegionDict['w'] - currRegionDict['e'])
+    yScale = heightMap / abs(currRegionDict['n'] - currRegionDict['s'])
+    currScale = (xScale + yScale) / 2
+ 
+    if not paperToMap:
+        textEasting, textNorthing = x, y
+        eastingDiff = textEasting - cornerEasting 
+        if currRegionDict['w'] > currRegionDict['e']:
+            eastingDiff = - eastingDiff
+        else:
+            eastingDiff = eastingDiff
+
+        northingDiff = textNorthing - cornerNorthing
+        if currRegionDict['n'] > currRegionDict['s']:
+            northingDiff = - northingDiff 
+        else:
+            northingDiff = northingDiff
+
+        xPaper = xMap + unitConv.convert(value = eastingDiff, fromUnit = 'meter', toUnit = 'inch') * currScale
+        yPaper = yMap + unitConv.convert(value = northingDiff, fromUnit = 'meter', toUnit = 'inch') * currScale
+        return xPaper, yPaper
+    else:
+        if currRegionDict['w'] < currRegionDict['e']:
+            eastingDiff = (x - xMap) 
+        else:
+            eastingDiff = (xMap - x)
+        if currRegionDict['n'] < currRegionDict['s']:
+            northingDiff = (y - yMap) 
+        else:
+            northingDiff = (yMap - y)
+
+        textEasting = cornerEasting + unitConv.convert(value = eastingDiff, fromUnit = 'inch', toUnit = 'meter') / currScale
+        textNorthing = cornerNorthing + unitConv.convert(value = northingDiff, fromUnit = 'inch', toUnit = 'meter') / currScale
+        return int(textEasting), int(textNorthing)
+        
+def AutoAdjust(self, scaleType,  rect, map = None, mapType = None, region = None):
+    """!Computes map scale, center and map frame rectangle to fit region (scale is not fixed)"""
+    currRegionDict = {}
+    if scaleType == 0 and map:# automatic, region from raster or vector
+        res = ''
+        if mapType == 'raster': 
+            try:
+                res = grass.read_command("g.region", flags = 'gu', rast = map)
+            except grass.ScriptError:
+                pass
+        elif mapType == 'vector':
+            res = grass.read_command("g.region", flags = 'gu', vect = map)
+        currRegionDict = grass.parse_key_val(res, val_type = float)
+    elif scaleType == 1 and region: # saved region
+        res = grass.read_command("g.region", flags = 'gu', region = region)
+        currRegionDict = grass.parse_key_val(res, val_type = float)
+    elif scaleType == 2: # current region
+        env = grass.gisenv()
+        windFilePath = os.path.join(env['GISDBASE'], env['LOCATION_NAME'], env['MAPSET'], 'WIND')
+        try:
+            windFile = open(windFilePath, 'r').read()
+        except IOError:
+            currRegionDict = grass.region()
+        regionDict = grass.parse_key_val(windFile, sep = ':', val_type = float)
+        region = grass.read_command("g.region", flags = 'gu', n = regionDict['north'], s = regionDict['south'],
+                                    e = regionDict['east'], w = regionDict['west'])
+        currRegionDict = grass.parse_key_val(region, val_type = float)
+                                                                
+    else:
+        return None, None, None
+    
+    if not currRegionDict:
+        return None, None, None
+    rX = rect.x
+    rY = rect.y
+    rW = rect.width
+    rH = rect.height
+    if not hasattr(self, 'unitConv'):
+        self.unitConv = UnitConversion(self)
+    toM = 1
+    if projInfo()['proj'] != 'xy':
+        toM = float(projInfo()['meters'])
+
+    mW = self.unitConv.convert(value = (currRegionDict['e'] - currRegionDict['w']) * toM, fromUnit = 'meter', toUnit = 'inch')
+    mH = self.unitConv.convert(value = (currRegionDict['n'] - currRegionDict['s']) * toM, fromUnit = 'meter', toUnit = 'inch')
+    scale = min(rW/mW, rH/mH)
+    
+    if rW/rH > mW/mH:
+        x = rX - (rH*(mW/mH) - rW)/2
+        y = rY
+        rWNew = rH*(mW/mH)
+        rHNew = rH
+    else:
+        x = rX
+        y = rY - (rW*(mH/mW) - rH)/2
+        rHNew = rW*(mH/mW)
+        rWNew = rW
+
+    # center
+    cE = (currRegionDict['w'] + currRegionDict['e'])/2
+    cN = (currRegionDict['n'] + currRegionDict['s'])/2
+    return scale, (cE, cN), Rect2D(x, y, rWNew, rHNew) #inch
+
+def SetResolution(dpi, width, height):
+    """!If resolution is too high, lower it
+    
+    @param dpi max DPI
+    @param width map frame width
+    @param height map frame height
+    """
+    region = grass.region()
+    if region['cols'] > width * dpi or region['rows'] > height * dpi:
+        rows = height * dpi
+        cols = width * dpi
+        RunCommand('g.region', rows = rows, cols = cols)
+               
+def ComputeSetRegion(self, mapDict):
+    """!Computes and sets region from current scale, map center coordinates and map rectangle"""
+
+    if mapDict['scaleType'] == 3: # fixed scale
+        scale = mapDict['scale']
+            
+        if not hasattr(self, 'unitConv'):
+            self.unitConv = UnitConversion(self)
+        
+        fromM = 1
+        if projInfo()['proj'] != 'xy':
+            fromM = float(projInfo()['meters'])
+        rectHalfInch = (mapDict['rect'].width/2, mapDict['rect'].height/2)
+        rectHalfMeter = (self.unitConv.convert(value = rectHalfInch[0], fromUnit = 'inch', toUnit = 'meter')/ fromM /scale,
+                         self.unitConv.convert(value = rectHalfInch[1], fromUnit = 'inch', toUnit = 'meter')/ fromM /scale) 
+        
+        centerE = mapDict['center'][0]
+        centerN = mapDict['center'][1]
+        
+        raster = self.instruction.FindInstructionByType('raster')
+        if raster:
+            rasterId = raster.id 
+        else:
+            rasterId = None
+
+        if rasterId:
+            RunCommand('g.region', n = ceil(centerN + rectHalfMeter[1]),
+                       s = floor(centerN - rectHalfMeter[1]),
+                       e = ceil(centerE + rectHalfMeter[0]),
+                       w = floor(centerE - rectHalfMeter[0]),
+                       rast = self.instruction[rasterId]['raster'])
+        else:
+            RunCommand('g.region', n = ceil(centerN + rectHalfMeter[1]),
+                       s = floor(centerN - rectHalfMeter[1]),
+                       e = ceil(centerE + rectHalfMeter[0]),
+                       w = floor(centerE - rectHalfMeter[0]))
+                    
+def projInfo():
+    """!Return region projection and map units information,
+    taken from render.py"""
+    
+    projinfo = dict()
+    
+    ret = RunCommand('g.proj', read = True, flags = 'p')
+    
+    if not ret:
+        return projinfo
+    
+    for line in ret.splitlines():
+        if ':' in line:
+            key, val = line.split(':')
+            projinfo[key.strip()] = val.strip()
+        elif "XY location (unprojected)" in line:
+            projinfo['proj'] = 'xy'
+            projinfo['units'] = ''
+            break
+    
+    return projinfo
+
+def GetMapBounds(filename, portrait = True):
+    """!Run ps.map -b to get information about map bounding box
+    
+        @param filename psmap input file
+        @param portrait page orientation"""
+    orient = ''
+    if not portrait:
+        orient = 'r'
+    try:
+        bb = map(float, grass.read_command('ps.map',
+                                           flags = 'b' + orient,
+                                           quiet = True,
+                                           input = filename).strip().split('=')[1].split(','))
+    except (grass.ScriptError, IndexError):
+        GError(message = _("Unable to run `ps.map -b`"))
+        return None
+    return Rect2D(bb[0], bb[3], bb[2] - bb[0], bb[1] - bb[3])
+
+def getRasterType(map):
+    """!Returns type of raster map (CELL, FCELL, DCELL)"""
+    if map is None:
+        map = ''
+    file = grass.find_file(name = map, element = 'cell')
+    if file['file']:
+        rasterType = grass.raster_info(map)['datatype']
+        return rasterType
+    else:
+        return None
+   
+def PilImageToWxImage(pilImage, copyAlpha = True):
+    """!Convert PIL image to wx.Image
+    
+    Based on http://wiki.wxpython.org/WorkingWithImages
+    """
+    hasAlpha = pilImage.mode[-1] == 'A'
+    if copyAlpha and hasAlpha :  # Make sure there is an alpha layer copy.
+        wxImage = wx.EmptyImage( *pilImage.size )
+        pilImageCopyRGBA = pilImage.copy()
+        pilImageCopyRGB = pilImageCopyRGBA.convert('RGB')    # RGBA --> RGB
+        pilImageRgbData = pilImageCopyRGB.tostring()
+        wxImage.SetData(pilImageRgbData)
+        wxImage.SetAlphaData(pilImageCopyRGBA.tostring()[3::4])  # Create layer and insert alpha values.
+
+    else :    # The resulting image will not have alpha.
+        wxImage = wx.EmptyImage(*pilImage.size)
+        pilImageCopy = pilImage.copy()
+        pilImageCopyRGB = pilImageCopy.convert('RGB')    # Discard any alpha from the PIL image.
+        pilImageRgbData = pilImageCopyRGB.tostring()
+        wxImage.SetData(pilImageRgbData)
+
+    return wxImage
+
+def BBoxAfterRotation(w, h, angle):
+    """!Compute bounding box or rotated rectangle
+    
+    @param w rectangle width
+    @param h rectangle height
+    @param angle angle (0, 360) in degrees
+    """
+    angleRad = angle / 180. * pi
+    ct = cos(angleRad)
+    st = sin(angleRad)
+    
+    hct = h * ct
+    wct = w * ct
+    hst = h * st
+    wst = w * st
+    y = x = 0
+    
+    if 0 < angle <= 90:
+        y_min = y
+        y_max = y + hct + wst
+        x_min = x - hst
+        x_max = x + wct
+    elif 90 < angle <= 180:
+        y_min = y + hct
+        y_max = y + wst
+        x_min = x - hst + wct
+        x_max = x
+    elif 180 < angle <= 270:
+        y_min = y + wst + hct
+        y_max = y
+        x_min = x + wct
+        x_max = x - hst
+    elif 270 < angle <= 360:
+        y_min = y + wst
+        y_max = y + hct
+        x_min = x
+        x_max = x + wct - hst
+        
+    width = int(ceil(abs(x_max) + abs(x_min)))
+    height = int(ceil(abs(y_max) + abs(y_min)))
+    return width, height

+ 24 - 16
gui/wxpython/wxpythonlib.dox

@@ -357,25 +357,9 @@ available in GRASS 5 and GRASS 6.
 \subsection psmap Cartograpic Composer
 
 - psmap::dialogs
- - dialogs::UnitConversion
  - dialogs::TCValidator
  - dialogs::PenStyleComboBox
  - dialogs::CheckListCtrl
- - dialogs::Instruction
- - dialogs::InstructionObject
- - dialogs::InitMap
- - dialogs::MapFrame
- - dialogs::PageSetup
- - dialogs::Mapinfo
- - dialogs::Text
- - dialogs::Image
- - dialogs::NorthArrow
- - dialogs::Scalebar
- - dialogs::RasterLegend
- - dialogs::VectorLegend
- - dialogs::Raster
- - dialogs::Vector
- - dialogs::VProperties
  - dialogs::PsmapDialog
  - dialogs::PageSetupDialog
  - dialogs::MapDialog
@@ -391,6 +375,30 @@ available in GRASS 5 and GRASS 6.
  - dialogs::TextDialog
  - dialogs::ImageDialog
  - dialogs::NorthArrowDialog
+- psmap::instructions
+ - dialogs::Instruction
+ - dialogs::InstructionObject
+ - dialogs::InitMap
+ - dialogs::MapFrame
+ - dialogs::PageSetup
+ - dialogs::Mapinfo
+ - dialogs::Text
+ - dialogs::Image
+ - dialogs::NorthArrow
+ - dialogs::Point
+ - dialogs::Line
+ - dialogs::Rectangle
+ - dialogs::Scalebar
+ - dialogs::RasterLegend
+ - dialogs::VectorLegend
+ - dialogs::Raster
+ - dialogs::Vector
+ - dialogs::VProperties
+- psmap::utils
+ - utils::Rect2D
+ - utils::Rect2DPP
+ - utils::Rect2DPS
+ - utils::UnitConversion
 - psmap::frame
  - frame::PsMapFrame
  - frame::PsMapBufferedWindow