|
GRASS Programmer's Manual
6.5.svn(2012)-r51648
|
00001 """! 00002 @package psmap.frame 00003 00004 @brief GUI for ps.map 00005 00006 Classes: 00007 - frame::PsMapFrame 00008 - frame::PsMapBufferedWindow 00009 00010 (C) 2011-2012 by Anna Kratochvilova, and the GRASS Development Team 00011 This program is free software under the GNU General Public License 00012 (>=v2). Read the file COPYING that comes with GRASS for details. 00013 00014 @author Anna Kratochvilova <kratochanna gmail.com> (bachelor's project) 00015 @author Martin Landa <landa.martin gmail.com> (mentor) 00016 """ 00017 00018 import os 00019 import sys 00020 import textwrap 00021 import Queue 00022 from math import sin, cos, pi, sqrt 00023 00024 if __name__ == "__main__": 00025 sys.path.append(os.path.join(os.getenv('GISBASE'), 'etc', 'gui', 'wxpython')) 00026 from core import globalvar 00027 import wx 00028 00029 try: 00030 import wx.lib.agw.flatnotebook as fnb 00031 except ImportError: 00032 import wx.lib.flatnotebook as fnb 00033 00034 import grass.script as grass 00035 00036 from gui_core.menu import Menu 00037 from gui_core.goutput import CmdThread, EVT_CMD_DONE 00038 from psmap.toolbars import PsMapToolbar 00039 from core.gcmd import RunCommand, GError, GMessage 00040 from core.settings import UserSettings 00041 from gui_core.forms import GUI 00042 from psmap.menudata import PsMapData 00043 00044 from psmap.dialogs import * 00045 from psmap.instructions import * 00046 from psmap.utils import * 00047 00048 class PsMapFrame(wx.Frame): 00049 def __init__(self, parent = None, id = wx.ID_ANY, 00050 title = _("GRASS GIS Cartographic Composer (experimental prototype)"), **kwargs): 00051 """!Main window of ps.map GUI 00052 00053 @param parent parent window 00054 @param id window id 00055 @param title window title 00056 00057 @param kwargs wx.Frames' arguments 00058 """ 00059 self.parent = parent 00060 00061 wx.Frame.__init__(self, parent = parent, id = id, title = title, name = "PsMap", **kwargs) 00062 self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO)) 00063 #menubar 00064 self.menubar = Menu(parent = self, data = PsMapData()) 00065 self.SetMenuBar(self.menubar) 00066 #toolbar 00067 00068 self.toolbar = PsMapToolbar(parent = self) 00069 self.SetToolBar(self.toolbar) 00070 00071 self.actionOld = self.toolbar.action['id'] 00072 self.iconsize = (16, 16) 00073 #satusbar 00074 self.statusbar = self.CreateStatusBar(number = 1) 00075 00076 # mouse attributes -- position on the screen, begin and end of 00077 # dragging, and type of drawing 00078 self.mouse = { 00079 'begin': [0, 0], # screen coordinates 00080 'end' : [0, 0], 00081 'use' : "pointer", 00082 } 00083 # available cursors 00084 self.cursors = { 00085 "default" : wx.StockCursor(wx.CURSOR_ARROW), 00086 "cross" : wx.StockCursor(wx.CURSOR_CROSS), 00087 "hand" : wx.StockCursor(wx.CURSOR_HAND), 00088 "sizenwse": wx.StockCursor(wx.CURSOR_SIZENWSE) 00089 } 00090 # pen and brush 00091 self.pen = { 00092 'paper': wx.Pen(colour = "BLACK", width = 1), 00093 'margins': wx.Pen(colour = "GREY", width = 1), 00094 'map': wx.Pen(colour = wx.Color(86, 122, 17), width = 2), 00095 'rasterLegend': wx.Pen(colour = wx.Color(219, 216, 4), width = 2), 00096 'vectorLegend': wx.Pen(colour = wx.Color(219, 216, 4), width = 2), 00097 'mapinfo': wx.Pen(colour = wx.Color(5, 184, 249), width = 2), 00098 'scalebar': wx.Pen(colour = wx.Color(150, 150, 150), width = 2), 00099 'image': wx.Pen(colour = wx.Color(255, 150, 50), width = 2), 00100 'northArrow': wx.Pen(colour = wx.Color(200, 200, 200), width = 2), 00101 'point': wx.Pen(colour = wx.Color(100, 100, 100), width = 2), 00102 'line': wx.Pen(colour = wx.Color(0, 0, 0), width = 2), 00103 'box': wx.Pen(colour = 'RED', width = 2, style = wx.SHORT_DASH), 00104 'select': wx.Pen(colour = 'BLACK', width = 1, style = wx.SHORT_DASH), 00105 'resize': wx.Pen(colour = 'BLACK', width = 1) 00106 } 00107 self.brush = { 00108 'paper': wx.WHITE_BRUSH, 00109 'margins': wx.TRANSPARENT_BRUSH, 00110 'map': wx.Brush(wx.Color(151, 214, 90)), 00111 'rasterLegend': wx.Brush(wx.Color(250, 247, 112)), 00112 'vectorLegend': wx.Brush(wx.Color(250, 247, 112)), 00113 'mapinfo': wx.Brush(wx.Color(127, 222, 252)), 00114 'scalebar': wx.Brush(wx.Color(200, 200, 200)), 00115 'image': wx.Brush(wx.Color(255, 200, 50)), 00116 'northArrow': wx.Brush(wx.Color(255, 255, 255)), 00117 'point': wx.Brush(wx.Color(200, 200, 200)), 00118 'line': wx.TRANSPARENT_BRUSH, 00119 'box': wx.TRANSPARENT_BRUSH, 00120 'select':wx.TRANSPARENT_BRUSH, 00121 'resize': wx.BLACK_BRUSH 00122 } 00123 00124 00125 # list of objects to draw 00126 self.objectId = [] 00127 00128 # instructions 00129 self.instruction = Instruction(parent = self, objectsToDraw = self.objectId) 00130 # open dialogs 00131 self.openDialogs = dict() 00132 00133 self.pageId = wx.NewId() 00134 #current page of flatnotebook 00135 self.currentPage = 0 00136 #canvas for draft mode 00137 self.canvas = PsMapBufferedWindow(parent = self, mouse = self.mouse, pen = self.pen, 00138 brush = self.brush, cursors = self.cursors, 00139 instruction = self.instruction, openDialogs = self.openDialogs, 00140 pageId = self.pageId, objectId = self.objectId, 00141 preview = False) 00142 00143 self.canvas.SetCursor(self.cursors["default"]) 00144 self.getInitMap() 00145 00146 00147 # image path 00148 env = grass.gisenv() 00149 self.imgName = grass.tempfile() 00150 00151 #canvas for preview 00152 self.previewCanvas = PsMapBufferedWindow(parent = self, mouse = self.mouse, cursors = self.cursors, 00153 pen = self.pen, brush = self.brush, preview = True) 00154 00155 # set WIND_OVERRIDE 00156 grass.use_temp_region() 00157 00158 # create queues 00159 self.requestQ = Queue.Queue() 00160 self.resultQ = Queue.Queue() 00161 # thread 00162 self.cmdThread = CmdThread(self, self.requestQ, self.resultQ) 00163 00164 self._layout() 00165 self.SetMinSize(wx.Size(750, 600)) 00166 00167 self.Bind(fnb.EVT_FLATNOTEBOOK_PAGE_CHANGING, self.OnPageChanging) 00168 self.Bind(fnb.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnPageChanged) 00169 self.Bind(wx.EVT_CLOSE, self.OnCloseWindow) 00170 self.Bind(EVT_CMD_DONE, self.OnCmdDone) 00171 00172 if not havePILImage: 00173 wx.CallAfter(self._showErrMsg) 00174 00175 def _showErrMsg(self): 00176 """!Show error message (missing preview) 00177 """ 00178 GError(parent = self, 00179 message = _("Python Imaging Library is not available.\n" 00180 "'Preview' functionality won't work."), 00181 showTraceback = False) 00182 00183 def _layout(self): 00184 """!Do layout 00185 """ 00186 mainSizer = wx.BoxSizer(wx.VERTICAL) 00187 if globalvar.hasAgw: 00188 self.book = fnb.FlatNotebook(parent = self, id = wx.ID_ANY, 00189 agwStyle = fnb.FNB_FANCY_TABS | fnb.FNB_BOTTOM | 00190 fnb.FNB_NO_NAV_BUTTONS | fnb.FNB_NO_X_BUTTON) 00191 else: 00192 self.book = fnb.FlatNotebook(parent = self, id = wx.ID_ANY, 00193 style = fnb.FNB_FANCY_TABS | fnb.FNB_BOTTOM | 00194 fnb.FNB_NO_NAV_BUTTONS | fnb.FNB_NO_X_BUTTON) 00195 #self.book = fnb.FlatNotebook(self, wx.ID_ANY, style = fnb.FNB_BOTTOM) 00196 self.book.AddPage(self.canvas, "Draft mode") 00197 self.book.AddPage(self.previewCanvas, "Preview") 00198 self.book.SetSelection(0) 00199 00200 mainSizer.Add(self.book,1, wx.EXPAND) 00201 00202 self.SetSizer(mainSizer) 00203 mainSizer.Fit(self) 00204 00205 00206 def InstructionFile(self): 00207 """!Creates mapping instructions""" 00208 00209 return str(self.instruction) 00210 00211 def OnPSFile(self, event): 00212 """!Generate PostScript""" 00213 filename = self.getFile(wildcard = "PostScript (*.ps)|*.ps|Encapsulated PostScript (*.eps)|*.eps") 00214 if filename: 00215 self.PSFile(filename) 00216 00217 def OnPsMapDialog(self, event): 00218 """!Launch ps.map dialog 00219 """ 00220 GUI(parent = self).ParseCommand(cmd = ['ps.map']) 00221 00222 def OnPDFFile(self, event): 00223 """!Generate PDF from PS with ps2pdf if available""" 00224 try: 00225 p = grass.Popen(["ps2pdf"], stderr = grass.PIPE) 00226 p.stderr.close() 00227 00228 except OSError: 00229 GMessage(parent = self, 00230 message = _("Program ps2pdf is not available. Please install it first to create PDF.")) 00231 return 00232 00233 filename = self.getFile(wildcard = "PDF (*.pdf)|*.pdf") 00234 if filename: 00235 self.PSFile(filename, pdf = True) 00236 00237 def OnPreview(self, event): 00238 """!Run ps.map and show result""" 00239 self.PSFile() 00240 00241 def PSFile(self, filename = None, pdf = False): 00242 """!Create temporary instructions file and run ps.map with output = filename""" 00243 instrFile = grass.tempfile() 00244 instrFileFd = open(instrFile, mode = 'w') 00245 instrFileFd.write(self.InstructionFile()) 00246 instrFileFd.flush() 00247 instrFileFd.close() 00248 00249 temp = False 00250 regOld = grass.region() 00251 00252 if pdf: 00253 pdfname = filename 00254 else: 00255 pdfname = None 00256 #preview or pdf 00257 if not filename or (filename and pdf): 00258 temp = True 00259 filename = grass.tempfile() 00260 if not pdf: # lower resolution for preview 00261 if self.instruction.FindInstructionByType('map'): 00262 mapId = self.instruction.FindInstructionByType('map').id 00263 SetResolution(dpi = 100, width = self.instruction[mapId]['rect'][2], 00264 height = self.instruction[mapId]['rect'][3]) 00265 00266 cmd = ['ps.map', '--overwrite'] 00267 if os.path.splitext(filename)[1] == '.eps': 00268 cmd.append('-e') 00269 if self.instruction[self.pageId]['Orientation'] == 'Landscape': 00270 cmd.append('-r') 00271 cmd.append('input=%s' % instrFile) 00272 cmd.append('output=%s' % filename) 00273 if pdf: 00274 self.SetStatusText(_('Generating PDF...'), 0) 00275 elif not temp: 00276 self.SetStatusText(_('Generating PostScript...'), 0) 00277 else: 00278 self.SetStatusText(_('Generating preview...'), 0) 00279 00280 self.cmdThread.RunCmd(cmd, userData = {'instrFile' : instrFile, 'filename' : filename, 00281 'pdfname' : pdfname, 'temp' : temp, 'regionOld' : regOld}) 00282 00283 def OnCmdDone(self, event): 00284 """!ps.map process finished""" 00285 00286 if event.returncode != 0: 00287 GMessage(parent = self, 00288 message = _("Ps.map exited with return code %s") % event.returncode) 00289 00290 grass.try_remove(event.userData['instrFile']) 00291 if event.userData['temp']: 00292 grass.try_remove(event.userData['filename']) 00293 return 00294 00295 if event.userData['pdfname']: 00296 try: 00297 proc = grass.Popen(['ps2pdf', '-dPDFSETTINGS=/prepress', '-r1200', 00298 event.userData['filename'], event.userData['pdfname']]) 00299 00300 ret = proc.wait() 00301 if ret > 0: 00302 GMessage(parent = self, 00303 message = _("ps2pdf exited with return code %s") % ret) 00304 00305 except OSError, e: 00306 GError(parent = self, 00307 message = _("Program ps2pdf is not available. Please install it to create PDF.\n\n %s") % e) 00308 00309 # show preview only when user doesn't want to create ps or pdf 00310 if havePILImage and event.userData['temp'] and not event.userData['pdfname']: 00311 RunCommand('g.region', cols = event.userData['regionOld']['cols'], rows = event.userData['regionOld']['rows']) 00312 ## wx.BusyInfo does not display the message 00313 ## busy = wx.BusyInfo(message = "Generating preview, wait please", parent = self) 00314 00315 try: 00316 im = PILImage.open(event.userData['filename']) 00317 if self.instruction[self.pageId]['Orientation'] == 'Landscape': 00318 im = im.rotate(270) 00319 00320 im.save(self.imgName, format = 'PNG') 00321 00322 except IOError, e: 00323 GError(parent = self, 00324 message = _("Unable to generate preview. %s") % e) 00325 return 00326 00327 00328 rect = self.previewCanvas.ImageRect() 00329 self.previewCanvas.image = wx.Image(self.imgName, wx.BITMAP_TYPE_PNG) 00330 self.previewCanvas.DrawImage(rect = rect) 00331 00332 ## busy.Destroy() 00333 self.SetStatusText(_('Preview generated'), 0) 00334 self.book.SetSelection(1) 00335 self.currentPage = 1 00336 00337 grass.try_remove(event.userData['instrFile']) 00338 if event.userData['temp']: 00339 grass.try_remove(event.userData['filename']) 00340 00341 def getFile(self, wildcard): 00342 suffix = [] 00343 for filter in wildcard.split('|')[1::2]: 00344 s = filter.strip('*').split('.')[1] 00345 if s: 00346 s = '.' + s 00347 suffix.append(s) 00348 raster = self.instruction.FindInstructionByType('raster') 00349 if raster: 00350 rasterId = raster.id 00351 else: 00352 rasterId = None 00353 00354 00355 if rasterId and self.instruction[rasterId]['raster']: 00356 mapName = self.instruction[rasterId]['raster'].split('@')[0] + suffix[0] 00357 else: 00358 mapName = '' 00359 00360 filename = '' 00361 dlg = wx.FileDialog(self, message = _("Save file as"), defaultDir = "", 00362 defaultFile = mapName, wildcard = wildcard, 00363 style = wx.CHANGE_DIR | wx.SAVE | wx.OVERWRITE_PROMPT) 00364 if dlg.ShowModal() == wx.ID_OK: 00365 filename = dlg.GetPath() 00366 suffix = suffix[dlg.GetFilterIndex()] 00367 if not os.path.splitext(filename)[1]: 00368 filename = filename + suffix 00369 elif os.path.splitext(filename)[1] != suffix and suffix != '': 00370 filename = os.path.splitext(filename)[0] + suffix 00371 00372 dlg.Destroy() 00373 return filename 00374 00375 def OnInstructionFile(self, event): 00376 filename = self.getFile(wildcard = "*.psmap|*.psmap|Text file(*.txt)|*.txt|All files(*.*)|*.*") 00377 if filename: 00378 instrFile = open(filename, "w") 00379 instrFile.write(self.InstructionFile()) 00380 instrFile.close() 00381 00382 def OnLoadFile(self, event): 00383 """!Load file and read instructions""" 00384 #find file 00385 filename = '' 00386 dlg = wx.FileDialog(self, message = "Find instructions file", defaultDir = "", 00387 defaultFile = '', wildcard = "All files (*.*)|*.*", 00388 style = wx.CHANGE_DIR|wx.OPEN) 00389 if dlg.ShowModal() == wx.ID_OK: 00390 filename = dlg.GetPath() 00391 dlg.Destroy() 00392 if not filename: 00393 return 00394 # load instructions 00395 readObjectId = [] 00396 readInstruction = Instruction(parent = self, objectsToDraw = readObjectId) 00397 ok = readInstruction.Read(filename) 00398 if not ok: 00399 GMessage(_("Failed to read file %s.") % filename) 00400 else: 00401 self.instruction = self.canvas.instruction = readInstruction 00402 self.objectId = self.canvas.objectId = readObjectId 00403 self.pageId = self.canvas.pageId = self.instruction.FindInstructionByType('page').id 00404 self.canvas.UpdateMapLabel() 00405 self.canvas.dragId = -1 00406 self.canvas.Clear() 00407 self.canvas.SetPage() 00408 #self.canvas.ZoomAll() 00409 00410 self.DialogDataChanged(self.objectId) 00411 00412 def OnPageSetup(self, event = None): 00413 """!Specify paper size, margins and orientation""" 00414 id = self.instruction.FindInstructionByType('page').id 00415 dlg = PageSetupDialog(self, id = id, settings = self.instruction) 00416 dlg.CenterOnScreen() 00417 val = dlg.ShowModal() 00418 if val == wx.ID_OK: 00419 self.canvas.SetPage() 00420 self.getInitMap() 00421 self.canvas.RecalculatePosition(ids = self.objectId) 00422 dlg.Destroy() 00423 00424 def OnPointer(self, event): 00425 self.toolbar.OnTool(event) 00426 self.mouse["use"] = "pointer" 00427 self.canvas.SetCursor(self.cursors["default"]) 00428 self.previewCanvas.SetCursor(self.cursors["default"]) 00429 00430 def OnPan(self, event): 00431 self.toolbar.OnTool(event) 00432 self.mouse["use"] = "pan" 00433 self.canvas.SetCursor(self.cursors["hand"]) 00434 self.previewCanvas.SetCursor(self.cursors["hand"]) 00435 00436 def OnZoomIn(self, event): 00437 self.toolbar.OnTool(event) 00438 self.mouse["use"] = "zoomin" 00439 self.canvas.SetCursor(self.cursors["cross"]) 00440 self.previewCanvas.SetCursor(self.cursors["cross"]) 00441 00442 def OnZoomOut(self, event): 00443 self.toolbar.OnTool(event) 00444 self.mouse["use"] = "zoomout" 00445 self.canvas.SetCursor(self.cursors["cross"]) 00446 self.previewCanvas.SetCursor(self.cursors["cross"]) 00447 00448 def OnZoomAll(self, event): 00449 self.mouseOld = self.mouse['use'] 00450 if self.currentPage == 0: 00451 self.cursorOld = self.canvas.GetCursor() 00452 else: 00453 self.cursorOld = self.previewCanvas.GetCursor() 00454 self.previewCanvas.GetCursor() 00455 self.mouse["use"] = "zoomin" 00456 if self.currentPage == 0: 00457 self.canvas.ZoomAll() 00458 else: 00459 self.previewCanvas.ZoomAll() 00460 self.mouse["use"] = self.mouseOld 00461 if self.currentPage == 0: 00462 self.canvas.SetCursor(self.cursorOld) 00463 else: 00464 self.previewCanvas.SetCursor(self.cursorOld) 00465 00466 00467 def OnAddMap(self, event, notebook = False): 00468 """!Add or edit map frame""" 00469 if event is not None: 00470 if event.GetId() != self.toolbar.action['id']: 00471 self.actionOld = self.toolbar.action['id'] 00472 self.mouseOld = self.mouse['use'] 00473 self.cursorOld = self.canvas.GetCursor() 00474 self.toolbar.OnTool(event) 00475 00476 if self.instruction.FindInstructionByType('map'): 00477 mapId = self.instruction.FindInstructionByType('map').id 00478 else: mapId = None 00479 id = [mapId, None, None] 00480 00481 if notebook: 00482 if self.instruction.FindInstructionByType('vector'): 00483 vectorId = self.instruction.FindInstructionByType('vector').id 00484 else: vectorId = None 00485 if self.instruction.FindInstructionByType('raster'): 00486 rasterId = self.instruction.FindInstructionByType('raster').id 00487 else: rasterId = None 00488 id[1] = rasterId 00489 id[2] = vectorId 00490 00491 00492 if mapId: # map exists 00493 00494 self.toolbar.ToggleTool(self.actionOld, True) 00495 self.toolbar.ToggleTool(self.toolbar.action['id'], False) 00496 self.toolbar.action['id'] = self.actionOld 00497 try: 00498 self.canvas.SetCursor(self.cursorOld) 00499 except AttributeError: 00500 pass 00501 00502 ## dlg = MapDialog(parent = self, id = id, settings = self.instruction, 00503 ## notebook = notebook) 00504 ## dlg.ShowModal() 00505 if notebook: 00506 #check map, raster, vector and save, destroy them 00507 if 'map' in self.openDialogs: 00508 self.openDialogs['map'].OnOK(event = None) 00509 if 'raster' in self.openDialogs: 00510 self.openDialogs['raster'].OnOK(event = None) 00511 if 'vector' in self.openDialogs: 00512 self.openDialogs['vector'].OnOK(event = None) 00513 00514 if 'mapNotebook' not in self.openDialogs: 00515 dlg = MapDialog(parent = self, id = id, settings = self.instruction, 00516 notebook = notebook) 00517 self.openDialogs['mapNotebook'] = dlg 00518 self.openDialogs['mapNotebook'].Show() 00519 else: 00520 if 'mapNotebook' in self.openDialogs: 00521 self.openDialogs['mapNotebook'].notebook.ChangeSelection(0) 00522 else: 00523 if 'map' not in self.openDialogs: 00524 dlg = MapDialog(parent = self, id = id, settings = self.instruction, 00525 notebook = notebook) 00526 self.openDialogs['map'] = dlg 00527 self.openDialogs['map'].Show() 00528 00529 00530 else: # sofar no map 00531 self.mouse["use"] = "addMap" 00532 self.canvas.SetCursor(self.cursors["cross"]) 00533 if self.currentPage == 1: 00534 self.book.SetSelection(0) 00535 self.currentPage = 0 00536 00537 def OnAddRaster(self, event): 00538 """!Add raster map""" 00539 if self.instruction.FindInstructionByType('raster'): 00540 id = self.instruction.FindInstructionByType('raster').id 00541 else: id = None 00542 if self.instruction.FindInstructionByType('map'): 00543 mapId = self.instruction.FindInstructionByType('map').id 00544 else: mapId = None 00545 00546 if not id: 00547 if not mapId: 00548 GMessage(message = _("Please, create map frame first.")) 00549 return 00550 00551 ## dlg = RasterDialog(self, id = id, settings = self.instruction) 00552 ## dlg.ShowModal() 00553 if 'mapNotebook' in self.openDialogs: 00554 self.openDialogs['mapNotebook'].notebook.ChangeSelection(1) 00555 else: 00556 if 'raster' not in self.openDialogs: 00557 dlg = RasterDialog(self, id = id, settings = self.instruction) 00558 self.openDialogs['raster'] = dlg 00559 self.openDialogs['raster'].Show() 00560 00561 def OnAddVect(self, event): 00562 """!Add vector map""" 00563 if self.instruction.FindInstructionByType('vector'): 00564 id = self.instruction.FindInstructionByType('vector').id 00565 else: id = None 00566 if self.instruction.FindInstructionByType('map'): 00567 mapId = self.instruction.FindInstructionByType('map').id 00568 else: mapId = None 00569 if not id: 00570 if not mapId: 00571 GMessage(message = _("Please, create map frame first.")) 00572 return 00573 00574 ## dlg = MainVectorDialog(self, id = id, settings = self.instruction) 00575 ## dlg.ShowModal() 00576 if 'mapNotebook' in self.openDialogs: 00577 self.openDialogs['mapNotebook'].notebook.ChangeSelection(2) 00578 else: 00579 if 'vector' not in self.openDialogs: 00580 dlg = MainVectorDialog(self, id = id, settings = self.instruction) 00581 self.openDialogs['vector'] = dlg 00582 self.openDialogs['vector'].Show() 00583 00584 def OnAddScalebar(self, event): 00585 """!Add scalebar""" 00586 if projInfo()['proj'] == 'll': 00587 GMessage(message = _("Scalebar is not appropriate for this projection")) 00588 return 00589 if self.instruction.FindInstructionByType('scalebar'): 00590 id = self.instruction.FindInstructionByType('scalebar').id 00591 else: id = None 00592 00593 if 'scalebar' not in self.openDialogs: 00594 dlg = ScalebarDialog(self, id = id, settings = self.instruction) 00595 self.openDialogs['scalebar'] = dlg 00596 self.openDialogs['scalebar'].Show() 00597 00598 def OnAddLegend(self, event, page = 0): 00599 """!Add raster or vector legend""" 00600 if self.instruction.FindInstructionByType('rasterLegend'): 00601 idR = self.instruction.FindInstructionByType('rasterLegend').id 00602 else: idR = None 00603 if self.instruction.FindInstructionByType('vectorLegend'): 00604 idV = self.instruction.FindInstructionByType('vectorLegend').id 00605 else: idV = None 00606 00607 if 'rasterLegend' not in self.openDialogs: 00608 dlg = LegendDialog(self, id = [idR, idV], settings = self.instruction, page = page) 00609 self.openDialogs['rasterLegend'] = dlg 00610 self.openDialogs['vectorLegend'] = dlg 00611 self.openDialogs['rasterLegend'].notebook.ChangeSelection(page) 00612 self.openDialogs['rasterLegend'].Show() 00613 00614 def OnAddMapinfo(self, event): 00615 if self.instruction.FindInstructionByType('mapinfo'): 00616 id = self.instruction.FindInstructionByType('mapinfo').id 00617 else: id = None 00618 00619 if 'mapinfo' not in self.openDialogs: 00620 dlg = MapinfoDialog(self, id = id, settings = self.instruction) 00621 self.openDialogs['mapinfo'] = dlg 00622 self.openDialogs['mapinfo'].Show() 00623 00624 def OnAddImage(self, event, id = None): 00625 """!Show dialog for image adding and editing""" 00626 position = None 00627 if 'image' in self.openDialogs: 00628 position = self.openDialogs['image'].GetPosition() 00629 self.openDialogs['image'].OnApply(event = None) 00630 self.openDialogs['image'].Destroy() 00631 dlg = ImageDialog(self, id = id, settings = self.instruction) 00632 self.openDialogs['image'] = dlg 00633 if position: 00634 dlg.SetPosition(position) 00635 dlg.Show() 00636 00637 def OnAddNorthArrow(self, event, id = None): 00638 """!Show dialog for north arrow adding and editing""" 00639 if self.instruction.FindInstructionByType('northArrow'): 00640 id = self.instruction.FindInstructionByType('northArrow').id 00641 else: id = None 00642 00643 if 'northArrow' not in self.openDialogs: 00644 dlg = NorthArrowDialog(self, id = id, settings = self.instruction) 00645 self.openDialogs['northArrow'] = dlg 00646 self.openDialogs['northArrow'].Show() 00647 00648 def OnAddText(self, event, id = None): 00649 """!Show dialog for text adding and editing""" 00650 position = None 00651 if 'text' in self.openDialogs: 00652 position = self.openDialogs['text'].GetPosition() 00653 self.openDialogs['text'].OnApply(event = None) 00654 self.openDialogs['text'].Destroy() 00655 dlg = TextDialog(self, id = id, settings = self.instruction) 00656 self.openDialogs['text'] = dlg 00657 if position: 00658 dlg.SetPosition(position) 00659 dlg.Show() 00660 00661 def OnAddPoint(self, event): 00662 """!Add point action selected""" 00663 self.mouse["use"] = "addPoint" 00664 self.canvas.SetCursor(self.cursors["cross"]) 00665 00666 def AddPoint(self, id = None, coordinates = None): 00667 """!Add point and open property dialog. 00668 00669 @param id id point id (None if creating new point) 00670 @param coordinates coordinates of new point 00671 """ 00672 position = None 00673 if 'point' in self.openDialogs: 00674 position = self.openDialogs['point'].GetPosition() 00675 self.openDialogs['point'].OnApply(event = None) 00676 self.openDialogs['point'].Destroy() 00677 dlg = PointDialog(self, id = id, settings = self.instruction, 00678 coordinates = coordinates) 00679 self.openDialogs['point'] = dlg 00680 if position: 00681 dlg.SetPosition(position) 00682 if coordinates: 00683 dlg.OnApply(event = None) 00684 dlg.Show() 00685 00686 def OnAddLine(self, event): 00687 """!Add line action selected""" 00688 self.mouse["use"] = "addLine" 00689 self.canvas.SetCursor(self.cursors["cross"]) 00690 00691 def AddLine(self, id = None, coordinates = None): 00692 """!Add line and open property dialog. 00693 00694 @param id id line id (None if creating new line) 00695 @param coordinates coordinates of new line 00696 """ 00697 position = None 00698 if 'line' in self.openDialogs: 00699 position = self.openDialogs['line'].GetPosition() 00700 self.openDialogs['line'].OnApply(event = None) 00701 self.openDialogs['line'].Destroy() 00702 dlg = RectangleDialog(self, id = id, settings = self.instruction, 00703 type = 'line', coordinates = coordinates) 00704 self.openDialogs['line'] = dlg 00705 if position: 00706 dlg.SetPosition(position) 00707 if coordinates: 00708 dlg.OnApply(event = None) 00709 dlg.Show() 00710 00711 def OnAddRectangle(self, event): 00712 """!Add rectangle action selected""" 00713 self.mouse["use"] = "addRectangle" 00714 self.canvas.SetCursor(self.cursors["cross"]) 00715 00716 def AddRectangle(self, id = None, coordinates = None): 00717 """!Add rectangle and open property dialog. 00718 00719 @param id id rectangle id (None if creating new rectangle) 00720 @param coordinates coordinates of new rectangle 00721 """ 00722 position = None 00723 if 'rectangle' in self.openDialogs: 00724 position = self.openDialogs['rectangle'].GetPosition() 00725 self.openDialogs['rectangle'].OnApply(event = None) 00726 self.openDialogs['rectangle'].Destroy() 00727 dlg = RectangleDialog(self, id = id, settings = self.instruction, 00728 type = 'rectangle', coordinates = coordinates) 00729 self.openDialogs['rectangle'] = dlg 00730 if position: 00731 dlg.SetPosition(position) 00732 if coordinates: 00733 dlg.OnApply(event = None) 00734 dlg.Show() 00735 00736 def getModifiedTextBounds(self, x, y, textExtent, rotation): 00737 """!computes bounding box of rotated text, not very precisely""" 00738 w, h = textExtent 00739 rotation = float(rotation)/180*pi 00740 H = float(w) * sin(rotation) 00741 W = float(w) * cos(rotation) 00742 X, Y = x, y 00743 if pi/2 < rotation <= 3*pi/2: 00744 X = x + W 00745 if 0 < rotation < pi: 00746 Y = y - H 00747 if rotation == 0: 00748 return wx.Rect(x,y, *textExtent) 00749 else: 00750 return wx.Rect(X, Y, abs(W), abs(H)).Inflate(h,h) 00751 00752 def makePSFont(self, textDict): 00753 """!creates a wx.Font object from selected postscript font. To be 00754 used for estimating bounding rectangle of text""" 00755 00756 fontsize = textDict['fontsize'] * self.canvas.currScale 00757 fontface = textDict['font'].split('-')[0] 00758 try: 00759 fontstyle = textDict['font'].split('-')[1] 00760 except IndexError: 00761 fontstyle = '' 00762 00763 if fontface == "Times": 00764 family = wx.FONTFAMILY_ROMAN 00765 face = "times" 00766 elif fontface == "Helvetica": 00767 family = wx.FONTFAMILY_SWISS 00768 face = 'helvetica' 00769 elif fontface == "Courier": 00770 family = wx.FONTFAMILY_TELETYPE 00771 face = 'courier' 00772 else: 00773 family = wx.FONTFAMILY_DEFAULT 00774 face = '' 00775 00776 style = wx.FONTSTYLE_NORMAL 00777 weight = wx.FONTWEIGHT_NORMAL 00778 00779 if 'Oblique' in fontstyle: 00780 style = wx.FONTSTYLE_SLANT 00781 00782 if 'Italic' in fontstyle: 00783 style = wx.FONTSTYLE_ITALIC 00784 00785 if 'Bold' in fontstyle: 00786 weight = wx.FONTWEIGHT_BOLD 00787 00788 try: 00789 fn = wx.Font(pointSize = fontsize, family = family, style = style, 00790 weight = weight, face = face) 00791 except: 00792 fn = wx.Font(pointSize = fontsize, family = wx.FONTFAMILY_DEFAULT, 00793 style = wx.FONTSTYLE_NORMAL, weight = wx.FONTWEIGHT_NORMAL) 00794 00795 return fn 00796 00797 00798 def getTextExtent(self, textDict): 00799 """!Estimates bounding rectangle of text""" 00800 #fontsize = str(fontsize if fontsize >= 4 else 4) 00801 dc = wx.ClientDC(self) # dc created because of method GetTextExtent, which pseudoDC lacks 00802 00803 fn = self.makePSFont(textDict) 00804 00805 try: 00806 dc.SetFont(fn) 00807 w,h,lh = dc.GetMultiLineTextExtent(textDict['text']) 00808 return (w,h) 00809 except: 00810 return (0,0) 00811 00812 def getInitMap(self): 00813 """!Create default map frame when no map is selected, needed for coordinates in map units""" 00814 instrFile = grass.tempfile() 00815 instrFileFd = open(instrFile, mode = 'w') 00816 instrFileFd.write(self.InstructionFile()) 00817 instrFileFd.flush() 00818 instrFileFd.close() 00819 00820 page = self.instruction.FindInstructionByType('page') 00821 mapInitRect = GetMapBounds(instrFile, portrait = (page['Orientation'] == 'Portrait')) 00822 grass.try_remove(instrFile) 00823 00824 region = grass.region() 00825 units = UnitConversion(self) 00826 realWidth = units.convert(value = abs(region['w'] - region['e']), fromUnit = 'meter', toUnit = 'inch') 00827 scale = mapInitRect.Get()[2]/realWidth 00828 00829 initMap = self.instruction.FindInstructionByType('initMap') 00830 if initMap: 00831 id = initMap.id 00832 else: 00833 id = None 00834 00835 00836 if not id: 00837 id = wx.NewId() 00838 initMap = InitMap(id) 00839 self.instruction.AddInstruction(initMap) 00840 self.instruction[id].SetInstruction(dict(rect = mapInitRect, scale = scale)) 00841 00842 def OnDelete(self, event): 00843 if self.canvas.dragId != -1 and self.currentPage == 0: 00844 if self.instruction[self.canvas.dragId].type == 'map': 00845 self.deleteObject(self.canvas.dragId) 00846 self.getInitMap() 00847 self.canvas.RecalculateEN() 00848 else: 00849 self.deleteObject(self.canvas.dragId) 00850 00851 def deleteObject(self, id): 00852 """!Deletes object, his id and redraws""" 00853 #delete from canvas 00854 self.canvas.pdcObj.RemoveId(id) 00855 if id == self.canvas.dragId: 00856 self.canvas.pdcTmp.RemoveAll() 00857 self.canvas.dragId = -1 00858 self.canvas.Refresh() 00859 00860 # delete from instructions 00861 del self.instruction[id] 00862 00863 def DialogDataChanged(self, id): 00864 ids = id 00865 if type(id) == int: 00866 ids = [id] 00867 for id in ids: 00868 itype = self.instruction[id].type 00869 00870 if itype in ('scalebar', 'mapinfo', 'image'): 00871 drawRectangle = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'], canvasToPaper = False) 00872 self.canvas.UpdateLabel(itype = itype, id = id) 00873 self.canvas.Draw(pen = self.pen[itype], brush = self.brush[itype], 00874 pdc = self.canvas.pdcObj, drawid = id, pdctype = 'rectText', bb = drawRectangle) 00875 self.canvas.RedrawSelectBox(id) 00876 if itype == 'northArrow': 00877 self.canvas.UpdateLabel(itype = itype, id = id) 00878 drawRectangle = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'], canvasToPaper = False) 00879 self.canvas.Draw(pen = self.pen[itype], brush = self.brush[itype], 00880 pdc = self.canvas.pdcObj, drawid = id, pdctype = 'bitmap', bb = drawRectangle) 00881 self.canvas.RedrawSelectBox(id) 00882 00883 if itype in ('point', 'line', 'rectangle'): 00884 drawRectangle = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'], canvasToPaper = False) 00885 # coords only for line 00886 coords = None 00887 if itype == 'line': 00888 point1 = self.instruction[id]['where'][0] 00889 point2 = self.instruction[id]['where'][1] 00890 point1Coords = self.canvas.CanvasPaperCoordinates(rect = Rect2DPS(point1, (0, 0)), canvasToPaper = False)[:2] 00891 point2Coords = self.canvas.CanvasPaperCoordinates(rect = Rect2DPS(point2, (0, 0)), canvasToPaper = False)[:2] 00892 coords = (point1Coords, point2Coords) 00893 00894 # fill color is not in line 00895 fcolor = None 00896 if 'fcolor' in self.instruction[id].GetInstruction(): 00897 fcolor = self.instruction[id]['fcolor'] 00898 # width is not in point 00899 width = None 00900 if 'width' in self.instruction[id].GetInstruction(): 00901 width = self.instruction[id]['width'] 00902 00903 self.canvas.DrawGraphics(drawid = id, color = self.instruction[id]['color'], shape = itype, 00904 fcolor = fcolor, width = width, bb = drawRectangle, lineCoords = coords) 00905 00906 self.canvas.RedrawSelectBox(id) 00907 00908 if itype == 'text': 00909 00910 if self.instruction[id]['rotate']: 00911 rot = float(self.instruction[id]['rotate']) 00912 else: 00913 rot = 0 00914 00915 extent = self.getTextExtent(textDict = self.instruction[id].GetInstruction()) 00916 rect = Rect2DPS(self.instruction[id]['where'], (0, 0)) 00917 self.instruction[id]['coords'] = list(self.canvas.CanvasPaperCoordinates(rect = rect, canvasToPaper = False)[:2]) 00918 00919 #computes text coordinates according to reference point, not precisely 00920 if self.instruction[id]['ref'].split()[0] == 'lower': 00921 self.instruction[id]['coords'][1] -= extent[1] 00922 elif self.instruction[id]['ref'].split()[0] == 'center': 00923 self.instruction[id]['coords'][1] -= extent[1]/2 00924 if self.instruction[id]['ref'].split()[1] == 'right': 00925 self.instruction[id]['coords'][0] -= extent[0] * cos(rot/180*pi) 00926 self.instruction[id]['coords'][1] += extent[0] * sin(rot/180*pi) 00927 elif self.instruction[id]['ref'].split()[1] == 'center': 00928 self.instruction[id]['coords'][0] -= extent[0]/2 * cos(rot/180*pi) 00929 self.instruction[id]['coords'][1] += extent[0]/2 * sin(rot/180*pi) 00930 00931 self.instruction[id]['coords'][0] += self.instruction[id]['xoffset'] 00932 self.instruction[id]['coords'][1] -= self.instruction[id]['yoffset'] 00933 coords = self.instruction[id]['coords'] 00934 self.instruction[id]['rect'] = bounds = self.getModifiedTextBounds(coords[0], coords[1], extent, rot) 00935 self.canvas.DrawRotText(pdc = self.canvas.pdcObj, drawId = id, 00936 textDict = self.instruction[id].GetInstruction(), 00937 coords = coords, bounds = bounds) 00938 self.canvas.RedrawSelectBox(id) 00939 00940 if itype in ('map', 'vector', 'raster'): 00941 00942 if itype == 'raster':#set resolution 00943 info = grass.raster_info(self.instruction[id]['raster']) 00944 RunCommand('g.region', nsres = info['nsres'], ewres = info['ewres']) 00945 # change current raster in raster legend 00946 00947 if 'rasterLegend' in self.openDialogs: 00948 self.openDialogs['rasterLegend'].updateDialog() 00949 id = self.instruction.FindInstructionByType('map').id 00950 00951 #check resolution 00952 if itype == 'raster': 00953 SetResolution(dpi = self.instruction[id]['resolution'], 00954 width = self.instruction[id]['rect'].width, 00955 height = self.instruction[id]['rect'].height) 00956 rectCanvas = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'], 00957 canvasToPaper = False) 00958 self.canvas.RecalculateEN() 00959 self.canvas.UpdateMapLabel() 00960 00961 self.canvas.Draw(pen = self.pen['map'], brush = self.brush['map'], 00962 pdc = self.canvas.pdcObj, drawid = id, pdctype = 'rectText', bb = rectCanvas) 00963 # redraw select box 00964 self.canvas.RedrawSelectBox(id) 00965 self.canvas.pdcTmp.RemoveId(self.canvas.idZoomBoxTmp) 00966 # redraw to get map to the bottom layer 00967 #self.canvas.Zoom(zoomFactor = 1, view = (0, 0)) 00968 00969 if itype == 'rasterLegend': 00970 if self.instruction[id]['rLegend']: 00971 self.canvas.UpdateLabel(itype = itype, id = id) 00972 drawRectangle = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'], canvasToPaper = False) 00973 self.canvas.Draw(pen = self.pen[itype], brush = self.brush[itype], 00974 pdc = self.canvas.pdcObj, drawid = id, pdctype = 'rectText', bb = drawRectangle) 00975 self.canvas.RedrawSelectBox(id) 00976 else: 00977 self.deleteObject(id) 00978 00979 if itype == 'vectorLegend': 00980 if not self.instruction.FindInstructionByType('vector'): 00981 self.deleteObject(id) 00982 elif self.instruction[id]['vLegend']: 00983 self.canvas.UpdateLabel(itype = itype, id = id) 00984 drawRectangle = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'], canvasToPaper = False) 00985 self.canvas.Draw(pen = self.pen[itype], brush = self.brush[itype], 00986 pdc = self.canvas.pdcObj, drawid = id, pdctype = 'rectText', bb = drawRectangle) 00987 self.canvas.RedrawSelectBox(id) 00988 00989 else: 00990 self.deleteObject(id) 00991 00992 def OnPageChanged(self, event): 00993 """!Flatnotebook page has changed""" 00994 self.currentPage = self.book.GetPageIndex(self.book.GetCurrentPage()) 00995 00996 00997 def OnPageChanging(self, event): 00998 """!Flatnotebook page is changing""" 00999 if self.currentPage == 0 and self.mouse['use'] == 'addMap': 01000 event.Veto() 01001 01002 def OnHelp(self, event): 01003 """!Show help""" 01004 if self.parent and self.parent.GetName() == 'LayerManager': 01005 log = self.parent.GetLogWindow() 01006 log.RunCmd(['g.manual', 01007 'entry=wxGUI.PsMap']) 01008 else: 01009 RunCommand('g.manual', 01010 quiet = True, 01011 entry = 'wxGUI.PsMap') 01012 01013 def OnAbout(self, event): 01014 """!Display About window""" 01015 info = wx.AboutDialogInfo() 01016 01017 info.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO)) 01018 info.SetName(_('wxGUI Cartographic Composer')) 01019 info.SetWebSite('http://grass.osgeo.org') 01020 info.SetDescription(_('(C) 2011 by the GRASS Development Team\n\n') + 01021 '\n'.join(textwrap.wrap(_('This program is free software under the GNU General Public License' 01022 '(>=v2). Read the file COPYING that comes with GRASS for details.'), 75))) 01023 01024 wx.AboutBox(info) 01025 01026 def OnCloseWindow(self, event): 01027 """!Close window""" 01028 try: 01029 os.remove(self.imgName) 01030 except OSError: 01031 pass 01032 grass.set_raise_on_error(False) 01033 self.Destroy() 01034 01035 01036 01037 class PsMapBufferedWindow(wx.Window): 01038 """!A buffered window class. 01039 01040 @param parent parent window 01041 @param kwargs other wx.Window parameters 01042 """ 01043 def __init__(self, parent, id = wx.ID_ANY, 01044 style = wx.NO_FULL_REPAINT_ON_RESIZE, 01045 **kwargs): 01046 wx.Window.__init__(self, parent, id = id, style = style) 01047 self.parent = parent 01048 01049 self.FitInside() 01050 01051 # store an off screen empty bitmap for saving to file 01052 self._buffer = None 01053 # indicates whether or not a resize event has taken place 01054 self.resize = False 01055 01056 self.mouse = kwargs['mouse'] 01057 self.cursors = kwargs['cursors'] 01058 self.preview = kwargs['preview'] 01059 self.pen = kwargs['pen'] 01060 self.brush = kwargs['brush'] 01061 01062 if kwargs.has_key('instruction'): 01063 self.instruction = kwargs['instruction'] 01064 if kwargs.has_key('openDialogs'): 01065 self.openDialogs = kwargs['openDialogs'] 01066 if kwargs.has_key('pageId'): 01067 self.pageId = kwargs['pageId'] 01068 if kwargs.has_key('objectId'): 01069 self.objectId = kwargs['objectId'] 01070 01071 01072 #labels 01073 self.itemLabelsDict = { 'map': 'MAP FRAME', 01074 'rasterLegend': 'RASTER LEGEND', 01075 'vectorLegend': 'VECTOR LEGEND', 01076 'mapinfo': 'MAP INFO', 01077 'scalebar': 'SCALE BAR', 01078 'image': 'IMAGE', 01079 'northArrow': 'NORTH ARROW'} 01080 self.itemLabels = {} 01081 01082 # define PseudoDC 01083 self.pdc = wx.PseudoDC() 01084 self.pdcObj = wx.PseudoDC() 01085 self.pdcPaper = wx.PseudoDC() 01086 self.pdcTmp = wx.PseudoDC() 01087 self.pdcImage = wx.PseudoDC() 01088 dc = wx.ClientDC(self) 01089 self.font = dc.GetFont() 01090 01091 self.SetClientSize((700,510))#? 01092 self._buffer = wx.EmptyBitmap(*self.GetClientSize()) 01093 01094 self.idBoxTmp = wx.NewId() 01095 self.idZoomBoxTmp = wx.NewId() 01096 self.idResizeBoxTmp = wx.NewId() 01097 self.idLinePointsTmp = (wx.NewId(), wx.NewId()) # ids of marks for moving line vertices 01098 01099 self.resizeBoxSize = wx.Size(8, 8) 01100 01101 01102 01103 self.dragId = -1 01104 01105 if self.preview: 01106 self.image = None 01107 self.imageId = 2000 01108 self.imgName = self.parent.imgName 01109 01110 01111 01112 self.currScale = None 01113 01114 self.Clear() 01115 01116 self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: None) 01117 01118 self.Bind(wx.EVT_PAINT, self.OnPaint) 01119 self.Bind(wx.EVT_SIZE, self.OnSize) 01120 self.Bind(wx.EVT_IDLE, self.OnIdle) 01121 # self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouse) 01122 self.Bind(wx.EVT_MOUSE_EVENTS, self.MouseActions) 01123 01124 01125 def Clear(self): 01126 """!Clear canvas and set paper 01127 """ 01128 bg = wx.LIGHT_GREY_BRUSH 01129 self.pdcPaper.BeginDrawing() 01130 self.pdcPaper.SetBackground(bg) 01131 self.pdcPaper.Clear() 01132 self.pdcPaper.EndDrawing() 01133 01134 self.pdcObj.RemoveAll() 01135 self.pdcTmp.RemoveAll() 01136 01137 01138 01139 if not self.preview: 01140 self.SetPage() 01141 01142 01143 def CanvasPaperCoordinates(self, rect, canvasToPaper = True): 01144 """!Converts canvas (pixel) -> paper (inch) coordinates and size and vice versa""" 01145 01146 units = UnitConversion(self) 01147 01148 fromU = 'pixel' 01149 toU = 'inch' 01150 pRect = self.pdcPaper.GetIdBounds(self.pageId) 01151 pRectx, pRecty = pRect.x, pRect.y 01152 scale = 1/self.currScale 01153 if not canvasToPaper: # paper -> canvas 01154 fromU = 'inch' 01155 toU = 'pixel' 01156 scale = self.currScale 01157 pRectx = units.convert(value = - pRect.x, fromUnit = 'pixel', toUnit = 'inch' ) /scale #inch, real, negative 01158 pRecty = units.convert(value = - pRect.y, fromUnit = 'pixel', toUnit = 'inch' ) /scale 01159 Width = units.convert(value = rect.GetWidth(), fromUnit = fromU, toUnit = toU) * scale 01160 Height = units.convert(value = rect.GetHeight(), fromUnit = fromU, toUnit = toU) * scale 01161 X = units.convert(value = (rect.GetX() - pRectx), fromUnit = fromU, toUnit = toU) * scale 01162 Y = units.convert(value = (rect.GetY() - pRecty), fromUnit = fromU, toUnit = toU) * scale 01163 01164 return Rect2D(X, Y, Width, Height) 01165 01166 01167 01168 def SetPage(self): 01169 """!Sets and changes page, redraws paper""" 01170 01171 page = self.instruction[self.pageId] 01172 if not page: 01173 page = PageSetup(id = self.pageId) 01174 self.instruction.AddInstruction(page) 01175 01176 ppi = wx.ClientDC(self).GetPPI() 01177 cW, cH = self.GetClientSize() 01178 pW, pH = page['Width']*ppi[0], page['Height']*ppi[1] 01179 01180 if self.currScale is None: 01181 self.currScale = min(cW/pW, cH/pH) 01182 pW = pW * self.currScale 01183 pH = pH * self.currScale 01184 01185 x = cW/2 - pW/2 01186 y = cH/2 - pH/2 01187 self.DrawPaper(wx.Rect(x, y, pW, pH)) 01188 01189 01190 def modifyRectangle(self, r): 01191 """! Recalculates rectangle not to have negative size""" 01192 if r.GetWidth() < 0: 01193 r.SetX(r.GetX() + r.GetWidth()) 01194 if r.GetHeight() < 0: 01195 r.SetY(r.GetY() + r.GetHeight()) 01196 r.SetWidth(abs(r.GetWidth())) 01197 r.SetHeight(abs(r.GetHeight())) 01198 return r 01199 01200 def RecalculateEN(self): 01201 """!Recalculate east and north for texts (eps, points) after their or map's movement""" 01202 try: 01203 mapId = self.instruction.FindInstructionByType('map').id 01204 except AttributeError: 01205 mapId = self.instruction.FindInstructionByType('initMap').id 01206 01207 for itemType in ('text', 'image', 'northArrow', 'point', 'line', 'rectangle'): 01208 items = self.instruction.FindInstructionByType(itemType, list = True) 01209 for item in items: 01210 instr = self.instruction[item.id] 01211 if itemType in ('line', 'rectangle'): 01212 if itemType == 'line': 01213 e1, n1 = PaperMapCoordinates(mapInstr = self.instruction[mapId], x = instr['where'][0][0], 01214 y = instr['where'][0][1], paperToMap = True) 01215 e2, n2 = PaperMapCoordinates(mapInstr = self.instruction[mapId], x = instr['where'][1][0], 01216 y = instr['where'][1][1], paperToMap = True) 01217 else: 01218 e1, n1 = PaperMapCoordinates(mapInstr = self.instruction[mapId], x = instr['rect'].GetLeft(), 01219 y = instr['rect'].GetTop(), paperToMap = True) 01220 e2, n2 = PaperMapCoordinates(mapInstr = self.instruction[mapId], x = instr['rect'].GetRight(), 01221 y = instr['rect'].GetBottom(), paperToMap = True) 01222 instr['east1'] = e1 01223 instr['north1'] = n1 01224 instr['east2'] = e2 01225 instr['north2'] = n2 01226 else: 01227 e, n = PaperMapCoordinates(mapInstr = self.instruction[mapId], x = instr['where'][0], 01228 y = instr['where'][1], paperToMap = True) 01229 instr['east'], instr['north'] = e, n 01230 01231 def OnPaint(self, event): 01232 """!Draw pseudo DC to buffer 01233 """ 01234 if not self._buffer: 01235 return 01236 dc = wx.BufferedPaintDC(self, self._buffer) 01237 # use PrepareDC to set position correctly 01238 self.PrepareDC(dc) 01239 01240 dc.SetBackground(wx.LIGHT_GREY_BRUSH) 01241 dc.Clear() 01242 01243 # draw paper 01244 if not self.preview: 01245 self.pdcPaper.DrawToDC(dc) 01246 # draw to the DC using the calculated clipping rect 01247 01248 rgn = self.GetUpdateRegion() 01249 01250 if not self.preview: 01251 self.pdcObj.DrawToDCClipped(dc, rgn.GetBox()) 01252 else: 01253 self.pdcImage.DrawToDCClipped(dc, rgn.GetBox()) 01254 self.pdcTmp.DrawToDCClipped(dc, rgn.GetBox()) 01255 01256 def MouseActions(self, event): 01257 """!Mouse motion and button click notifier 01258 """ 01259 # zoom with mouse wheel 01260 if event.GetWheelRotation() != 0: 01261 self.OnMouseWheel(event) 01262 01263 # left mouse button pressed 01264 elif event.LeftDown(): 01265 self.OnLeftDown(event) 01266 01267 # left mouse button released 01268 elif event.LeftUp(): 01269 self.OnLeftUp(event) 01270 01271 # dragging 01272 elif event.Dragging(): 01273 self.OnDragging(event) 01274 01275 # double click 01276 elif event.ButtonDClick(): 01277 self.OnButtonDClick(event) 01278 01279 # middle mouse button pressed 01280 elif event.MiddleDown(): 01281 self.OnMiddleDown(event) 01282 01283 elif event.Moving(): 01284 self.OnMouseMoving(event) 01285 01286 def OnMouseWheel(self, event): 01287 """!Mouse wheel scrolled. 01288 01289 Changes zoom.""" 01290 if not UserSettings.Get(group = 'display', 01291 key = 'mouseWheelZoom', 01292 subkey = 'enabled'): 01293 event.Skip() 01294 return 01295 01296 zoom = event.GetWheelRotation() 01297 oldUse = self.mouse['use'] 01298 self.mouse['begin'] = event.GetPosition() 01299 01300 if UserSettings.Get(group = 'display', 01301 key = 'mouseWheelZoom', 01302 subkey = 'selection'): 01303 zoom *= -1 01304 01305 if zoom > 0: 01306 self.mouse['use'] = 'zoomin' 01307 else: 01308 self.mouse['use'] = 'zoomout' 01309 01310 zoomFactor, view = self.ComputeZoom(wx.Rect(0, 0, 0, 0)) 01311 self.Zoom(zoomFactor, view) 01312 self.mouse['use'] = oldUse 01313 01314 def OnMouseMoving(self, event): 01315 """!Mouse cursor moving. 01316 01317 Change cursor when moving over resize marker. 01318 """ 01319 if self.mouse['use'] in ('pointer', 'resize'): 01320 pos = event.GetPosition() 01321 foundResize = self.pdcTmp.FindObjects(pos[0], pos[1]) 01322 if foundResize and foundResize[0] in (self.idResizeBoxTmp,) + self.idLinePointsTmp: 01323 self.SetCursor(self.cursors["sizenwse"]) 01324 self.parent.SetStatusText(_('Click and drag to resize object'), 0) 01325 else: 01326 self.parent.SetStatusText('', 0) 01327 self.SetCursor(self.cursors["default"]) 01328 01329 def OnLeftDown(self, event): 01330 """!Left mouse button pressed. 01331 01332 Select objects, redraw, prepare for moving/resizing. 01333 """ 01334 self.mouse['begin'] = event.GetPosition() 01335 self.begin = self.mouse['begin'] 01336 01337 # select 01338 if self.mouse['use'] == 'pointer': 01339 found = self.pdcObj.FindObjects(self.mouse['begin'][0], self.mouse['begin'][1]) 01340 foundResize = self.pdcTmp.FindObjects(self.mouse['begin'][0], self.mouse['begin'][1]) 01341 01342 if foundResize and foundResize[0] in (self.idResizeBoxTmp,) + self.idLinePointsTmp: 01343 self.mouse['use'] = 'resize' 01344 01345 # when resizing, proportions match region 01346 if self.instruction[self.dragId].type == 'map': 01347 self.constraint = False 01348 self.mapBounds = self.pdcObj.GetIdBounds(self.dragId) 01349 if self.instruction[self.dragId]['scaleType'] in (0, 1, 2): 01350 self.constraint = True 01351 self.mapBounds = self.pdcObj.GetIdBounds(self.dragId) 01352 01353 if self.instruction[self.dragId].type == 'line': 01354 self.currentLinePoint = self.idLinePointsTmp.index(foundResize[0]) 01355 01356 elif found: 01357 self.dragId = found[0] 01358 self.RedrawSelectBox(self.dragId) 01359 if self.instruction[self.dragId].type not in ('map', 'rectangle'): 01360 self.pdcTmp.RemoveId(self.idResizeBoxTmp) 01361 self.Refresh() 01362 if self.instruction[self.dragId].type != 'line': 01363 for id in self.idLinePointsTmp: 01364 self.pdcTmp.RemoveId(id) 01365 self.Refresh() 01366 01367 else: 01368 self.dragId = -1 01369 self.pdcTmp.RemoveId(self.idBoxTmp) 01370 self.pdcTmp.RemoveId(self.idResizeBoxTmp) 01371 for id in self.idLinePointsTmp: 01372 self.pdcTmp.RemoveId(id) 01373 self.Refresh() 01374 01375 def OnLeftUp(self, event): 01376 """!Left mouse button released. 01377 01378 Recalculate zooming/resizing/moving and redraw. 01379 """ 01380 # zoom in, zoom out 01381 if self.mouse['use'] in ('zoomin','zoomout'): 01382 zoomR = self.pdcTmp.GetIdBounds(self.idZoomBoxTmp) 01383 self.pdcTmp.RemoveId(self.idZoomBoxTmp) 01384 self.Refresh() 01385 zoomFactor, view = self.ComputeZoom(zoomR) 01386 self.Zoom(zoomFactor, view) 01387 01388 # draw map frame 01389 if self.mouse['use'] == 'addMap': 01390 rectTmp = self.pdcTmp.GetIdBounds(self.idZoomBoxTmp) 01391 # too small rectangle, it's usually some mistake 01392 if rectTmp.GetWidth() < 20 or rectTmp.GetHeight() < 20: 01393 self.pdcTmp.RemoveId(self.idZoomBoxTmp) 01394 self.Refresh() 01395 return 01396 rectPaper = self.CanvasPaperCoordinates(rect = rectTmp, canvasToPaper = True) 01397 01398 dlg = MapDialog(parent = self.parent, id = [None, None, None], settings = self.instruction, 01399 rect = rectPaper) 01400 self.openDialogs['map'] = dlg 01401 self.openDialogs['map'].Show() 01402 01403 self.mouse['use'] = self.parent.mouseOld 01404 01405 self.SetCursor(self.parent.cursorOld) 01406 self.parent.toolbar.ToggleTool(self.parent.actionOld, True) 01407 self.parent.toolbar.ToggleTool(self.parent.toolbar.action['id'], False) 01408 self.parent.toolbar.action['id'] = self.parent.actionOld 01409 return 01410 01411 # resize resizable objects (map, line, rectangle) 01412 if self.mouse['use'] == 'resize': 01413 mapObj = self.instruction.FindInstructionByType('map') 01414 if not mapObj: 01415 mapObj = self.instruction.FindInstructionByType('initMap') 01416 mapId = mapObj.id 01417 01418 if self.dragId == mapId: 01419 # necessary to change either map frame (scaleType 0,1,2) or region (scaletype 3) 01420 newRectCanvas = self.pdcObj.GetIdBounds(mapId) 01421 newRectPaper = self.CanvasPaperCoordinates(rect = newRectCanvas, canvasToPaper = True) 01422 self.instruction[mapId]['rect'] = newRectPaper 01423 01424 if self.instruction[mapId]['scaleType'] in (0, 1, 2): 01425 if self.instruction[mapId]['scaleType'] == 0: 01426 01427 scale, foo, rect = AutoAdjust(self, scaleType = 0, 01428 map = self.instruction[mapId]['map'], 01429 mapType = self.instruction[mapId]['mapType'], 01430 rect = self.instruction[mapId]['rect']) 01431 01432 elif self.instruction[mapId]['scaleType'] == 1: 01433 scale, foo, rect = AutoAdjust(self, scaleType = 1, 01434 region = self.instruction[mapId]['region'], 01435 rect = self.instruction[mapId]['rect']) 01436 else: 01437 scale, foo, rect = AutoAdjust(self, scaleType = 2, 01438 rect = self.instruction[mapId]['rect']) 01439 self.instruction[mapId]['rect'] = rect 01440 self.instruction[mapId]['scale'] = scale 01441 01442 rectCanvas = self.CanvasPaperCoordinates(rect = rect, canvasToPaper = False) 01443 self.Draw(pen = self.pen['map'], brush = self.brush['map'], 01444 pdc = self.pdcObj, drawid = mapId, pdctype = 'rectText', bb = rectCanvas) 01445 01446 elif self.instruction[mapId]['scaleType'] == 3: 01447 ComputeSetRegion(self, mapDict = self.instruction[mapId].GetInstruction()) 01448 #check resolution 01449 SetResolution(dpi = self.instruction[mapId]['resolution'], 01450 width = self.instruction[mapId]['rect'].width, 01451 height = self.instruction[mapId]['rect'].height) 01452 01453 self.RedrawSelectBox(mapId) 01454 self.Zoom(zoomFactor = 1, view = (0, 0)) 01455 01456 elif self.instruction[self.dragId].type == 'line': 01457 points = self.instruction[self.dragId]['where'] 01458 self.instruction[self.dragId]['rect'] = Rect2DPP(points[0], points[1]) 01459 self.RecalculatePosition(ids = [self.dragId]) 01460 01461 elif self.instruction[self.dragId].type == 'rectangle': 01462 self.RecalculatePosition(ids = [self.dragId]) 01463 01464 self.mouse['use'] = 'pointer' 01465 01466 # recalculate the position of objects after dragging 01467 if self.mouse['use'] in ('pointer', 'resize') and self.dragId != -1: 01468 if self.mouse['begin'] != event.GetPosition(): #for double click 01469 01470 self.RecalculatePosition(ids = [self.dragId]) 01471 if self.instruction[self.dragId].type in self.openDialogs: 01472 self.openDialogs[self.instruction[self.dragId].type].updateDialog() 01473 01474 elif self.mouse['use'] in ('addPoint', 'addLine', 'addRectangle'): 01475 endCoordinates = self.CanvasPaperCoordinates(rect = wx.Rect(event.GetX(), event.GetY(), 0, 0), 01476 canvasToPaper = True)[:2] 01477 01478 diffX = event.GetX() - self.mouse['begin'][0] 01479 diffY = event.GetY() - self.mouse['begin'][1] 01480 01481 if self.mouse['use'] == 'addPoint': 01482 self.parent.AddPoint(coordinates = endCoordinates) 01483 elif self.mouse['use'] in ('addLine', 'addRectangle'): 01484 # not too small lines/rectangles 01485 if sqrt(diffX * diffX + diffY * diffY) < 5: 01486 self.pdcTmp.RemoveId(self.idZoomBoxTmp) 01487 self.Refresh() 01488 return 01489 01490 beginCoordinates = self.CanvasPaperCoordinates(rect = wx.Rect(self.mouse['begin'][0], 01491 self.mouse['begin'][1], 0, 0), 01492 canvasToPaper = True)[:2] 01493 if self.mouse['use'] == 'addLine': 01494 self.parent.AddLine(coordinates = [beginCoordinates, endCoordinates]) 01495 else: 01496 self.parent.AddRectangle(coordinates = [beginCoordinates, endCoordinates]) 01497 self.pdcTmp.RemoveId(self.idZoomBoxTmp) 01498 self.Refresh() 01499 01500 def OnButtonDClick(self, event): 01501 """!Open object dialog for editing.""" 01502 if self.mouse['use'] == 'pointer' and self.dragId != -1: 01503 itemCall = {'text':self.parent.OnAddText, 01504 'mapinfo': self.parent.OnAddMapinfo, 01505 'scalebar': self.parent.OnAddScalebar, 01506 'image': self.parent.OnAddImage, 01507 'northArrow' : self.parent.OnAddNorthArrow, 01508 'point': self.parent.AddPoint, 01509 'line': self.parent.AddLine, 01510 'rectangle': self.parent.AddRectangle, 01511 'rasterLegend': self.parent.OnAddLegend, 01512 'vectorLegend': self.parent.OnAddLegend, 01513 'map': self.parent.OnAddMap} 01514 01515 itemArg = { 'text': dict(event = None, id = self.dragId), 01516 'mapinfo': dict(event = None), 01517 'scalebar': dict(event = None), 01518 'image': dict(event = None, id = self.dragId), 01519 'northArrow': dict(event = None, id = self.dragId), 01520 'point': dict(id = self.dragId), 01521 'line': dict(id = self.dragId), 01522 'rectangle': dict(id = self.dragId), 01523 'rasterLegend': dict(event = None), 01524 'vectorLegend': dict(event = None, page = 1), 01525 'map': dict(event = None, notebook = True)} 01526 01527 type = self.instruction[self.dragId].type 01528 itemCall[type](**itemArg[type]) 01529 01530 def OnDragging(self, event): 01531 """!Process panning/resizing/drawing/moving.""" 01532 if event.MiddleIsDown(): 01533 # panning 01534 self.mouse['end'] = event.GetPosition() 01535 self.Pan(begin = self.mouse['begin'], end = self.mouse['end']) 01536 self.mouse['begin'] = event.GetPosition() 01537 01538 elif event.LeftIsDown(): 01539 # draw box when zooming, creating map 01540 if self.mouse['use'] in ('zoomin', 'zoomout', 'addMap', 'addLine', 'addRectangle'): 01541 self.mouse['end'] = event.GetPosition() 01542 r = wx.Rect(self.mouse['begin'][0], self.mouse['begin'][1], 01543 self.mouse['end'][0]-self.mouse['begin'][0], self.mouse['end'][1]-self.mouse['begin'][1]) 01544 r = self.modifyRectangle(r) 01545 01546 if self.mouse['use'] in ('addLine', 'addRectangle'): 01547 if self.mouse['use'] == 'addLine': 01548 pdcType = 'line' 01549 lineCoords = (self.mouse['begin'], self.mouse['end']) 01550 else: 01551 pdcType = 'rect' 01552 lineCoords = None 01553 if r[2] < 2 or r[3] < 2: 01554 # to avoid strange behavoiur 01555 return 01556 01557 self.Draw(pen = self.pen['line'], brush = self.brush['line'], 01558 pdc = self.pdcTmp, drawid = self.idZoomBoxTmp, 01559 pdctype = pdcType, bb = r, lineCoords = lineCoords) 01560 01561 else: 01562 self.Draw(pen = self.pen['box'], brush = self.brush['box'], 01563 pdc = self.pdcTmp, drawid = self.idZoomBoxTmp, 01564 pdctype = 'rect', bb = r) 01565 01566 # panning 01567 if self.mouse["use"] == 'pan': 01568 self.mouse['end'] = event.GetPosition() 01569 self.Pan(begin = self.mouse['begin'], end = self.mouse['end']) 01570 self.mouse['begin'] = event.GetPosition() 01571 01572 # move object 01573 if self.mouse['use'] == 'pointer' and self.dragId != -1: 01574 self.mouse['end'] = event.GetPosition() 01575 dx, dy = self.mouse['end'][0] - self.begin[0], self.mouse['end'][1] - self.begin[1] 01576 self.pdcObj.TranslateId(self.dragId, dx, dy) 01577 self.pdcTmp.TranslateId(self.idBoxTmp, dx, dy) 01578 self.pdcTmp.TranslateId(self.idResizeBoxTmp, dx, dy) 01579 for id in self.idLinePointsTmp: 01580 self.pdcTmp.TranslateId(id, dx, dy) 01581 if self.instruction[self.dragId].type == 'text': 01582 self.instruction[self.dragId]['coords'] = self.instruction[self.dragId]['coords'][0] + dx,\ 01583 self.instruction[self.dragId]['coords'][1] + dy 01584 self.begin = event.GetPosition() 01585 self.Refresh() 01586 01587 # resize object 01588 if self.mouse['use'] == 'resize': 01589 pos = event.GetPosition() 01590 diffX = pos[0] - self.mouse['begin'][0] 01591 diffY = pos[1] - self.mouse['begin'][1] 01592 if self.instruction[self.dragId].type == 'map': 01593 x, y = self.mapBounds.GetX(), self.mapBounds.GetY() 01594 width, height = self.mapBounds.GetWidth(), self.mapBounds.GetHeight() 01595 # match given region 01596 if self.constraint: 01597 if width > height: 01598 newWidth = width + diffX 01599 newHeight = height + diffX * (float(height) / width) 01600 else: 01601 newWidth = width + diffY * (float(width) / height) 01602 newHeight = height + diffY 01603 else: 01604 newWidth = width + diffX 01605 newHeight = height + diffY 01606 01607 if newWidth < 10 or newHeight < 10: 01608 return 01609 01610 bounds = wx.Rect(x, y, newWidth, newHeight) 01611 self.Draw(pen = self.pen['map'], brush = self.brush['map'], pdc = self.pdcObj, drawid = self.dragId, 01612 pdctype = 'rectText', bb = bounds) 01613 01614 elif self.instruction[self.dragId].type == 'rectangle': 01615 instr = self.instruction[self.dragId] 01616 rect = self.CanvasPaperCoordinates(rect = instr['rect'], canvasToPaper = False) 01617 rect.SetWidth(rect.GetWidth() + diffX) 01618 rect.SetHeight(rect.GetHeight() + diffY) 01619 01620 if rect.GetWidth() < 5 or rect.GetHeight() < 5: 01621 return 01622 01623 self.DrawGraphics(drawid = self.dragId, shape = 'rectangle', color = instr['color'], 01624 fcolor = instr['fcolor'], width = instr['width'], bb = rect) 01625 01626 elif self.instruction[self.dragId].type == 'line': 01627 instr = self.instruction[self.dragId] 01628 points = instr['where'] 01629 # moving point 01630 if self.currentLinePoint == 0: 01631 pPaper = points[1] 01632 else: 01633 pPaper = points[0] 01634 pCanvas = self.CanvasPaperCoordinates(rect = Rect2DPS(pPaper, (0, 0)), 01635 canvasToPaper = False)[:2] 01636 bounds = wx.RectPP(pCanvas, pos) 01637 self.DrawGraphics(drawid = self.dragId, shape = 'line', color = instr['color'], 01638 width = instr['width'], bb = bounds, lineCoords = (pos, pCanvas)) 01639 01640 # update paper coordinates 01641 points[self.currentLinePoint] = self.CanvasPaperCoordinates(rect = wx.RectPS(pos, (0, 0)), 01642 canvasToPaper = True)[:2] 01643 01644 self.RedrawSelectBox(self.dragId) 01645 01646 def OnMiddleDown(self, event): 01647 """!Middle mouse button pressed.""" 01648 self.mouse['begin'] = event.GetPosition() 01649 01650 def Pan(self, begin, end): 01651 """!Move canvas while dragging. 01652 01653 @param begin x,y coordinates of first point 01654 @param end x,y coordinates of second point 01655 """ 01656 view = begin[0] - end[0], begin[1] - end[1] 01657 zoomFactor = 1 01658 self.Zoom(zoomFactor, view) 01659 01660 def RecalculatePosition(self, ids): 01661 for id in ids: 01662 itype = self.instruction[id].type 01663 if itype in ('map', 'rectangle'): 01664 self.instruction[id]['rect'] = self.CanvasPaperCoordinates(rect = self.pdcObj.GetIdBounds(id), 01665 canvasToPaper = True) 01666 self.RecalculateEN() 01667 01668 elif itype in ('mapinfo' ,'rasterLegend', 'vectorLegend', 'image', 'northArrow'): 01669 self.instruction[id]['rect'] = self.CanvasPaperCoordinates(rect = self.pdcObj.GetIdBounds(id), 01670 canvasToPaper = True) 01671 self.instruction[id]['where'] = self.CanvasPaperCoordinates(rect = self.pdcObj.GetIdBounds(id), 01672 canvasToPaper = True)[:2] 01673 if itype in ('image', 'northArrow'): 01674 self.RecalculateEN() 01675 01676 elif itype == 'point': 01677 rect = self.pdcObj.GetIdBounds(id) 01678 self.instruction[id]['rect'] = self.CanvasPaperCoordinates(rect = rect, 01679 canvasToPaper = True) 01680 rect.OffsetXY(rect.GetWidth()/2, rect.GetHeight()/2) 01681 self.instruction[id]['where'] = self.CanvasPaperCoordinates(rect = rect, 01682 canvasToPaper = True)[:2] 01683 self.RecalculateEN() 01684 01685 elif itype == 'line': 01686 rect = self.pdcObj.GetIdBounds(id) 01687 oldRect = self.instruction[id]['rect'] 01688 newRect = self.CanvasPaperCoordinates(rect = rect, canvasToPaper = True) 01689 xDiff = newRect[0] - oldRect[0] 01690 yDiff = newRect[1] - oldRect[1] 01691 self.instruction[id]['rect'] = newRect 01692 01693 point1 = wx.Point2D(xDiff, yDiff) + self.instruction[id]['where'][0] 01694 point2 = wx.Point2D(xDiff, yDiff) + self.instruction[id]['where'][1] 01695 self.instruction[id]['where'] = [point1, point2] 01696 01697 self.RecalculateEN() 01698 01699 elif itype == 'scalebar': 01700 self.instruction[id]['rect'] = self.CanvasPaperCoordinates(rect = self.pdcObj.GetIdBounds(id), 01701 canvasToPaper = True) 01702 01703 01704 self.instruction[id]['where'] = self.instruction[id]['rect'].GetCentre() 01705 01706 elif itype == 'text': 01707 x, y = self.instruction[id]['coords'][0] - self.instruction[id]['xoffset'],\ 01708 self.instruction[id]['coords'][1] + self.instruction[id]['yoffset'] 01709 extent = self.parent.getTextExtent(textDict = self.instruction[id]) 01710 if self.instruction[id]['rotate'] is not None: 01711 rot = float(self.instruction[id]['rotate'])/180*pi 01712 else: 01713 rot = 0 01714 01715 if self.instruction[id]['ref'].split()[0] == 'lower': 01716 y += extent[1] 01717 elif self.instruction[id]['ref'].split()[0] == 'center': 01718 y += extent[1]/2 01719 if self.instruction[id]['ref'].split()[1] == 'right': 01720 x += extent[0] * cos(rot) 01721 y -= extent[0] * sin(rot) 01722 elif self.instruction[id]['ref'].split()[1] == 'center': 01723 x += extent[0]/2 * cos(rot) 01724 y -= extent[0]/2 * sin(rot) 01725 01726 self.instruction[id]['where'] = self.CanvasPaperCoordinates(rect = Rect2D(x, y, 0, 0), 01727 canvasToPaper = True)[:2] 01728 self.RecalculateEN() 01729 01730 def ComputeZoom(self, rect): 01731 """!Computes zoom factor and scroll view""" 01732 zoomFactor = 1 01733 cW, cH = self.GetClientSize() 01734 cW = float(cW) 01735 if rect.IsEmpty(): # clicked on canvas 01736 zoomFactor = 1.5 01737 if self.mouse['use'] == 'zoomout': 01738 zoomFactor = 1./zoomFactor 01739 x,y = self.mouse['begin'] 01740 xView = x - x/zoomFactor#x - cW/(zoomFactor * 2) 01741 yView = y - y/zoomFactor#y - cH/(zoomFactor * 2) 01742 01743 else: #dragging 01744 rW, rH = float(rect.GetWidth()), float(rect.GetHeight()) 01745 try: 01746 zoomFactor = 1/max(rW/cW, rH/cH) 01747 except ZeroDivisionError: 01748 zoomFactor = 1 01749 # when zooming to full extent, in some cases, there was zoom 1.01..., which causes problem 01750 if abs(zoomFactor - 1) > 0.01: 01751 zoomFactor = zoomFactor 01752 else: 01753 zoomFactor = 1. 01754 01755 01756 if self.mouse['use'] == 'zoomout': 01757 zoomFactor = min(rW/cW, rH/cH) 01758 try: 01759 if rW/rH > cW/cH: 01760 yView = rect.GetY() - (rW*(cH/cW) - rH)/2 01761 xView = rect.GetX() 01762 01763 if self.mouse['use'] == 'zoomout': 01764 x,y = rect.GetX() + (rW-(cW/cH)*rH)/2, rect.GetY() 01765 xView, yView = -x, -y 01766 else: 01767 xView = rect.GetX() - (rH*(cW/cH) - rW)/2 01768 yView = rect.GetY() 01769 if self.mouse['use'] == 'zoomout': 01770 x,y = rect.GetX(), rect.GetY() + (rH-(cH/cW)*rW)/2 01771 xView, yView = -x, -y 01772 except ZeroDivisionError: 01773 xView, yView = rect.GetX(), rect.GetY() 01774 return zoomFactor, (int(xView), int(yView)) 01775 01776 01777 def Zoom(self, zoomFactor, view): 01778 """! Zoom to specified region, scroll view, redraw""" 01779 if not self.currScale: 01780 return 01781 self.currScale = self.currScale*zoomFactor 01782 01783 if self.currScale > 10 or self.currScale < 0.1: 01784 self.currScale = self.currScale/zoomFactor 01785 return 01786 if not self.preview: 01787 # redraw paper 01788 pRect = self.pdcPaper.GetIdBounds(self.pageId) 01789 pRect.OffsetXY(-view[0], -view[1]) 01790 pRect = self.ScaleRect(rect = pRect, scale = zoomFactor) 01791 self.DrawPaper(pRect) 01792 01793 #redraw objects 01794 for id in self.objectId: 01795 oRect = self.CanvasPaperCoordinates( 01796 rect = self.instruction[id]['rect'], canvasToPaper = False) 01797 01798 type = self.instruction[id].type 01799 if type == 'text': 01800 coords = self.instruction[id]['coords']# recalculate coordinates, they are not equal to BB 01801 self.instruction[id]['coords'] = coords = [(int(coord) - view[i]) * zoomFactor 01802 for i, coord in enumerate(coords)] 01803 self.DrawRotText(pdc = self.pdcObj, drawId = id, textDict = self.instruction[id], 01804 coords = coords, bounds = oRect ) 01805 extent = self.parent.getTextExtent(textDict = self.instruction[id]) 01806 if self.instruction[id]['rotate']: 01807 rot = float(self.instruction[id]['rotate']) 01808 else: 01809 rot = 0 01810 01811 self.instruction[id]['rect'] = bounds = self.parent.getModifiedTextBounds(coords[0], coords[1], extent, rot) 01812 self.pdcObj.SetIdBounds(id, bounds) 01813 elif type == 'northArrow': 01814 self.Draw(pen = self.pen[type], brush = self.brush[type], pdc = self.pdcObj, 01815 drawid = id, pdctype = 'bitmap', bb = oRect) 01816 01817 elif type in ('point', 'line', 'rectangle'): 01818 instr = self.instruction[id] 01819 color = self.instruction[id]['color'] 01820 width = fcolor = coords = None 01821 01822 if type in ('point', 'rectangle'): 01823 fcolor = self.instruction[id]['fcolor'] 01824 if type in ('line', 'rectangle'): 01825 width = self.instruction[id]['width'] 01826 if type in ('line'): 01827 point1, point2 = instr['where'][0], instr['where'][1] 01828 point1 = self.CanvasPaperCoordinates(rect = Rect2DPS(point1, (0, 0)), 01829 canvasToPaper = False)[:2] 01830 point2 = self.CanvasPaperCoordinates(rect = Rect2DPS(point2, (0, 0)), 01831 canvasToPaper = False)[:2] 01832 coords = (point1, point2) 01833 01834 self.DrawGraphics(drawid = id, shape = type, bb = oRect, lineCoords = coords, 01835 color = color, fcolor = fcolor, width = width) 01836 01837 else: 01838 self.Draw(pen = self.pen[type], brush = self.brush[type], pdc = self.pdcObj, 01839 drawid = id, pdctype = 'rectText', bb = oRect) 01840 #redraw tmp objects 01841 if self.dragId != -1: 01842 self.RedrawSelectBox(self.dragId) 01843 01844 #redraw preview 01845 else: # preview mode 01846 imageRect = self.pdcImage.GetIdBounds(self.imageId) 01847 imageRect.OffsetXY(-view[0], -view[1]) 01848 imageRect = self.ScaleRect(rect = imageRect, scale = zoomFactor) 01849 self.DrawImage(imageRect) 01850 01851 def ZoomAll(self): 01852 """! Zoom to full extent""" 01853 if not self.preview: 01854 bounds = self.pdcPaper.GetIdBounds(self.pageId) 01855 else: 01856 bounds = self.pdcImage.GetIdBounds(self.imageId) 01857 zoomP = bounds.Inflate(bounds.width/20, bounds.height/20) 01858 zoomFactor, view = self.ComputeZoom(zoomP) 01859 self.Zoom(zoomFactor, view) 01860 01861 def Draw(self, pen, brush, pdc, drawid = None, pdctype = 'rect', bb = wx.Rect(0,0,0,0), lineCoords = None): 01862 """! Draw object with given pen and brush. 01863 01864 @param pdc PseudoDC 01865 @param pdctype 'bitmap'/'rectText'/'rect'/'point'/'line' 01866 @param bb bounding box 01867 @param lineCoords coordinates of line start, end points (wx.Point, wx.Point) 01868 """ 01869 if drawid is None: 01870 drawid = wx.NewId() 01871 bb = bb.Get() 01872 pdc.BeginDrawing() 01873 pdc.RemoveId(drawid) 01874 pdc.SetId(drawid) 01875 pdc.SetPen(pen) 01876 pdc.SetBrush(brush) 01877 01878 if pdctype == 'bitmap': 01879 if havePILImage: 01880 file = self.instruction[drawid]['epsfile'] 01881 rotation = self.instruction[drawid]['rotate'] 01882 self.DrawBitmap(pdc = pdc, filePath = file, rotation = rotation, bbox = bb) 01883 else: # draw only rectangle with label 01884 pdctype = 'rectText' 01885 01886 if pdctype in ('rect', 'rectText'): 01887 pdc.DrawRectangle(*bb) 01888 01889 if pdctype == 'rectText': 01890 dc = wx.ClientDC(self) # dc created because of method GetTextExtent, which pseudoDC lacks 01891 font = self.font 01892 size = 10 01893 font.SetPointSize(size) 01894 font.SetStyle(wx.ITALIC) 01895 dc.SetFont(font) 01896 pdc.SetFont(font) 01897 text = '\n'.join(self.itemLabels[drawid]) 01898 w,h,lh = dc.GetMultiLineTextExtent(text) 01899 textExtent = (w,h) 01900 textRect = wx.Rect(0, 0, *textExtent).CenterIn(bb) 01901 r = map(int, bb) 01902 while not wx.Rect(*r).ContainsRect(textRect) and size >= 8: 01903 size -= 2 01904 font.SetPointSize(size) 01905 dc.SetFont(font) 01906 pdc.SetFont(font) 01907 textExtent = dc.GetTextExtent(text) 01908 textRect = wx.Rect(0, 0, *textExtent).CenterIn(bb) 01909 pdc.SetTextForeground(wx.Color(100,100,100,200)) 01910 pdc.SetBackgroundMode(wx.TRANSPARENT) 01911 pdc.DrawText(text = text, x = textRect.x, y = textRect.y) 01912 01913 elif pdctype == 'point': 01914 pdc.DrawCircle(x = bb[0] + bb[2] / 2, 01915 y = bb[1] + bb[3] / 2, 01916 radius = bb[2] / 2) 01917 01918 elif pdctype == 'line': 01919 pdc.DrawLinePoint(lineCoords[0], lineCoords[1]) 01920 01921 pdc.SetIdBounds(drawid, bb) 01922 pdc.EndDrawing() 01923 self.Refresh() 01924 01925 return drawid 01926 01927 def DrawGraphics(self, drawid, shape, color, bb, width = None, fcolor = None, lineCoords = None): 01928 """!Draw point/line/rectangle with given color and width 01929 01930 @param drawid id of drawn object 01931 @param shape drawn shape: 'point'/'line'/'rectangle' 01932 @param color pen outline color ('RRR:GGG:BBB') 01933 @param fcolor brush fill color, if meaningful ('RRR:GGG:BBB') 01934 @param width pen width 01935 @param bb bounding box 01936 @param lineCoords line coordinates (for line only) 01937 """ 01938 pdctype = {'point' : 'point', 01939 'line' : 'line', 01940 'rectangle' : 'rect'} 01941 01942 if color == 'none': 01943 pen = wx.TRANSPARENT_PEN 01944 else: 01945 if width is not None: 01946 units = UnitConversion(self) 01947 width = int(units.convert(value = width, fromUnit = 'point', toUnit = 'pixel') * self.currScale) 01948 else: 01949 width = 2 01950 pen = wx.Pen(colour = convertRGB(color), width = width) 01951 pen.SetCap(wx.CAP_BUTT) # this is how ps.map draws 01952 01953 brush = wx.TRANSPARENT_BRUSH 01954 if fcolor and fcolor != 'none': 01955 brush = wx.Brush(colour = convertRGB(fcolor)) 01956 01957 self.Draw(pen = pen, brush = brush, pdc = self.pdcObj, pdctype = pdctype[shape], 01958 drawid = drawid, bb = bb, lineCoords = lineCoords) 01959 01960 def DrawBitmap(self, pdc, filePath, rotation, bbox): 01961 """!Draw bitmap using PIL""" 01962 pImg = PILImage.open(filePath) 01963 if rotation: 01964 # get rid of black background 01965 pImg = pImg.convert("RGBA") 01966 rot = pImg.rotate(rotation, expand = 1) 01967 new = PILImage.new('RGBA', rot.size, (255,) * 4) 01968 pImg = PILImage.composite(rot, new, rot) 01969 pImg = pImg.resize((int(bbox[2]), int(bbox[3])), resample = PILImage.BICUBIC) 01970 img = PilImageToWxImage(pImg) 01971 bitmap = img.ConvertToBitmap() 01972 mask = wx.Mask(bitmap, wx.WHITE) 01973 bitmap.SetMask(mask) 01974 pdc.DrawBitmap(bitmap, bbox[0], bbox[1], useMask = True) 01975 01976 def DrawRotText(self, pdc, drawId, textDict, coords, bounds): 01977 if textDict['rotate']: 01978 rot = float(textDict['rotate']) 01979 else: 01980 rot = 0 01981 01982 fontsize = textDict['fontsize'] * self.currScale 01983 if textDict['background'] != 'none': 01984 background = textDict['background'] 01985 else: 01986 background = None 01987 01988 pdc.RemoveId(drawId) 01989 pdc.SetId(drawId) 01990 pdc.BeginDrawing() 01991 01992 # border is not redrawn when zoom changes, why? 01993 ## if textDict['border'] != 'none' and not rot: 01994 ## units = UnitConversion(self) 01995 ## borderWidth = units.convert(value = textDict['width'], 01996 ## fromUnit = 'point', toUnit = 'pixel' ) * self.currScale 01997 ## pdc.SetPen(wx.Pen(colour = convertRGB(textDict['border']), width = borderWidth)) 01998 ## pdc.DrawRectangle(*bounds) 01999 02000 if background: 02001 pdc.SetTextBackground(convertRGB(background)) 02002 pdc.SetBackgroundMode(wx.SOLID) 02003 else: 02004 pdc.SetBackgroundMode(wx.TRANSPARENT) 02005 02006 fn = self.parent.makePSFont(textDict) 02007 02008 pdc.SetFont(fn) 02009 pdc.SetTextForeground(convertRGB(textDict['color'])) 02010 pdc.DrawRotatedText(textDict['text'], coords[0], coords[1], rot) 02011 02012 pdc.SetIdBounds(drawId, wx.Rect(*bounds)) 02013 self.Refresh() 02014 pdc.EndDrawing() 02015 02016 def DrawImage(self, rect): 02017 """!Draw preview image to pseudoDC""" 02018 self.pdcImage.ClearId(self.imageId) 02019 self.pdcImage.SetId(self.imageId) 02020 img = self.image 02021 02022 02023 if img.GetWidth() != rect.width or img.GetHeight() != rect.height: 02024 img = img.Scale(rect.width, rect.height) 02025 bitmap = img.ConvertToBitmap() 02026 02027 self.pdcImage.BeginDrawing() 02028 self.pdcImage.DrawBitmap(bitmap, rect.x, rect.y) 02029 self.pdcImage.SetIdBounds(self.imageId, rect) 02030 self.pdcImage.EndDrawing() 02031 self.Refresh() 02032 02033 def DrawPaper(self, rect): 02034 """!Draw paper and margins""" 02035 page = self.instruction[self.pageId] 02036 scale = page['Width'] / rect.GetWidth() 02037 w = (page['Width'] - page['Right'] - page['Left']) / scale 02038 h = (page['Height'] - page['Top'] - page['Bottom']) / scale 02039 x = page['Left'] / scale + rect.GetX() 02040 y = page['Top'] / scale + rect.GetY() 02041 02042 self.pdcPaper.BeginDrawing() 02043 self.pdcPaper.RemoveId(self.pageId) 02044 self.pdcPaper.SetId(self.pageId) 02045 self.pdcPaper.SetPen(self.pen['paper']) 02046 self.pdcPaper.SetBrush(self.brush['paper']) 02047 self.pdcPaper.DrawRectangleRect(rect) 02048 02049 self.pdcPaper.SetPen(self.pen['margins']) 02050 self.pdcPaper.SetBrush(self.brush['margins']) 02051 self.pdcPaper.DrawRectangle(x, y, w, h) 02052 02053 self.pdcPaper.SetIdBounds(self.pageId, rect) 02054 self.pdcPaper.EndDrawing() 02055 self.Refresh() 02056 02057 02058 def ImageRect(self): 02059 """!Returns image centered in canvas, computes scale""" 02060 img = wx.Image(self.imgName, wx.BITMAP_TYPE_PNG) 02061 cW, cH = self.GetClientSize() 02062 iW, iH = img.GetWidth(), img.GetHeight() 02063 02064 self.currScale = min(float(cW)/iW, float(cH)/iH) 02065 iW = iW * self.currScale 02066 iH = iH * self.currScale 02067 x = cW/2 - iW/2 02068 y = cH/2 - iH/2 02069 imageRect = wx.Rect(x, y, iW, iH) 02070 02071 return imageRect 02072 02073 def RedrawSelectBox(self, id): 02074 """!Redraws select box when selected object changes its size""" 02075 if self.dragId == id: 02076 rect = self.pdcObj.GetIdBounds(id) 02077 if self.instruction[id].type != 'line': 02078 rect = rect.Inflate(3,3) 02079 # draw select box around object 02080 self.Draw(pen = self.pen['select'], brush = self.brush['select'], pdc = self.pdcTmp, 02081 drawid = self.idBoxTmp, pdctype = 'rect', bb = rect) 02082 02083 # draw small marks signalizing resizing 02084 if self.instruction[id].type in ('map', 'rectangle'): 02085 controlP = self.pdcObj.GetIdBounds(id).GetBottomRight() 02086 rect = wx.RectPS(controlP, self.resizeBoxSize) 02087 self.Draw(pen = self.pen['resize'], brush = self.brush['resize'], pdc = self.pdcTmp, 02088 drawid = self.idResizeBoxTmp, pdctype = 'rect', bb = rect) 02089 02090 elif self.instruction[id].type == 'line': 02091 p1Paper = self.instruction[id]['where'][0] 02092 p2Paper = self.instruction[id]['where'][1] 02093 p1Canvas = self.CanvasPaperCoordinates(rect = Rect2DPS(p1Paper, (0, 0)), canvasToPaper = False)[:2] 02094 p2Canvas = self.CanvasPaperCoordinates(rect = Rect2DPS(p2Paper, (0, 0)), canvasToPaper = False)[:2] 02095 rect = [] 02096 box = wx.RectS(self.resizeBoxSize) 02097 rect.append(box.CenterIn(wx.RectPS(p1Canvas, wx.Size()))) 02098 rect.append(box.CenterIn(wx.RectPS(p2Canvas, wx.Size()))) 02099 for i, point in enumerate((p1Canvas, p2Canvas)): 02100 self.Draw(pen = self.pen['resize'], brush = self.brush['resize'], pdc = self.pdcTmp, 02101 drawid = self.idLinePointsTmp[i], pdctype = 'rect', bb = rect[i]) 02102 02103 def UpdateMapLabel(self): 02104 """!Updates map frame label""" 02105 02106 vector = self.instruction.FindInstructionByType('vector') 02107 if vector: 02108 vectorId = vector.id 02109 else: 02110 vectorId = None 02111 02112 raster = self.instruction.FindInstructionByType('raster') 02113 if raster: 02114 rasterId = raster.id 02115 else: 02116 rasterId = None 02117 02118 rasterName = 'None' 02119 if rasterId: 02120 rasterName = self.instruction[rasterId]['raster'].split('@')[0] 02121 02122 mapId = self.instruction.FindInstructionByType('map').id 02123 self.itemLabels[mapId] = [] 02124 self.itemLabels[mapId].append(self.itemLabelsDict['map']) 02125 self.itemLabels[mapId].append("raster: " + rasterName) 02126 if vectorId: 02127 for map in self.instruction[vectorId]['list']: 02128 self.itemLabels[mapId].append('vector: ' + map[0].split('@')[0]) 02129 02130 def UpdateLabel(self, itype, id): 02131 self.itemLabels[id] = [] 02132 self.itemLabels[id].append(self.itemLabelsDict[itype]) 02133 if itype == 'image': 02134 file = os.path.basename(self.instruction[id]['epsfile']) 02135 self.itemLabels[id].append(file) 02136 02137 def OnSize(self, event): 02138 """!Init image size to match window size 02139 """ 02140 # not zoom all when notebook page is changed 02141 if self.preview and self.parent.currentPage == 1 or not self.preview and self.parent.currentPage == 0: 02142 self.ZoomAll() 02143 self.OnIdle(None) 02144 event.Skip() 02145 02146 def OnIdle(self, event): 02147 """!Only re-render a image during idle time instead of 02148 multiple times during resizing. 02149 """ 02150 02151 width, height = self.GetClientSize() 02152 # Make new off screen bitmap: this bitmap will always have the 02153 # current drawing in it, so it can be used to save the image 02154 # to a file, or whatever. 02155 self._buffer = wx.EmptyBitmap(width, height) 02156 # re-render image on idle 02157 self.resize = True 02158 02159 def ScaleRect(self, rect, scale): 02160 """! Scale rectangle""" 02161 return wx.Rect(rect.GetLeft()*scale, rect.GetTop()*scale, 02162 rect.GetSize()[0]*scale, rect.GetSize()[1]*scale) 02163 02164 def main(): 02165 import gettext 02166 gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True) 02167 02168 app = wx.PySimpleApp() 02169 wx.InitAllImageHandlers() 02170 frame = PsMapFrame() 02171 frame.Show() 02172 02173 app.MainLoop() 02174 02175 if __name__ == "__main__": 02176 main()