GRASS Programmer's Manual  6.4.4(2014)-r
mapdisp/mapwindow.py
Go to the documentation of this file.
1 """!
2 @package mapdisp.mapwindow
3 
4 @brief Map display canvas - buffered window.
5 
6 Classes:
7  - mapwindow::BufferedWindow
8 
9 (C) 2006-2011 by the GRASS Development Team
10 
11 This program is free software under the GNU General Public License
12 (>=v2). Read the file COPYING that comes with GRASS for details.
13 
14 @author Martin Landa <landa.martin gmail.com>
15 @author Michael Barton
16 @author Jachym Cepicky
17 """
18 
19 import os
20 import time
21 import math
22 import sys
23 
24 import wx
25 
26 import grass.script as grass
27 
28 from gui_core.dialogs import SavedRegion
29 from core.gcmd import RunCommand, GException, GError, GMessage
30 from core.debug import Debug
31 from core.settings import UserSettings
32 from gui_core.mapwindow import MapWindow
33 try:
34  import grass.lib.gis as gislib
35  haveCtypes = True
36 except ImportError:
37  haveCtypes = False
38 
39 class BufferedWindow(MapWindow, wx.Window):
40  """!A Buffered window class (2D view mode)
41 
42  Superclass for VDigitWindow (vector digitizer).
43 
44  When the drawing needs to change, you app needs to call the
45  UpdateMap() method. Since the drawing is stored in a bitmap, you
46  can also save the drawing to file by calling the
47  SaveToFile() method.
48  """
49  def __init__(self, parent, id = wx.ID_ANY,
50  Map = None, tree = None, lmgr = None, overlays = None,
51  style = wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
52  MapWindow.__init__(self, parent, id, Map, tree, lmgr, **kwargs)
53  wx.Window.__init__(self, parent, id, style = style, **kwargs)
54 
55  # flags
56  self.resize = False # indicates whether or not a resize event has taken place
57  self.dragimg = None # initialize variable for map panning
58 
59  # variables for drawing on DC
60  self.pen = None # pen for drawing zoom boxes, etc.
61  self.polypen = None # pen for drawing polylines (measurements, profiles, etc)
62  # List of wx.Point tuples defining a polyline (geographical coordinates)
63  self.polycoords = []
64  # ID of rubber band line
65  self.lineid = None
66  # ID of poly line resulting from cumulative rubber band lines (e.g. measurement)
67  self.plineid = None
68 
69  # event bindings
70  self.Bind(wx.EVT_PAINT, self.OnPaint)
71  self.Bind(wx.EVT_SIZE, self.OnSize)
72  self.Bind(wx.EVT_IDLE, self.OnIdle)
73  self._bindMouseEvents()
74 
75  self.processMouse = True
76 
77  # render output objects
78  self.mapfile = None # image file to be rendered
79  self.img = None # wx.Image object (self.mapfile)
80  # decoration overlays
81  self.overlays = overlays
82  # images and their PseudoDC ID's for painting and dragging
83  self.imagedict = {}
84  self.select = {} # selecting/unselecting decorations for dragging
85  self.textdict = {} # text, font, and color indexed by id
86  self.currtxtid = None # PseudoDC id for currently selected text
87 
88  # zoom objects
89  self.zoomhistory = [] # list of past zoom extents
90  self.currzoom = 0 # current set of extents in zoom history being used
91  self.zoomtype = 1 # 1 zoom in, 0 no zoom, -1 zoom out
92  self.hitradius = 10 # distance for selecting map decorations
93  self.dialogOffset = 5 # offset for dialog (e.g. DisplayAttributesDialog)
94 
95  # OnSize called to make sure the buffer is initialized.
96  # This might result in OnSize getting called twice on some
97  # platforms at initialization, but little harm done.
98  ### self.OnSize(None)
99 
100  self._definePseudoDC()
101  # redraw all pdc's, pdcTmp layer is redrawn always (speed issue)
102  self.redrawAll = True
103 
104  # will store an off screen empty bitmap for saving to file
105  self._buffer = None
106 
107  self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x:None)
108 
109  # vars for handling mouse clicks
110  self.dragid = -1
111  self.lastpos = (0, 0)
112 
113  def _definePseudoDC(self):
114  """!Define PseudoDC objects to use
115  """
116  # create PseudoDC used for background map, map decorations like scales and legends
117  self.pdc = wx.PseudoDC()
118  # used for digitization tool
119  self.pdcVector = None
120  # decorations (region box, etc.)
121  self.pdcDec = wx.PseudoDC()
122  # pseudoDC for temporal objects (select box, measurement tool, etc.)
123  self.pdcTmp = wx.PseudoDC()
124 
125  def _bindMouseEvents(self):
126  self.Bind(wx.EVT_MOUSE_EVENTS, self.MouseActions)
127  self.Bind(wx.EVT_MOTION, self.OnMotion)
128 
129  def Draw(self, pdc, img = None, drawid = None, pdctype = 'image', coords = [0, 0, 0, 0]):
130  """!Draws map and overlay decorations
131  """
132  if drawid == None:
133  if pdctype == 'image' and img:
134  drawid = self.imagedict[img]
135  elif pdctype == 'clear':
136  drawid == None
137  else:
138  drawid = wx.NewId()
139 
140  if img and pdctype == 'image':
141  # self.imagedict[img]['coords'] = coords
142  self.select[self.imagedict[img]['id']] = False # ?
143 
144  pdc.BeginDrawing()
145 
146  if drawid != 99:
147  bg = wx.TRANSPARENT_BRUSH
148  else:
149  bg = wx.Brush(self.GetBackgroundColour())
150 
151  pdc.SetBackground(bg)
152 
153  Debug.msg (5, "BufferedWindow.Draw(): id=%s, pdctype = %s, coord=%s" % \
154  (drawid, pdctype, coords))
155 
156  # set PseudoDC id
157  if drawid is not None:
158  pdc.SetId(drawid)
159 
160  if pdctype == 'clear': # erase the display
161  bg = wx.WHITE_BRUSH
162  # bg = wx.Brush(self.GetBackgroundColour())
163  pdc.SetBackground(bg)
164  pdc.RemoveAll()
165  pdc.Clear()
166  pdc.EndDrawing()
167 
168  self.Refresh()
169  return
170 
171  if pdctype == 'image': # draw selected image
172  bitmap = wx.BitmapFromImage(img)
173  w,h = bitmap.GetSize()
174  pdc.DrawBitmap(bitmap, coords[0], coords[1], True) # draw the composite map
175  pdc.SetIdBounds(drawid, wx.Rect(coords[0],coords[1], w, h))
176 
177  elif pdctype == 'box': # draw a box on top of the map
178  if self.pen:
179  pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
180  pdc.SetPen(self.pen)
181  x2 = max(coords[0],coords[2])
182  x1 = min(coords[0],coords[2])
183  y2 = max(coords[1],coords[3])
184  y1 = min(coords[1],coords[3])
185  rwidth = x2-x1
186  rheight = y2-y1
187  rect = wx.Rect(x1, y1, rwidth, rheight)
188  pdc.DrawRectangleRect(rect)
189  pdc.SetIdBounds(drawid, rect)
190 
191  elif pdctype == 'line': # draw a line on top of the map
192  if self.pen:
193  pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
194  pdc.SetPen(self.pen)
195  pdc.DrawLinePoint(wx.Point(coords[0], coords[1]),wx.Point(coords[2], coords[3]))
196  pdc.SetIdBounds(drawid, wx.Rect(coords[0], coords[1], coords[2], coords[3]))
197 
198  elif pdctype == 'polyline': # draw a polyline on top of the map
199  if self.polypen:
200  pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
201  pdc.SetPen(self.polypen)
202  if (len(coords) < 2):
203  return
204  i = 1
205  while i < len(coords):
206  pdc.DrawLinePoint(wx.Point(coords[i-1][0], coords[i-1][1]),
207  wx.Point(coords[i][0], coords[i][1]))
208  i += 1
209 
210  # get bounding rectangle for polyline
211  xlist = []
212  ylist = []
213  if len(coords) > 0:
214  for point in coords:
215  x,y = point
216  xlist.append(x)
217  ylist.append(y)
218  x1 = min(xlist)
219  x2 = max(xlist)
220  y1 = min(ylist)
221  y2 = max(ylist)
222  pdc.SetIdBounds(drawid, wx.Rect(x1,y1,x2,y2))
223  # self.ovlcoords[drawid] = [x1,y1,x2,y2]
224 
225  elif pdctype == 'point': # draw point
226  if self.pen:
227  pdc.SetPen(self.pen)
228  pdc.DrawPoint(coords[0], coords[1])
229  coordsBound = (coords[0] - 5,
230  coords[1] - 5,
231  coords[0] + 5,
232  coords[1] + 5)
233  pdc.SetIdBounds(drawid, wx.Rect(coordsBound))
234 
235  elif pdctype == 'text': # draw text on top of map
236  if not img['active']:
237  return # only draw active text
238  if 'rotation' in img:
239  rotation = float(img['rotation'])
240  else:
241  rotation = 0.0
242  w, h = self.GetFullTextExtent(img['text'])[0:2]
243  pdc.SetFont(img['font'])
244  pdc.SetTextForeground(img['color'])
245  coords, bbox = self.TextBounds(img)
246  if rotation == 0:
247  pdc.DrawText(img['text'], coords[0], coords[1])
248  else:
249  pdc.DrawRotatedText(img['text'], coords[0], coords[1], rotation)
250  pdc.SetIdBounds(drawid, bbox)
251 
252  pdc.EndDrawing()
253 
254  self.Refresh()
255 
256  return drawid
257 
258  def TextBounds(self, textinfo, relcoords = False):
259  """!Return text boundary data
260 
261  @param textinfo text metadata (text, font, color, rotation)
262  @param coords reference point
263 
264  @return coords of nonrotated text bbox (TL corner)
265  @return bbox of rotated text bbox (wx.Rect)
266  @return relCoords are text coord inside bbox
267  """
268  if 'rotation' in textinfo:
269  rotation = float(textinfo['rotation'])
270  else:
271  rotation = 0.0
272 
273  coords = textinfo['coords']
274  bbox = wx.Rect(coords[0], coords[1], 0, 0)
275  relCoords = (0, 0)
276  Debug.msg (4, "BufferedWindow.TextBounds(): text=%s, rotation=%f" % \
277  (textinfo['text'], rotation))
278 
279  self.Update()
280 
281  self.SetFont(textinfo['font'])
282 
283  w, h = self.GetTextExtent(textinfo['text'])
284 
285  if rotation == 0:
286  bbox[2], bbox[3] = w, h
287  if relcoords:
288  return coords, bbox, relCoords
289  else:
290  return coords, bbox
291 
292  boxh = math.fabs(math.sin(math.radians(rotation)) * w) + h
293  boxw = math.fabs(math.cos(math.radians(rotation)) * w) + h
294  if rotation > 0 and rotation < 90:
295  bbox[1] -= boxh
296  relCoords = (0, boxh)
297  elif rotation >= 90 and rotation < 180:
298  bbox[0] -= boxw
299  bbox[1] -= boxh
300  relCoords = (boxw, boxh)
301  elif rotation >= 180 and rotation < 270:
302  bbox[0] -= boxw
303  relCoords = (boxw, 0)
304  bbox[2] = boxw
305  bbox[3] = boxh
306  bbox.Inflate(h,h)
307  if relcoords:
308  return coords, bbox, relCoords
309  else:
310  return coords, bbox
311 
312  def OnPaint(self, event):
313  """!Draw PseudoDC's to buffered paint DC
314 
315  If self.redrawAll is False on self.pdcTmp content is re-drawn
316  """
317  Debug.msg(4, "BufferedWindow.OnPaint(): redrawAll=%s" % self.redrawAll)
318 
319  dc = wx.BufferedPaintDC(self, self.buffer)
320  dc.Clear()
321 
322  # use PrepareDC to set position correctly
323  # probably does nothing, removed from wxPython 2.9
324  # self.PrepareDC(dc)
325 
326  # create a clipping rect from our position and size
327  # and update region
328  rgn = self.GetUpdateRegion().GetBox()
329  dc.SetClippingRect(rgn)
330 
331  switchDraw = False
332  if self.redrawAll is None:
333  self.redrawAll = True
334  switchDraw = True
335 
336  if self.redrawAll: # redraw pdc and pdcVector
337  # draw to the dc using the calculated clipping rect
338  self.pdc.DrawToDCClipped(dc, rgn)
339 
340  # draw vector map layer
341  if hasattr(self, "digit"):
342  # decorate with GDDC (transparency)
343  try:
344  gcdc = wx.GCDC(dc)
345  self.pdcVector.DrawToDCClipped(gcdc, rgn)
346  except NotImplementedError, e:
347  print >> sys.stderr, e
348  self.pdcVector.DrawToDCClipped(dc, rgn)
349 
350  self.bufferLast = None
351  else: # do not redraw pdc and pdcVector
352  if self.bufferLast is None:
353  # draw to the dc
354  self.pdc.DrawToDC(dc)
355 
356  if hasattr(self, "digit"):
357  # decorate with GDDC (transparency)
358  try:
359  gcdc = wx.GCDC(dc)
360  self.pdcVector.DrawToDC(gcdc)
361  except NotImplementedError, e:
362  print >> sys.stderr, e
363  self.pdcVector.DrawToDC(dc)
364 
365  # store buffered image
366  # self.bufferLast = wx.BitmapFromImage(self.buffer.ConvertToImage())
367  self.bufferLast = dc.GetAsBitmap(wx.Rect(0, 0, self.Map.width, self.Map.height))
368 
369  self.pdc.DrawBitmap(self.bufferLast, 0, 0, False)
370  self.pdc.DrawToDC(dc)
371 
372  # draw decorations (e.g. region box)
373  try:
374  gcdc = wx.GCDC(dc)
375  self.pdcDec.DrawToDC(gcdc)
376  except NotImplementedError, e:
377  print >> sys.stderr, e
378  self.pdcDec.DrawToDC(dc)
379 
380  # draw temporary object on the foreground
381  ### self.pdcTmp.DrawToDCClipped(dc, rgn)
382  self.pdcTmp.DrawToDC(dc)
383 
384  if switchDraw:
385  self.redrawAll = False
386 
387  def OnSize(self, event):
388  """!Scale map image so that it is the same size as the Window
389  """
390  Debug.msg(3, "BufferedWindow.OnSize():")
391 
392  # set size of the input image
393  self.Map.ChangeMapSize(self.GetClientSize())
394  # align extent based on center point and display resolution
395  # this causes that image is not resized when display windows is resized
396  ### self.Map.AlignExtentFromDisplay()
397 
398  # Make new off screen bitmap: this bitmap will always have the
399  # current drawing in it, so it can be used to save the image to
400  # a file, or whatever.
401  self.buffer = wx.EmptyBitmap(max(1, self.Map.width), max(1, self.Map.height))
402 
403  # get the image to be rendered
404  self.img = self.GetImage()
405 
406  # update map display
407  if self.img and self.Map.width + self.Map.height > 0: # scale image during resize
408  self.img = self.img.Scale(self.Map.width, self.Map.height)
409  if len(self.Map.GetListOfLayers()) > 0:
410  self.UpdateMap()
411 
412  # re-render image on idle
413  self.resize = True
414 
415  # reposition checkbox in statusbar
416  self.parent.StatusbarReposition()
417 
418  # update statusbar
419  self.parent.StatusbarUpdate()
420 
421  def OnIdle(self, event):
422  """!Only re-render a composite map image from GRASS during
423  idle time instead of multiple times during resizing.
424  """
425  if self.resize:
426  self.UpdateMap(render = True)
427 
428  event.Skip()
429 
430  def SaveToFile(self, FileName, FileType, width, height):
431  """!This draws the pseudo DC to a buffer that can be saved to
432  a file.
433 
434  @param FileName file name
435  @param FileType type of bitmap
436  @param width image width
437  @param height image height
438  """
439  busy = wx.BusyInfo(message = _("Please wait, exporting image..."),
440  parent = self)
441  wx.Yield()
442 
443  self.Map.ChangeMapSize((width, height))
444  ibuffer = wx.EmptyBitmap(max(1, width), max(1, height))
445  self.Map.Render(force = True, windres = True)
446  img = self.GetImage()
447  self.pdc.RemoveAll()
448  self.Draw(self.pdc, img, drawid = 99)
449 
450  # compute size ratio to move overlay accordingly
451  cSize = self.GetClientSizeTuple()
452  ratio = float(width) / cSize[0], float(height) / cSize[1]
453 
454  # redraw lagend, scalebar
455  for img in self.GetOverlay():
456  # draw any active and defined overlays
457  if self.imagedict[img]['layer'].IsActive():
458  id = self.imagedict[img]['id']
459  coords = int(ratio[0] * self.overlays[id].coords[0]),\
460  int(ratio[1] * self.overlays[id].coords[1])
461  self.Draw(self.pdc, img = img, drawid = id,
462  pdctype = self.overlays[id].pdcType, coords = coords)
463 
464  # redraw text labels
465  for id in self.textdict.keys():
466  textinfo = self.textdict[id]
467  oldCoords = textinfo['coords']
468  textinfo['coords'] = ratio[0] * textinfo['coords'][0],\
469  ratio[1] * textinfo['coords'][1]
470  self.Draw(self.pdc, img = self.textdict[id], drawid = id,
471  pdctype = 'text')
472  # set back old coordinates
473  textinfo['coords'] = oldCoords
474 
475  dc = wx.BufferedDC(None, ibuffer)
476  dc.Clear()
477  # probably does nothing, removed from wxPython 2.9
478  # self.PrepareDC(dc)
479  self.pdc.DrawToDC(dc)
480  if self.pdcVector:
481  self.pdcVector.DrawToDC(dc)
482  ibuffer.SaveFile(FileName, FileType)
483 
484  busy.Destroy()
485 
486  self.UpdateMap(render = True)
487  self.Refresh()
488 
489  def GetOverlay(self):
490  """!Converts rendered overlay files to wx.Image
491 
492  Updates self.imagedict
493 
494  @return list of images
495  """
496  imgs = []
497  for overlay in self.Map.GetListOfLayers(l_type = "overlay", l_active = True):
498  if overlay.mapfile is not None \
499  and os.path.isfile(overlay.mapfile) and os.path.getsize(overlay.mapfile):
500  img = wx.Image(overlay.mapfile, wx.BITMAP_TYPE_ANY)
501 
502  for key in self.imagedict.keys():
503  if self.imagedict[key]['id'] == overlay.id:
504  del self.imagedict[key]
505 
506  self.imagedict[img] = { 'id' : overlay.id,
507  'layer' : overlay }
508  imgs.append(img)
509 
510  return imgs
511 
512  def GetImage(self):
513  """!Converts redered map files to wx.Image
514 
515  Updates self.imagedict (id=99)
516 
517  @return wx.Image instance (map composition)
518  """
519  imgId = 99
520  if self.mapfile and self.Map.mapfile and os.path.isfile(self.Map.mapfile) and \
521  os.path.getsize(self.Map.mapfile):
522  img = wx.Image(self.Map.mapfile, wx.BITMAP_TYPE_ANY)
523  else:
524  img = None
525 
526  for key in self.imagedict.keys():
527  if self.imagedict[key]['id'] == imgId:
528  del self.imagedict[key]
529 
530  self.imagedict[img] = { 'id': imgId }
531 
532  return img
533 
534  def UpdateMap(self, render = True, renderVector = True):
535  """!Updates the canvas anytime there is a change to the
536  underlaying images or to the geometry of the canvas.
537 
538  @param render re-render map composition
539  @param renderVector re-render vector map layer enabled for editing (used for digitizer)
540  """
541  start = time.clock()
542 
543  self.resize = False
544 
545  if self.img is None:
546  render = True
547 
548  #
549  # initialize process bar (only on 'render')
550  #
551  if render or renderVector:
552  self.parent.GetProgressBar().Show()
553  if self.parent.GetProgressBar().GetRange() > 0:
554  self.parent.GetProgressBar().SetValue(1)
555 
556  #
557  # render background image if needed
558  #
559  # update layer dictionary if there has been a change in layers
560  if self.tree and self.tree.reorder:
561  self.tree.ReorderLayers()
562 
563  # reset flag for auto-rendering
564  if self.tree:
565  self.tree.rerender = False
566 
567  try:
568  if render:
569  # update display size
570  self.Map.ChangeMapSize(self.GetClientSize())
571  if self.parent.GetProperty('resolution'):
572  # use computation region resolution for rendering
573  windres = True
574  else:
575  windres = False
576  self.mapfile = self.Map.Render(force = True, mapWindow = self.parent,
577  windres = windres)
578  else:
579  self.mapfile = self.Map.Render(force = False, mapWindow = self.parent)
580  except GException, e:
581  GError(message = e.value)
582  self.mapfile = None
583 
584  self.img = self.GetImage() # id=99
585 
586  #
587  # clear pseudoDcs
588  #
589  for pdc in (self.pdc,
590  self.pdcDec,
591  self.pdcTmp):
592  pdc.Clear()
593  pdc.RemoveAll()
594 
595  #
596  # draw background map image to PseudoDC
597  #
598  if not self.img:
599  self.Draw(self.pdc, pdctype = 'clear')
600  else:
601  try:
602  id = self.imagedict[self.img]['id']
603  except:
604  return False
605 
606  self.Draw(self.pdc, self.img, drawid = id)
607 
608  #
609  # render vector map layer
610  #
611  if renderVector and hasattr(self, "digit"):
612  self._updateMap()
613  #
614  # render overlays
615  #
616  for img in self.GetOverlay():
617  # draw any active and defined overlays
618  if self.imagedict[img]['layer'].IsActive():
619  id = self.imagedict[img]['id']
620  self.Draw(self.pdc, img = img, drawid = id,
621  pdctype = self.overlays[id].pdcType, coords = self.overlays[id].coords)
622 
623  for id in self.textdict.keys():
624  self.Draw(self.pdc, img = self.textdict[id], drawid = id,
625  pdctype = 'text', coords = [10, 10, 10, 10])
626 
627  # optionally draw computational extent box
628  self.DrawCompRegionExtent()
629 
630  #
631  # redraw pdcTmp if needed
632  #
633  if len(self.polycoords) > 0:
634  self.DrawLines(self.pdcTmp)
635 
636  if not self.parent.IsStandalone() and \
637  self.parent.GetLayerManager().gcpmanagement:
638  # -> georectifier (redraw GCPs)
639  if self.parent.GetMapToolbar():
640  if self == self.parent.TgtMapWindow:
641  coordtype = 'target'
642  else:
643  coordtype = 'source'
644 
645  self.parent.DrawGCP(coordtype)
646 
647  #
648  # clear measurement
649  #
650  if self.mouse["use"] == "measure":
651  self.ClearLines(pdc = self.pdcTmp)
652  self.polycoords = []
653  self.mouse['use'] = 'pointer'
654  self.mouse['box'] = 'point'
655  self.mouse['end'] = [0, 0]
656  self.SetCursor(self.parent.cursors["default"])
657 
658  stop = time.clock()
659 
660  #
661  # hide process bar
662  #
663  self.parent.GetProgressBar().Hide()
664 
665  #
666  # update statusbar
667  #
668  ### self.Map.SetRegion()
669  self.parent.StatusbarUpdate()
670 
671  Debug.msg (1, "BufferedWindow.UpdateMap(): render=%s, renderVector=%s -> time=%g" % \
672  (render, renderVector, (stop-start)))
673 
674  return True
675 
677  """!Draw computational region extent in the display
678 
679  Display region is drawn as a blue box inside the computational region,
680  computational region inside a display region as a red box).
681  """
682  if hasattr(self, "regionCoords"):
683  compReg = self.Map.GetRegion()
684  dispReg = self.Map.GetCurrentRegion()
685  reg = None
686  if self.IsInRegion(dispReg, compReg):
687  self.polypen = wx.Pen(colour = wx.Colour(0, 0, 255, 128), width = 3, style = wx.SOLID)
688  reg = dispReg
689  else:
690  self.polypen = wx.Pen(colour = wx.Colour(255, 0, 0, 128),
691  width = 3, style = wx.SOLID)
692  reg = compReg
693 
694  self.regionCoords = []
695  self.regionCoords.append((reg['w'], reg['n']))
696  self.regionCoords.append((reg['e'], reg['n']))
697  self.regionCoords.append((reg['e'], reg['s']))
698  self.regionCoords.append((reg['w'], reg['s']))
699  self.regionCoords.append((reg['w'], reg['n']))
700  # draw region extent
701  self.DrawLines(pdc = self.pdcDec, polycoords = self.regionCoords)
702 
703  def IsInRegion(self, region, refRegion):
704  """!
705  Test if 'region' is inside of 'refRegion'
706 
707  @param region input region
708  @param refRegion reference region (e.g. computational region)
709 
710  @return True if region is inside of refRegion
711  @return False
712  """
713  if region['s'] >= refRegion['s'] and \
714  region['n'] <= refRegion['n'] and \
715  region['w'] >= refRegion['w'] and \
716  region['e'] <= refRegion['e']:
717  return True
718 
719  return False
720 
721  def EraseMap(self):
722  """!Erase map canvas
723  """
724  self.Draw(self.pdc, pdctype = 'clear')
725 
726  if hasattr(self, "digit"):
727  self.Draw(self.pdcVector, pdctype = 'clear')
728 
729  self.Draw(self.pdcDec, pdctype = 'clear')
730  self.Draw(self.pdcTmp, pdctype = 'clear')
731 
732  def DragMap(self, moveto):
733  """!Drag the entire map image for panning.
734 
735  @param moveto dx,dy
736  """
737  dc = wx.BufferedDC(wx.ClientDC(self))
738  dc.SetBackground(wx.Brush("White"))
739  dc.Clear()
740 
741  self.dragimg = wx.DragImage(self.buffer)
742  self.dragimg.BeginDrag((0, 0), self)
743  self.dragimg.GetImageRect(moveto)
744  self.dragimg.Move(moveto)
745 
746  self.dragimg.DoDrawImage(dc, moveto)
747  self.dragimg.EndDrag()
748 
749  def DragItem(self, id, event):
750  """!Drag an overlay decoration item
751  """
752  if id == 99 or id == '' or id == None: return
753  Debug.msg (5, "BufferedWindow.DragItem(): id=%d" % id)
754  x, y = self.lastpos
755  dx = event.GetX() - x
756  dy = event.GetY() - y
757  self.pdc.SetBackground(wx.Brush(self.GetBackgroundColour()))
758  r = self.pdc.GetIdBounds(id)
759  if type(r) is list:
760  r = wx.Rect(r[0], r[1], r[2], r[3])
761  if id > 100: # text dragging
762  rtop = (r[0],r[1]-r[3],r[2],r[3])
763  r = r.Union(rtop)
764  rleft = (r[0]-r[2],r[1],r[2],r[3])
765  r = r.Union(rleft)
766  self.pdc.TranslateId(id, dx, dy)
767 
768  r2 = self.pdc.GetIdBounds(id)
769  if type(r2) is list:
770  r2 = wx.Rect(r[0], r[1], r[2], r[3])
771  if id > 100: # text
772  self.textdict[id]['bbox'] = r2
773  self.textdict[id]['coords'][0] += dx
774  self.textdict[id]['coords'][1] += dy
775  r = r.Union(r2)
776  r.Inflate(4,4)
777  self.RefreshRect(r, False)
778  self.lastpos = (event.GetX(), event.GetY())
779 
780  def MouseDraw(self, pdc = None, begin = None, end = None):
781  """!Mouse box or line from 'begin' to 'end'
782 
783  If not given from self.mouse['begin'] to self.mouse['end'].
784  """
785  if not pdc:
786  return
787 
788  if begin is None:
789  begin = self.mouse['begin']
790  if end is None:
791  end = self.mouse['end']
792 
793  Debug.msg (5, "BufferedWindow.MouseDraw(): use=%s, box=%s, begin=%f,%f, end=%f,%f" % \
794  (self.mouse['use'], self.mouse['box'],
795  begin[0], begin[1], end[0], end[1]))
796 
797  if self.mouse['box'] == "box":
798  boxid = wx.ID_NEW
799  mousecoords = [begin[0], begin[1],
800  end[0], end[1]]
801  r = pdc.GetIdBounds(boxid)
802  if type(r) is list:
803  r = wx.Rect(r[0], r[1], r[2], r[3])
804  r.Inflate(4, 4)
805  try:
806  pdc.ClearId(boxid)
807  except:
808  pass
809  self.RefreshRect(r, False)
810  pdc.SetId(boxid)
811  self.Draw(pdc, drawid = boxid, pdctype = 'box', coords = mousecoords)
812 
813  elif self.mouse['box'] == "line":
814  self.lineid = wx.ID_NEW
815  mousecoords = [begin[0], begin[1], \
816  end[0], end[1]]
817  x1 = min(begin[0],end[0])
818  x2 = max(begin[0],end[0])
819  y1 = min(begin[1],end[1])
820  y2 = max(begin[1],end[1])
821  r = wx.Rect(x1,y1,x2-x1,y2-y1)
822  r.Inflate(4,4)
823  try:
824  pdc.ClearId(self.lineid)
825  except:
826  pass
827  self.RefreshRect(r, False)
828  pdc.SetId(self.lineid)
829  self.Draw(pdc, drawid = self.lineid, pdctype = 'line', coords = mousecoords)
830 
831  def DrawLines(self, pdc = None, polycoords = None):
832  """!Draw polyline in PseudoDC
833 
834  Set self.pline to wx.NEW_ID + 1
835 
836  polycoords - list of polyline vertices, geographical coordinates
837  (if not given, self.polycoords is used)
838  """
839  if not pdc:
840  pdc = self.pdcTmp
841 
842  if not polycoords:
843  polycoords = self.polycoords
844 
845  if len(polycoords) > 0:
846  self.plineid = wx.ID_NEW + 1
847  # convert from EN to XY
848  coords = []
849  for p in polycoords:
850  coords.append(self.Cell2Pixel(p))
851 
852  self.Draw(pdc, drawid = self.plineid, pdctype = 'polyline', coords = coords)
853 
854  Debug.msg (4, "BufferedWindow.DrawLines(): coords=%s, id=%s" % \
855  (coords, self.plineid))
856 
857  return self.plineid
858 
859  return -1
860 
861  def DrawCross(self, pdc, coords, size, rotation = 0,
862  text = None, textAlign = 'lr', textOffset = (5, 5)):
863  """!Draw cross in PseudoDC
864 
865  @todo implement rotation
866 
867  @param pdc PseudoDC
868  @param coord center coordinates
869  @param rotation rotate symbol
870  @param text draw also text (text, font, color, rotation)
871  @param textAlign alignment (default 'lower-right')
872  @textOffset offset for text (from center point)
873  """
874  Debug.msg(4, "BufferedWindow.DrawCross(): pdc=%s, coords=%s, size=%d" % \
875  (pdc, coords, size))
876  coordsCross = ((coords[0] - size, coords[1], coords[0] + size, coords[1]),
877  (coords[0], coords[1] - size, coords[0], coords[1] + size))
878 
879  self.lineid = wx.NewId()
880  for lineCoords in coordsCross:
881  self.Draw(pdc, drawid = self.lineid, pdctype = 'line', coords = lineCoords)
882 
883  if not text:
884  return self.lineid
885 
886  if textAlign == 'ul':
887  coord = [coords[0] - textOffset[0], coords[1] - textOffset[1], 0, 0]
888  elif textAlign == 'ur':
889  coord = [coords[0] + textOffset[0], coords[1] - textOffset[1], 0, 0]
890  elif textAlign == 'lr':
891  coord = [coords[0] + textOffset[0], coords[1] + textOffset[1], 0, 0]
892  else:
893  coord = [coords[0] - textOffset[0], coords[1] + textOffset[1], 0, 0]
894 
895  self.Draw(pdc, img = text,
896  pdctype = 'text', coords = coord)
897 
898  return self.lineid
899 
900  def _computeZoomToPointAndRecenter(self, position, zoomtype):
901  """!Computes zoom parameters for recenter mode.
902 
903  Computes begin and end parameters for Zoom() method.
904  Used for zooming by single click (not box)
905  and mouse wheel zooming (zoom and recenter mode).
906  """
907  if zoomtype > 0:
908  begin = (position[0] - self.Map.width / 4,
909  position[1] - self.Map.height / 4)
910  end = (position[0] + self.Map.width / 4,
911  position[1] + self.Map.height / 4)
912  else:
913  begin = ((self.Map.width - position[0]) / 2,
914  (self.Map.height - position[1]) / 2)
915  end = (begin[0] + self.Map.width / 2,
916  begin[1] + self.Map.height / 2)
917  return begin, end
918 
919  def MouseActions(self, event):
920  """!Mouse motion and button click notifier
921  """
922  if not self.processMouse:
923  return
924 
925  # zoom with mouse wheel
926  if event.GetWheelRotation() != 0:
927  self.OnMouseWheel(event)
928 
929  # left mouse button pressed
930  elif event.LeftDown():
931  self.OnLeftDown(event)
932 
933  # left mouse button released
934  elif event.LeftUp():
935  self.OnLeftUp(event)
936 
937  # dragging
938  elif event.Dragging():
939  self.OnDragging(event)
940 
941  # double click
942  elif event.ButtonDClick():
943  self.OnButtonDClick(event)
944 
945  # middle mouse button pressed
946  elif event.MiddleDown():
947  self.OnMiddleDown(event)
948 
949  # middle mouse button relesed
950  elif event.MiddleUp():
951  self.OnMiddleUp(event)
952 
953  # right mouse button pressed
954  elif event.RightDown():
955  self.OnRightDown(event)
956 
957  # right mouse button released
958  elif event.RightUp():
959  self.OnRightUp(event)
960 
961  elif event.Entering():
962  self.OnMouseEnter(event)
963 
964  elif event.Moving():
965  self.OnMouseMoving(event)
966 
967  def OnMouseWheel(self, event):
968  """!Mouse wheel moved
969  """
970  zoomBehaviour = UserSettings.Get(group = 'display',
971  key = 'mouseWheelZoom',
972  subkey = 'selection')
973  if zoomBehaviour == 2:
974  event.Skip()
975  return
976 
977  self.processMouse = False
978  current = event.GetPositionTuple()[:]
979  wheel = event.GetWheelRotation()
980  Debug.msg (5, "BufferedWindow.MouseAction(): wheel=%d" % wheel)
981 
982  if wheel > 0:
983  zoomtype = 1
984  else:
985  zoomtype = -1
986  if UserSettings.Get(group = 'display',
987  key = 'scrollDirection',
988  subkey = 'selection'):
989  zoomtype *= -1
990  # zoom 1/2 of the screen (TODO: settings)
991  if zoomBehaviour == 0: # zoom and recenter
992  begin, end = self._computeZoomToPointAndRecenter(position = current, zoomtype = zoomtype)
993 
994  elif zoomBehaviour == 1: # zoom to current cursor position
995  begin = (current[0]/2, current[1]/2)
996  end = ((self.Map.width - current[0])/2 + current[0],
997  (self.Map.height - current[1])/2 + current[1])
998 
999  # zoom
1000  self.Zoom(begin, end, zoomtype)
1001 
1002  # redraw map
1003  self.UpdateMap()
1004 
1005  # update statusbar
1006  self.parent.StatusbarUpdate()
1007 
1008  self.Refresh()
1009  self.processMouse = True
1010 
1011  def OnDragging(self, event):
1012  """!Mouse dragging
1013  """
1014  Debug.msg (5, "BufferedWindow.MouseAction(): Dragging")
1015  current = event.GetPositionTuple()[:]
1016  previous = self.mouse['begin']
1017  move = (current[0] - previous[0],
1018  current[1] - previous[1])
1019 
1020  if hasattr(self, "digit"):
1021  digitToolbar = self.toolbar
1022  else:
1023  digitToolbar = None
1024 
1025  # dragging or drawing box with left button
1026  if self.mouse['use'] == 'pan' or \
1027  event.MiddleIsDown():
1028  self.DragMap(move)
1029 
1030  # dragging decoration overlay item
1031  elif (self.mouse['use'] == 'pointer' and
1032  not digitToolbar and
1033  self.dragid != None):
1034  self.DragItem(self.dragid, event)
1035 
1036  # dragging anything else - rubber band box or line
1037  else:
1038  if (self.mouse['use'] == 'pointer' and
1039  not digitToolbar):
1040  return
1041 
1042  self.mouse['end'] = event.GetPositionTuple()[:]
1043  if (event.LeftIsDown() and
1044  not (digitToolbar and
1045  digitToolbar.GetAction() in ("moveLine",) and
1046  self.digit.GetDisplay().GetSelected() > 0)):
1047  self.MouseDraw(pdc = self.pdcTmp)
1048 
1049  def OnLeftDown(self, event):
1050  """!Left mouse button pressed
1051  """
1052  Debug.msg (5, "BufferedWindow.OnLeftDown(): use=%s" % \
1053  self.mouse["use"])
1054 
1055  self.mouse['begin'] = event.GetPositionTuple()[:]
1056 
1057  if self.mouse["use"] in ["measure", "profile"]:
1058  # measure or profile
1059  if len(self.polycoords) == 0:
1060  self.mouse['end'] = self.mouse['begin']
1061  self.polycoords.append(self.Pixel2Cell(self.mouse['begin']))
1062  self.ClearLines(pdc=self.pdcTmp)
1063  else:
1064  self.mouse['begin'] = self.mouse['end']
1065 
1066  elif self.mouse['use'] in ('zoom', 'legend'):
1067  pass
1068 
1069  # vector digizer
1070  elif self.mouse["use"] == "pointer" and \
1071  hasattr(self, "digit"):
1072  if event.ControlDown():
1073  self.OnLeftDownUndo(event)
1074  else:
1075  self._onLeftDown(event)
1076 
1077  elif self.mouse['use'] == 'pointer':
1078  # get decoration or text id
1079  self.idlist = []
1080  self.dragid = ''
1081  self.lastpos = self.mouse['begin']
1082  idlist = self.pdc.FindObjects(self.lastpos[0], self.lastpos[1],
1083  self.hitradius)
1084  if 99 in idlist:
1085  idlist.remove(99)
1086  if idlist != []:
1087  self.dragid = idlist[0] #drag whatever is on top
1088  else:
1089  pass
1090 
1091  event.Skip()
1092 
1093  def OnLeftUp(self, event):
1094  """!Left mouse button released
1095  """
1096  Debug.msg (5, "BufferedWindow.OnLeftUp(): use=%s" % \
1097  self.mouse["use"])
1098 
1099  self.mouse['end'] = event.GetPositionTuple()[:]
1100 
1101  if self.mouse['use'] in ["zoom", "pan"]:
1102  # set region in zoom or pan
1103  begin = self.mouse['begin']
1104  end = self.mouse['end']
1105 
1106  if self.mouse['use'] == 'zoom':
1107  # set region for click (zero-width box)
1108  if begin[0] - end[0] == 0 or \
1109  begin[1] - end[1] == 0:
1110  begin, end = self._computeZoomToPointAndRecenter(position = end, zoomtype = self.zoomtype)
1111  self.Zoom(begin, end, self.zoomtype)
1112 
1113  # redraw map
1114  self.UpdateMap(render = True)
1115 
1116  # update statusbar
1117  self.parent.StatusbarUpdate()
1118 
1119  elif self.mouse["use"] == "query":
1120  # querying
1121  if self.parent.IsStandalone():
1122  GMessage(parent = self.parent,
1123  message = _("Querying is not implemented in standalone mode of Map Display"))
1124  return
1125 
1126  layers = self.GetSelectedLayer(type = 'item', multi = True)
1127 
1128  self.parent.Query(self.mouse['begin'][0],self.mouse['begin'][1], layers)
1129 
1130  elif self.mouse["use"] in ["measure", "profile"]:
1131  # measure or profile
1132  if self.mouse["use"] == "measure":
1133  self.parent.MeasureDist(self.mouse['begin'], self.mouse['end'])
1134 
1135  self.polycoords.append(self.Pixel2Cell(self.mouse['end']))
1136  self.ClearLines(pdc = self.pdcTmp)
1137  self.DrawLines(pdc = self.pdcTmp)
1138 
1139  elif self.mouse["use"] == "pointer" and \
1140  self.parent.GetLayerManager().gcpmanagement:
1141  # -> GCP manager
1142  if self.parent.GetToolbar('gcpdisp'):
1143  coord = self.Pixel2Cell(self.mouse['end'])
1144  if self.parent.MapWindow == self.parent.SrcMapWindow:
1145  coordtype = 'source'
1146  else:
1147  coordtype = 'target'
1148 
1149  self.parent.GetLayerManager().gcpmanagement.SetGCPData(coordtype, coord, self, confirm = True)
1150  self.UpdateMap(render = False, renderVector = False)
1151 
1152  elif self.mouse["use"] == "pointer" and \
1153  hasattr(self, "digit"):
1154  self._onLeftUp(event)
1155 
1156  elif (self.mouse['use'] == 'pointer' and
1157  self.dragid >= 0):
1158  # end drag of overlay decoration
1159 
1160  if self.dragid < 99 and self.dragid in self.overlays:
1161  self.overlays[self.dragid].coords = self.pdc.GetIdBounds(self.dragid)
1162  elif self.dragid > 100 and self.dragid in self.textdict:
1163  self.textdict[self.dragid]['bbox'] = self.pdc.GetIdBounds(self.dragid)
1164  else:
1165  pass
1166  self.dragid = None
1167  self.currtxtid = None
1168 
1169  elif self.mouse['use'] == 'legend':
1170  self.parent.dialogs['legend'].resizeBtn.SetValue(False)
1171  screenSize = self.GetClientSizeTuple()
1172  self.overlays[1].ResizeLegend(self.mouse["begin"], self.mouse["end"], screenSize)
1173 
1174  self.parent.MapWindow.SetCursor(self.parent.cursors["default"])
1175  self.parent.MapWindow.mouse['use'] = 'pointer'
1176 
1177  self.UpdateMap()
1178 
1179  def OnButtonDClick(self, event):
1180  """!Mouse button double click
1181  """
1182  Debug.msg (5, "BufferedWindow.OnButtonDClick(): use=%s" % \
1183  self.mouse["use"])
1184 
1185  if self.mouse["use"] == "measure":
1186  # measure
1187  self.ClearLines(pdc=self.pdcTmp)
1188  self.polycoords = []
1189  self.mouse['use'] = 'pointer'
1190  self.mouse['box'] = 'point'
1191  self.mouse['end'] = [0, 0]
1192  self.Refresh()
1193  self.SetCursor(self.parent.cursors["default"])
1194 
1195  elif self.mouse["use"] != "profile" or \
1196  (self.mouse['use'] != 'pointer' and \
1197  hasattr(self, "digit")):
1198  # select overlay decoration options dialog
1199  clickposition = event.GetPositionTuple()[:]
1200  idlist = self.pdc.FindObjects(clickposition[0], clickposition[1], self.hitradius)
1201  if idlist == []:
1202  return
1203  self.dragid = idlist[0]
1204 
1205  # self.ovlcoords[self.dragid] = self.pdc.GetIdBounds(self.dragid)
1206  if self.dragid > 100:
1207  self.currtxtid = self.dragid
1208  self.parent.OnAddText(None)
1209  elif self.dragid == 0:
1210  self.parent.AddBarscale()
1211  elif self.dragid == 1:
1212  self.parent.AddLegend()
1213 
1214  def OnRightDown(self, event):
1215  """!Right mouse button pressed
1216  """
1217  Debug.msg (5, "BufferedWindow.OnRightDown(): use=%s" % \
1218  self.mouse["use"])
1219 
1220  if hasattr(self, "digit"):
1221  self._onRightDown(event)
1222 
1223  event.Skip()
1224 
1225  def OnRightUp(self, event):
1226  """!Right mouse button released
1227  """
1228  Debug.msg (5, "BufferedWindow.OnRightUp(): use=%s" % \
1229  self.mouse["use"])
1230 
1231  if hasattr(self, "digit"):
1232  self._onRightUp(event)
1233 
1234  self.redrawAll = True
1235  self.Refresh()
1236 
1237  event.Skip()
1238 
1239  def OnMiddleDown(self, event):
1240  """!Middle mouse button pressed
1241  """
1242  if not event:
1243  return
1244 
1245  self.mouse['begin'] = event.GetPositionTuple()[:]
1246 
1247  def OnMiddleUp(self, event):
1248  """!Middle mouse button released
1249  """
1250  self.mouse['end'] = event.GetPositionTuple()[:]
1251 
1252  # set region in zoom or pan
1253  begin = self.mouse['begin']
1254  end = self.mouse['end']
1255 
1256  self.Zoom(begin, end, 0) # no zoom
1257 
1258  # redraw map
1259  self.UpdateMap(render = True)
1260 
1261  # update statusbar
1262  self.parent.StatusbarUpdate()
1263 
1264  def OnMouseEnter(self, event):
1265  """!Mouse entered window and no mouse buttons were pressed
1266  """
1267  if self.parent.GetLayerManager().gcpmanagement:
1268  if self.parent.GetToolbar('gcpdisp'):
1269  if not self.parent.MapWindow == self:
1270  self.parent.MapWindow = self
1271  self.parent.Map = self.Map
1272  self.parent.UpdateActive(self)
1273  # needed for wingrass
1274  self.SetFocus()
1275  else:
1276  event.Skip()
1277 
1278  def OnMouseMoving(self, event):
1279  """!Motion event and no mouse buttons were pressed
1280  """
1281  if self.mouse["use"] == "pointer" and \
1282  hasattr(self, "digit"):
1283  self._onMouseMoving(event)
1284 
1285  event.Skip()
1286 
1287  def ClearLines(self, pdc = None):
1288  """!Clears temporary drawn lines from PseudoDC
1289  """
1290  if not pdc:
1291  pdc = self.pdcTmp
1292  try:
1293  pdc.ClearId(self.lineid)
1294  pdc.RemoveId(self.lineid)
1295  except:
1296  pass
1297 
1298  try:
1299  pdc.ClearId(self.plineid)
1300  pdc.RemoveId(self.plineid)
1301  except:
1302  pass
1303 
1304  Debug.msg(4, "BufferedWindow.ClearLines(): lineid=%s, plineid=%s" %
1305  (self.lineid, self.plineid))
1306 
1307  return True
1308 
1309  def Pixel2Cell(self, (x, y)):
1310  """!Convert image coordinates to real word coordinates
1311 
1312  @param x, y image coordinates
1313 
1314  @return easting, northing
1315  @return None on error
1316  """
1317  try:
1318  x = int(x)
1319  y = int(y)
1320  except:
1321  return None
1322 
1323  if self.Map.region["ewres"] > self.Map.region["nsres"]:
1324  res = self.Map.region["ewres"]
1325  else:
1326  res = self.Map.region["nsres"]
1327 
1328  w = self.Map.region["center_easting"] - (self.Map.width / 2) * res
1329  n = self.Map.region["center_northing"] + (self.Map.height / 2) * res
1330 
1331  east = w + x * res
1332  north = n - y * res
1333 
1334  return (east, north)
1335 
1336  def Cell2Pixel(self, (east, north)):
1337  """!Convert real word coordinates to image coordinates
1338  """
1339  try:
1340  east = float(east)
1341  north = float(north)
1342  except:
1343  return None
1344 
1345  if self.Map.region["ewres"] > self.Map.region["nsres"]:
1346  res = self.Map.region["ewres"]
1347  else:
1348  res = self.Map.region["nsres"]
1349 
1350  w = self.Map.region["center_easting"] - (self.Map.width / 2) * res
1351  n = self.Map.region["center_northing"] + (self.Map.height / 2) * res
1352 
1353  x = (east - w) / res
1354  y = (n - north) / res
1355 
1356  return (x, y)
1357 
1358  def Zoom(self, begin, end, zoomtype):
1359  """!
1360  Calculates new region while (un)zoom/pan-ing
1361  """
1362  x1, y1 = begin
1363  x2, y2 = end
1364  newreg = {}
1365 
1366  # threshold - too small squares do not make sense
1367  # can only zoom to windows of > 5x5 screen pixels
1368  if abs(x2-x1) > 5 and abs(y2-y1) > 5 and zoomtype != 0:
1369  if x1 > x2:
1370  x1, x2 = x2, x1
1371  if y1 > y2:
1372  y1, y2 = y2, y1
1373 
1374  # zoom in
1375  if zoomtype > 0:
1376  newreg['w'], newreg['n'] = self.Pixel2Cell((x1, y1))
1377  newreg['e'], newreg['s'] = self.Pixel2Cell((x2, y2))
1378 
1379  # zoom out
1380  elif zoomtype < 0:
1381  newreg['w'], newreg['n'] = self.Pixel2Cell((-x1 * 2, -y1 * 2))
1382  newreg['e'], newreg['s'] = self.Pixel2Cell((self.Map.width + 2 * \
1383  (self.Map.width - x2),
1384  self.Map.height + 2 * \
1385  (self.Map.height - y2)))
1386  # pan
1387  elif zoomtype == 0:
1388  dx = x1 - x2
1389  dy = y1 - y2
1390  if dx == 0 and dy == 0:
1391  dx = x1 - self.Map.width / 2
1392  dy = y1 - self.Map.height / 2
1393  newreg['w'], newreg['n'] = self.Pixel2Cell((dx, dy))
1394  newreg['e'], newreg['s'] = self.Pixel2Cell((self.Map.width + dx,
1395  self.Map.height + dy))
1396 
1397  # if new region has been calculated, set the values
1398  if newreg != {}:
1399  # LL locations
1400  if self.Map.projinfo['proj'] == 'll':
1401  self.Map.region['n'] = min(self.Map.region['n'], 90.0)
1402  self.Map.region['s'] = max(self.Map.region['s'], -90.0)
1403 
1404  ce = newreg['w'] + (newreg['e'] - newreg['w']) / 2
1405  cn = newreg['s'] + (newreg['n'] - newreg['s']) / 2
1406 
1407  # calculate new center point and display resolution
1408  self.Map.region['center_easting'] = ce
1409  self.Map.region['center_northing'] = cn
1410  self.Map.region['ewres'] = (newreg['e'] - newreg['w']) / self.Map.width
1411  self.Map.region['nsres'] = (newreg['n'] - newreg['s']) / self.Map.height
1412  if not self.parent.HasProperty('alignExtent') or \
1413  self.parent.GetProperty('alignExtent'):
1414  self.Map.AlignExtentFromDisplay()
1415  else:
1416  for k in ('n', 's', 'e', 'w'):
1417  self.Map.region[k] = newreg[k]
1418 
1419  if hasattr(self, "digit") and \
1420  hasattr(self, "moveInfo"):
1421  self._zoom(None)
1422 
1423  self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
1424  self.Map.region['e'], self.Map.region['w'])
1425 
1426  if self.redrawAll is False:
1427  self.redrawAll = True
1428 
1429  def ZoomBack(self):
1430  """!Zoom to previous extents in zoomhistory list
1431  """
1432  zoom = list()
1433 
1434  if len(self.zoomhistory) > 1:
1435  self.zoomhistory.pop()
1436  zoom = self.zoomhistory[-1]
1437 
1438  # disable tool if stack is empty
1439  if len(self.zoomhistory) < 2: # disable tool
1440  toolbar = self.parent.GetMapToolbar()
1441  toolbar.Enable('zoomBack', enable = False)
1442 
1443  # zoom to selected region
1444  self.Map.GetRegion(n = zoom[0], s = zoom[1],
1445  e = zoom[2], w = zoom[3],
1446  update = True)
1447  # update map
1448  self.UpdateMap()
1449 
1450  # update statusbar
1451  self.parent.StatusbarUpdate()
1452 
1453  def ZoomHistory(self, n, s, e, w):
1454  """!Manages a list of last 10 zoom extents
1455 
1456  @param n,s,e,w north, south, east, west
1457 
1458  @return removed history item if exists (or None)
1459  """
1460  removed = None
1461  self.zoomhistory.append((n,s,e,w))
1462 
1463  if len(self.zoomhistory) > 10:
1464  removed = self.zoomhistory.pop(0)
1465 
1466  if removed:
1467  Debug.msg(4, "BufferedWindow.ZoomHistory(): hist=%s, removed=%s" %
1468  (self.zoomhistory, removed))
1469  else:
1470  Debug.msg(4, "BufferedWindow.ZoomHistory(): hist=%s" %
1471  (self.zoomhistory))
1472 
1473  # update toolbar
1474  if len(self.zoomhistory) > 1:
1475  enable = True
1476  else:
1477  enable = False
1478 
1479  toolbar = self.parent.GetMapToolbar()
1480 
1481  toolbar.Enable('zoomBack', enable)
1482 
1483  return removed
1484 
1485  def ResetZoomHistory(self):
1486  """!Reset zoom history"""
1487  self.zoomhistory = list()
1488 
1489  def ZoomToMap(self, layers = None, ignoreNulls = False, render = True):
1490  """!Set display extents to match selected raster
1491  or vector map(s).
1492 
1493  @param layers list of layers to be zoom to
1494  @param ignoreNulls True to ignore null-values (valid only for rasters)
1495  @param render True to re-render display
1496  """
1497  zoomreg = {}
1498 
1499  if not layers:
1500  layers = self.GetSelectedLayer(multi = True)
1501 
1502  if not layers:
1503  return
1504 
1505  rast = []
1506  vect = []
1507  updated = False
1508  for l in layers:
1509  # only raster/vector layers are currently supported
1510  if l.type == 'raster':
1511  rast.append(l.GetName())
1512  elif l.type == 'vector':
1513  if hasattr(self, "digit") and \
1514  self.toolbar.GetLayer() == l:
1515  w, s, b, e, n, t = self.digit.GetDisplay().GetMapBoundingBox()
1516  self.Map.GetRegion(n = n, s = s, w = w, e = e,
1517  update = True)
1518  updated = True
1519  else:
1520  vect.append(l.name)
1521  elif l.type == 'rgb':
1522  for rname in l.GetName().splitlines():
1523  rast.append(rname)
1524 
1525  if not updated:
1526  self.Map.GetRegion(rast = rast,
1527  vect = vect,
1528  update = True)
1529 
1530  self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
1531  self.Map.region['e'], self.Map.region['w'])
1532 
1533  if render:
1534  self.UpdateMap()
1535 
1536  self.parent.StatusbarUpdate()
1537 
1538  def ZoomToWind(self):
1539  """!Set display geometry to match computational region
1540  settings (set with g.region)
1541  """
1542  self.Map.region = self.Map.GetRegion()
1543 
1544  self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
1545  self.Map.region['e'], self.Map.region['w'])
1546 
1547  self.UpdateMap()
1548 
1549  self.parent.StatusbarUpdate()
1550 
1551  def ZoomToDefault(self):
1552  """!Set display geometry to match default region settings
1553  """
1554  self.Map.region = self.Map.GetRegion(default = True)
1555  self.Map.AdjustRegion() # aling region extent to the display
1556 
1557  self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
1558  self.Map.region['e'], self.Map.region['w'])
1559 
1560  self.UpdateMap()
1561 
1562  self.parent.StatusbarUpdate()
1563 
1564 
1565  def GoTo(self, e, n):
1566  region = self.Map.GetCurrentRegion()
1567 
1568  region['center_easting'], region['center_northing'] = e, n
1569 
1570  dn = (region['nsres'] * region['rows']) / 2.
1571  region['n'] = region['center_northing'] + dn
1572  region['s'] = region['center_northing'] - dn
1573  de = (region['ewres'] * region['cols']) / 2.
1574  region['e'] = region['center_easting'] + de
1575  region['w'] = region['center_easting'] - de
1576 
1577  self.Map.AdjustRegion()
1578 
1579  # add to zoom history
1580  self.ZoomHistory(region['n'], region['s'],
1581  region['e'], region['w'])
1582  self.UpdateMap()
1583 
1584  def DisplayToWind(self):
1585  """!Set computational region (WIND file) to match display
1586  extents
1587  """
1588  tmpreg = os.getenv("GRASS_REGION")
1589  if tmpreg:
1590  del os.environ["GRASS_REGION"]
1591 
1592  # We ONLY want to set extents here. Don't mess with resolution. Leave that
1593  # for user to set explicitly with g.region
1594  new = self.Map.AlignResolution()
1595  RunCommand('g.region',
1596  parent = self,
1597  overwrite = True,
1598  n = new['n'],
1599  s = new['s'],
1600  e = new['e'],
1601  w = new['w'],
1602  rows = int(new['rows']),
1603  cols = int(new['cols']))
1604 
1605  if tmpreg:
1606  os.environ["GRASS_REGION"] = tmpreg
1607 
1608  def ZoomToSaved(self):
1609  """!Set display geometry to match extents in
1610  saved region file
1611  """
1612  dlg = SavedRegion(parent = self,
1613  title = _("Zoom to saved region extents"),
1614  loadsave='load')
1615 
1616  if dlg.ShowModal() == wx.ID_CANCEL or not dlg.wind:
1617  dlg.Destroy()
1618  return
1619 
1620  if not grass.find_file(name = dlg.wind, element = 'windows')['name']:
1621  wx.MessageBox(parent = self,
1622  message = _("Region <%s> not found. Operation canceled.") % dlg.wind,
1623  caption = _("Error"), style = wx.ICON_ERROR | wx.OK | wx.CENTRE)
1624  dlg.Destroy()
1625  return
1626 
1627  self.Map.GetRegion(regionName = dlg.wind,
1628  update = True)
1629 
1630  dlg.Destroy()
1631 
1632  self.ZoomHistory(self.Map.region['n'],
1633  self.Map.region['s'],
1634  self.Map.region['e'],
1635  self.Map.region['w'])
1636 
1637  self.UpdateMap()
1638 
1640  """!Save display extents to named region file.
1641  """
1642  dlg = SavedRegion(parent = self,
1643  title = _("Save display extents to region file"),
1644  loadsave='save')
1645 
1646  if dlg.ShowModal() == wx.ID_CANCEL or not dlg.wind:
1647  dlg.Destroy()
1648  return
1649 
1650  # test to see if it already exists and ask permission to overwrite
1651  if grass.find_file(name = dlg.wind, element = 'windows')['name']:
1652  overwrite = wx.MessageBox(parent = self,
1653  message = _("Region file <%s> already exists. "
1654  "Do you want to overwrite it?") % (dlg.wind),
1655  caption = _("Warning"), style = wx.YES_NO | wx.CENTRE)
1656  if (overwrite == wx.YES):
1657  self.SaveRegion(dlg.wind)
1658  else:
1659  self.SaveRegion(dlg.wind)
1660 
1661  dlg.Destroy()
1662 
1663  def SaveRegion(self, wind):
1664  """!Save region settings
1665 
1666  @param wind region name
1667  """
1668  new = self.Map.GetCurrentRegion()
1669 
1670  tmpreg = os.getenv("GRASS_REGION")
1671  if tmpreg:
1672  del os.environ["GRASS_REGION"]
1673 
1674  RunCommand('g.region',
1675  overwrite = True,
1676  parent = self,
1677  flags = 'u',
1678  n = new['n'],
1679  s = new['s'],
1680  e = new['e'],
1681  w = new['w'],
1682  rows = int(new['rows']),
1683  cols = int(new['cols']),
1684  save = wind)
1685 
1686  if tmpreg:
1687  os.environ["GRASS_REGION"] = tmpreg
1688 
1689  def Distance(self, beginpt, endpt, screen = True):
1690  """!Calculete distance
1691 
1692  Ctypes required for LL-locations
1693 
1694  @param beginpt first point
1695  @param endpt second point
1696  @param screen True for screen coordinates otherwise EN
1697  """
1698  if screen:
1699  e1, n1 = self.Pixel2Cell(beginpt)
1700  e2, n2 = self.Pixel2Cell(endpt)
1701  else:
1702  e1, n1 = beginpt
1703  e2, n2 = endpt
1704 
1705  dEast = (e2 - e1)
1706  dNorth = (n2 - n1)
1707 
1708  if self.parent.Map.projinfo['proj'] == 'll' and haveCtypes:
1709  dist = gislib.G_distance(e1, n1, e2, n2)
1710  else:
1711  dist = math.sqrt(math.pow((dEast), 2) + math.pow((dNorth), 2))
1712 
1713  return (dist, (dEast, dNorth))
def OnMotion(self, event)
Tracks mouse motion and update statusbar.
def DrawLines
Draw polyline in PseudoDC.
Abstract map display window class.
wxGUI command interface
def IsInRegion(self, region, refRegion)
Test if 'region' is inside of 'refRegion'.
def OnRightUp(self, event)
Right mouse button released.
def DrawCompRegionExtent(self)
Draw computational region extent in the display.
def ZoomHistory(self, n, s, e, w)
Manages a list of last 10 zoom extents.
def ZoomToMap
Set display extents to match selected raster or vector map(s).
def ZoomToDefault(self)
Set display geometry to match default region settings.
#define min(x, y)
Definition: draw2.c:68
def GetOverlay(self)
Converts rendered overlay files to wx.Image.
wxGUI debugging
def Cell2Pixel(self, east, north)
Convert real word coordinates to image coordinates.
def GetSelectedLayer
Get selected layer from layer tree.
def SetValue(self, value)
Definition: widgets.py:115
def OnMouseWheel(self, event)
Mouse wheel moved.
Map display canvas - base class for buffered window.
def OnButtonDClick(self, event)
Mouse button double click.
def Zoom(self, begin, end, zoomtype)
Calculates new region while (un)zoom/pan-ing.
def OnIdle(self, event)
Only re-render a composite map image from GRASS during idle time instead of multiple times during res...
redrawAll
self.OnSize(None)
def ResetZoomHistory(self)
Reset zoom history.
#define max(x, y)
Definition: draw2.c:69
def OnPaint(self, event)
Draw PseudoDC's to buffered paint DC.
def OnDragging(self, event)
Mouse dragging.
Various dialogs used in wxGUI.
def __init__(self, parent, id=wx.ID_ANY, Map=None, tree=None, lmgr=None, overlays=None, style=wx.NO_FULL_REPAINT_ON_RESIZE, kwargs)
def UpdateMap
Updates the canvas anytime there is a change to the underlaying images or to the geometry of the canv...
def Draw(self, pdc, img=None, drawid=None, pdctype='image', coords=[0)
Draws map and overlay decorations.
def DragItem(self, id, event)
Drag an overlay decoration item.
def OnSize(self, event)
Scale map image so that it is the same size as the Window.
def DragMap(self, moveto)
Drag the entire map image for panning.
def Pixel2Cell(self, x, y)
Convert image coordinates to real word coordinates.
def OnRightDown(self, event)
Right mouse button pressed.
def ZoomBack(self)
Zoom to previous extents in zoomhistory list.
def DrawCross(self, pdc, coords, size, rotation=0, text=None, textAlign='lr', textOffset=(5, 5)
Draw cross in PseudoDC.
def SaveToFile(self, FileName, FileType, width, height)
This draws the pseudo DC to a buffer that can be saved to a file.
buffer
self.Map.AlignExtentFromDisplay()
def EraseMap(self)
Erase map canvas.
def Cell2Pixel(self, east, north)
def TextBounds
Return text boundary data.
def Pixel2Cell(self, x, y)
def OnLeftUp(self, event)
Left mouse button released.
def SaveRegion(self, wind)
Save region settings.
def OnMouseMoving(self, event)
Motion event and no mouse buttons were pressed.
def OnMouseEnter(self, event)
Mouse entered window and no mouse buttons were pressed.
def ZoomToSaved(self)
Set display geometry to match extents in saved region file.
def OnMiddleDown(self, event)
Middle mouse button pressed.
def RunCommand(prog, flags="", overwrite=False, quiet=False, verbose=False, parent=None, read=False, stdin=None, getErrorMsg=False, kwargs)
Run GRASS command.
Definition: gcmd.py:633
def SaveDisplayRegion(self)
Save display extents to named region file.
def Distance
Calculete distance.
def MouseDraw
Mouse box or line from 'begin' to 'end'.
Default GUI settings.
def OnLeftDown(self, event)
Left mouse button pressed.
def DisplayToWind(self)
Set computational region (WIND file) to match display extents.
def MouseActions(self, event)
Mouse motion and button click notifier.
def _definePseudoDC(self)
Define PseudoDC objects to use.
A Buffered window class (2D view mode)
def _computeZoomToPointAndRecenter(self, position, zoomtype)
Computes zoom parameters for recenter mode.
def ZoomToWind(self)
Set display geometry to match computational region settings (set with g.region)
def OnMiddleUp(self, event)
Middle mouse button released.
def GetImage(self)
Converts redered map files to wx.Image.
def ClearLines
Clears temporary drawn lines from PseudoDC.