mapwindow.py 104 KB

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