GRASS Programmer's Manual  6.5.svn(2014)-r66266
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
psmap/frame.py
Go to the documentation of this file.
1 """!
2 @package psmap.frame
3 
4 @brief GUI for ps.map
5 
6 Classes:
7  - frame::PsMapFrame
8  - frame::PsMapBufferedWindow
9 
10 (C) 2011-2012 by Anna Kratochvilova, and 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.
13 
14 @author Anna Kratochvilova <kratochanna gmail.com> (bachelor's project)
15 @author Martin Landa <landa.martin gmail.com> (mentor)
16 """
17 
18 import os
19 import sys
20 import textwrap
21 import Queue
22 from math import sin, cos, pi, sqrt
23 
24 if __name__ == "__main__":
25  sys.path.append(os.path.join(os.getenv('GISBASE'), 'etc', 'gui', 'wxpython'))
26 from core import globalvar
27 import wx
28 
29 try:
30  import wx.lib.agw.flatnotebook as fnb
31 except ImportError:
32  import wx.lib.flatnotebook as fnb
33 
34 import grass.script as grass
35 
36 from gui_core.menu import Menu
37 from gui_core.goutput import CmdThread, EVT_CMD_DONE
38 from psmap.toolbars import PsMapToolbar
39 from core.gcmd import RunCommand, GError, GMessage
40 from core.settings import UserSettings
41 from gui_core.forms import GUI
42 from gui_core.dialogs import HyperlinkDialog
43 from psmap.menudata import PsMapData
44 
45 from psmap.dialogs import *
46 from psmap.instructions import *
47 from psmap.utils import *
48 
49 class PsMapFrame(wx.Frame):
50  def __init__(self, parent = None, id = wx.ID_ANY,
51  title = _("GRASS GIS Cartographic Composer (experimental prototype)"), **kwargs):
52  """!Main window of ps.map GUI
53 
54  @param parent parent window
55  @param id window id
56  @param title window title
57 
58  @param kwargs wx.Frames' arguments
59  """
60  self.parent = parent
61 
62  wx.Frame.__init__(self, parent = parent, id = id, title = title, name = "PsMap", **kwargs)
63  self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
64  #menubar
65  self.menubar = Menu(parent = self, data = PsMapData())
66  self.SetMenuBar(self.menubar)
67  #toolbar
68 
69  self.toolbar = PsMapToolbar(parent = self)
70  self.SetToolBar(self.toolbar)
71 
72  self.actionOld = self.toolbar.action['id']
73  self.iconsize = (16, 16)
74  #satusbar
75  self.statusbar = self.CreateStatusBar(number = 1)
76 
77  # mouse attributes -- position on the screen, begin and end of
78  # dragging, and type of drawing
79  self.mouse = {
80  'begin': [0, 0], # screen coordinates
81  'end' : [0, 0],
82  'use' : "pointer",
83  }
84  # available cursors
85  self.cursors = {
86  "default" : wx.StockCursor(wx.CURSOR_ARROW),
87  "cross" : wx.StockCursor(wx.CURSOR_CROSS),
88  "hand" : wx.StockCursor(wx.CURSOR_HAND),
89  "sizenwse": wx.StockCursor(wx.CURSOR_SIZENWSE)
90  }
91  # pen and brush
92  self.pen = {
93  'paper': wx.Pen(colour = "BLACK", width = 1),
94  'margins': wx.Pen(colour = "GREY", width = 1),
95  'map': wx.Pen(colour = wx.Colour(86, 122, 17), width = 2),
96  'rasterLegend': wx.Pen(colour = wx.Colour(219, 216, 4), width = 2),
97  'vectorLegend': wx.Pen(colour = wx.Colour(219, 216, 4), width = 2),
98  'mapinfo': wx.Pen(colour = wx.Colour(5, 184, 249), width = 2),
99  'scalebar': wx.Pen(colour = wx.Colour(150, 150, 150), width = 2),
100  'image': wx.Pen(colour = wx.Colour(255, 150, 50), width = 2),
101  'northArrow': wx.Pen(colour = wx.Colour(200, 200, 200), width = 2),
102  'point': wx.Pen(colour = wx.Colour(100, 100, 100), width = 2),
103  'line': wx.Pen(colour = wx.Colour(0, 0, 0), width = 2),
104  'box': wx.Pen(colour = 'RED', width = 2, style = wx.SHORT_DASH),
105  'select': wx.Pen(colour = 'BLACK', width = 1, style = wx.SHORT_DASH),
106  'resize': wx.Pen(colour = 'BLACK', width = 1)
107  }
108  self.brush = {
109  'paper': wx.WHITE_BRUSH,
110  'margins': wx.TRANSPARENT_BRUSH,
111  'map': wx.Brush(wx.Colour(151, 214, 90)),
112  'rasterLegend': wx.Brush(wx.Colour(250, 247, 112)),
113  'vectorLegend': wx.Brush(wx.Colour(250, 247, 112)),
114  'mapinfo': wx.Brush(wx.Colour(127, 222, 252)),
115  'scalebar': wx.Brush(wx.Colour(200, 200, 200)),
116  'image': wx.Brush(wx.Colour(255, 200, 50)),
117  'northArrow': wx.Brush(wx.Colour(255, 255, 255)),
118  'point': wx.Brush(wx.Colour(200, 200, 200)),
119  'line': wx.TRANSPARENT_BRUSH,
120  'box': wx.TRANSPARENT_BRUSH,
121  'select':wx.TRANSPARENT_BRUSH,
122  'resize': wx.BLACK_BRUSH
123  }
124 
125 
126  # list of objects to draw
127  self.objectId = []
128 
129  # instructions
130  self.instruction = Instruction(parent = self, objectsToDraw = self.objectId)
131  # open dialogs
132  self.openDialogs = dict()
133 
134  self.pageId = wx.NewId()
135  #current page of flatnotebook
136  self.currentPage = 0
137  #canvas for draft mode
138  self.canvas = PsMapBufferedWindow(parent = self, mouse = self.mouse, pen = self.pen,
139  brush = self.brush, cursors = self.cursors,
140  instruction = self.instruction, openDialogs = self.openDialogs,
141  pageId = self.pageId, objectId = self.objectId,
142  preview = False)
143 
144  self.canvas.SetCursor(self.cursors["default"])
145  self.getInitMap()
146 
147 
148  # image path
149  env = grass.gisenv()
150  self.imgName = grass.tempfile()
151 
152  #canvas for preview
153  self.previewCanvas = PsMapBufferedWindow(parent = self, mouse = self.mouse, cursors = self.cursors,
154  pen = self.pen, brush = self.brush, preview = True)
155 
156  # set WIND_OVERRIDE
157  grass.use_temp_region()
158 
159  # create queues
160  self.requestQ = Queue.Queue()
161  self.resultQ = Queue.Queue()
162  # thread
163  self.cmdThread = CmdThread(self, self.requestQ, self.resultQ)
164 
165  self._layout()
166  self.SetMinSize(wx.Size(750, 600))
167 
168  self.Bind(fnb.EVT_FLATNOTEBOOK_PAGE_CHANGING, self.OnPageChanging)
169  self.Bind(fnb.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
170  self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
171  self.Bind(EVT_CMD_DONE, self.OnCmdDone)
172 
173  if not havePILImage:
174  wx.CallAfter(self._showErrMsg)
175 
176  def _showErrMsg(self):
177  """!Show error message (missing preview)
178  """
179  GError(parent = self,
180  message = _("Python Imaging Library is not available.\n"
181  "'Preview' functionality won't work."),
182  showTraceback = False)
183 
184  def _layout(self):
185  """!Do layout
186  """
187  mainSizer = wx.BoxSizer(wx.VERTICAL)
188  if globalvar.hasAgw:
189  self.book = fnb.FlatNotebook(parent = self, id = wx.ID_ANY,
190  agwStyle = fnb.FNB_FANCY_TABS | fnb.FNB_BOTTOM |
191  fnb.FNB_NO_NAV_BUTTONS | fnb.FNB_NO_X_BUTTON)
192  else:
193  self.book = fnb.FlatNotebook(parent = self, id = wx.ID_ANY,
194  style = fnb.FNB_FANCY_TABS | fnb.FNB_BOTTOM |
195  fnb.FNB_NO_NAV_BUTTONS | fnb.FNB_NO_X_BUTTON)
196  #self.book = fnb.FlatNotebook(self, wx.ID_ANY, style = fnb.FNB_BOTTOM)
197  self.book.AddPage(self.canvas, "Draft mode")
198  self.book.AddPage(self.previewCanvas, "Preview")
199  self.book.SetSelection(0)
200 
201  mainSizer.Add(self.book,1, wx.EXPAND)
202 
203  self.SetSizer(mainSizer)
204  mainSizer.Fit(self)
205 
206 
207  def InstructionFile(self):
208  """!Creates mapping instructions"""
209 
210  return str(self.instruction)
211 
212  def OnPSFile(self, event):
213  """!Generate PostScript"""
214  filename = self.getFile(wildcard = "PostScript (*.ps)|*.ps|Encapsulated PostScript (*.eps)|*.eps")
215  if filename:
216  self.PSFile(filename)
217 
218  def OnPsMapDialog(self, event):
219  """!Launch ps.map dialog
220  """
221  GUI(parent = self).ParseCommand(cmd = ['ps.map'])
222 
223  def OnPDFFile(self, event):
224  """!Generate PDF from PS with ps2pdf if available"""
225  if not sys.platform == 'win32':
226  try:
227  p = grass.Popen(["ps2pdf"], stderr = grass.PIPE)
228  p.stderr.close()
229 
230  except OSError:
231  GMessage(parent = self,
232  message = _("Program ps2pdf is not available. Please install it first to create PDF."))
233  return
234 
235  filename = self.getFile(wildcard = "PDF (*.pdf)|*.pdf")
236  if filename:
237  self.PSFile(filename, pdf = True)
238 
239  def OnPreview(self, event):
240  """!Run ps.map and show result"""
241  self.PSFile()
242 
243  def PSFile(self, filename = None, pdf = False):
244  """!Create temporary instructions file and run ps.map with output = filename"""
245  instrFile = grass.tempfile()
246  instrFileFd = open(instrFile, mode = 'w')
247  instrFileFd.write(self.InstructionFile())
248  instrFileFd.flush()
249  instrFileFd.close()
250 
251  temp = False
252  regOld = grass.region()
253 
254  if pdf:
255  pdfname = filename
256  else:
257  pdfname = None
258  #preview or pdf
259  if not filename or (filename and pdf):
260  temp = True
261  filename = grass.tempfile()
262  if not pdf: # lower resolution for preview
263  if self.instruction.FindInstructionByType('map'):
264  mapId = self.instruction.FindInstructionByType('map').id
265  SetResolution(dpi = 100, width = self.instruction[mapId]['rect'][2],
266  height = self.instruction[mapId]['rect'][3])
267 
268  cmd = ['ps.map', '--overwrite']
269  if os.path.splitext(filename)[1] == '.eps':
270  cmd.append('-e')
271  if self.instruction[self.pageId]['Orientation'] == 'Landscape':
272  cmd.append('-r')
273  cmd.append('input=%s' % instrFile)
274  cmd.append('output=%s' % filename)
275  if pdf:
276  self.SetStatusText(_('Generating PDF...'), 0)
277  elif not temp:
278  self.SetStatusText(_('Generating PostScript...'), 0)
279  else:
280  self.SetStatusText(_('Generating preview...'), 0)
281 
282  self.cmdThread.RunCmd(cmd, userData = {'instrFile' : instrFile, 'filename' : filename,
283  'pdfname' : pdfname, 'temp' : temp, 'regionOld' : regOld})
284 
285  def OnCmdDone(self, event):
286  """!ps.map process finished"""
287 
288  if event.returncode != 0:
289  GMessage(parent = self,
290  message = _("Ps.map exited with return code %s") % event.returncode)
291 
292  grass.try_remove(event.userData['instrFile'])
293  if event.userData['temp']:
294  grass.try_remove(event.userData['filename'])
295  return
296 
297  if event.userData['pdfname']:
298  if sys.platform == 'win32':
299  command = ['gswin32c',
300  '-P-', '-dSAFER',
301  '-dCompatibilityLevel=1.4',
302  '-q', '-P-',
303  '-dNOPAUSE', '-dBATCH',
304  '-sDEVICE=pdfwrite',
305  '-dPDFSETTINGS=/prepress', '-r1200',
306  '-sstdout=%stderr',
307  '-sOutputFile=%s' % event.userData['pdfname'],
308  '-P-', '-dSAFER',
309  '-dCompatibilityLevel=1.4',
310  '-c', '.setpdfwrite', '-f',
311  event.userData['filename']]
312  else:
313  command = ['ps2pdf', '-dPDFSETTINGS=/prepress', '-r1200',
314  event.userData['filename'], event.userData['pdfname']]
315  try:
316  proc = grass.Popen(command)
317  ret = proc.wait()
318  if ret > 0:
319  GMessage(parent = self,
320  message = _("%(prg)s exited with return code %(code)s") % {'prg': command[0],
321  'code': ret})
322  else:
323  self.SetStatusText(_('PDF generated'), 0)
324  except OSError, e:
325  GError(parent = self,
326  message = _("Program ps2pdf is not available. Please install it to create PDF.\n\n %s") % e)
327 
328  elif not event.userData['temp']:
329  self.SetStatusText(_('PostScript file generated'), 0)
330 
331  # show preview only when user doesn't want to create ps or pdf
332  if havePILImage and event.userData['temp'] and not event.userData['pdfname']:
333  RunCommand('g.region', cols = event.userData['regionOld']['cols'], rows = event.userData['regionOld']['rows'])
334 
335  busy = wx.BusyInfo(message = _("Generating preview, wait please"), parent = self)
336  wx.Yield()
337  try:
338  im = PILImage.open(event.userData['filename'])
339  if self.instruction[self.pageId]['Orientation'] == 'Landscape':
340  im = im.rotate(270)
341 
342  # hack for Windows, change method for loading EPS
343  if sys.platform == 'win32':
344  import types
345  im.load = types.MethodType(loadPSForWindows, im)
346  im.save(self.imgName, format = 'PNG')
347  except IOError, e:
348  del busy
349  dlg = HyperlinkDialog(self, title=_("Preview not available"),
350  message=_("Preview is not available probably due to missing Ghostscript."),
351  hyperlink='http://trac.osgeo.org/grass/wiki/CompileOnWindows#Ghostscript',
352  hyperlinkLabel=_("Please follow instructions on GRASS Trac Wiki."))
353  dlg.ShowModal()
354  dlg.Destroy()
355  return
356 
357 
358  rect = self.previewCanvas.ImageRect()
359  self.previewCanvas.image = wx.Image(self.imgName, wx.BITMAP_TYPE_PNG)
360  self.previewCanvas.DrawImage(rect = rect)
361 
362  del busy
363  self.SetStatusText(_('Preview generated'), 0)
364  self.book.SetSelection(1)
365  self.currentPage = 1
366 
367  grass.try_remove(event.userData['instrFile'])
368  if event.userData['temp']:
369  grass.try_remove(event.userData['filename'])
370 
371  self.delayedCall = wx.CallLater(4000, lambda: self.SetStatusText("", 0))
372 
373  def getFile(self, wildcard):
374  suffix = []
375  for filter in wildcard.split('|')[1::2]:
376  s = filter.strip('*').split('.')[1]
377  if s:
378  s = '.' + s
379  suffix.append(s)
380  raster = self.instruction.FindInstructionByType('raster')
381  if raster:
382  rasterId = raster.id
383  else:
384  rasterId = None
385 
386 
387  if rasterId and self.instruction[rasterId]['raster']:
388  mapName = self.instruction[rasterId]['raster'].split('@')[0] + suffix[0]
389  else:
390  mapName = ''
391 
392  filename = ''
393  dlg = wx.FileDialog(self, message = _("Save file as"), defaultDir = "",
394  defaultFile = mapName, wildcard = wildcard,
395  style = wx.CHANGE_DIR | wx.SAVE | wx.OVERWRITE_PROMPT)
396  if dlg.ShowModal() == wx.ID_OK:
397  filename = dlg.GetPath()
398  suffix = suffix[dlg.GetFilterIndex()]
399  if not os.path.splitext(filename)[1]:
400  filename = filename + suffix
401  elif os.path.splitext(filename)[1] != suffix and suffix != '':
402  filename = os.path.splitext(filename)[0] + suffix
403 
404  dlg.Destroy()
405  return filename
406 
407  def OnInstructionFile(self, event):
408  filename = self.getFile(wildcard = "*.psmap|*.psmap|Text file(*.txt)|*.txt|All files(*.*)|*.*")
409  if filename:
410  instrFile = open(filename, "w")
411  instrFile.write(self.InstructionFile())
412  instrFile.close()
413 
414  def OnLoadFile(self, event):
415  """!Load file and read instructions"""
416  #find file
417  filename = ''
418  dlg = wx.FileDialog(self, message = "Find instructions file", defaultDir = "",
419  defaultFile = '', wildcard = "All files (*.*)|*.*",
420  style = wx.CHANGE_DIR|wx.OPEN)
421  if dlg.ShowModal() == wx.ID_OK:
422  filename = dlg.GetPath()
423  dlg.Destroy()
424  if not filename:
425  return
426  # load instructions
427  readObjectId = []
428  readInstruction = Instruction(parent = self, objectsToDraw = readObjectId)
429  ok = readInstruction.Read(filename)
430  if not ok:
431  GMessage(_("Failed to read file %s.") % filename)
432  else:
433  self.instruction = self.canvas.instruction = readInstruction
434  self.objectId = self.canvas.objectId = readObjectId
435  self.pageId = self.canvas.pageId = self.instruction.FindInstructionByType('page').id
436  self.canvas.UpdateMapLabel()
437  self.canvas.dragId = -1
438  self.canvas.Clear()
439  self.canvas.SetPage()
440  #self.canvas.ZoomAll()
441 
442  self.DialogDataChanged(self.objectId)
443 
444  def OnPageSetup(self, event = None):
445  """!Specify paper size, margins and orientation"""
446  id = self.instruction.FindInstructionByType('page').id
447  dlg = PageSetupDialog(self, id = id, settings = self.instruction)
448  dlg.CenterOnScreen()
449  val = dlg.ShowModal()
450  if val == wx.ID_OK:
451  self.canvas.SetPage()
452  self.getInitMap()
453  self.canvas.RecalculatePosition(ids = self.objectId)
454  dlg.Destroy()
455 
456  def OnPointer(self, event):
457  self.toolbar.OnTool(event)
458  self.mouse["use"] = "pointer"
459  self.canvas.SetCursor(self.cursors["default"])
460  self.previewCanvas.SetCursor(self.cursors["default"])
461 
462  def OnPan(self, event):
463  self.toolbar.OnTool(event)
464  self.mouse["use"] = "pan"
465  self.canvas.SetCursor(self.cursors["hand"])
466  self.previewCanvas.SetCursor(self.cursors["hand"])
467 
468  def OnZoomIn(self, event):
469  self.toolbar.OnTool(event)
470  self.mouse["use"] = "zoomin"
471  self.canvas.SetCursor(self.cursors["cross"])
472  self.previewCanvas.SetCursor(self.cursors["cross"])
473 
474  def OnZoomOut(self, event):
475  self.toolbar.OnTool(event)
476  self.mouse["use"] = "zoomout"
477  self.canvas.SetCursor(self.cursors["cross"])
478  self.previewCanvas.SetCursor(self.cursors["cross"])
479 
480  def OnZoomAll(self, event):
481  self.mouseOld = self.mouse['use']
482  if self.currentPage == 0:
483  self.cursorOld = self.canvas.GetCursor()
484  else:
485  self.cursorOld = self.previewCanvas.GetCursor()
486  self.previewCanvas.GetCursor()
487  self.mouse["use"] = "zoomin"
488  if self.currentPage == 0:
489  self.canvas.ZoomAll()
490  else:
491  self.previewCanvas.ZoomAll()
492  self.mouse["use"] = self.mouseOld
493  if self.currentPage == 0:
494  self.canvas.SetCursor(self.cursorOld)
495  else:
496  self.previewCanvas.SetCursor(self.cursorOld)
497 
498 
499  def OnAddMap(self, event, notebook = False):
500  """!Add or edit map frame"""
501  if event is not None:
502  if event.GetId() != self.toolbar.action['id']:
503  self.actionOld = self.toolbar.action['id']
504  self.mouseOld = self.mouse['use']
505  self.cursorOld = self.canvas.GetCursor()
506  self.toolbar.OnTool(event)
507 
508  if self.instruction.FindInstructionByType('map'):
509  mapId = self.instruction.FindInstructionByType('map').id
510  else: mapId = None
511  id = [mapId, None, None]
512 
513  if notebook:
514  if self.instruction.FindInstructionByType('vector'):
515  vectorId = self.instruction.FindInstructionByType('vector').id
516  else: vectorId = None
517  if self.instruction.FindInstructionByType('raster'):
518  rasterId = self.instruction.FindInstructionByType('raster').id
519  else: rasterId = None
520  id[1] = rasterId
521  id[2] = vectorId
522 
523 
524  if mapId: # map exists
525 
526  self.toolbar.ToggleTool(self.actionOld, True)
527  self.toolbar.ToggleTool(self.toolbar.action['id'], False)
528  self.toolbar.action['id'] = self.actionOld
529  try:
530  self.canvas.SetCursor(self.cursorOld)
531  except AttributeError:
532  pass
533 
534 ## dlg = MapDialog(parent = self, id = id, settings = self.instruction,
535 ## notebook = notebook)
536 ## dlg.ShowModal()
537  if notebook:
538  #check map, raster, vector and save, destroy them
539  if 'map' in self.openDialogs:
540  self.openDialogs['map'].OnOK(event = None)
541  if 'raster' in self.openDialogs:
542  self.openDialogs['raster'].OnOK(event = None)
543  if 'vector' in self.openDialogs:
544  self.openDialogs['vector'].OnOK(event = None)
545 
546  if 'mapNotebook' not in self.openDialogs:
547  dlg = MapDialog(parent = self, id = id, settings = self.instruction,
548  notebook = notebook)
549  self.openDialogs['mapNotebook'] = dlg
550  self.openDialogs['mapNotebook'].Show()
551  else:
552  if 'mapNotebook' in self.openDialogs:
553  self.openDialogs['mapNotebook'].notebook.ChangeSelection(0)
554  else:
555  if 'map' not in self.openDialogs:
556  dlg = MapDialog(parent = self, id = id, settings = self.instruction,
557  notebook = notebook)
558  self.openDialogs['map'] = dlg
559  self.openDialogs['map'].Show()
560 
561 
562  else: # sofar no map
563  self.mouse["use"] = "addMap"
564  self.canvas.SetCursor(self.cursors["cross"])
565  if self.currentPage == 1:
566  self.book.SetSelection(0)
567  self.currentPage = 0
568 
569  def OnAddRaster(self, event):
570  """!Add raster map"""
571  if self.instruction.FindInstructionByType('raster'):
572  id = self.instruction.FindInstructionByType('raster').id
573  else: id = None
574  if self.instruction.FindInstructionByType('map'):
575  mapId = self.instruction.FindInstructionByType('map').id
576  else: mapId = None
577 
578  if not id:
579  if not mapId:
580  GMessage(message = _("Please, create map frame first."))
581  return
582 
583 ## dlg = RasterDialog(self, id = id, settings = self.instruction)
584 ## dlg.ShowModal()
585  if 'mapNotebook' in self.openDialogs:
586  self.openDialogs['mapNotebook'].notebook.ChangeSelection(1)
587  else:
588  if 'raster' not in self.openDialogs:
589  dlg = RasterDialog(self, id = id, settings = self.instruction)
590  self.openDialogs['raster'] = dlg
591  self.openDialogs['raster'].Show()
592 
593  def OnAddVect(self, event):
594  """!Add vector map"""
595  if self.instruction.FindInstructionByType('vector'):
596  id = self.instruction.FindInstructionByType('vector').id
597  else: id = None
598  if self.instruction.FindInstructionByType('map'):
599  mapId = self.instruction.FindInstructionByType('map').id
600  else: mapId = None
601  if not id:
602  if not mapId:
603  GMessage(message = _("Please, create map frame first."))
604  return
605 
606 ## dlg = MainVectorDialog(self, id = id, settings = self.instruction)
607 ## dlg.ShowModal()
608  if 'mapNotebook' in self.openDialogs:
609  self.openDialogs['mapNotebook'].notebook.ChangeSelection(2)
610  else:
611  if 'vector' not in self.openDialogs:
612  dlg = MainVectorDialog(self, id = id, settings = self.instruction)
613  self.openDialogs['vector'] = dlg
614  self.openDialogs['vector'].Show()
615 
616  def OnAddScalebar(self, event):
617  """!Add scalebar"""
618  if projInfo()['proj'] == 'll':
619  GMessage(message = _("Scalebar is not appropriate for this projection"))
620  return
621  if self.instruction.FindInstructionByType('scalebar'):
622  id = self.instruction.FindInstructionByType('scalebar').id
623  else: id = None
624 
625  if 'scalebar' not in self.openDialogs:
626  dlg = ScalebarDialog(self, id = id, settings = self.instruction)
627  self.openDialogs['scalebar'] = dlg
628  self.openDialogs['scalebar'].Show()
629 
630  def OnAddLegend(self, event, page = 0):
631  """!Add raster or vector legend"""
632  if self.instruction.FindInstructionByType('rasterLegend'):
633  idR = self.instruction.FindInstructionByType('rasterLegend').id
634  else: idR = None
635  if self.instruction.FindInstructionByType('vectorLegend'):
636  idV = self.instruction.FindInstructionByType('vectorLegend').id
637  else: idV = None
638 
639  if 'rasterLegend' not in self.openDialogs:
640  dlg = LegendDialog(self, id = [idR, idV], settings = self.instruction, page = page)
641  self.openDialogs['rasterLegend'] = dlg
642  self.openDialogs['vectorLegend'] = dlg
643  self.openDialogs['rasterLegend'].notebook.ChangeSelection(page)
644  self.openDialogs['rasterLegend'].Show()
645 
646  def OnAddMapinfo(self, event):
647  if self.instruction.FindInstructionByType('mapinfo'):
648  id = self.instruction.FindInstructionByType('mapinfo').id
649  else: id = None
650 
651  if 'mapinfo' not in self.openDialogs:
652  dlg = MapinfoDialog(self, id = id, settings = self.instruction)
653  self.openDialogs['mapinfo'] = dlg
654  self.openDialogs['mapinfo'].Show()
655 
656  def OnAddImage(self, event, id = None):
657  """!Show dialog for image adding and editing"""
658  position = None
659  if 'image' in self.openDialogs:
660  position = self.openDialogs['image'].GetPosition()
661  self.openDialogs['image'].OnApply(event = None)
662  self.openDialogs['image'].Destroy()
663  dlg = ImageDialog(self, id = id, settings = self.instruction)
664  self.openDialogs['image'] = dlg
665  if position:
666  dlg.SetPosition(position)
667  dlg.Show()
668 
669  def OnAddNorthArrow(self, event, id = None):
670  """!Show dialog for north arrow adding and editing"""
671  if self.instruction.FindInstructionByType('northArrow'):
672  id = self.instruction.FindInstructionByType('northArrow').id
673  else: id = None
674 
675  if 'northArrow' not in self.openDialogs:
676  dlg = NorthArrowDialog(self, id = id, settings = self.instruction)
677  self.openDialogs['northArrow'] = dlg
678  self.openDialogs['northArrow'].Show()
679 
680  def OnAddText(self, event, id = None):
681  """!Show dialog for text adding and editing"""
682  position = None
683  if 'text' in self.openDialogs:
684  position = self.openDialogs['text'].GetPosition()
685  self.openDialogs['text'].OnApply(event = None)
686  self.openDialogs['text'].Destroy()
687  dlg = TextDialog(self, id = id, settings = self.instruction)
688  self.openDialogs['text'] = dlg
689  if position:
690  dlg.SetPosition(position)
691  dlg.Show()
692 
693  def OnAddPoint(self, event):
694  """!Add point action selected"""
695  self.mouse["use"] = "addPoint"
696  self.canvas.SetCursor(self.cursors["cross"])
697 
698  def AddPoint(self, id = None, coordinates = None):
699  """!Add point and open property dialog.
700 
701  @param id id point id (None if creating new point)
702  @param coordinates coordinates of new point
703  """
704  position = None
705  if 'point' in self.openDialogs:
706  position = self.openDialogs['point'].GetPosition()
707  self.openDialogs['point'].OnApply(event = None)
708  self.openDialogs['point'].Destroy()
709  dlg = PointDialog(self, id = id, settings = self.instruction,
710  coordinates = coordinates)
711  self.openDialogs['point'] = dlg
712  if position:
713  dlg.SetPosition(position)
714  if coordinates:
715  dlg.OnApply(event = None)
716  dlg.Show()
717 
718  def OnAddLine(self, event):
719  """!Add line action selected"""
720  self.mouse["use"] = "addLine"
721  self.canvas.SetCursor(self.cursors["cross"])
722 
723  def AddLine(self, id = None, coordinates = None):
724  """!Add line and open property dialog.
725 
726  @param id id line id (None if creating new line)
727  @param coordinates coordinates of new line
728  """
729  position = None
730  if 'line' in self.openDialogs:
731  position = self.openDialogs['line'].GetPosition()
732  self.openDialogs['line'].OnApply(event = None)
733  self.openDialogs['line'].Destroy()
734  dlg = RectangleDialog(self, id = id, settings = self.instruction,
735  type = 'line', coordinates = coordinates)
736  self.openDialogs['line'] = dlg
737  if position:
738  dlg.SetPosition(position)
739  if coordinates:
740  dlg.OnApply(event = None)
741  dlg.Show()
742 
743  def OnAddRectangle(self, event):
744  """!Add rectangle action selected"""
745  self.mouse["use"] = "addRectangle"
746  self.canvas.SetCursor(self.cursors["cross"])
747 
748  def AddRectangle(self, id = None, coordinates = None):
749  """!Add rectangle and open property dialog.
750 
751  @param id id rectangle id (None if creating new rectangle)
752  @param coordinates coordinates of new rectangle
753  """
754  position = None
755  if 'rectangle' in self.openDialogs:
756  position = self.openDialogs['rectangle'].GetPosition()
757  self.openDialogs['rectangle'].OnApply(event = None)
758  self.openDialogs['rectangle'].Destroy()
759  dlg = RectangleDialog(self, id = id, settings = self.instruction,
760  type = 'rectangle', coordinates = coordinates)
761  self.openDialogs['rectangle'] = dlg
762  if position:
763  dlg.SetPosition(position)
764  if coordinates:
765  dlg.OnApply(event = None)
766  dlg.Show()
767 
768  def getModifiedTextBounds(self, x, y, textExtent, rotation):
769  """!computes bounding box of rotated text, not very precisely"""
770  w, h = textExtent
771  rotation = float(rotation)/180*pi
772  H = float(w) * sin(rotation)
773  W = float(w) * cos(rotation)
774  X, Y = x, y
775  if pi/2 < rotation <= 3*pi/2:
776  X = x + W
777  if 0 < rotation < pi:
778  Y = y - H
779  if rotation == 0:
780  return wx.Rect(x,y, *textExtent)
781  else:
782  return wx.Rect(X, Y, abs(W), abs(H)).Inflate(h,h)
783 
784  def makePSFont(self, textDict):
785  """!creates a wx.Font object from selected postscript font. To be
786  used for estimating bounding rectangle of text"""
787 
788  fontsize = textDict['fontsize'] * self.canvas.currScale
789  fontface = textDict['font'].split('-')[0]
790  try:
791  fontstyle = textDict['font'].split('-')[1]
792  except IndexError:
793  fontstyle = ''
794 
795  if fontface == "Times":
796  family = wx.FONTFAMILY_ROMAN
797  face = "times"
798  elif fontface == "Helvetica":
799  family = wx.FONTFAMILY_SWISS
800  face = 'helvetica'
801  elif fontface == "Courier":
802  family = wx.FONTFAMILY_TELETYPE
803  face = 'courier'
804  else:
805  family = wx.FONTFAMILY_DEFAULT
806  face = ''
807 
808  style = wx.FONTSTYLE_NORMAL
809  weight = wx.FONTWEIGHT_NORMAL
810 
811  if 'Oblique' in fontstyle:
812  style = wx.FONTSTYLE_SLANT
813 
814  if 'Italic' in fontstyle:
815  style = wx.FONTSTYLE_ITALIC
816 
817  if 'Bold' in fontstyle:
818  weight = wx.FONTWEIGHT_BOLD
819 
820  try:
821  fn = wx.Font(pointSize = fontsize, family = family, style = style,
822  weight = weight, face = face)
823  except:
824  fn = wx.Font(pointSize = fontsize, family = wx.FONTFAMILY_DEFAULT,
825  style = wx.FONTSTYLE_NORMAL, weight = wx.FONTWEIGHT_NORMAL)
826 
827  return fn
828 
829 
830  def getTextExtent(self, textDict):
831  """!Estimates bounding rectangle of text"""
832  #fontsize = str(fontsize if fontsize >= 4 else 4)
833  dc = wx.ClientDC(self) # dc created because of method GetTextExtent, which pseudoDC lacks
834 
835  fn = self.makePSFont(textDict)
836 
837  try:
838  dc.SetFont(fn)
839  w,h,lh = dc.GetMultiLineTextExtent(textDict['text'])
840  return (w,h)
841  except:
842  return (0,0)
843 
844  def getInitMap(self):
845  """!Create default map frame when no map is selected, needed for coordinates in map units"""
846  instrFile = grass.tempfile()
847  instrFileFd = open(instrFile, mode = 'w')
848  instrFileFd.write(self.InstructionFile())
849  instrFileFd.flush()
850  instrFileFd.close()
851 
852  page = self.instruction.FindInstructionByType('page')
853  mapInitRect = GetMapBounds(instrFile, portrait = (page['Orientation'] == 'Portrait'))
854  grass.try_remove(instrFile)
855 
856  region = grass.region()
857  units = UnitConversion(self)
858  realWidth = units.convert(value = abs(region['w'] - region['e']), fromUnit = 'meter', toUnit = 'inch')
859  scale = mapInitRect.Get()[2]/realWidth
860 
861  initMap = self.instruction.FindInstructionByType('initMap')
862  if initMap:
863  id = initMap.id
864  else:
865  id = None
866 
867 
868  if not id:
869  id = wx.NewId()
870  initMap = InitMap(id)
871  self.instruction.AddInstruction(initMap)
872  self.instruction[id].SetInstruction(dict(rect = mapInitRect, scale = scale))
873 
874  def OnDelete(self, event):
875  if self.canvas.dragId != -1 and self.currentPage == 0:
876  if self.instruction[self.canvas.dragId].type == 'map':
877  self.deleteObject(self.canvas.dragId)
878  self.getInitMap()
879  self.canvas.RecalculateEN()
880  else:
881  self.deleteObject(self.canvas.dragId)
882 
883  def deleteObject(self, id):
884  """!Deletes object, his id and redraws"""
885  #delete from canvas
886  self.canvas.pdcObj.RemoveId(id)
887  if id == self.canvas.dragId:
888  self.canvas.pdcTmp.RemoveAll()
889  self.canvas.dragId = -1
890  self.canvas.Refresh()
891 
892  # delete from instructions
893  del self.instruction[id]
894 
895  def DialogDataChanged(self, id):
896  ids = id
897  if type(id) == int:
898  ids = [id]
899  for id in ids:
900  itype = self.instruction[id].type
901 
902  if itype in ('scalebar', 'mapinfo', 'image'):
903  drawRectangle = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'], canvasToPaper = False)
904  self.canvas.UpdateLabel(itype = itype, id = id)
905  self.canvas.Draw(pen = self.pen[itype], brush = self.brush[itype],
906  pdc = self.canvas.pdcObj, drawid = id, pdctype = 'rectText', bb = drawRectangle)
907  self.canvas.RedrawSelectBox(id)
908  if itype == 'northArrow':
909  self.canvas.UpdateLabel(itype = itype, id = id)
910  drawRectangle = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'], canvasToPaper = False)
911  self.canvas.Draw(pen = self.pen[itype], brush = self.brush[itype],
912  pdc = self.canvas.pdcObj, drawid = id, pdctype = 'bitmap', bb = drawRectangle)
913  self.canvas.RedrawSelectBox(id)
914 
915  if itype in ('point', 'line', 'rectangle'):
916  drawRectangle = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'], canvasToPaper = False)
917  # coords only for line
918  coords = None
919  if itype == 'line':
920  point1 = self.instruction[id]['where'][0]
921  point2 = self.instruction[id]['where'][1]
922  point1Coords = self.canvas.CanvasPaperCoordinates(rect = Rect2DPS(point1, (0, 0)), canvasToPaper = False)[:2]
923  point2Coords = self.canvas.CanvasPaperCoordinates(rect = Rect2DPS(point2, (0, 0)), canvasToPaper = False)[:2]
924  coords = (point1Coords, point2Coords)
925 
926  # fill color is not in line
927  fcolor = None
928  if 'fcolor' in self.instruction[id].GetInstruction():
929  fcolor = self.instruction[id]['fcolor']
930  # width is not in point
931  width = None
932  if 'width' in self.instruction[id].GetInstruction():
933  width = self.instruction[id]['width']
934 
935  self.canvas.DrawGraphics(drawid = id, color = self.instruction[id]['color'], shape = itype,
936  fcolor = fcolor, width = width, bb = drawRectangle, lineCoords = coords)
937 
938  self.canvas.RedrawSelectBox(id)
939 
940  if itype == 'text':
941 
942  if self.instruction[id]['rotate']:
943  rot = float(self.instruction[id]['rotate'])
944  else:
945  rot = 0
946 
947  extent = self.getTextExtent(textDict = self.instruction[id].GetInstruction())
948  rect = Rect2DPS(self.instruction[id]['where'], (0, 0))
949  self.instruction[id]['coords'] = list(self.canvas.CanvasPaperCoordinates(rect = rect, canvasToPaper = False)[:2])
950 
951  #computes text coordinates according to reference point, not precisely
952  if self.instruction[id]['ref'].split()[0] == 'lower':
953  self.instruction[id]['coords'][1] -= extent[1]
954  elif self.instruction[id]['ref'].split()[0] == 'center':
955  self.instruction[id]['coords'][1] -= extent[1]/2
956  if self.instruction[id]['ref'].split()[1] == 'right':
957  self.instruction[id]['coords'][0] -= extent[0] * cos(rot/180*pi)
958  self.instruction[id]['coords'][1] += extent[0] * sin(rot/180*pi)
959  elif self.instruction[id]['ref'].split()[1] == 'center':
960  self.instruction[id]['coords'][0] -= extent[0]/2 * cos(rot/180*pi)
961  self.instruction[id]['coords'][1] += extent[0]/2 * sin(rot/180*pi)
962 
963  self.instruction[id]['coords'][0] += self.instruction[id]['xoffset']
964  self.instruction[id]['coords'][1] -= self.instruction[id]['yoffset']
965  coords = self.instruction[id]['coords']
966  self.instruction[id]['rect'] = bounds = self.getModifiedTextBounds(coords[0], coords[1], extent, rot)
967  self.canvas.DrawRotText(pdc = self.canvas.pdcObj, drawId = id,
968  textDict = self.instruction[id].GetInstruction(),
969  coords = coords, bounds = bounds)
970  self.canvas.RedrawSelectBox(id)
971 
972  if itype in ('map', 'vector', 'raster'):
973 
974  if itype == 'raster':#set resolution
975  info = grass.raster_info(self.instruction[id]['raster'])
976  RunCommand('g.region', nsres = info['nsres'], ewres = info['ewres'])
977  # change current raster in raster legend
978 
979  if 'rasterLegend' in self.openDialogs:
980  self.openDialogs['rasterLegend'].updateDialog()
981  id = self.instruction.FindInstructionByType('map').id
982 
983  #check resolution
984  if itype == 'raster':
985  SetResolution(dpi = self.instruction[id]['resolution'],
986  width = self.instruction[id]['rect'].width,
987  height = self.instruction[id]['rect'].height)
988  rectCanvas = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'],
989  canvasToPaper = False)
990  self.canvas.RecalculateEN()
991  self.canvas.UpdateMapLabel()
992 
993  self.canvas.Draw(pen = self.pen['map'], brush = self.brush['map'],
994  pdc = self.canvas.pdcObj, drawid = id, pdctype = 'rectText', bb = rectCanvas)
995  # redraw select box
996  self.canvas.RedrawSelectBox(id)
997  self.canvas.pdcTmp.RemoveId(self.canvas.idZoomBoxTmp)
998  # redraw to get map to the bottom layer
999  #self.canvas.Zoom(zoomFactor = 1, view = (0, 0))
1000 
1001  if itype == 'rasterLegend':
1002  if self.instruction[id]['rLegend']:
1003  self.canvas.UpdateLabel(itype = itype, id = id)
1004  drawRectangle = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'], canvasToPaper = False)
1005  self.canvas.Draw(pen = self.pen[itype], brush = self.brush[itype],
1006  pdc = self.canvas.pdcObj, drawid = id, pdctype = 'rectText', bb = drawRectangle)
1007  self.canvas.RedrawSelectBox(id)
1008  else:
1009  self.deleteObject(id)
1010 
1011  if itype == 'vectorLegend':
1012  if not self.instruction.FindInstructionByType('vector'):
1013  self.deleteObject(id)
1014  elif self.instruction[id]['vLegend']:
1015  self.canvas.UpdateLabel(itype = itype, id = id)
1016  drawRectangle = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'], canvasToPaper = False)
1017  self.canvas.Draw(pen = self.pen[itype], brush = self.brush[itype],
1018  pdc = self.canvas.pdcObj, drawid = id, pdctype = 'rectText', bb = drawRectangle)
1019  self.canvas.RedrawSelectBox(id)
1020 
1021  else:
1022  self.deleteObject(id)
1023 
1024  def OnPageChanged(self, event):
1025  """!Flatnotebook page has changed"""
1026  self.currentPage = self.book.GetPageIndex(self.book.GetCurrentPage())
1027  if self.currentPage == 1:
1028  self.SetStatusText(_("Press button with green triangle icon to generate preview."))
1029  else:
1030  self.SetStatusText('')
1031 
1032 
1033 
1034  def OnPageChanging(self, event):
1035  """!Flatnotebook page is changing"""
1036  if self.currentPage == 0 and self.mouse['use'] == 'addMap':
1037  event.Veto()
1038 
1039  def OnHelp(self, event):
1040  """!Show help"""
1041  if self.parent and self.parent.GetName() == 'LayerManager':
1042  log = self.parent.GetLogWindow()
1043  log.RunCmd(['g.manual',
1044  'entry=wxGUI.PsMap'])
1045  else:
1046  RunCommand('g.manual',
1047  quiet = True,
1048  entry = 'wxGUI.PsMap')
1049 
1050  def OnAbout(self, event):
1051  """!Display About window"""
1052  info = wx.AboutDialogInfo()
1053 
1054  info.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
1055  info.SetName(_('wxGUI Cartographic Composer'))
1056  info.SetWebSite('http://grass.osgeo.org')
1057  info.SetDescription(_('(C) 2011 by the GRASS Development Team\n\n') +
1058  '\n'.join(textwrap.wrap(_('This program is free software under the GNU General Public License'
1059  '(>=v2). Read the file COPYING that comes with GRASS for details.'), 75)))
1060 
1061  wx.AboutBox(info)
1062 
1063  def OnCloseWindow(self, event):
1064  """!Close window"""
1065  try:
1066  os.remove(self.imgName)
1067  except OSError:
1068  pass
1069  grass.set_raise_on_error(False)
1070  if hasattr(self, 'delayedCall') and self.delayedCall.IsRunning():
1071  self.delayedCall.Stop()
1072  self.Destroy()
1073 
1074 
1075 
1076 class PsMapBufferedWindow(wx.Window):
1077  """!A buffered window class.
1078 
1079  @param parent parent window
1080  @param kwargs other wx.Window parameters
1081  """
1082  def __init__(self, parent, id = wx.ID_ANY,
1083  style = wx.NO_FULL_REPAINT_ON_RESIZE,
1084  **kwargs):
1085  wx.Window.__init__(self, parent, id = id, style = style)
1086  self.parent = parent
1087 
1088  self.FitInside()
1089 
1090  # store an off screen empty bitmap for saving to file
1091  self._buffer = None
1092  # indicates whether or not a resize event has taken place
1093  self.resize = False
1094 
1095  self.mouse = kwargs['mouse']
1096  self.cursors = kwargs['cursors']
1097  self.preview = kwargs['preview']
1098  self.pen = kwargs['pen']
1099  self.brush = kwargs['brush']
1100 
1101  if kwargs.has_key('instruction'):
1102  self.instruction = kwargs['instruction']
1103  if kwargs.has_key('openDialogs'):
1104  self.openDialogs = kwargs['openDialogs']
1105  if kwargs.has_key('pageId'):
1106  self.pageId = kwargs['pageId']
1107  if kwargs.has_key('objectId'):
1108  self.objectId = kwargs['objectId']
1109 
1110 
1111  #labels
1112  self.itemLabelsDict = { 'map': 'MAP FRAME',
1113  'rasterLegend': 'RASTER LEGEND',
1114  'vectorLegend': 'VECTOR LEGEND',
1115  'mapinfo': 'MAP INFO',
1116  'scalebar': 'SCALE BAR',
1117  'image': 'IMAGE',
1118  'northArrow': 'NORTH ARROW'}
1119  self.itemLabels = {}
1120 
1121  # define PseudoDC
1122  self.pdc = wx.PseudoDC()
1123  self.pdcObj = wx.PseudoDC()
1124  self.pdcPaper = wx.PseudoDC()
1125  self.pdcTmp = wx.PseudoDC()
1126  self.pdcImage = wx.PseudoDC()
1127 
1128  self.SetClientSize((700,510))#?
1129  self._buffer = wx.EmptyBitmap(*self.GetClientSize())
1130 
1131  self.idBoxTmp = wx.NewId()
1132  self.idZoomBoxTmp = wx.NewId()
1133  self.idResizeBoxTmp = wx.NewId()
1134  self.idLinePointsTmp = (wx.NewId(), wx.NewId()) # ids of marks for moving line vertices
1135 
1136  self.resizeBoxSize = wx.Size(8, 8)
1137  self.showResizeHelp = False # helper for correctly working statusbar
1138 
1139 
1140 
1141  self.dragId = -1
1142 
1143  if self.preview:
1144  self.image = None
1145  self.imageId = 2000
1146  self.imgName = self.parent.imgName
1147 
1148 
1149 
1150  self.currScale = None
1151 
1152  self.Clear()
1153 
1154  self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: None)
1155 
1156  self.Bind(wx.EVT_PAINT, self.OnPaint)
1157  self.Bind(wx.EVT_SIZE, self.OnSize)
1158  self.Bind(wx.EVT_IDLE, self.OnIdle)
1159  # self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouse)
1160  self.Bind(wx.EVT_MOUSE_EVENTS, self.MouseActions)
1161 
1162 
1163  def Clear(self):
1164  """!Clear canvas and set paper
1165  """
1166  bg = wx.LIGHT_GREY_BRUSH
1167  self.pdcPaper.BeginDrawing()
1168  self.pdcPaper.SetBackground(bg)
1169  self.pdcPaper.Clear()
1170  self.pdcPaper.EndDrawing()
1171 
1172  self.pdcObj.RemoveAll()
1173  self.pdcTmp.RemoveAll()
1174 
1175 
1176 
1177  if not self.preview:
1178  self.SetPage()
1179 
1180 
1181  def CanvasPaperCoordinates(self, rect, canvasToPaper = True):
1182  """!Converts canvas (pixel) -> paper (inch) coordinates and size and vice versa"""
1183 
1184  units = UnitConversion(self)
1185 
1186  fromU = 'pixel'
1187  toU = 'inch'
1188  pRect = self.pdcPaper.GetIdBounds(self.pageId)
1189  pRectx, pRecty = pRect.x, pRect.y
1190  scale = 1/self.currScale
1191  if not canvasToPaper: # paper -> canvas
1192  fromU = 'inch'
1193  toU = 'pixel'
1194  scale = self.currScale
1195  pRectx = units.convert(value = - pRect.x, fromUnit = 'pixel', toUnit = 'inch' ) /scale #inch, real, negative
1196  pRecty = units.convert(value = - pRect.y, fromUnit = 'pixel', toUnit = 'inch' ) /scale
1197  Width = units.convert(value = rect.GetWidth(), fromUnit = fromU, toUnit = toU) * scale
1198  Height = units.convert(value = rect.GetHeight(), fromUnit = fromU, toUnit = toU) * scale
1199  X = units.convert(value = (rect.GetX() - pRectx), fromUnit = fromU, toUnit = toU) * scale
1200  Y = units.convert(value = (rect.GetY() - pRecty), fromUnit = fromU, toUnit = toU) * scale
1201 
1202  return Rect2D(X, Y, Width, Height)
1203 
1204 
1205 
1206  def SetPage(self):
1207  """!Sets and changes page, redraws paper"""
1208 
1209  page = self.instruction[self.pageId]
1210  if not page:
1211  page = PageSetup(id = self.pageId)
1212  self.instruction.AddInstruction(page)
1213 
1214  ppi = wx.ClientDC(self).GetPPI()
1215  cW, cH = self.GetClientSize()
1216  pW, pH = page['Width']*ppi[0], page['Height']*ppi[1]
1217 
1218  if self.currScale is None:
1219  self.currScale = min(cW/pW, cH/pH)
1220  pW = pW * self.currScale
1221  pH = pH * self.currScale
1222 
1223  x = cW/2 - pW/2
1224  y = cH/2 - pH/2
1225  self.DrawPaper(wx.Rect(x, y, pW, pH))
1226 
1227 
1228  def modifyRectangle(self, r):
1229  """! Recalculates rectangle not to have negative size"""
1230  if r.GetWidth() < 0:
1231  r.SetX(r.GetX() + r.GetWidth())
1232  if r.GetHeight() < 0:
1233  r.SetY(r.GetY() + r.GetHeight())
1234  r.SetWidth(abs(r.GetWidth()))
1235  r.SetHeight(abs(r.GetHeight()))
1236  return r
1237 
1238  def RecalculateEN(self):
1239  """!Recalculate east and north for texts (eps, points) after their or map's movement"""
1240  try:
1241  mapId = self.instruction.FindInstructionByType('map').id
1242  except AttributeError:
1243  mapId = self.instruction.FindInstructionByType('initMap').id
1244 
1245  for itemType in ('text', 'image', 'northArrow', 'point', 'line', 'rectangle'):
1246  items = self.instruction.FindInstructionByType(itemType, list = True)
1247  for item in items:
1248  instr = self.instruction[item.id]
1249  if itemType in ('line', 'rectangle'):
1250  if itemType == 'line':
1251  e1, n1 = PaperMapCoordinates(mapInstr = self.instruction[mapId], x = instr['where'][0][0],
1252  y = instr['where'][0][1], paperToMap = True)
1253  e2, n2 = PaperMapCoordinates(mapInstr = self.instruction[mapId], x = instr['where'][1][0],
1254  y = instr['where'][1][1], paperToMap = True)
1255  else:
1256  e1, n1 = PaperMapCoordinates(mapInstr = self.instruction[mapId], x = instr['rect'].GetLeft(),
1257  y = instr['rect'].GetTop(), paperToMap = True)
1258  e2, n2 = PaperMapCoordinates(mapInstr = self.instruction[mapId], x = instr['rect'].GetRight(),
1259  y = instr['rect'].GetBottom(), paperToMap = True)
1260  instr['east1'] = e1
1261  instr['north1'] = n1
1262  instr['east2'] = e2
1263  instr['north2'] = n2
1264  else:
1265  e, n = PaperMapCoordinates(mapInstr = self.instruction[mapId], x = instr['where'][0],
1266  y = instr['where'][1], paperToMap = True)
1267  instr['east'], instr['north'] = e, n
1268 
1269  def OnPaint(self, event):
1270  """!Draw pseudo DC to buffer
1271  """
1272  if not self._buffer:
1273  return
1274  dc = wx.BufferedPaintDC(self, self._buffer)
1275  # use PrepareDC to set position correctly
1276  # probably does nothing, removed from wxPython 2.9
1277  # self.PrepareDC(dc)
1278 
1279  dc.SetBackground(wx.LIGHT_GREY_BRUSH)
1280  dc.Clear()
1281 
1282  # draw paper
1283  if not self.preview:
1284  self.pdcPaper.DrawToDC(dc)
1285  # draw to the DC using the calculated clipping rect
1286 
1287  rgn = self.GetUpdateRegion()
1288 
1289  if not self.preview:
1290  self.pdcObj.DrawToDCClipped(dc, rgn.GetBox())
1291  else:
1292  self.pdcImage.DrawToDCClipped(dc, rgn.GetBox())
1293  self.pdcTmp.DrawToDCClipped(dc, rgn.GetBox())
1294 
1295  def MouseActions(self, event):
1296  """!Mouse motion and button click notifier
1297  """
1298  # zoom with mouse wheel
1299  if event.GetWheelRotation() != 0:
1300  self.OnMouseWheel(event)
1301 
1302  # left mouse button pressed
1303  elif event.LeftDown():
1304  self.OnLeftDown(event)
1305 
1306  # left mouse button released
1307  elif event.LeftUp():
1308  self.OnLeftUp(event)
1309 
1310  # dragging
1311  elif event.Dragging():
1312  self.OnDragging(event)
1313 
1314  # double click
1315  elif event.ButtonDClick():
1316  self.OnButtonDClick(event)
1317 
1318  # middle mouse button pressed
1319  elif event.MiddleDown():
1320  self.OnMiddleDown(event)
1321 
1322  elif event.Moving():
1323  self.OnMouseMoving(event)
1324 
1325  def OnMouseWheel(self, event):
1326  """!Mouse wheel scrolled.
1327 
1328  Changes zoom."""
1329  if UserSettings.Get(group = 'display',
1330  key = 'mouseWheelZoom',
1331  subkey = 'selection') == 2:
1332  event.Skip()
1333  return
1334 
1335  zoom = event.GetWheelRotation()
1336  oldUse = self.mouse['use']
1337  self.mouse['begin'] = event.GetPosition()
1338 
1339  if UserSettings.Get(group = 'display',
1340  key = 'scrollDirection',
1341  subkey = 'selection'):
1342  zoom *= -1
1343 
1344  if zoom > 0:
1345  self.mouse['use'] = 'zoomin'
1346  else:
1347  self.mouse['use'] = 'zoomout'
1348 
1349  zoomFactor, view = self.ComputeZoom(wx.Rect(0, 0, 0, 0))
1350  self.Zoom(zoomFactor, view)
1351  self.mouse['use'] = oldUse
1352 
1353  def OnMouseMoving(self, event):
1354  """!Mouse cursor moving.
1355 
1356  Change cursor when moving over resize marker.
1357  """
1358  if self.preview:
1359  return
1360 
1361  if self.mouse['use'] in ('pointer', 'resize'):
1362  pos = event.GetPosition()
1363  foundResize = self.pdcTmp.FindObjects(pos[0], pos[1])
1364  if foundResize and foundResize[0] in (self.idResizeBoxTmp,) + self.idLinePointsTmp:
1365  self.SetCursor(self.cursors["sizenwse"])
1366  self.parent.SetStatusText(_('Click and drag to resize object'), 0)
1367  self.showResizeHelp = True
1368  else:
1369  if self.showResizeHelp:
1370  self.parent.SetStatusText('', 0)
1371  self.SetCursor(self.cursors["default"])
1372  self.showResizeHelp = False
1373 
1374  def OnLeftDown(self, event):
1375  """!Left mouse button pressed.
1376 
1377  Select objects, redraw, prepare for moving/resizing.
1378  """
1379  self.mouse['begin'] = event.GetPosition()
1380  self.begin = self.mouse['begin']
1381 
1382  # select
1383  if self.mouse['use'] == 'pointer':
1384  found = self.pdcObj.FindObjects(self.mouse['begin'][0], self.mouse['begin'][1])
1385  foundResize = self.pdcTmp.FindObjects(self.mouse['begin'][0], self.mouse['begin'][1])
1386 
1387  if foundResize and foundResize[0] in (self.idResizeBoxTmp,) + self.idLinePointsTmp:
1388  self.mouse['use'] = 'resize'
1389 
1390  # when resizing, proportions match region
1391  if self.instruction[self.dragId].type == 'map':
1392  self.constraint = False
1393  self.mapBounds = self.pdcObj.GetIdBounds(self.dragId)
1394  if self.instruction[self.dragId]['scaleType'] in (0, 1, 2):
1395  self.constraint = True
1396  self.mapBounds = self.pdcObj.GetIdBounds(self.dragId)
1397 
1398  if self.instruction[self.dragId].type == 'line':
1399  self.currentLinePoint = self.idLinePointsTmp.index(foundResize[0])
1400 
1401  elif found:
1402  self.dragId = found[0]
1403  self.RedrawSelectBox(self.dragId)
1404  if self.instruction[self.dragId].type not in ('map', 'rectangle'):
1405  self.pdcTmp.RemoveId(self.idResizeBoxTmp)
1406  self.Refresh()
1407  if self.instruction[self.dragId].type != 'line':
1408  for id in self.idLinePointsTmp:
1409  self.pdcTmp.RemoveId(id)
1410  self.Refresh()
1411 
1412  else:
1413  self.dragId = -1
1414  self.pdcTmp.RemoveId(self.idBoxTmp)
1415  self.pdcTmp.RemoveId(self.idResizeBoxTmp)
1416  for id in self.idLinePointsTmp:
1417  self.pdcTmp.RemoveId(id)
1418  self.Refresh()
1419 
1420  def OnLeftUp(self, event):
1421  """!Left mouse button released.
1422 
1423  Recalculate zooming/resizing/moving and redraw.
1424  """
1425  # zoom in, zoom out
1426  if self.mouse['use'] in ('zoomin','zoomout'):
1427  zoomR = self.pdcTmp.GetIdBounds(self.idZoomBoxTmp)
1428  self.pdcTmp.RemoveId(self.idZoomBoxTmp)
1429  self.Refresh()
1430  zoomFactor, view = self.ComputeZoom(zoomR)
1431  self.Zoom(zoomFactor, view)
1432 
1433  # draw map frame
1434  if self.mouse['use'] == 'addMap':
1435  rectTmp = self.pdcTmp.GetIdBounds(self.idZoomBoxTmp)
1436  # too small rectangle, it's usually some mistake
1437  if rectTmp.GetWidth() < 20 or rectTmp.GetHeight() < 20:
1438  self.pdcTmp.RemoveId(self.idZoomBoxTmp)
1439  self.Refresh()
1440  return
1441  rectPaper = self.CanvasPaperCoordinates(rect = rectTmp, canvasToPaper = True)
1442 
1443  dlg = MapDialog(parent = self.parent, id = [None, None, None], settings = self.instruction,
1444  rect = rectPaper)
1445  self.openDialogs['map'] = dlg
1446  self.openDialogs['map'].Show()
1447 
1448  self.mouse['use'] = self.parent.mouseOld
1449 
1450  self.SetCursor(self.parent.cursorOld)
1451  self.parent.toolbar.ToggleTool(self.parent.actionOld, True)
1452  self.parent.toolbar.ToggleTool(self.parent.toolbar.action['id'], False)
1453  self.parent.toolbar.action['id'] = self.parent.actionOld
1454  return
1455 
1456  # resize resizable objects (map, line, rectangle)
1457  if self.mouse['use'] == 'resize':
1458  mapObj = self.instruction.FindInstructionByType('map')
1459  if not mapObj:
1460  mapObj = self.instruction.FindInstructionByType('initMap')
1461  mapId = mapObj.id
1462 
1463  if self.dragId == mapId:
1464  # necessary to change either map frame (scaleType 0,1,2) or region (scaletype 3)
1465  newRectCanvas = self.pdcObj.GetIdBounds(mapId)
1466  newRectPaper = self.CanvasPaperCoordinates(rect = newRectCanvas, canvasToPaper = True)
1467  self.instruction[mapId]['rect'] = newRectPaper
1468 
1469  if self.instruction[mapId]['scaleType'] in (0, 1, 2):
1470  if self.instruction[mapId]['scaleType'] == 0:
1471 
1472  scale, foo, rect = AutoAdjust(self, scaleType = 0,
1473  map = self.instruction[mapId]['map'],
1474  mapType = self.instruction[mapId]['mapType'],
1475  rect = self.instruction[mapId]['rect'])
1476 
1477  elif self.instruction[mapId]['scaleType'] == 1:
1478  scale, foo, rect = AutoAdjust(self, scaleType = 1,
1479  region = self.instruction[mapId]['region'],
1480  rect = self.instruction[mapId]['rect'])
1481  else:
1482  scale, foo, rect = AutoAdjust(self, scaleType = 2,
1483  rect = self.instruction[mapId]['rect'])
1484  self.instruction[mapId]['rect'] = rect
1485  self.instruction[mapId]['scale'] = scale
1486 
1487  rectCanvas = self.CanvasPaperCoordinates(rect = rect, canvasToPaper = False)
1488  self.Draw(pen = self.pen['map'], brush = self.brush['map'],
1489  pdc = self.pdcObj, drawid = mapId, pdctype = 'rectText', bb = rectCanvas)
1490 
1491  elif self.instruction[mapId]['scaleType'] == 3:
1492  ComputeSetRegion(self, mapDict = self.instruction[mapId].GetInstruction())
1493  #check resolution
1494  SetResolution(dpi = self.instruction[mapId]['resolution'],
1495  width = self.instruction[mapId]['rect'].width,
1496  height = self.instruction[mapId]['rect'].height)
1497 
1498  self.RedrawSelectBox(mapId)
1499  self.Zoom(zoomFactor = 1, view = (0, 0))
1500 
1501  elif self.instruction[self.dragId].type == 'line':
1502  points = self.instruction[self.dragId]['where']
1503  self.instruction[self.dragId]['rect'] = Rect2DPP(points[0], points[1])
1504  self.RecalculatePosition(ids = [self.dragId])
1505 
1506  elif self.instruction[self.dragId].type == 'rectangle':
1507  self.RecalculatePosition(ids = [self.dragId])
1508 
1509  self.mouse['use'] = 'pointer'
1510 
1511  # recalculate the position of objects after dragging
1512  if self.mouse['use'] in ('pointer', 'resize') and self.dragId != -1:
1513  if self.mouse['begin'] != event.GetPosition(): #for double click
1514 
1515  self.RecalculatePosition(ids = [self.dragId])
1516  if self.instruction[self.dragId].type in self.openDialogs:
1517  self.openDialogs[self.instruction[self.dragId].type].updateDialog()
1518 
1519  elif self.mouse['use'] in ('addPoint', 'addLine', 'addRectangle'):
1520  endCoordinates = self.CanvasPaperCoordinates(rect = wx.Rect(event.GetX(), event.GetY(), 0, 0),
1521  canvasToPaper = True)[:2]
1522 
1523  diffX = event.GetX() - self.mouse['begin'][0]
1524  diffY = event.GetY() - self.mouse['begin'][1]
1525 
1526  if self.mouse['use'] == 'addPoint':
1527  self.parent.AddPoint(coordinates = endCoordinates)
1528  elif self.mouse['use'] in ('addLine', 'addRectangle'):
1529  # not too small lines/rectangles
1530  if sqrt(diffX * diffX + diffY * diffY) < 5:
1531  self.pdcTmp.RemoveId(self.idZoomBoxTmp)
1532  self.Refresh()
1533  return
1534 
1535  beginCoordinates = self.CanvasPaperCoordinates(rect = wx.Rect(self.mouse['begin'][0],
1536  self.mouse['begin'][1], 0, 0),
1537  canvasToPaper = True)[:2]
1538  if self.mouse['use'] == 'addLine':
1539  self.parent.AddLine(coordinates = [beginCoordinates, endCoordinates])
1540  else:
1541  self.parent.AddRectangle(coordinates = [beginCoordinates, endCoordinates])
1542  self.pdcTmp.RemoveId(self.idZoomBoxTmp)
1543  self.Refresh()
1544 
1545  def OnButtonDClick(self, event):
1546  """!Open object dialog for editing."""
1547  if self.mouse['use'] == 'pointer' and self.dragId != -1:
1548  itemCall = {'text':self.parent.OnAddText,
1549  'mapinfo': self.parent.OnAddMapinfo,
1550  'scalebar': self.parent.OnAddScalebar,
1551  'image': self.parent.OnAddImage,
1552  'northArrow' : self.parent.OnAddNorthArrow,
1553  'point': self.parent.AddPoint,
1554  'line': self.parent.AddLine,
1555  'rectangle': self.parent.AddRectangle,
1556  'rasterLegend': self.parent.OnAddLegend,
1557  'vectorLegend': self.parent.OnAddLegend,
1558  'map': self.parent.OnAddMap}
1559 
1560  itemArg = { 'text': dict(event = None, id = self.dragId),
1561  'mapinfo': dict(event = None),
1562  'scalebar': dict(event = None),
1563  'image': dict(event = None, id = self.dragId),
1564  'northArrow': dict(event = None, id = self.dragId),
1565  'point': dict(id = self.dragId),
1566  'line': dict(id = self.dragId),
1567  'rectangle': dict(id = self.dragId),
1568  'rasterLegend': dict(event = None),
1569  'vectorLegend': dict(event = None, page = 1),
1570  'map': dict(event = None, notebook = True)}
1571 
1572  type = self.instruction[self.dragId].type
1573  itemCall[type](**itemArg[type])
1574 
1575  def OnDragging(self, event):
1576  """!Process panning/resizing/drawing/moving."""
1577  if event.MiddleIsDown():
1578  # panning
1579  self.mouse['end'] = event.GetPosition()
1580  self.Pan(begin = self.mouse['begin'], end = self.mouse['end'])
1581  self.mouse['begin'] = event.GetPosition()
1582 
1583  elif event.LeftIsDown():
1584  # draw box when zooming, creating map
1585  if self.mouse['use'] in ('zoomin', 'zoomout', 'addMap', 'addLine', 'addRectangle'):
1586  self.mouse['end'] = event.GetPosition()
1587  r = wx.Rect(self.mouse['begin'][0], self.mouse['begin'][1],
1588  self.mouse['end'][0]-self.mouse['begin'][0], self.mouse['end'][1]-self.mouse['begin'][1])
1589  r = self.modifyRectangle(r)
1590 
1591  if self.mouse['use'] in ('addLine', 'addRectangle'):
1592  if self.mouse['use'] == 'addLine':
1593  pdcType = 'line'
1594  lineCoords = (self.mouse['begin'], self.mouse['end'])
1595  else:
1596  pdcType = 'rect'
1597  lineCoords = None
1598  if r[2] < 2 or r[3] < 2:
1599  # to avoid strange behavoiur
1600  return
1601 
1602  self.Draw(pen = self.pen['line'], brush = self.brush['line'],
1603  pdc = self.pdcTmp, drawid = self.idZoomBoxTmp,
1604  pdctype = pdcType, bb = r, lineCoords = lineCoords)
1605 
1606  else:
1607  self.Draw(pen = self.pen['box'], brush = self.brush['box'],
1608  pdc = self.pdcTmp, drawid = self.idZoomBoxTmp,
1609  pdctype = 'rect', bb = r)
1610 
1611  # panning
1612  if self.mouse["use"] == 'pan':
1613  self.mouse['end'] = event.GetPosition()
1614  self.Pan(begin = self.mouse['begin'], end = self.mouse['end'])
1615  self.mouse['begin'] = event.GetPosition()
1616 
1617  # move object
1618  if self.mouse['use'] == 'pointer' and self.dragId != -1:
1619  self.mouse['end'] = event.GetPosition()
1620  dx, dy = self.mouse['end'][0] - self.begin[0], self.mouse['end'][1] - self.begin[1]
1621  self.pdcObj.TranslateId(self.dragId, dx, dy)
1622  self.pdcTmp.TranslateId(self.idBoxTmp, dx, dy)
1623  self.pdcTmp.TranslateId(self.idResizeBoxTmp, dx, dy)
1624  for id in self.idLinePointsTmp:
1625  self.pdcTmp.TranslateId(id, dx, dy)
1626  if self.instruction[self.dragId].type == 'text':
1627  self.instruction[self.dragId]['coords'] = self.instruction[self.dragId]['coords'][0] + dx,\
1628  self.instruction[self.dragId]['coords'][1] + dy
1629  self.begin = event.GetPosition()
1630  self.Refresh()
1631 
1632  # resize object
1633  if self.mouse['use'] == 'resize':
1634  pos = event.GetPosition()
1635  diffX = pos[0] - self.mouse['begin'][0]
1636  diffY = pos[1] - self.mouse['begin'][1]
1637  if self.instruction[self.dragId].type == 'map':
1638  x, y = self.mapBounds.GetX(), self.mapBounds.GetY()
1639  width, height = self.mapBounds.GetWidth(), self.mapBounds.GetHeight()
1640  # match given region
1641  if self.constraint:
1642  if width > height:
1643  newWidth = width + diffX
1644  newHeight = height + diffX * (float(height) / width)
1645  else:
1646  newWidth = width + diffY * (float(width) / height)
1647  newHeight = height + diffY
1648  else:
1649  newWidth = width + diffX
1650  newHeight = height + diffY
1651 
1652  if newWidth < 10 or newHeight < 10:
1653  return
1654 
1655  bounds = wx.Rect(x, y, newWidth, newHeight)
1656  self.Draw(pen = self.pen['map'], brush = self.brush['map'], pdc = self.pdcObj, drawid = self.dragId,
1657  pdctype = 'rectText', bb = bounds)
1658 
1659  elif self.instruction[self.dragId].type == 'rectangle':
1660  instr = self.instruction[self.dragId]
1661  rect = self.CanvasPaperCoordinates(rect = instr['rect'], canvasToPaper = False)
1662  rect.SetWidth(rect.GetWidth() + diffX)
1663  rect.SetHeight(rect.GetHeight() + diffY)
1664 
1665  if rect.GetWidth() < 5 or rect.GetHeight() < 5:
1666  return
1667 
1668  self.DrawGraphics(drawid = self.dragId, shape = 'rectangle', color = instr['color'],
1669  fcolor = instr['fcolor'], width = instr['width'], bb = rect)
1670 
1671  elif self.instruction[self.dragId].type == 'line':
1672  instr = self.instruction[self.dragId]
1673  points = instr['where']
1674  # moving point
1675  if self.currentLinePoint == 0:
1676  pPaper = points[1]
1677  else:
1678  pPaper = points[0]
1679  pCanvas = self.CanvasPaperCoordinates(rect = Rect2DPS(pPaper, (0, 0)),
1680  canvasToPaper = False)[:2]
1681  bounds = wx.RectPP(pCanvas, pos)
1682  self.DrawGraphics(drawid = self.dragId, shape = 'line', color = instr['color'],
1683  width = instr['width'], bb = bounds, lineCoords = (pos, pCanvas))
1684 
1685  # update paper coordinates
1686  points[self.currentLinePoint] = self.CanvasPaperCoordinates(rect = wx.RectPS(pos, (0, 0)),
1687  canvasToPaper = True)[:2]
1688 
1689  self.RedrawSelectBox(self.dragId)
1690 
1691  def OnMiddleDown(self, event):
1692  """!Middle mouse button pressed."""
1693  self.mouse['begin'] = event.GetPosition()
1694 
1695  def Pan(self, begin, end):
1696  """!Move canvas while dragging.
1697 
1698  @param begin x,y coordinates of first point
1699  @param end x,y coordinates of second point
1700  """
1701  view = begin[0] - end[0], begin[1] - end[1]
1702  zoomFactor = 1
1703  self.Zoom(zoomFactor, view)
1704 
1705  def RecalculatePosition(self, ids):
1706  for id in ids:
1707  itype = self.instruction[id].type
1708  if itype in ('map', 'rectangle'):
1709  self.instruction[id]['rect'] = self.CanvasPaperCoordinates(rect = self.pdcObj.GetIdBounds(id),
1710  canvasToPaper = True)
1711  self.RecalculateEN()
1712 
1713  elif itype in ('mapinfo' ,'rasterLegend', 'vectorLegend', 'image', 'northArrow'):
1714  self.instruction[id]['rect'] = self.CanvasPaperCoordinates(rect = self.pdcObj.GetIdBounds(id),
1715  canvasToPaper = True)
1716  self.instruction[id]['where'] = self.CanvasPaperCoordinates(rect = self.pdcObj.GetIdBounds(id),
1717  canvasToPaper = True)[:2]
1718  if itype in ('image', 'northArrow'):
1719  self.RecalculateEN()
1720 
1721  elif itype == 'point':
1722  rect = self.pdcObj.GetIdBounds(id)
1723  self.instruction[id]['rect'] = self.CanvasPaperCoordinates(rect = rect,
1724  canvasToPaper = True)
1725  rect.OffsetXY(rect.GetWidth()/2, rect.GetHeight()/2)
1726  self.instruction[id]['where'] = self.CanvasPaperCoordinates(rect = rect,
1727  canvasToPaper = True)[:2]
1728  self.RecalculateEN()
1729 
1730  elif itype == 'line':
1731  rect = self.pdcObj.GetIdBounds(id)
1732  oldRect = self.instruction[id]['rect']
1733  newRect = self.CanvasPaperCoordinates(rect = rect, canvasToPaper = True)
1734  xDiff = newRect[0] - oldRect[0]
1735  yDiff = newRect[1] - oldRect[1]
1736  self.instruction[id]['rect'] = newRect
1737 
1738  point1 = wx.Point2D(xDiff, yDiff) + self.instruction[id]['where'][0]
1739  point2 = wx.Point2D(xDiff, yDiff) + self.instruction[id]['where'][1]
1740  self.instruction[id]['where'] = [point1, point2]
1741 
1742  self.RecalculateEN()
1743 
1744  elif itype == 'scalebar':
1745  self.instruction[id]['rect'] = self.CanvasPaperCoordinates(rect = self.pdcObj.GetIdBounds(id),
1746  canvasToPaper = True)
1747 
1748 
1749  self.instruction[id]['where'] = self.instruction[id]['rect'].GetCentre()
1750 
1751  elif itype == 'text':
1752  x, y = self.instruction[id]['coords'][0] - self.instruction[id]['xoffset'],\
1753  self.instruction[id]['coords'][1] + self.instruction[id]['yoffset']
1754  extent = self.parent.getTextExtent(textDict = self.instruction[id])
1755  if self.instruction[id]['rotate'] is not None:
1756  rot = float(self.instruction[id]['rotate'])/180*pi
1757  else:
1758  rot = 0
1759 
1760  if self.instruction[id]['ref'].split()[0] == 'lower':
1761  y += extent[1]
1762  elif self.instruction[id]['ref'].split()[0] == 'center':
1763  y += extent[1]/2
1764  if self.instruction[id]['ref'].split()[1] == 'right':
1765  x += extent[0] * cos(rot)
1766  y -= extent[0] * sin(rot)
1767  elif self.instruction[id]['ref'].split()[1] == 'center':
1768  x += extent[0]/2 * cos(rot)
1769  y -= extent[0]/2 * sin(rot)
1770 
1771  self.instruction[id]['where'] = self.CanvasPaperCoordinates(rect = Rect2D(x, y, 0, 0),
1772  canvasToPaper = True)[:2]
1773  self.RecalculateEN()
1774 
1775  def ComputeZoom(self, rect):
1776  """!Computes zoom factor and scroll view"""
1777  zoomFactor = 1
1778  cW, cH = self.GetClientSize()
1779  cW = float(cW)
1780  if rect.IsEmpty(): # clicked on canvas
1781  zoomFactor = 1.5
1782  if self.mouse['use'] == 'zoomout':
1783  zoomFactor = 1./zoomFactor
1784  x,y = self.mouse['begin']
1785  xView = x - x/zoomFactor#x - cW/(zoomFactor * 2)
1786  yView = y - y/zoomFactor#y - cH/(zoomFactor * 2)
1787 
1788  else: #dragging
1789  rW, rH = float(rect.GetWidth()), float(rect.GetHeight())
1790  try:
1791  zoomFactor = 1/max(rW/cW, rH/cH)
1792  except ZeroDivisionError:
1793  zoomFactor = 1
1794  # when zooming to full extent, in some cases, there was zoom 1.01..., which causes problem
1795  if abs(zoomFactor - 1) > 0.01:
1796  zoomFactor = zoomFactor
1797  else:
1798  zoomFactor = 1.
1799 
1800 
1801  if self.mouse['use'] == 'zoomout':
1802  zoomFactor = min(rW/cW, rH/cH)
1803  try:
1804  if rW/rH > cW/cH:
1805  yView = rect.GetY() - (rW*(cH/cW) - rH)/2
1806  xView = rect.GetX()
1807 
1808  if self.mouse['use'] == 'zoomout':
1809  x,y = rect.GetX() + (rW-(cW/cH)*rH)/2, rect.GetY()
1810  xView, yView = -x, -y
1811  else:
1812  xView = rect.GetX() - (rH*(cW/cH) - rW)/2
1813  yView = rect.GetY()
1814  if self.mouse['use'] == 'zoomout':
1815  x,y = rect.GetX(), rect.GetY() + (rH-(cH/cW)*rW)/2
1816  xView, yView = -x, -y
1817  except ZeroDivisionError:
1818  xView, yView = rect.GetX(), rect.GetY()
1819  return zoomFactor, (int(xView), int(yView))
1820 
1821 
1822  def Zoom(self, zoomFactor, view):
1823  """! Zoom to specified region, scroll view, redraw"""
1824  if not self.currScale:
1825  return
1826  self.currScale = self.currScale*zoomFactor
1827 
1828  if self.currScale > 10 or self.currScale < 0.1:
1829  self.currScale = self.currScale/zoomFactor
1830  return
1831  if not self.preview:
1832  # redraw paper
1833  pRect = self.pdcPaper.GetIdBounds(self.pageId)
1834  pRect.OffsetXY(-view[0], -view[1])
1835  pRect = self.ScaleRect(rect = pRect, scale = zoomFactor)
1836  self.DrawPaper(pRect)
1837 
1838  #redraw objects
1839  for id in self.objectId:
1840  oRect = self.CanvasPaperCoordinates(
1841  rect = self.instruction[id]['rect'], canvasToPaper = False)
1842 
1843  type = self.instruction[id].type
1844  if type == 'text':
1845  coords = self.instruction[id]['coords']# recalculate coordinates, they are not equal to BB
1846  self.instruction[id]['coords'] = coords = [(int(coord) - view[i]) * zoomFactor
1847  for i, coord in enumerate(coords)]
1848  extent = self.parent.getTextExtent(textDict = self.instruction[id])
1849  if self.instruction[id]['rotate']:
1850  rot = float(self.instruction[id]['rotate'])
1851  else:
1852  rot = 0
1853  self.instruction[id]['rect'] = bounds = self.parent.getModifiedTextBounds(coords[0], coords[1], extent, rot)
1854  self.DrawRotText(pdc = self.pdcObj, drawId = id, textDict = self.instruction[id],
1855  coords = coords, bounds = bounds )
1856 
1857  self.pdcObj.SetIdBounds(id, bounds)
1858 
1859  elif type == 'northArrow':
1860  self.Draw(pen = self.pen[type], brush = self.brush[type], pdc = self.pdcObj,
1861  drawid = id, pdctype = 'bitmap', bb = oRect)
1862 
1863  elif type in ('point', 'line', 'rectangle'):
1864  instr = self.instruction[id]
1865  color = self.instruction[id]['color']
1866  width = fcolor = coords = None
1867 
1868  if type in ('point', 'rectangle'):
1869  fcolor = self.instruction[id]['fcolor']
1870  if type in ('line', 'rectangle'):
1871  width = self.instruction[id]['width']
1872  if type in ('line'):
1873  point1, point2 = instr['where'][0], instr['where'][1]
1874  point1 = self.CanvasPaperCoordinates(rect = Rect2DPS(point1, (0, 0)),
1875  canvasToPaper = False)[:2]
1876  point2 = self.CanvasPaperCoordinates(rect = Rect2DPS(point2, (0, 0)),
1877  canvasToPaper = False)[:2]
1878  coords = (point1, point2)
1879 
1880  self.DrawGraphics(drawid = id, shape = type, bb = oRect, lineCoords = coords,
1881  color = color, fcolor = fcolor, width = width)
1882 
1883  else:
1884  self.Draw(pen = self.pen[type], brush = self.brush[type], pdc = self.pdcObj,
1885  drawid = id, pdctype = 'rectText', bb = oRect)
1886  #redraw tmp objects
1887  if self.dragId != -1:
1888  self.RedrawSelectBox(self.dragId)
1889 
1890  #redraw preview
1891  else: # preview mode
1892  imageRect = self.pdcImage.GetIdBounds(self.imageId)
1893  imageRect.OffsetXY(-view[0], -view[1])
1894  imageRect = self.ScaleRect(rect = imageRect, scale = zoomFactor)
1895  self.DrawImage(imageRect)
1896 
1897  def ZoomAll(self):
1898  """! Zoom to full extent"""
1899  if not self.preview:
1900  bounds = self.pdcPaper.GetIdBounds(self.pageId)
1901  else:
1902  bounds = self.pdcImage.GetIdBounds(self.imageId)
1903  zoomP = bounds.Inflate(bounds.width/20, bounds.height/20)
1904  zoomFactor, view = self.ComputeZoom(zoomP)
1905  self.Zoom(zoomFactor, view)
1906 
1907  def Draw(self, pen, brush, pdc, drawid = None, pdctype = 'rect', bb = wx.Rect(0,0,0,0), lineCoords = None):
1908  """! Draw object with given pen and brush.
1909 
1910  @param pdc PseudoDC
1911  @param pdctype 'bitmap'/'rectText'/'rect'/'point'/'line'
1912  @param bb bounding box
1913  @param lineCoords coordinates of line start, end points (wx.Point, wx.Point)
1914  """
1915  if drawid is None:
1916  drawid = wx.NewId()
1917  bb = bb.Get()
1918  pdc.BeginDrawing()
1919  pdc.RemoveId(drawid)
1920  pdc.SetId(drawid)
1921  pdc.SetPen(pen)
1922  pdc.SetBrush(brush)
1923 
1924  if pdctype == 'bitmap':
1925  if havePILImage:
1926  file = self.instruction[drawid]['epsfile']
1927  rotation = self.instruction[drawid]['rotate']
1928  self.DrawBitmap(pdc = pdc, filePath = file, rotation = rotation, bbox = bb)
1929  else: # draw only rectangle with label
1930  pdctype = 'rectText'
1931 
1932  if pdctype in ('rect', 'rectText'):
1933  pdc.DrawRectangle(*bb)
1934 
1935  if pdctype == 'rectText':
1936  dc = wx.ClientDC(self) # dc created because of method GetTextExtent, which pseudoDC lacks
1937  font = dc.GetFont()
1938  size = 10
1939  font.SetPointSize(size)
1940  font.SetStyle(wx.ITALIC)
1941  dc.SetFont(font)
1942  pdc.SetFont(font)
1943  text = '\n'.join(self.itemLabels[drawid])
1944  w,h,lh = dc.GetMultiLineTextExtent(text)
1945  textExtent = (w,h)
1946  textRect = wx.Rect(0, 0, *textExtent).CenterIn(bb)
1947  r = map(int, bb)
1948  while not wx.Rect(*r).ContainsRect(textRect) and size >= 8:
1949  size -= 2
1950  font.SetPointSize(size)
1951  dc.SetFont(font)
1952  pdc.SetFont(font)
1953  textExtent = dc.GetTextExtent(text)
1954  textRect = wx.Rect(0, 0, *textExtent).CenterIn(bb)
1955  pdc.SetTextForeground(wx.Colour(100,100,100,200))
1956  pdc.SetBackgroundMode(wx.TRANSPARENT)
1957  pdc.DrawLabel(text = text, rect = textRect)
1958 
1959  elif pdctype == 'point':
1960  pdc.DrawCircle(x = bb[0] + bb[2] / 2,
1961  y = bb[1] + bb[3] / 2,
1962  radius = bb[2] / 2)
1963 
1964  elif pdctype == 'line':
1965  pdc.DrawLinePoint(lineCoords[0], lineCoords[1])
1966 
1967  pdc.SetIdBounds(drawid, bb)
1968  pdc.EndDrawing()
1969  self.Refresh()
1970 
1971  return drawid
1972 
1973  def DrawGraphics(self, drawid, shape, color, bb, width = None, fcolor = None, lineCoords = None):
1974  """!Draw point/line/rectangle with given color and width
1975 
1976  @param drawid id of drawn object
1977  @param shape drawn shape: 'point'/'line'/'rectangle'
1978  @param color pen outline color ('RRR:GGG:BBB')
1979  @param fcolor brush fill color, if meaningful ('RRR:GGG:BBB')
1980  @param width pen width
1981  @param bb bounding box
1982  @param lineCoords line coordinates (for line only)
1983  """
1984  pdctype = {'point' : 'point',
1985  'line' : 'line',
1986  'rectangle' : 'rect'}
1987 
1988  if color == 'none':
1989  pen = wx.TRANSPARENT_PEN
1990  else:
1991  if width is not None:
1992  units = UnitConversion(self)
1993  width = int(units.convert(value = width, fromUnit = 'point', toUnit = 'pixel') * self.currScale)
1994  else:
1995  width = 2
1996  pen = wx.Pen(colour = convertRGB(color), width = width)
1997  pen.SetCap(wx.CAP_BUTT) # this is how ps.map draws
1998 
1999  brush = wx.TRANSPARENT_BRUSH
2000  if fcolor and fcolor != 'none':
2001  brush = wx.Brush(colour = convertRGB(fcolor))
2002 
2003  self.Draw(pen = pen, brush = brush, pdc = self.pdcObj, pdctype = pdctype[shape],
2004  drawid = drawid, bb = bb, lineCoords = lineCoords)
2005 
2006  def DrawBitmap(self, pdc, filePath, rotation, bbox):
2007  """!Draw bitmap using PIL"""
2008  pImg = PILImage.open(filePath)
2009  if sys.platform == 'win32' and \
2010  'eps' in os.path.splitext(filePath)[1].lower():
2011  import types
2012  pImg.load = types.MethodType(loadPSForWindows, pImg)
2013 
2014  if rotation:
2015  # get rid of black background
2016  pImg = pImg.convert("RGBA")
2017  rot = pImg.rotate(rotation, expand = 1)
2018  new = PILImage.new('RGBA', rot.size, (255,) * 4)
2019  pImg = PILImage.composite(rot, new, rot)
2020  pImg = pImg.resize((int(bbox[2]), int(bbox[3])), resample = PILImage.BICUBIC)
2021  img = PilImageToWxImage(pImg)
2022  bitmap = img.ConvertToBitmap()
2023  mask = wx.Mask(bitmap, wx.WHITE)
2024  bitmap.SetMask(mask)
2025  pdc.DrawBitmap(bitmap, bbox[0], bbox[1], useMask = True)
2026 
2027  def DrawRotText(self, pdc, drawId, textDict, coords, bounds):
2028  if textDict['rotate']:
2029  rot = float(textDict['rotate'])
2030  else:
2031  rot = 0
2032 
2033  if textDict['background'] != 'none':
2034  background = textDict['background']
2035  else:
2036  background = None
2037 
2038  pdc.RemoveId(drawId)
2039  pdc.SetId(drawId)
2040  pdc.BeginDrawing()
2041 
2042  # border is not redrawn when zoom changes, why?
2043 ## if textDict['border'] != 'none' and not rot:
2044 ## units = UnitConversion(self)
2045 ## borderWidth = units.convert(value = textDict['width'],
2046 ## fromUnit = 'point', toUnit = 'pixel' ) * self.currScale
2047 ## pdc.SetPen(wx.Pen(colour = convertRGB(textDict['border']), width = borderWidth))
2048 ## pdc.DrawRectangle(*bounds)
2049 
2050  if background:
2051  pdc.SetTextBackground(convertRGB(background))
2052  pdc.SetBackgroundMode(wx.SOLID)
2053  else:
2054  pdc.SetBackgroundMode(wx.TRANSPARENT)
2055 
2056  fn = self.parent.makePSFont(textDict)
2057 
2058  pdc.SetFont(fn)
2059  pdc.SetTextForeground(convertRGB(textDict['color']))
2060  if rot == 0:
2061  pdc.DrawLabel(text=textDict['text'], rect=bounds)
2062  else:
2063  pdc.DrawRotatedText(textDict['text'], coords[0], coords[1], rot)
2064 
2065  pdc.SetIdBounds(drawId, wx.Rect(*bounds))
2066  self.Refresh()
2067  pdc.EndDrawing()
2068 
2069  def DrawImage(self, rect):
2070  """!Draw preview image to pseudoDC"""
2071  self.pdcImage.ClearId(self.imageId)
2072  self.pdcImage.SetId(self.imageId)
2073  img = self.image
2074 
2075 
2076  if img.GetWidth() != rect.width or img.GetHeight() != rect.height:
2077  img = img.Scale(rect.width, rect.height)
2078  bitmap = img.ConvertToBitmap()
2079 
2080  self.pdcImage.BeginDrawing()
2081  self.pdcImage.DrawBitmap(bitmap, rect.x, rect.y)
2082  self.pdcImage.SetIdBounds(self.imageId, rect)
2083  self.pdcImage.EndDrawing()
2084  self.Refresh()
2085 
2086  def DrawPaper(self, rect):
2087  """!Draw paper and margins"""
2088  page = self.instruction[self.pageId]
2089  scale = page['Width'] / rect.GetWidth()
2090  w = (page['Width'] - page['Right'] - page['Left']) / scale
2091  h = (page['Height'] - page['Top'] - page['Bottom']) / scale
2092  x = page['Left'] / scale + rect.GetX()
2093  y = page['Top'] / scale + rect.GetY()
2094 
2095  self.pdcPaper.BeginDrawing()
2096  self.pdcPaper.RemoveId(self.pageId)
2097  self.pdcPaper.SetId(self.pageId)
2098  self.pdcPaper.SetPen(self.pen['paper'])
2099  self.pdcPaper.SetBrush(self.brush['paper'])
2100  self.pdcPaper.DrawRectangleRect(rect)
2101 
2102  self.pdcPaper.SetPen(self.pen['margins'])
2103  self.pdcPaper.SetBrush(self.brush['margins'])
2104  self.pdcPaper.DrawRectangle(x, y, w, h)
2105 
2106  self.pdcPaper.SetIdBounds(self.pageId, rect)
2107  self.pdcPaper.EndDrawing()
2108  self.Refresh()
2109 
2110 
2111  def ImageRect(self):
2112  """!Returns image centered in canvas, computes scale"""
2113  img = wx.Image(self.imgName, wx.BITMAP_TYPE_PNG)
2114  cW, cH = self.GetClientSize()
2115  iW, iH = img.GetWidth(), img.GetHeight()
2116 
2117  self.currScale = min(float(cW)/iW, float(cH)/iH)
2118  iW = iW * self.currScale
2119  iH = iH * self.currScale
2120  x = cW/2 - iW/2
2121  y = cH/2 - iH/2
2122  imageRect = wx.Rect(x, y, iW, iH)
2123 
2124  return imageRect
2125 
2126  def RedrawSelectBox(self, id):
2127  """!Redraws select box when selected object changes its size"""
2128  if self.dragId == id:
2129  rect = self.pdcObj.GetIdBounds(id)
2130  if self.instruction[id].type != 'line':
2131  rect = rect.Inflate(3,3)
2132  # draw select box around object
2133  self.Draw(pen = self.pen['select'], brush = self.brush['select'], pdc = self.pdcTmp,
2134  drawid = self.idBoxTmp, pdctype = 'rect', bb = rect)
2135 
2136  # draw small marks signalizing resizing
2137  if self.instruction[id].type in ('map', 'rectangle'):
2138  controlP = self.pdcObj.GetIdBounds(id).GetBottomRight()
2139  rect = wx.RectPS(controlP, self.resizeBoxSize)
2140  self.Draw(pen = self.pen['resize'], brush = self.brush['resize'], pdc = self.pdcTmp,
2141  drawid = self.idResizeBoxTmp, pdctype = 'rect', bb = rect)
2142 
2143  elif self.instruction[id].type == 'line':
2144  p1Paper = self.instruction[id]['where'][0]
2145  p2Paper = self.instruction[id]['where'][1]
2146  p1Canvas = self.CanvasPaperCoordinates(rect = Rect2DPS(p1Paper, (0, 0)), canvasToPaper = False)[:2]
2147  p2Canvas = self.CanvasPaperCoordinates(rect = Rect2DPS(p2Paper, (0, 0)), canvasToPaper = False)[:2]
2148  rect = []
2149  box = wx.RectS(self.resizeBoxSize)
2150  rect.append(box.CenterIn(wx.RectPS(p1Canvas, wx.Size())))
2151  rect.append(box.CenterIn(wx.RectPS(p2Canvas, wx.Size())))
2152  for i, point in enumerate((p1Canvas, p2Canvas)):
2153  self.Draw(pen = self.pen['resize'], brush = self.brush['resize'], pdc = self.pdcTmp,
2154  drawid = self.idLinePointsTmp[i], pdctype = 'rect', bb = rect[i])
2155 
2156  def UpdateMapLabel(self):
2157  """!Updates map frame label"""
2158 
2159  vector = self.instruction.FindInstructionByType('vector')
2160  if vector:
2161  vectorId = vector.id
2162  else:
2163  vectorId = None
2164 
2165  raster = self.instruction.FindInstructionByType('raster')
2166  if raster:
2167  rasterId = raster.id
2168  else:
2169  rasterId = None
2170 
2171  rasterName = 'None'
2172  if rasterId:
2173  rasterName = self.instruction[rasterId]['raster'].split('@')[0]
2174 
2175  mapId = self.instruction.FindInstructionByType('map').id
2176  self.itemLabels[mapId] = []
2177  self.itemLabels[mapId].append(self.itemLabelsDict['map'])
2178  self.itemLabels[mapId].append("raster: " + rasterName)
2179  if vectorId:
2180  for map in self.instruction[vectorId]['list']:
2181  self.itemLabels[mapId].append('vector: ' + map[0].split('@')[0])
2182 
2183  def UpdateLabel(self, itype, id):
2184  self.itemLabels[id] = []
2185  self.itemLabels[id].append(self.itemLabelsDict[itype])
2186  if itype == 'image':
2187  file = os.path.basename(self.instruction[id]['epsfile'])
2188  self.itemLabels[id].append(file)
2189 
2190  def OnSize(self, event):
2191  """!Init image size to match window size
2192  """
2193  # not zoom all when notebook page is changed
2194  if self.preview and self.parent.currentPage == 1 or not self.preview and self.parent.currentPage == 0:
2195  self.ZoomAll()
2196  self.OnIdle(None)
2197  event.Skip()
2198 
2199  def OnIdle(self, event):
2200  """!Only re-render a image during idle time instead of
2201  multiple times during resizing.
2202  """
2203 
2204  width, height = self.GetClientSize()
2205  # Make new off screen bitmap: this bitmap will always have the
2206  # current drawing in it, so it can be used to save the image
2207  # to a file, or whatever.
2208  self._buffer = wx.EmptyBitmap(width, height)
2209  # re-render image on idle
2210  self.resize = True
2211 
2212  def ScaleRect(self, rect, scale):
2213  """! Scale rectangle"""
2214  return wx.Rect(rect.GetLeft()*scale, rect.GetTop()*scale,
2215  rect.GetSize()[0]*scale, rect.GetSize()[1]*scale)
2216 
2217 
2218 def main():
2219  import gettext
2220  gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
2221 
2222  app = wx.PySimpleApp()
2223  wx.InitAllImageHandlers()
2224  frame = PsMapFrame()
2225  frame.Show()
2226 
2227  app.MainLoop()
2228 
2229 if __name__ == "__main__":
2230  main()
def convertRGB
Converts wx.Colour(r,g,b,a) to string &#39;r:g:b&#39; or named color, or named color/r:g:b string to wx...
Definition: psmap/utils.py:136
def OnLeftDown
Left mouse button pressed.
def OnSize
Init image size to match window size.
def OnLeftUp
Left mouse button released.
def modifyRectangle
Recalculates rectangle not to have negative size.
def OnAddText
Show dialog for text adding and editing.
Definition: psmap/frame.py:680
def projInfo
Return region projection and map units information, taken from render.py.
Definition: psmap/utils.py:309
def DrawPaper
Draw paper and margins.
wxGUI command interface
def OnPSFile
Generate PostScript.
Definition: psmap/frame.py:212
def MouseActions
Mouse motion and button click notifier.
def OnPageChanging
Flatnotebook page is changing.
def PaperMapCoordinates
Converts paper (inch) coordinates &lt;-&gt; map coordinates.
Definition: psmap/utils.py:159
def OnCloseWindow
Close window.
#define min(x, y)
Definition: draw2.c:68
def __init__
Main window of ps.map GUI.
Definition: psmap/frame.py:51
def InstructionFile
Creates mapping instructions.
Definition: psmap/frame.py:207
def OnPsMapDialog
Launch ps.map dialog.
Definition: psmap/frame.py:218
def SetResolution
If resolution is too high, lower it.
Definition: psmap/utils.py:259
def UpdateMapLabel
Updates map frame label.
def ImageRect
Returns image centered in canvas, computes scale.
def OnOK
Apply changes, close dialog.
def OnAbout
Display About window.
def OnPaint
Draw pseudo DC to buffer.
def RecalculateEN
Recalculate east and north for texts (eps, points) after their or map&#39;s movement. ...
#define max(x, y)
Definition: draw2.c:69
def ScaleRect
Scale rectangle.
def Clear
Clear canvas and set paper.
def AutoAdjust
Computes map scale, center and map frame rectangle to fit region (scale is not fixed) ...
Definition: psmap/utils.py:196
def getInitMap
Create default map frame when no map is selected, needed for coordinates in map units.
Definition: psmap/frame.py:844
def Draw
Draw object with given pen and brush.
def PSFile
Create temporary instructions file and run ps.map with output = filename.
Definition: psmap/frame.py:243
def OnMouseMoving
Mouse cursor moving.
def AddPoint
Add point and open property dialog.
Definition: psmap/frame.py:698
Various dialogs used in wxGUI.
def OnPreview
Run ps.map and show result.
Definition: psmap/frame.py:239
Menu classes for wxGUI.
def split
Platform spefic shlex.split.
Definition: core/utils.py:37
def OnAddScalebar
Add scalebar.
Definition: psmap/frame.py:616
def OnAddMapinfo
Definition: psmap/frame.py:646
def DrawImage
Draw preview image to pseudoDC.
def OnAddLegend
Add raster or vector legend.
Definition: psmap/frame.py:630
def OnAddNorthArrow
Show dialog for north arrow adding and editing.
Definition: psmap/frame.py:669
def UpdateLabel
def OnCmdDone
ps.map process finished
Definition: psmap/frame.py:285
def RedrawSelectBox
Redraws select box when selected object changes its size.
def ComputeSetRegion
Computes and sets region from current scale, map center coordinates and map rectangle.
Definition: psmap/utils.py:272
def OnDelete
Definition: psmap/frame.py:874
def deleteObject
Deletes object, his id and redraws.
Definition: psmap/frame.py:883
def OnAddRectangle
Add rectangle action selected.
Definition: psmap/frame.py:743
def DrawBitmap
Draw bitmap using PIL.
def OnMouseWheel
Mouse wheel scrolled.
def OnAddLine
Add line action selected.
Definition: psmap/frame.py:718
def ComputeZoom
Computes zoom factor and scroll view.
def AddRectangle
Add rectangle and open property dialog.
Definition: psmap/frame.py:748
utilities for wxpsmap (classes, functions)
A buffered window class.
def updateDialog
Update legend coordinates after moving.
def OnHelp
Show help.
Map feature objects.
def Pan
Move canvas while dragging.
def OnAddRaster
Add raster map.
Definition: psmap/frame.py:569
def OnDragging
Process panning/resizing/drawing/moving.
def PilImageToWxImage
Convert PIL image to wx.Image.
Definition: psmap/utils.py:360
def OnAddImage
Show dialog for image adding and editing.
Definition: psmap/frame.py:656
def SetPage
Sets and changes page, redraws paper.
def OnAddPoint
Add point action selected.
Definition: psmap/frame.py:693
def OnAddVect
Add vector map.
Definition: psmap/frame.py:593
def OnPageChanged
Flatnotebook page has changed.
def makePSFont
creates a wx.Font object from selected postscript font.
Definition: psmap/frame.py:784
def _showErrMsg
Show error message (missing preview)
Definition: psmap/frame.py:176
def _layout
Do layout.
Definition: psmap/frame.py:184
def OnLoadFile
Load file and read instructions.
Definition: psmap/frame.py:414
def GetMapBounds
Run ps.map -b to get information about map bounding box.
Definition: psmap/utils.py:331
def Zoom
Zoom to specified region, scroll view, redraw.
def OnPageSetup
Specify paper size, margins and orientation.
Definition: psmap/frame.py:444
def getTextExtent
Estimates bounding rectangle of text.
Definition: psmap/frame.py:830
wxPsMap toolbars classes
def getModifiedTextBounds
computes bounding box of rotated text, not very precisely
Definition: psmap/frame.py:768
def AddLine
Add line and open property dialog.
Definition: psmap/frame.py:723
def DialogDataChanged
Definition: psmap/frame.py:895
Default GUI settings.
def ZoomAll
Zoom to full extent.
def OnButtonDClick
Open object dialog for editing.
def OnAddMap
Add or edit map frame.
Definition: psmap/frame.py:499
def OnApply
parent.font[&#39;colorLabel&#39;] = wx.StaticText(parent, id = wx.ID_ANY, label = _(&quot;Color:&quot;)) colorChoices...
def OnMiddleDown
Middle mouse button pressed.
def DrawGraphics
Draw point/line/rectangle with given color and width.
def OnPDFFile
Generate PDF from PS with ps2pdf if available.
Definition: psmap/frame.py:223
dialogs for wxPsMap
Command output widgets.
def OnIdle
Only re-render a image during idle time instead of multiple times during resizing.
def RunCommand
Run GRASS command.
Definition: gcmd.py:625
def CanvasPaperCoordinates
Converts canvas (pixel) -&gt; paper (inch) coordinates and size and vice versa.