psmap.py 70 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703
  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. fontsize = textDict['fontsize'] * self.canvas.currScale
  578. fontface = textDict['font'].split('-')[0]
  579. try:
  580. fontstyle = textDict['font'].split('-')[1]
  581. except:
  582. fontstyle = 'normal'
  583. if fontface == "Times":
  584. family = wx.FONTFAMILY_ROMAN
  585. face = "times"
  586. elif fontface == "Helvetica":
  587. family = wx.FONTFAMILY_SWISS
  588. face = 'helvetica'
  589. elif fontface == "Courier":
  590. family = wx.FONTFAMILY_TELETYPE
  591. face = 'courier'
  592. else:
  593. family = wx.FONTFAMILY_DEFAULT
  594. face = ''
  595. if fontstyle == 'normal':
  596. style = wx.FONTSTYLE_NORMAL
  597. weight = wx.FONTWEIGHT_NORMAL
  598. if 'oblique' in fontstyle:
  599. style = wx.FONTSTYLE_SLANT
  600. if 'italic' in fontstyle:
  601. style = wx.FONTSTYLE_ITALIC
  602. if 'bold' in fontstyle:
  603. weight = wx.FONTWEIGHT_BOLD
  604. else:
  605. weight = wx.FONTWEIGHT_NORMAL
  606. try:
  607. fn = wx.Font(pointSize=fontsize, family=wx.FONTFAMILY_DEFAULT,
  608. style=wx.FONTSTYLE_NORMAL, weight=wx.FONTWEIGHT_NORMAL)
  609. except:
  610. fn = wx.Font(pointSize=10, family=family, style=style, weight=weight, face=face)
  611. return fn
  612. def getTextExtent(self, textDict):
  613. #fontsize = str(fontsize if fontsize >= 4 else 4)
  614. dc = wx.PaintDC(self) # dc created because of method GetTextExtent, which pseudoDC lacks
  615. fn = self.makePSFont(textDict)
  616. try:
  617. dc.SetFont(fn)
  618. w,h,lh = dc.GetMultiLineTextExtent(textDict['text'])
  619. return (w,h)
  620. except:
  621. return (0,0)
  622. def getInitMap(self):
  623. """!Create default map frame when no map is selected, needed for coordinates in map units"""
  624. instrFile = grass.tempfile()
  625. instrFileFd = open(instrFile, mode = 'w')
  626. instrFileFd.write(self.InstructionFile())
  627. instrFileFd.flush()
  628. instrFileFd.close()
  629. mapInitRect = GetMapBounds(instrFile)
  630. grass.try_remove(instrFile)
  631. region = grass.region()
  632. units = UnitConversion(self)
  633. realWidth = units.convert(value = abs(region['w'] - region['e']), fromUnit = 'meter', toUnit = 'inch')
  634. scale = mapInitRect.Get()[2]/realWidth
  635. initMap = self.instruction.FindInstructionByType('initMap')
  636. if initMap:
  637. id = initMap.id
  638. else:
  639. id = None
  640. if not id:
  641. id = wx.NewId()
  642. initMap = InitMap(id)
  643. self.instruction.AddInstruction(initMap)
  644. self.instruction[id].SetInstruction(dict(rect = mapInitRect, scale = scale))
  645. def OnDelete(self, event):
  646. if self.canvas.dragId != -1 and self.currentPage == 0:
  647. if self.instruction[self.canvas.dragId].type == 'map':
  648. self.deleteObject(self.canvas.dragId)
  649. self.getInitMap()
  650. self.canvas.RecalculateEN()
  651. else:
  652. self.deleteObject(self.canvas.dragId)
  653. def deleteObject(self, id):
  654. """!Deletes object, his id and redraws"""
  655. #delete from canvas
  656. self.canvas.pdcObj.RemoveId(id)
  657. if id == self.canvas.dragId:
  658. self.canvas.pdcTmp.RemoveAll()
  659. self.canvas.dragId = -1
  660. self.canvas.Refresh()
  661. # delete from instructions
  662. del self.instruction[id]
  663. def DialogDataChanged(self, id):
  664. ids = id
  665. if type(id) == int:
  666. ids = [id]
  667. for id in ids:
  668. itype = self.instruction[id].type
  669. if itype in ('scalebar', 'mapinfo'):
  670. drawRectangle = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'], canvasToPaper = False)
  671. self.canvas.Draw(pen = self.pen[itype], brush = self.brush[itype],
  672. pdc = self.canvas.pdcObj, drawid = id, pdctype = 'rectText', bb = drawRectangle)
  673. self.canvas.RedrawSelectBox(id)
  674. if itype == 'text':
  675. if self.instruction[id]['rotate']:
  676. rot = float(self.instruction[id]['rotate'])
  677. else:
  678. rot = 0
  679. extent = self.getTextExtent(textDict = self.instruction[id].GetInstruction())
  680. rect = wx.Rect2D(self.instruction[id]['where'][0], self.instruction[id]['where'][1], 0, 0)
  681. self.instruction[id]['coords'] = list(self.canvas.CanvasPaperCoordinates(rect = rect, canvasToPaper = False)[:2])
  682. #computes text coordinates according to reference point, not precisely
  683. if self.instruction[id]['ref'].split()[0] == 'lower':
  684. self.instruction[id]['coords'][1] -= extent[1]
  685. elif self.instruction[id]['ref'].split()[0] == 'center':
  686. self.instruction[id]['coords'][1] -= extent[1]/2
  687. if self.instruction[id]['ref'].split()[1] == 'right':
  688. self.instruction[id]['coords'][0] -= extent[0] * cos(rot/180*pi)
  689. self.instruction[id]['coords'][1] += extent[0] * sin(rot/180*pi)
  690. elif self.instruction[id]['ref'].split()[1] == 'center':
  691. self.instruction[id]['coords'][0] -= extent[0]/2 * cos(rot/180*pi)
  692. self.instruction[id]['coords'][1] += extent[0]/2 * sin(rot/180*pi)
  693. self.instruction[id]['coords'][0] += self.instruction[id]['xoffset']
  694. self.instruction[id]['coords'][1] -= self.instruction[id]['yoffset']
  695. coords = self.instruction[id]['coords']
  696. self.instruction[id]['rect'] = bounds = self.getModifiedTextBounds(coords[0], coords[1], extent, rot)
  697. self.canvas.DrawRotText(pdc = self.canvas.pdcObj, drawId = id,
  698. textDict = self.instruction[id].GetInstruction(),
  699. coords = coords, bounds = bounds)
  700. self.canvas.RedrawSelectBox(id)
  701. if itype in ('map', 'vector', 'raster'):
  702. if itype == 'raster':#set resolution
  703. resol = RunCommand('r.info', read = True, flags = 's', map = self.instruction[id]['raster'])
  704. resol = grass.parse_key_val(resol, val_type = float)
  705. RunCommand('g.region', nsres = resol['nsres'], ewres = resol['ewres'])
  706. # change current raster in raster legend
  707. if 'rasterLegend' in self.openDialogs:
  708. self.openDialogs['rasterLegend'].updateDialog()
  709. id = self.instruction.FindInstructionByType('map').id
  710. #check resolution
  711. if itype == 'raster':
  712. SetResolution(dpi = self.instruction[id]['resolution'],
  713. width = self.instruction[id]['rect'].width,
  714. height = self.instruction[id]['rect'].height)
  715. rectCanvas = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'],
  716. canvasToPaper = False)
  717. self.canvas.RecalculateEN()
  718. self.canvas.UpdateMapLabel()
  719. self.canvas.Draw(pen = self.pen['map'], brush = self.brush['map'],
  720. pdc = self.canvas.pdcObj, drawid = id, pdctype = 'rectText', bb = rectCanvas)
  721. # redraw select box
  722. self.canvas.RedrawSelectBox(id)
  723. self.canvas.pdcTmp.RemoveId(self.canvas.idZoomBoxTmp)
  724. # redraw to get map to the bottom layer
  725. #self.canvas.Zoom(zoomFactor = 1, view = (0, 0))
  726. if itype == 'rasterLegend':
  727. if self.instruction[id]['rLegend']:
  728. drawRectangle = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'], canvasToPaper = False)
  729. self.canvas.Draw(pen = self.pen[itype], brush = self.brush[itype],
  730. pdc = self.canvas.pdcObj, drawid = id, pdctype = 'rectText', bb = drawRectangle)
  731. self.canvas.RedrawSelectBox(id)
  732. else:
  733. self.deleteObject(id)
  734. if itype == 'vectorLegend':
  735. if not self.instruction.FindInstructionByType('vector'):
  736. self.deleteObject(id)
  737. elif self.instruction[id]['vLegend']:
  738. drawRectangle = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'], canvasToPaper = False)
  739. self.canvas.Draw(pen = self.pen[itype], brush = self.brush[itype],
  740. pdc = self.canvas.pdcObj, drawid = id, pdctype = 'rectText', bb = drawRectangle)
  741. self.canvas.RedrawSelectBox(id)
  742. else:
  743. self.deleteObject(id)
  744. def OnPageChanged(self, event):
  745. """!Flatnotebook page has changed"""
  746. self.currentPage = self.book.GetPageIndex(self.book.GetCurrentPage())
  747. def OnPageChanging(self, event):
  748. """!Flatnotebook page is changing"""
  749. if self.currentPage == 0 and self.mouse['use'] == 'addMap':
  750. event.Veto()
  751. def OnHelp(self, event):
  752. """!Show help"""
  753. if self.parent and self.parent.GetName() == 'LayerManager':
  754. log = self.parent.GetLogWindow()
  755. log.RunCmd(['g.manual',
  756. 'entry=wxGUI.PsMap'])
  757. else:
  758. RunCommand('g.manual',
  759. quiet = True,
  760. entry = 'wxGUI.PsMap')
  761. def OnAbout(self, event):
  762. """!Display About window"""
  763. info = wx.AboutDialogInfo()
  764. info.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
  765. info.SetName(_('wxGUI Hardcopy Map Utility'))
  766. info.SetWebSite('http://grass.osgeo.org')
  767. info.SetDescription(_('(C) 2011 by the GRASS Development Team\n\n') +
  768. '\n'.join(textwrap.wrap(_('This program is free software under the GNU General Public License'
  769. '(>=v2). Read the file COPYING that comes with GRASS for details.'), 75)))
  770. wx.AboutBox(info)
  771. def OnCloseWindow(self, event):
  772. """!Close window"""
  773. try:
  774. os.remove(self.imgName)
  775. except OSError:
  776. pass
  777. grass.set_raise_on_error(False)
  778. self.Destroy()
  779. class PsMapBufferedWindow(wx.Window):
  780. """!A buffered window class.
  781. @param parent parent window
  782. @param kwargs other wx.Window parameters
  783. """
  784. def __init__(self, parent, id = wx.ID_ANY,
  785. style = wx.NO_FULL_REPAINT_ON_RESIZE,
  786. **kwargs):
  787. wx.Window.__init__(self, parent, id = id, style = style)
  788. self.parent = parent
  789. self.FitInside()
  790. # store an off screen empty bitmap for saving to file
  791. self._buffer = None
  792. # indicates whether or not a resize event has taken place
  793. self.resize = False
  794. self.mouse = kwargs['mouse']
  795. self.cursors = kwargs['cursors']
  796. self.preview = kwargs['preview']
  797. self.pen = kwargs['pen']
  798. self.brush = kwargs['brush']
  799. if kwargs.has_key('instruction'):
  800. self.instruction = kwargs['instruction']
  801. if kwargs.has_key('openDialogs'):
  802. self.openDialogs = kwargs['openDialogs']
  803. if kwargs.has_key('pageId'):
  804. self.pageId = kwargs['pageId']
  805. if kwargs.has_key('objectId'):
  806. self.objectId = kwargs['objectId']
  807. #labels
  808. self.itemLabels = { 'map': ['MAP FRAME'],
  809. 'rasterLegend': ['RASTER LEGEND'],
  810. 'vectorLegend': ['VECTOR LEGEND'],
  811. 'mapinfo': ['MAP INFO'],
  812. 'scalebar': ['SCALE BAR']}
  813. # define PseudoDC
  814. self.pdc = wx.PseudoDC()
  815. self.pdcObj = wx.PseudoDC()
  816. self.pdcPaper = wx.PseudoDC()
  817. self.pdcTmp = wx.PseudoDC()
  818. self.pdcImage = wx.PseudoDC()
  819. dc = wx.PaintDC(self)
  820. self.font = dc.GetFont()
  821. self.SetClientSize((700,510))#?
  822. self._buffer = wx.EmptyBitmap(*self.GetClientSize())
  823. self.idBoxTmp = wx.NewId()
  824. self.idZoomBoxTmp = wx.NewId()
  825. self.idResizeBoxTmp = wx.NewId()
  826. self.dragId = -1
  827. if self.preview:
  828. self.image = None
  829. self.imageId = 2000
  830. self.imgName = self.parent.imgName
  831. self.currScale = None
  832. self.Clear()
  833. self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: None)
  834. self.Bind(wx.EVT_PAINT, self.OnPaint)
  835. self.Bind(wx.EVT_SIZE, self.OnSize)
  836. self.Bind(wx.EVT_IDLE, self.OnIdle)
  837. self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouse)
  838. def Clear(self):
  839. """!Clear canvas and set paper
  840. """
  841. bg = wx.LIGHT_GREY_BRUSH
  842. self.pdcPaper.BeginDrawing()
  843. self.pdcPaper.SetBackground(bg)
  844. self.pdcPaper.Clear()
  845. self.pdcPaper.EndDrawing()
  846. self.pdcObj.RemoveAll()
  847. self.pdcTmp.RemoveAll()
  848. if not self.preview:
  849. self.SetPage()
  850. def CanvasPaperCoordinates(self, rect, canvasToPaper = True):
  851. """!Converts canvas (pixel) -> paper (inch) coordinates and size and vice versa"""
  852. units = UnitConversion(self)
  853. fromU = 'pixel'
  854. toU = 'inch'
  855. pRect = self.pdcPaper.GetIdBounds(self.pageId)
  856. pRectx, pRecty = pRect.x, pRect.y
  857. scale = 1/self.currScale
  858. if not canvasToPaper: # paper -> canvas
  859. fromU = 'inch'
  860. toU = 'pixel'
  861. scale = self.currScale
  862. pRectx = units.convert(value = - pRect.x, fromUnit = 'pixel', toUnit = 'inch' ) /scale #inch, real, negative
  863. pRecty = units.convert(value = - pRect.y, fromUnit = 'pixel', toUnit = 'inch' ) /scale
  864. Width = units.convert(value = rect.width, fromUnit = fromU, toUnit = toU) * scale
  865. Height = units.convert(value = rect.height, fromUnit = fromU, toUnit = toU) * scale
  866. X = units.convert(value = (rect.x - pRectx), fromUnit = fromU, toUnit = toU) * scale
  867. Y = units.convert(value = (rect.y - pRecty), fromUnit = fromU, toUnit = toU) * scale
  868. return wx.Rect2D(X, Y, Width, Height)
  869. def SetPage(self):
  870. """!Sets and changes page, redraws paper"""
  871. page = self.instruction[self.pageId]
  872. if not page:
  873. page = PageSetup(id = self.pageId)
  874. self.instruction.AddInstruction(page)
  875. ppi = wx.PaintDC(self).GetPPI()
  876. cW, cH = self.GetClientSize()
  877. pW, pH = page['Width']*ppi[0], page['Height']*ppi[1]
  878. if self.currScale is None:
  879. self.currScale = min(cW/pW, cH/pH)
  880. pW = pW * self.currScale
  881. pH = pH * self.currScale
  882. x = cW/2 - pW/2
  883. y = cH/2 - pH/2
  884. self.DrawPaper(wx.Rect(x, y, pW, pH))
  885. def modifyRectangle(self, r):
  886. """! Recalculates rectangle not to have negative size"""
  887. if r.GetWidth() < 0:
  888. r.SetX(r.GetX() + r.GetWidth())
  889. if r.GetHeight() < 0:
  890. r.SetY(r.GetY() + r.GetHeight())
  891. r.SetWidth(abs(r.GetWidth()))
  892. r.SetHeight(abs(r.GetHeight()))
  893. return r
  894. def RecalculateEN(self):
  895. """!Recalculate east and north for texts (eps, points) after their or map's movement"""
  896. try:
  897. mapId = self.instruction.FindInstructionByType('map').id
  898. except AttributeError:
  899. mapId = self.instruction.FindInstructionByType('initMap').id
  900. texts = self.instruction.FindInstructionByType('text', list = True)
  901. for text in texts:
  902. e, n = PaperMapCoordinates(map = self.instruction[mapId], x = self.instruction[text.id]['where'][0],
  903. y = self.instruction[text.id]['where'][1], paperToMap = True)
  904. self.instruction[text.id]['east'], self.instruction[text.id]['north'] = e, n
  905. def OnPaint(self, event):
  906. """!Draw pseudo DC to buffer
  907. """
  908. if not self._buffer:
  909. return
  910. dc = wx.BufferedPaintDC(self, self._buffer)
  911. # use PrepareDC to set position correctly
  912. self.PrepareDC(dc)
  913. dc.SetBackground(wx.LIGHT_GREY_BRUSH)
  914. dc.Clear()
  915. # draw paper
  916. if not self.preview:
  917. self.pdcPaper.DrawToDC(dc)
  918. # draw to the DC using the calculated clipping rect
  919. rgn = self.GetUpdateRegion()
  920. if not self.preview:
  921. self.pdcObj.DrawToDCClipped(dc, rgn.GetBox())
  922. else:
  923. self.pdcImage.DrawToDCClipped(dc, rgn.GetBox())
  924. self.pdcTmp.DrawToDCClipped(dc, rgn.GetBox())
  925. def OnMouse(self, event):
  926. if event.GetWheelRotation():
  927. zoom = event.GetWheelRotation()
  928. use = self.mouse['use']
  929. self.mouse['begin'] = event.GetPosition()
  930. if zoom > 0:
  931. self.mouse['use'] = 'zoomin'
  932. else:
  933. self.mouse['use'] = 'zoomout'
  934. zoomFactor, view = self.ComputeZoom(wx.Rect(0,0,0,0))
  935. self.Zoom(zoomFactor, view)
  936. self.mouse['use'] = use
  937. if event.Moving():
  938. if self.mouse['use'] in ('pointer', 'resize'):
  939. pos = event.GetPosition()
  940. foundResize = self.pdcTmp.FindObjects(pos[0], pos[1])
  941. if foundResize and foundResize[0] == self.idResizeBoxTmp:
  942. self.SetCursor(self.cursors["sizenwse"])
  943. self.parent.SetStatusText(_('Click and drag to resize object'), 0)
  944. else:
  945. self.parent.SetStatusText('', 0)
  946. self.SetCursor(self.cursors["default"])
  947. elif event.LeftDown():
  948. self.mouse['begin'] = event.GetPosition()
  949. self.begin = self.mouse['begin']
  950. if self.mouse['use'] in ('pan', 'zoomin', 'zoomout', 'addMap'):
  951. pass
  952. #select
  953. if self.mouse['use'] == 'pointer':
  954. found = self.pdcObj.FindObjects(self.mouse['begin'][0], self.mouse['begin'][1])
  955. foundResize = self.pdcTmp.FindObjects(self.mouse['begin'][0], self.mouse['begin'][1])
  956. if foundResize and foundResize[0] == self.idResizeBoxTmp:
  957. self.mouse['use'] = 'resize'
  958. # when resizing, proportions match region
  959. if self.instruction[self.dragId].type == 'map':
  960. self.constraint = False
  961. self.mapBounds = self.pdcObj.GetIdBounds(self.dragId)
  962. if self.instruction[self.dragId]['scaleType'] in (0, 1, 2):
  963. self.constraint = True
  964. self.mapBounds = self.pdcObj.GetIdBounds(self.dragId)
  965. elif found:
  966. self.dragId = found[0]
  967. self.RedrawSelectBox(self.dragId)
  968. if self.instruction[self.dragId].type != 'map':
  969. self.pdcTmp.RemoveId(self.idResizeBoxTmp)
  970. self.Refresh()
  971. else:
  972. self.dragId = -1
  973. self.pdcTmp.RemoveId(self.idBoxTmp)
  974. self.pdcTmp.RemoveId(self.idResizeBoxTmp)
  975. self.Refresh()
  976. elif event.Dragging() and event.LeftIsDown():
  977. #draw box when zooming, creating map
  978. if self.mouse['use'] in ('zoomin', 'zoomout', 'addMap'):
  979. self.mouse['end'] = event.GetPosition()
  980. r = wx.Rect(self.mouse['begin'][0], self.mouse['begin'][1],
  981. self.mouse['end'][0]-self.mouse['begin'][0], self.mouse['end'][1]-self.mouse['begin'][1])
  982. r = self.modifyRectangle(r)
  983. self.Draw(pen = self.pen['box'], brush = self.brush['box'], pdc = self.pdcTmp, drawid = self.idZoomBoxTmp,
  984. pdctype = 'rect', bb = r)
  985. # panning
  986. if self.mouse["use"] == 'pan':
  987. self.mouse['end'] = event.GetPosition()
  988. view = self.mouse['begin'][0] - self.mouse['end'][0], self.mouse['begin'][1] - self.mouse['end'][1]
  989. zoomFactor = 1
  990. self.Zoom(zoomFactor, view)
  991. self.mouse['begin'] = event.GetPosition()
  992. #move object
  993. if self.mouse['use'] == 'pointer' and self.dragId != -1:
  994. self.mouse['end'] = event.GetPosition()
  995. dx, dy = self.mouse['end'][0] - self.begin[0], self.mouse['end'][1] - self.begin[1]
  996. self.pdcObj.TranslateId(self.dragId, dx, dy)
  997. self.pdcTmp.TranslateId(self.idBoxTmp, dx, dy)
  998. self.pdcTmp.TranslateId(self.idResizeBoxTmp, dx, dy)
  999. if self.instruction[self.dragId].type == 'text':
  1000. self.instruction[self.dragId]['coords'] = self.instruction[self.dragId]['coords'][0] + dx,\
  1001. self.instruction[self.dragId]['coords'][1] + dy
  1002. self.begin = event.GetPosition()
  1003. self.Refresh()
  1004. # resize object
  1005. if self.mouse['use'] == 'resize':
  1006. type = self.instruction[self.dragId].type
  1007. pos = event.GetPosition()
  1008. x, y = self.mapBounds.GetX(), self.mapBounds.GetY()
  1009. width, height = self.mapBounds.GetWidth(), self.mapBounds.GetHeight()
  1010. diffX = pos[0] - self.mouse['begin'][0]
  1011. diffY = pos[1] - self.mouse['begin'][1]
  1012. # match given region
  1013. if self.constraint:
  1014. if width > height:
  1015. newWidth = width + diffX
  1016. newHeight = height + diffX * (float(height) / width)
  1017. else:
  1018. newWidth = width + diffY * (float(width) / height)
  1019. newHeight = height + diffY
  1020. else:
  1021. newWidth = width + diffX
  1022. newHeight = height + diffY
  1023. if newWidth < 10 or newHeight < 10:
  1024. return
  1025. bounds = wx.Rect(x, y, newWidth, newHeight)
  1026. self.Draw(pen = self.pen[type], brush = self.brush[type], pdc = self.pdcObj, drawid = self.dragId,
  1027. pdctype = 'rectText', bb = bounds)
  1028. self.RedrawSelectBox(self.dragId)
  1029. elif event.LeftUp():
  1030. # zoom in, zoom out
  1031. if self.mouse['use'] in ('zoomin','zoomout'):
  1032. zoomR = self.pdcTmp.GetIdBounds(self.idZoomBoxTmp)
  1033. self.pdcTmp.RemoveId(self.idZoomBoxTmp)
  1034. self.Refresh()
  1035. zoomFactor, view = self.ComputeZoom(zoomR)
  1036. self.Zoom(zoomFactor, view)
  1037. # draw map frame
  1038. if self.mouse['use'] == 'addMap':
  1039. rectTmp = self.pdcTmp.GetIdBounds(self.idZoomBoxTmp)
  1040. # too small rectangle, it's usually some mistake
  1041. if rectTmp.GetWidth() < 20 or rectTmp.GetHeight() < 20:
  1042. self.pdcTmp.RemoveId(self.idZoomBoxTmp)
  1043. self.Refresh()
  1044. return
  1045. rectPaper = self.CanvasPaperCoordinates(rect = rectTmp, canvasToPaper = True)
  1046. dlg = MapDialog(parent = self.parent, id = [None, None, None], settings = self.instruction,
  1047. rect = rectPaper)
  1048. self.openDialogs['map'] = dlg
  1049. self.openDialogs['map'].Show()
  1050. self.mouse['use'] = self.parent.mouseOld
  1051. self.SetCursor(self.parent.cursorOld)
  1052. self.parent.toolbar.ToggleTool(self.parent.actionOld, True)
  1053. self.parent.toolbar.ToggleTool(self.parent.toolbar.action['id'], False)
  1054. self.parent.toolbar.action['id'] = self.parent.actionOld
  1055. # resize resizable objects (only map sofar)
  1056. if self.mouse['use'] == 'resize':
  1057. mapId = self.instruction.FindInstructionByType('map').id
  1058. if self.dragId == mapId:
  1059. # necessary to change either map frame (scaleType 0,1,2) or region (scaletype 3)
  1060. newRectCanvas = self.pdcObj.GetIdBounds(mapId)
  1061. newRectPaper = self.CanvasPaperCoordinates(rect = newRectCanvas, canvasToPaper = True)
  1062. self.instruction[mapId]['rect'] = newRectPaper
  1063. if self.instruction[mapId]['scaleType'] in (0, 1, 2):
  1064. if self.instruction[mapId]['scaleType'] == 0:
  1065. scale, foo, rect = AutoAdjust(self, scaleType = 0,
  1066. map = self.instruction[mapId]['map'],
  1067. mapType = self.instruction[mapId]['mapType'],
  1068. rect = self.instruction[mapId]['rect'])
  1069. elif self.instruction[mapId]['scaleType'] == 1:
  1070. scale, foo, rect = AutoAdjust(self, scaleType = 1,
  1071. region = self.instruction[mapId]['region'],
  1072. rect = self.instruction[mapId]['rect'])
  1073. else:
  1074. scale, foo, rect = AutoAdjust(self, scaleType = 2,
  1075. rect = self.instruction[mapId]['rect'])
  1076. self.instruction[mapId]['rect'] = rect
  1077. self.instruction[mapId]['scale'] = scale
  1078. rectCanvas = self.CanvasPaperCoordinates(rect = rect, canvasToPaper = False)
  1079. self.Draw(pen = self.pen['map'], brush = self.brush['map'],
  1080. pdc = self.pdcObj, drawid = mapId, pdctype = 'rectText', bb = rectCanvas)
  1081. elif self.instruction[mapId]['scaleType'] == 3:
  1082. ComputeSetRegion(self, mapDict = self.instruction[mapId].GetInstruction())
  1083. #check resolution
  1084. SetResolution(dpi = self.instruction[mapId]['resolution'],
  1085. width = self.instruction[mapId]['rect'].width,
  1086. height = self.instruction[mapId]['rect'].height)
  1087. self.RedrawSelectBox(mapId)
  1088. self.Zoom(zoomFactor = 1, view = (0, 0))
  1089. self.mouse['use'] = 'pointer'
  1090. # recalculate the position of objects after dragging
  1091. if self.mouse['use'] in ('pointer', 'resize') and self.dragId != -1:
  1092. if self.mouse['begin'] != event.GetPosition(): #for double click
  1093. self.RecalculatePosition(ids = [self.dragId])
  1094. if self.instruction[self.dragId].type in self.openDialogs:
  1095. self.openDialogs[self.instruction[self.dragId].type].updateDialog()
  1096. # double click launches dialogs
  1097. elif event.LeftDClick():
  1098. if self.mouse['use'] == 'pointer' and self.dragId != -1:
  1099. itemCall = { 'text':self.parent.OnAddText, 'mapinfo': self.parent.OnAddMapinfo,
  1100. 'scalebar': self.parent.OnAddScalebar,
  1101. 'rasterLegend': self.parent.OnAddLegend, 'vectorLegend': self.parent.OnAddLegend,
  1102. 'map': self.parent.OnAddMap}
  1103. itemArg = { 'text': dict(event = None, id = self.dragId), 'mapinfo': dict(event = None),
  1104. 'scalebar': dict(event = None),
  1105. 'rasterLegend': dict(event = None), 'vectorLegend': dict(event = None, page = 1),
  1106. 'map': dict(event = None, notebook = True)}
  1107. type = self.instruction[self.dragId].type
  1108. itemCall[type](**itemArg[type])
  1109. def RecalculatePosition(self, ids):
  1110. for id in ids:
  1111. itype = self.instruction[id].type
  1112. if itype == 'map':
  1113. self.instruction[id]['rect'] = self.CanvasPaperCoordinates(rect = self.pdcObj.GetIdBounds(id),
  1114. canvasToPaper = True)
  1115. self.RecalculateEN()
  1116. elif itype in ('mapinfo' ,'rasterLegend', 'vectorLegend'):
  1117. self.instruction[id]['rect'] = self.CanvasPaperCoordinates(rect = self.pdcObj.GetIdBounds(id),
  1118. canvasToPaper = True)
  1119. self.instruction[id]['where'] = self.CanvasPaperCoordinates(rect = self.pdcObj.GetIdBounds(id),
  1120. canvasToPaper = True)[:2]
  1121. elif itype == 'scalebar':
  1122. self.instruction[id]['rect'] = self.CanvasPaperCoordinates(rect = self.pdcObj.GetIdBounds(id),
  1123. canvasToPaper = True)
  1124. self.instruction[id]['where'] = self.instruction[id]['rect'].GetCentre()
  1125. elif itype == 'text':
  1126. x, y = self.instruction[id]['coords'][0] - self.instruction[id]['xoffset'],\
  1127. self.instruction[id]['coords'][1] + self.instruction[id]['yoffset']
  1128. extent = self.parent.getTextExtent(textDict = self.instruction[id])
  1129. if self.instruction[id]['rotate'] is not None:
  1130. rot = float(self.instruction[id]['rotate'])/180*pi
  1131. else:
  1132. rot = 0
  1133. if self.instruction[id]['ref'].split()[0] == 'lower':
  1134. y += extent[1]
  1135. elif self.instruction[id]['ref'].split()[0] == 'center':
  1136. y += extent[1]/2
  1137. if self.instruction[id]['ref'].split()[1] == 'right':
  1138. x += extent[0] * cos(rot)
  1139. y -= extent[0] * sin(rot)
  1140. elif self.instruction[id]['ref'].split()[1] == 'center':
  1141. x += extent[0]/2 * cos(rot)
  1142. y -= extent[0]/2 * sin(rot)
  1143. self.instruction[id]['where'] = self.CanvasPaperCoordinates(rect = wx.Rect2D(x, y, 0, 0),
  1144. canvasToPaper = True)[:2]
  1145. self.RecalculateEN()
  1146. def ComputeZoom(self, rect):
  1147. """!Computes zoom factor and scroll view"""
  1148. zoomFactor = 1
  1149. cW, cH = self.GetClientSize()
  1150. cW = float(cW)
  1151. if rect.IsEmpty(): # clicked on canvas
  1152. zoomFactor = 1.5
  1153. if self.mouse['use'] == 'zoomout':
  1154. zoomFactor = 1./zoomFactor
  1155. x,y = self.mouse['begin']
  1156. xView = x - x/zoomFactor#x - cW/(zoomFactor * 2)
  1157. yView = y - y/zoomFactor#y - cH/(zoomFactor * 2)
  1158. else: #dragging
  1159. rW, rH = float(rect.GetWidth()), float(rect.GetHeight())
  1160. zoomFactor = 1/max(rW/cW, rH/cH)
  1161. # when zooming to full extent, in some cases, there was zoom 1.01..., which causes problem
  1162. if abs(zoomFactor - 1) > 0.01:
  1163. zoomFactor = zoomFactor
  1164. else:
  1165. zoomFactor = 1.
  1166. if self.mouse['use'] == 'zoomout':
  1167. zoomFactor = min(rW/cW, rH/cH)
  1168. if rW/rH > cW/cH:
  1169. yView = rect.GetY() - (rW*(cH/cW) - rH)/2
  1170. xView = rect.GetX()
  1171. if self.mouse['use'] == 'zoomout':
  1172. x,y = rect.GetX() + (rW-(cW/cH)*rH)/2, rect.GetY()
  1173. xView, yView = -x, -y
  1174. else:
  1175. xView = rect.GetX() - (rH*(cW/cH) - rW)/2
  1176. yView = rect.GetY()
  1177. if self.mouse['use'] == 'zoomout':
  1178. x,y = rect.GetX(), rect.GetY() + (rH-(cH/cW)*rW)/2
  1179. xView, yView = -x, -y
  1180. return zoomFactor, (int(xView), int(yView))
  1181. def Zoom(self, zoomFactor, view):
  1182. """! Zoom to specified region, scroll view, redraw"""
  1183. if not self.currScale:
  1184. return
  1185. self.currScale = self.currScale*zoomFactor
  1186. if self.currScale > 10 or self.currScale < 0.1:
  1187. self.currScale = self.currScale/zoomFactor
  1188. return
  1189. if not self.preview:
  1190. # redraw paper
  1191. pRect = self.pdcPaper.GetIdBounds(self.pageId)
  1192. pRect.OffsetXY(-view[0], -view[1])
  1193. pRect = self.ScaleRect(rect = pRect, scale = zoomFactor)
  1194. self.DrawPaper(pRect)
  1195. #redraw objects
  1196. for id in self.objectId:
  1197. oRect = self.CanvasPaperCoordinates(
  1198. rect = self.instruction[id]['rect'], canvasToPaper = False)
  1199. type = self.instruction[id].type
  1200. if type == 'text':
  1201. coords = self.instruction[id]['coords']# recalculate coordinates, they are not equal to BB
  1202. self.instruction[id]['coords'] = coords = [(int(coord) - view[i]) * zoomFactor
  1203. for i, coord in enumerate(coords)]
  1204. self.DrawRotText(pdc = self.pdcObj, drawId = id, textDict = self.instruction[id],
  1205. coords = coords, bounds = oRect )
  1206. extent = self.parent.getTextExtent(textDict = self.instruction[id])
  1207. if self.instruction[id]['rotate']:
  1208. rot = float(self.instruction[id]['rotate'])
  1209. else:
  1210. rot = 0
  1211. self.instruction[id]['rect'] = bounds = self.parent.getModifiedTextBounds(coords[0], coords[1], extent, rot)
  1212. self.pdcObj.SetIdBounds(id, bounds)
  1213. else:
  1214. self.Draw(pen = self.pen[type], brush = self.brush[type], pdc = self.pdcObj,
  1215. drawid = id, pdctype = 'rectText', bb = oRect)
  1216. #redraw tmp objects
  1217. if self.dragId != -1:
  1218. self.RedrawSelectBox(self.dragId)
  1219. #redraw preview
  1220. else: # preview mode
  1221. imageRect = self.pdcImage.GetIdBounds(self.imageId)
  1222. imageRect.OffsetXY(-view[0], -view[1])
  1223. imageRect = self.ScaleRect(rect = imageRect, scale = zoomFactor)
  1224. self.DrawImage(imageRect)
  1225. def ZoomAll(self):
  1226. """! Zoom to full extent"""
  1227. if not self.preview:
  1228. bounds = self.pdcPaper.GetIdBounds(self.pageId)
  1229. else:
  1230. bounds = self.pdcImage.GetIdBounds(self.imageId)
  1231. zoomP = bounds.Inflate(bounds.width/20, bounds.height/20)
  1232. zoomFactor, view = self.ComputeZoom(zoomP)
  1233. self.Zoom(zoomFactor, view)
  1234. def Draw(self, pen, brush, pdc, drawid = None, pdctype = 'rect', bb = wx.Rect(0,0,0,0)):
  1235. """! Draw object"""
  1236. if drawid is None:
  1237. drawid = wx.NewId()
  1238. bb = bb.Get()
  1239. pdc.BeginDrawing()
  1240. pdc.RemoveId(drawid)
  1241. pdc.SetId(drawid)
  1242. pdc.SetPen(pen)
  1243. pdc.SetBrush(brush)
  1244. if pdctype in ('rect', 'rectText'):
  1245. pdc.DrawRectangle(*bb)
  1246. if pdctype == 'rectText':
  1247. dc = wx.PaintDC(self) # dc created because of method GetTextExtent, which pseudoDC lacks
  1248. font = self.font
  1249. size = 10
  1250. font.SetPointSize(size)
  1251. font.SetStyle(wx.ITALIC)
  1252. dc.SetFont(font)
  1253. pdc.SetFont(font)
  1254. text = '\n'.join(self.itemLabels[self.instruction[drawid].type])
  1255. w,h,lh = dc.GetMultiLineTextExtent(text)
  1256. textExtent = (w,h)
  1257. textRect = wx.Rect(0, 0, *textExtent).CenterIn(bb)
  1258. r = map(int, bb)
  1259. while not wx.Rect(*r).ContainsRect(textRect) and size >= 8:
  1260. size -= 2
  1261. font.SetPointSize(size)
  1262. dc.SetFont(font)
  1263. pdc.SetFont(font)
  1264. w,h,lh = dc.GetMutiLineTextExtent(text)
  1265. textExtent = (w,h)
  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. # doesn't work
  1288. if background:
  1289. pdc.SetBackground(wx.Brush(convertRGB(background)))
  1290. pdc.SetBackgroundMode(wx.SOLID)
  1291. else:
  1292. pdc.SetBackground(wx.TRANSPARENT_BRUSH)
  1293. pdc.SetBackgroundMode(wx.TRANSPARENT)
  1294. fn = self.parent.makePSFont(textDict)
  1295. pdc.SetFont(fn)
  1296. pdc.SetTextForeground(convertRGB(textDict['color']))
  1297. pdc.DrawRotatedText(textDict['text'], coords[0], coords[1], rot)
  1298. pdc.SetIdBounds(drawId, wx.Rect(*bounds))
  1299. self.Refresh()
  1300. pdc.EndDrawing()
  1301. def DrawImage(self, rect):
  1302. """!Draw preview image to pseudoDC"""
  1303. self.pdcImage.ClearId(self.imageId)
  1304. self.pdcImage.SetId(self.imageId)
  1305. img = self.image
  1306. if img.GetWidth() != rect.width or img.GetHeight() != rect.height:
  1307. img = img.Scale(rect.width, rect.height)
  1308. bitmap = img.ConvertToBitmap()
  1309. self.pdcImage.BeginDrawing()
  1310. self.pdcImage.DrawBitmap(bitmap, rect.x, rect.y)
  1311. self.pdcImage.SetIdBounds(self.imageId, rect)
  1312. self.pdcImage.EndDrawing()
  1313. self.Refresh()
  1314. def DrawPaper(self, rect):
  1315. """!Draw paper and margins"""
  1316. page = self.instruction[self.pageId]
  1317. scale = page['Width'] / rect.GetWidth()
  1318. w = (page['Width'] - page['Right'] - page['Left']) / scale
  1319. h = (page['Height'] - page['Top'] - page['Bottom']) / scale
  1320. x = page['Left'] / scale + rect.GetX()
  1321. y = page['Top'] / scale + rect.GetY()
  1322. self.pdcPaper.BeginDrawing()
  1323. self.pdcPaper.RemoveId(self.pageId)
  1324. self.pdcPaper.SetId(self.pageId)
  1325. self.pdcPaper.SetPen(self.pen['paper'])
  1326. self.pdcPaper.SetBrush(self.brush['paper'])
  1327. self.pdcPaper.DrawRectangleRect(rect)
  1328. self.pdcPaper.SetPen(self.pen['margins'])
  1329. self.pdcPaper.SetBrush(self.brush['margins'])
  1330. self.pdcPaper.DrawRectangle(x, y, w, h)
  1331. self.pdcPaper.SetIdBounds(self.pageId, rect)
  1332. self.pdcPaper.EndDrawing()
  1333. self.Refresh()
  1334. def ImageRect(self):
  1335. """!Returns image centered in canvas, computes scale"""
  1336. img = wx.Image(self.imgName, wx.BITMAP_TYPE_PNG)
  1337. cW, cH = self.GetClientSize()
  1338. iW, iH = img.GetWidth(), img.GetHeight()
  1339. self.currScale = min(float(cW)/iW, float(cH)/iH)
  1340. iW = iW * self.currScale
  1341. iH = iH * self.currScale
  1342. x = cW/2 - iW/2
  1343. y = cH/2 - iH/2
  1344. imageRect = wx.Rect(x, y, iW, iH)
  1345. return imageRect
  1346. def RedrawSelectBox(self, id):
  1347. """!Redraws select box when selected object changes its size"""
  1348. if self.dragId == id:
  1349. rect = [self.pdcObj.GetIdBounds(id).Inflate(3,3)]
  1350. type = ['select']
  1351. ids = [self.idBoxTmp]
  1352. if self.instruction[id].type == 'map':
  1353. controlP = self.pdcObj.GetIdBounds(id).GetBottomRight()
  1354. rect.append(wx.Rect(controlP.x, controlP.y, 10,10))
  1355. type.append('resize')
  1356. ids.append(self.idResizeBoxTmp)
  1357. for id, type, rect in zip(ids, type, rect):
  1358. self.Draw(pen = self.pen[type], brush = self.brush[type], pdc = self.pdcTmp,
  1359. drawid = id, pdctype = 'rect', bb = rect)
  1360. def UpdateMapLabel(self):
  1361. """!Updates map frame label"""
  1362. vector = self.instruction.FindInstructionByType('vector')
  1363. if vector:
  1364. vectorId = vector.id
  1365. else:
  1366. vectorId = None
  1367. raster = self.instruction.FindInstructionByType('raster')
  1368. if raster:
  1369. rasterId = raster.id
  1370. else:
  1371. rasterId = None
  1372. rasterName = 'None'
  1373. if rasterId:
  1374. rasterName = self.instruction[rasterId]['raster'].split('@')[0]
  1375. self.itemLabels['map'] = self.itemLabels['map'][0:1]
  1376. self.itemLabels['map'].append("raster: " + rasterName)
  1377. if vectorId:
  1378. for map in self.instruction[vectorId]['list']:
  1379. self.itemLabels['map'].append('vector: ' + map[0].split('@')[0])
  1380. def OnSize(self, event):
  1381. """!Init image size to match window size
  1382. """
  1383. # not zoom all when notebook page is changed
  1384. if self.preview and self.parent.currentPage == 1 or not self.preview and self.parent.currentPage == 0:
  1385. self.ZoomAll()
  1386. self.OnIdle(None)
  1387. event.Skip()
  1388. def OnIdle(self, event):
  1389. """!Only re-render a image during idle time instead of
  1390. multiple times during resizing.
  1391. """
  1392. width, height = self.GetClientSize()
  1393. # Make new off screen bitmap: this bitmap will always have the
  1394. # current drawing in it, so it can be used to save the image
  1395. # to a file, or whatever.
  1396. self._buffer = wx.EmptyBitmap(width, height)
  1397. # re-render image on idle
  1398. self.resize = True
  1399. def ScaleRect(self, rect, scale):
  1400. """! Scale rectangle"""
  1401. return wx.Rect(rect.GetLeft()*scale, rect.GetTop()*scale,
  1402. rect.GetSize()[0]*scale, rect.GetSize()[1]*scale)
  1403. def main():
  1404. import gettext
  1405. gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
  1406. app = wx.PySimpleApp()
  1407. wx.InitAllImageHandlers()
  1408. frame = PsMapFrame()
  1409. frame.Show()
  1410. app.MainLoop()
  1411. if __name__ == "__main__":
  1412. main()