psmap.py 70 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711
  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 Hardcopy Map Output Utility"), **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. #filename = '/home/anna/Desktop/reading.txt'
  325. readObjectId = []
  326. readInstruction = Instruction(parent = self, objectsToDraw = readObjectId)
  327. ok = readInstruction.Read(filename)
  328. if not ok:
  329. GMessage(_("Failed to read file %s.") % filename)
  330. else:
  331. self.instruction = self.canvas.instruction = readInstruction
  332. self.objectId = self.canvas.objectId = readObjectId
  333. self.pageId = self.canvas.pageId = self.instruction.FindInstructionByType('page').id
  334. self.canvas.UpdateMapLabel()
  335. self.canvas.dragId = -1
  336. self.canvas.Clear()
  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.canvas.RecalculatePosition(ids = self.objectId)
  348. dlg.Destroy()
  349. def OnPointer(self, event):
  350. self.toolbar.OnTool(event)
  351. self.mouse["use"] = "pointer"
  352. self.canvas.SetCursor(self.cursors["default"])
  353. self.previewCanvas.SetCursor(self.cursors["default"])
  354. def OnPan(self, event):
  355. self.toolbar.OnTool(event)
  356. self.mouse["use"] = "pan"
  357. self.canvas.SetCursor(self.cursors["hand"])
  358. self.previewCanvas.SetCursor(self.cursors["hand"])
  359. def OnZoomIn(self, event):
  360. self.toolbar.OnTool(event)
  361. self.mouse["use"] = "zoomin"
  362. self.canvas.SetCursor(self.cursors["cross"])
  363. self.previewCanvas.SetCursor(self.cursors["cross"])
  364. def OnZoomOut(self, event):
  365. self.toolbar.OnTool(event)
  366. self.mouse["use"] = "zoomout"
  367. self.canvas.SetCursor(self.cursors["cross"])
  368. self.previewCanvas.SetCursor(self.cursors["cross"])
  369. def OnZoomAll(self, event):
  370. self.mouseOld = self.mouse['use']
  371. if self.currentPage == 0:
  372. self.cursorOld = self.canvas.GetCursor()
  373. else:
  374. self.previewCanvas.GetCursor()
  375. self.mouse["use"] = "zoomin"
  376. if self.currentPage == 0:
  377. self.canvas.ZoomAll()
  378. else:
  379. self.previewCanvas.ZoomAll()
  380. self.mouse["use"] = self.mouseOld
  381. if self.currentPage == 0:
  382. self.canvas.SetCursor(self.cursorOld)
  383. else:
  384. self.previewCanvas.SetCursor(self.cursorOld)
  385. def OnAddMap(self, event, notebook = False):
  386. """!Add or edit map frame"""
  387. if event is not None:
  388. if event.GetId() != self.toolbar.action['id']:
  389. self.actionOld = self.toolbar.action['id']
  390. self.mouseOld = self.mouse['use']
  391. self.cursorOld = self.canvas.GetCursor()
  392. self.toolbar.OnTool(event)
  393. if self.instruction.FindInstructionByType('map'):
  394. mapId = self.instruction.FindInstructionByType('map').id
  395. else: mapId = None
  396. id = [mapId, None, None]
  397. if notebook:
  398. if self.instruction.FindInstructionByType('vector'):
  399. vectorId = self.instruction.FindInstructionByType('vector').id
  400. else: vectorId = None
  401. if self.instruction.FindInstructionByType('raster'):
  402. rasterId = self.instruction.FindInstructionByType('raster').id
  403. else: rasterId = None
  404. id[1] = rasterId
  405. id[2] = vectorId
  406. if mapId: # map exists
  407. self.toolbar.ToggleTool(self.actionOld, True)
  408. self.toolbar.ToggleTool(self.toolbar.action['id'], False)
  409. self.toolbar.action['id'] = self.actionOld
  410. try:
  411. self.canvas.SetCursor(self.cursorOld)
  412. except AttributeError:
  413. pass
  414. ## dlg = MapDialog(parent = self, id = id, settings = self.instruction,
  415. ## notebook = notebook)
  416. ## dlg.ShowModal()
  417. if notebook:
  418. #check map, raster, vector and save, destroy them
  419. if 'map' in self.openDialogs:
  420. self.openDialogs['map'].OnOK(event = None)
  421. if 'raster' in self.openDialogs:
  422. self.openDialogs['raster'].OnOK(event = None)
  423. if 'vector' in self.openDialogs:
  424. self.openDialogs['vector'].OnOK(event = None)
  425. if 'mapNotebook' not in self.openDialogs:
  426. dlg = MapDialog(parent = self, id = id, settings = self.instruction,
  427. notebook = notebook)
  428. self.openDialogs['mapNotebook'] = dlg
  429. self.openDialogs['mapNotebook'].Show()
  430. else:
  431. if 'mapNotebook' in self.openDialogs:
  432. self.openDialogs['mapNotebook'].notebook.ChangeSelection(0)
  433. else:
  434. if 'map' not in self.openDialogs:
  435. dlg = MapDialog(parent = self, id = id, settings = self.instruction,
  436. notebook = notebook)
  437. self.openDialogs['map'] = dlg
  438. self.openDialogs['map'].Show()
  439. else: # sofar no map
  440. self.mouse["use"] = "addMap"
  441. self.canvas.SetCursor(self.cursors["cross"])
  442. if self.currentPage == 1:
  443. self.book.SetSelection(0)
  444. self.currentPage = 0
  445. def OnAddRaster(self, event):
  446. """!Add raster map"""
  447. if self.instruction.FindInstructionByType('raster'):
  448. id = self.instruction.FindInstructionByType('raster').id
  449. else: id = None
  450. if self.instruction.FindInstructionByType('map'):
  451. mapId = self.instruction.FindInstructionByType('map').id
  452. else: mapId = None
  453. if not id:
  454. if not mapId:
  455. GMessage(message = _("Please, create map frame first."))
  456. return
  457. ## dlg = RasterDialog(self, id = id, settings = self.instruction)
  458. ## dlg.ShowModal()
  459. if 'mapNotebook' in self.openDialogs:
  460. self.openDialogs['mapNotebook'].notebook.ChangeSelection(1)
  461. else:
  462. if 'raster' not in self.openDialogs:
  463. dlg = RasterDialog(self, id = id, settings = self.instruction)
  464. self.openDialogs['raster'] = dlg
  465. self.openDialogs['raster'].Show()
  466. def OnAddVect(self, event):
  467. """!Add vector map"""
  468. if self.instruction.FindInstructionByType('vector'):
  469. id = self.instruction.FindInstructionByType('vector').id
  470. else: id = None
  471. if self.instruction.FindInstructionByType('map'):
  472. mapId = self.instruction.FindInstructionByType('map').id
  473. else: mapId = None
  474. if not id:
  475. if not mapId:
  476. GMessage(message = _("Please, create map frame first."))
  477. return
  478. ## dlg = MainVectorDialog(self, id = id, settings = self.instruction)
  479. ## dlg.ShowModal()
  480. if 'mapNotebook' in self.openDialogs:
  481. self.openDialogs['mapNotebook'].notebook.ChangeSelection(2)
  482. else:
  483. if 'vector' not in self.openDialogs:
  484. dlg = MainVectorDialog(self, id = id, settings = self.instruction)
  485. self.openDialogs['vector'] = dlg
  486. self.openDialogs['vector'].Show()
  487. def OnDecoration(self, event):
  488. """!Decorations overlay menu
  489. """
  490. decmenu = wx.Menu()
  491. # legend
  492. AddLegend = wx.MenuItem(decmenu, wx.ID_ANY, Icons['psMap']["addLegend"].GetLabel())
  493. AddLegend.SetBitmap(Icons['psMap']["addLegend"].GetBitmap(self.iconsize))
  494. decmenu.AppendItem(AddLegend)
  495. self.Bind(wx.EVT_MENU, self.OnAddLegend, AddLegend)
  496. # mapinfo
  497. AddMapinfo = wx.MenuItem(decmenu, wx.ID_ANY, Icons['psMap']["addMapinfo"].GetLabel())
  498. AddMapinfo.SetBitmap(Icons['psMap']["addMapinfo"].GetBitmap(self.iconsize))
  499. decmenu.AppendItem(AddMapinfo)
  500. self.Bind(wx.EVT_MENU, self.OnAddMapinfo, AddMapinfo)
  501. # scalebar
  502. AddScalebar = wx.MenuItem(decmenu, wx.ID_ANY, Icons['psMap']["addScalebar"].GetLabel())
  503. AddScalebar.SetBitmap(Icons['psMap']["addScalebar"].GetBitmap(self.iconsize))
  504. decmenu.AppendItem(AddScalebar)
  505. self.Bind(wx.EVT_MENU, self.OnAddScalebar, AddScalebar)
  506. # text
  507. AddText = wx.MenuItem(decmenu, wx.ID_ANY, Icons['psMap']["addText"].GetLabel())
  508. AddText.SetBitmap(Icons['psMap']["addText"].GetBitmap(self.iconsize))
  509. decmenu.AppendItem(AddText)
  510. self.Bind(wx.EVT_MENU, self.OnAddText, AddText)
  511. # Popup the menu. If an item is selected then its handler
  512. # will be called before PopupMenu returns.
  513. self.PopupMenu(decmenu)
  514. decmenu.Destroy()
  515. def OnAddScalebar(self, event):
  516. """!Add scalebar"""
  517. if projInfo()['proj'] == 'll':
  518. GMessage(message = _("Scalebar is not appropriate for this projection"))
  519. return
  520. if self.instruction.FindInstructionByType('scalebar'):
  521. id = self.instruction.FindInstructionByType('scalebar').id
  522. else: id = None
  523. if 'scalebar' not in self.openDialogs:
  524. dlg = ScalebarDialog(self, id = id, settings = self.instruction)
  525. self.openDialogs['scalebar'] = dlg
  526. self.openDialogs['scalebar'].Show()
  527. def OnAddLegend(self, event, page = 0):
  528. """!Add raster or vector legend"""
  529. if self.instruction.FindInstructionByType('rasterLegend'):
  530. idR = self.instruction.FindInstructionByType('rasterLegend').id
  531. else: idR = None
  532. if self.instruction.FindInstructionByType('vectorLegend'):
  533. idV = self.instruction.FindInstructionByType('vectorLegend').id
  534. else: idV = None
  535. if 'rasterLegend' not in self.openDialogs:
  536. dlg = LegendDialog(self, id = [idR, idV], settings = self.instruction, page = page)
  537. self.openDialogs['rasterLegend'] = dlg
  538. self.openDialogs['vectorLegend'] = dlg
  539. self.openDialogs['rasterLegend'].notebook.ChangeSelection(page)
  540. self.openDialogs['rasterLegend'].Show()
  541. def OnAddMapinfo(self, event):
  542. if self.instruction.FindInstructionByType('mapinfo'):
  543. id = self.instruction.FindInstructionByType('mapinfo').id
  544. else: id = None
  545. if 'mapinfo' not in self.openDialogs:
  546. dlg = MapinfoDialog(self, id = id, settings = self.instruction)
  547. self.openDialogs['mapinfo'] = dlg
  548. self.openDialogs['mapinfo'].Show()
  549. def OnAddText(self, event, id = None):
  550. """!Show dialog for text adding and editing"""
  551. position = None
  552. if 'text' in self.openDialogs:
  553. position = self.openDialogs['text'].GetPosition()
  554. self.openDialogs['text'].OnApply(event = None)
  555. self.openDialogs['text'].Destroy()
  556. dlg = TextDialog(self, id = id, settings = self.instruction)
  557. self.openDialogs['text'] = dlg
  558. if position:
  559. dlg.SetPosition(position)
  560. dlg.Show()
  561. def getModifiedTextBounds(self, x, y, textExtent, rotation):
  562. """!computes bounding box of rotated text, not very precisely"""
  563. w, h = textExtent
  564. rotation = float(rotation)/180*pi
  565. H = float(w) * sin(rotation)
  566. W = float(w) * cos(rotation)
  567. X, Y = x, y
  568. if pi/2 < rotation <= 3*pi/2:
  569. X = x + W
  570. if 0 < rotation < pi:
  571. Y = y - H
  572. if rotation == 0:
  573. return wx.Rect(x,y, *textExtent)
  574. else:
  575. return wx.Rect(X, Y, abs(W), abs(H)).Inflate(h,h)
  576. def makePSFont(self, textDict):
  577. """!creates a wx.Font object from selected postscript font. To be
  578. used for estimating bounding rectangle of text"""
  579. fontsize = textDict['fontsize'] * self.canvas.currScale
  580. fontface = textDict['font'].split('-')[0]
  581. try:
  582. fontstyle = textDict['font'].split('-')[1]
  583. except IndexError:
  584. fontstyle = ''
  585. if fontface == "Times":
  586. family = wx.FONTFAMILY_ROMAN
  587. face = "times"
  588. elif fontface == "Helvetica":
  589. family = wx.FONTFAMILY_SWISS
  590. face = 'helvetica'
  591. elif fontface == "Courier":
  592. family = wx.FONTFAMILY_TELETYPE
  593. face = 'courier'
  594. else:
  595. family = wx.FONTFAMILY_DEFAULT
  596. face = ''
  597. style = wx.FONTSTYLE_NORMAL
  598. weight = wx.FONTWEIGHT_NORMAL
  599. if 'Oblique' in fontstyle:
  600. style = wx.FONTSTYLE_SLANT
  601. if 'Italic' in fontstyle:
  602. style = wx.FONTSTYLE_ITALIC
  603. if 'Bold' in fontstyle:
  604. weight = wx.FONTWEIGHT_BOLD
  605. try:
  606. fn = wx.Font(pointSize = fontsize, family = family, style = style,
  607. weight = weight, face = face)
  608. except:
  609. fn = wx.Font(pointSize = fontsize, family = wx.FONTFAMILY_DEFAULT,
  610. style = wx.FONTSTYLE_NORMAL, weight = wx.FONTWEIGHT_NORMAL)
  611. return fn
  612. def getTextExtent(self, textDict):
  613. """!Estimates bounding rectangle of text"""
  614. #fontsize = str(fontsize if fontsize >= 4 else 4)
  615. dc = wx.PaintDC(self) # dc created because of method GetTextExtent, which pseudoDC lacks
  616. fn = self.makePSFont(textDict)
  617. try:
  618. dc.SetFont(fn)
  619. w,h,lh = dc.GetMultiLineTextExtent(textDict['text'])
  620. return (w,h)
  621. except:
  622. return (0,0)
  623. def getInitMap(self):
  624. """!Create default map frame when no map is selected, needed for coordinates in map units"""
  625. instrFile = grass.tempfile()
  626. instrFileFd = open(instrFile, mode = 'w')
  627. instrFileFd.write(self.InstructionFile())
  628. instrFileFd.flush()
  629. instrFileFd.close()
  630. mapInitRect = GetMapBounds(instrFile)
  631. grass.try_remove(instrFile)
  632. region = grass.region()
  633. units = UnitConversion(self)
  634. realWidth = units.convert(value = abs(region['w'] - region['e']), fromUnit = 'meter', toUnit = 'inch')
  635. scale = mapInitRect.Get()[2]/realWidth
  636. initMap = self.instruction.FindInstructionByType('initMap')
  637. if initMap:
  638. id = initMap.id
  639. else:
  640. id = None
  641. if not id:
  642. id = wx.NewId()
  643. initMap = InitMap(id)
  644. self.instruction.AddInstruction(initMap)
  645. self.instruction[id].SetInstruction(dict(rect = mapInitRect, scale = scale))
  646. def OnDelete(self, event):
  647. if self.canvas.dragId != -1 and self.currentPage == 0:
  648. if self.instruction[self.canvas.dragId].type == 'map':
  649. self.deleteObject(self.canvas.dragId)
  650. self.getInitMap()
  651. self.canvas.RecalculateEN()
  652. else:
  653. self.deleteObject(self.canvas.dragId)
  654. def deleteObject(self, id):
  655. """!Deletes object, his id and redraws"""
  656. #delete from canvas
  657. self.canvas.pdcObj.RemoveId(id)
  658. if id == self.canvas.dragId:
  659. self.canvas.pdcTmp.RemoveAll()
  660. self.canvas.dragId = -1
  661. self.canvas.Refresh()
  662. # delete from instructions
  663. del self.instruction[id]
  664. def DialogDataChanged(self, id):
  665. ids = id
  666. if type(id) == int:
  667. ids = [id]
  668. for id in ids:
  669. itype = self.instruction[id].type
  670. if itype in ('scalebar', 'mapinfo'):
  671. drawRectangle = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'], canvasToPaper = False)
  672. self.canvas.Draw(pen = self.pen[itype], brush = self.brush[itype],
  673. pdc = self.canvas.pdcObj, drawid = id, pdctype = 'rectText', bb = drawRectangle)
  674. self.canvas.RedrawSelectBox(id)
  675. if itype == 'text':
  676. if self.instruction[id]['rotate']:
  677. rot = float(self.instruction[id]['rotate'])
  678. else:
  679. rot = 0
  680. extent = self.getTextExtent(textDict = self.instruction[id].GetInstruction())
  681. rect = wx.Rect2D(self.instruction[id]['where'][0], self.instruction[id]['where'][1], 0, 0)
  682. self.instruction[id]['coords'] = list(self.canvas.CanvasPaperCoordinates(rect = rect, canvasToPaper = False)[:2])
  683. #computes text coordinates according to reference point, not precisely
  684. if self.instruction[id]['ref'].split()[0] == 'lower':
  685. self.instruction[id]['coords'][1] -= extent[1]
  686. elif self.instruction[id]['ref'].split()[0] == 'center':
  687. self.instruction[id]['coords'][1] -= extent[1]/2
  688. if self.instruction[id]['ref'].split()[1] == 'right':
  689. self.instruction[id]['coords'][0] -= extent[0] * cos(rot/180*pi)
  690. self.instruction[id]['coords'][1] += extent[0] * sin(rot/180*pi)
  691. elif self.instruction[id]['ref'].split()[1] == 'center':
  692. self.instruction[id]['coords'][0] -= extent[0]/2 * cos(rot/180*pi)
  693. self.instruction[id]['coords'][1] += extent[0]/2 * sin(rot/180*pi)
  694. self.instruction[id]['coords'][0] += self.instruction[id]['xoffset']
  695. self.instruction[id]['coords'][1] -= self.instruction[id]['yoffset']
  696. coords = self.instruction[id]['coords']
  697. self.instruction[id]['rect'] = bounds = self.getModifiedTextBounds(coords[0], coords[1], extent, rot)
  698. self.canvas.DrawRotText(pdc = self.canvas.pdcObj, drawId = id,
  699. textDict = self.instruction[id].GetInstruction(),
  700. coords = coords, bounds = bounds)
  701. self.canvas.RedrawSelectBox(id)
  702. if itype in ('map', 'vector', 'raster'):
  703. if itype == 'raster':#set resolution
  704. resol = RunCommand('r.info', read = True, flags = 's', map = self.instruction[id]['raster'])
  705. resol = grass.parse_key_val(resol, val_type = float)
  706. RunCommand('g.region', nsres = resol['nsres'], ewres = resol['ewres'])
  707. # change current raster in raster legend
  708. if 'rasterLegend' in self.openDialogs:
  709. self.openDialogs['rasterLegend'].updateDialog()
  710. id = self.instruction.FindInstructionByType('map').id
  711. #check resolution
  712. if itype == 'raster':
  713. SetResolution(dpi = self.instruction[id]['resolution'],
  714. width = self.instruction[id]['rect'].width,
  715. height = self.instruction[id]['rect'].height)
  716. rectCanvas = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'],
  717. canvasToPaper = False)
  718. self.canvas.RecalculateEN()
  719. self.canvas.UpdateMapLabel()
  720. self.canvas.Draw(pen = self.pen['map'], brush = self.brush['map'],
  721. pdc = self.canvas.pdcObj, drawid = id, pdctype = 'rectText', bb = rectCanvas)
  722. # redraw select box
  723. self.canvas.RedrawSelectBox(id)
  724. self.canvas.pdcTmp.RemoveId(self.canvas.idZoomBoxTmp)
  725. # redraw to get map to the bottom layer
  726. #self.canvas.Zoom(zoomFactor = 1, view = (0, 0))
  727. if itype == 'rasterLegend':
  728. if self.instruction[id]['rLegend']:
  729. drawRectangle = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'], canvasToPaper = False)
  730. self.canvas.Draw(pen = self.pen[itype], brush = self.brush[itype],
  731. pdc = self.canvas.pdcObj, drawid = id, pdctype = 'rectText', bb = drawRectangle)
  732. self.canvas.RedrawSelectBox(id)
  733. else:
  734. self.deleteObject(id)
  735. if itype == 'vectorLegend':
  736. if not self.instruction.FindInstructionByType('vector'):
  737. self.deleteObject(id)
  738. elif self.instruction[id]['vLegend']:
  739. drawRectangle = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'], canvasToPaper = False)
  740. self.canvas.Draw(pen = self.pen[itype], brush = self.brush[itype],
  741. pdc = self.canvas.pdcObj, drawid = id, pdctype = 'rectText', bb = drawRectangle)
  742. self.canvas.RedrawSelectBox(id)
  743. else:
  744. self.deleteObject(id)
  745. def OnPageChanged(self, event):
  746. """!Flatnotebook page has changed"""
  747. self.currentPage = self.book.GetPageIndex(self.book.GetCurrentPage())
  748. def OnPageChanging(self, event):
  749. """!Flatnotebook page is changing"""
  750. if self.currentPage == 0 and self.mouse['use'] == 'addMap':
  751. event.Veto()
  752. def OnHelp(self, event):
  753. """!Show help"""
  754. if self.parent and self.parent.GetName() == 'LayerManager':
  755. log = self.parent.GetLogWindow()
  756. log.RunCmd(['g.manual',
  757. 'entry=wxGUI.PsMap'])
  758. else:
  759. RunCommand('g.manual',
  760. quiet = True,
  761. entry = 'wxGUI.PsMap')
  762. def OnAbout(self, event):
  763. """!Display About window"""
  764. info = wx.AboutDialogInfo()
  765. info.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
  766. info.SetName(_('wxGUI Hardcopy Map Utility'))
  767. info.SetWebSite('http://grass.osgeo.org')
  768. info.SetDescription(_('(C) 2011 by the GRASS Development Team\n\n') +
  769. '\n'.join(textwrap.wrap(_('This program is free software under the GNU General Public License'
  770. '(>=v2). Read the file COPYING that comes with GRASS for details.'), 75)))
  771. wx.AboutBox(info)
  772. def OnCloseWindow(self, event):
  773. """!Close window"""
  774. try:
  775. os.remove(self.imgName)
  776. except OSError:
  777. pass
  778. grass.set_raise_on_error(False)
  779. self.Destroy()
  780. class PsMapBufferedWindow(wx.Window):
  781. """!A buffered window class.
  782. @param parent parent window
  783. @param kwargs other wx.Window parameters
  784. """
  785. def __init__(self, parent, id = wx.ID_ANY,
  786. style = wx.NO_FULL_REPAINT_ON_RESIZE,
  787. **kwargs):
  788. wx.Window.__init__(self, parent, id = id, style = style)
  789. self.parent = parent
  790. self.FitInside()
  791. # store an off screen empty bitmap for saving to file
  792. self._buffer = None
  793. # indicates whether or not a resize event has taken place
  794. self.resize = False
  795. self.mouse = kwargs['mouse']
  796. self.cursors = kwargs['cursors']
  797. self.preview = kwargs['preview']
  798. self.pen = kwargs['pen']
  799. self.brush = kwargs['brush']
  800. if kwargs.has_key('instruction'):
  801. self.instruction = kwargs['instruction']
  802. if kwargs.has_key('openDialogs'):
  803. self.openDialogs = kwargs['openDialogs']
  804. if kwargs.has_key('pageId'):
  805. self.pageId = kwargs['pageId']
  806. if kwargs.has_key('objectId'):
  807. self.objectId = kwargs['objectId']
  808. #labels
  809. self.itemLabels = { 'map': ['MAP FRAME'],
  810. 'rasterLegend': ['RASTER LEGEND'],
  811. 'vectorLegend': ['VECTOR LEGEND'],
  812. 'mapinfo': ['MAP INFO'],
  813. 'scalebar': ['SCALE BAR']}
  814. # define PseudoDC
  815. self.pdc = wx.PseudoDC()
  816. self.pdcObj = wx.PseudoDC()
  817. self.pdcPaper = wx.PseudoDC()
  818. self.pdcTmp = wx.PseudoDC()
  819. self.pdcImage = wx.PseudoDC()
  820. dc = wx.PaintDC(self)
  821. self.font = dc.GetFont()
  822. self.SetClientSize((700,510))#?
  823. self._buffer = wx.EmptyBitmap(*self.GetClientSize())
  824. self.idBoxTmp = wx.NewId()
  825. self.idZoomBoxTmp = wx.NewId()
  826. self.idResizeBoxTmp = wx.NewId()
  827. self.dragId = -1
  828. if self.preview:
  829. self.image = None
  830. self.imageId = 2000
  831. self.imgName = self.parent.imgName
  832. self.currScale = None
  833. self.Clear()
  834. self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: None)
  835. self.Bind(wx.EVT_PAINT, self.OnPaint)
  836. self.Bind(wx.EVT_SIZE, self.OnSize)
  837. self.Bind(wx.EVT_IDLE, self.OnIdle)
  838. self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouse)
  839. def Clear(self):
  840. """!Clear canvas and set paper
  841. """
  842. bg = wx.LIGHT_GREY_BRUSH
  843. self.pdcPaper.BeginDrawing()
  844. self.pdcPaper.SetBackground(bg)
  845. self.pdcPaper.Clear()
  846. self.pdcPaper.EndDrawing()
  847. self.pdcObj.RemoveAll()
  848. self.pdcTmp.RemoveAll()
  849. if not self.preview:
  850. self.SetPage()
  851. def CanvasPaperCoordinates(self, rect, canvasToPaper = True):
  852. """!Converts canvas (pixel) -> paper (inch) coordinates and size and vice versa"""
  853. units = UnitConversion(self)
  854. fromU = 'pixel'
  855. toU = 'inch'
  856. pRect = self.pdcPaper.GetIdBounds(self.pageId)
  857. pRectx, pRecty = pRect.x, pRect.y
  858. scale = 1/self.currScale
  859. if not canvasToPaper: # paper -> canvas
  860. fromU = 'inch'
  861. toU = 'pixel'
  862. scale = self.currScale
  863. pRectx = units.convert(value = - pRect.x, fromUnit = 'pixel', toUnit = 'inch' ) /scale #inch, real, negative
  864. pRecty = units.convert(value = - pRect.y, fromUnit = 'pixel', toUnit = 'inch' ) /scale
  865. Width = units.convert(value = rect.width, fromUnit = fromU, toUnit = toU) * scale
  866. Height = units.convert(value = rect.height, fromUnit = fromU, toUnit = toU) * scale
  867. X = units.convert(value = (rect.x - pRectx), fromUnit = fromU, toUnit = toU) * scale
  868. Y = units.convert(value = (rect.y - pRecty), fromUnit = fromU, toUnit = toU) * scale
  869. return wx.Rect2D(X, Y, Width, Height)
  870. def SetPage(self):
  871. """!Sets and changes page, redraws paper"""
  872. page = self.instruction[self.pageId]
  873. if not page:
  874. page = PageSetup(id = self.pageId)
  875. self.instruction.AddInstruction(page)
  876. ppi = wx.PaintDC(self).GetPPI()
  877. cW, cH = self.GetClientSize()
  878. pW, pH = page['Width']*ppi[0], page['Height']*ppi[1]
  879. if self.currScale is None:
  880. self.currScale = min(cW/pW, cH/pH)
  881. pW = pW * self.currScale
  882. pH = pH * self.currScale
  883. x = cW/2 - pW/2
  884. y = cH/2 - pH/2
  885. self.DrawPaper(wx.Rect(x, y, pW, pH))
  886. def modifyRectangle(self, r):
  887. """! Recalculates rectangle not to have negative size"""
  888. if r.GetWidth() < 0:
  889. r.SetX(r.GetX() + r.GetWidth())
  890. if r.GetHeight() < 0:
  891. r.SetY(r.GetY() + r.GetHeight())
  892. r.SetWidth(abs(r.GetWidth()))
  893. r.SetHeight(abs(r.GetHeight()))
  894. return r
  895. def RecalculateEN(self):
  896. """!Recalculate east and north for texts (eps, points) after their or map's movement"""
  897. try:
  898. mapId = self.instruction.FindInstructionByType('map').id
  899. except AttributeError:
  900. mapId = self.instruction.FindInstructionByType('initMap').id
  901. texts = self.instruction.FindInstructionByType('text', list = True)
  902. for text in texts:
  903. e, n = PaperMapCoordinates(map = self.instruction[mapId], x = self.instruction[text.id]['where'][0],
  904. y = self.instruction[text.id]['where'][1], paperToMap = True)
  905. self.instruction[text.id]['east'], self.instruction[text.id]['north'] = e, n
  906. def OnPaint(self, event):
  907. """!Draw pseudo DC to buffer
  908. """
  909. if not self._buffer:
  910. return
  911. dc = wx.BufferedPaintDC(self, self._buffer)
  912. # use PrepareDC to set position correctly
  913. self.PrepareDC(dc)
  914. dc.SetBackground(wx.LIGHT_GREY_BRUSH)
  915. dc.Clear()
  916. # draw paper
  917. if not self.preview:
  918. self.pdcPaper.DrawToDC(dc)
  919. # draw to the DC using the calculated clipping rect
  920. rgn = self.GetUpdateRegion()
  921. if not self.preview:
  922. self.pdcObj.DrawToDCClipped(dc, rgn.GetBox())
  923. else:
  924. self.pdcImage.DrawToDCClipped(dc, rgn.GetBox())
  925. self.pdcTmp.DrawToDCClipped(dc, rgn.GetBox())
  926. def OnMouse(self, event):
  927. if event.GetWheelRotation():
  928. zoom = event.GetWheelRotation()
  929. use = self.mouse['use']
  930. self.mouse['begin'] = event.GetPosition()
  931. if zoom > 0:
  932. self.mouse['use'] = 'zoomin'
  933. else:
  934. self.mouse['use'] = 'zoomout'
  935. zoomFactor, view = self.ComputeZoom(wx.Rect(0,0,0,0))
  936. self.Zoom(zoomFactor, view)
  937. self.mouse['use'] = use
  938. if event.Moving():
  939. if self.mouse['use'] in ('pointer', 'resize'):
  940. pos = event.GetPosition()
  941. foundResize = self.pdcTmp.FindObjects(pos[0], pos[1])
  942. if foundResize and foundResize[0] == self.idResizeBoxTmp:
  943. self.SetCursor(self.cursors["sizenwse"])
  944. self.parent.SetStatusText(_('Click and drag to resize object'), 0)
  945. else:
  946. self.parent.SetStatusText('', 0)
  947. self.SetCursor(self.cursors["default"])
  948. elif event.LeftDown():
  949. self.mouse['begin'] = event.GetPosition()
  950. self.begin = self.mouse['begin']
  951. if self.mouse['use'] in ('pan', 'zoomin', 'zoomout', 'addMap'):
  952. pass
  953. #select
  954. if self.mouse['use'] == 'pointer':
  955. found = self.pdcObj.FindObjects(self.mouse['begin'][0], self.mouse['begin'][1])
  956. foundResize = self.pdcTmp.FindObjects(self.mouse['begin'][0], self.mouse['begin'][1])
  957. if foundResize and foundResize[0] == self.idResizeBoxTmp:
  958. self.mouse['use'] = 'resize'
  959. # when resizing, proportions match region
  960. if self.instruction[self.dragId].type == 'map':
  961. self.constraint = False
  962. self.mapBounds = self.pdcObj.GetIdBounds(self.dragId)
  963. if self.instruction[self.dragId]['scaleType'] in (0, 1, 2):
  964. self.constraint = True
  965. self.mapBounds = self.pdcObj.GetIdBounds(self.dragId)
  966. elif found:
  967. self.dragId = found[0]
  968. self.RedrawSelectBox(self.dragId)
  969. if self.instruction[self.dragId].type != 'map':
  970. self.pdcTmp.RemoveId(self.idResizeBoxTmp)
  971. self.Refresh()
  972. else:
  973. self.dragId = -1
  974. self.pdcTmp.RemoveId(self.idBoxTmp)
  975. self.pdcTmp.RemoveId(self.idResizeBoxTmp)
  976. self.Refresh()
  977. elif event.Dragging() and event.LeftIsDown():
  978. #draw box when zooming, creating map
  979. if self.mouse['use'] in ('zoomin', 'zoomout', 'addMap'):
  980. self.mouse['end'] = event.GetPosition()
  981. r = wx.Rect(self.mouse['begin'][0], self.mouse['begin'][1],
  982. self.mouse['end'][0]-self.mouse['begin'][0], self.mouse['end'][1]-self.mouse['begin'][1])
  983. r = self.modifyRectangle(r)
  984. self.Draw(pen = self.pen['box'], brush = self.brush['box'], pdc = self.pdcTmp, drawid = self.idZoomBoxTmp,
  985. pdctype = 'rect', bb = r)
  986. # panning
  987. if self.mouse["use"] == 'pan':
  988. self.mouse['end'] = event.GetPosition()
  989. view = self.mouse['begin'][0] - self.mouse['end'][0], self.mouse['begin'][1] - self.mouse['end'][1]
  990. zoomFactor = 1
  991. self.Zoom(zoomFactor, view)
  992. self.mouse['begin'] = event.GetPosition()
  993. #move object
  994. if self.mouse['use'] == 'pointer' and self.dragId != -1:
  995. self.mouse['end'] = event.GetPosition()
  996. dx, dy = self.mouse['end'][0] - self.begin[0], self.mouse['end'][1] - self.begin[1]
  997. self.pdcObj.TranslateId(self.dragId, dx, dy)
  998. self.pdcTmp.TranslateId(self.idBoxTmp, dx, dy)
  999. self.pdcTmp.TranslateId(self.idResizeBoxTmp, dx, dy)
  1000. if self.instruction[self.dragId].type == 'text':
  1001. self.instruction[self.dragId]['coords'] = self.instruction[self.dragId]['coords'][0] + dx,\
  1002. self.instruction[self.dragId]['coords'][1] + dy
  1003. self.begin = event.GetPosition()
  1004. self.Refresh()
  1005. # resize object
  1006. if self.mouse['use'] == 'resize':
  1007. type = self.instruction[self.dragId].type
  1008. pos = event.GetPosition()
  1009. x, y = self.mapBounds.GetX(), self.mapBounds.GetY()
  1010. width, height = self.mapBounds.GetWidth(), self.mapBounds.GetHeight()
  1011. diffX = pos[0] - self.mouse['begin'][0]
  1012. diffY = pos[1] - self.mouse['begin'][1]
  1013. # match given region
  1014. if self.constraint:
  1015. if width > height:
  1016. newWidth = width + diffX
  1017. newHeight = height + diffX * (float(height) / width)
  1018. else:
  1019. newWidth = width + diffY * (float(width) / height)
  1020. newHeight = height + diffY
  1021. else:
  1022. newWidth = width + diffX
  1023. newHeight = height + diffY
  1024. if newWidth < 10 or newHeight < 10:
  1025. return
  1026. bounds = wx.Rect(x, y, newWidth, newHeight)
  1027. self.Draw(pen = self.pen[type], brush = self.brush[type], pdc = self.pdcObj, drawid = self.dragId,
  1028. pdctype = 'rectText', bb = bounds)
  1029. self.RedrawSelectBox(self.dragId)
  1030. elif event.LeftUp():
  1031. # zoom in, zoom out
  1032. if self.mouse['use'] in ('zoomin','zoomout'):
  1033. zoomR = self.pdcTmp.GetIdBounds(self.idZoomBoxTmp)
  1034. self.pdcTmp.RemoveId(self.idZoomBoxTmp)
  1035. self.Refresh()
  1036. zoomFactor, view = self.ComputeZoom(zoomR)
  1037. self.Zoom(zoomFactor, view)
  1038. # draw map frame
  1039. if self.mouse['use'] == 'addMap':
  1040. rectTmp = self.pdcTmp.GetIdBounds(self.idZoomBoxTmp)
  1041. # too small rectangle, it's usually some mistake
  1042. if rectTmp.GetWidth() < 20 or rectTmp.GetHeight() < 20:
  1043. self.pdcTmp.RemoveId(self.idZoomBoxTmp)
  1044. self.Refresh()
  1045. return
  1046. rectPaper = self.CanvasPaperCoordinates(rect = rectTmp, canvasToPaper = True)
  1047. dlg = MapDialog(parent = self.parent, id = [None, None, None], settings = self.instruction,
  1048. rect = rectPaper)
  1049. self.openDialogs['map'] = dlg
  1050. self.openDialogs['map'].Show()
  1051. self.mouse['use'] = self.parent.mouseOld
  1052. self.SetCursor(self.parent.cursorOld)
  1053. self.parent.toolbar.ToggleTool(self.parent.actionOld, True)
  1054. self.parent.toolbar.ToggleTool(self.parent.toolbar.action['id'], False)
  1055. self.parent.toolbar.action['id'] = self.parent.actionOld
  1056. # resize resizable objects (only map sofar)
  1057. if self.mouse['use'] == 'resize':
  1058. mapId = self.instruction.FindInstructionByType('map').id
  1059. if self.dragId == mapId:
  1060. # necessary to change either map frame (scaleType 0,1,2) or region (scaletype 3)
  1061. newRectCanvas = self.pdcObj.GetIdBounds(mapId)
  1062. newRectPaper = self.CanvasPaperCoordinates(rect = newRectCanvas, canvasToPaper = True)
  1063. self.instruction[mapId]['rect'] = newRectPaper
  1064. if self.instruction[mapId]['scaleType'] in (0, 1, 2):
  1065. if self.instruction[mapId]['scaleType'] == 0:
  1066. scale, foo, rect = AutoAdjust(self, scaleType = 0,
  1067. map = self.instruction[mapId]['map'],
  1068. mapType = self.instruction[mapId]['mapType'],
  1069. rect = self.instruction[mapId]['rect'])
  1070. elif self.instruction[mapId]['scaleType'] == 1:
  1071. scale, foo, rect = AutoAdjust(self, scaleType = 1,
  1072. region = self.instruction[mapId]['region'],
  1073. rect = self.instruction[mapId]['rect'])
  1074. else:
  1075. scale, foo, rect = AutoAdjust(self, scaleType = 2,
  1076. rect = self.instruction[mapId]['rect'])
  1077. self.instruction[mapId]['rect'] = rect
  1078. self.instruction[mapId]['scale'] = scale
  1079. rectCanvas = self.CanvasPaperCoordinates(rect = rect, canvasToPaper = False)
  1080. self.Draw(pen = self.pen['map'], brush = self.brush['map'],
  1081. pdc = self.pdcObj, drawid = mapId, pdctype = 'rectText', bb = rectCanvas)
  1082. elif self.instruction[mapId]['scaleType'] == 3:
  1083. ComputeSetRegion(self, mapDict = self.instruction[mapId].GetInstruction())
  1084. #check resolution
  1085. SetResolution(dpi = self.instruction[mapId]['resolution'],
  1086. width = self.instruction[mapId]['rect'].width,
  1087. height = self.instruction[mapId]['rect'].height)
  1088. self.RedrawSelectBox(mapId)
  1089. self.Zoom(zoomFactor = 1, view = (0, 0))
  1090. self.mouse['use'] = 'pointer'
  1091. # recalculate the position of objects after dragging
  1092. if self.mouse['use'] in ('pointer', 'resize') and self.dragId != -1:
  1093. if self.mouse['begin'] != event.GetPosition(): #for double click
  1094. self.RecalculatePosition(ids = [self.dragId])
  1095. if self.instruction[self.dragId].type in self.openDialogs:
  1096. self.openDialogs[self.instruction[self.dragId].type].updateDialog()
  1097. # double click launches dialogs
  1098. elif event.LeftDClick():
  1099. if self.mouse['use'] == 'pointer' and self.dragId != -1:
  1100. itemCall = { 'text':self.parent.OnAddText, 'mapinfo': self.parent.OnAddMapinfo,
  1101. 'scalebar': self.parent.OnAddScalebar,
  1102. 'rasterLegend': self.parent.OnAddLegend, 'vectorLegend': self.parent.OnAddLegend,
  1103. 'map': self.parent.OnAddMap}
  1104. itemArg = { 'text': dict(event = None, id = self.dragId), 'mapinfo': dict(event = None),
  1105. 'scalebar': dict(event = None),
  1106. 'rasterLegend': dict(event = None), 'vectorLegend': dict(event = None, page = 1),
  1107. 'map': dict(event = None, notebook = True)}
  1108. type = self.instruction[self.dragId].type
  1109. itemCall[type](**itemArg[type])
  1110. def RecalculatePosition(self, ids):
  1111. for id in ids:
  1112. itype = self.instruction[id].type
  1113. if itype == 'map':
  1114. self.instruction[id]['rect'] = self.CanvasPaperCoordinates(rect = self.pdcObj.GetIdBounds(id),
  1115. canvasToPaper = True)
  1116. self.RecalculateEN()
  1117. elif itype in ('mapinfo' ,'rasterLegend', 'vectorLegend'):
  1118. self.instruction[id]['rect'] = self.CanvasPaperCoordinates(rect = self.pdcObj.GetIdBounds(id),
  1119. canvasToPaper = True)
  1120. self.instruction[id]['where'] = self.CanvasPaperCoordinates(rect = self.pdcObj.GetIdBounds(id),
  1121. canvasToPaper = True)[:2]
  1122. elif itype == 'scalebar':
  1123. self.instruction[id]['rect'] = self.CanvasPaperCoordinates(rect = self.pdcObj.GetIdBounds(id),
  1124. canvasToPaper = True)
  1125. self.instruction[id]['where'] = self.instruction[id]['rect'].GetCentre()
  1126. elif itype == 'text':
  1127. x, y = self.instruction[id]['coords'][0] - self.instruction[id]['xoffset'],\
  1128. self.instruction[id]['coords'][1] + self.instruction[id]['yoffset']
  1129. extent = self.parent.getTextExtent(textDict = self.instruction[id])
  1130. if self.instruction[id]['rotate'] is not None:
  1131. rot = float(self.instruction[id]['rotate'])/180*pi
  1132. else:
  1133. rot = 0
  1134. if self.instruction[id]['ref'].split()[0] == 'lower':
  1135. y += extent[1]
  1136. elif self.instruction[id]['ref'].split()[0] == 'center':
  1137. y += extent[1]/2
  1138. if self.instruction[id]['ref'].split()[1] == 'right':
  1139. x += extent[0] * cos(rot)
  1140. y -= extent[0] * sin(rot)
  1141. elif self.instruction[id]['ref'].split()[1] == 'center':
  1142. x += extent[0]/2 * cos(rot)
  1143. y -= extent[0]/2 * sin(rot)
  1144. self.instruction[id]['where'] = self.CanvasPaperCoordinates(rect = wx.Rect2D(x, y, 0, 0),
  1145. canvasToPaper = True)[:2]
  1146. self.RecalculateEN()
  1147. def ComputeZoom(self, rect):
  1148. """!Computes zoom factor and scroll view"""
  1149. zoomFactor = 1
  1150. cW, cH = self.GetClientSize()
  1151. cW = float(cW)
  1152. if rect.IsEmpty(): # clicked on canvas
  1153. zoomFactor = 1.5
  1154. if self.mouse['use'] == 'zoomout':
  1155. zoomFactor = 1./zoomFactor
  1156. x,y = self.mouse['begin']
  1157. xView = x - x/zoomFactor#x - cW/(zoomFactor * 2)
  1158. yView = y - y/zoomFactor#y - cH/(zoomFactor * 2)
  1159. else: #dragging
  1160. rW, rH = float(rect.GetWidth()), float(rect.GetHeight())
  1161. zoomFactor = 1/max(rW/cW, rH/cH)
  1162. # when zooming to full extent, in some cases, there was zoom 1.01..., which causes problem
  1163. if abs(zoomFactor - 1) > 0.01:
  1164. zoomFactor = zoomFactor
  1165. else:
  1166. zoomFactor = 1.
  1167. if self.mouse['use'] == 'zoomout':
  1168. zoomFactor = min(rW/cW, rH/cH)
  1169. if rW/rH > cW/cH:
  1170. yView = rect.GetY() - (rW*(cH/cW) - rH)/2
  1171. xView = rect.GetX()
  1172. if self.mouse['use'] == 'zoomout':
  1173. x,y = rect.GetX() + (rW-(cW/cH)*rH)/2, rect.GetY()
  1174. xView, yView = -x, -y
  1175. else:
  1176. xView = rect.GetX() - (rH*(cW/cH) - rW)/2
  1177. yView = rect.GetY()
  1178. if self.mouse['use'] == 'zoomout':
  1179. x,y = rect.GetX(), rect.GetY() + (rH-(cH/cW)*rW)/2
  1180. xView, yView = -x, -y
  1181. return zoomFactor, (int(xView), int(yView))
  1182. def Zoom(self, zoomFactor, view):
  1183. """! Zoom to specified region, scroll view, redraw"""
  1184. if not self.currScale:
  1185. return
  1186. self.currScale = self.currScale*zoomFactor
  1187. if self.currScale > 10 or self.currScale < 0.1:
  1188. self.currScale = self.currScale/zoomFactor
  1189. return
  1190. if not self.preview:
  1191. # redraw paper
  1192. pRect = self.pdcPaper.GetIdBounds(self.pageId)
  1193. pRect.OffsetXY(-view[0], -view[1])
  1194. pRect = self.ScaleRect(rect = pRect, scale = zoomFactor)
  1195. self.DrawPaper(pRect)
  1196. #redraw objects
  1197. for id in self.objectId:
  1198. oRect = self.CanvasPaperCoordinates(
  1199. rect = self.instruction[id]['rect'], canvasToPaper = False)
  1200. type = self.instruction[id].type
  1201. if type == 'text':
  1202. coords = self.instruction[id]['coords']# recalculate coordinates, they are not equal to BB
  1203. self.instruction[id]['coords'] = coords = [(int(coord) - view[i]) * zoomFactor
  1204. for i, coord in enumerate(coords)]
  1205. self.DrawRotText(pdc = self.pdcObj, drawId = id, textDict = self.instruction[id],
  1206. coords = coords, bounds = oRect )
  1207. extent = self.parent.getTextExtent(textDict = self.instruction[id])
  1208. if self.instruction[id]['rotate']:
  1209. rot = float(self.instruction[id]['rotate'])
  1210. else:
  1211. rot = 0
  1212. self.instruction[id]['rect'] = bounds = self.parent.getModifiedTextBounds(coords[0], coords[1], extent, rot)
  1213. self.pdcObj.SetIdBounds(id, bounds)
  1214. else:
  1215. self.Draw(pen = self.pen[type], brush = self.brush[type], pdc = self.pdcObj,
  1216. drawid = id, pdctype = 'rectText', bb = oRect)
  1217. #redraw tmp objects
  1218. if self.dragId != -1:
  1219. self.RedrawSelectBox(self.dragId)
  1220. #redraw preview
  1221. else: # preview mode
  1222. imageRect = self.pdcImage.GetIdBounds(self.imageId)
  1223. imageRect.OffsetXY(-view[0], -view[1])
  1224. imageRect = self.ScaleRect(rect = imageRect, scale = zoomFactor)
  1225. self.DrawImage(imageRect)
  1226. def ZoomAll(self):
  1227. """! Zoom to full extent"""
  1228. if not self.preview:
  1229. bounds = self.pdcPaper.GetIdBounds(self.pageId)
  1230. else:
  1231. bounds = self.pdcImage.GetIdBounds(self.imageId)
  1232. zoomP = bounds.Inflate(bounds.width/20, bounds.height/20)
  1233. zoomFactor, view = self.ComputeZoom(zoomP)
  1234. self.Zoom(zoomFactor, view)
  1235. def Draw(self, pen, brush, pdc, drawid = None, pdctype = 'rect', bb = wx.Rect(0,0,0,0)):
  1236. """! Draw object"""
  1237. if drawid is None:
  1238. drawid = wx.NewId()
  1239. bb = bb.Get()
  1240. pdc.BeginDrawing()
  1241. pdc.RemoveId(drawid)
  1242. pdc.SetId(drawid)
  1243. pdc.SetPen(pen)
  1244. pdc.SetBrush(brush)
  1245. if pdctype in ('rect', 'rectText'):
  1246. pdc.DrawRectangle(*bb)
  1247. if pdctype == 'rectText':
  1248. dc = wx.PaintDC(self) # dc created because of method GetTextExtent, which pseudoDC lacks
  1249. font = self.font
  1250. size = 10
  1251. font.SetPointSize(size)
  1252. font.SetStyle(wx.ITALIC)
  1253. dc.SetFont(font)
  1254. pdc.SetFont(font)
  1255. text = '\n'.join(self.itemLabels[self.instruction[drawid].type])
  1256. w,h,lh = dc.GetMultiLineTextExtent(text)
  1257. textExtent = (w,h)
  1258. textRect = wx.Rect(0, 0, *textExtent).CenterIn(bb)
  1259. r = map(int, bb)
  1260. while not wx.Rect(*r).ContainsRect(textRect) and size >= 8:
  1261. size -= 2
  1262. font.SetPointSize(size)
  1263. dc.SetFont(font)
  1264. pdc.SetFont(font)
  1265. textExtent = dc.GetTextExtent(text)
  1266. textRect = wx.Rect(0, 0, *textExtent).CenterIn(bb)
  1267. pdc.SetTextForeground(wx.Color(100,100,100,200))
  1268. pdc.SetBackgroundMode(wx.TRANSPARENT)
  1269. pdc.DrawText(text = text, x = textRect.x, y = textRect.y)
  1270. pdc.SetIdBounds(drawid, bb)
  1271. pdc.EndDrawing()
  1272. self.Refresh()
  1273. return drawid
  1274. def DrawRotText(self, pdc, drawId, textDict, coords, bounds):
  1275. if textDict['rotate']:
  1276. rot = float(textDict['rotate'])
  1277. else:
  1278. rot = 0
  1279. fontsize = textDict['fontsize'] * self.currScale
  1280. if textDict['background'] != 'none':
  1281. background = textDict['background']
  1282. else:
  1283. background = None
  1284. pdc.RemoveId(drawId)
  1285. pdc.SetId(drawId)
  1286. pdc.BeginDrawing()
  1287. # border is not redrawn when zoom changes, why?
  1288. ## if textDict['border'] != 'none' and not rot:
  1289. ## units = UnitConversion(self)
  1290. ## borderWidth = units.convert(value = textDict['width'],
  1291. ## fromUnit = 'point', toUnit = 'pixel' ) * self.currScale
  1292. ## pdc.SetPen(wx.Pen(colour = convertRGB(textDict['border']), width = borderWidth))
  1293. ## pdc.DrawRectangle(*bounds)
  1294. if background:
  1295. pdc.SetTextBackground(convertRGB(background))
  1296. pdc.SetBackgroundMode(wx.SOLID)
  1297. else:
  1298. pdc.SetBackgroundMode(wx.TRANSPARENT)
  1299. fn = self.parent.makePSFont(textDict)
  1300. pdc.SetFont(fn)
  1301. pdc.SetTextForeground(convertRGB(textDict['color']))
  1302. pdc.DrawRotatedText(textDict['text'], coords[0], coords[1], rot)
  1303. pdc.SetIdBounds(drawId, wx.Rect(*bounds))
  1304. self.Refresh()
  1305. pdc.EndDrawing()
  1306. def DrawImage(self, rect):
  1307. """!Draw preview image to pseudoDC"""
  1308. self.pdcImage.ClearId(self.imageId)
  1309. self.pdcImage.SetId(self.imageId)
  1310. img = self.image
  1311. if img.GetWidth() != rect.width or img.GetHeight() != rect.height:
  1312. img = img.Scale(rect.width, rect.height)
  1313. bitmap = img.ConvertToBitmap()
  1314. self.pdcImage.BeginDrawing()
  1315. self.pdcImage.DrawBitmap(bitmap, rect.x, rect.y)
  1316. self.pdcImage.SetIdBounds(self.imageId, rect)
  1317. self.pdcImage.EndDrawing()
  1318. self.Refresh()
  1319. def DrawPaper(self, rect):
  1320. """!Draw paper and margins"""
  1321. page = self.instruction[self.pageId]
  1322. scale = page['Width'] / rect.GetWidth()
  1323. w = (page['Width'] - page['Right'] - page['Left']) / scale
  1324. h = (page['Height'] - page['Top'] - page['Bottom']) / scale
  1325. x = page['Left'] / scale + rect.GetX()
  1326. y = page['Top'] / scale + rect.GetY()
  1327. self.pdcPaper.BeginDrawing()
  1328. self.pdcPaper.RemoveId(self.pageId)
  1329. self.pdcPaper.SetId(self.pageId)
  1330. self.pdcPaper.SetPen(self.pen['paper'])
  1331. self.pdcPaper.SetBrush(self.brush['paper'])
  1332. self.pdcPaper.DrawRectangleRect(rect)
  1333. self.pdcPaper.SetPen(self.pen['margins'])
  1334. self.pdcPaper.SetBrush(self.brush['margins'])
  1335. self.pdcPaper.DrawRectangle(x, y, w, h)
  1336. self.pdcPaper.SetIdBounds(self.pageId, rect)
  1337. self.pdcPaper.EndDrawing()
  1338. self.Refresh()
  1339. def ImageRect(self):
  1340. """!Returns image centered in canvas, computes scale"""
  1341. img = wx.Image(self.imgName, wx.BITMAP_TYPE_PNG)
  1342. cW, cH = self.GetClientSize()
  1343. iW, iH = img.GetWidth(), img.GetHeight()
  1344. self.currScale = min(float(cW)/iW, float(cH)/iH)
  1345. iW = iW * self.currScale
  1346. iH = iH * self.currScale
  1347. x = cW/2 - iW/2
  1348. y = cH/2 - iH/2
  1349. imageRect = wx.Rect(x, y, iW, iH)
  1350. return imageRect
  1351. def RedrawSelectBox(self, id):
  1352. """!Redraws select box when selected object changes its size"""
  1353. if self.dragId == id:
  1354. rect = [self.pdcObj.GetIdBounds(id).Inflate(3,3)]
  1355. type = ['select']
  1356. ids = [self.idBoxTmp]
  1357. if self.instruction[id].type == 'map':
  1358. controlP = self.pdcObj.GetIdBounds(id).GetBottomRight()
  1359. rect.append(wx.Rect(controlP.x, controlP.y, 10,10))
  1360. type.append('resize')
  1361. ids.append(self.idResizeBoxTmp)
  1362. for id, type, rect in zip(ids, type, rect):
  1363. self.Draw(pen = self.pen[type], brush = self.brush[type], pdc = self.pdcTmp,
  1364. drawid = id, pdctype = 'rect', bb = rect)
  1365. def UpdateMapLabel(self):
  1366. """!Updates map frame label"""
  1367. vector = self.instruction.FindInstructionByType('vector')
  1368. if vector:
  1369. vectorId = vector.id
  1370. else:
  1371. vectorId = None
  1372. raster = self.instruction.FindInstructionByType('raster')
  1373. if raster:
  1374. rasterId = raster.id
  1375. else:
  1376. rasterId = None
  1377. rasterName = 'None'
  1378. if rasterId:
  1379. rasterName = self.instruction[rasterId]['raster'].split('@')[0]
  1380. self.itemLabels['map'] = self.itemLabels['map'][0:1]
  1381. self.itemLabels['map'].append("raster: " + rasterName)
  1382. if vectorId:
  1383. for map in self.instruction[vectorId]['list']:
  1384. self.itemLabels['map'].append('vector: ' + map[0].split('@')[0])
  1385. def OnSize(self, event):
  1386. """!Init image size to match window size
  1387. """
  1388. # not zoom all when notebook page is changed
  1389. if self.preview and self.parent.currentPage == 1 or not self.preview and self.parent.currentPage == 0:
  1390. self.ZoomAll()
  1391. self.OnIdle(None)
  1392. event.Skip()
  1393. def OnIdle(self, event):
  1394. """!Only re-render a image during idle time instead of
  1395. multiple times during resizing.
  1396. """
  1397. width, height = self.GetClientSize()
  1398. # Make new off screen bitmap: this bitmap will always have the
  1399. # current drawing in it, so it can be used to save the image
  1400. # to a file, or whatever.
  1401. self._buffer = wx.EmptyBitmap(width, height)
  1402. # re-render image on idle
  1403. self.resize = True
  1404. def ScaleRect(self, rect, scale):
  1405. """! Scale rectangle"""
  1406. return wx.Rect(rect.GetLeft()*scale, rect.GetTop()*scale,
  1407. rect.GetSize()[0]*scale, rect.GetSize()[1]*scale)
  1408. def main():
  1409. import gettext
  1410. gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
  1411. app = wx.PySimpleApp()
  1412. wx.InitAllImageHandlers()
  1413. frame = PsMapFrame()
  1414. frame.Show()
  1415. app.MainLoop()
  1416. if __name__ == "__main__":
  1417. main()