2 @package mapdisp.mapwindow
4 @brief Map display canvas - buffered window.
7 - mapwindow::BufferedWindow
9 (C) 2006-2011 by the GRASS Development Team
11 This program is free software under the GNU General Public License
12 (>=v2). Read the file COPYING that comes with GRASS for details.
14 @author Martin Landa <landa.martin gmail.com>
15 @author Michael Barton
16 @author Jachym Cepicky
29 from core.gcmd import RunCommand, GException, GError, GMessage
40 """!A Buffered window class (2D view mode)
42 Superclass for VDigitWindow (vector digitizer).
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
49 def __init__(self, parent, id = wx.ID_ANY,
50 Map =
None, tree =
None, lmgr =
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)
70 self.Bind(wx.EVT_PAINT, self.
OnPaint)
71 self.Bind(wx.EVT_SIZE, self.
OnSize)
72 self.Bind(wx.EVT_IDLE, self.
OnIdle)
107 self.Bind(wx.EVT_ERASE_BACKGROUND,
lambda x:
None)
113 def _definePseudoDC(self):
114 """!Define PseudoDC objects to use
125 def _bindMouseEvents(self):
127 self.Bind(wx.EVT_MOTION, self.
OnMotion)
129 def Draw(self, pdc, img = None, drawid = None, pdctype = 'image', coords = [0, 0, 0, 0]):
130 """!Draws map and overlay decorations
133 if pdctype ==
'image' and img:
135 elif pdctype ==
'clear':
140 if img
and pdctype ==
'image':
147 bg = wx.TRANSPARENT_BRUSH
149 bg = wx.Brush(self.GetBackgroundColour())
151 pdc.SetBackground(bg)
153 Debug.msg (5,
"BufferedWindow.Draw(): id=%s, pdctype = %s, coord=%s" % \
154 (drawid, pdctype, coords))
157 if drawid
is not None:
160 if pdctype ==
'clear':
163 pdc.SetBackground(bg)
171 if pdctype ==
'image':
172 bitmap = wx.BitmapFromImage(img)
173 w,h = bitmap.GetSize()
174 pdc.DrawBitmap(bitmap, coords[0], coords[1],
True)
175 pdc.SetIdBounds(drawid, wx.Rect(coords[0],coords[1], w, h))
177 elif pdctype ==
'box':
179 pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
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])
187 rect = wx.Rect(x1, y1, rwidth, rheight)
188 pdc.DrawRectangleRect(rect)
189 pdc.SetIdBounds(drawid, rect)
191 elif pdctype ==
'line':
193 pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
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]))
198 elif pdctype ==
'polyline':
200 pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
202 if (len(coords) < 2):
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]))
222 pdc.SetIdBounds(drawid, wx.Rect(x1,y1,x2,y2))
225 elif pdctype ==
'point':
228 pdc.DrawPoint(coords[0], coords[1])
229 coordsBound = (coords[0] - 5,
233 pdc.SetIdBounds(drawid, wx.Rect(coordsBound))
235 elif pdctype ==
'text':
236 if not img[
'active']:
238 if 'rotation' in img:
239 rotation = float(img[
'rotation'])
242 w, h = self.GetFullTextExtent(img[
'text'])[0:2]
243 pdc.SetFont(img[
'font'])
244 pdc.SetTextForeground(img[
'color'])
247 pdc.DrawText(img[
'text'], coords[0], coords[1])
249 pdc.DrawRotatedText(img[
'text'], coords[0], coords[1], rotation)
250 pdc.SetIdBounds(drawid, bbox)
259 """!Return text boundary data
261 @param textinfo text metadata (text, font, color, rotation)
262 @param coords reference point
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
268 if 'rotation' in textinfo:
269 rotation = float(textinfo[
'rotation'])
273 coords = textinfo[
'coords']
274 bbox = wx.Rect(coords[0], coords[1], 0, 0)
276 Debug.msg (4,
"BufferedWindow.TextBounds(): text=%s, rotation=%f" % \
277 (textinfo[
'text'], rotation))
281 self.SetFont(textinfo[
'font'])
283 w, h = self.GetTextExtent(textinfo[
'text'])
286 bbox[2], bbox[3] = w, h
288 return coords, bbox, relCoords
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:
296 relCoords = (0, boxh)
297 elif rotation >= 90
and rotation < 180:
300 relCoords = (boxw, boxh)
301 elif rotation >= 180
and rotation < 270:
303 relCoords = (boxw, 0)
308 return coords, bbox, relCoords
313 """!Draw PseudoDC's to buffered paint DC
315 If self.redrawAll is False on self.pdcTmp content is re-drawn
317 Debug.msg(4,
"BufferedWindow.OnPaint(): redrawAll=%s" % self.
redrawAll)
319 dc = wx.BufferedPaintDC(self, self.
buffer)
328 rgn = self.GetUpdateRegion().GetBox()
329 dc.SetClippingRect(rgn)
338 self.pdc.DrawToDCClipped(dc, rgn)
341 if hasattr(self,
"digit"):
345 self.pdcVector.DrawToDCClipped(gcdc, rgn)
346 except NotImplementedError, e:
347 print >> sys.stderr, e
348 self.pdcVector.DrawToDCClipped(dc, rgn)
354 self.pdc.DrawToDC(dc)
356 if hasattr(self,
"digit"):
360 self.pdcVector.DrawToDC(gcdc)
361 except NotImplementedError, e:
362 print >> sys.stderr, e
363 self.pdcVector.DrawToDC(dc)
367 self.
bufferLast = dc.GetAsBitmap(wx.Rect(0, 0, self.Map.width, self.Map.height))
369 self.pdc.DrawBitmap(self.
bufferLast, 0, 0,
False)
370 self.pdc.DrawToDC(dc)
375 self.pdcDec.DrawToDC(gcdc)
376 except NotImplementedError, e:
377 print >> sys.stderr, e
378 self.pdcDec.DrawToDC(dc)
382 self.pdcTmp.DrawToDC(dc)
388 """!Scale map image so that it is the same size as the Window
390 Debug.msg(3,
"BufferedWindow.OnSize():")
393 self.Map.ChangeMapSize(self.GetClientSize())
401 self.
buffer = wx.EmptyBitmap(
max(1, self.Map.width),
max(1, self.Map.height))
407 if self.
img and self.Map.width + self.Map.height > 0:
408 self.
img = self.img.Scale(self.Map.width, self.Map.height)
409 if len(self.Map.GetListOfLayers()) > 0:
416 self.parent.StatusbarReposition()
419 self.parent.StatusbarUpdate()
422 """!Only re-render a composite map image from GRASS during
423 idle time instead of multiple times during resizing.
431 """!This draws the pseudo DC to a buffer that can be saved to
434 @param FileName file name
435 @param FileType type of bitmap
436 @param width image width
437 @param height image height
439 busy = wx.BusyInfo(message = _(
"Please wait, exporting image..."),
443 self.Map.ChangeMapSize((width, height))
444 ibuffer = wx.EmptyBitmap(
max(1, width),
max(1, height))
445 self.Map.Render(force =
True, windres =
False)
448 self.
Draw(self.
pdc, img, drawid = 99)
451 cSize = self.GetClientSizeTuple()
452 ratio = float(width) / cSize[0], float(height) / cSize[1]
457 if self.
imagedict[img][
'layer'].IsActive():
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)
465 for id
in self.textdict.keys():
467 oldCoords = textinfo[
'coords']
468 textinfo[
'coords'] = ratio[0] * textinfo[
'coords'][0],\
469 ratio[1] * textinfo[
'coords'][1]
473 textinfo[
'coords'] = oldCoords
475 dc = wx.BufferedDC(
None, ibuffer)
479 self.pdc.DrawToDC(dc)
481 self.pdcVector.DrawToDC(dc)
482 ibuffer.SaveFile(FileName, FileType)
490 """!Converts rendered overlay files to wx.Image
492 Updates self.imagedict
494 @return list of images
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)
502 for key
in self.imagedict.keys():
503 if self.
imagedict[key][
'id'] == overlay.id:
506 self.
imagedict[img] = {
'id' : overlay.id,
513 """!Converts redered map files to wx.Image
515 Updates self.imagedict (id=99)
517 @return wx.Image instance (map composition)
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)
526 for key
in self.imagedict.keys():
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.
538 @param render re-render map composition
539 @param renderVector re-render vector map layer enabled for editing (used for digitizer)
551 if render
or renderVector:
552 self.parent.GetProgressBar().Show()
553 if self.parent.GetProgressBar().GetRange() > 0:
554 self.parent.GetProgressBar().
SetValue(1)
560 if self.
tree and self.tree.reorder:
561 self.tree.ReorderLayers()
565 self.tree.rerender =
False
570 self.Map.ChangeMapSize(self.GetClientSize())
571 if self.parent.GetProperty(
'resolution'):
576 self.
mapfile = self.Map.Render(force =
True, mapWindow = self.
parent,
579 self.
mapfile = self.Map.Render(force =
False, mapWindow = self.
parent)
580 except GException, e:
581 GError(message = e.value)
589 for pdc
in (self.
pdc,
599 self.
Draw(self.
pdc, pdctype =
'clear')
611 if renderVector
and hasattr(self,
"digit"):
618 if self.
imagedict[img][
'layer'].IsActive():
620 self.
Draw(self.
pdc, img = img, drawid = id,
621 pdctype = self.
overlays[id][
'pdcType'], coords = self.
overlays[id][
'coords'])
623 for id
in self.textdict.keys():
625 pdctype =
'text', coords = [10, 10, 10, 10])
636 if not self.parent.IsStandalone()
and \
637 self.parent.GetLayerManager().gcpmanagement:
639 if self.parent.GetMapToolbar():
640 if self == self.parent.TgtMapWindow:
645 self.parent.DrawGCP(coordtype)
650 if self.
mouse[
"use"] ==
"measure":
653 self.
mouse[
'use'] =
'pointer'
654 self.
mouse[
'box'] =
'point'
655 self.
mouse[
'end'] = [0, 0]
656 self.SetCursor(self.parent.cursors[
"default"])
663 self.parent.GetProgressBar().Hide()
669 self.parent.StatusbarUpdate()
671 Debug.msg (1,
"BufferedWindow.UpdateMap(): render=%s, renderVector=%s -> time=%g" % \
672 (render, renderVector, (stop-start)))
677 """!Draw computational region extent in the display
679 Display region is drawn as a blue box inside the computational region,
680 computational region inside a display region as a red box).
682 if hasattr(self,
"regionCoords"):
683 compReg = self.Map.GetRegion()
684 dispReg = self.Map.GetCurrentRegion()
687 self.
polypen = wx.Pen(colour = wx.Colour(0, 0, 255, 128), width = 3, style = wx.SOLID)
690 self.
polypen = wx.Pen(colour = wx.Colour(255, 0, 0, 128),
691 width = 3, style = wx.SOLID)
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']))
705 Test if 'region' is inside of 'refRegion'
707 @param region input region
708 @param refRegion reference region (e.g. computational region)
710 @return True if region is inside of refRegion
713 if region[
's'] >= refRegion[
's']
and \
714 region[
'n'] <= refRegion[
'n']
and \
715 region[
'w'] >= refRegion[
'w']
and \
716 region[
'e'] <= refRegion[
'e']:
724 self.
Draw(self.
pdc, pdctype =
'clear')
726 if hasattr(self,
"digit"):
733 """!Drag the entire map image for panning.
737 dc = wx.BufferedDC(wx.ClientDC(self))
738 dc.SetBackground(wx.Brush(
"White"))
742 self.dragimg.BeginDrag((0, 0), self)
743 self.dragimg.GetImageRect(moveto)
744 self.dragimg.Move(moveto)
746 self.dragimg.DoDrawImage(dc, moveto)
747 self.dragimg.EndDrag()
750 """!Drag an overlay decoration item
752 if id == 99
or id ==
'' or id ==
None:
return
753 Debug.msg (5,
"BufferedWindow.DragItem(): id=%d" % id)
755 dx = event.GetX() - x
756 dy = event.GetY() - y
757 self.pdc.SetBackground(wx.Brush(self.GetBackgroundColour()))
758 r = self.pdc.GetIdBounds(id)
760 r = wx.Rect(r[0], r[1], r[2], r[3])
762 rtop = (r[0],r[1]-r[3],r[2],r[3])
764 rleft = (r[0]-r[2],r[1],r[2],r[3])
766 self.pdc.TranslateId(id, dx, dy)
768 r2 = self.pdc.GetIdBounds(id)
770 r2 = wx.Rect(r[0], r[1], r[2], r[3])
773 self.
textdict[id][
'coords'][0] += dx
774 self.
textdict[id][
'coords'][1] += dy
777 self.RefreshRect(r,
False)
778 self.
lastpos = (event.GetX(), event.GetY())
780 def MouseDraw(self, pdc = None, begin = None, end = None):
781 """!Mouse box or line from 'begin' to 'end'
783 If not given from self.mouse['begin'] to self.mouse['end'].
789 begin = self.
mouse[
'begin']
791 end = self.
mouse[
'end']
793 Debug.msg (5,
"BufferedWindow.MouseDraw(): use=%s, box=%s, begin=%f,%f, end=%f,%f" % \
795 begin[0], begin[1], end[0], end[1]))
797 if self.
mouse[
'box'] ==
"box":
799 mousecoords = [begin[0], begin[1],
801 r = pdc.GetIdBounds(boxid)
803 r = wx.Rect(r[0], r[1], r[2], r[3])
809 self.RefreshRect(r,
False)
811 self.
Draw(pdc, drawid = boxid, pdctype =
'box', coords = mousecoords)
813 elif self.
mouse[
'box'] ==
"line":
815 mousecoords = [begin[0], begin[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)
827 self.RefreshRect(r,
False)
829 self.
Draw(pdc, drawid = self.
lineid, pdctype =
'line', coords = mousecoords)
832 """!Draw polyline in PseudoDC
834 Set self.pline to wx.NEW_ID + 1
836 polycoords - list of polyline vertices, geographical coordinates
837 (if not given, self.polycoords is used)
845 if len(polycoords) > 0:
852 self.
Draw(pdc, drawid = self.
plineid, pdctype =
'polyline', coords = coords)
854 Debug.msg (4,
"BufferedWindow.DrawLines(): coords=%s, id=%s" % \
861 def DrawCross(self, pdc, coords, size, rotation = 0,
862 text =
None, textAlign =
'lr', textOffset = (5, 5)):
863 """!Draw cross in PseudoDC
865 @todo implement rotation
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)
874 Debug.msg(4,
"BufferedWindow.DrawCross(): pdc=%s, coords=%s, size=%d" % \
876 coordsCross = ((coords[0] - size, coords[1], coords[0] + size, coords[1]),
877 (coords[0], coords[1] - size, coords[0], coords[1] + size))
880 for lineCoords
in coordsCross:
881 self.
Draw(pdc, drawid = self.
lineid, pdctype =
'line', coords = lineCoords)
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]
893 coord = [coords[0] - textOffset[0], coords[1] + textOffset[1], 0, 0]
895 self.
Draw(pdc, img = text,
896 pdctype =
'text', coords = coord)
900 def _computeZoomToPointAndRecenter(self, position, zoomtype):
901 """!Computes zoom parameters for recenter mode.
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).
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)
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)
920 """!Mouse motion and button click notifier
926 if event.GetWheelRotation() != 0:
930 elif event.LeftDown():
938 elif event.Dragging():
942 elif event.ButtonDClick():
946 elif event.MiddleDown():
950 elif event.MiddleUp():
954 elif event.RightDown():
958 elif event.RightUp():
961 elif event.Entering():
968 """!Mouse wheel moved
970 zoomBehaviour = UserSettings.Get(group =
'display',
971 key =
'mouseWheelZoom',
972 subkey =
'selection')
973 if zoomBehaviour == 2:
978 current = event.GetPositionTuple()[:]
979 wheel = event.GetWheelRotation()
980 Debug.msg (5,
"BufferedWindow.MouseAction(): wheel=%d" % wheel)
986 if UserSettings.Get(group =
'display',
987 key =
'scrollDirection',
988 subkey =
'selection'):
991 if zoomBehaviour == 0:
994 elif zoomBehaviour == 1:
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])
1000 self.
Zoom(begin, end, zoomtype)
1006 self.parent.StatusbarUpdate()
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])
1020 if hasattr(self,
"digit"):
1021 digitToolbar = self.toolbar
1026 if self.
mouse[
'use'] ==
'pan' or \
1027 event.MiddleIsDown():
1031 elif (self.
mouse[
'use'] ==
'pointer' and
1032 not digitToolbar
and
1038 if (self.
mouse[
'use'] ==
'pointer' and
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)):
1050 """!Left mouse button pressed
1052 Debug.msg (5,
"BufferedWindow.OnLeftDown(): use=%s" % \
1055 self.
mouse[
'begin'] = event.GetPositionTuple()[:]
1057 if self.
mouse[
"use"]
in [
"measure",
"profile"]:
1066 elif self.
mouse[
'use']
in (
'zoom',
'legend'):
1070 elif self.
mouse[
"use"] ==
"pointer" and \
1071 hasattr(self,
"digit"):
1072 if event.ControlDown():
1073 self.OnLeftDownUndo(event)
1075 self._onLeftDown(event)
1077 elif self.
mouse[
'use'] ==
'pointer':
1094 """!Left mouse button released
1096 Debug.msg (5,
"BufferedWindow.OnLeftUp(): use=%s" % \
1099 self.
mouse[
'end'] = event.GetPositionTuple()[:]
1101 if self.
mouse[
'use']
in [
"zoom",
"pan"]:
1103 begin = self.
mouse[
'begin']
1104 end = self.
mouse[
'end']
1106 if self.
mouse[
'use'] ==
'zoom':
1108 if begin[0] - end[0] == 0
or \
1109 begin[1] - end[1] == 0:
1117 self.parent.StatusbarUpdate()
1119 elif self.
mouse[
"use"] ==
"query":
1121 if self.parent.IsStandalone():
1122 GMessage(parent = self.
parent,
1123 message = _(
"Querying is not implemented in standalone mode of Map Display"))
1128 self.parent.Query(self.
mouse[
'begin'][0],self.
mouse[
'begin'][1], layers)
1130 elif self.
mouse[
"use"]
in [
"measure",
"profile"]:
1132 if self.
mouse[
"use"] ==
"measure":
1133 self.parent.MeasureDist(self.
mouse[
'begin'], self.
mouse[
'end'])
1139 elif self.
mouse[
"use"] ==
"pointer" and \
1140 self.parent.GetLayerManager().gcpmanagement:
1142 if self.parent.GetToolbar(
'gcpdisp'):
1144 if self.parent.MapWindow == self.parent.SrcMapWindow:
1145 coordtype =
'source'
1147 coordtype =
'target'
1149 self.parent.GetLayerManager().gcpmanagement.SetGCPData(coordtype, coord, self, confirm =
True)
1150 self.
UpdateMap(render =
False, renderVector =
False)
1152 elif self.
mouse[
"use"] ==
"pointer" and \
1153 hasattr(self,
"digit"):
1154 self._onLeftUp(event)
1156 elif (self.
mouse[
'use'] ==
'pointer' and
1169 elif self.
mouse[
'use'] ==
'legend':
1171 self.parent.dialogs[
'legend'].FindWindowByName(
"resize").
SetValue(
False)
1172 self.Map.GetOverlay(1).SetActive(
True)
1173 self.parent.MapWindow.SetCursor(self.parent.cursors[
"default"])
1174 self.parent.MapWindow.mouse[
'use'] =
'pointer'
1179 """!Mouse button double click
1181 Debug.msg (5,
"BufferedWindow.OnButtonDClick(): use=%s" % \
1184 if self.
mouse[
"use"] ==
"measure":
1188 self.
mouse[
'use'] =
'pointer'
1189 self.
mouse[
'box'] =
'point'
1190 self.
mouse[
'end'] = [0, 0]
1192 self.SetCursor(self.parent.cursors[
"default"])
1194 elif self.
mouse[
"use"] !=
"profile" or \
1195 (self.
mouse[
'use'] !=
'pointer' and \
1196 hasattr(self,
"digit")):
1198 clickposition = event.GetPositionTuple()[:]
1199 idlist = self.pdc.FindObjects(clickposition[0], clickposition[1], self.
hitradius)
1207 self.parent.OnAddText(
None)
1209 self.parent.OnAddBarscale(
None)
1211 self.parent.OnAddLegend(
None)
1214 """!Right mouse button pressed
1216 Debug.msg (5,
"BufferedWindow.OnRightDown(): use=%s" % \
1219 if hasattr(self,
"digit"):
1220 self._onRightDown(event)
1225 """!Right mouse button released
1227 Debug.msg (5,
"BufferedWindow.OnRightUp(): use=%s" % \
1230 if hasattr(self,
"digit"):
1231 self._onRightUp(event)
1239 """!Middle mouse button pressed
1244 self.
mouse[
'begin'] = event.GetPositionTuple()[:]
1247 """!Middle mouse button released
1249 self.
mouse[
'end'] = event.GetPositionTuple()[:]
1252 begin = self.
mouse[
'begin']
1253 end = self.
mouse[
'end']
1255 self.
Zoom(begin, end, 0)
1261 self.parent.StatusbarUpdate()
1264 """!Mouse entered window and no mouse buttons were pressed
1266 if self.parent.GetLayerManager().gcpmanagement:
1267 if self.parent.GetToolbar(
'gcpdisp'):
1268 if not self.parent.MapWindow == self:
1269 self.parent.MapWindow = self
1270 self.parent.Map = self.
Map
1271 self.parent.UpdateActive(self)
1278 """!Motion event and no mouse buttons were pressed
1280 if self.
mouse[
"use"] ==
"pointer" and \
1281 hasattr(self,
"digit"):
1282 self._onMouseMoving(event)
1287 """!Clears temporary drawn lines from PseudoDC
1293 pdc.RemoveId(self.
lineid)
1303 Debug.msg(4,
"BufferedWindow.ClearLines(): lineid=%s, plineid=%s" %
1309 """!Convert image coordinates to real word coordinates
1311 @param x, y image coordinates
1313 @return easting, northing
1314 @return None on error
1322 if self.Map.region[
"ewres"] > self.Map.region[
"nsres"]:
1323 res = self.Map.region[
"ewres"]
1325 res = self.Map.region[
"nsres"]
1327 w = self.Map.region[
"center_easting"] - (self.Map.width / 2) * res
1328 n = self.Map.region[
"center_northing"] + (self.Map.height / 2) * res
1333 return (east, north)
1336 """!Convert real word coordinates to image coordinates
1340 north = float(north)
1344 if self.Map.region[
"ewres"] > self.Map.region[
"nsres"]:
1345 res = self.Map.region[
"ewres"]
1347 res = self.Map.region[
"nsres"]
1349 w = self.Map.region[
"center_easting"] - (self.Map.width / 2) * res
1350 n = self.Map.region[
"center_northing"] + (self.Map.height / 2) * res
1352 x = (east - w) / res
1353 y = (n - north) / res
1358 w = abs(begin[0] - end[0])
1359 h = abs(begin[1] - end[1])
1360 if begin[0] < end[0]:
1364 if begin[1] < end[1]:
1368 screenRect = wx.Rect(x, y, w, h)
1369 screenSize = self.GetClientSizeTuple()
1370 at = [(screenSize[1] - (y + h)) / float(screenSize[1]) * 100,
1371 (screenSize[1] - y) / float(screenSize[1]) * 100,
1372 x / float(screenSize[0]) * 100,
1373 (x + w) / float(screenSize[0]) * 100]
1374 for i, subcmd
in enumerate(self.
overlays[1][
'cmd']):
1375 if subcmd.startswith(
'at='):
1376 self.
overlays[1][
'cmd'][i] =
"at=%d,%d,%d,%d" % (at[0], at[1], at[2], at[3])
1377 self.Map.ChangeOverlay(1,
True, command = self.
overlays[1][
'cmd'])
1380 def Zoom(self, begin, end, zoomtype):
1382 Calculates new region while (un)zoom/pan-ing
1390 if abs(x2-x1) > 5
and abs(y2-y1) > 5
and zoomtype != 0:
1398 newreg[
'w'], newreg[
'n'] = self.
Pixel2Cell((x1, y1))
1399 newreg[
'e'], newreg[
's'] = self.
Pixel2Cell((x2, y2))
1403 newreg[
'w'], newreg[
'n'] = self.
Pixel2Cell((-x1 * 2, -y1 * 2))
1404 newreg[
'e'], newreg[
's'] = self.
Pixel2Cell((self.Map.width + 2 * \
1405 (self.Map.width - x2),
1406 self.Map.height + 2 * \
1407 (self.Map.height - y2)))
1412 if dx == 0
and dy == 0:
1413 dx = x1 - self.Map.width / 2
1414 dy = y1 - self.Map.height / 2
1415 newreg[
'w'], newreg[
'n'] = self.
Pixel2Cell((dx, dy))
1416 newreg[
'e'], newreg[
's'] = self.
Pixel2Cell((self.Map.width + dx,
1417 self.Map.height + dy))
1422 if self.Map.projinfo[
'proj'] ==
'll':
1423 self.Map.region[
'n'] =
min(self.Map.region[
'n'], 90.0)
1424 self.Map.region[
's'] =
max(self.Map.region[
's'], -90.0)
1426 ce = newreg[
'w'] + (newreg[
'e'] - newreg[
'w']) / 2
1427 cn = newreg[
's'] + (newreg[
'n'] - newreg[
's']) / 2
1430 self.Map.region[
'center_easting'] = ce
1431 self.Map.region[
'center_northing'] = cn
1432 self.Map.region[
'ewres'] = (newreg[
'e'] - newreg[
'w']) / self.Map.width
1433 self.Map.region[
'nsres'] = (newreg[
'n'] - newreg[
's']) / self.Map.height
1434 if not self.parent.HasProperty(
'alignExtent')
or \
1435 self.parent.GetProperty(
'alignExtent'):
1436 self.Map.AlignExtentFromDisplay()
1438 for k
in (
'n',
's',
'e',
'w'):
1439 self.Map.region[k] = newreg[k]
1441 if hasattr(self,
"digit")
and \
1442 hasattr(self,
"moveInfo"):
1445 self.
ZoomHistory(self.Map.region[
'n'], self.Map.region[
's'],
1446 self.Map.region[
'e'], self.Map.region[
'w'])
1452 """!Zoom to previous extents in zoomhistory list
1457 self.zoomhistory.pop()
1462 toolbar = self.parent.GetMapToolbar()
1463 toolbar.Enable(
'zoomBack', enable =
False)
1466 self.Map.GetRegion(n = zoom[0], s = zoom[1],
1467 e = zoom[2], w = zoom[3],
1473 self.parent.StatusbarUpdate()
1476 """!Manages a list of last 10 zoom extents
1478 @param n,s,e,w north, south, east, west
1480 @return removed history item if exists (or None)
1483 self.zoomhistory.append((n,s,e,w))
1486 removed = self.zoomhistory.pop(0)
1489 Debug.msg(4,
"BufferedWindow.ZoomHistory(): hist=%s, removed=%s" %
1492 Debug.msg(4,
"BufferedWindow.ZoomHistory(): hist=%s" %
1501 toolbar = self.parent.GetMapToolbar()
1503 toolbar.Enable(
'zoomBack', enable)
1508 """!Reset zoom history"""
1511 def ZoomToMap(self, layers = None, ignoreNulls = False, render = True):
1512 """!Set display extents to match selected raster
1515 @param layers list of layers to be zoom to
1516 @param ignoreNulls True to ignore null-values (valid only for rasters)
1517 @param render True to re-render display
1532 if l.type ==
'raster':
1533 rast.append(l.GetName())
1534 elif l.type ==
'vector':
1535 if hasattr(self,
"digit")
and \
1536 self.toolbar.GetLayer() == l:
1537 w, s, b, e, n, t = self.digit.GetDisplay().GetMapBoundingBox()
1538 self.Map.GetRegion(n = n, s = s, w = w, e = e,
1543 elif l.type ==
'rgb':
1544 for rname
in l.GetName().splitlines():
1548 self.Map.GetRegion(rast = rast,
1552 self.
ZoomHistory(self.Map.region[
'n'], self.Map.region[
's'],
1553 self.Map.region[
'e'], self.Map.region[
'w'])
1558 self.parent.StatusbarUpdate()
1561 """!Set display geometry to match computational region
1562 settings (set with g.region)
1564 self.Map.region = self.Map.GetRegion()
1566 self.
ZoomHistory(self.Map.region[
'n'], self.Map.region[
's'],
1567 self.Map.region[
'e'], self.Map.region[
'w'])
1571 self.parent.StatusbarUpdate()
1574 """!Set display geometry to match default region settings
1576 self.Map.region = self.Map.GetRegion(default =
True)
1577 self.Map.AdjustRegion()
1579 self.
ZoomHistory(self.Map.region[
'n'], self.Map.region[
's'],
1580 self.Map.region[
'e'], self.Map.region[
'w'])
1584 self.parent.StatusbarUpdate()
1588 region = self.Map.GetCurrentRegion()
1590 region[
'center_easting'], region[
'center_northing'] = e, n
1592 dn = (region[
'nsres'] * region[
'rows']) / 2.
1593 region[
'n'] = region[
'center_northing'] + dn
1594 region[
's'] = region[
'center_northing'] - dn
1595 de = (region[
'ewres'] * region[
'cols']) / 2.
1596 region[
'e'] = region[
'center_easting'] + de
1597 region[
'w'] = region[
'center_easting'] - de
1599 self.Map.AdjustRegion()
1603 region[
'e'], region[
'w'])
1607 """!Set computational region (WIND file) to match display
1610 tmpreg = os.getenv(
"GRASS_REGION")
1612 del os.environ[
"GRASS_REGION"]
1616 new = self.Map.AlignResolution()
1624 rows = int(new[
'rows']),
1625 cols = int(new[
'cols']))
1628 os.environ[
"GRASS_REGION"] = tmpreg
1631 """!Set display geometry to match extents in
1634 dlg = SavedRegion(parent = self,
1635 title = _(
"Zoom to saved region extents"),
1638 if dlg.ShowModal() == wx.ID_CANCEL
or not dlg.GetName():
1642 if not grass.find_file(name = dlg.GetName(), element =
'windows')[
'name']:
1643 wx.MessageBox(parent = self,
1644 message = _(
"Region <%s> not found. Operation canceled.") % dlg.GetName(),
1645 caption = _(
"Error"), style = wx.ICON_ERROR | wx.OK | wx.CENTRE)
1649 self.Map.GetRegion(regionName = dlg.GetName(),
1655 self.Map.region[
's'],
1656 self.Map.region[
'e'],
1657 self.Map.region[
'w'])
1662 """!Save display extents/compulational region to named region
1665 @param display True for display extends otherwise computational region
1668 title = _(
"Save display extents to region file")
1670 title = _(
"Save computational region to region file")
1672 dlg = SavedRegion(parent = self, title = title, loadsave =
'save')
1673 if dlg.ShowModal() == wx.ID_CANCEL
or not dlg.GetName():
1678 if grass.find_file(name = dlg.GetName(), element =
'windows')[
'name']:
1679 overwrite = wx.MessageBox(parent = self,
1680 message = _(
"Region file <%s> already exists. "
1681 "Do you want to overwrite it?") % (dlg.GetName()),
1682 caption = _(
"Warning"), style = wx.YES_NO | wx.CENTRE)
1683 if overwrite != wx.YES:
1694 def _saveCompRegion(self, name):
1695 """!Save region settings to region file
1697 @param name region name
1705 def _saveDisplayRegion(self, name):
1706 """!Save display extents to region file
1708 @param name region name
1710 new = self.Map.GetCurrentRegion()
1712 tmpreg = os.getenv(
"GRASS_REGION")
1714 del os.environ[
"GRASS_REGION"]
1724 rows = int(new[
'rows']),
1725 cols = int(new[
'cols']),
1729 os.environ[
"GRASS_REGION"] = tmpreg
1732 """!Calculete distance
1734 Ctypes required for LL-locations
1736 @param beginpt first point
1737 @param endpt second point
1738 @param screen True for screen coordinates otherwise EN
1750 if self.parent.Map.projinfo[
'proj'] ==
'll' and haveCtypes:
1751 dist = gislib.G_distance(e1, n1, e2, n2)
1753 dist = math.sqrt(math.pow((dEast), 2) + math.pow((dNorth), 2))
1755 return (dist, (dEast, dNorth))
def ZoomToSaved
Set display geometry to match extents in saved region file.
def OnLeftUp
Left mouse button released.
def DrawLines
Draw polyline in PseudoDC.
def OnDragging
Mouse dragging.
Abstract map display window class.
def OnButtonDClick
Mouse button double click.
def ZoomBack
Zoom to previous extents in zoomhistory list.
def ZoomToMap
Set display extents to match selected raster or vector map(s).
def OnIdle
Only re-render a composite map image from GRASS during idle time instead of multiple times during res...
def OnMiddleUp
Middle mouse button released.
def DrawCompRegionExtent
Draw computational region extent in the display.
def GetSelectedLayer
Get selected layer from layer tree.
Map display canvas - base class for buffered window.
def OnLeftDown
Left mouse button pressed.
def OnMotion
Tracks mouse motion and update statusbar.
redrawAll
self.OnSize(None)
def _definePseudoDC
Define PseudoDC objects to use.
def DrawCross
Draw cross in PseudoDC.
def _saveDisplayRegion
Save display extents to region file.
def ZoomHistory
Manages a list of last 10 zoom extents.
def Zoom
Calculates new region while (un)zoom/pan-ing.
def OnMouseEnter
Mouse entered window and no mouse buttons were pressed.
def OnPaint
Draw PseudoDC's to buffered paint DC.
Various dialogs used in wxGUI.
def UpdateMap
Updates the canvas anytime there is a change to the underlaying images or to the geometry of the canv...
def DisplayToWind
Set computational region (WIND file) to match display extents.
def ResetZoomHistory
Reset zoom history.
def OnSize
Scale map image so that it is the same size as the Window.
def GetOverlay
Converts rendered overlay files to wx.Image.
def _saveCompRegion
Save region settings to region file.
def IsInRegion
Test if 'region' is inside of 'refRegion'.
buffer
self.Map.AlignExtentFromDisplay()
def EraseMap
Erase map canvas.
def SaveRegion
Save display extents/compulational region to named region file.
def GetImage
Converts redered map files to wx.Image.
def Pixel2Cell
Convert image coordinates to real word coordinates.
def MouseActions
Mouse motion and button click notifier.
def TextBounds
Return text boundary data.
def SaveToFile
This draws the pseudo DC to a buffer that can be saved to a file.
def DragItem
Drag an overlay decoration item.
def OnMouseMoving
Motion event and no mouse buttons were pressed.
def Draw
Draws map and overlay decorations.
def OnRightUp
Right mouse button released.
def ZoomToWind
Set display geometry to match computational region settings (set with g.region)
def Cell2Pixel
Convert real word coordinates to image coordinates.
def Distance
Calculete distance.
def OnMiddleDown
Middle mouse button pressed.
def MouseDraw
Mouse box or line from 'begin' to 'end'.
def _computeZoomToPointAndRecenter
Computes zoom parameters for recenter mode.
def OnMouseWheel
Mouse wheel moved.
A Buffered window class (2D view mode)
def DragMap
Drag the entire map image for panning.
def ZoomToDefault
Set display geometry to match default region settings.
def RunCommand
Run GRASS command.
def ClearLines
Clears temporary drawn lines from PseudoDC.
def OnRightDown
Right mouse button pressed.