mapwindow.py 100 KB

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