4 @brief utilities for wxpsmap (classes, functions)
10 - utils::UnitConversion
12 (C) 2012 by Anna Kratochvilova, and the GRASS Development Team
13 This program is free software under the GNU General Public License
14 (>=v2). Read the file COPYING that comes with GRASS for details.
16 @author Anna Kratochvilova <kratochanna gmail.com>
21 from math
import ceil, floor, sin, cos, pi
24 from PIL
import Image
as PILImage
33 """!Class representing rectangle with floating point values.
35 Overrides wx.Rect2D to unify Rect access methods, which are
36 different (e.g. wx.Rect.GetTopLeft() x wx.Rect2D.GetLeftTop()).
37 More methods can be added depending on needs.
39 def __init__(self, x = 0, y = 0, width = 0, height = 0):
40 wx.Rect2D.__init__(self, x = x, y = y, w = width, h = height)
61 """!Rectangle specified by 2 points (with floating point values).
65 def __init__(self, topLeft = wx.Point2D(), bottomRight = wx.Point2D()):
66 Rect2D.__init__(self, x = 0, y = 0, width = 0, height = 0)
68 x1, y1 = topLeft[0], topLeft[1]
69 x2, y2 = bottomRight[0], bottomRight[1]
71 self.SetLeft(
min(x1, x2))
72 self.SetTop(
min(y1, y2))
73 self.SetRight(
max(x1, x2))
74 self.SetBottom(
max(y1, y2))
77 """!Rectangle specified by point and size (with floating point values).
81 def __init__(self, pos = wx.Point2D(), size = (0, 0)):
82 Rect2D.__init__(self, x = pos[0], y = pos[1], width = size[0], height = size[1])
85 """! Class for converting units"""
89 ppi = wx.ClientDC(self.
parent).GetPPI()
92 self.
_unitsPage = {
'inch' : {
'val': 1.0,
'tr' : _(
"inch")},
93 'point' : {
'val': 72.0,
'tr' : _(
"point")},
94 'centimeter' : {
'val': 2.54,
'tr' : _(
"centimeter")},
95 'millimeter' : {
'val': 25.4,
'tr' : _(
"millimeter")}}
96 self.
_unitsMap = {
'meters' : {
'val': 0.0254,
'tr' : _(
"meters")},
97 'kilometers' : {
'val': 2.54e-5,
'tr' : _(
"kilometers")},
98 'feet' : {
'val': 1./12,
'tr' : _(
"feet")},
99 'miles' : {
'val': 1./63360,
'tr' : _(
"miles")},
100 'nautical miles': {
'val': 1/72913.386,
'tr' : _(
"nautical miles")}}
102 self.
_units = {
'pixel' : {
'val': ppi[0],
'tr' : _(
"pixel")},
103 'meter' : {
'val': 0.0254,
'tr' : _(
"meter")},
104 'nautmiles' : {
'val': 1/72913.386,
'tr' :_(
"nautical miles")},
105 'degrees' : {
'val': 0.0254 ,
'tr' : _(
"degree")}
111 return sorted(self.
_unitsPage[unit][
'tr']
for unit
in self._unitsPage.keys())
114 return sorted(self.
_unitsMap[unit][
'tr']
for unit
in self._unitsMap.keys())
117 return sorted(self._units.keys())
120 """!Returns unit by its tr. string"""
121 for unit
in self._units.keys():
122 if self.
_units[unit][
'tr'] == name:
127 """!Returns tr. string of a unit"""
129 return self.
_units[unit][
'tr']
133 def convert(self, value, fromUnit = None, toUnit = None):
134 return float(value)/self.
_units[fromUnit][
'val']*self.
_units[toUnit][
'val']
137 """!Converts wx.Colour(r,g,b,a) to string 'r:g:b' or named color,
138 or named color/r:g:b string to wx.Colour, depending on input"""
140 if type(rgb) == wx.Colour:
141 for name, color
in grass.named_colors.items():
142 if rgb.Red() == int(color[0] * 255)
and\
143 rgb.Green() == int(color[1] * 255)
and\
144 rgb.Blue() == int(color[2] * 255):
146 return str(rgb.Red()) +
':' + str(rgb.Green()) +
':' + str(rgb.Blue())
149 color = (grass.parse_color(rgb)[0]*255,
150 grass.parse_color(rgb)[1]*255,
151 grass.parse_color(rgb)[2]*255)
152 color = wx.Colour(*color)
160 """!Converts paper (inch) coordinates <-> map coordinates.
162 @param mapInstr map frame instruction
163 @param x,y paper coords in inches or mapcoords in map units
164 @param paperToMap specify conversion direction
166 region = grass.region()
167 mapWidthPaper = mapInstr[
'rect'].GetWidth()
168 mapHeightPaper = mapInstr[
'rect'].GetHeight()
169 mapWidthEN = region[
'e'] - region[
'w']
170 mapHeightEN = region[
'n'] - region[
's']
173 diffX = x - mapInstr[
'rect'].GetX()
174 diffY = y - mapInstr[
'rect'].GetY()
175 diffEW = diffX * mapWidthEN / mapWidthPaper
176 diffNS = diffY * mapHeightEN / mapHeightPaper
177 e = region[
'w'] + diffEW
178 n = region[
'n'] - diffNS
183 return int(e), int(n)
186 diffEW = x - region[
'w']
187 diffNS = region[
'n'] - y
188 diffX = mapWidthPaper * diffEW / mapWidthEN
189 diffY = mapHeightPaper * diffNS / mapHeightEN
190 xPaper = mapInstr[
'rect'].GetX() + diffX
191 yPaper = mapInstr[
'rect'].GetY() + diffY
193 return xPaper, yPaper
196 def AutoAdjust(self, scaleType, rect, map = None, mapType = None, region = None):
197 """!Computes map scale, center and map frame rectangle to fit region (scale is not fixed)"""
199 if scaleType == 0
and map:
201 if mapType ==
'raster':
203 res = grass.read_command(
"g.region", flags =
'gu', rast = map)
204 except grass.ScriptError:
206 elif mapType ==
'vector':
207 res = grass.read_command(
"g.region", flags =
'gu', vect = map)
208 currRegionDict = grass.parse_key_val(res, val_type = float)
209 elif scaleType == 1
and region:
210 res = grass.read_command(
"g.region", flags =
'gu', region = region)
211 currRegionDict = grass.parse_key_val(res, val_type = float)
214 windFilePath = os.path.join(env[
'GISDBASE'], env[
'LOCATION_NAME'], env[
'MAPSET'],
'WIND')
216 windFile = open(windFilePath,
'r').read()
218 currRegionDict = grass.region()
219 regionDict = grass.parse_key_val(windFile, sep =
':', val_type = float)
220 region = grass.read_command(
"g.region", flags =
'gu', n = regionDict[
'north'], s = regionDict[
'south'],
221 e = regionDict[
'east'], w = regionDict[
'west'])
222 currRegionDict = grass.parse_key_val(region, val_type = float)
225 return None,
None,
None
227 if not currRegionDict:
228 return None,
None,
None
233 if not hasattr(self,
'unitConv'):
239 mW = self.unitConv.convert(value = (currRegionDict[
'e'] - currRegionDict[
'w']) * toM, fromUnit =
'meter', toUnit =
'inch')
240 mH = self.unitConv.convert(value = (currRegionDict[
'n'] - currRegionDict[
's']) * toM, fromUnit =
'meter', toUnit =
'inch')
241 scale =
min(rW/mW, rH/mH)
244 x = rX - (rH*(mW/mH) - rW)/2
250 y = rY - (rW*(mH/mW) - rH)/2
255 cE = (currRegionDict[
'w'] + currRegionDict[
'e'])/2
256 cN = (currRegionDict[
'n'] + currRegionDict[
's'])/2
257 return scale, (cE, cN),
Rect2D(x, y, rWNew, rHNew)
260 """!If resolution is too high, lower it
263 @param width map frame width
264 @param height map frame height
266 region = grass.region()
267 if region[
'cols'] > width * dpi
or region[
'rows'] > height * dpi:
270 RunCommand(
'g.region', rows = rows, cols = cols)
273 """!Computes and sets region from current scale, map center coordinates and map rectangle"""
275 if mapDict[
'scaleType'] == 3:
276 scale = mapDict[
'scale']
278 if not hasattr(self,
'unitConv'):
284 rectHalfInch = (mapDict[
'rect'].width/2, mapDict[
'rect'].height/2)
285 rectHalfMeter = (self.unitConv.convert(value = rectHalfInch[0], fromUnit =
'inch', toUnit =
'meter')/ fromM /scale,
286 self.unitConv.convert(value = rectHalfInch[1], fromUnit =
'inch', toUnit =
'meter')/ fromM /scale)
288 centerE = mapDict[
'center'][0]
289 centerN = mapDict[
'center'][1]
291 raster = self.instruction.FindInstructionByType(
'raster')
298 RunCommand(
'g.region', n = ceil(centerN + rectHalfMeter[1]),
299 s = floor(centerN - rectHalfMeter[1]),
300 e = ceil(centerE + rectHalfMeter[0]),
301 w = floor(centerE - rectHalfMeter[0]),
302 rast = self.instruction[rasterId][
'raster'])
304 RunCommand(
'g.region', n = ceil(centerN + rectHalfMeter[1]),
305 s = floor(centerN - rectHalfMeter[1]),
306 e = ceil(centerE + rectHalfMeter[0]),
307 w = floor(centerE - rectHalfMeter[0]))
310 """!Return region projection and map units information,
311 taken from render.py"""
315 ret =
RunCommand(
'g.proj', read =
True, flags =
'p')
320 for line
in ret.splitlines():
322 key, val = line.split(
':')
323 projinfo[key.strip()] = val.strip()
324 elif "XY location (unprojected)" in line:
325 projinfo[
'proj'] =
'xy'
326 projinfo[
'units'] =
''
332 """!Run ps.map -b to get information about map bounding box
334 @param filename psmap input file
335 @param portrait page orientation"""
340 bb = map(float, grass.read_command(
'ps.map',
341 flags =
'b' + orient,
343 input = filename).strip().
split(
'=')[1].
split(
','))
344 except (grass.ScriptError, IndexError):
345 GError(message = _(
"Unable to run `ps.map -b`"))
347 return Rect2D(bb[0], bb[3], bb[2] - bb[0], bb[1] - bb[3])
350 """!Returns type of raster map (CELL, FCELL, DCELL)"""
353 file = grass.find_file(name = map, element =
'cell')
355 rasterType = grass.raster_info(map)[
'datatype']
361 """!Convert PIL image to wx.Image
363 Based on http://wiki.wxpython.org/WorkingWithImages
365 hasAlpha = pilImage.mode[-1] ==
'A'
366 if copyAlpha
and hasAlpha :
367 wxImage = wx.EmptyImage( *pilImage.size )
368 pilImageCopyRGBA = pilImage.copy()
369 pilImageCopyRGB = pilImageCopyRGBA.convert(
'RGB')
370 pilImageRgbData = pilImageCopyRGB.tostring()
371 wxImage.SetData(pilImageRgbData)
372 wxImage.SetAlphaData(pilImageCopyRGBA.tostring()[3::4])
375 wxImage = wx.EmptyImage(*pilImage.size)
376 pilImageCopy = pilImage.copy()
377 pilImageCopyRGB = pilImageCopy.convert(
'RGB')
378 pilImageRgbData = pilImageCopyRGB.tostring()
379 wxImage.SetData(pilImageRgbData)
384 """!Compute bounding box or rotated rectangle
386 @param w rectangle width
387 @param h rectangle height
388 @param angle angle (0, 360) in degrees
390 angleRad = angle / 180. * pi
402 y_max = y + hct + wst
405 elif 90 < angle <= 180:
408 x_min = x - hst + wct
410 elif 180 < angle <= 270:
411 y_min = y + wst + hct
415 elif 270 < angle <= 360:
419 x_max = x + wct - hst
421 width = int(ceil(abs(x_max) + abs(x_min)))
422 height = int(ceil(abs(y_max) + abs(y_min)))
432 self.mode = self.im.mode
433 self.size = self.im.size
437 """Render an image using Ghostscript (Windows only)"""
439 decoder, tile, offset, data = tile[0]
444 file = tempfile.mkstemp()[1]
447 command = [
"gswin32c",
452 "-sOutputFile=%s" % file
455 command = string.join(command)
459 gs = os.popen(command,
"w")
461 if bbox[0] != 0
or bbox[1] != 0:
462 gs.write(
"%d %d translate\n" % (-bbox[0], -bbox[1]))
468 length = length - len(s)
472 raise IOError(
"gs failed (status %d)" % status)
473 im = PILImage.core.open_ppm(file)
def convertRGB
Converts wx.Colour(r,g,b,a) to string 'r:g:b' or named color, or named color/r:g:b string to wx...
def projInfo
Return region projection and map units information, taken from render.py.
def getRasterType
Returns type of raster map (CELL, FCELL, DCELL)
def PaperMapCoordinates
Converts paper (inch) coordinates <-> map coordinates.
Class representing rectangle with floating point values.
def SetResolution
If resolution is too high, lower it.
def AutoAdjust
Computes map scale, center and map frame rectangle to fit region (scale is not fixed) ...
Rectangle specified by point and size (with floating point values).
def split
Platform spefic shlex.split.
def ComputeSetRegion
Computes and sets region from current scale, map center coordinates and map rectangle.
def BBoxAfterRotation
Compute bounding box or rotated rectangle.
Rectangle specified by 2 points (with floating point values).
Class for converting units.
def PilImageToWxImage
Convert PIL image to wx.Image.
def GhostscriptForWindows
def GetMapBounds
Run ps.map -b to get information about map bounding box.
def findUnit
Returns unit by its tr.
def RunCommand
Run GRASS command.