mapwindow.py 100 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680
  1. """
  2. @package nviz.mapwindow
  3. @brief wxGUI 3D view mode (map canvas)
  4. This module implements 3D visualization mode for map display.
  5. List of classes:
  6. - mapwindow::NvizThread
  7. - mapwindow::GLWindow
  8. (C) 2008-2011 by the GRASS Development Team
  9. This program is free software under the GNU General Public License
  10. (>=v2). Read the file COPYING that comes with GRASS for details.
  11. @author Martin Landa <landa.martin gmail.com> (Google SoC 2008/2010)
  12. @author Anna Kratochvilova <kratochanna gmail.com> (Google SoC 2011)
  13. """
  14. import os
  15. import sys
  16. import six
  17. import time
  18. import copy
  19. import math
  20. import types
  21. from threading import Thread
  22. import wx
  23. from wx.lib.newevent import NewEvent
  24. from wx import glcanvas
  25. from wx.glcanvas import WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE
  26. import grass.script as grass
  27. from grass.pydispatch.signal import Signal
  28. from core.gcmd import GMessage, GException, GError
  29. from core.debug import Debug
  30. from mapwin.base import MapWindowBase
  31. from core.settings import UserSettings
  32. from nviz.workspace import NvizSettings
  33. from nviz.animation import Animation
  34. from nviz import wxnviz
  35. from core.globalvar import CheckWxVersion
  36. from core.utils import str2rgb, _
  37. from core.giface import Notification
  38. wxUpdateProperties, EVT_UPDATE_PROP = NewEvent()
  39. wxUpdateView, EVT_UPDATE_VIEW = NewEvent()
  40. wxUpdateLight, EVT_UPDATE_LIGHT = NewEvent()
  41. wxUpdateCPlane, EVT_UPDATE_CPLANE = NewEvent()
  42. class NvizThread(Thread):
  43. def __init__(self, log, progressbar, window):
  44. Thread.__init__(self)
  45. Debug.msg(5, "NvizThread.__init__():")
  46. self.log = log
  47. self.progressbar = progressbar
  48. self.window = window
  49. self._display = None
  50. self.setDaemon(True)
  51. def run(self):
  52. self._display = wxnviz.Nviz(self.log, self.progressbar)
  53. def GetDisplay(self):
  54. """Get display instance"""
  55. return self._display
  56. class GLWindow(MapWindowBase, glcanvas.GLCanvas):
  57. """OpenGL canvas for Map Display Window"""
  58. def __init__(self, parent, giface, frame, Map, tree, lmgr, id=wx.ID_ANY):
  59. """All parameters except for id are mandatory. The todo is to remove
  60. them completely."""
  61. self.parent = parent
  62. self.tree = tree
  63. self.lmgr = lmgr
  64. self.frame = frame
  65. # for wxGTK we need to set WX_GL_DEPTH_SIZE to draw vectors correctly
  66. # but we don't know the right value
  67. # in wxpython 2.9, there is IsDisplaySupported
  68. if CheckWxVersion(version=[2, 8, 11]) and \
  69. sys.platform not in ('win32', 'darwin'):
  70. depthBuffer = int(
  71. UserSettings.Get(
  72. group='display',
  73. key='nvizDepthBuffer',
  74. subkey='value'))
  75. attribs = [
  76. WX_GL_RGBA,
  77. WX_GL_DOUBLEBUFFER,
  78. WX_GL_DEPTH_SIZE,
  79. depthBuffer,
  80. 0]
  81. glcanvas.GLCanvas.__init__(self, parent, id, attribList=attribs)
  82. else:
  83. glcanvas.GLCanvas.__init__(self, parent, id)
  84. MapWindowBase.__init__(self, parent=parent, giface=giface, Map=Map)
  85. self.Hide()
  86. # TODO: same signals as in BufferedMapWindow
  87. # same interface is good, but how to ensure same names
  88. # or avoid duplication, define in map window base class?
  89. # Emitted when mouse us moving (mouse motion event)
  90. # Parametres are x and y of the mouse position in map (cell) units
  91. self.mouseMoving = Signal('GLWindow.mouseMoving')
  92. # Emitted when the zoom history stack is emptied
  93. self.zoomHistoryUnavailable = Signal('GLWindow.zoomHistoryUnavailable')
  94. # Emitted when the zoom history stack is not empty
  95. self.zoomHistoryAvailable = Signal('GLWindow.zoomHistoryAvailable')
  96. # Emitted when map was queried, parameters x, y are mouse coordinates
  97. # TODO: change pixel coordinates to map coordinates (using Pixel2Cell)
  98. self.mapQueried = Signal('GLWindow.mapQueried')
  99. self.init = False
  100. self.initView = False
  101. self.context = None
  102. if CheckWxVersion(version=[2, 9]):
  103. self.context = glcanvas.GLContext(self)
  104. # render mode
  105. self.render = {'quick': False,
  106. # do not render vector lines in quick mode
  107. 'vlines': False,
  108. 'vpoints': False,
  109. 'overlays': False}
  110. self.mouse = {
  111. 'use': 'pointer'
  112. }
  113. # list of loaded map layers (layer tree items)
  114. self.layers = list()
  115. # list of constant surfaces
  116. self.constants = list()
  117. # id of base surface (when vector is loaded and no surface exist)
  118. self.baseId = -1
  119. # list of cutting planes
  120. self.cplanes = list()
  121. # list of query points
  122. self.qpoints = list()
  123. # list of past views
  124. self.viewhistory = []
  125. self.saveHistory = False
  126. # offset for dialog (e.g. DisplayAttributesDialog)
  127. self.dialogOffset = 5
  128. # overlays
  129. self.overlays = {}
  130. self.imagelist = []
  131. self.overlay = wx.Overlay()
  132. #self.pdc = wx.PseudoDC()
  133. self.dragid = -1
  134. self.hitradius = 5
  135. # layer manager toolwindow
  136. self.toolWin = None
  137. if self.lmgr:
  138. self.log = self.lmgr._gconsole
  139. logerr = self.lmgr._gconsole.GetLog(err=True)
  140. logmsg = self.lmgr._gconsole.GetLog()
  141. else:
  142. self.log = logmsg = sys.stdout
  143. logerr = sys.stderr
  144. # create nviz instance - use display region instead of computational
  145. os.environ['GRASS_REGION'] = self.Map.SetRegion(
  146. windres=True, windres3=True)
  147. self.nvizThread = NvizThread(logerr,
  148. self.parent.GetProgressBar(),
  149. logmsg)
  150. self.nvizThread.start()
  151. time.sleep(.1)
  152. self._display = self.nvizThread.GetDisplay()
  153. # GRASS_REGION needed only for initialization
  154. del os.environ['GRASS_REGION']
  155. self.img = wx.Image(self.Map.mapfile, wx.BITMAP_TYPE_ANY)
  156. # size of MapWindow, to avoid resizing if size is the same
  157. self.size = (0, 0)
  158. # default values
  159. self.nvizDefault = NvizSettings()
  160. self.view = copy.deepcopy(
  161. UserSettings.Get(
  162. group='nviz',
  163. key='view')) # copy
  164. self.iview = UserSettings.Get(
  165. group='nviz', key='view', settings_type='internal')
  166. self.light = copy.deepcopy(
  167. UserSettings.Get(
  168. group='nviz',
  169. key='light')) # copy
  170. self.decoration = self.nvizDefault.SetDecorDefaultProp(type='arrow')
  171. self.decoration['scalebar'] = []
  172. self.decoration['arrow']['size'] = self._getDecorationSize()
  173. self.fly = self.InitFly()
  174. # timer for flythrough
  175. self.timerFly = wx.Timer(self, id=wx.NewId())
  176. # timer for animations
  177. self.timerAnim = wx.Timer(self, id=wx.NewId())
  178. self.animation = Animation(mapWindow=self, timer=self.timerAnim)
  179. self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
  180. self.Bind(wx.EVT_SIZE, self.OnSize)
  181. self.Bind(wx.EVT_PAINT, self.OnPaint)
  182. self._bindMouseEvents()
  183. self.Bind(EVT_UPDATE_PROP, self.UpdateMapObjProperties)
  184. self.Bind(EVT_UPDATE_VIEW, self.OnUpdateView)
  185. self.Bind(EVT_UPDATE_LIGHT, self.UpdateLight)
  186. self.Bind(EVT_UPDATE_CPLANE, self.OnUpdateCPlane)
  187. self.Bind(wx.EVT_TIMER, self.OnTimerAnim, self.timerAnim)
  188. self.Bind(wx.EVT_TIMER, self.OnTimerFly, self.timerFly)
  189. self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
  190. self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
  191. self.Bind(wx.EVT_CLOSE, self.OnClose)
  192. if CheckWxVersion(version=[2, 8, 11]) and \
  193. sys.platform not in ('win32', 'darwin'):
  194. wx.CallLater(3000, self._warningDepthBuffer)
  195. # cplanes cannot be initialized now
  196. wx.CallAfter(self.InitCPlanes)
  197. def _warningDepthBuffer(self):
  198. if not self.initView:
  199. message = _("Opening 3D view was not successful. "
  200. "Please try to change the value of depth buffer "
  201. "in GUI Settings dialog > tab Map Display > Advanced "
  202. "and restart GUI.")
  203. GMessage(message)
  204. def InitFly(self):
  205. """Initialize fly through dictionary"""
  206. fly = {'interval': 10, # interval for timerFly
  207. 'value': [0, 0, 0], # calculated values for navigation
  208. 'mode': 0, # fly through mode (0, 1)
  209. 'exag': { # sensitivity
  210. 'move': UserSettings.Get(group='nviz', key='fly', subkey=['exag', 'move']),
  211. 'turn': UserSettings.Get(group='nviz', key='fly', subkey=['exag', 'turn'])},
  212. 'exagMultiplier': 3, # speed up by Shift
  213. 'flySpeed': 4, # speed of flying
  214. 'mouseControl': None, # if mouse or keys are used
  215. # virtual mouse position when using arrows
  216. 'pos': {'x': 0, 'y': 0},
  217. 'arrowStep': 50, # step in pixels (when using arrows)
  218. 'flySpeedStep': 2,
  219. }
  220. return fly
  221. def OnTimerFly(self, event):
  222. """Fly event was emitted, move the scene"""
  223. if self.mouse['use'] != 'fly':
  224. return
  225. if self.fly['mouseControl']:
  226. mx, my = self.ComputeMxMy(*self.mouse['tmp'])
  227. else:
  228. mx, my = self.ComputeMxMy(
  229. self.fly['pos']['x'],
  230. self.fly['pos']['y'])
  231. self.ComputeFlyValues(mx=mx, my=my)
  232. self._display.FlyThrough(
  233. flyInfo=self.fly['value'],
  234. mode=self.fly['mode'],
  235. exagInfo=self.fly['exag'])
  236. self.ChangeInnerView()
  237. self.render['quick'] = True
  238. self.Refresh(False)
  239. def ComputeMxMy(self, x, y):
  240. """Compute values for flythrough navigation
  241. (ComputeFlyValues should follow).
  242. Based on visualization/nviz/src/togl_flythrough.c.
  243. :param x,y: screen coordinates
  244. """
  245. sx, sy = self.GetClientSize()
  246. dx = dy = 0.01
  247. mx = 2 * (float(x) / sx) - 1
  248. my = 2 * (float(y) / sy) - 1
  249. if mx < - dx:
  250. mx += dx
  251. elif mx > dx:
  252. mx -= dx
  253. else:
  254. mx = 0.0 # ?
  255. if my < - dy:
  256. my += dy
  257. elif my > dy:
  258. my -= dy
  259. else:
  260. my = 0.0
  261. mx = mx / (1.0 - dx)
  262. my = my / (1.0 - dy)
  263. # Quadratic seems smoother
  264. mx *= abs(mx)
  265. my *= abs(my)
  266. return mx, my
  267. def ComputeFlyValues(self, mx, my):
  268. """Compute parameters for fly-through navigation
  269. :param mx,my: results from ComputeMxMy method
  270. """
  271. self.fly['value'] = [0, 0, 0]
  272. if self.fly['mode'] == 0:
  273. self.fly['value'][0] = self.fly[
  274. 'flySpeed'] * self.fly['interval'] / 1000. # forward */
  275. self.fly['value'][
  276. 1] = mx * 0.1 * self.fly['interval'] / 1000. # heading
  277. self.fly['value'][
  278. 2] = my * 0.1 * self.fly['interval'] / 1000. # pitch
  279. else:
  280. self.fly['value'][0] = mx * 100.0 * self.fly['interval'] / 1000.
  281. self.fly['value'][2] = - my * 100.0 * self.fly['interval'] / 1000.
  282. def ChangeFlySpeed(self, increase):
  283. """Increase/decrease flight spped"""
  284. if increase:
  285. self.fly['flySpeed'] += self.fly['flySpeedStep']
  286. else:
  287. self.fly['flySpeed'] -= self.fly['flySpeedStep']
  288. def __del__(self):
  289. """Stop timers if running, unload data"""
  290. self.StopTimer(self.timerAnim)
  291. self.StopTimer(self.timerFly)
  292. self.UnloadDataLayers(force=True)
  293. def StopTimer(self, timer):
  294. """Stop timer if running"""
  295. if timer.IsRunning():
  296. timer.Stop()
  297. def _bindMouseEvents(self):
  298. self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouseAction)
  299. self.Bind(wx.EVT_MOTION, self.OnMotion)
  300. def InitCPlanes(self):
  301. """Initialize cutting planes list"""
  302. for i in range(self._display.GetCPlanesCount()):
  303. cplane = copy.deepcopy(
  304. UserSettings.Get(
  305. group='nviz',
  306. key='cplane'))
  307. cplane['on'] = False
  308. self.cplanes.append(cplane)
  309. def SetToolWin(self, toolWin):
  310. """Sets reference to nviz toolwindow in layer manager"""
  311. self.toolWin = toolWin
  312. def GetToolWin(self):
  313. """Returns reference to nviz toolwindow in layer manager"""
  314. return self.toolWin
  315. def OnClose(self, event):
  316. self.StopTimer(self.timerAnim)
  317. self.StopTimer(self.timerFly)
  318. # cleanup when window actually closes (on quit) and not just is hidden
  319. self.UnloadDataLayers(force=True)
  320. def OnEraseBackground(self, event):
  321. pass # do nothing, to avoid flashing on MSW
  322. def OnSize(self, event):
  323. size = self.GetClientSize()
  324. if CheckWxVersion(version=[2, 9]):
  325. context = self.context
  326. else:
  327. context = self.GetContext()
  328. if self.size != size \
  329. and context:
  330. Debug.msg(3, "GLCanvas.OnSize(): w = %d, h = %d" %
  331. (size.width, size.height))
  332. if CheckWxVersion(version=[2, 9]):
  333. self.SetCurrent(self.context)
  334. else:
  335. self.SetCurrent()
  336. self._display.ResizeWindow(size.width,
  337. size.height)
  338. # reposition checkbox in statusbar
  339. self.parent.StatusbarReposition()
  340. # update statusbar
  341. self.parent.StatusbarUpdate()
  342. self.size = size
  343. event.Skip()
  344. def OnPaint(self, event):
  345. Debug.msg(1, "GLCanvas.OnPaint()")
  346. self.render['overlays'] = True
  347. dc = wx.PaintDC(self)
  348. self.DoPaint()
  349. def DoPaint(self):
  350. if CheckWxVersion(version=[2, 9]):
  351. self.SetCurrent(self.context)
  352. else:
  353. self.SetCurrent()
  354. if not self.initView:
  355. self._display.InitView()
  356. self.initView = True
  357. self.LoadDataLayers()
  358. self.UnloadDataLayers()
  359. if not self.init:
  360. self.ResetView()
  361. if hasattr(self.lmgr, "nviz"):
  362. self.lmgr.nviz.UpdatePage('view')
  363. self.lmgr.nviz.UpdatePage('light')
  364. self.lmgr.nviz.UpdatePage('cplane')
  365. self.lmgr.nviz.UpdatePage('decoration')
  366. self.lmgr.nviz.UpdatePage('animation')
  367. layer = self.tree.GetSelectedLayer(
  368. multi=False, checkedOnly=True)
  369. if layer:
  370. layer = self.tree.GetLayerInfo(layer, key='maplayer')
  371. if layer.type == 'raster':
  372. self.lmgr.nviz.UpdatePage('surface')
  373. self.lmgr.nviz.UpdatePage('fringe')
  374. elif layer.type == 'vector':
  375. self.lmgr.nviz.UpdatePage('vector')
  376. self.lmgr.nviz.UpdateSettings()
  377. # update widgets
  378. win = self.lmgr.nviz.FindWindowById(
  379. self.lmgr.nviz.win['vector']['lines']['surface'])
  380. win.SetItems(self.GetLayerNames('raster'))
  381. self.init = True
  382. self.UpdateMap()
  383. def DrawImages(self):
  384. """Draw overlay image"""
  385. for texture in self.imagelist:
  386. if texture.IsActive():
  387. texture.Draw()
  388. def UpdateOverlays(self):
  389. """Renders overlays (legend, text).
  390. Once this is done _onUpdateOverlays is called"""
  391. self.Map.ChangeMapSize(self.GetClientSize())
  392. self.Map.RenderOverlays(force=True)
  393. def _onUpdateOverlays(self):
  394. """Converts rendered overlay files and text labels to wx.Image
  395. and then to textures so that they can be rendered by OpenGL.
  396. Updates self.imagelist"""
  397. # update images (legend and text)
  398. for oid, overlay in six.iteritems(self.overlays):
  399. if not overlay.IsShown() or overlay.name in ('barscale', 'northarrow'):
  400. continue
  401. if oid not in [t.GetId() for t in self.imagelist]: # new
  402. self.CreateTexture(overlay=overlay)
  403. else:
  404. for t in self.imagelist:
  405. if t.GetId() == oid: # check if it is the same
  406. if not t.Corresponds(overlay):
  407. self.imagelist.remove(t)
  408. t = self.CreateTexture(overlay=overlay)
  409. self.Refresh()
  410. def CreateTexture(self, overlay):
  411. """Create texture from overlay image"""
  412. texture = wxnviz.ImageTexture(
  413. filepath=overlay.layer.mapfile, overlayId=overlay.id,
  414. coords=list(overlay.coords), cmd=overlay.GetCmd())
  415. if not texture.textureId: # texture too big
  416. GMessage(
  417. parent=self, message=_(
  418. "Image is too large, your OpenGL implementation "
  419. "supports maximum texture size %d px.") %
  420. texture.maxSize)
  421. return texture
  422. self.imagelist.append(texture)
  423. return texture
  424. def ClearTextures(self):
  425. self.imagelist = []
  426. def FindObjects(self, mouseX, mouseY, radius):
  427. """Find object which was clicked on"""
  428. for texture in self.imagelist:
  429. if texture.HitTest(mouseX, mouseY, radius):
  430. return texture.id
  431. return -1
  432. def OnTimerAnim(self, event):
  433. self.animation.Update()
  434. def GetAnimation(self):
  435. return self.animation
  436. def OnKeyDown(self, event):
  437. """Key was pressed.
  438. Used for fly-through mode.
  439. """
  440. if not self.mouse['use'] == 'fly':
  441. return
  442. key = event.GetKeyCode()
  443. if key == wx.WXK_CONTROL: # Mac ?
  444. self.fly['mode'] = 1
  445. elif key == wx.WXK_SHIFT:
  446. self.fly['exag']['move'] *= self.fly['exagMultiplier']
  447. self.fly['exag']['turn'] *= self.fly['exagMultiplier']
  448. elif key == wx.WXK_ESCAPE and self.timerFly.IsRunning() and not self.fly['mouseControl']:
  449. self.StopTimer(self.timerFly)
  450. self.fly['mouseControl'] = None
  451. self.render['quick'] = False
  452. self.Refresh(False)
  453. elif key in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT, wx.WXK_RIGHT):
  454. if not self.fly['mouseControl']:
  455. if not self.timerFly.IsRunning():
  456. sx, sy = self.GetClientSize()
  457. self.fly['pos']['x'] = sx / 2
  458. self.fly['pos']['y'] = sy / 2
  459. self.fly['mouseControl'] = False # controlled by keyboard
  460. self.timerFly.Start(self.fly['interval'])
  461. self.ProcessFlyByArrows(keyCode=key)
  462. # change speed of flight when using mouse
  463. else:
  464. if key == wx.WXK_UP:
  465. self.ChangeFlySpeed(increase=True)
  466. elif key == wx.WXK_DOWN:
  467. self.ChangeFlySpeed(increase=False)
  468. elif key in (wx.WXK_HOME, wx.WXK_PAGEUP) and self.timerFly.IsRunning():
  469. self.ChangeFlySpeed(increase=True)
  470. elif key in (wx.WXK_END, wx.WXK_PAGEDOWN) and self.timerFly.IsRunning():
  471. self.ChangeFlySpeed(increase=False)
  472. event.Skip()
  473. def ProcessFlyByArrows(self, keyCode):
  474. """Process arrow key during fly-through"""
  475. step = self.fly['arrowStep']
  476. if keyCode == wx.WXK_UP:
  477. self.fly['pos']['y'] -= step
  478. elif keyCode == wx.WXK_DOWN:
  479. self.fly['pos']['y'] += step
  480. elif keyCode == wx.WXK_LEFT:
  481. self.fly['pos']['x'] -= step
  482. elif keyCode == wx.WXK_RIGHT:
  483. self.fly['pos']['x'] += step
  484. def OnKeyUp(self, event):
  485. """Key was released.
  486. Used for fly-through mode.
  487. """
  488. if not self.mouse['use'] == 'fly':
  489. return
  490. key = event.GetKeyCode()
  491. if key == wx.WXK_CONTROL: # Mac ?
  492. self.fly['mode'] = 0
  493. elif key == wx.WXK_SHIFT:
  494. self.fly['exag']['move'] = math.floor(
  495. self.fly['exag']['move'] / self.fly['exagMultiplier'])
  496. self.fly['exag']['turn'] = math.floor(
  497. self.fly['exag']['turn'] / self.fly['exagMultiplier'])
  498. event.Skip()
  499. def OnMouseAction(self, event):
  500. """Handle mouse events"""
  501. # zoom with mouse wheel
  502. if event.GetWheelRotation() != 0:
  503. self.OnMouseWheel(event)
  504. # left mouse button pressed
  505. elif event.LeftDown():
  506. self.OnLeftDown(event)
  507. # left mouse button released
  508. elif event.LeftUp():
  509. self.OnLeftUp(event)
  510. # dragging
  511. elif event.Dragging():
  512. self.OnDragging(event)
  513. # double click
  514. elif event.ButtonDClick():
  515. self.OnDClick(event)
  516. elif event.Moving():
  517. pixelCoordinates = event.GetPosition()
  518. coordinates = self.Pixel2Cell(pixelCoordinates)
  519. # coordinates are none when no map is loaded
  520. # TODO: handle in more clever way: check the state
  521. if coordinates is not None:
  522. self.mouseMoving.emit(x=coordinates[0], y=coordinates[1])
  523. event.Skip()
  524. def OnMouseWheel(self, event):
  525. """Change perspective"""
  526. if UserSettings.Get(group='display',
  527. key='mouseWheelZoom',
  528. subkey='selection') == 2:
  529. event.Skip()
  530. return
  531. wheel = event.GetWheelRotation()
  532. Debug.msg(5, "GLWindow.OnMouseWheel(): wheel = %d" % wheel)
  533. if self.timerFly.IsRunning() and self.fly['mouseControl']:
  534. if wheel > 0:
  535. self.ChangeFlySpeed(increase=True)
  536. else:
  537. self.ChangeFlySpeed(increase=False)
  538. else:
  539. if UserSettings.Get(group='display',
  540. key='scrollDirection',
  541. subkey='selection'):
  542. wheel *= -1
  543. self.DoZoom(zoomtype=wheel, pos=event.GetPosition())
  544. # update statusbar
  545. # self.parent.StatusbarUpdate()
  546. def OnLeftDown(self, event):
  547. """On left mouse down"""
  548. self.mouse['begin'] = event.GetPosition()
  549. self.mouse['tmp'] = event.GetPosition()
  550. if self.mouse['use'] == "lookHere":
  551. size = self.GetClientSize()
  552. self._display.LookHere(
  553. self.mouse['begin'][0],
  554. size[1] - self.mouse['begin'][1])
  555. focus = self._display.GetFocus()
  556. for i, coord in enumerate(('x', 'y', 'z')):
  557. self.iview['focus'][coord] = focus[i]
  558. self.saveHistory = True
  559. self.Refresh(False)
  560. toggle = self.lmgr.nviz.FindWindowByName('here')
  561. toggle.SetValue(False)
  562. self.mouse['use'] = 'pointer'
  563. self.SetNamedCursor('default')
  564. if self.mouse['use'] == 'arrow':
  565. pos = event.GetPosition()
  566. size = self.GetClientSize()
  567. self.SetDrawArrow((pos[0], size[1] - pos[1]))
  568. if self.mouse['use'] == 'scalebar':
  569. pos = event.GetPosition()
  570. size = self.GetClientSize()
  571. self.SetDrawScalebar((pos[0], size[1] - pos[1]))
  572. if self.mouse['use'] == 'pointer':
  573. # get decoration id
  574. self.dragid = self.FindObjects(
  575. self.mouse['tmp'][0],
  576. self.mouse['tmp'][1],
  577. self.hitradius)
  578. if self.mouse['use'] == 'fly':
  579. if not self.timerFly.IsRunning():
  580. self.timerFly.Start(self.fly['interval'])
  581. self.fly['mouseControl'] = True
  582. event.Skip()
  583. def OnDragging(self, event):
  584. if self.mouse['use'] == 'pointer':
  585. if self.dragid >= 0:
  586. self.DragItem(self.dragid, event.GetPosition())
  587. if self.mouse['use'] == 'rotate':
  588. dx, dy = event.GetX(
  589. ) - self.mouse['tmp'][0], event.GetY() - self.mouse['tmp'][1]
  590. angle, x, y, z = self._display.GetRotationParameters(dx, dy)
  591. self._display.Rotate(angle, x, y, z)
  592. self.render['quick'] = True
  593. self.Refresh(False)
  594. if self.mouse['use'] == 'pan':
  595. self.FocusPanning(event)
  596. self.mouse['tmp'] = event.GetPosition()
  597. event.Skip()
  598. def Pixel2Cell(self, xyCoords):
  599. """Convert image coordinates to real word coordinates
  600. :param xyCoords: image coordinates
  601. :return: easting, northing
  602. :return: None on error
  603. """
  604. size = self.GetClientSize()
  605. # UL -> LL
  606. x, y = xyCoords
  607. sid, x, y, z = self._display.GetPointOnSurface(x, size[1] - y)
  608. if not sid:
  609. return None
  610. return (x, y)
  611. def DoZoom(self, zoomtype, pos):
  612. """Change perspective and focus"""
  613. prev_value = self.view['persp']['value']
  614. if zoomtype > 0:
  615. value = -1 * self.view['persp']['step']
  616. else:
  617. value = self.view['persp']['step']
  618. self.view['persp']['value'] += value
  619. if self.view['persp']['value'] < 1:
  620. self.view['persp']['value'] = 1
  621. elif self.view['persp']['value'] > 180:
  622. self.view['persp']['value'] = 180
  623. if prev_value != self.view['persp']['value']:
  624. if hasattr(self.lmgr, "nviz"):
  625. self.lmgr.nviz.UpdateSettings()
  626. x, y = pos[0], self.GetClientSize()[1] - pos[1]
  627. result = self._display.GetPointOnSurface(x, y)
  628. if result[0]:
  629. self._display.LookHere(x, y)
  630. focus = self._display.GetFocus()
  631. for i, coord in enumerate(('x', 'y', 'z')):
  632. self.iview['focus'][coord] = focus[i]
  633. self._display.SetView(
  634. self.view['position']['x'],
  635. self.view['position']['y'],
  636. self.iview['height']['value'],
  637. self.view['persp']['value'],
  638. self.view['twist']['value'])
  639. self.saveHistory = True
  640. # redraw map
  641. self.DoPaint()
  642. def OnLeftUp(self, event):
  643. self.mouse['end'] = event.GetPosition()
  644. if self.mouse["use"] == "query":
  645. # here changed from 'begin' to 'end' because it is more common
  646. # behavior used also in 2d map window
  647. # and moreover we are in left up
  648. self.mapQueried.emit(x=self.mouse['end'][0],
  649. y=self.mouse['end'][1])
  650. elif self.mouse["use"] in ('arrow', 'scalebar'):
  651. self.lmgr.nviz.FindWindowById(
  652. self.lmgr.nviz.win['decoration'][
  653. self.mouse["use"]]['place']).SetValue(False)
  654. if self.mouse["use"] == 'scalebar':
  655. scalebarNum = len(self.decoration['scalebar'])
  656. self.lmgr.nviz.AddScalebar(scalebarNum - 1)
  657. else:
  658. self.lmgr.nviz.AddArrow()
  659. self.mouse['use'] = 'pointer'
  660. self.SetNamedCursor('default')
  661. elif self.mouse['use'] == 'pointer':
  662. if self.dragid >= 0:
  663. dx = self.mouse['end'][0] - self.mouse['begin'][0]
  664. dy = self.mouse['end'][1] - self.mouse['begin'][1]
  665. if self.dragid in self.overlays:
  666. coords = self.overlays[self.dragid].coords
  667. self.overlays[
  668. self.dragid].coords = [
  669. coords[0] + dx, coords[1] + dy]
  670. self.dragid = -1
  671. self.render['quick'] = False
  672. self.Refresh(False)
  673. elif self.mouse['use'] == 'rotate':
  674. self._display.UnsetRotation()
  675. self.iview['rotation'] = self._display.GetRotationMatrix()
  676. self.saveHistory = True
  677. self.render['quick'] = False
  678. self.Refresh(False)
  679. elif self.mouse['use'] == 'pan':
  680. self.saveHistory = True
  681. self.render['quick'] = False
  682. self.Refresh(False)
  683. elif self.mouse['use'] == 'fly':
  684. if self.fly['mouseControl']:
  685. self.StopTimer(self.timerFly)
  686. self.fly['mouseControl'] = None
  687. # for key in self.iview['dir'].keys():
  688. #self.iview[''][key] = -1
  689. # this causes sudden change, but it should be there
  690. # if hasattr(self.lmgr, "nviz"):
  691. # self.lmgr.nviz.UpdateSettings()
  692. self.render['quick'] = False
  693. self.Refresh(False)
  694. elif self.mouse['use'] == 'zoom':
  695. self.DoZoom(zoomtype=self.zoomtype, pos=self.mouse['end'])
  696. event.Skip()
  697. def OnDClick(self, event):
  698. """On mouse double click"""
  699. if self.mouse['use'] != 'pointer':
  700. return
  701. pos = event.GetPosition()
  702. self.dragid = self.FindObjects(pos[0], pos[1], self.hitradius)
  703. self.overlayActivated.emit(overlayId=self.dragid)
  704. def FocusPanning(self, event):
  705. """Simulation of panning using focus"""
  706. size = self.GetClientSize()
  707. id1, x1, y1, z1 = self._display.GetPointOnSurface(
  708. self.mouse['tmp'][0], size[1] - self.mouse['tmp'][1])
  709. id2, x2, y2, z2 = self._display.GetPointOnSurface(
  710. event.GetX(), size[1] - event.GetY())
  711. if id1 and id1 == id2:
  712. dx, dy, dz = x2 - x1, y2 - y1, z2 - z1
  713. focus = self.iview['focus']
  714. focus['x'], focus['y'], focus['z'] = self._display.GetFocus()
  715. focus['x'] -= dx
  716. focus['y'] -= dy
  717. focus['z'] -= dz
  718. # update properties
  719. self.PostViewEvent()
  720. self.mouse['tmp'] = event.GetPosition()
  721. self.render['quick'] = True
  722. self.Refresh(False)
  723. def HorizontalPanning(self, event):
  724. """Move all layers in horizontal (x, y) direction.
  725. Currently not used.
  726. """
  727. size = self.GetClientSize()
  728. id1, x1, y1, z1 = self._display.GetPointOnSurface(
  729. self.mouse['tmp'][0], size[1] - self.mouse['tmp'][1])
  730. id2, x2, y2, z2 = self._display.GetPointOnSurface(
  731. event.GetX(), size[1] - event.GetY())
  732. if id1 and id1 == id2:
  733. dx, dy = x2 - x1, y2 - y1
  734. # find raster and volume
  735. for item in self.layers:
  736. mapLayer = self.tree.GetLayerInfo(item, key='maplayer')
  737. data = self.tree.GetLayerInfo(item, key='nviz')
  738. if mapLayer.GetType() == 'raster':
  739. data['surface']['position']['x'] += dx
  740. data['surface']['position']['y'] += dy
  741. data['surface']['position']['update'] = None
  742. # update properties
  743. evt = wxUpdateProperties(data=data)
  744. wx.PostEvent(self, evt)
  745. if event.CmdDown() and id1 == data[
  746. 'surface']['object']['id']:
  747. break
  748. elif mapLayer.GetType() == 'raster_3d':
  749. if 'x' not in data['volume']['position']:
  750. data['volume']['position']['x'] = 0
  751. data['volume']['position']['y'] = 0
  752. data['volume']['position']['z'] = 0
  753. data['volume']['position']['x'] += dx
  754. data['volume']['position']['y'] += dy
  755. data['volume']['position']['update'] = None
  756. # update properties
  757. evt = wxUpdateProperties(data=data)
  758. wx.PostEvent(self, evt)
  759. self.mouse['tmp'] = event.GetPosition()
  760. self.render['quick'] = True
  761. self.Refresh(False)
  762. def DragItem(self, id, coords):
  763. """Drag an overlay decoration item
  764. """
  765. if id is None:
  766. return
  767. Debug.msg(5, "GLWindow.DragItem(): id=%d" % id)
  768. x, y = self.mouse['tmp']
  769. dx = coords[0] - x
  770. dy = coords[1] - y
  771. for texture in self.imagelist:
  772. if texture.id == id:
  773. texture.MoveTexture(dx, dy)
  774. self.render['quick'] = True
  775. self.Refresh(False)
  776. self.mouse['tmp'] = coords
  777. def ZoomBack(self):
  778. """Set previous view in history list
  779. """
  780. view = {}
  781. if len(self.viewhistory) > 1:
  782. self.viewhistory.pop()
  783. view = copy.deepcopy(self.viewhistory[-1])
  784. # disable tool if stack is empty
  785. if len(self.viewhistory) < 2: # disable tool
  786. self.zoomHistoryUnavailable.emit()
  787. # set view and update nviz view page
  788. self.lmgr.nviz.UpdateState(view=view[0], iview=view[1])
  789. self.lmgr.nviz.UpdatePage('view')
  790. # update map
  791. self.Refresh(False)
  792. def ViewHistory(self, view, iview):
  793. """Manages a list of last 10 views
  794. :param view: view dictionary
  795. :param iview: view dictionary (internal)
  796. :return: removed history item if exists (or None)
  797. """
  798. removed = None
  799. hview = copy.deepcopy(view)
  800. hiview = copy.deepcopy(iview)
  801. if not (self.viewhistory and self.viewhistory[-1] == (hview, hiview)):
  802. self.viewhistory.append((hview, hiview))
  803. if len(self.viewhistory) > 10:
  804. removed = self.viewhistory.pop(0)
  805. if removed:
  806. Debug.msg(4, "GLWindow.ViewHistory(): hist=%s, removed=%s" %
  807. (self.viewhistory, removed))
  808. else:
  809. Debug.msg(4, "GLWindow.ViewHistory(): hist=%s" %
  810. (self.viewhistory))
  811. # update toolbar
  812. if len(self.viewhistory) > 1:
  813. self.zoomHistoryAvailable.emit()
  814. else:
  815. self.zoomHistoryUnavailable.emit()
  816. return removed
  817. def ResetViewHistory(self):
  818. """Reset view history"""
  819. self.viewhistory = list()
  820. def GoTo(self, e, n):
  821. """Focus on given point"""
  822. w = self.Map.region['w']
  823. s = self.Map.region['s']
  824. e -= w
  825. n -= s
  826. focus = self.iview['focus']
  827. focus['x'], focus['y'] = e, n
  828. self.saveHistory = True
  829. # update properties
  830. self.PostViewEvent()
  831. self.render['quick'] = False
  832. self.Refresh(False)
  833. def QuerySurface(self, x, y):
  834. """Query surface on given position"""
  835. size = self.GetClientSize()
  836. result = self._display.QueryMap(x, size[1] - y)
  837. if result:
  838. self.qpoints.append((result['x'], result['y'], result['z']))
  839. self.log.WriteLog("%-30s: %.3f" % (_("Easting"), result['x']))
  840. self.log.WriteLog("%-30s: %.3f" % (_("Northing"), result['y']))
  841. self.log.WriteLog("%-30s: %.3f" % (_("Elevation"), result['z']))
  842. name = ''
  843. for item in self.layers:
  844. if self.tree.GetLayerInfo(item, key='maplayer').type == 'raster' and self.tree.GetLayerInfo(
  845. item, key='nviz')['surface']['object']['id'] == result['id']:
  846. name = self.tree.GetLayerInfo(item, key='maplayer').name
  847. self.log.WriteLog("%-30s: %s" % (_("Surface map name"), name))
  848. self.log.WriteLog(
  849. "%-30s: %s" %
  850. (_("Surface map elevation"), result['elevation']))
  851. self.log.WriteLog("%-30s: %s" %
  852. (_("Surface map color"), result['color']))
  853. if len(self.qpoints) > 1:
  854. prev = self.qpoints[-2]
  855. curr = self.qpoints[-1]
  856. dxy = math.sqrt(pow(prev[0] - curr[0], 2) +
  857. pow(prev[1] - curr[1], 2))
  858. dxyz = math.sqrt(pow(prev[0] - curr[0], 2) +
  859. pow(prev[1] - curr[1], 2) +
  860. pow(prev[2] - curr[2], 2))
  861. self.log.WriteLog(
  862. "%-30s: %.3f" %
  863. (_("XY distance from previous"), dxy))
  864. self.log.WriteLog(
  865. "%-30s: %.3f" %
  866. (_("XYZ distance from previous"), dxyz))
  867. self.log.WriteLog(
  868. "%-30s: %.3f" %
  869. (_("Distance along surface"), self._display.GetDistanceAlongSurface(
  870. result['id'], (curr[0], curr[1]), (prev[0], prev[1]), useExag=False)))
  871. self.log.WriteLog(
  872. "%-30s: %.3f" %
  873. (_("Distance along exag. surface"), self._display.GetDistanceAlongSurface(
  874. result['id'], (curr[0], curr[1]), (prev[0], prev[1]), useExag=True)))
  875. self.log.WriteCmdLog('-' * 80)
  876. else:
  877. self.log.WriteLog(_("No point on surface"))
  878. self.log.WriteCmdLog('-' * 80)
  879. def PostViewEvent(self, zExag=False):
  880. """Change view settings"""
  881. event = wxUpdateView(zExag=zExag)
  882. wx.PostEvent(self, event)
  883. def OnQueryVector(self, event):
  884. """Query vector on given position"""
  885. self.parent.QueryVector(*event.GetPosition())
  886. def ChangeInnerView(self):
  887. """Get current viewdir and viewpoint and set view"""
  888. view = self.view
  889. iview = self.iview
  890. (view['position']['x'], view['position']['y'],
  891. iview['height']['value']) = self._display.GetViewpointPosition()
  892. for key, val in zip(('x', 'y', 'z'), self._display.GetViewdir()):
  893. iview['dir'][key] = val
  894. iview['dir']['use'] = True
  895. def OnUpdateView(self, event):
  896. """Change view settings"""
  897. if event:
  898. self.UpdateView(zexag=event.zExag)
  899. self.saveHistory = True
  900. if event:
  901. event.Skip()
  902. def UpdateView(self, zexag=False):
  903. """Change view settings"""
  904. view = self.view
  905. iview = self.iview
  906. if zexag and 'value' in view['z-exag']:
  907. self._display.SetZExag(
  908. view['z-exag']['value'] /
  909. iview['z-exag']['llRatio'])
  910. self._display.SetView(view['position']['x'], view['position']['y'],
  911. iview['height']['value'],
  912. view['persp']['value'],
  913. view['twist']['value'])
  914. if iview['dir']['use']:
  915. self._display.SetViewdir(
  916. iview['dir']['x'],
  917. iview['dir']['y'],
  918. iview['dir']['z'])
  919. elif iview['focus']['x'] != -1:
  920. self._display.SetFocus(
  921. self.iview['focus']['x'],
  922. self.iview['focus']['y'],
  923. self.iview['focus']['z'])
  924. if 'rotation' in iview:
  925. if iview['rotation']:
  926. self._display.SetRotationMatrix(iview['rotation'])
  927. else:
  928. self._display.ResetRotation()
  929. def UpdateLight(self, event):
  930. """Change light settings"""
  931. data = self.light
  932. self._display.SetLight(
  933. x=data['position']['x'],
  934. y=data['position']['y'],
  935. z=data['position']['z'] / 100.,
  936. color=data['color'],
  937. bright=data['bright'] / 100.,
  938. ambient=data['ambient'] / 100.)
  939. self._display.DrawLightingModel()
  940. if event.refresh:
  941. self.Refresh(False)
  942. def UpdateMap(self, render=True):
  943. """Updates the canvas anytime there is a change to the
  944. underlaying images or to the geometry of the canvas.
  945. :param render: re-render map composition
  946. :type render: bool
  947. """
  948. start = time.clock()
  949. self.resize = False
  950. if self.render['quick'] is False:
  951. if sys.platform != 'darwin': # causes recursion for some reason on Mac
  952. self.parent.GetProgressBar().Show()
  953. self.parent.GetProgressBar().SetRange(2)
  954. self.parent.GetProgressBar().SetValue(0)
  955. if self.render['quick'] is False:
  956. if sys.platform != 'darwin':
  957. self.parent.GetProgressBar().SetValue(1)
  958. self._display.Draw(False, -1)
  959. if self.saveHistory:
  960. self.ViewHistory(view=self.view, iview=self.iview)
  961. self.saveHistory = False
  962. elif self.render['quick'] is True:
  963. # quick
  964. mode = wxnviz.DRAW_QUICK_SURFACE | wxnviz.DRAW_QUICK_VOLUME
  965. if self.render['vlines']:
  966. mode |= wxnviz.DRAW_QUICK_VLINES
  967. if self.render['vpoints']:
  968. mode |= wxnviz.DRAW_QUICK_VPOINTS
  969. self._display.Draw(True, mode)
  970. else: # None -> reuse last rendered image
  971. pass # TODO
  972. self.SwapBuffers()
  973. # draw fringe after SwapBuffers, otherwise it don't have to be visible
  974. # on some computers
  975. if self.render['quick'] is False:
  976. self._display.DrawFringe()
  977. if self.decoration['arrow']['show']:
  978. self._display.DrawArrow()
  979. if self.decoration['scalebar']:
  980. self._display.DrawScalebar()
  981. if self.imagelist:
  982. if ((self.render['quick'] and self.dragid > -1) or # during dragging
  983. (not self.render['quick'] and self.dragid < 0)): # redraw
  984. self._display.Start2D()
  985. self.DrawImages()
  986. stop = time.clock()
  987. if self.render['quick'] is False:
  988. if sys.platform != 'darwin':
  989. self.parent.GetProgressBar().SetValue(2)
  990. # hide process bar
  991. self.parent.GetProgressBar().Hide()
  992. Debug.msg(3, "GLWindow.UpdateMap(): quick = %d, -> time = %g" %
  993. (self.render['quick'], (stop - start)))
  994. def EraseMap(self):
  995. """Erase the canvas
  996. """
  997. self._display.EraseMap()
  998. self.SwapBuffers()
  999. def _getDecorationSize(self):
  1000. """Get initial size of north arrow/scalebar"""
  1001. size = self._display.GetLongDim() / 8.
  1002. coef = 0.01
  1003. if size < 1:
  1004. coef = 100.
  1005. return int(size * coef) / coef
  1006. def SetDrawArrow(self, pos):
  1007. """North arrow drawing.
  1008. Also, opens Appearance page of nviz notebook (needs refactoring).
  1009. """
  1010. if self._display.SetArrow(pos[0], pos[1],
  1011. self.decoration['arrow']['size'],
  1012. self.decoration['arrow']['color']):
  1013. self._display.DrawArrow()
  1014. # update
  1015. self.decoration['arrow']['show'] = True
  1016. self.decoration['arrow']['position']['x'] = pos[0]
  1017. self.decoration['arrow']['position']['y'] = pos[1]
  1018. self.Refresh(False)
  1019. # this was in mapdisp/frame.py but moved here to be with similar calls
  1020. # such as self.lmgr.nviz.UpdatePage
  1021. # anyway, it need to be handled in some another way
  1022. self.lmgr.nviz.SetPage('decoration')
  1023. def SetDrawScalebar(self, pos):
  1024. """Add scale bar, sets properties and draw"""
  1025. if len(self.decoration['scalebar']) == 0:
  1026. self.decoration['scalebar'].append(
  1027. self.nvizDefault.SetDecorDefaultProp(
  1028. type='scalebar')['scalebar'])
  1029. self.decoration['scalebar'][0]['size'] = self._getDecorationSize()
  1030. else:
  1031. self.decoration['scalebar'].append(
  1032. copy.deepcopy(self.decoration['scalebar'][-1]))
  1033. self.decoration['scalebar'][-1]['id'] += 1
  1034. ret = self._display.SetScalebar(
  1035. self.decoration['scalebar'][-1]['id'],
  1036. pos[0],
  1037. pos[1],
  1038. self.decoration['scalebar'][-1]['size'],
  1039. self.decoration['scalebar'][-1]['color'])
  1040. if ret:
  1041. self._display.DrawScalebar()
  1042. # update
  1043. self.decoration['scalebar'][-1]['position']['x'] = pos[0]
  1044. self.decoration['scalebar'][-1]['position']['y'] = pos[1]
  1045. self.Refresh(False)
  1046. self.lmgr.nviz.SetPage('decoration')
  1047. def IsLoaded(self, item):
  1048. """Check if layer (item) is already loaded
  1049. :param item: layer item
  1050. """
  1051. layer = self.tree.GetLayerInfo(item, key='maplayer')
  1052. data = self.tree.GetLayerInfo(item, key='nviz')
  1053. if not data:
  1054. return 0
  1055. if layer.type == 'raster':
  1056. if 'object' not in data['surface']:
  1057. return 0
  1058. elif layer.type == 'vector':
  1059. if 'object' not in data['vector']['lines'] and \
  1060. 'object' not in data['vector']['points']:
  1061. return 0
  1062. return 1
  1063. def _GetDataLayers(self, item, litems):
  1064. """Return get list of enabled map layers"""
  1065. # load raster & vector maps
  1066. while item and item.IsOk():
  1067. type = self.tree.GetLayerInfo(item, key='type')
  1068. if type == 'group':
  1069. item = self.tree.GetNextItem(item)
  1070. continue
  1071. if not item.IsChecked() or \
  1072. type not in ('raster', 'vector', 'raster_3d'):
  1073. item = self.tree.GetNextItem(item)
  1074. continue
  1075. litems.append(item)
  1076. item = self.tree.GetNextItem(item)
  1077. def LoadDataLayers(self):
  1078. """Load raster/vector from current layer tree
  1079. .. todo::
  1080. volumes
  1081. """
  1082. if not self.tree:
  1083. return
  1084. listOfItems = []
  1085. item = self.tree.GetFirstChild(self.tree.root)[0]
  1086. self._GetDataLayers(item, listOfItems)
  1087. start = time.time()
  1088. while(len(listOfItems) > 0):
  1089. item = listOfItems.pop()
  1090. type = self.tree.GetLayerInfo(item, key='type')
  1091. if item in self.layers:
  1092. continue
  1093. # "raster (double click to set properties)" - tries to load this
  1094. # layer - no idea how to fix it
  1095. if ' ' in self.tree.GetLayerInfo(item, key='maplayer').name:
  1096. return
  1097. try:
  1098. if type == 'raster':
  1099. self.LoadRaster(item)
  1100. elif type == 'raster_3d':
  1101. self.LoadRaster3d(item)
  1102. elif type == 'vector':
  1103. layer = self.tree.GetLayerInfo(item, key='maplayer')
  1104. vInfo = grass.vector_info_topo(layer.GetName())
  1105. if (vInfo['points']) > 0:
  1106. # include vInfo['centroids'] to initially load
  1107. # centroids
  1108. self.LoadVector(item, points=True)
  1109. if (vInfo['lines'] + vInfo['boundaries']) > 0:
  1110. self.LoadVector(item, points=False)
  1111. if vInfo['map3d'] and(
  1112. vInfo['kernels'] + vInfo['faces']) > 0:
  1113. self.LoadVector(item, points=None)
  1114. except GException as e:
  1115. GError(parent=self,
  1116. message=e.value)
  1117. stop = time.time()
  1118. Debug.msg(1, "GLWindow.LoadDataLayers(): time = %f" % (stop - start))
  1119. def UnloadDataLayers(self, force=False):
  1120. """Unload any layers that have been deleted from layer tree
  1121. :param bool force: True to unload all data layers
  1122. """
  1123. if not self.tree:
  1124. return
  1125. listOfItems = []
  1126. if not force:
  1127. item = self.tree.GetFirstChild(self.tree.root)[0]
  1128. self._GetDataLayers(item, listOfItems)
  1129. start = time.time()
  1130. update = False
  1131. layersTmp = self.layers[:]
  1132. for layer in layersTmp:
  1133. if layer in listOfItems:
  1134. continue
  1135. ltype = self.tree.GetLayerInfo(layer, key='type')
  1136. try:
  1137. if ltype == 'raster':
  1138. self.UnloadRaster(layer)
  1139. elif ltype == 'raster_3d':
  1140. self.UnloadRaster3d(layer)
  1141. elif ltype == 'vector':
  1142. maplayer = self.tree.GetLayerInfo(layer, key='maplayer')
  1143. vInfo = grass.vector_info_topo(maplayer.GetName())
  1144. if (vInfo['points'] + vInfo['centroids']) > 0:
  1145. self.UnloadVector(layer, points=True)
  1146. if (vInfo['lines'] + vInfo['boundaries']
  1147. ) > 0 or vInfo['map3d']:
  1148. self.UnloadVector(layer, points=False)
  1149. except GException as e:
  1150. GError(parent=self,
  1151. message=e.value)
  1152. if force and self.baseId > 0: # unload base surface when quitting
  1153. ret = self._display.UnloadSurface(self.baseId)
  1154. self.baseId = -1
  1155. if update:
  1156. self.lmgr.nviz.UpdateSettings()
  1157. self.UpdateView(None)
  1158. stop = time.time()
  1159. Debug.msg(1, "GLWindow.UnloadDataLayers(): time = %f" % (stop - start))
  1160. def SetVectorSurface(self, data):
  1161. """Set reference surfaces of vector"""
  1162. data['mode']['surface'] = {}
  1163. data['mode']['surface']['value'] = list()
  1164. data['mode']['surface']['show'] = list()
  1165. for name in self.GetLayerNames('raster'):
  1166. data['mode']['surface']['value'].append(name)
  1167. data['mode']['surface']['show'].append(True)
  1168. def SetVectorFromCmd(self, item, data):
  1169. """Set 3D view properties from cmd (d.vect)
  1170. :param item: Layer Tree item
  1171. :param nviz: data
  1172. """
  1173. cmd = self.tree.GetLayerInfo(item, key='cmd')
  1174. if cmd[0] != 'd.vect':
  1175. return
  1176. for opt in cmd[1:]:
  1177. try:
  1178. key, value = opt.split('=')
  1179. except ValueError:
  1180. continue
  1181. if key == 'color':
  1182. if not ':' in value:
  1183. value = ':'.join(map(str, str2rgb[value]))
  1184. data['lines']['color']['value'] = value
  1185. data['points']['color']['value'] = value
  1186. def SetMapObjProperties(self, item, id, nvizType):
  1187. """Set map object properties
  1188. Properties must be afterwards updated by
  1189. UpdateMapObjProperties().
  1190. :param item: layer item
  1191. :param id: nviz layer id (or -1)
  1192. :param nvizType: nviz data type (surface, points, vector)
  1193. """
  1194. if nvizType != 'constant':
  1195. mapType = self.tree.GetLayerInfo(item, key='maplayer').type
  1196. # reference to original layer properties (can be None)
  1197. data = self.tree.GetLayerInfo(item, key='nviz')
  1198. else:
  1199. mapType = nvizType
  1200. data = self.constants[item]
  1201. if not data:
  1202. # init data structure
  1203. if nvizType != 'constant':
  1204. self.tree.SetLayerInfo(item, key='nviz', value={})
  1205. data = self.tree.GetLayerInfo(item, key='nviz')
  1206. if mapType == 'raster':
  1207. # reset to default properties
  1208. data[nvizType] = self.nvizDefault.SetSurfaceDefaultProp()
  1209. elif mapType == 'vector':
  1210. # reset to default properties (lines/points)
  1211. data['vector'] = self.nvizDefault.SetVectorDefaultProp(longDim=self._display.GetLongDim())
  1212. self.SetVectorFromCmd(item, data['vector'])
  1213. self.SetVectorSurface(data['vector']['points'])
  1214. self.SetVectorSurface(data['vector']['lines'])
  1215. elif mapType == 'raster_3d':
  1216. # reset to default properties
  1217. data[nvizType] = self.nvizDefault.SetVolumeDefaultProp()
  1218. elif mapType == 'constant':
  1219. data['constant'] = self.nvizDefault.SetConstantDefaultProp()
  1220. else:
  1221. # complete data (use default values), not sure if this is necessary
  1222. if mapType == 'raster':
  1223. if not data['surface']:
  1224. data['surface'] = self.nvizDefault.SetSurfaceDefaultProp()
  1225. if mapType == 'vector':
  1226. if not data['vector']['lines']:
  1227. self.nvizDefault.SetVectorLinesDefaultProp(
  1228. data
  1229. ['vector']
  1230. ['lines'])
  1231. if not data['vector']['points']:
  1232. self.nvizDefault.SetVectorPointsDefaultProp(
  1233. data['vector']['points'], self._display.GetLongDim())
  1234. # set updates
  1235. for sec in data.keys():
  1236. for sec1 in data[sec].keys():
  1237. if sec1 == 'position':
  1238. data[sec][sec1]['update'] = None
  1239. continue
  1240. if isinstance(data[sec][sec1], types.DictType):
  1241. for sec2 in data[sec][sec1].keys():
  1242. if sec2 not in ('all', 'init', 'id'):
  1243. data[sec][sec1][sec2]['update'] = None
  1244. elif isinstance(data[sec][sec1], types.ListType):
  1245. for i in range(len(data[sec][sec1])):
  1246. for sec2 in data[sec][sec1][i].keys():
  1247. data[sec][sec1][i][sec2]['update'] = None
  1248. event = wxUpdateProperties(data=data)
  1249. wx.PostEvent(self, event)
  1250. # set id
  1251. if id > 0:
  1252. if mapType in ('raster', 'raster_3d'):
  1253. data[nvizType]['object'] = {'id': id,
  1254. 'init': False}
  1255. elif mapType == 'vector':
  1256. data['vector'][nvizType]['object'] = {'id': id,
  1257. 'init': False}
  1258. elif mapType == 'constant':
  1259. data[nvizType]['object'] = {'id': id,
  1260. 'init': False}
  1261. return data
  1262. def LoadRaster(self, item):
  1263. """Load 2d raster map and set surface attributes
  1264. :param layer: item
  1265. """
  1266. return self._loadRaster(item)
  1267. def LoadRaster3d(self, item):
  1268. """Load 3d raster map and set surface attributes
  1269. :param layer: item
  1270. """
  1271. return self._loadRaster(item)
  1272. def _loadRaster(self, item):
  1273. """Load 2d/3d raster map and set its attributes
  1274. :param layer: item
  1275. """
  1276. layer = self.tree.GetLayerInfo(item, key='maplayer')
  1277. if layer.type not in ('raster', 'raster_3d'):
  1278. return
  1279. if layer.type == 'raster':
  1280. id = self._display.LoadSurface(str(layer.name), None, None)
  1281. nvizType = 'surface'
  1282. errorMsg = _("Loading raster map")
  1283. elif layer.type == 'raster_3d':
  1284. id = self._display.LoadVolume(str(layer.name), None, None)
  1285. nvizType = 'volume'
  1286. errorMsg = _("Loading 3d raster map")
  1287. else:
  1288. id = -1
  1289. if id < 0:
  1290. if layer.type in ('raster', 'raster_3d'):
  1291. self.log.WriteError(
  1292. "%s <%s> %s" %
  1293. (errorMsg, layer.name, _("failed")))
  1294. else:
  1295. self.log.WriteError(
  1296. _("Unsupported layer type '%s'") %
  1297. layer.type)
  1298. self.layers.append(item)
  1299. # set default/workspace layer properties
  1300. data = self.SetMapObjProperties(item, id, nvizType)
  1301. # update properties
  1302. event = wxUpdateProperties(data=data)
  1303. wx.PostEvent(self, event)
  1304. # update tools window
  1305. if hasattr(
  1306. self.lmgr, "nviz") and item == self.tree.GetSelectedLayer(
  1307. multi=False, checkedOnly=True):
  1308. toolWin = self.lmgr.nviz
  1309. if layer.type == 'raster':
  1310. win = toolWin.FindWindowById(
  1311. toolWin.win['vector']['lines']['surface'])
  1312. win.SetItems(self.GetLayerNames(layer.type))
  1313. # toolWin.UpdatePage(nvizType)
  1314. # toolWin.SetPage(nvizType)
  1315. return id
  1316. def NewConstant(self):
  1317. """Create new constant"""
  1318. index = len(self.constants)
  1319. try:
  1320. name = self.constants[-1]['constant']['object']['name'] + 1
  1321. except IndexError:
  1322. name = 1
  1323. data = dict()
  1324. self.constants.append(data)
  1325. data = self.SetMapObjProperties(item=index, id=-1, nvizType='constant')
  1326. self.AddConstant(data, name)
  1327. return name
  1328. def AddConstant(self, data, name):
  1329. """Add new constant"""
  1330. id = self._display.AddConstant(
  1331. value=data['constant']['value'],
  1332. color=data['constant']['color'])
  1333. self._display.SetSurfaceRes(
  1334. id, data['constant']['resolution'],
  1335. data['constant']['resolution'])
  1336. data['constant']['object'] = {'id': id,
  1337. 'name': name,
  1338. 'init': False}
  1339. def DeleteConstant(self, index):
  1340. """Delete constant layer"""
  1341. id = self.constants[index]['constant']['object']['id']
  1342. self._display.UnloadSurface(id)
  1343. del self.constants[index]
  1344. def SelectCPlane(self, index):
  1345. """Select cutting plane"""
  1346. for plane in range(self._display.GetCPlanesCount()):
  1347. if plane == index:
  1348. self._display.SelectCPlane(plane)
  1349. self.cplanes[plane]['on'] = True
  1350. self._display.SetFenceColor(self.cplanes[plane]['shading'])
  1351. else:
  1352. self._display.UnselectCPlane(plane)
  1353. try:
  1354. self.cplanes[plane]['on'] = False
  1355. except IndexError:
  1356. pass
  1357. def OnUpdateCPlane(self, event):
  1358. """Change cutting plane settings"""
  1359. self.UpdateCPlane(event.current, event.update)
  1360. def UpdateCPlane(self, index, changes):
  1361. """Change cutting plane settings"""
  1362. for each in changes:
  1363. if each == 'rotation':
  1364. self._display.SetCPlaneRotation(
  1365. 0, self.cplanes[index]['rotation']['tilt'],
  1366. self.cplanes[index]['rotation']['rot'])
  1367. if each == 'position':
  1368. self._display.SetCPlaneTranslation(
  1369. self.cplanes[index]['position']['x'],
  1370. self.cplanes[index]['position']['y'],
  1371. self.cplanes[index]['position']['z'])
  1372. if each == 'shading':
  1373. self._display.SetFenceColor(self.cplanes[index]['shading'])
  1374. def UnloadRaster(self, item):
  1375. """Unload 2d raster map
  1376. :param layer: item
  1377. """
  1378. return self._unloadRaster(item)
  1379. def UnloadRaster3d(self, item):
  1380. """Unload 3d raster map
  1381. :param layer: item
  1382. """
  1383. return self._unloadRaster(item)
  1384. def _unloadRaster(self, item):
  1385. """Unload 2d/3d raster map
  1386. :param item: layer item
  1387. """
  1388. layer = self.tree.GetLayerInfo(item, key='maplayer')
  1389. if layer.type not in ('raster', 'raster_3d'):
  1390. return
  1391. data = self.tree.GetLayerInfo(item, key='nviz')
  1392. if layer.type == 'raster':
  1393. nvizType = 'surface'
  1394. unloadFn = self._display.UnloadSurface
  1395. errorMsg = _("Unable to unload raster map")
  1396. successMsg = _("Raster map")
  1397. else:
  1398. nvizType = 'volume'
  1399. unloadFn = self._display.UnloadVolume
  1400. errorMsg = _("Unable to unload 3d raster map")
  1401. successMsg = _("3d raster map")
  1402. try:
  1403. id = data[nvizType]['object']['id']
  1404. except KeyError:
  1405. return
  1406. if unloadFn(id) == 0:
  1407. self.log.WriteError("%s <%s>" % (errorMsg, layer.name))
  1408. else:
  1409. self.log.WriteLog(
  1410. "%s <%s> %s" %
  1411. (successMsg, layer.name, _("unloaded successfully")))
  1412. data[nvizType].pop('object')
  1413. self.layers.remove(item)
  1414. # update tools window
  1415. if hasattr(self.lmgr, "nviz"):
  1416. toolWin = self.lmgr.nviz
  1417. if layer.type == 'raster':
  1418. win = toolWin.FindWindowById(
  1419. toolWin.win['vector']['lines']['surface'])
  1420. win.SetItems(self.GetLayerNames(layer.type))
  1421. win = toolWin.FindWindowById(toolWin.win['surface']['map'])
  1422. win.SetValue('')
  1423. if layer.type == 'raster_3d':
  1424. win = toolWin.FindWindowById(toolWin.win['volume']['map'])
  1425. win.SetValue('')
  1426. if layer.type == 'vector':
  1427. win = toolWin.FindWindowById(toolWin.win['vector']['map'])
  1428. win.SetValue('')
  1429. def LoadVector(self, item, points=None, append=True):
  1430. """Load 2D or 3D vector map overlay
  1431. :param item: layer item
  1432. :param points: True to load points, False to load lines, None
  1433. to load both
  1434. :param bool append: append vector to layer list
  1435. """
  1436. layer = self.tree.GetLayerInfo(item, key='maplayer')
  1437. if layer.type != 'vector':
  1438. return
  1439. # set default properties
  1440. if points is None:
  1441. self.SetMapObjProperties(item, -1, 'lines')
  1442. self.SetMapObjProperties(item, -1, 'points')
  1443. vecTypes = ('points', 'lines')
  1444. elif points:
  1445. self.SetMapObjProperties(item, -1, 'points')
  1446. vecTypes = ('points', )
  1447. else:
  1448. self.SetMapObjProperties(item, -1, 'lines')
  1449. vecTypes = ('lines', )
  1450. id = -1
  1451. for vecType in vecTypes:
  1452. if vecType == 'lines':
  1453. id, baseId = self._display.LoadVector(
  1454. str(layer.GetName()), False)
  1455. else:
  1456. id, baseId = self._display.LoadVector(
  1457. str(layer.GetName()), True)
  1458. if id < 0:
  1459. self.log.WriteError(
  1460. _("Loading vector map <%(name)s> (%(type)s) failed") %
  1461. {'name': layer.name, 'type': vecType})
  1462. # update layer properties
  1463. self.SetMapObjProperties(item, id, vecType)
  1464. if baseId > 0:
  1465. # id of base surface (when no surface is loaded)
  1466. self.baseId = baseId
  1467. if append:
  1468. self.layers.append(item)
  1469. # update properties
  1470. data = self.tree.GetLayerInfo(item, key='nviz')
  1471. event = wxUpdateProperties(data=data)
  1472. wx.PostEvent(self, event)
  1473. # update tools window
  1474. if hasattr(
  1475. self.lmgr, "nviz") and item == self.tree.GetSelectedLayer(
  1476. multi=False, checkedOnly=True):
  1477. toolWin = self.lmgr.nviz
  1478. toolWin.UpdatePage('vector')
  1479. # toolWin.SetPage('vector')
  1480. return id
  1481. def UnloadVector(self, item, points=None, remove=True):
  1482. """Unload vector map overlay
  1483. :param item: layer item
  1484. :param points, lines: True to unload given feature type
  1485. :param remove: remove layer from list
  1486. :type remove: bool
  1487. """
  1488. layer = self.tree.GetLayerInfo(item, key='maplayer')
  1489. data = self.tree.GetLayerInfo(item, key='nviz')['vector']
  1490. # if vecType is None:
  1491. # vecType = []
  1492. # for v in ('lines', 'points'):
  1493. # if UserSettings.Get(group = 'nviz', key = 'vector',
  1494. # subkey = [v, 'show']):
  1495. # vecType.append(v)
  1496. if points is None:
  1497. vecTypes = ('points', 'lines')
  1498. elif points:
  1499. vecTypes = ('points', )
  1500. else:
  1501. vecTypes = ('lines', )
  1502. for vecType in vecTypes:
  1503. if 'object' not in data[vecType]:
  1504. continue
  1505. id = data[vecType]['object']['id']
  1506. if vecType == 'lines':
  1507. ret = self._display.UnloadVector(id, False)
  1508. else:
  1509. ret = self._display.UnloadVector(id, True)
  1510. if ret == 0:
  1511. self.log.WriteError(
  1512. _("Unable to unload vector map <%(name)s> (%(type)s)") %
  1513. {'name': layer.name, 'type': vecType})
  1514. else:
  1515. self.log.WriteLog(
  1516. _("Vector map <%(name)s> (%(type)s) unloaded successfully") % {
  1517. 'name': layer.name,
  1518. 'type': vecType})
  1519. data[vecType].pop('object')
  1520. if remove and item in self.layers:
  1521. self.layers.remove(item)
  1522. def ResetView(self):
  1523. """Reset to default view"""
  1524. zexagOriginal, \
  1525. self.iview['height']['value'], \
  1526. self.iview['height']['min'], \
  1527. self.iview['height']['max'] = self._display.SetViewDefault()
  1528. # hack for latlon projection
  1529. # TODO find more precise way or better rewrite it in OGSF
  1530. self.iview['z-exag']['llRatio'] = 1
  1531. if grass.locn_is_latlong():
  1532. self.iview['z-exag']['llRatio'] = math.pi / 180 * 6371000 * math.cos(
  1533. (grass.region()['n'] + grass.region()['s']) / 2)
  1534. self.view[
  1535. 'z-exag']['value'] = round(zexagOriginal * self.iview['z-exag']['llRatio'])
  1536. self.view['z-exag']['min'] = UserSettings.Get(group='nviz', key='view',
  1537. subkey=('z-exag', 'min'))
  1538. zexagMax = UserSettings.Get(group='nviz', key='view',
  1539. subkey=('z-exag', 'max'))
  1540. if zexagMax <= self.view['z-exag']['value']:
  1541. self.view['z-exag']['max'] = self.view['z-exag']['value'] * 2
  1542. elif self.view['z-exag']['value'] < 1:
  1543. if self.view['z-exag']['value'] == 0:
  1544. self.view['z-exag']['value'] = 1
  1545. self.view['z-exag']['max'] = 10 * self.view['z-exag']['value']
  1546. else:
  1547. self.view['z-exag']['max'] = zexagMax
  1548. self.view['position']['x'] = UserSettings.Get(group='nviz', key='view',
  1549. subkey=('position', 'x'))
  1550. self.view['position']['y'] = UserSettings.Get(group='nviz', key='view',
  1551. subkey=('position', 'y'))
  1552. self.view['persp']['value'] = UserSettings.Get(
  1553. group='nviz', key='view', subkey=('persp', 'value'))
  1554. self.view['twist']['value'] = UserSettings.Get(
  1555. group='nviz', key='view', subkey=('twist', 'value'))
  1556. self._display.ResetRotation()
  1557. self.iview['rotation'] = None
  1558. self._display.LookAtCenter()
  1559. focus = self.iview['focus']
  1560. focus['x'], focus['y'], focus['z'] = self._display.GetFocus()
  1561. self.PostViewEvent()
  1562. def UpdateMapObjProperties(self, event):
  1563. """Generic method to update data layer properties"""
  1564. data = event.data
  1565. if 'surface' in data:
  1566. try:
  1567. id = data['surface']['object']['id']
  1568. except KeyError:
  1569. return
  1570. self.UpdateSurfaceProperties(id, data['surface'])
  1571. # -> initialized
  1572. data['surface']['object']['init'] = True
  1573. elif 'constant' in data:
  1574. id = data['constant']['object']['id']
  1575. self.UpdateConstantProperties(id, data['constant'])
  1576. # -> initialized
  1577. data['constant']['object']['init'] = True
  1578. elif 'volume' in data:
  1579. id = data['volume']['object']['id']
  1580. self.UpdateVolumeProperties(id, data['volume'])
  1581. # -> initialized
  1582. data['volume']['object']['init'] = True
  1583. elif 'vector' in data:
  1584. for type in ('lines', 'points'):
  1585. if 'object' in data['vector'][type]:
  1586. id = data['vector'][type]['object']['id']
  1587. self.UpdateVectorProperties(id, data['vector'], type)
  1588. # -> initialized
  1589. data['vector'][type]['object']['init'] = True
  1590. def UpdateConstantProperties(self, id, data):
  1591. """Update surface map object properties"""
  1592. self._display.SetSurfaceColor(id=id, map=False, value=data['color'])
  1593. self._display.SetSurfaceTopo(id=id, map=False, value=data['value'])
  1594. self._display.SetSurfaceRes(id, data['resolution'], data['resolution'])
  1595. if data['transp'] == 0:
  1596. self._display.UnsetSurfaceTransp(id)
  1597. else:
  1598. self._display.SetSurfaceTransp(id, map=False, value=data['transp'])
  1599. def UpdateSurfaceProperties(self, id, data):
  1600. """Update surface map object properties"""
  1601. # surface attributes
  1602. for attrb in ('color', 'mask',
  1603. 'transp', 'shine'):
  1604. if attrb not in data['attribute'] or \
  1605. 'update' not in data['attribute'][attrb]:
  1606. continue
  1607. map = data['attribute'][attrb]['map']
  1608. value = data['attribute'][attrb]['value']
  1609. if map is None: # unset
  1610. # only optional attributes
  1611. if attrb == 'mask':
  1612. # TODO: invert mask
  1613. # TODO: broken in NVIZ
  1614. self._display.UnsetSurfaceMask(id)
  1615. elif attrb == 'transp':
  1616. self._display.UnsetSurfaceTransp(id)
  1617. else:
  1618. if isinstance(value, types.StringType):
  1619. if len(value) == 0: # ignore empty values (TODO: warning)
  1620. continue
  1621. if map and not grass.find_file(value, element='cell')[
  1622. 'fullname']:
  1623. continue
  1624. if attrb == 'color':
  1625. self._display.SetSurfaceColor(id, map, str(value))
  1626. elif attrb == 'mask':
  1627. # TODO: invert mask
  1628. # TODO: broken in NVIZ
  1629. self._display.SetSurfaceMask(id, False, str(value))
  1630. elif attrb == 'transp':
  1631. self._display.SetSurfaceTransp(id, map, str(value))
  1632. elif attrb == 'shine':
  1633. self._display.SetSurfaceShine(id, map, str(value))
  1634. data['attribute'][attrb].pop('update')
  1635. # draw res
  1636. if 'update' in data['draw']['resolution']:
  1637. coarse = data['draw']['resolution']['coarse']
  1638. fine = data['draw']['resolution']['fine']
  1639. if data['draw']['all']:
  1640. self._display.SetSurfaceRes(-1, fine, coarse)
  1641. else:
  1642. self._display.SetSurfaceRes(id, fine, coarse)
  1643. data['draw']['resolution'].pop('update')
  1644. # draw style
  1645. if 'update' in data['draw']['mode']:
  1646. if data['draw']['mode']['value'] < 0: # need to calculate
  1647. data['draw']['mode']['value'] = self.nvizDefault.GetDrawMode(
  1648. mode=data['draw']['mode']['desc']['mode'],
  1649. style=data['draw']['mode']['desc']['style'],
  1650. shade=data['draw']['mode']['desc']['shading'],
  1651. string=True)
  1652. style = data['draw']['mode']['value']
  1653. if data['draw']['all']:
  1654. self._display.SetSurfaceStyle(-1, style)
  1655. else:
  1656. self._display.SetSurfaceStyle(id, style)
  1657. data['draw']['mode'].pop('update')
  1658. # wire color
  1659. if 'update' in data['draw']['wire-color']:
  1660. color = data['draw']['wire-color']['value']
  1661. if data['draw']['all']:
  1662. self._display.SetWireColor(-1, str(color))
  1663. else:
  1664. self._display.SetWireColor(id, str(color))
  1665. data['draw']['wire-color'].pop('update')
  1666. # position
  1667. if 'update' in data['position']:
  1668. x = data['position']['x']
  1669. y = data['position']['y']
  1670. z = data['position']['z']
  1671. self._display.SetSurfacePosition(id, x, y, z)
  1672. data['position'].pop('update')
  1673. data['draw']['all'] = False
  1674. def UpdateVolumeProperties(self, id, data, isosurfId=None):
  1675. """Update volume (isosurface/slice) map object properties"""
  1676. if 'update' in data['draw']['resolution']:
  1677. if data['draw']['mode']['value'] == 0:
  1678. self._display.SetIsosurfaceRes(
  1679. id, data['draw']['resolution']['isosurface']['value'])
  1680. else:
  1681. self._display.SetSliceRes(
  1682. id, data['draw']['resolution']['slice']['value'])
  1683. data['draw']['resolution'].pop('update')
  1684. if 'update' in data['draw']['shading']:
  1685. if data['draw']['mode']['value'] == 0:
  1686. if data['draw']['shading']['isosurface'][
  1687. 'value'] < 0: # need to calculate
  1688. mode = data['draw']['shading']['isosurface']['value'] = \
  1689. self.nvizDefault.GetDrawMode(shade=data['draw']['shading']['isosurface'],
  1690. string=False)
  1691. self._display.SetIsosurfaceMode(id, mode)
  1692. else:
  1693. if data['draw']['shading']['slice'][
  1694. 'value'] < 0: # need to calculate
  1695. mode = data['draw']['shading']['slice']['value'] = \
  1696. self.nvizDefault.GetDrawMode(shade=data['draw']['shading']['slice'],
  1697. string=False)
  1698. self._display.SetSliceMode(id, mode)
  1699. data['draw']['shading'].pop('update')
  1700. #
  1701. # isosurface attributes
  1702. #
  1703. isosurfId = 0
  1704. for isosurf in data['isosurface']:
  1705. self._display.AddIsosurface(id, 0, isosurf_id=isosurfId)
  1706. for attrb in ('topo', 'color', 'mask',
  1707. 'transp', 'shine'):
  1708. if attrb not in isosurf or \
  1709. 'update' not in isosurf[attrb]:
  1710. continue
  1711. map = isosurf[attrb]['map']
  1712. value = isosurf[attrb]['value']
  1713. if map is None: # unset
  1714. # only optional attributes
  1715. if attrb == 'topo':
  1716. self._display.SetIsosurfaceTopo(
  1717. id, isosurfId, map, str(value))
  1718. elif attrb == 'mask':
  1719. # TODO: invert mask
  1720. # TODO: broken in NVIZ
  1721. self._display.UnsetIsosurfaceMask(id, isosurfId)
  1722. elif attrb == 'transp':
  1723. self._display.UnsetIsosurfaceTransp(id, isosurfId)
  1724. else:
  1725. if isinstance(value, types.StringType):
  1726. if len(value) == 0: # ignore empty values (TODO: warning)
  1727. continue
  1728. if map and not grass.find_file(value, element='grid3')[
  1729. 'fullname']:
  1730. continue
  1731. if attrb == 'color':
  1732. self._display.SetIsosurfaceColor(
  1733. id, isosurfId, map, str(value))
  1734. elif attrb == 'mask':
  1735. # TODO: invert mask
  1736. # TODO: broken in NVIZ
  1737. self._display.SetIsosurfaceMask(
  1738. id, isosurfId, False, str(value))
  1739. elif attrb == 'transp':
  1740. self._display.SetIsosurfaceTransp(
  1741. id, isosurfId, map, str(value))
  1742. elif attrb == 'shine':
  1743. self._display.SetIsosurfaceShine(
  1744. id, isosurfId, map, str(value))
  1745. isosurf[attrb].pop('update')
  1746. isosurfId += 1
  1747. #
  1748. # slice attributes
  1749. #
  1750. sliceId = 0
  1751. for slice in data['slice']:
  1752. ret = self._display.AddSlice(id, slice_id=sliceId)
  1753. if 'update' in slice['position']:
  1754. pos = slice['position']
  1755. ret = self._display.SetSlicePosition(
  1756. id, sliceId, pos['x1'],
  1757. pos['x2'],
  1758. pos['y1'],
  1759. pos['y2'],
  1760. pos['z1'],
  1761. pos['z2'],
  1762. pos['axis'])
  1763. slice['position'].pop('update')
  1764. if 'update' in slice['transp']:
  1765. tr = slice['transp']['value']
  1766. self._display.SetSliceTransp(id, sliceId, tr)
  1767. sliceId += 1
  1768. # position
  1769. if 'update' in data['position'] and 'x' in data['position']:
  1770. x = data['position']['x']
  1771. y = data['position']['y']
  1772. z = data['position']['z']
  1773. self._display.SetVolumePosition(id, x, y, z)
  1774. data['position'].pop('update')
  1775. def UpdateVectorProperties(self, id, data, type):
  1776. """Update vector layer properties
  1777. :param id: layer id
  1778. :param data: properties
  1779. :param type: lines/points
  1780. """
  1781. if type == 'points':
  1782. self.UpdateVectorPointsProperties(id, data[type])
  1783. else:
  1784. self.UpdateVectorLinesProperties(id, data[type])
  1785. def UpdateVectorLinesProperties(self, id, data):
  1786. """Update vector line map object properties"""
  1787. # mode
  1788. if 'update' in data['color'] or \
  1789. 'update' in data['width'] or \
  1790. 'update' in data['mode']:
  1791. width = data['width']['value']
  1792. color = data['color']['value']
  1793. if data['mode']['type'] == '3d':
  1794. use_3D = True
  1795. if 'surface' in data['mode']:
  1796. data['mode'].pop('surface')
  1797. else:
  1798. use_3D = False
  1799. self._display.SetVectorLineMode(id, color,
  1800. width, use_3D)
  1801. if 'update' in data['color']:
  1802. data['color'].pop('update')
  1803. if 'update' in data['width']:
  1804. data['width'].pop('update')
  1805. # height
  1806. if 'update' in data['height']:
  1807. self._display.SetVectorLineHeight(id,
  1808. data['height']['value'])
  1809. data['height'].pop('update')
  1810. # thematic
  1811. if 'update' in data['thematic']:
  1812. color = width = None
  1813. colorTable = False
  1814. if data['thematic']['usecolor'] or data['thematic']['usewidth']:
  1815. if data['thematic']['usecolor']:
  1816. color = data['thematic']['rgbcolumn']
  1817. if self._display.CheckColorTable(id=id, type='lines'):
  1818. colorTable = True
  1819. if data['thematic']['usewidth']:
  1820. width = data['thematic']['sizecolumn']
  1821. self._display.SetLinesStyleThematic(
  1822. id=id, layer=data['thematic']['layer'],
  1823. color=color, colorTable=colorTable, width=width)
  1824. else:
  1825. self._display.UnsetLinesStyleThematic(id=id)
  1826. data['thematic'].pop('update')
  1827. # surface
  1828. if 'surface' in data['mode'] and 'update' in data['mode']:
  1829. for item in range(len(data['mode']['surface']['value'])):
  1830. for type in ('raster', 'constant'):
  1831. sid = self.GetLayerId(
  1832. type=type, name=data['mode']['surface']['value'][item])
  1833. if sid > -1:
  1834. if data['mode']['surface']['show'][item]:
  1835. self._display.SetVectorLineSurface(id, sid)
  1836. else:
  1837. self._display.UnsetVectorLineSurface(id, sid)
  1838. break
  1839. if 'update' in data['mode']:
  1840. data['mode'].pop('update')
  1841. def UpdateVectorPointsProperties(self, id, data):
  1842. """Update vector point map object properties"""
  1843. if 'update' in data['size'] or \
  1844. 'update' in data['width'] or \
  1845. 'update' in data['marker'] or \
  1846. 'update' in data['color']:
  1847. ret = self._display.SetVectorPointMode(
  1848. id, data['color']['value'],
  1849. data['width']['value'],
  1850. float(data['size']['value']),
  1851. data['marker']['value'] + 1)
  1852. error = None
  1853. if ret == -1:
  1854. error = _("Vector point layer not found (id = %d)") % id
  1855. elif ret == -2:
  1856. error = _("Unable to set data layer properties (id = %d)") % id
  1857. if error:
  1858. raise GException(
  1859. _("Setting data layer properties failed.\n\n%s") %
  1860. error)
  1861. for prop in ('size', 'width', 'marker', 'color'):
  1862. if 'update' in data[prop]:
  1863. data[prop].pop('update')
  1864. # height
  1865. if 'update' in data['height']:
  1866. self._display.SetVectorPointHeight(id,
  1867. data['height']['value'])
  1868. data['height'].pop('update')
  1869. # thematic
  1870. if 'update' in data['thematic']:
  1871. color = size = None
  1872. colorTable = False
  1873. if data['thematic']['usecolor'] or data['thematic']['usesize']:
  1874. if data['thematic']['usecolor']:
  1875. color = data['thematic']['rgbcolumn']
  1876. if self._display.CheckColorTable(id=id, type='points'):
  1877. colorTable = True
  1878. if data['thematic']['usesize']:
  1879. size = data['thematic']['sizecolumn']
  1880. self._display.SetPointsStyleThematic(
  1881. id=id, layer=data['thematic']['layer'],
  1882. color=color, colorTable=colorTable, size=size)
  1883. else:
  1884. self._display.UnsetPointsStyleThematic(id=id)
  1885. data['thematic'].pop('update')
  1886. # surface
  1887. if 'update' in data['mode']:
  1888. if data['mode'].get('3d', False):
  1889. self._display.SetVectorPointZMode(id, True)
  1890. elif 'surface' in data['mode']:
  1891. self._display.SetVectorPointZMode(id, False)
  1892. for item in range(len(data['mode']['surface']['value'])):
  1893. for type in ('raster', 'constant'):
  1894. sid = self.GetLayerId(
  1895. type=type, name=data['mode']['surface']['value'][item])
  1896. if sid > -1:
  1897. if data['mode']['surface']['show'][item]:
  1898. self._display.SetVectorPointSurface(id, sid)
  1899. else:
  1900. self._display.UnsetVectorPointSurface(id, sid)
  1901. break
  1902. data['mode'].pop('update')
  1903. def GetLayerNames(self, type):
  1904. """Return list of map layer names of given type"""
  1905. layerName = []
  1906. if type == 'constant':
  1907. for item in self.constants:
  1908. layerName.append(_("constant#") +
  1909. str(item['constant']['object']['name']))
  1910. else:
  1911. for item in self.layers:
  1912. mapLayer = self.tree.GetLayerInfo(item, key='maplayer')
  1913. if type != mapLayer.GetType():
  1914. continue
  1915. layerName.append(mapLayer.GetName())
  1916. return layerName
  1917. def GetLayerId(self, type, name, vsubtyp=None):
  1918. """Get layer object id or -1"""
  1919. if len(name) < 1:
  1920. return -1
  1921. if type == 'constant':
  1922. for item in self.constants:
  1923. if _("constant#") + str(item['constant']
  1924. ['object']['name']) == name:
  1925. return item['constant']['object']['id']
  1926. for item in self.layers:
  1927. mapLayer = self.tree.GetLayerInfo(item, key='maplayer')
  1928. if type != mapLayer.GetType() or \
  1929. name != mapLayer.GetName():
  1930. continue
  1931. data = self.tree.GetLayerInfo(item, key='nviz')
  1932. try:
  1933. if type == 'raster':
  1934. return data['surface']['object']['id']
  1935. elif type == 'vector':
  1936. if vsubtyp == 'vpoint':
  1937. return data['vector']['points']['object']['id']
  1938. elif vsubtyp == 'vline':
  1939. return data['vector']['lines']['object']['id']
  1940. elif type == 'raster_3d':
  1941. return data['volume']['object']['id']
  1942. except KeyError:
  1943. return -1
  1944. return -1
  1945. def ReloadLayersData(self):
  1946. """Delete nviz data of all loaded layers and reload them from current settings"""
  1947. for item in self.layers:
  1948. type = self.tree.GetLayerInfo(item, key='type')
  1949. layer = self.tree.GetLayerInfo(item, key='maplayer')
  1950. data = self.tree.GetLayerInfo(item, key='nviz')
  1951. if type == 'raster':
  1952. self.nvizDefault.SetSurfaceDefaultProp(data['surface'])
  1953. if type == 'vector':
  1954. vInfo = grass.vector_info_topo(layer.GetName())
  1955. if (vInfo['points'] + vInfo['centroids']) > 0:
  1956. self.nvizDefault.SetVectorPointsDefaultProp(
  1957. data['vector']['points'], self._display.GetLongDim())
  1958. if (vInfo['lines'] + vInfo['boundaries']) > 0:
  1959. self.nvizDefault.SetVectorLinesDefaultProp(
  1960. data
  1961. ['vector']
  1962. ['lines'])
  1963. def NvizCmdCommand(self):
  1964. """Generate command for m.nviz.image according to current state"""
  1965. cmd = 'm.nviz.image '
  1966. rasters = []
  1967. vectors = []
  1968. volumes = []
  1969. for item in self.layers:
  1970. if self.tree.GetLayerInfo(item, key='type') == 'raster':
  1971. rasters.append(item)
  1972. elif self.tree.GetLayerInfo(item, key='type') == 'raster_3d':
  1973. volumes.append(item)
  1974. elif self.tree.GetLayerInfo(item, key='type') == 'vector':
  1975. vectors.append(item)
  1976. # if not rasters and not self.constants:
  1977. # return _("At least one raster map required")
  1978. # elevation_map/elevation_value
  1979. if self.constants:
  1980. subcmd = "elevation_value="
  1981. for constant in self.constants:
  1982. subcmd += "%d," % constant['constant']['value']
  1983. subcmd = subcmd.strip(', ') + ' '
  1984. cmd += subcmd
  1985. if rasters:
  1986. subcmd = "elevation_map="
  1987. for item in rasters:
  1988. subcmd += "%s," % self.tree.GetLayerInfo(
  1989. item, key='maplayer').GetName()
  1990. subcmd = subcmd.strip(', ') + ' '
  1991. cmd += subcmd
  1992. #
  1993. # draw mode
  1994. #
  1995. cmdMode = "mode="
  1996. cmdFine = "resolution_fine="
  1997. cmdCoarse = "resolution_coarse="
  1998. cmdShading = "shading="
  1999. cmdStyle = "style="
  2000. cmdWire = "wire_color="
  2001. # test -a flag
  2002. flag_a = "-a "
  2003. nvizDataFirst = self.tree.GetLayerInfo(
  2004. rasters[0], key='nviz')['surface']['draw']
  2005. for item in rasters:
  2006. nvizData = self.tree.GetLayerInfo(item, key='nviz')[
  2007. 'surface']['draw']
  2008. if nvizDataFirst != nvizData:
  2009. flag_a = ""
  2010. cmd += flag_a
  2011. for item in rasters:
  2012. nvizData = self.tree.GetLayerInfo(item, key='nviz')[
  2013. 'surface']['draw']
  2014. cmdMode += "%s," % nvizData['mode']['desc']['mode']
  2015. cmdFine += "%s," % nvizData['resolution']['fine']
  2016. cmdCoarse += "%s," % nvizData['resolution']['coarse']
  2017. cmdShading += "%s," % nvizData['mode']['desc']['shading']
  2018. cmdStyle += "%s," % nvizData['mode']['desc']['style']
  2019. cmdWire += "%s," % nvizData['wire-color']['value']
  2020. for item in self.constants:
  2021. cmdMode += "fine,"
  2022. cmdFine += "%s," % item['constant']['resolution']
  2023. cmdCoarse += "%s," % item['constant']['resolution']
  2024. cmdShading += "gouraud,"
  2025. cmdStyle += "surface,"
  2026. cmdWire += "0:0:0,"
  2027. mode = []
  2028. for subcmd in (cmdMode, cmdFine, cmdCoarse,
  2029. cmdShading, cmdStyle, cmdWire):
  2030. if flag_a:
  2031. mode.append(subcmd.split(',')[0] + ' ')
  2032. else:
  2033. subcmd = subcmd.strip(', ') + ' '
  2034. cmd += subcmd
  2035. if flag_a: # write only meaningful possibilities
  2036. cmd += mode[0]
  2037. if 'fine' in mode[0]:
  2038. cmd += mode[1]
  2039. elif 'coarse' in mode[0]:
  2040. cmd += mode[2]
  2041. elif 'both' in mode[0]:
  2042. cmd += mode[2]
  2043. cmd += mode[1]
  2044. if 'flat' in mode[3]:
  2045. cmd += mode[3]
  2046. if 'wire' in mode[4]:
  2047. cmd += mode[4]
  2048. if 'coarse' in mode[0] or 'both' in mode[
  2049. 0] and 'wire' in mode[3]:
  2050. cmd += mode[5]
  2051. #
  2052. # attributes
  2053. #
  2054. cmdColorMap = "color_map="
  2055. cmdColorVal = "color="
  2056. for item in rasters:
  2057. nvizData = self.tree.GetLayerInfo(
  2058. item, key='nviz')['surface']['attribute']
  2059. if 'color' not in nvizData:
  2060. cmdColorMap += "%s," % self.tree.GetLayerInfo(
  2061. item, key='maplayer').GetName()
  2062. else:
  2063. if nvizData['color']['map']:
  2064. cmdColorMap += "%s," % nvizData['color']['value']
  2065. else:
  2066. cmdColorVal += "%s," % nvizData['color']['value']
  2067. # TODO
  2068. # transparency, shine, mask
  2069. for item in self.constants:
  2070. cmdColorVal += "%s," % item['constant']['color']
  2071. if cmdColorMap.split("=")[1]:
  2072. cmd += cmdColorMap.strip(', ') + ' '
  2073. if cmdColorVal.split("=")[1]:
  2074. cmd += cmdColorVal.strip(', ') + ' '
  2075. cmd += "\\\n"
  2076. #
  2077. # vlines
  2078. #
  2079. if vectors:
  2080. cmdLines = cmdLWidth = cmdLHeight = cmdLColor = cmdLMode = cmdLPos = \
  2081. cmdPoints = cmdPWidth = cmdPSize = cmdPColor = cmdPMarker = cmdPPos = cmdPLayer = ""
  2082. markers = ['x', 'box', 'sphere', 'cube', 'diamond',
  2083. 'dec_tree', 'con_tree', 'aster', 'gyro', 'histogram']
  2084. for vector in vectors:
  2085. layerName = self.tree.GetLayerInfo(
  2086. vector, key='maplayer').GetName()
  2087. vInfo = grass.vector_info_topo(layerName)
  2088. nvizData = self.tree.GetLayerInfo(vector, key='nviz')['vector']
  2089. if (vInfo['lines'] + vInfo['boundaries']) > 0:
  2090. cmdLines += "%s," % self.tree.GetLayerInfo(
  2091. vector, key='maplayer').GetName()
  2092. cmdLWidth += "%d," % nvizData['lines']['width']['value']
  2093. cmdLHeight += "%d," % nvizData['lines']['height']['value']
  2094. cmdLColor += "%s," % nvizData['lines']['color']['value']
  2095. cmdLMode += "%s," % nvizData['lines']['mode']['type']
  2096. cmdLPos += "0,0,%d," % nvizData['lines']['height']['value']
  2097. if (vInfo['points'] + vInfo['centroids']) > 0:
  2098. cmdPoints += "%s," % self.tree.GetLayerInfo(
  2099. vector, key='maplayer').GetName()
  2100. cmdPWidth += "%d," % nvizData['points']['width']['value']
  2101. cmdPSize += "%d," % nvizData['points']['size']['value']
  2102. cmdPColor += "%s," % nvizData['points']['color']['value']
  2103. cmdPMarker += "%s," % markers[
  2104. nvizData['points']['marker']['value']]
  2105. cmdPPos += "0,0,%d," % nvizData[
  2106. 'points']['height']['value']
  2107. cmdPLayer += "1,1,"
  2108. if cmdLines:
  2109. cmd += "vline=" + cmdLines.strip(',') + ' '
  2110. cmd += "vline_width=" + cmdLWidth.strip(',') + ' '
  2111. cmd += "vline_color=" + cmdLColor.strip(',') + ' '
  2112. cmd += "vline_height=" + cmdLHeight.strip(',') + ' '
  2113. cmd += "vline_mode=" + cmdLMode.strip(',') + ' '
  2114. cmd += "vline_position=" + cmdLPos.strip(',') + ' '
  2115. if cmdPoints:
  2116. cmd += "vpoint=" + cmdPoints.strip(',') + ' '
  2117. cmd += "vpoint_width=" + cmdPWidth.strip(',') + ' '
  2118. cmd += "vpoint_color=" + cmdPColor.strip(',') + ' '
  2119. cmd += "vpoint_size=" + cmdPSize.strip(',') + ' '
  2120. cmd += "vpoint_marker=" + cmdPMarker.strip(',') + ' '
  2121. cmd += "vpoint_position=" + cmdPPos.strip(',') + ' '
  2122. cmd += "vpoint_layer=" + cmdPLayer.strip(',') + ' '
  2123. cmd += "\\\n"
  2124. #
  2125. # volumes
  2126. #
  2127. if volumes:
  2128. cmdName = cmdShade = cmdRes = cmdPos = cmdIso = ""
  2129. cmdIsoColorMap = cmdIsoColorVal = cmdIsoTrMap = cmdIsoTrVal = ""
  2130. cmdSlice = cmdSliceTransp = cmdSlicePos = ""
  2131. for i, volume in enumerate(volumes):
  2132. nvizData = self.tree.GetLayerInfo(volume, key='nviz')['volume']
  2133. cmdName += "%s," % self.tree.GetLayerInfo(
  2134. volume, key='maplayer').GetName()
  2135. cmdShade += "%s," % nvizData['draw'][
  2136. 'shading']['isosurface']['desc']
  2137. cmdRes += "%d," % nvizData['draw'][
  2138. 'resolution']['isosurface']['value']
  2139. if nvizData['position']:
  2140. cmdPos += "%d,%d,%d," % (
  2141. nvizData['position']['x'],
  2142. nvizData['position']['y'],
  2143. nvizData['position']['z'])
  2144. for iso in nvizData['isosurface']:
  2145. level = iso['topo']['value']
  2146. cmdIso += "%d:%s," % (i + 1, level)
  2147. if iso['color']['map']:
  2148. cmdIsoColorMap += "%s," % iso['color']['value']
  2149. else:
  2150. cmdIsoColorVal += "%s," % iso['color']['value']
  2151. if 'transp' in iso:
  2152. if iso['transp']['map']:
  2153. cmdIsoTrMap += "%s," % iso['transp']['value']
  2154. else:
  2155. cmdIsoTrVal += "%s," % iso['transp']['value']
  2156. for slice in nvizData['slice']:
  2157. axis = ('x', 'y', 'z')[slice['position']['axis']]
  2158. cmdSlice += "%d:%s," % (i + 1, axis)
  2159. for coord in ('x1', 'x2', 'y1', 'y2', 'z1', 'z2'):
  2160. cmdSlicePos += "%f," % slice['position'][coord]
  2161. cmdSliceTransp += "%s," % slice['transp']['value']
  2162. cmd += "volume=" + cmdName.strip(',') + ' '
  2163. cmd += "volume_shading=" + cmdShade.strip(',') + ' '
  2164. cmd += "volume_resolution=" + cmdRes.strip(',') + ' '
  2165. if nvizData['position']:
  2166. cmd += "volume_position=" + cmdPos.strip(',') + ' '
  2167. if cmdIso:
  2168. cmd += "isosurf_level=" + cmdIso.strip(',') + ' '
  2169. if cmdIsoColorMap:
  2170. cmd += "isosurf_color_map=" + \
  2171. cmdIsoColorMap.strip(',') + ' '
  2172. if cmdIsoColorVal:
  2173. cmd += "isosurf_color_value=" + \
  2174. cmdIsoColorVal.strip(',') + ' '
  2175. if cmdIsoTrMap:
  2176. cmd += "isosurf_transp_map=" + cmdIsoTrMap.strip(',') + ' '
  2177. if cmdIsoTrVal:
  2178. cmd += "isosurf_transp_value=" + \
  2179. cmdIsoTrVal.strip(',') + ' '
  2180. if cmdSlice:
  2181. cmd += "slice=" + cmdSlice.strip(',') + ' '
  2182. cmd += "slice_position=" + cmdSlicePos.strip(',') + ' '
  2183. cmd += "slice_transparency=" + cmdSliceTransp.strip(',') + ' '
  2184. #
  2185. # cutting planes
  2186. #
  2187. cplane = self.lmgr.nviz.FindWindowById(
  2188. self.lmgr.nviz.win['cplane']['planes']).GetStringSelection()
  2189. try:
  2190. planeIndex = int(cplane.split()[-1]) - 1
  2191. except (IndexError, ValueError):
  2192. planeIndex = None
  2193. if planeIndex is not None:
  2194. shading = ['clear', 'top', 'bottom', 'blend', 'shaded']
  2195. cmd += "cplane=%d " % planeIndex
  2196. cmd += "cplane_rotation=%d " % self.cplanes[
  2197. planeIndex]['rotation']['rot']
  2198. cmd += "cplane_tilt=%d " % self.cplanes[
  2199. planeIndex]['rotation']['tilt']
  2200. cmd += "cplane_position=%d,%d,%d " % (
  2201. self.cplanes[planeIndex]['position']['x'],
  2202. self.cplanes[planeIndex]['position']['y'],
  2203. self.cplanes[planeIndex]['position']['z'])
  2204. cmd += "cplane_shading=%s " % shading[
  2205. self.cplanes[planeIndex]['shading']]
  2206. cmd += "\\\n"
  2207. #
  2208. # viewpoint
  2209. #
  2210. subcmd = "position=%.2f,%.2f " % (
  2211. self.view['position']['x'],
  2212. self.view['position']['y'])
  2213. subcmd += "height=%d " % (self.iview['height']['value'])
  2214. subcmd += "perspective=%d " % (self.view['persp']['value'])
  2215. subcmd += "twist=%d " % (self.view['twist']['value'])
  2216. subcmd += "zexag=%f " % (self.view['z-exag']
  2217. ['value'] / self.iview['z-exag']['llRatio'])
  2218. subcmd += "focus=%d,%d,%d " % (
  2219. self.iview['focus']['x'],
  2220. self.iview['focus']['y'],
  2221. self.iview['focus']['z'])
  2222. cmd += subcmd
  2223. # background
  2224. subcmd = "bgcolor=%d:%d:%d " % (self.view['background']['color'][:3])
  2225. if self.view['background']['color'] != (255, 255, 255):
  2226. cmd += subcmd
  2227. cmd += "\\\n"
  2228. # light
  2229. subcmd = "light_position=%.2f,%.2f,%.2f " % (
  2230. self.light['position']['x'],
  2231. self.light['position']['y'],
  2232. self.light['position']['z'] / 100.)
  2233. subcmd += "light_brightness=%d " % (self.light['bright'])
  2234. subcmd += "light_ambient=%d " % (self.light['ambient'])
  2235. subcmd += "light_color=%d:%d:%d " % (self.light['color'][:3])
  2236. cmd += subcmd
  2237. cmd += "\\\n"
  2238. # fringe
  2239. toolWindow = self.lmgr.nviz
  2240. direction = ''
  2241. for dir in ('nw', 'ne', 'sw', 'se'):
  2242. if toolWindow.FindWindowById(
  2243. toolWindow.win['fringe'][dir]).IsChecked():
  2244. direction += "%s," % dir
  2245. if direction:
  2246. subcmd = "fringe=%s " % (direction.strip(','))
  2247. color = toolWindow.FindWindowById(
  2248. toolWindow.win['fringe']['color']).GetValue()
  2249. subcmd += "fringe_color=%d:%d:%d " % (color[0], color[1], color[2])
  2250. subcmd += "fringe_elevation=%d " % (toolWindow.FindWindowById(
  2251. toolWindow.win['fringe']['elev']).GetValue())
  2252. cmd += subcmd
  2253. cmd += "\\\n"
  2254. # north arrow
  2255. if self.decoration['arrow']['show']:
  2256. subcmd = "arrow_position=%d,%d " % (
  2257. self.decoration['arrow']['position']['x'],
  2258. self.decoration['arrow']['position']['y'])
  2259. subcmd += "arrow_color=%s " % self.decoration['arrow']['color']
  2260. subcmd += "arrow_size=%d " % self.decoration['arrow']['size']
  2261. cmd += subcmd
  2262. # output
  2263. width, height = self.GetClientSize()
  2264. subcmd = 'output=nviz_output '
  2265. subcmd += 'format=ppm '
  2266. subcmd += 'size=%d,%d ' % (width, height)
  2267. cmd += subcmd
  2268. return cmd
  2269. def OnNvizCmd(self):
  2270. """Generate and write command to command output"""
  2271. self.log.WriteLog(
  2272. self.NvizCmdCommand(),
  2273. notification=Notification.RAISE_WINDOW)
  2274. def SaveToFile(self, FileName, FileType, width, height):
  2275. """This draws the DC to a buffer that can be saved to a file.
  2276. .. todo::
  2277. fix BufferedPaintDC
  2278. :param filename: file name
  2279. :param FileType: type of bitmap
  2280. :param width: image width
  2281. :param height: image height
  2282. """
  2283. self._display.SaveToFile(FileName, width, height, FileType)
  2284. # pbuffer = wx.EmptyBitmap(max(1, self.Map.width), max(1, self.Map.height))
  2285. # dc = wx.BufferedPaintDC(self, pbuffer)
  2286. # dc.Clear()
  2287. # self.SetCurrent()
  2288. # self._display.Draw(False, -1)
  2289. # pbuffer.SaveFile(FileName, FileType)
  2290. # self.SwapBuffers()
  2291. def GetDisplay(self):
  2292. """Get display instance"""
  2293. return self._display
  2294. def ZoomToMap(self, layers):
  2295. """Reset view
  2296. :param layers: so far unused
  2297. """
  2298. self.lmgr.nviz.OnResetView(None)
  2299. def DisactivateWin(self):
  2300. """Use when the class instance is hidden in MapFrame."""
  2301. pass
  2302. def ActivateWin(self):
  2303. """Used when the class instance is activated in MapFrame."""
  2304. pass