psmap.py 71 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714
  1. """!
  2. @package psmap.py
  3. @brief GUI for ps.map
  4. Classes:
  5. - PsMapFrame
  6. - PsMapBufferedWindow
  7. (C) 2011 by Anna Kratochvilova, and the GRASS Development Team
  8. This program is free software under the GNU General Public License
  9. (>=v2). Read the file COPYING that comes with GRASS for details.
  10. @author Anna Kratochvilova <kratochanna gmail.com> (bachelor's project)
  11. @author Martin Landa <landa.martin gmail.com> (mentor)
  12. """
  13. import os
  14. import sys
  15. import textwrap
  16. import Queue
  17. try:
  18. import Image
  19. haveImage = True
  20. except ImportError:
  21. haveImage = False
  22. from math import sin, cos, pi
  23. import grass.script as grass
  24. if int(grass.version()['version'].split('.')[0]) > 6:
  25. sys.path.append(os.path.join(os.getenv('GISBASE'), 'etc', 'gui', 'wxpython',
  26. 'gui_modules'))
  27. else:
  28. sys.path.append(os.path.join(os.getenv('GISBASE'), 'etc', 'wxpython',
  29. 'gui_modules'))
  30. import globalvar
  31. import menu
  32. from goutput import CmdThread, EVT_CMD_DONE
  33. from menudata import PsMapData
  34. from toolbars import PsMapToolbar
  35. from icon import Icons, MetaIcon, iconSet
  36. from gcmd import RunCommand, GError, GMessage
  37. from menuform import GUI
  38. from psmap_dialogs import *
  39. import wx
  40. try:
  41. import wx.lib.agw.flatnotebook as fnb
  42. except ImportError:
  43. import wx.lib.flatnotebook as fnb
  44. class PsMapFrame(wx.Frame):
  45. def __init__(self, parent = None, id = wx.ID_ANY,
  46. title = _("GRASS GIS Cartographic Composer"), **kwargs):
  47. """!Main window of ps.map GUI
  48. @param parent parent window
  49. @param id window id
  50. @param title window title
  51. @param kwargs wx.Frames' arguments
  52. """
  53. self.parent = parent
  54. wx.Frame.__init__(self, parent = parent, id = id, title = title, name = "PsMap", **kwargs)
  55. self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
  56. #menubar
  57. self.menubar = menu.Menu(parent = self, data = PsMapData())
  58. self.SetMenuBar(self.menubar)
  59. #toolbar
  60. self.toolbar = PsMapToolbar(parent = self)
  61. self.SetToolBar(self.toolbar)
  62. self.actionOld = self.toolbar.action['id']
  63. self.iconsize = (16, 16)
  64. #satusbar
  65. self.statusbar = self.CreateStatusBar(number = 1)
  66. # mouse attributes -- position on the screen, begin and end of
  67. # dragging, and type of drawing
  68. self.mouse = {
  69. 'begin': [0, 0], # screen coordinates
  70. 'end' : [0, 0],
  71. 'use' : "pointer",
  72. }
  73. # available cursors
  74. self.cursors = {
  75. "default" : wx.StockCursor(wx.CURSOR_ARROW),
  76. "cross" : wx.StockCursor(wx.CURSOR_CROSS),
  77. "hand" : wx.StockCursor(wx.CURSOR_HAND),
  78. "sizenwse": wx.StockCursor(wx.CURSOR_SIZENWSE)
  79. }
  80. # pen and brush
  81. self.pen = {
  82. 'paper': wx.Pen(colour = "BLACK", width = 1),
  83. 'margins': wx.Pen(colour = "GREY", width = 1),
  84. 'map': wx.Pen(colour = wx.Color(86, 122, 17), width = 2),
  85. 'rasterLegend': wx.Pen(colour = wx.Color(219, 216, 4), width = 2),
  86. 'vectorLegend': wx.Pen(colour = wx.Color(219, 216, 4), width = 2),
  87. 'mapinfo': wx.Pen(colour = wx.Color(5, 184, 249), width = 2),
  88. 'scalebar': wx.Pen(colour = wx.Color(150, 150, 150), width = 2),
  89. 'box': wx.Pen(colour = 'RED', width = 2, style = wx.SHORT_DASH),
  90. 'select': wx.Pen(colour = 'BLACK', width = 1, style = wx.SHORT_DASH),
  91. 'resize': wx.Pen(colour = 'BLACK', width = 1)
  92. }
  93. self.brush = {
  94. 'paper': wx.WHITE_BRUSH,
  95. 'margins': wx.TRANSPARENT_BRUSH,
  96. 'map': wx.Brush(wx.Color(151, 214, 90)),
  97. 'rasterLegend': wx.Brush(wx.Color(250, 247, 112)),
  98. 'vectorLegend': wx.Brush(wx.Color(250, 247, 112)),
  99. 'mapinfo': wx.Brush(wx.Color(127, 222, 252)),
  100. 'scalebar': wx.Brush(wx.Color(200, 200, 200)),
  101. 'box': wx.TRANSPARENT_BRUSH,
  102. 'select':wx.TRANSPARENT_BRUSH,
  103. 'resize': wx.BLACK_BRUSH
  104. }
  105. # list of objects to draw
  106. self.objectId = []
  107. # instructions
  108. self.instruction = Instruction(parent = self, objectsToDraw = self.objectId)
  109. # open dialogs
  110. self.openDialogs = dict()
  111. self.pageId = wx.NewId()
  112. #current page of flatnotebook
  113. self.currentPage = 0
  114. #canvas for draft mode
  115. self.canvas = PsMapBufferedWindow(parent = self, mouse = self.mouse, pen = self.pen,
  116. brush = self.brush, cursors = self.cursors,
  117. instruction = self.instruction, openDialogs = self.openDialogs,
  118. pageId = self.pageId, objectId = self.objectId,
  119. preview = False)
  120. self.canvas.SetCursor(self.cursors["default"])
  121. self.getInitMap()
  122. # image path
  123. env = grass.gisenv()
  124. self.imgName = os.path.join(env['GISDBASE'], env['LOCATION_NAME'], env['MAPSET'], '.tmp', 'tmpImage.png')
  125. #canvas for preview
  126. self.previewCanvas = PsMapBufferedWindow(parent = self, mouse = self.mouse, cursors = self.cursors,
  127. pen = self.pen, brush = self.brush, preview = True)
  128. # set WIND_OVERRIDE
  129. grass.use_temp_region()
  130. # create queues
  131. self.requestQ = Queue.Queue()
  132. self.resultQ = Queue.Queue()
  133. # thread
  134. self.cmdThread = CmdThread(self, self.requestQ, self.resultQ)
  135. self._layout()
  136. self.SetMinSize(wx.Size(750, 600))
  137. self.Bind(fnb.EVT_FLATNOTEBOOK_PAGE_CHANGING, self.OnPageChanging)
  138. self.Bind(fnb.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
  139. self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
  140. self.Bind(EVT_CMD_DONE, self.OnCmdDone)
  141. if not haveImage:
  142. wx.CallAfter(self._showErrMsg)
  143. def _showErrMsg(self):
  144. """!Show error message (missing preview)
  145. """
  146. GError(parent = self,
  147. message = _("Python Imaging Library is not available.\n"
  148. "'Preview' functionality won't work."),
  149. showTraceback = False)
  150. def _layout(self):
  151. """!Do layout
  152. """
  153. mainSizer = wx.BoxSizer(wx.VERTICAL)
  154. if globalvar.hasAgw:
  155. self.book = fnb.FlatNotebook(parent = self, id = wx.ID_ANY,
  156. agwStyle = fnb.FNB_FANCY_TABS | fnb.FNB_BOTTOM |
  157. fnb.FNB_NO_NAV_BUTTONS | fnb.FNB_NO_X_BUTTON)
  158. else:
  159. self.book = fnb.FlatNotebook(parent = self, id = wx.ID_ANY,
  160. style = fnb.FNB_FANCY_TABS | fnb.FNB_BOTTOM |
  161. fnb.FNB_NO_NAV_BUTTONS | fnb.FNB_NO_X_BUTTON)
  162. #self.book = fnb.FlatNotebook(self, wx.ID_ANY, style = fnb.FNB_BOTTOM)
  163. self.book.AddPage(self.canvas, "Draft mode")
  164. self.book.AddPage(self.previewCanvas, "Preview")
  165. self.book.SetSelection(0)
  166. mainSizer.Add(self.book,1, wx.EXPAND)
  167. self.SetSizer(mainSizer)
  168. mainSizer.Fit(self)
  169. def InstructionFile(self):
  170. """!Creates mapping instructions"""
  171. return str(self.instruction)
  172. def OnPSFile(self, event):
  173. """!Generate PostScript"""
  174. filename = self.getFile(wildcard = "PostScript (*.ps)|*.ps|Encapsulated PostScript (*.eps)|*.eps")
  175. if filename:
  176. self.PSFile(filename)
  177. def OnPsMapDialog(self, event):
  178. """!Launch ps.map dialog
  179. """
  180. GUI(parent = self).ParseCommand(cmd = ['ps.map'])
  181. def OnPDFFile(self, event):
  182. """!Generate PDF from PS with ps2pdf if available"""
  183. try:
  184. p = grass.Popen(["ps2pdf"], stderr = grass.PIPE)
  185. p.stderr.close()
  186. except OSError:
  187. GMessage(parent = self,
  188. message = _("Program ps2pdf is not available. Please install it first to create PDF."))
  189. return
  190. filename = self.getFile(wildcard = "PDF (*.pdf)|*.pdf")
  191. if filename:
  192. self.PSFile(filename, pdf = True)
  193. def OnPreview(self, event):
  194. """!Run ps.map and show result"""
  195. self.PSFile()
  196. def PSFile(self, filename = None, pdf = False):
  197. """!Create temporary instructions file and run ps.map with output = filename"""
  198. instrFile = grass.tempfile()
  199. instrFileFd = open(instrFile, mode = 'w')
  200. instrFileFd.write(self.InstructionFile())
  201. instrFileFd.flush()
  202. instrFileFd.close()
  203. temp = False
  204. regOld = grass.region()
  205. if pdf:
  206. pdfname = filename
  207. else:
  208. pdfname = None
  209. #preview or pdf
  210. if not filename or (filename and pdf):
  211. temp = True
  212. filename = grass.tempfile()
  213. if not pdf: # lower resolution for preview
  214. if self.instruction.FindInstructionByType('map'):
  215. mapId = self.instruction.FindInstructionByType('map').id
  216. SetResolution(dpi = 100, width = self.instruction[mapId]['rect'][2],
  217. height = self.instruction[mapId]['rect'][3])
  218. cmd = ['ps.map', '--overwrite']
  219. if os.path.splitext(filename)[1] == '.eps':
  220. cmd.append('-e')
  221. if self.instruction[self.pageId]['Orientation'] == 'Landscape':
  222. cmd.append('-r')
  223. cmd.append('input=%s' % instrFile)
  224. cmd.append('output=%s' % filename)
  225. if pdf:
  226. self.SetStatusText(_('Generating PDF...'), 0)
  227. elif not temp:
  228. self.SetStatusText(_('Generating PostScript...'), 0)
  229. else:
  230. self.SetStatusText(_('Generating preview...'), 0)
  231. self.cmdThread.RunCmd(cmd, userData = {'instrFile' : instrFile, 'filename' : filename,
  232. 'pdfname' : pdfname, 'temp' : temp, 'regionOld' : regOld})
  233. def OnCmdDone(self, event):
  234. """!ps.map process finished"""
  235. if event.returncode != 0:
  236. GMessage(parent = self,
  237. message = _("Ps.map exited with return code %s") % event.returncode)
  238. grass.try_remove(event.userData['instrFile'])
  239. if event.userData['temp']:
  240. grass.try_remove(event.userData['filename'])
  241. return
  242. if event.userData['pdfname']:
  243. try:
  244. proc = grass.Popen(['ps2pdf', '-dPDFSETTINGS=/prepress', '-r1200',
  245. event.userData['filename'], event.userData['pdfname']])
  246. ret = proc.wait()
  247. if ret > 0:
  248. GMessage(parent = self,
  249. message = _("ps2pdf exited with return code %s") % ret)
  250. except OSError, e:
  251. GError(parent = self,
  252. message = _("Program ps2pdf is not available. Please install it to create PDF.\n\n %s") % e)
  253. # show preview only when user doesn't want to create ps or pdf
  254. if haveImage and event.userData['temp'] and not event.userData['pdfname']:
  255. RunCommand('g.region', cols = event.userData['regionOld']['cols'], rows = event.userData['regionOld']['rows'])
  256. ## wx.BusyInfo does not display the message
  257. ## busy = wx.BusyInfo(message = "Generating preview, wait please", parent = self)
  258. try:
  259. im = Image.open(event.userData['filename'])
  260. if self.instruction[self.pageId]['Orientation'] == 'Landscape':
  261. im = im.rotate(270)
  262. im.save(self.imgName, format = 'png')
  263. except IOError, e:
  264. GError(parent = self,
  265. message = _("Unable to generate preview. %s") % e)
  266. rect = self.previewCanvas.ImageRect()
  267. self.previewCanvas.image = wx.Image(self.imgName, wx.BITMAP_TYPE_PNG)
  268. self.previewCanvas.DrawImage(rect = rect)
  269. ## busy.Destroy()
  270. self.SetStatusText(_('Preview generated'), 0)
  271. self.book.SetSelection(1)
  272. self.currentPage = 1
  273. grass.try_remove(event.userData['instrFile'])
  274. if event.userData['temp']:
  275. grass.try_remove(event.userData['filename'])
  276. def getFile(self, wildcard):
  277. suffix = []
  278. for filter in wildcard.split('|')[1::2]:
  279. s = filter.strip('*').split('.')[1]
  280. if s:
  281. s = '.' + s
  282. suffix.append(s)
  283. raster = self.instruction.FindInstructionByType('raster')
  284. if raster:
  285. rasterId = raster.id
  286. else:
  287. rasterId = None
  288. if rasterId and self.instruction[rasterId]['raster']:
  289. mapName = self.instruction[rasterId]['raster'].split('@')[0] + suffix[0]
  290. else:
  291. mapName = ''
  292. filename = ''
  293. dlg = wx.FileDialog(self, message = _("Save file as"), defaultDir = "",
  294. defaultFile = mapName, wildcard = wildcard,
  295. style = wx.CHANGE_DIR | wx.SAVE | wx.OVERWRITE_PROMPT)
  296. if dlg.ShowModal() == wx.ID_OK:
  297. filename = dlg.GetPath()
  298. suffix = suffix[dlg.GetFilterIndex()]
  299. if not os.path.splitext(filename)[1]:
  300. filename = filename + suffix
  301. elif os.path.splitext(filename)[1] != suffix and suffix != '':
  302. filename = os.path.splitext(filename)[0] + suffix
  303. dlg.Destroy()
  304. return filename
  305. def OnInstructionFile(self, event):
  306. filename = self.getFile(wildcard = "*.psmap|*.psmap|Text file(*.txt)|*.txt|All files(*.*)|*.*")
  307. if filename:
  308. instrFile = open(filename, "w")
  309. instrFile.write(self.InstructionFile())
  310. instrFile.close()
  311. def OnLoadFile(self, event):
  312. """!Load file and read instructions"""
  313. #find file
  314. filename = ''
  315. dlg = wx.FileDialog(self, message = "Find instructions file", defaultDir = "",
  316. defaultFile = '', wildcard = "All files (*.*)|*.*",
  317. style = wx.CHANGE_DIR|wx.OPEN)
  318. if dlg.ShowModal() == wx.ID_OK:
  319. filename = dlg.GetPath()
  320. dlg.Destroy()
  321. if not filename:
  322. return
  323. # load instructions
  324. readObjectId = []
  325. readInstruction = Instruction(parent = self, objectsToDraw = readObjectId)
  326. ok = readInstruction.Read(filename)
  327. if not ok:
  328. GMessage(_("Failed to read file %s.") % filename)
  329. else:
  330. self.instruction = self.canvas.instruction = readInstruction
  331. self.objectId = self.canvas.objectId = readObjectId
  332. self.pageId = self.canvas.pageId = self.instruction.FindInstructionByType('page').id
  333. self.canvas.UpdateMapLabel()
  334. self.canvas.dragId = -1
  335. self.canvas.Clear()
  336. self.canvas.SetPage()
  337. #self.canvas.ZoomAll()
  338. self.DialogDataChanged(self.objectId)
  339. def OnPageSetup(self, event = None):
  340. """!Specify paper size, margins and orientation"""
  341. id = self.instruction.FindInstructionByType('page').id
  342. dlg = PageSetupDialog(self, id = id, settings = self.instruction)
  343. dlg.CenterOnScreen()
  344. val = dlg.ShowModal()
  345. if val == wx.ID_OK:
  346. self.canvas.SetPage()
  347. self.getInitMap()
  348. self.canvas.RecalculatePosition(ids = self.objectId)
  349. dlg.Destroy()
  350. def OnPointer(self, event):
  351. self.toolbar.OnTool(event)
  352. self.mouse["use"] = "pointer"
  353. self.canvas.SetCursor(self.cursors["default"])
  354. self.previewCanvas.SetCursor(self.cursors["default"])
  355. def OnPan(self, event):
  356. self.toolbar.OnTool(event)
  357. self.mouse["use"] = "pan"
  358. self.canvas.SetCursor(self.cursors["hand"])
  359. self.previewCanvas.SetCursor(self.cursors["hand"])
  360. def OnZoomIn(self, event):
  361. self.toolbar.OnTool(event)
  362. self.mouse["use"] = "zoomin"
  363. self.canvas.SetCursor(self.cursors["cross"])
  364. self.previewCanvas.SetCursor(self.cursors["cross"])
  365. def OnZoomOut(self, event):
  366. self.toolbar.OnTool(event)
  367. self.mouse["use"] = "zoomout"
  368. self.canvas.SetCursor(self.cursors["cross"])
  369. self.previewCanvas.SetCursor(self.cursors["cross"])
  370. def OnZoomAll(self, event):
  371. self.mouseOld = self.mouse['use']
  372. if self.currentPage == 0:
  373. self.cursorOld = self.canvas.GetCursor()
  374. else:
  375. self.cursorOld = self.previewCanvas.GetCursor()
  376. self.previewCanvas.GetCursor()
  377. self.mouse["use"] = "zoomin"
  378. if self.currentPage == 0:
  379. self.canvas.ZoomAll()
  380. else:
  381. self.previewCanvas.ZoomAll()
  382. self.mouse["use"] = self.mouseOld
  383. if self.currentPage == 0:
  384. self.canvas.SetCursor(self.cursorOld)
  385. else:
  386. self.previewCanvas.SetCursor(self.cursorOld)
  387. def OnAddMap(self, event, notebook = False):
  388. """!Add or edit map frame"""
  389. if event is not None:
  390. if event.GetId() != self.toolbar.action['id']:
  391. self.actionOld = self.toolbar.action['id']
  392. self.mouseOld = self.mouse['use']
  393. self.cursorOld = self.canvas.GetCursor()
  394. self.toolbar.OnTool(event)
  395. if self.instruction.FindInstructionByType('map'):
  396. mapId = self.instruction.FindInstructionByType('map').id
  397. else: mapId = None
  398. id = [mapId, None, None]
  399. if notebook:
  400. if self.instruction.FindInstructionByType('vector'):
  401. vectorId = self.instruction.FindInstructionByType('vector').id
  402. else: vectorId = None
  403. if self.instruction.FindInstructionByType('raster'):
  404. rasterId = self.instruction.FindInstructionByType('raster').id
  405. else: rasterId = None
  406. id[1] = rasterId
  407. id[2] = vectorId
  408. if mapId: # map exists
  409. self.toolbar.ToggleTool(self.actionOld, True)
  410. self.toolbar.ToggleTool(self.toolbar.action['id'], False)
  411. self.toolbar.action['id'] = self.actionOld
  412. try:
  413. self.canvas.SetCursor(self.cursorOld)
  414. except AttributeError:
  415. pass
  416. ## dlg = MapDialog(parent = self, id = id, settings = self.instruction,
  417. ## notebook = notebook)
  418. ## dlg.ShowModal()
  419. if notebook:
  420. #check map, raster, vector and save, destroy them
  421. if 'map' in self.openDialogs:
  422. self.openDialogs['map'].OnOK(event = None)
  423. if 'raster' in self.openDialogs:
  424. self.openDialogs['raster'].OnOK(event = None)
  425. if 'vector' in self.openDialogs:
  426. self.openDialogs['vector'].OnOK(event = None)
  427. if 'mapNotebook' not in self.openDialogs:
  428. dlg = MapDialog(parent = self, id = id, settings = self.instruction,
  429. notebook = notebook)
  430. self.openDialogs['mapNotebook'] = dlg
  431. self.openDialogs['mapNotebook'].Show()
  432. else:
  433. if 'mapNotebook' in self.openDialogs:
  434. self.openDialogs['mapNotebook'].notebook.ChangeSelection(0)
  435. else:
  436. if 'map' not in self.openDialogs:
  437. dlg = MapDialog(parent = self, id = id, settings = self.instruction,
  438. notebook = notebook)
  439. self.openDialogs['map'] = dlg
  440. self.openDialogs['map'].Show()
  441. else: # sofar no map
  442. self.mouse["use"] = "addMap"
  443. self.canvas.SetCursor(self.cursors["cross"])
  444. if self.currentPage == 1:
  445. self.book.SetSelection(0)
  446. self.currentPage = 0
  447. def OnAddRaster(self, event):
  448. """!Add raster map"""
  449. if self.instruction.FindInstructionByType('raster'):
  450. id = self.instruction.FindInstructionByType('raster').id
  451. else: id = None
  452. if self.instruction.FindInstructionByType('map'):
  453. mapId = self.instruction.FindInstructionByType('map').id
  454. else: mapId = None
  455. if not id:
  456. if not mapId:
  457. GMessage(message = _("Please, create map frame first."))
  458. return
  459. ## dlg = RasterDialog(self, id = id, settings = self.instruction)
  460. ## dlg.ShowModal()
  461. if 'mapNotebook' in self.openDialogs:
  462. self.openDialogs['mapNotebook'].notebook.ChangeSelection(1)
  463. else:
  464. if 'raster' not in self.openDialogs:
  465. dlg = RasterDialog(self, id = id, settings = self.instruction)
  466. self.openDialogs['raster'] = dlg
  467. self.openDialogs['raster'].Show()
  468. def OnAddVect(self, event):
  469. """!Add vector map"""
  470. if self.instruction.FindInstructionByType('vector'):
  471. id = self.instruction.FindInstructionByType('vector').id
  472. else: id = None
  473. if self.instruction.FindInstructionByType('map'):
  474. mapId = self.instruction.FindInstructionByType('map').id
  475. else: mapId = None
  476. if not id:
  477. if not mapId:
  478. GMessage(message = _("Please, create map frame first."))
  479. return
  480. ## dlg = MainVectorDialog(self, id = id, settings = self.instruction)
  481. ## dlg.ShowModal()
  482. if 'mapNotebook' in self.openDialogs:
  483. self.openDialogs['mapNotebook'].notebook.ChangeSelection(2)
  484. else:
  485. if 'vector' not in self.openDialogs:
  486. dlg = MainVectorDialog(self, id = id, settings = self.instruction)
  487. self.openDialogs['vector'] = dlg
  488. self.openDialogs['vector'].Show()
  489. def OnDecoration(self, event):
  490. """!Decorations overlay menu
  491. """
  492. decmenu = wx.Menu()
  493. # legend
  494. AddLegend = wx.MenuItem(decmenu, wx.ID_ANY, Icons['psMap']["addLegend"].GetLabel())
  495. AddLegend.SetBitmap(Icons['psMap']["addLegend"].GetBitmap(self.iconsize))
  496. decmenu.AppendItem(AddLegend)
  497. self.Bind(wx.EVT_MENU, self.OnAddLegend, AddLegend)
  498. # mapinfo
  499. AddMapinfo = wx.MenuItem(decmenu, wx.ID_ANY, Icons['psMap']["addMapinfo"].GetLabel())
  500. AddMapinfo.SetBitmap(Icons['psMap']["addMapinfo"].GetBitmap(self.iconsize))
  501. decmenu.AppendItem(AddMapinfo)
  502. self.Bind(wx.EVT_MENU, self.OnAddMapinfo, AddMapinfo)
  503. # scalebar
  504. AddScalebar = wx.MenuItem(decmenu, wx.ID_ANY, Icons['psMap']["addScalebar"].GetLabel())
  505. AddScalebar.SetBitmap(Icons['psMap']["addScalebar"].GetBitmap(self.iconsize))
  506. decmenu.AppendItem(AddScalebar)
  507. self.Bind(wx.EVT_MENU, self.OnAddScalebar, AddScalebar)
  508. # text
  509. AddText = wx.MenuItem(decmenu, wx.ID_ANY, Icons['psMap']["addText"].GetLabel())
  510. AddText.SetBitmap(Icons['psMap']["addText"].GetBitmap(self.iconsize))
  511. decmenu.AppendItem(AddText)
  512. self.Bind(wx.EVT_MENU, self.OnAddText, AddText)
  513. # Popup the menu. If an item is selected then its handler
  514. # will be called before PopupMenu returns.
  515. self.PopupMenu(decmenu)
  516. decmenu.Destroy()
  517. def OnAddScalebar(self, event):
  518. """!Add scalebar"""
  519. if projInfo()['proj'] == 'll':
  520. GMessage(message = _("Scalebar is not appropriate for this projection"))
  521. return
  522. if self.instruction.FindInstructionByType('scalebar'):
  523. id = self.instruction.FindInstructionByType('scalebar').id
  524. else: id = None
  525. if 'scalebar' not in self.openDialogs:
  526. dlg = ScalebarDialog(self, id = id, settings = self.instruction)
  527. self.openDialogs['scalebar'] = dlg
  528. self.openDialogs['scalebar'].Show()
  529. def OnAddLegend(self, event, page = 0):
  530. """!Add raster or vector legend"""
  531. if self.instruction.FindInstructionByType('rasterLegend'):
  532. idR = self.instruction.FindInstructionByType('rasterLegend').id
  533. else: idR = None
  534. if self.instruction.FindInstructionByType('vectorLegend'):
  535. idV = self.instruction.FindInstructionByType('vectorLegend').id
  536. else: idV = None
  537. if 'rasterLegend' not in self.openDialogs:
  538. dlg = LegendDialog(self, id = [idR, idV], settings = self.instruction, page = page)
  539. self.openDialogs['rasterLegend'] = dlg
  540. self.openDialogs['vectorLegend'] = dlg
  541. self.openDialogs['rasterLegend'].notebook.ChangeSelection(page)
  542. self.openDialogs['rasterLegend'].Show()
  543. def OnAddMapinfo(self, event):
  544. if self.instruction.FindInstructionByType('mapinfo'):
  545. id = self.instruction.FindInstructionByType('mapinfo').id
  546. else: id = None
  547. if 'mapinfo' not in self.openDialogs:
  548. dlg = MapinfoDialog(self, id = id, settings = self.instruction)
  549. self.openDialogs['mapinfo'] = dlg
  550. self.openDialogs['mapinfo'].Show()
  551. def OnAddText(self, event, id = None):
  552. """!Show dialog for text adding and editing"""
  553. position = None
  554. if 'text' in self.openDialogs:
  555. position = self.openDialogs['text'].GetPosition()
  556. self.openDialogs['text'].OnApply(event = None)
  557. self.openDialogs['text'].Destroy()
  558. dlg = TextDialog(self, id = id, settings = self.instruction)
  559. self.openDialogs['text'] = dlg
  560. if position:
  561. dlg.SetPosition(position)
  562. dlg.Show()
  563. def getModifiedTextBounds(self, x, y, textExtent, rotation):
  564. """!computes bounding box of rotated text, not very precisely"""
  565. w, h = textExtent
  566. rotation = float(rotation)/180*pi
  567. H = float(w) * sin(rotation)
  568. W = float(w) * cos(rotation)
  569. X, Y = x, y
  570. if pi/2 < rotation <= 3*pi/2:
  571. X = x + W
  572. if 0 < rotation < pi:
  573. Y = y - H
  574. if rotation == 0:
  575. return wx.Rect(x,y, *textExtent)
  576. else:
  577. return wx.Rect(X, Y, abs(W), abs(H)).Inflate(h,h)
  578. def makePSFont(self, textDict):
  579. """!creates a wx.Font object from selected postscript font. To be
  580. used for estimating bounding rectangle of text"""
  581. fontsize = textDict['fontsize'] * self.canvas.currScale
  582. fontface = textDict['font'].split('-')[0]
  583. try:
  584. fontstyle = textDict['font'].split('-')[1]
  585. except IndexError:
  586. fontstyle = ''
  587. if fontface == "Times":
  588. family = wx.FONTFAMILY_ROMAN
  589. face = "times"
  590. elif fontface == "Helvetica":
  591. family = wx.FONTFAMILY_SWISS
  592. face = 'helvetica'
  593. elif fontface == "Courier":
  594. family = wx.FONTFAMILY_TELETYPE
  595. face = 'courier'
  596. else:
  597. family = wx.FONTFAMILY_DEFAULT
  598. face = ''
  599. style = wx.FONTSTYLE_NORMAL
  600. weight = wx.FONTWEIGHT_NORMAL
  601. if 'Oblique' in fontstyle:
  602. style = wx.FONTSTYLE_SLANT
  603. if 'Italic' in fontstyle:
  604. style = wx.FONTSTYLE_ITALIC
  605. if 'Bold' in fontstyle:
  606. weight = wx.FONTWEIGHT_BOLD
  607. try:
  608. fn = wx.Font(pointSize = fontsize, family = family, style = style,
  609. weight = weight, face = face)
  610. except:
  611. fn = wx.Font(pointSize = fontsize, family = wx.FONTFAMILY_DEFAULT,
  612. style = wx.FONTSTYLE_NORMAL, weight = wx.FONTWEIGHT_NORMAL)
  613. return fn
  614. def getTextExtent(self, textDict):
  615. """!Estimates bounding rectangle of text"""
  616. #fontsize = str(fontsize if fontsize >= 4 else 4)
  617. dc = wx.PaintDC(self) # dc created because of method GetTextExtent, which pseudoDC lacks
  618. fn = self.makePSFont(textDict)
  619. try:
  620. dc.SetFont(fn)
  621. w,h,lh = dc.GetMultiLineTextExtent(textDict['text'])
  622. return (w,h)
  623. except:
  624. return (0,0)
  625. def getInitMap(self):
  626. """!Create default map frame when no map is selected, needed for coordinates in map units"""
  627. instrFile = grass.tempfile()
  628. instrFileFd = open(instrFile, mode = 'w')
  629. instrFileFd.write(self.InstructionFile())
  630. instrFileFd.flush()
  631. instrFileFd.close()
  632. page = self.instruction.FindInstructionByType('page')
  633. mapInitRect = GetMapBounds(instrFile, portrait = (page['Orientation'] == 'Portrait'))
  634. grass.try_remove(instrFile)
  635. region = grass.region()
  636. units = UnitConversion(self)
  637. realWidth = units.convert(value = abs(region['w'] - region['e']), fromUnit = 'meter', toUnit = 'inch')
  638. scale = mapInitRect.Get()[2]/realWidth
  639. initMap = self.instruction.FindInstructionByType('initMap')
  640. if initMap:
  641. id = initMap.id
  642. else:
  643. id = None
  644. if not id:
  645. id = wx.NewId()
  646. initMap = InitMap(id)
  647. self.instruction.AddInstruction(initMap)
  648. self.instruction[id].SetInstruction(dict(rect = mapInitRect, scale = scale))
  649. def OnDelete(self, event):
  650. if self.canvas.dragId != -1 and self.currentPage == 0:
  651. if self.instruction[self.canvas.dragId].type == 'map':
  652. self.deleteObject(self.canvas.dragId)
  653. self.getInitMap()
  654. self.canvas.RecalculateEN()
  655. else:
  656. self.deleteObject(self.canvas.dragId)
  657. def deleteObject(self, id):
  658. """!Deletes object, his id and redraws"""
  659. #delete from canvas
  660. self.canvas.pdcObj.RemoveId(id)
  661. if id == self.canvas.dragId:
  662. self.canvas.pdcTmp.RemoveAll()
  663. self.canvas.dragId = -1
  664. self.canvas.Refresh()
  665. # delete from instructions
  666. del self.instruction[id]
  667. def DialogDataChanged(self, id):
  668. ids = id
  669. if type(id) == int:
  670. ids = [id]
  671. for id in ids:
  672. itype = self.instruction[id].type
  673. if itype in ('scalebar', 'mapinfo'):
  674. drawRectangle = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'], canvasToPaper = False)
  675. self.canvas.Draw(pen = self.pen[itype], brush = self.brush[itype],
  676. pdc = self.canvas.pdcObj, drawid = id, pdctype = 'rectText', bb = drawRectangle)
  677. self.canvas.RedrawSelectBox(id)
  678. if itype == 'text':
  679. if self.instruction[id]['rotate']:
  680. rot = float(self.instruction[id]['rotate'])
  681. else:
  682. rot = 0
  683. extent = self.getTextExtent(textDict = self.instruction[id].GetInstruction())
  684. rect = wx.Rect2D(self.instruction[id]['where'][0], self.instruction[id]['where'][1], 0, 0)
  685. self.instruction[id]['coords'] = list(self.canvas.CanvasPaperCoordinates(rect = rect, canvasToPaper = False)[:2])
  686. #computes text coordinates according to reference point, not precisely
  687. if self.instruction[id]['ref'].split()[0] == 'lower':
  688. self.instruction[id]['coords'][1] -= extent[1]
  689. elif self.instruction[id]['ref'].split()[0] == 'center':
  690. self.instruction[id]['coords'][1] -= extent[1]/2
  691. if self.instruction[id]['ref'].split()[1] == 'right':
  692. self.instruction[id]['coords'][0] -= extent[0] * cos(rot/180*pi)
  693. self.instruction[id]['coords'][1] += extent[0] * sin(rot/180*pi)
  694. elif self.instruction[id]['ref'].split()[1] == 'center':
  695. self.instruction[id]['coords'][0] -= extent[0]/2 * cos(rot/180*pi)
  696. self.instruction[id]['coords'][1] += extent[0]/2 * sin(rot/180*pi)
  697. self.instruction[id]['coords'][0] += self.instruction[id]['xoffset']
  698. self.instruction[id]['coords'][1] -= self.instruction[id]['yoffset']
  699. coords = self.instruction[id]['coords']
  700. self.instruction[id]['rect'] = bounds = self.getModifiedTextBounds(coords[0], coords[1], extent, rot)
  701. self.canvas.DrawRotText(pdc = self.canvas.pdcObj, drawId = id,
  702. textDict = self.instruction[id].GetInstruction(),
  703. coords = coords, bounds = bounds)
  704. self.canvas.RedrawSelectBox(id)
  705. if itype in ('map', 'vector', 'raster'):
  706. if itype == 'raster':#set resolution
  707. resol = RunCommand('r.info', read = True, flags = 's', map = self.instruction[id]['raster'])
  708. resol = grass.parse_key_val(resol, val_type = float)
  709. RunCommand('g.region', nsres = resol['nsres'], ewres = resol['ewres'])
  710. # change current raster in raster legend
  711. if 'rasterLegend' in self.openDialogs:
  712. self.openDialogs['rasterLegend'].updateDialog()
  713. id = self.instruction.FindInstructionByType('map').id
  714. #check resolution
  715. if itype == 'raster':
  716. SetResolution(dpi = self.instruction[id]['resolution'],
  717. width = self.instruction[id]['rect'].width,
  718. height = self.instruction[id]['rect'].height)
  719. rectCanvas = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'],
  720. canvasToPaper = False)
  721. self.canvas.RecalculateEN()
  722. self.canvas.UpdateMapLabel()
  723. self.canvas.Draw(pen = self.pen['map'], brush = self.brush['map'],
  724. pdc = self.canvas.pdcObj, drawid = id, pdctype = 'rectText', bb = rectCanvas)
  725. # redraw select box
  726. self.canvas.RedrawSelectBox(id)
  727. self.canvas.pdcTmp.RemoveId(self.canvas.idZoomBoxTmp)
  728. # redraw to get map to the bottom layer
  729. #self.canvas.Zoom(zoomFactor = 1, view = (0, 0))
  730. if itype == 'rasterLegend':
  731. if self.instruction[id]['rLegend']:
  732. drawRectangle = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'], canvasToPaper = False)
  733. self.canvas.Draw(pen = self.pen[itype], brush = self.brush[itype],
  734. pdc = self.canvas.pdcObj, drawid = id, pdctype = 'rectText', bb = drawRectangle)
  735. self.canvas.RedrawSelectBox(id)
  736. else:
  737. self.deleteObject(id)
  738. if itype == 'vectorLegend':
  739. if not self.instruction.FindInstructionByType('vector'):
  740. self.deleteObject(id)
  741. elif self.instruction[id]['vLegend']:
  742. drawRectangle = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'], canvasToPaper = False)
  743. self.canvas.Draw(pen = self.pen[itype], brush = self.brush[itype],
  744. pdc = self.canvas.pdcObj, drawid = id, pdctype = 'rectText', bb = drawRectangle)
  745. self.canvas.RedrawSelectBox(id)
  746. else:
  747. self.deleteObject(id)
  748. def OnPageChanged(self, event):
  749. """!Flatnotebook page has changed"""
  750. self.currentPage = self.book.GetPageIndex(self.book.GetCurrentPage())
  751. def OnPageChanging(self, event):
  752. """!Flatnotebook page is changing"""
  753. if self.currentPage == 0 and self.mouse['use'] == 'addMap':
  754. event.Veto()
  755. def OnHelp(self, event):
  756. """!Show help"""
  757. if self.parent and self.parent.GetName() == 'LayerManager':
  758. log = self.parent.GetLogWindow()
  759. log.RunCmd(['g.manual',
  760. 'entry=wxGUI.PsMap'])
  761. else:
  762. RunCommand('g.manual',
  763. quiet = True,
  764. entry = 'wxGUI.PsMap')
  765. def OnAbout(self, event):
  766. """!Display About window"""
  767. info = wx.AboutDialogInfo()
  768. info.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
  769. info.SetName(_('wxGUI Cartographic Composer'))
  770. info.SetWebSite('http://grass.osgeo.org')
  771. info.SetDescription(_('(C) 2011 by the GRASS Development Team\n\n') +
  772. '\n'.join(textwrap.wrap(_('This program is free software under the GNU General Public License'
  773. '(>=v2). Read the file COPYING that comes with GRASS for details.'), 75)))
  774. wx.AboutBox(info)
  775. def OnCloseWindow(self, event):
  776. """!Close window"""
  777. try:
  778. os.remove(self.imgName)
  779. except OSError:
  780. pass
  781. grass.set_raise_on_error(False)
  782. self.Destroy()
  783. class PsMapBufferedWindow(wx.Window):
  784. """!A buffered window class.
  785. @param parent parent window
  786. @param kwargs other wx.Window parameters
  787. """
  788. def __init__(self, parent, id = wx.ID_ANY,
  789. style = wx.NO_FULL_REPAINT_ON_RESIZE,
  790. **kwargs):
  791. wx.Window.__init__(self, parent, id = id, style = style)
  792. self.parent = parent
  793. self.FitInside()
  794. # store an off screen empty bitmap for saving to file
  795. self._buffer = None
  796. # indicates whether or not a resize event has taken place
  797. self.resize = False
  798. self.mouse = kwargs['mouse']
  799. self.cursors = kwargs['cursors']
  800. self.preview = kwargs['preview']
  801. self.pen = kwargs['pen']
  802. self.brush = kwargs['brush']
  803. if kwargs.has_key('instruction'):
  804. self.instruction = kwargs['instruction']
  805. if kwargs.has_key('openDialogs'):
  806. self.openDialogs = kwargs['openDialogs']
  807. if kwargs.has_key('pageId'):
  808. self.pageId = kwargs['pageId']
  809. if kwargs.has_key('objectId'):
  810. self.objectId = kwargs['objectId']
  811. #labels
  812. self.itemLabels = { 'map': ['MAP FRAME'],
  813. 'rasterLegend': ['RASTER LEGEND'],
  814. 'vectorLegend': ['VECTOR LEGEND'],
  815. 'mapinfo': ['MAP INFO'],
  816. 'scalebar': ['SCALE BAR']}
  817. # define PseudoDC
  818. self.pdc = wx.PseudoDC()
  819. self.pdcObj = wx.PseudoDC()
  820. self.pdcPaper = wx.PseudoDC()
  821. self.pdcTmp = wx.PseudoDC()
  822. self.pdcImage = wx.PseudoDC()
  823. dc = wx.PaintDC(self)
  824. self.font = dc.GetFont()
  825. self.SetClientSize((700,510))#?
  826. self._buffer = wx.EmptyBitmap(*self.GetClientSize())
  827. self.idBoxTmp = wx.NewId()
  828. self.idZoomBoxTmp = wx.NewId()
  829. self.idResizeBoxTmp = wx.NewId()
  830. self.dragId = -1
  831. if self.preview:
  832. self.image = None
  833. self.imageId = 2000
  834. self.imgName = self.parent.imgName
  835. self.currScale = None
  836. self.Clear()
  837. self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: None)
  838. self.Bind(wx.EVT_PAINT, self.OnPaint)
  839. self.Bind(wx.EVT_SIZE, self.OnSize)
  840. self.Bind(wx.EVT_IDLE, self.OnIdle)
  841. self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouse)
  842. def Clear(self):
  843. """!Clear canvas and set paper
  844. """
  845. bg = wx.LIGHT_GREY_BRUSH
  846. self.pdcPaper.BeginDrawing()
  847. self.pdcPaper.SetBackground(bg)
  848. self.pdcPaper.Clear()
  849. self.pdcPaper.EndDrawing()
  850. self.pdcObj.RemoveAll()
  851. self.pdcTmp.RemoveAll()
  852. if not self.preview:
  853. self.SetPage()
  854. def CanvasPaperCoordinates(self, rect, canvasToPaper = True):
  855. """!Converts canvas (pixel) -> paper (inch) coordinates and size and vice versa"""
  856. units = UnitConversion(self)
  857. fromU = 'pixel'
  858. toU = 'inch'
  859. pRect = self.pdcPaper.GetIdBounds(self.pageId)
  860. pRectx, pRecty = pRect.x, pRect.y
  861. scale = 1/self.currScale
  862. if not canvasToPaper: # paper -> canvas
  863. fromU = 'inch'
  864. toU = 'pixel'
  865. scale = self.currScale
  866. pRectx = units.convert(value = - pRect.x, fromUnit = 'pixel', toUnit = 'inch' ) /scale #inch, real, negative
  867. pRecty = units.convert(value = - pRect.y, fromUnit = 'pixel', toUnit = 'inch' ) /scale
  868. Width = units.convert(value = rect.width, fromUnit = fromU, toUnit = toU) * scale
  869. Height = units.convert(value = rect.height, fromUnit = fromU, toUnit = toU) * scale
  870. X = units.convert(value = (rect.x - pRectx), fromUnit = fromU, toUnit = toU) * scale
  871. Y = units.convert(value = (rect.y - pRecty), fromUnit = fromU, toUnit = toU) * scale
  872. return wx.Rect2D(X, Y, Width, Height)
  873. def SetPage(self):
  874. """!Sets and changes page, redraws paper"""
  875. page = self.instruction[self.pageId]
  876. if not page:
  877. page = PageSetup(id = self.pageId)
  878. self.instruction.AddInstruction(page)
  879. ppi = wx.PaintDC(self).GetPPI()
  880. cW, cH = self.GetClientSize()
  881. pW, pH = page['Width']*ppi[0], page['Height']*ppi[1]
  882. if self.currScale is None:
  883. self.currScale = min(cW/pW, cH/pH)
  884. pW = pW * self.currScale
  885. pH = pH * self.currScale
  886. x = cW/2 - pW/2
  887. y = cH/2 - pH/2
  888. self.DrawPaper(wx.Rect(x, y, pW, pH))
  889. def modifyRectangle(self, r):
  890. """! Recalculates rectangle not to have negative size"""
  891. if r.GetWidth() < 0:
  892. r.SetX(r.GetX() + r.GetWidth())
  893. if r.GetHeight() < 0:
  894. r.SetY(r.GetY() + r.GetHeight())
  895. r.SetWidth(abs(r.GetWidth()))
  896. r.SetHeight(abs(r.GetHeight()))
  897. return r
  898. def RecalculateEN(self):
  899. """!Recalculate east and north for texts (eps, points) after their or map's movement"""
  900. try:
  901. mapId = self.instruction.FindInstructionByType('map').id
  902. except AttributeError:
  903. mapId = self.instruction.FindInstructionByType('initMap').id
  904. texts = self.instruction.FindInstructionByType('text', list = True)
  905. for text in texts:
  906. e, n = PaperMapCoordinates(map = self.instruction[mapId], x = self.instruction[text.id]['where'][0],
  907. y = self.instruction[text.id]['where'][1], paperToMap = True)
  908. self.instruction[text.id]['east'], self.instruction[text.id]['north'] = e, n
  909. def OnPaint(self, event):
  910. """!Draw pseudo DC to buffer
  911. """
  912. if not self._buffer:
  913. return
  914. dc = wx.BufferedPaintDC(self, self._buffer)
  915. # use PrepareDC to set position correctly
  916. self.PrepareDC(dc)
  917. dc.SetBackground(wx.LIGHT_GREY_BRUSH)
  918. dc.Clear()
  919. # draw paper
  920. if not self.preview:
  921. self.pdcPaper.DrawToDC(dc)
  922. # draw to the DC using the calculated clipping rect
  923. rgn = self.GetUpdateRegion()
  924. if not self.preview:
  925. self.pdcObj.DrawToDCClipped(dc, rgn.GetBox())
  926. else:
  927. self.pdcImage.DrawToDCClipped(dc, rgn.GetBox())
  928. self.pdcTmp.DrawToDCClipped(dc, rgn.GetBox())
  929. def OnMouse(self, event):
  930. if event.GetWheelRotation():
  931. zoom = event.GetWheelRotation()
  932. use = self.mouse['use']
  933. self.mouse['begin'] = event.GetPosition()
  934. if zoom > 0:
  935. self.mouse['use'] = 'zoomin'
  936. else:
  937. self.mouse['use'] = 'zoomout'
  938. zoomFactor, view = self.ComputeZoom(wx.Rect(0,0,0,0))
  939. self.Zoom(zoomFactor, view)
  940. self.mouse['use'] = use
  941. if event.Moving():
  942. if self.mouse['use'] in ('pointer', 'resize'):
  943. pos = event.GetPosition()
  944. foundResize = self.pdcTmp.FindObjects(pos[0], pos[1])
  945. if foundResize and foundResize[0] == self.idResizeBoxTmp:
  946. self.SetCursor(self.cursors["sizenwse"])
  947. self.parent.SetStatusText(_('Click and drag to resize object'), 0)
  948. else:
  949. self.parent.SetStatusText('', 0)
  950. self.SetCursor(self.cursors["default"])
  951. elif event.LeftDown():
  952. self.mouse['begin'] = event.GetPosition()
  953. self.begin = self.mouse['begin']
  954. if self.mouse['use'] in ('pan', 'zoomin', 'zoomout', 'addMap'):
  955. pass
  956. #select
  957. if self.mouse['use'] == 'pointer':
  958. found = self.pdcObj.FindObjects(self.mouse['begin'][0], self.mouse['begin'][1])
  959. foundResize = self.pdcTmp.FindObjects(self.mouse['begin'][0], self.mouse['begin'][1])
  960. if foundResize and foundResize[0] == self.idResizeBoxTmp:
  961. self.mouse['use'] = 'resize'
  962. # when resizing, proportions match region
  963. if self.instruction[self.dragId].type == 'map':
  964. self.constraint = False
  965. self.mapBounds = self.pdcObj.GetIdBounds(self.dragId)
  966. if self.instruction[self.dragId]['scaleType'] in (0, 1, 2):
  967. self.constraint = True
  968. self.mapBounds = self.pdcObj.GetIdBounds(self.dragId)
  969. elif found:
  970. self.dragId = found[0]
  971. self.RedrawSelectBox(self.dragId)
  972. if self.instruction[self.dragId].type != 'map':
  973. self.pdcTmp.RemoveId(self.idResizeBoxTmp)
  974. self.Refresh()
  975. else:
  976. self.dragId = -1
  977. self.pdcTmp.RemoveId(self.idBoxTmp)
  978. self.pdcTmp.RemoveId(self.idResizeBoxTmp)
  979. self.Refresh()
  980. elif event.Dragging() and event.LeftIsDown():
  981. #draw box when zooming, creating map
  982. if self.mouse['use'] in ('zoomin', 'zoomout', 'addMap'):
  983. self.mouse['end'] = event.GetPosition()
  984. r = wx.Rect(self.mouse['begin'][0], self.mouse['begin'][1],
  985. self.mouse['end'][0]-self.mouse['begin'][0], self.mouse['end'][1]-self.mouse['begin'][1])
  986. r = self.modifyRectangle(r)
  987. self.Draw(pen = self.pen['box'], brush = self.brush['box'], pdc = self.pdcTmp, drawid = self.idZoomBoxTmp,
  988. pdctype = 'rect', bb = r)
  989. # panning
  990. if self.mouse["use"] == 'pan':
  991. self.mouse['end'] = event.GetPosition()
  992. view = self.mouse['begin'][0] - self.mouse['end'][0], self.mouse['begin'][1] - self.mouse['end'][1]
  993. zoomFactor = 1
  994. self.Zoom(zoomFactor, view)
  995. self.mouse['begin'] = event.GetPosition()
  996. #move object
  997. if self.mouse['use'] == 'pointer' and self.dragId != -1:
  998. self.mouse['end'] = event.GetPosition()
  999. dx, dy = self.mouse['end'][0] - self.begin[0], self.mouse['end'][1] - self.begin[1]
  1000. self.pdcObj.TranslateId(self.dragId, dx, dy)
  1001. self.pdcTmp.TranslateId(self.idBoxTmp, dx, dy)
  1002. self.pdcTmp.TranslateId(self.idResizeBoxTmp, dx, dy)
  1003. if self.instruction[self.dragId].type == 'text':
  1004. self.instruction[self.dragId]['coords'] = self.instruction[self.dragId]['coords'][0] + dx,\
  1005. self.instruction[self.dragId]['coords'][1] + dy
  1006. self.begin = event.GetPosition()
  1007. self.Refresh()
  1008. # resize object
  1009. if self.mouse['use'] == 'resize':
  1010. type = self.instruction[self.dragId].type
  1011. pos = event.GetPosition()
  1012. x, y = self.mapBounds.GetX(), self.mapBounds.GetY()
  1013. width, height = self.mapBounds.GetWidth(), self.mapBounds.GetHeight()
  1014. diffX = pos[0] - self.mouse['begin'][0]
  1015. diffY = pos[1] - self.mouse['begin'][1]
  1016. # match given region
  1017. if self.constraint:
  1018. if width > height:
  1019. newWidth = width + diffX
  1020. newHeight = height + diffX * (float(height) / width)
  1021. else:
  1022. newWidth = width + diffY * (float(width) / height)
  1023. newHeight = height + diffY
  1024. else:
  1025. newWidth = width + diffX
  1026. newHeight = height + diffY
  1027. if newWidth < 10 or newHeight < 10:
  1028. return
  1029. bounds = wx.Rect(x, y, newWidth, newHeight)
  1030. self.Draw(pen = self.pen[type], brush = self.brush[type], pdc = self.pdcObj, drawid = self.dragId,
  1031. pdctype = 'rectText', bb = bounds)
  1032. self.RedrawSelectBox(self.dragId)
  1033. elif event.LeftUp():
  1034. # zoom in, zoom out
  1035. if self.mouse['use'] in ('zoomin','zoomout'):
  1036. zoomR = self.pdcTmp.GetIdBounds(self.idZoomBoxTmp)
  1037. self.pdcTmp.RemoveId(self.idZoomBoxTmp)
  1038. self.Refresh()
  1039. zoomFactor, view = self.ComputeZoom(zoomR)
  1040. self.Zoom(zoomFactor, view)
  1041. # draw map frame
  1042. if self.mouse['use'] == 'addMap':
  1043. rectTmp = self.pdcTmp.GetIdBounds(self.idZoomBoxTmp)
  1044. # too small rectangle, it's usually some mistake
  1045. if rectTmp.GetWidth() < 20 or rectTmp.GetHeight() < 20:
  1046. self.pdcTmp.RemoveId(self.idZoomBoxTmp)
  1047. self.Refresh()
  1048. return
  1049. rectPaper = self.CanvasPaperCoordinates(rect = rectTmp, canvasToPaper = True)
  1050. dlg = MapDialog(parent = self.parent, id = [None, None, None], settings = self.instruction,
  1051. rect = rectPaper)
  1052. self.openDialogs['map'] = dlg
  1053. self.openDialogs['map'].Show()
  1054. self.mouse['use'] = self.parent.mouseOld
  1055. self.SetCursor(self.parent.cursorOld)
  1056. self.parent.toolbar.ToggleTool(self.parent.actionOld, True)
  1057. self.parent.toolbar.ToggleTool(self.parent.toolbar.action['id'], False)
  1058. self.parent.toolbar.action['id'] = self.parent.actionOld
  1059. # resize resizable objects (only map sofar)
  1060. if self.mouse['use'] == 'resize':
  1061. mapId = self.instruction.FindInstructionByType('map').id
  1062. if self.dragId == mapId:
  1063. # necessary to change either map frame (scaleType 0,1,2) or region (scaletype 3)
  1064. newRectCanvas = self.pdcObj.GetIdBounds(mapId)
  1065. newRectPaper = self.CanvasPaperCoordinates(rect = newRectCanvas, canvasToPaper = True)
  1066. self.instruction[mapId]['rect'] = newRectPaper
  1067. if self.instruction[mapId]['scaleType'] in (0, 1, 2):
  1068. if self.instruction[mapId]['scaleType'] == 0:
  1069. scale, foo, rect = AutoAdjust(self, scaleType = 0,
  1070. map = self.instruction[mapId]['map'],
  1071. mapType = self.instruction[mapId]['mapType'],
  1072. rect = self.instruction[mapId]['rect'])
  1073. elif self.instruction[mapId]['scaleType'] == 1:
  1074. scale, foo, rect = AutoAdjust(self, scaleType = 1,
  1075. region = self.instruction[mapId]['region'],
  1076. rect = self.instruction[mapId]['rect'])
  1077. else:
  1078. scale, foo, rect = AutoAdjust(self, scaleType = 2,
  1079. rect = self.instruction[mapId]['rect'])
  1080. self.instruction[mapId]['rect'] = rect
  1081. self.instruction[mapId]['scale'] = scale
  1082. rectCanvas = self.CanvasPaperCoordinates(rect = rect, canvasToPaper = False)
  1083. self.Draw(pen = self.pen['map'], brush = self.brush['map'],
  1084. pdc = self.pdcObj, drawid = mapId, pdctype = 'rectText', bb = rectCanvas)
  1085. elif self.instruction[mapId]['scaleType'] == 3:
  1086. ComputeSetRegion(self, mapDict = self.instruction[mapId].GetInstruction())
  1087. #check resolution
  1088. SetResolution(dpi = self.instruction[mapId]['resolution'],
  1089. width = self.instruction[mapId]['rect'].width,
  1090. height = self.instruction[mapId]['rect'].height)
  1091. self.RedrawSelectBox(mapId)
  1092. self.Zoom(zoomFactor = 1, view = (0, 0))
  1093. self.mouse['use'] = 'pointer'
  1094. # recalculate the position of objects after dragging
  1095. if self.mouse['use'] in ('pointer', 'resize') and self.dragId != -1:
  1096. if self.mouse['begin'] != event.GetPosition(): #for double click
  1097. self.RecalculatePosition(ids = [self.dragId])
  1098. if self.instruction[self.dragId].type in self.openDialogs:
  1099. self.openDialogs[self.instruction[self.dragId].type].updateDialog()
  1100. # double click launches dialogs
  1101. elif event.LeftDClick():
  1102. if self.mouse['use'] == 'pointer' and self.dragId != -1:
  1103. itemCall = { 'text':self.parent.OnAddText, 'mapinfo': self.parent.OnAddMapinfo,
  1104. 'scalebar': self.parent.OnAddScalebar,
  1105. 'rasterLegend': self.parent.OnAddLegend, 'vectorLegend': self.parent.OnAddLegend,
  1106. 'map': self.parent.OnAddMap}
  1107. itemArg = { 'text': dict(event = None, id = self.dragId), 'mapinfo': dict(event = None),
  1108. 'scalebar': dict(event = None),
  1109. 'rasterLegend': dict(event = None), 'vectorLegend': dict(event = None, page = 1),
  1110. 'map': dict(event = None, notebook = True)}
  1111. type = self.instruction[self.dragId].type
  1112. itemCall[type](**itemArg[type])
  1113. def RecalculatePosition(self, ids):
  1114. for id in ids:
  1115. itype = self.instruction[id].type
  1116. if itype == 'map':
  1117. self.instruction[id]['rect'] = self.CanvasPaperCoordinates(rect = self.pdcObj.GetIdBounds(id),
  1118. canvasToPaper = True)
  1119. self.RecalculateEN()
  1120. elif itype in ('mapinfo' ,'rasterLegend', 'vectorLegend'):
  1121. self.instruction[id]['rect'] = self.CanvasPaperCoordinates(rect = self.pdcObj.GetIdBounds(id),
  1122. canvasToPaper = True)
  1123. self.instruction[id]['where'] = self.CanvasPaperCoordinates(rect = self.pdcObj.GetIdBounds(id),
  1124. canvasToPaper = True)[:2]
  1125. elif itype == 'scalebar':
  1126. self.instruction[id]['rect'] = self.CanvasPaperCoordinates(rect = self.pdcObj.GetIdBounds(id),
  1127. canvasToPaper = True)
  1128. self.instruction[id]['where'] = self.instruction[id]['rect'].GetCentre()
  1129. elif itype == 'text':
  1130. x, y = self.instruction[id]['coords'][0] - self.instruction[id]['xoffset'],\
  1131. self.instruction[id]['coords'][1] + self.instruction[id]['yoffset']
  1132. extent = self.parent.getTextExtent(textDict = self.instruction[id])
  1133. if self.instruction[id]['rotate'] is not None:
  1134. rot = float(self.instruction[id]['rotate'])/180*pi
  1135. else:
  1136. rot = 0
  1137. if self.instruction[id]['ref'].split()[0] == 'lower':
  1138. y += extent[1]
  1139. elif self.instruction[id]['ref'].split()[0] == 'center':
  1140. y += extent[1]/2
  1141. if self.instruction[id]['ref'].split()[1] == 'right':
  1142. x += extent[0] * cos(rot)
  1143. y -= extent[0] * sin(rot)
  1144. elif self.instruction[id]['ref'].split()[1] == 'center':
  1145. x += extent[0]/2 * cos(rot)
  1146. y -= extent[0]/2 * sin(rot)
  1147. self.instruction[id]['where'] = self.CanvasPaperCoordinates(rect = wx.Rect2D(x, y, 0, 0),
  1148. canvasToPaper = True)[:2]
  1149. self.RecalculateEN()
  1150. def ComputeZoom(self, rect):
  1151. """!Computes zoom factor and scroll view"""
  1152. zoomFactor = 1
  1153. cW, cH = self.GetClientSize()
  1154. cW = float(cW)
  1155. if rect.IsEmpty(): # clicked on canvas
  1156. zoomFactor = 1.5
  1157. if self.mouse['use'] == 'zoomout':
  1158. zoomFactor = 1./zoomFactor
  1159. x,y = self.mouse['begin']
  1160. xView = x - x/zoomFactor#x - cW/(zoomFactor * 2)
  1161. yView = y - y/zoomFactor#y - cH/(zoomFactor * 2)
  1162. else: #dragging
  1163. rW, rH = float(rect.GetWidth()), float(rect.GetHeight())
  1164. zoomFactor = 1/max(rW/cW, rH/cH)
  1165. # when zooming to full extent, in some cases, there was zoom 1.01..., which causes problem
  1166. if abs(zoomFactor - 1) > 0.01:
  1167. zoomFactor = zoomFactor
  1168. else:
  1169. zoomFactor = 1.
  1170. if self.mouse['use'] == 'zoomout':
  1171. zoomFactor = min(rW/cW, rH/cH)
  1172. if rW/rH > cW/cH:
  1173. yView = rect.GetY() - (rW*(cH/cW) - rH)/2
  1174. xView = rect.GetX()
  1175. if self.mouse['use'] == 'zoomout':
  1176. x,y = rect.GetX() + (rW-(cW/cH)*rH)/2, rect.GetY()
  1177. xView, yView = -x, -y
  1178. else:
  1179. xView = rect.GetX() - (rH*(cW/cH) - rW)/2
  1180. yView = rect.GetY()
  1181. if self.mouse['use'] == 'zoomout':
  1182. x,y = rect.GetX(), rect.GetY() + (rH-(cH/cW)*rW)/2
  1183. xView, yView = -x, -y
  1184. return zoomFactor, (int(xView), int(yView))
  1185. def Zoom(self, zoomFactor, view):
  1186. """! Zoom to specified region, scroll view, redraw"""
  1187. if not self.currScale:
  1188. return
  1189. self.currScale = self.currScale*zoomFactor
  1190. if self.currScale > 10 or self.currScale < 0.1:
  1191. self.currScale = self.currScale/zoomFactor
  1192. return
  1193. if not self.preview:
  1194. # redraw paper
  1195. pRect = self.pdcPaper.GetIdBounds(self.pageId)
  1196. pRect.OffsetXY(-view[0], -view[1])
  1197. pRect = self.ScaleRect(rect = pRect, scale = zoomFactor)
  1198. self.DrawPaper(pRect)
  1199. #redraw objects
  1200. for id in self.objectId:
  1201. oRect = self.CanvasPaperCoordinates(
  1202. rect = self.instruction[id]['rect'], canvasToPaper = False)
  1203. type = self.instruction[id].type
  1204. if type == 'text':
  1205. coords = self.instruction[id]['coords']# recalculate coordinates, they are not equal to BB
  1206. self.instruction[id]['coords'] = coords = [(int(coord) - view[i]) * zoomFactor
  1207. for i, coord in enumerate(coords)]
  1208. self.DrawRotText(pdc = self.pdcObj, drawId = id, textDict = self.instruction[id],
  1209. coords = coords, bounds = oRect )
  1210. extent = self.parent.getTextExtent(textDict = self.instruction[id])
  1211. if self.instruction[id]['rotate']:
  1212. rot = float(self.instruction[id]['rotate'])
  1213. else:
  1214. rot = 0
  1215. self.instruction[id]['rect'] = bounds = self.parent.getModifiedTextBounds(coords[0], coords[1], extent, rot)
  1216. self.pdcObj.SetIdBounds(id, bounds)
  1217. else:
  1218. self.Draw(pen = self.pen[type], brush = self.brush[type], pdc = self.pdcObj,
  1219. drawid = id, pdctype = 'rectText', bb = oRect)
  1220. #redraw tmp objects
  1221. if self.dragId != -1:
  1222. self.RedrawSelectBox(self.dragId)
  1223. #redraw preview
  1224. else: # preview mode
  1225. imageRect = self.pdcImage.GetIdBounds(self.imageId)
  1226. imageRect.OffsetXY(-view[0], -view[1])
  1227. imageRect = self.ScaleRect(rect = imageRect, scale = zoomFactor)
  1228. self.DrawImage(imageRect)
  1229. def ZoomAll(self):
  1230. """! Zoom to full extent"""
  1231. if not self.preview:
  1232. bounds = self.pdcPaper.GetIdBounds(self.pageId)
  1233. else:
  1234. bounds = self.pdcImage.GetIdBounds(self.imageId)
  1235. zoomP = bounds.Inflate(bounds.width/20, bounds.height/20)
  1236. zoomFactor, view = self.ComputeZoom(zoomP)
  1237. self.Zoom(zoomFactor, view)
  1238. def Draw(self, pen, brush, pdc, drawid = None, pdctype = 'rect', bb = wx.Rect(0,0,0,0)):
  1239. """! Draw object"""
  1240. if drawid is None:
  1241. drawid = wx.NewId()
  1242. bb = bb.Get()
  1243. pdc.BeginDrawing()
  1244. pdc.RemoveId(drawid)
  1245. pdc.SetId(drawid)
  1246. pdc.SetPen(pen)
  1247. pdc.SetBrush(brush)
  1248. if pdctype in ('rect', 'rectText'):
  1249. pdc.DrawRectangle(*bb)
  1250. if pdctype == 'rectText':
  1251. dc = wx.PaintDC(self) # dc created because of method GetTextExtent, which pseudoDC lacks
  1252. font = self.font
  1253. size = 10
  1254. font.SetPointSize(size)
  1255. font.SetStyle(wx.ITALIC)
  1256. dc.SetFont(font)
  1257. pdc.SetFont(font)
  1258. text = '\n'.join(self.itemLabels[self.instruction[drawid].type])
  1259. w,h,lh = dc.GetMultiLineTextExtent(text)
  1260. textExtent = (w,h)
  1261. textRect = wx.Rect(0, 0, *textExtent).CenterIn(bb)
  1262. r = map(int, bb)
  1263. while not wx.Rect(*r).ContainsRect(textRect) and size >= 8:
  1264. size -= 2
  1265. font.SetPointSize(size)
  1266. dc.SetFont(font)
  1267. pdc.SetFont(font)
  1268. textExtent = dc.GetTextExtent(text)
  1269. textRect = wx.Rect(0, 0, *textExtent).CenterIn(bb)
  1270. pdc.SetTextForeground(wx.Color(100,100,100,200))
  1271. pdc.SetBackgroundMode(wx.TRANSPARENT)
  1272. pdc.DrawText(text = text, x = textRect.x, y = textRect.y)
  1273. pdc.SetIdBounds(drawid, bb)
  1274. pdc.EndDrawing()
  1275. self.Refresh()
  1276. return drawid
  1277. def DrawRotText(self, pdc, drawId, textDict, coords, bounds):
  1278. if textDict['rotate']:
  1279. rot = float(textDict['rotate'])
  1280. else:
  1281. rot = 0
  1282. fontsize = textDict['fontsize'] * self.currScale
  1283. if textDict['background'] != 'none':
  1284. background = textDict['background']
  1285. else:
  1286. background = None
  1287. pdc.RemoveId(drawId)
  1288. pdc.SetId(drawId)
  1289. pdc.BeginDrawing()
  1290. # border is not redrawn when zoom changes, why?
  1291. ## if textDict['border'] != 'none' and not rot:
  1292. ## units = UnitConversion(self)
  1293. ## borderWidth = units.convert(value = textDict['width'],
  1294. ## fromUnit = 'point', toUnit = 'pixel' ) * self.currScale
  1295. ## pdc.SetPen(wx.Pen(colour = convertRGB(textDict['border']), width = borderWidth))
  1296. ## pdc.DrawRectangle(*bounds)
  1297. if background:
  1298. pdc.SetTextBackground(convertRGB(background))
  1299. pdc.SetBackgroundMode(wx.SOLID)
  1300. else:
  1301. pdc.SetBackgroundMode(wx.TRANSPARENT)
  1302. fn = self.parent.makePSFont(textDict)
  1303. pdc.SetFont(fn)
  1304. pdc.SetTextForeground(convertRGB(textDict['color']))
  1305. pdc.DrawRotatedText(textDict['text'], coords[0], coords[1], rot)
  1306. pdc.SetIdBounds(drawId, wx.Rect(*bounds))
  1307. self.Refresh()
  1308. pdc.EndDrawing()
  1309. def DrawImage(self, rect):
  1310. """!Draw preview image to pseudoDC"""
  1311. self.pdcImage.ClearId(self.imageId)
  1312. self.pdcImage.SetId(self.imageId)
  1313. img = self.image
  1314. if img.GetWidth() != rect.width or img.GetHeight() != rect.height:
  1315. img = img.Scale(rect.width, rect.height)
  1316. bitmap = img.ConvertToBitmap()
  1317. self.pdcImage.BeginDrawing()
  1318. self.pdcImage.DrawBitmap(bitmap, rect.x, rect.y)
  1319. self.pdcImage.SetIdBounds(self.imageId, rect)
  1320. self.pdcImage.EndDrawing()
  1321. self.Refresh()
  1322. def DrawPaper(self, rect):
  1323. """!Draw paper and margins"""
  1324. page = self.instruction[self.pageId]
  1325. scale = page['Width'] / rect.GetWidth()
  1326. w = (page['Width'] - page['Right'] - page['Left']) / scale
  1327. h = (page['Height'] - page['Top'] - page['Bottom']) / scale
  1328. x = page['Left'] / scale + rect.GetX()
  1329. y = page['Top'] / scale + rect.GetY()
  1330. self.pdcPaper.BeginDrawing()
  1331. self.pdcPaper.RemoveId(self.pageId)
  1332. self.pdcPaper.SetId(self.pageId)
  1333. self.pdcPaper.SetPen(self.pen['paper'])
  1334. self.pdcPaper.SetBrush(self.brush['paper'])
  1335. self.pdcPaper.DrawRectangleRect(rect)
  1336. self.pdcPaper.SetPen(self.pen['margins'])
  1337. self.pdcPaper.SetBrush(self.brush['margins'])
  1338. self.pdcPaper.DrawRectangle(x, y, w, h)
  1339. self.pdcPaper.SetIdBounds(self.pageId, rect)
  1340. self.pdcPaper.EndDrawing()
  1341. self.Refresh()
  1342. def ImageRect(self):
  1343. """!Returns image centered in canvas, computes scale"""
  1344. img = wx.Image(self.imgName, wx.BITMAP_TYPE_PNG)
  1345. cW, cH = self.GetClientSize()
  1346. iW, iH = img.GetWidth(), img.GetHeight()
  1347. self.currScale = min(float(cW)/iW, float(cH)/iH)
  1348. iW = iW * self.currScale
  1349. iH = iH * self.currScale
  1350. x = cW/2 - iW/2
  1351. y = cH/2 - iH/2
  1352. imageRect = wx.Rect(x, y, iW, iH)
  1353. return imageRect
  1354. def RedrawSelectBox(self, id):
  1355. """!Redraws select box when selected object changes its size"""
  1356. if self.dragId == id:
  1357. rect = [self.pdcObj.GetIdBounds(id).Inflate(3,3)]
  1358. type = ['select']
  1359. ids = [self.idBoxTmp]
  1360. if self.instruction[id].type == 'map':
  1361. controlP = self.pdcObj.GetIdBounds(id).GetBottomRight()
  1362. rect.append(wx.Rect(controlP.x, controlP.y, 10,10))
  1363. type.append('resize')
  1364. ids.append(self.idResizeBoxTmp)
  1365. for id, type, rect in zip(ids, type, rect):
  1366. self.Draw(pen = self.pen[type], brush = self.brush[type], pdc = self.pdcTmp,
  1367. drawid = id, pdctype = 'rect', bb = rect)
  1368. def UpdateMapLabel(self):
  1369. """!Updates map frame label"""
  1370. vector = self.instruction.FindInstructionByType('vector')
  1371. if vector:
  1372. vectorId = vector.id
  1373. else:
  1374. vectorId = None
  1375. raster = self.instruction.FindInstructionByType('raster')
  1376. if raster:
  1377. rasterId = raster.id
  1378. else:
  1379. rasterId = None
  1380. rasterName = 'None'
  1381. if rasterId:
  1382. rasterName = self.instruction[rasterId]['raster'].split('@')[0]
  1383. self.itemLabels['map'] = self.itemLabels['map'][0:1]
  1384. self.itemLabels['map'].append("raster: " + rasterName)
  1385. if vectorId:
  1386. for map in self.instruction[vectorId]['list']:
  1387. self.itemLabels['map'].append('vector: ' + map[0].split('@')[0])
  1388. def OnSize(self, event):
  1389. """!Init image size to match window size
  1390. """
  1391. # not zoom all when notebook page is changed
  1392. if self.preview and self.parent.currentPage == 1 or not self.preview and self.parent.currentPage == 0:
  1393. self.ZoomAll()
  1394. self.OnIdle(None)
  1395. event.Skip()
  1396. def OnIdle(self, event):
  1397. """!Only re-render a image during idle time instead of
  1398. multiple times during resizing.
  1399. """
  1400. width, height = self.GetClientSize()
  1401. # Make new off screen bitmap: this bitmap will always have the
  1402. # current drawing in it, so it can be used to save the image
  1403. # to a file, or whatever.
  1404. self._buffer = wx.EmptyBitmap(width, height)
  1405. # re-render image on idle
  1406. self.resize = True
  1407. def ScaleRect(self, rect, scale):
  1408. """! Scale rectangle"""
  1409. return wx.Rect(rect.GetLeft()*scale, rect.GetTop()*scale,
  1410. rect.GetSize()[0]*scale, rect.GetSize()[1]*scale)
  1411. def main():
  1412. import gettext
  1413. gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
  1414. app = wx.PySimpleApp()
  1415. wx.InitAllImageHandlers()
  1416. frame = PsMapFrame()
  1417. frame.Show()
  1418. app.MainLoop()
  1419. if __name__ == "__main__":
  1420. main()