mapwindow.py 100 KB

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