mapdisp.py 135 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562
  1. """
  2. MODULE: mapdisp.py
  3. CLASSES:
  4. - Command
  5. - BufferedWindow
  6. - MapFrame
  7. - MapApp
  8. PURPOSE: GIS map display canvas, with toolbar for various display
  9. management functions, and second toolbar for vector
  10. digitizing. Can be used either from GIS Manager or as p.mon backend
  11. Usage:
  12. python mapdisp.py monitor-identifier /path/to/command/file
  13. AUTHORS: Michael Barton
  14. Jachym Cepicky
  15. Martin Landa <landa.martin gmail.com>
  16. COPYRIGHT: (C) 2006-2008 by the GRASS Development Team
  17. This program is free software under the GNU General Public
  18. License (>=v2). Read the file COPYING that comes with GRASS
  19. for details.
  20. """
  21. import os
  22. import sys
  23. import time
  24. import glob
  25. import math
  26. import tempfile
  27. import wx
  28. import wx.aui
  29. from threading import Thread
  30. import globalvar
  31. try:
  32. import subprocess
  33. except:
  34. CompatPath = os.path.join(globalvar.ETCWXDIR)
  35. sys.path.append(CompatPath)
  36. from compat import subprocess
  37. gmpath = os.path.join(globalvar.ETCWXDIR, "icons")
  38. sys.path.append(gmpath)
  39. import render
  40. import toolbars
  41. import grassenv
  42. import track
  43. import menuform
  44. import gselect
  45. import disp_print
  46. import gcmd
  47. import dbm
  48. import histogram
  49. import profile
  50. import globalvar
  51. import utils
  52. import gdialogs
  53. from vdigit import VDigitCategoryDialog as VDigitCategoryDialog
  54. from vdigit import VDigitZBulkDialog as VDigitZBulkDialog
  55. from vdigit import VDigitDuplicatesDialog as VDigitDuplicatesDialog
  56. from vdigit import GV_LINES as VDigit_Lines_Type
  57. from debug import Debug as Debug
  58. from icon import Icons as Icons
  59. from preferences import globalSettings as UserSettings
  60. import images
  61. imagepath = images.__path__[0]
  62. sys.path.append(imagepath)
  63. ###
  64. ### global variables
  65. ###
  66. # for standalone app
  67. cmdfilename = None
  68. class Command(Thread):
  69. """
  70. Creates thread which will observe the command file and see, if
  71. there is new command to be executed
  72. """
  73. def __init__ (self, parent, Map):
  74. Thread.__init__(self)
  75. global cmdfilename
  76. self.parent = parent
  77. self.map = Map
  78. self.cmdfile = open(cmdfilename, "r")
  79. def run(self):
  80. """
  81. Run this in thread
  82. """
  83. dispcmd = []
  84. while 1:
  85. self.parent.redraw = False
  86. line = self.cmdfile.readline().strip()
  87. if line == "quit":
  88. break
  89. if line:
  90. try:
  91. Debug.msg (3, "Command.run(): cmd=%s" % (line))
  92. self.map.AddLayer(item=None, type="raster",
  93. name='',
  94. command=line,
  95. l_opacity=1)
  96. self.parent.redraw =True
  97. except Exception, e:
  98. print "Command Thread: ",e
  99. time.sleep(0.1)
  100. sys.exit()
  101. class BufferedWindow(wx.Window):
  102. """
  103. A Buffered window class.
  104. When the drawing needs to change, you app needs to call the
  105. UpdateMap() method. Since the drawing is stored in a bitmap, you
  106. can also save the drawing to file by calling the
  107. SaveToFile(self,file_name,file_type) method.
  108. """
  109. def __init__(self, parent, id,
  110. pos = wx.DefaultPosition,
  111. size = wx.DefaultSize,
  112. style=wx.NO_FULL_REPAINT_ON_RESIZE,
  113. Map=None, tree=None, gismgr=None):
  114. wx.Window.__init__(self, parent, id, pos, size, style)
  115. self.parent = parent
  116. self.Map = Map
  117. self.tree = tree
  118. self.gismanager = gismgr
  119. #
  120. # Flags
  121. #
  122. self.resize = False # indicates whether or not a resize event has taken place
  123. self.dragimg = None # initialize variable for map panning
  124. #
  125. # Variable for drawing on DC
  126. #
  127. self.pen = None # pen for drawing zoom boxes, etc.
  128. self.polypen = None # pen for drawing polylines (measurements, profiles, etc)
  129. # List of wx.Point tuples defining a polyline (geographical coordinates)
  130. self.polycoords = []
  131. # ID of rubber band line
  132. self.lineid = None
  133. # ID of poly line resulting from cumulative rubber band lines (e.g. measurement)
  134. self.plineid = None
  135. #
  136. # Event bindings
  137. #
  138. self.Bind(wx.EVT_PAINT, self.OnPaint)
  139. self.Bind(wx.EVT_SIZE, self.OnSize)
  140. self.Bind(wx.EVT_IDLE, self.OnIdle)
  141. self.Bind(wx.EVT_MOTION, self.MouseActions)
  142. self.Bind(wx.EVT_MOUSE_EVENTS, self.MouseActions)
  143. #
  144. # Render output objects
  145. #
  146. self.mapfile = None # image file to be rendered
  147. self.img = "" # wx.Image object (self.mapfile)
  148. # used in digitization tool (do not redraw vector map)
  149. self.imgVectorMap = None
  150. # decoration overlays
  151. self.overlays = {}
  152. # images and their PseudoDC ID's for painting and dragging
  153. self.imagedict = {}
  154. self.select = {} # selecting/unselecting decorations for dragging
  155. self.textdict = {} # text, font, and color indexed by id
  156. self.currtxtid = None # PseudoDC id for currently selected text
  157. #
  158. # Zoom objects
  159. #
  160. self.zoomhistory = [] # list of past zoom extents
  161. self.currzoom = 0 # current set of extents in zoom history being used
  162. #
  163. # mouse attributes like currently pressed buttons, position on
  164. # the screen, begin and end of dragging, and type of drawing
  165. #
  166. self.mouse = {
  167. 'l' : False,
  168. 'r' : False,
  169. 'm' : False,
  170. 'begin': [0, 0], # screen coordinates
  171. 'end' : [0, 0],
  172. 'use' : "pointer",
  173. 'box' : "point"
  174. }
  175. self.zoomtype = 1 # 1 zoom in, 0 no zoom, -1 zoom out
  176. self.hitradius = 10 # distance for selecting map decorations
  177. self.dialogOffset = 5 # offset for dialog (e.g. DisplayAttributesDialog)
  178. # OnSize called to make sure the buffer is initialized.
  179. # This might result in OnSize getting called twice on some
  180. # platforms at initialization, but little harm done.
  181. ### self.OnSize(None)
  182. # create PseudoDC used for background map, map decorations like scales and legends
  183. self.pdc = wx.PseudoDC()
  184. # used for digitization tool
  185. self.pdcVector = None
  186. # pseudoDC for temporal objects (select box, measurement tool, etc.)
  187. self.pdcTmp = wx.PseudoDC()
  188. # redraw all pdc's, pdcTmp layer is redrawn always (speed issue)
  189. self.redrawAll = True
  190. # will store an off screen empty bitmap for saving to file
  191. self._buffer = ''
  192. self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x:None)
  193. # vars for handling mouse clicks
  194. self.dragid = -1
  195. self.lastpos = (0, 0)
  196. def Draw(self, pdc, img=None, drawid=None, pdctype='image', coords=[0, 0, 0, 0]):
  197. """
  198. Draws map and overlay decorations
  199. """
  200. if drawid == None:
  201. if pdctype == 'image' and img:
  202. drawid = self.imagedict[img]
  203. elif pdctype == 'clear':
  204. drawid == None
  205. else:
  206. drawid = wx.NewId()
  207. if img and pdctype == 'image':
  208. # self.imagedict[img]['coords'] = coords
  209. self.select[self.imagedict[img]['id']] = False # ?
  210. pdc.BeginDrawing()
  211. if drawid != 99:
  212. bg = wx.TRANSPARENT_BRUSH
  213. else:
  214. bg = wx.Brush(self.GetBackgroundColour())
  215. pdc.SetBackground(bg)
  216. #pdc.Clear()
  217. self.Refresh()
  218. Debug.msg (5, "BufferedWindow.Draw(): id=%s, pdctype=%s, coord=%s" % \
  219. (drawid, pdctype, coords))
  220. # set PseudoDC id
  221. if drawid is not None:
  222. pdc.SetId(drawid)
  223. if pdctype == 'clear': # erase the display
  224. bg = wx.WHITE_BRUSH
  225. # bg = wx.Brush(self.GetBackgroundColour())
  226. pdc.SetBackground(bg)
  227. pdc.Clear()
  228. self.Refresh()
  229. pdc.EndDrawing()
  230. return
  231. if pdctype == 'image': # draw selected image
  232. bitmap = wx.BitmapFromImage(img)
  233. w,h = bitmap.GetSize()
  234. pdc.DrawBitmap(bitmap, coords[0], coords[1], True) # draw the composite map
  235. pdc.SetIdBounds(drawid, (coords[0],coords[1], w, h))
  236. elif pdctype == 'box': # draw a box on top of the map
  237. if self.pen:
  238. pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
  239. pdc.SetPen(self.pen)
  240. x2 = max(coords[0],coords[2])
  241. x1 = min(coords[0],coords[2])
  242. y2 = max(coords[1],coords[3])
  243. y1 = min(coords[1],coords[3])
  244. rwidth = x2-x1
  245. rheight = y2-y1
  246. rect = wx.Rect(x1,y1,rwidth,rheight)
  247. pdc.DrawRectangleRect(rect)
  248. pdc.SetIdBounds(drawid,rect)
  249. # self.ovlcoords[drawid] = coords
  250. elif pdctype == 'line': # draw a line on top of the map
  251. if self.pen:
  252. pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
  253. pdc.SetPen(self.pen)
  254. pdc.DrawLine(coords[0], coords[1], coords[2], coords[3])
  255. pdc.SetIdBounds(drawid,(coords[0], coords[1], coords[2], coords[3]))
  256. # self.ovlcoords[drawid] = coords
  257. elif pdctype == 'polyline': # draw a polyline on top of the map
  258. if self.polypen:
  259. pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
  260. pdc.SetPen(self.polypen)
  261. pdc.DrawLines(coords)
  262. # get bounding rectangle for polyline
  263. xlist = []
  264. ylist = []
  265. if len(coords) > 0:
  266. for point in coords:
  267. x,y = point
  268. xlist.append(x)
  269. ylist.append(y)
  270. x1=min(xlist)
  271. x2=max(xlist)
  272. y1=min(ylist)
  273. y2=max(ylist)
  274. pdc.SetIdBounds(drawid,(x1,y1,x2,y2))
  275. # self.ovlcoords[drawid] = [x1,y1,x2,y2]
  276. elif pdctype == 'point': # draw point
  277. if self.pen:
  278. pdc.SetPen(self.pen)
  279. pdc.DrawPoint(coords[0], coords[1])
  280. coordsBound = (coords[0] - 5,
  281. coords[1] - 5,
  282. coords[0] + 5,
  283. coords[1] + 5)
  284. pdc.SetIdBounds(drawid, coordsBound)
  285. # self.ovlcoords[drawid] = coords
  286. elif pdctype == 'text': # draw text on top of map
  287. text = img[0]
  288. rotation = float(img[3])
  289. w, h = self.GetFullTextExtent(img[0])[0:2]
  290. pdc.SetFont(img[1])
  291. pdc.SetTextForeground(img[2])
  292. coords, w, h = self.TextBounds(img, coords)
  293. if rotation == 0:
  294. pdc.DrawText(img[0], coords[0], coords[1])
  295. else:
  296. pdc.DrawRotatedText(img[0], coords[0], coords[1], rotation)
  297. pdc.SetIdBounds(drawid, (coords[0], coords[1], w, h))
  298. # self.ovlcoords[drawid] = coords
  299. pdc.EndDrawing()
  300. self.Refresh()
  301. return drawid
  302. def TextBounds(self, textinfo, coords):
  303. """
  304. Return text boundary data
  305. @param textinfo text metadata (text, font, color, rotation)
  306. @param coords reference point
  307. """
  308. rotation = float(textinfo[3])
  309. Debug.msg (4, "BufferedWindow.TextBounds(): text=%s, rotation=%f" % \
  310. (textinfo[0], rotation))
  311. self.Update()
  312. self.Refresh()
  313. self.SetFont(textinfo[1])
  314. w, h = self.GetTextExtent(textinfo[0])
  315. if rotation == 0:
  316. coords[2], coords[3] = coords[0] + w, coords[1] + h
  317. return coords, w, h
  318. boxh = math.fabs(math.sin(math.radians(rotation)) * w) + h
  319. boxw = math.fabs(math.cos(math.radians(rotation)) * w) + h
  320. coords[2] = coords[0] + boxw
  321. coords[3] = coords[1] + boxh
  322. return coords, boxw, boxh
  323. def OnPaint(self, event):
  324. """
  325. Draw PseudoDC's to buffered paint DC
  326. self.pdc for background and decorations
  327. self.pdcVector for vector map which is edited
  328. self.pdcTmp for temporaly drawn objects (self.polycoords)
  329. If self.redrawAll is False on self.pdcTmp content is re-drawn
  330. """
  331. Debug.msg(4, "BufferedWindow.OnPaint(): redrawAll=%s" % self.redrawAll)
  332. dc = wx.BufferedPaintDC(self, self.buffer)
  333. # we need to clear the dc BEFORE calling PrepareDC
  334. #bg = wx.Brush(self.GetBackgroundColour())
  335. dc.SetBackground(wx.Brush("White"))
  336. dc.Clear()
  337. # use PrepareDC to set position correctly
  338. self.PrepareDC(dc)
  339. # create a clipping rect from our position and size
  340. # and update region
  341. rgn = self.GetUpdateRegion().GetBox()
  342. dc.SetClippingRect(rgn)
  343. if self.redrawAll: # redraw pdc and pdcVector
  344. # draw to the dc using the calculated clipping rect
  345. self.pdc.DrawToDCClipped(dc, rgn)
  346. # draw vector map layer
  347. if self.pdcVector:
  348. self.pdcVector.DrawToDCClipped(dc, rgn)
  349. self.bufferLast = None
  350. else: # do not redraw pdc and pdcVector
  351. if self.bufferLast is None:
  352. # draw to the dc
  353. self.pdc.DrawToDC(dc)
  354. if self.pdcVector:
  355. self.pdcVector.DrawToDC(dc)
  356. # store buffered image
  357. # self.bufferLast = wx.BitmapFromImage(self.buffer.ConvertToImage())
  358. self.bufferLast = dc.GetAsBitmap(wx.Rect(0, 0, self.Map.width, self.Map.height))
  359. pdcLast = wx.PseudoDC()
  360. pdcLast.DrawBitmap(bmp=self.bufferLast, x=0, y=0)
  361. pdcLast.DrawToDC(dc)
  362. # draw temporary object on the foreground
  363. # self.pdcTmp.DrawToDCClipped(dc, rgn)
  364. self.pdcTmp.DrawToDC(dc)
  365. def OnSize(self, event):
  366. """
  367. Scale map image so that it is
  368. the same size as the Window
  369. """
  370. Debug.msg(3, "BufferedWindow.OnSize():")
  371. # set size of the input image
  372. self.Map.ChangeMapSize(self.GetClientSize())
  373. # align extent based on center point and display resolution
  374. # this causes that image is not resized when display windows is resized
  375. # self.Map.AlignExtentFromDisplay()
  376. # Make new off screen bitmap: this bitmap will always have the
  377. # current drawing in it, so it can be used to save the image to
  378. # a file, or whatever.
  379. self.buffer = wx.EmptyBitmap(max(1, self.Map.width), max(1, self.Map.height))
  380. # get the image to be rendered
  381. self.img = self.GetImage()
  382. # update map display
  383. if self.img and self.Map.width + self.Map.height > 0: # scale image during resize
  384. self.img = self.img.Scale(self.Map.width, self.Map.height)
  385. if len(self.Map.GetListOfLayers()) > 0:
  386. self.UpdateMap()
  387. # re-render image on idle
  388. self.resize = True
  389. # reposition checkbox in statusbar
  390. self.parent.StatusbarReposition()
  391. # update statusbar
  392. self.parent.StatusbarUpdate()
  393. def OnIdle(self, event):
  394. """
  395. Only re-render a composite map image from GRASS during
  396. idle time instead of multiple times during resizing.
  397. """
  398. if self.resize:
  399. self.UpdateMap(render=True)
  400. event.Skip()
  401. def SaveToFile(self, FileName, FileType):
  402. """
  403. This draws the psuedo DC to a buffer that
  404. can be saved to a file.
  405. """
  406. dc = wx.BufferedPaintDC(self, self.buffer)
  407. self.pdc.DrawToDC(dc)
  408. if self.pdcVector:
  409. self.pdcVector.DrawToDC(dc)
  410. self.buffer.SaveFile(FileName, FileType)
  411. def GetOverlay(self):
  412. """
  413. Converts rendered overlay files to wx.Image
  414. Updates self.imagedict
  415. @return list of images
  416. """
  417. imgs = []
  418. for overlay in self.Map.GetListOfLayers(l_type="overlay", l_active=True):
  419. if os.path.isfile(overlay.mapfile) and os.path.getsize(overlay.mapfile):
  420. img = wx.Image(overlay.mapfile, wx.BITMAP_TYPE_ANY)
  421. self.imagedict[img] = { 'id' : overlay.id,
  422. 'layer' : overlay }
  423. imgs.append(img)
  424. return imgs
  425. def GetImage(self):
  426. """
  427. Converts redered map files to wx.Image
  428. Updates self.imagedict (id=99)
  429. @return wx.Image instance (map composition)
  430. """
  431. imgId = 99
  432. if self.Map.mapfile and os.path.isfile(self.Map.mapfile) and \
  433. os.path.getsize(self.Map.mapfile):
  434. img = wx.Image(self.Map.mapfile, wx.BITMAP_TYPE_ANY)
  435. else:
  436. img = None
  437. self.imagedict[img] = { 'id': imgId }
  438. return img
  439. def UpdateMap(self, render=True, renderVector=True):
  440. """
  441. Updates the canvas anytime there is a change to the underlaying images
  442. or to the geometry of the canvas.
  443. @param render re-render map composition
  444. @param renderVector re-render vector map layer enabled for editing (used for digitizer)
  445. """
  446. start = time.clock()
  447. self.resize = False
  448. # if len(self.Map.GetListOfLayers()) == 0:
  449. # return False
  450. if self.img is None:
  451. render = True
  452. #
  453. # initialize process bar (only on 'render')
  454. #
  455. if render is True or renderVector is True:
  456. self.parent.onRenderGauge.Show()
  457. if self.parent.onRenderGauge.GetRange() > 0:
  458. self.parent.onRenderGauge.SetValue(1)
  459. self.parent.onRenderTimer.Start(100)
  460. self.parent.onRenderCounter = 0
  461. #
  462. # render background image if needed
  463. #
  464. if render:
  465. self.Map.ChangeMapSize(self.GetClientSize())
  466. windres = False
  467. if self.parent.compResolution.GetValue():
  468. # use computation region resolution for rendering
  469. windres = True
  470. self.mapfile = self.Map.Render(force=True, mapWindow=self.parent, windres=windres)
  471. else:
  472. self.mapfile = self.Map.Render(force=False, mapWindow=self.parent)
  473. self.img = self.GetImage() # id=99
  474. #
  475. # clear pseudoDcs
  476. #
  477. self.pdc.Clear()
  478. self.pdc.RemoveAll()
  479. self.pdcTmp.Clear()
  480. self.pdcTmp.RemoveAll()
  481. #
  482. # draw background map image to PseudoDC
  483. #
  484. if not self.img:
  485. self.Draw(self.pdc, pdctype='clear')
  486. else:
  487. try:
  488. id = self.imagedict[self.img]['id']
  489. except:
  490. return False
  491. self.Draw(self.pdc, self.img, drawid=id)
  492. #
  493. # render vector map layer
  494. #
  495. digitToolbar = self.parent.digittoolbar
  496. if renderVector and digitToolbar and \
  497. digitToolbar.layerSelectedID != None:
  498. # set region
  499. self.parent.digit.driver.UpdateRegion()
  500. # re-calculate threshold for digitization tool
  501. self.parent.digit.driver.GetThreshold()
  502. # draw map
  503. self.pdcVector.Clear()
  504. self.pdcVector.RemoveAll()
  505. self.parent.digit.driver.DrawMap()
  506. #
  507. # render overlays
  508. #
  509. for img in self.GetOverlay():
  510. # draw any active and defined overlays
  511. if self.imagedict[img]['layer'].IsActive():
  512. id = self.imagedict[img]['id']
  513. self.Draw(self.pdc, img=img, drawid=id,
  514. pdctype=self.overlays[id]['pdcType'], coords=self.overlays[id]['coords'])
  515. for id in self.textdict.keys():
  516. self.Draw(self.pdc, img=self.textdict[id], drawid=id,
  517. pdctype='text', coords=[10, 10, 10, 10])
  518. self.DrawCompRegionExtent()
  519. #
  520. # redraw pdcTmp if needed
  521. #
  522. if len(self.polycoords) > 0:
  523. self.DrawLines(self.pdcTmp)
  524. if self.parent.gismanager.georectifying:
  525. # -> georectifier (redraw GCPs)
  526. if self.parent.grtoolbar:
  527. coordtype = 'gcpcoord'
  528. else:
  529. coordtype = 'mapcoord'
  530. self.parent.gismanager.georectifying.DrawGCP(coordtype)
  531. stop = time.clock()
  532. #
  533. # hide process bar
  534. #
  535. if self.parent.onRenderGauge.GetRange() > 0:
  536. self.parent.onRenderTimer.Stop()
  537. self.parent.onRenderGauge.Hide()
  538. #
  539. # update statusbar
  540. #
  541. ### self.Map.SetRegion()
  542. self.parent.StatusbarUpdate()
  543. Debug.msg (2, "BufferedWindow.UpdateMap(): render=%s, renderVector=%s -> time=%g" % \
  544. (render, renderVector, (stop-start)))
  545. return True
  546. def DrawCompRegionExtent(self):
  547. """Draw computational region extent in the display
  548. Display region is drawn as a blue box inside the computational region,
  549. computational region inside a display region as a red box).
  550. """
  551. if hasattr(self, "regionCoords"):
  552. compReg = self.Map.GetRegion()
  553. dispReg = self.Map.GetCurrentRegion()
  554. reg = None
  555. if self.IsInRegion(dispReg, compReg):
  556. self.polypen = wx.Pen(colour='blue', width=3, style=wx.SOLID)
  557. reg = dispReg
  558. else:
  559. self.polypen = wx.Pen(colour='red', width=3, style=wx.SOLID)
  560. reg = compReg
  561. self.regionCoords = []
  562. self.regionCoords.append((reg['w'], reg['n']))
  563. self.regionCoords.append((reg['e'], reg['n']))
  564. self.regionCoords.append((reg['e'], reg['s']))
  565. self.regionCoords.append((reg['w'], reg['s']))
  566. self.regionCoords.append((reg['w'], reg['n']))
  567. # draw region extent
  568. self.DrawLines(pdc=self.pdcTmp, polycoords=self.regionCoords)
  569. def IsInRegion(self, region, refRegion):
  570. """Test if 'region' is inside of 'refRegion'
  571. @param region input region
  572. @param refRegion reference region (e.g. computational region)
  573. @return True if region is inside of refRegion
  574. @return False
  575. """
  576. if region['s'] >= refRegion['s'] and \
  577. region['n'] <= refRegion['n'] and \
  578. region['w'] >= refRegion['w'] and \
  579. region['e'] <= refRegion['e']:
  580. return True
  581. return False
  582. def EraseMap(self):
  583. """
  584. Erase the map display
  585. """
  586. self.Draw(self.pdc, pdctype='clear')
  587. def DragMap(self, moveto):
  588. """
  589. Drag the entire map image for panning.
  590. """
  591. dc = wx.BufferedDC(wx.ClientDC(self))
  592. dc.SetBackground(wx.Brush("White"))
  593. dc.Clear()
  594. self.dragimg = wx.DragImage(self.buffer)
  595. self.dragimg.BeginDrag((0, 0), self)
  596. self.dragimg.GetImageRect(moveto)
  597. self.dragimg.Move(moveto)
  598. self.dragimg.DoDrawImage(dc, moveto)
  599. self.dragimg.EndDrag()
  600. return True
  601. def DragItem(self, id, event):
  602. """
  603. Drag an overlay decoration item
  604. """
  605. Debug.msg (5, "BufferedWindow.DragItem(): id=%d" % \
  606. id)
  607. x, y = self.lastpos
  608. dx = event.GetX() - x
  609. dy = event.GetY() - y
  610. self.pdc.SetBackground(wx.Brush(self.GetBackgroundColour()))
  611. r = self.pdc.GetIdBounds(id)
  612. if self.dragid > 100: # text dragging
  613. rtop = (r[0],r[1]-r[3],r[2],r[3])
  614. r = r.Union(rtop)
  615. rleft = (r[0]-r[2],r[1],r[2],r[3])
  616. r = r.Union(rleft)
  617. self.pdc.TranslateId(id, dx, dy)
  618. r2 = self.pdc.GetIdBounds(id)
  619. r = r.Union(r2)
  620. r.Inflate(4,4)
  621. self.Update()
  622. self.RefreshRect(r, False)
  623. self.lastpos = (event.GetX(), event.GetY())
  624. def MouseDraw(self, pdc=None, begin=None, end=None):
  625. """
  626. Mouse box or line from 'begin' to 'end'
  627. If not given from self.mouse['begin'] to self.mouse['end'].
  628. """
  629. self.redrawAll = False
  630. if not pdc:
  631. return
  632. if begin is None:
  633. begin = self.mouse['begin']
  634. if end is None:
  635. end = self.mouse['end']
  636. Debug.msg (5, "BufferedWindow.MouseDraw(): use=%s, box=%s, begin=%f,%f, end=%f,%f" % \
  637. (self.mouse['use'], self.mouse['box'],
  638. begin[0], begin[1], end[0], end[1]))
  639. if self.mouse['box'] == "box":
  640. boxid = wx.ID_NEW
  641. mousecoords = [begin[0], begin[1],
  642. end[0], end[1]]
  643. r = pdc.GetIdBounds(boxid)
  644. r.Inflate(4,4)
  645. pdc.ClearId(boxid)
  646. self.RefreshRect(r, False)
  647. pdc.SetId(boxid)
  648. self.Draw(pdc, drawid=boxid, pdctype='box', coords=mousecoords)
  649. elif self.mouse['box'] == "line":
  650. self.lineid = wx.ID_NEW
  651. mousecoords = [begin[0], begin[1], \
  652. end[0], end[1]]
  653. x1=min(begin[0],end[0])
  654. x2=max(begin[0],end[0])
  655. y1=min(begin[1],end[1])
  656. y2=max(begin[1],end[1])
  657. r = wx.Rect(x1,y1,x2-x1,y2-y1)
  658. r.Inflate(4,4)
  659. try:
  660. pdc.ClearId(self.lineid)
  661. except:
  662. pass
  663. self.RefreshRect(r, False)
  664. pdc.SetId(self.lineid)
  665. self.Draw(pdc, drawid=self.lineid, pdctype='line', coords=mousecoords)
  666. def DrawLines(self, pdc=None, polycoords=None):
  667. """Draw polyline in PseudoDC
  668. Set self.pline to wx.NEW_ID + 1
  669. polycoords - list of polyline vertices, geographical coordinates
  670. (if not given, self.polycoords is used)
  671. """
  672. if not pdc:
  673. pdc = self.pdcTmp
  674. if not polycoords:
  675. polycoords = self.polycoords
  676. if len(polycoords) > 0:
  677. self.plineid = wx.ID_NEW + 1
  678. # convert from EN to XY
  679. coords = []
  680. for p in polycoords:
  681. coords.append(self.Cell2Pixel(p))
  682. self.Draw(pdc, drawid=self.plineid, pdctype='polyline', coords=coords)
  683. Debug.msg (4, "BufferedWindow.DrawLines(): coords=%s, id=%s" % \
  684. (coords, self.plineid))
  685. return self.plineid
  686. return -1
  687. def DrawCross(self, pdc, coords, size, rotation=0,
  688. text=None, textAlign='lr', textOffset=(5, 5)):
  689. """Draw cross in PseudoDC
  690. @todo implement rotation
  691. @param pdc PseudoDC
  692. @param coord center coordinates
  693. @param rotation rotate symbol
  694. @param text draw also text (text, font, color, rotation)
  695. @param textAlign alignment (default 'lower-right')
  696. @textOffset offset for text (from center point)
  697. """
  698. Debug.msg(4, "BufferedWindow.DrawCross(): pdc=%s, coords=%s, size=%d" % \
  699. (pdc, coords, size))
  700. coordsCross = ((coords[0] - size, coords[1], coords[0] + size, coords[1]),
  701. (coords[0], coords[1] - size, coords[0], coords[1] + size))
  702. self.lineid = wx.NewId()
  703. for lineCoords in coordsCross:
  704. self.Draw(pdc, drawid=self.lineid, pdctype='line', coords=lineCoords)
  705. if not text:
  706. return self.lineid
  707. if textAlign == 'ul':
  708. coord = [coords[0] - textOffset[0], coords[1] - textOffset[1], 0, 0]
  709. elif textAlign == 'ur':
  710. coord = [coords[0] + textOffset[0], coords[1] - textOffset[1], 0, 0]
  711. elif textAlign == 'lr':
  712. coord = [coords[0] + textOffset[0], coords[1] + textOffset[1], 0, 0]
  713. else:
  714. coord = [coords[0] - textOffset[0], coords[1] + textOffset[1], 0, 0]
  715. self.Draw(pdc, img=text,
  716. pdctype='text', coords=coord)
  717. return self.lineid
  718. def MouseActions(self, event):
  719. """
  720. Mouse motion and button click notifier
  721. """
  722. if self.redrawAll is False:
  723. self.redrawAll = True
  724. wheel = event.GetWheelRotation()
  725. # zoom with mouse wheel
  726. if wheel != 0:
  727. current = event.GetPositionTuple()[:]
  728. Debug.msg (5, "BufferedWindow.MouseAction(): wheel=%d" % wheel)
  729. # zoom 1/2 of the screen, centered to current mouse position (TODO: settings)
  730. begin = (current[0] - self.Map.width / 4,
  731. current[1] - self.Map.height / 4)
  732. end = (current[0] + self.Map.width / 4,
  733. current[1] + self.Map.height / 4)
  734. if wheel > 0:
  735. zoomtype = 1
  736. else:
  737. zoomtype = -1
  738. # zoom
  739. self.Zoom(begin, end, zoomtype)
  740. # redraw map
  741. self.UpdateMap()
  742. self.OnPaint(None)
  743. # update statusbar
  744. self.parent.StatusbarUpdate()
  745. # left mouse button pressed
  746. elif event.LeftDown():
  747. self.OnLeftDown(event)
  748. # left mouse button released
  749. elif event.LeftUp():
  750. self.OnLeftUp(event)
  751. # dragging
  752. elif event.Dragging():
  753. Debug.msg (5, "BufferedWindow.MouseAction(): Dragging")
  754. current = event.GetPositionTuple()[:]
  755. previous = self.mouse['begin']
  756. move = (current[0] - previous[0],
  757. current[1] - previous[1])
  758. # dragging or drawing box with left button
  759. if self.mouse['use'] == 'pan':
  760. self.DragMap(move)
  761. # dragging decoration overlay item
  762. elif (self.mouse['use'] == 'pointer' and not self.parent.digittoolbar) and \
  763. self.dragid != None and \
  764. self.dragid != 99:
  765. self.DragItem(self.dragid, event)
  766. # dragging anything else - rubber band box or line
  767. else:
  768. self.mouse['end'] = event.GetPositionTuple()[:]
  769. if event.LeftIsDown():
  770. # draw box only when left mouse button is pressed
  771. self.MouseDraw(pdc=self.pdcTmp)
  772. # double click
  773. elif event.ButtonDClick():
  774. self.OnButtonDClick(event)
  775. # middle mouse button pressed
  776. elif event.MiddleDown():
  777. self.OnMiddleDown(event)
  778. # right mouse button pressed
  779. elif event.RightDown():
  780. self.OnRightDown(event)
  781. # right mouse button released
  782. elif event.RightUp():
  783. self.OnRightUp(event)
  784. elif event.Moving():
  785. self.OnMouseMoving(event)
  786. event.Skip()
  787. def OnLeftDown(self, event):
  788. """
  789. Left mouse button pressed
  790. """
  791. Debug.msg (5, "BufferedWindow.OnLeftDown(): use=%s" % \
  792. self.mouse["use"])
  793. self.mouse['begin'] = event.GetPositionTuple()[:]
  794. if self.mouse["use"] in ["measure", "profile"]:
  795. # measure or profile
  796. if len(self.polycoords) == 0:
  797. self.mouse['end'] = self.mouse['begin']
  798. self.polycoords.append(self.Pixel2Cell(self.mouse['begin']))
  799. self.ClearLines(pdc=self.pdcTmp)
  800. else:
  801. self.mouse['begin'] = self.mouse['end']
  802. elif self.mouse['use'] == 'zoom':
  803. pass
  804. elif self.mouse["use"] == "pointer" and self.parent.digittoolbar:
  805. # digitization
  806. digitToolbar = self.parent.digittoolbar
  807. digitClass = self.parent.digit
  808. east, north = self.Pixel2Cell(self.mouse['begin'])
  809. try:
  810. map = digitToolbar.layers[digitToolbar.layerSelectedID].name
  811. except:
  812. map = None
  813. dlg = wx.MessageDialog(self, _("No vector map selected for editing."),
  814. _("Error"), wx.OK | wx.ICON_ERROR)
  815. dlg.ShowModal()
  816. dlg.Destroy()
  817. event.Skip()
  818. return
  819. # calculate position of 'update record' dialog
  820. position = self.mouse['begin']
  821. posWindow = self.ClientToScreen((position[0] + self.dialogOffset,
  822. position[1] + self.dialogOffset))
  823. if digitToolbar.action not in ["moveVertex", "addVertex",
  824. "removeVertex", "editLine"]:
  825. # set pen
  826. self.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
  827. self.polypen = wx.Pen(colour='dark green', width=2, style=wx.SOLID)
  828. if digitToolbar.action == "addLine":
  829. if digitToolbar.type in ["point", "centroid"]:
  830. # add new point
  831. if digitToolbar.type == 'point':
  832. point = True
  833. else:
  834. point = False
  835. digitClass.AddPoint(map, point, east, north)
  836. self.UpdateMap(render=False) # redraw map
  837. # add new record into atribute table
  838. if UserSettings.Get(group='vdigit', key="addRecord", subkey='enabled') is True:
  839. # select attributes based on layer and category
  840. cats = {}
  841. cats[UserSettings.Get(group='vdigit', key="layer", subkey='value')] = \
  842. (UserSettings.Get(group='vdigit', key="category", subkey='value'), )
  843. addRecordDlg = dbm.DisplayAttributesDialog(parent=self, map=map,
  844. cats=cats,
  845. pos=posWindow,
  846. action="add")
  847. if addRecordDlg.mapDBInfo and \
  848. addRecordDlg.ShowModal() == wx.ID_OK:
  849. sqlfile = tempfile.NamedTemporaryFile(mode="w")
  850. for sql in addRecordDlg.GetSQLString():
  851. sqlfile.file.write(sql + ";\n")
  852. sqlfile.file.flush()
  853. executeCommand = gcmd.Command(cmd=["db.execute",
  854. "--q",
  855. "input=%s" % sqlfile.name])
  856. elif digitToolbar.type in ["line", "boundary"]:
  857. # add new point to the line
  858. self.polycoords.append(self.Pixel2Cell(event.GetPositionTuple()[:]))
  859. self.DrawLines(pdc=self.pdcTmp)
  860. elif digitToolbar.action == "editLine" and hasattr(self, "moveIds"):
  861. self.polycoords.append(self.Pixel2Cell(self.mouse['begin']))
  862. self.moveIds.append(wx.NewId())
  863. self.DrawLines(pdc=self.pdcTmp)
  864. elif digitToolbar.action == "deleteLine":
  865. pass
  866. elif digitToolbar.action in ["moveLine", "moveVertex", "editLine"] and \
  867. not hasattr(self, "moveBegin"):
  868. # incremental value
  869. self.moveBegin = [0, 0]
  870. # geographic coordinates of initial position (self.mouse['end'])
  871. self.moveCoords = []
  872. # list of ids to modify
  873. self.moveIds = []
  874. if digitToolbar.action in ["moveVertex", "editLine"]:
  875. # set pen
  876. self.pen = self.polypen = wx.Pen(colour=UserSettings.Get(group='vdigit', key="symbolHighlight", subkey='color'),
  877. width=2, style=wx.SHORT_DASH)
  878. self.pdcTmp.SetPen(self.polypen)
  879. elif digitToolbar.action == "splitLine":
  880. pass
  881. elif digitToolbar.action in ["displayAttrs", "displayCats"]:
  882. qdist = digitClass.driver.GetThreshold(type='selectThresh')
  883. coords = (east, north)
  884. if digitToolbar.action == "displayAttrs":
  885. # select attributes based on coordinates (all layers)
  886. if self.parent.dialogs['attributes'] is None:
  887. if digitClass.type == 'vedit':
  888. self.parent.dialogs['attributes'] = dbm.DisplayAttributesDialog(parent=self, map=map,
  889. query=(coords, qdist),
  890. pos=posWindow,
  891. action="update")
  892. else:
  893. if digitClass.driver.SelectLineByPoint(coords,
  894. digitClass.GetSelectType()) is not None:
  895. self.parent.dialogs['attributes'] = dbm.DisplayAttributesDialog(parent=self, map=map,
  896. cats=digitClass.GetLineCats(),
  897. line=digitClass.driver.GetSelected()[0],
  898. action="update")
  899. else:
  900. # update currently open dialog
  901. if digitClass.type == 'vedit':
  902. self.parent.dialogs['attributes'].UpdateDialog(query=(coords, qdist))
  903. else:
  904. # unselect
  905. digitClass.driver.SetSelected([])
  906. # select new feature
  907. if digitClass.driver.SelectLineByPoint(coords,
  908. digitClass.GetSelectType()) is None:
  909. line = None
  910. else:
  911. line = digitClass.driver.GetSelected()[0]
  912. # upgrade dialog
  913. self.parent.dialogs['attributes'].UpdateDialog(cats=digitClass.GetLineCats(),
  914. line=line)
  915. line = self.parent.dialogs['attributes'].GetLine()
  916. if self.parent.dialogs['attributes'].mapDBInfo and line:
  917. # highlight feature & re-draw map
  918. digitClass.driver.SetSelected([line])
  919. if not self.parent.dialogs['attributes'].IsShown():
  920. self.parent.dialogs['attributes'].Show()
  921. else:
  922. digitClass.driver.SetSelected([])
  923. if self.parent.dialogs['attributes'].IsShown():
  924. self.parent.dialogs['attributes'].Hide()
  925. else: # displayCats
  926. if self.parent.dialogs['category'] is None:
  927. # open new dialog
  928. if digitClass.type == 'vedit':
  929. self.parent.dialogs['category'] = VDigitCategoryDialog(parent=self,
  930. map=map,
  931. query=(coords, qdist),
  932. pos=posWindow,
  933. title=_("Update categories"))
  934. else:
  935. if digitClass.driver.SelectLineByPoint(coords,
  936. digitClass.GetSelectType()) is not None:
  937. self.parent.dialogs['category'] = VDigitCategoryDialog(parent=self,
  938. map=map,
  939. cats=digitClass.GetLineCats(),
  940. line=digitClass.driver.GetSelected()[0],
  941. pos=posWindow,
  942. title=_("Update categories"))
  943. else:
  944. # update currently open dialog
  945. if digitClass.type == 'vedit':
  946. self.parent.dialogs['category'].UpdateDialog(query=(coords, qdist))
  947. else:
  948. # unselect
  949. digitClass.driver.SetSelected([])
  950. # select new feature
  951. if digitClass.driver.SelectLineByPoint(coords,
  952. digitClass.GetSelectType()) is None:
  953. line = None
  954. else:
  955. line = digitClass.driver.GetSelected()[0]
  956. # upgrade dialog
  957. self.parent.dialogs['category'].UpdateDialog(cats=digitClass.GetLineCats(),
  958. line=line)
  959. line = self.parent.dialogs['category'].GetLine()
  960. if line:
  961. # highlight feature & re-draw map
  962. digitClass.driver.SetSelected([line])
  963. if not self.parent.dialogs['category'].IsShown():
  964. self.parent.dialogs['category'].Show()
  965. else:
  966. digitClass.driver.SetSelected([])
  967. if self.parent.dialogs['category'].IsShown():
  968. self.parent.dialogs['category'].Hide()
  969. self.UpdateMap(render=False)
  970. elif digitToolbar.action == "copyCats":
  971. if not hasattr(self, "copyCatsList"):
  972. self.copyCatsList = []
  973. else:
  974. self.copyCatsIds = []
  975. self.mouse['box'] = 'box'
  976. elif digitToolbar.action == "copyLine":
  977. self.copyIds = None
  978. self.layerTmp = None
  979. elif digitToolbar.action == "zbulkLine":
  980. if len(self.polycoords) > 1: # start new line
  981. self.polycoords = []
  982. self.ClearLines(pdc=self.pdcTmp)
  983. self.polycoords.append(self.Pixel2Cell(event.GetPositionTuple()[:]))
  984. if len(self.polycoords) == 1:
  985. begin = self.Pixel2Cell(self.polycoords[-1])
  986. end = self.Pixel2Cell(self.mouse['end'])
  987. else:
  988. end = self.Pixel2Cell(self.polycoords[-1])
  989. begin = self.Pixel2Cell(self.mouse['begin'])
  990. self.DrawLines(self.pdcTmp, begin, end)
  991. elif digitToolbar.action == "connectLine":
  992. if len(digitClass.driver.GetSelected()) > 1:
  993. # if two line selected -> reset
  994. digitClass.driver.SetSelected([])
  995. digitClass.driver.SelectLineByPoint(self.Pixel2Cell(self.mouse['begin']),
  996. digitClass.GetSelectType())
  997. else:
  998. # get decoration id
  999. self.lastpos = self.mouse['begin']
  1000. idlist = self.pdc.FindObjects(x=self.lastpos[0], y=self.lastpos[1],
  1001. radius=self.hitradius)
  1002. if idlist != []:
  1003. self.dragid = idlist[0]
  1004. event.Skip()
  1005. def OnLeftUp(self, event):
  1006. """
  1007. Left mouse button released
  1008. """
  1009. Debug.msg (5, "BufferedWindow.OnLeftUp(): use=%s" % \
  1010. self.mouse["use"])
  1011. self.mouse['end'] = event.GetPositionTuple()[:]
  1012. if self.mouse['use'] in ["zoom", "pan"]:
  1013. # set region in zoom or pan
  1014. begin = self.mouse['begin']
  1015. end = self.mouse['end']
  1016. if self.mouse['use'] == 'zoom':
  1017. # set region for click (zero-width box)
  1018. if begin[0] - end[0] == 0 or \
  1019. begin[1] - end[1] == 0:
  1020. # zoom 1/2 of the screen (TODO: settings)
  1021. begin = (end[0] - self.Map.width / 4,
  1022. end[1] - self.Map.height / 4)
  1023. end = (end[0] + self.Map.width / 4,
  1024. end[1] + self.Map.height / 4)
  1025. self.Zoom(begin, end, self.zoomtype)
  1026. # redraw map
  1027. self.UpdateMap(render=True)
  1028. # update statusbar
  1029. self.parent.StatusbarUpdate()
  1030. elif self.mouse["use"] == "query":
  1031. # querying
  1032. self.parent.QueryMap(self.mouse['begin'][0],self.mouse['begin'][1])
  1033. elif self.mouse["use"] == "queryVector":
  1034. # editable mode for vector map layers
  1035. self.parent.QueryVector(self.mouse['begin'][0],self.mouse['begin'][1])
  1036. elif self.mouse["use"] in ["measure", "profile"]:
  1037. # measure or profile
  1038. if self.mouse["use"] == "measure":
  1039. self.parent.MeasureDist(self.mouse['begin'], self.mouse['end'])
  1040. try:
  1041. self.polycoords.append(self.Pixel2Cell(self.mouse['end']))
  1042. self.pdcTmp.ClearId(self.lineid)
  1043. self.DrawLines(pdc=self.pdcTmp)
  1044. except:
  1045. pass
  1046. elif self.mouse["use"] == "pointer" and self.parent.gismanager.georectifying:
  1047. # -> georectifying
  1048. coord = self.Pixel2Cell(self.mouse['end'])
  1049. if self.parent.grtoolbar:
  1050. coordtype = 'gcpcoord'
  1051. else:
  1052. coordtype = 'mapcoord'
  1053. self.parent.gismanager.georectifying.SetGCPData(coordtype, coord, self)
  1054. self.UpdateMap(render=False, renderVector=False)
  1055. elif self.mouse["use"] == "pointer" and self.parent.digittoolbar:
  1056. # digitization tool active
  1057. digitToolbar = self.parent.digittoolbar
  1058. digitClass = self.parent.digit
  1059. pos1 = self.Pixel2Cell(self.mouse['begin'])
  1060. pos2 = self.Pixel2Cell(self.mouse['end'])
  1061. if hasattr(self, "moveBegin"):
  1062. if len(digitClass.driver.GetSelected()) == 0:
  1063. self.moveCoords = pos2
  1064. else:
  1065. dx = pos2[0] - pos1[0]
  1066. dy = pos2[1] - pos1[1]
  1067. self.moveCoords = (self.moveCoords[0] + dx,
  1068. self.moveCoords[1] + dy)
  1069. # eliminate initial mouse moving efect
  1070. self.mouse['begin'] = self.mouse['end']
  1071. if digitToolbar.action in ["deleteLine", "moveLine", "moveVertex",
  1072. "copyCats", "editLine", "flipLine",
  1073. "mergeLine", "snapLine",
  1074. "queryLine", "breakLine", "typeConv"]:
  1075. nselected = 0
  1076. # -> delete line || move line || move vertex
  1077. if digitToolbar.action in ["moveVertex", "editLine"]:
  1078. if len(digitClass.driver.GetSelected()) == 0:
  1079. nselected = digitClass.driver.SelectLineByPoint(pos1, type=VDigit_Lines_Type)
  1080. if digitToolbar.action == "editLine":
  1081. self.UpdateMap(render=False)
  1082. selVertex = digitClass.driver.GetSelectedVertex(pos1)[0]
  1083. ids = digitClass.driver.GetSelected(grassId=False)
  1084. # move this line to tmp layer
  1085. self.polycoords = []
  1086. for id in ids:
  1087. if id % 2: # register only vertices
  1088. self.moveIds.append(id)
  1089. e, n = self.Pixel2Cell(self.pdcVector.GetIdBounds(id)[0:2])
  1090. self.polycoords.append((e, n))
  1091. self.pdcVector.RemoveId(id)
  1092. if selVertex < ids[-1] / 2:
  1093. # choose first or last node of line
  1094. self.moveIds.reverse()
  1095. self.polycoords.reverse()
  1096. self.UpdateMap(render=False, renderVector=False)
  1097. elif digitToolbar.action == "copyCats":
  1098. if not hasattr(self, "copyCatsIds"):
  1099. # collect categories
  1100. nselected = digitClass.driver.SelectLineByPoint(pos1, type=VDigit_Lines_Type)
  1101. if nselected:
  1102. qdist = 10.0 * ((self.Map.region['e'] - self.Map.region['w']) / \
  1103. self.Map.width)
  1104. vWhat = gcmd.Command(['v.what',
  1105. '--q',
  1106. 'map=%s' % digitClass.map,
  1107. 'east_north=%f,%f' % \
  1108. (float(pos1[0]), float(pos1[1])),
  1109. 'distance=%f' % qdist])
  1110. for line in vWhat.ReadStdOutput():
  1111. if "Category:" in line:
  1112. cat = int(line.split(':')[1].strip())
  1113. self.copyCatsList.append(cat)
  1114. else:
  1115. # collect ids
  1116. digitClass.driver.SetSelected([])
  1117. # return number of selected features (by box/point)
  1118. nselected = digitClass.driver.SelectLinesByBox(pos1, pos2,
  1119. digitClass.GetSelectType())
  1120. if nselected == 0:
  1121. if digitClass.driver.SelectLineByPoint(pos1,
  1122. digitClass.GetSelectType()) is not None:
  1123. nselected = 1
  1124. if nselected > 0:
  1125. self.copyCatsIds = digitClass.driver.GetSelected()
  1126. elif digitToolbar.action == "queryLine":
  1127. selected = digitClass.SelectLinesByQuery(pos1, pos2)
  1128. nselected = len(selected)
  1129. if nselected > 0:
  1130. digitClass.driver.SetSelected(selected)
  1131. else:
  1132. # -> moveLine || deleteLine, etc. (select by point/box)
  1133. if digitToolbar.action == 'moveLine' and \
  1134. len(digitClass.driver.GetSelected()) > 0:
  1135. nselected = 0
  1136. else:
  1137. nselected = digitClass.driver.SelectLinesByBox(pos1, pos2,
  1138. digitClass.GetSelectType())
  1139. if nselected == 0:
  1140. if digitClass.driver.SelectLineByPoint(pos1,
  1141. digitClass.GetSelectType()) is not None:
  1142. nselected = 1
  1143. if nselected > 0:
  1144. if digitToolbar.action in ["moveLine", "moveVertex"]:
  1145. # get pseudoDC id of objects which should be redrawn
  1146. if digitToolbar.action == "moveLine":
  1147. # -> move line
  1148. self.moveIds = digitClass.driver.GetSelected(grassId=False)
  1149. elif digitToolbar.action == "moveVertex":
  1150. # -> move vertex
  1151. self.moveIds = digitClass.driver.GetSelectedVertex(pos1)
  1152. if len(self.moveIds) == 0: # no vertex found
  1153. digitClass.driver.SetSelected([])
  1154. #
  1155. # check for duplicates
  1156. #
  1157. if UserSettings.Get(group='vdigit', key='checkForDupl', subkey='enabled') is True:
  1158. dupl = digitClass.driver.GetDuplicates()
  1159. self.UpdateMap(render=False)
  1160. if dupl:
  1161. posWindow = self.ClientToScreen((self.mouse['end'][0] + self.dialogOffset,
  1162. self.mouse['end'][1] + self.dialogOffset))
  1163. dlg = VDigitDuplicatesDialog(parent=self, data=dupl, pos=posWindow)
  1164. if dlg.ShowModal() == wx.ID_OK:
  1165. digitClass.driver.UnSelect(dlg.GetUnSelected())
  1166. # update selected
  1167. self.UpdateMap(render=False)
  1168. if digitToolbar.action != "editLine":
  1169. # -> move line || move vertex
  1170. self.UpdateMap(render=False)
  1171. else: # no vector object found
  1172. self.UpdateMap(render=False, renderVector=False)
  1173. elif digitToolbar.action in ["splitLine", "addVertex", "removeVertex"]:
  1174. pointOnLine = digitClass.driver.SelectLineByPoint(pos1,
  1175. type=VDigit_Lines_Type)
  1176. if pointOnLine:
  1177. self.UpdateMap(render=False) # highlight object
  1178. if digitToolbar.action in ["splitLine", "addVertex"]:
  1179. self.DrawCross(pdc=self.pdcTmp, coords=self.Cell2Pixel(pointOnLine),
  1180. size=5)
  1181. elif digitToolbar.action == "removeVertex":
  1182. # get only id of vertex
  1183. id = digitClass.driver.GetSelectedVertex(pos1)[0]
  1184. x, y = self.pdcVector.GetIdBounds(id)[0:2]
  1185. self.pdcVector.RemoveId(id)
  1186. self.DrawCross(pdc=self.pdcTmp, coords=(x, y),
  1187. size=5)
  1188. elif digitToolbar.action == "copyLine":
  1189. if UserSettings.Get(group='vdigit', key='backgroundMap', subkey='value') == '':
  1190. # no background map -> copy from current vector map layer
  1191. nselected = digitClass.driver.SelectLinesByBox(pos1, pos2,
  1192. digitClass.GetSelectType())
  1193. if nselected > 0:
  1194. # highlight selected features
  1195. self.UpdateMap(render=False)
  1196. else:
  1197. self.UpdateMap(render=False, renderVector=False)
  1198. else:
  1199. # copy features from background map
  1200. self.copyIds = digitClass.SelectLinesFromBackgroundMap(pos1, pos2)
  1201. if len(self.copyIds) > 0:
  1202. color = UserSettings.Get(group='vdigit', key='symbolHighlight', subkey='color')
  1203. colorStr = str(color[0]) + ":" + \
  1204. str(color[1]) + ":" + \
  1205. str(color[2]) + ":"
  1206. dVectTmp = ['d.vect',
  1207. 'map=%s' % UserSettings.Get(group='vdigit', key='backgroundMap', subkey='value'),
  1208. 'cats=%s' % utils.ListOfCatsToRange(self.copyIds),
  1209. '-i',
  1210. 'color=%s' % colorStr,
  1211. 'fcolor=%s' % colorStr,
  1212. 'type=point,line,boundary,centroid',
  1213. 'width=2']
  1214. self.layerTmp = self.Map.AddLayer(type='vector',
  1215. name=globalvar.QUERYLAYER,
  1216. command=dVectTmp)
  1217. self.UpdateMap(render=True, renderVector=False)
  1218. else:
  1219. self.UpdateMap(render=False, renderVector=False)
  1220. elif digitToolbar.action == "zbulkLine" and len(self.polycoords) == 2:
  1221. # select lines to be labeled
  1222. pos1 = self.polycoords[0]
  1223. pos2 = self.polycoords[1]
  1224. nselected = digitClass.driver.SelectLinesByBox(pos1, pos2,
  1225. digitClass.GetSelectType())
  1226. if nselected > 0:
  1227. # highlight selected features
  1228. self.UpdateMap(render=False)
  1229. self.DrawLines(pdc=self.pdcTmp) # redraw temp line
  1230. else:
  1231. self.UpdateMap(render=False, renderVector=False)
  1232. elif digitToolbar.action == "connectLine":
  1233. if len(digitClass.driver.GetSelected()) > 0:
  1234. self.UpdateMap(render=False)
  1235. elif self.dragid != None:
  1236. # end drag of overlay decoration
  1237. if self.overlays.has_key(self.dragid):
  1238. self.overlays[self.dragid]['coords'] = self.pdc.GetIdBounds(self.dragid)
  1239. self.dragid = None
  1240. self.currtxtid = None
  1241. self.Update()
  1242. event.Skip()
  1243. def OnButtonDClick(self, event):
  1244. """
  1245. Mouse button double click
  1246. """
  1247. Debug.msg (5, "BufferedWindow.OnButtonDClick(): use=%s" % \
  1248. self.mouse["use"])
  1249. if self.mouse["use"] == "measure":
  1250. # measure
  1251. self.ClearLines(pdc=self.pdcTmp)
  1252. self.polycoords = []
  1253. self.mouse['use'] = 'pointer'
  1254. self.mouse['box'] = 'point'
  1255. self.mouse['end'] = [0, 0]
  1256. self.Refresh()
  1257. self.SetCursor(self.parent.cursors["default"])
  1258. elif self.mouse["use"] == "profile":
  1259. # profile
  1260. pass
  1261. # self.pdc.ClearId(self.lineid)
  1262. # self.pdc.ClearId(self.plineid)
  1263. # print 'coordinates: ',self.polycoords
  1264. # self.polycoords = []
  1265. # self.mouse['begin'] = self.mouse['end'] = [0, 0]
  1266. # self.Refresh()
  1267. elif self.mouse['use'] == 'pointer' and self.parent.digittoolbar:
  1268. # digitization tool
  1269. pass
  1270. else:
  1271. # select overlay decoration options dialog
  1272. clickposition = event.GetPositionTuple()[:]
  1273. idlist = self.pdc.FindObjects(clickposition[0], clickposition[1], self.hitradius)
  1274. if idlist == []:
  1275. return
  1276. self.dragid = idlist[0]
  1277. # self.ovlcoords[self.dragid] = self.pdc.GetIdBounds(self.dragid)
  1278. if self.dragid > 100:
  1279. self.currtxtid = self.dragid
  1280. self.parent.OnAddText(None)
  1281. elif self.dragid == 0:
  1282. self.parent.OnAddBarscale(None)
  1283. elif self.dragid == 1:
  1284. self.parent.OnAddLegend(None)
  1285. event.Skip()
  1286. def OnRightDown(self, event):
  1287. """
  1288. Right mouse button pressed
  1289. """
  1290. Debug.msg (5, "BufferedWindow.OnRightDown(): use=%s" % \
  1291. self.mouse["use"])
  1292. x,y = event.GetPositionTuple()[:]
  1293. l = self.pdc.FindObjects(x=x, y=y, radius=self.hitradius)
  1294. if not l:
  1295. return
  1296. id = l[0]
  1297. if id != 99:
  1298. if self.pdc.GetIdGreyedOut(id) == True:
  1299. self.pdc.SetIdGreyedOut(id, False)
  1300. else:
  1301. self.pdc.SetIdGreyedOut(id, True)
  1302. r = self.pdc.GetIdBounds(id)
  1303. r.Inflate(4,4)
  1304. self.RefreshRect(r, False)
  1305. digitToolbar = self.parent.digittoolbar
  1306. if digitToolbar:
  1307. digitClass = self.parent.digit
  1308. # digitization tool (confirm action)
  1309. if digitToolbar.action in ["moveLine", "moveVertex"] and \
  1310. hasattr(self, "moveBegin"):
  1311. pTo = self.Pixel2Cell(event.GetPositionTuple())
  1312. pFrom = self.moveCoords
  1313. move = (pTo[0]-pFrom[0], pTo[1]-pFrom[1])
  1314. if digitToolbar.action == "moveLine":
  1315. # move line
  1316. digitClass.MoveSelectedLines(move)
  1317. elif digitToolbar.action == "moveVertex":
  1318. # move vertex
  1319. digitClass.MoveSelectedVertex(pFrom,
  1320. move)
  1321. del self.moveBegin
  1322. del self.moveCoords
  1323. del self.moveIds
  1324. event.Skip()
  1325. def OnRightUp(self, event):
  1326. """
  1327. Right mouse button released
  1328. """
  1329. Debug.msg (5, "BufferedWindow.OnRightUp(): use=%s" % \
  1330. self.mouse["use"])
  1331. digitToolbar = self.parent.digittoolbar
  1332. if digitToolbar:
  1333. digitClass = self.parent.digit
  1334. # digitization tool (confirm action)
  1335. if digitToolbar.action == "addLine" and \
  1336. digitToolbar.type in ["line", "boundary"]:
  1337. # -> add new line / boundary
  1338. try:
  1339. map = digitToolbar.layers[digitToolbar.layerSelectedID].name
  1340. except:
  1341. map = None
  1342. dlg = wx.MessageDialog(self, _("No vector map selected for editing."),
  1343. _("Error"), wx.OK | wx.ICON_ERROR)
  1344. dlg.ShowModal()
  1345. dlg.Destroy()
  1346. if map:
  1347. # mapcoords = []
  1348. # xy -> EN
  1349. # for coord in self.polycoords:
  1350. # mapcoords.append(self.Pixel2Cell(coord))
  1351. if digitToolbar.type == 'line':
  1352. line = True
  1353. else:
  1354. line = False
  1355. digitClass.AddLine(map, line, self.polycoords)
  1356. position = self.Cell2Pixel(self.polycoords[-1])
  1357. self.polycoords = []
  1358. self.UpdateMap(render=False)
  1359. # add new record into atribute table
  1360. if UserSettings.Get(group='vdigit', key="addRecord", subkey='enabled') is True:
  1361. posWindow = self.ClientToScreen((position[0] + self.dialogOffset,
  1362. position[1] + self.dialogOffset))
  1363. # select attributes based on layer and category
  1364. cats = {}
  1365. cats[UserSettings.Get(group='vdigit', key="layer", subkey='value')] = \
  1366. (UserSettings.Get(group='vdigit', key="category", subkey='value'), )
  1367. addRecordDlg = dbm.DisplayAttributesDialog(parent=self, map=map,
  1368. cats=cats,
  1369. pos=posWindow,
  1370. action="add")
  1371. if addRecordDlg.mapDBInfo and \
  1372. addRecordDlg.ShowModal() == wx.ID_OK:
  1373. sqlfile = tempfile.NamedTemporaryFile(mode="w")
  1374. for sql in addRecordDlg.GetSQLString():
  1375. sqlfile.file.write(sql + ";\n")
  1376. sqlfile.file.flush()
  1377. executeCommand = gcmd.Command(cmd=["db.execute",
  1378. "--q",
  1379. "input=%s" % sqlfile.name])
  1380. elif digitToolbar.action == "deleteLine":
  1381. # -> delete selected vector features
  1382. digitClass.DeleteSelectedLines()
  1383. elif digitToolbar.action == "splitLine":
  1384. # split line
  1385. digitClass.SplitLine(self.Pixel2Cell(self.mouse['begin']))
  1386. elif digitToolbar.action == "addVertex":
  1387. # add vertex
  1388. digitClass.AddVertex(self.Pixel2Cell(self.mouse['begin']))
  1389. elif digitToolbar.action == "removeVertex":
  1390. # remove vertex
  1391. digitClass.RemoveVertex(self.Pixel2Cell(self.mouse['begin']))
  1392. elif digitToolbar.action == "copyCats":
  1393. try:
  1394. digitClass.CopyCats(self.copyCatsList,
  1395. self.copyCatsIds)
  1396. del self.copyCatsList
  1397. del self.copyCatsIds
  1398. except:
  1399. pass
  1400. elif digitToolbar.action == "editLine" and hasattr(self, "moveBegin"):
  1401. line = digitClass.driver.GetSelected()
  1402. digitClass.EditLine(line, self.polycoords)
  1403. del self.moveBegin
  1404. del self.moveCoords
  1405. del self.moveIds
  1406. elif digitToolbar.action == "flipLine":
  1407. digitClass.FlipLine()
  1408. elif digitToolbar.action == "mergeLine":
  1409. digitClass.MergeLine()
  1410. elif digitToolbar.action == "breakLine":
  1411. digitClass.BreakLine()
  1412. elif digitToolbar.action == "snapLine":
  1413. digitClass.SnapLine()
  1414. elif digitToolbar.action == "connectLine":
  1415. if len(digitClass.driver.GetSelected()) == 2:
  1416. digitClass.ConnectLine()
  1417. elif digitToolbar.action == "copyLine":
  1418. digitClass.CopyLine(self.copyIds)
  1419. del self.copyIds
  1420. if self.layerTmp:
  1421. self.Map.DeleteLayer(self.layerTmp)
  1422. self.UpdateMap(render=True, renderVector=False)
  1423. del self.layerTmp
  1424. elif digitToolbar.action == "zbulkLine" and len(self.polycoords) == 2:
  1425. pos1 = self.polycoords[0]
  1426. pos2 = self.polycoords[1]
  1427. selected = digitClass.driver.GetSelected()
  1428. dlg = VDigitZBulkDialog(parent=self, title=_("Z bulk-labeling dialog"),
  1429. nselected=len(selected))
  1430. if dlg.ShowModal() == wx.ID_OK:
  1431. digitClass.ZBulkLine(pos1, pos2, dlg.value.GetValue(), dlg.step.GetValue())
  1432. self.UpdateMap(render=False, renderVector=True)
  1433. elif digitToolbar.action == "typeConv":
  1434. # -> feature type conversion
  1435. # - point <-> centroid
  1436. # - line <-> boundary
  1437. digitClass.TypeConvForSelectedLines()
  1438. if digitToolbar.action != "addLine":
  1439. # unselect and re-render
  1440. digitClass.driver.SetSelected([])
  1441. self.polycoords = []
  1442. self.UpdateMap(render=False)
  1443. event.Skip()
  1444. def OnMiddleDown(self, event):
  1445. """Middle mouse button pressed"""
  1446. digitToolbar = self.parent.digittoolbar
  1447. # digitization tool
  1448. if self.mouse["use"] == "pointer" and digitToolbar:
  1449. digitClass = self.parent.digit
  1450. if (digitToolbar.action == "addLine" and digitToolbar.type in ["line", "boundary"]) or \
  1451. digitToolbar.action == "editLine":
  1452. # add line or boundary -> remove last point from the line
  1453. try:
  1454. removed = self.polycoords.pop()
  1455. Debug.msg(4, "BufferedWindow.OnMiddleDown(): polycoords_poped=%s" % \
  1456. [removed,])
  1457. self.mouse['begin'] = self.Cell2Pixel(self.polycoords[-1])
  1458. except:
  1459. pass
  1460. if digitToolbar.action == "editLine":
  1461. # remove last vertex & line
  1462. self.moveIds.pop()
  1463. self.UpdateMap(render=False, renderVector=False)
  1464. elif digitToolbar.action in ["deleteLine", "moveLine", "splitLine",
  1465. "addVertex", "removeVertex", "moveVertex",
  1466. "copyCats", "flipLine", "mergeLine",
  1467. "snapLine", "connectLine", "copyLine",
  1468. "queryLine", "breakLine", "typeConv"]:
  1469. # varios tools -> unselected selected features
  1470. digitClass.driver.SetSelected([])
  1471. if digitToolbar.action in ["moveLine", "moveVertex", "editLine"] and \
  1472. hasattr(self, "moveBegin"):
  1473. del self.moveBegin
  1474. del self.moveCoords
  1475. del self.moveIds
  1476. elif digitToolbar.action == "copyCats":
  1477. try:
  1478. del self.copyCatsList
  1479. del self.copyCatsIds
  1480. except:
  1481. pass
  1482. elif digitToolbar.action == "copyLine":
  1483. del self.copyIds
  1484. if self.layerTmp:
  1485. self.Map.DeleteLayer(self.layerTmp)
  1486. self.UpdateMap(render=True, renderVector=False)
  1487. del self.layerTmp
  1488. self.polycoords = []
  1489. self.UpdateMap(render=False) # render vector
  1490. elif digitToolbar.action == "zbulkLine":
  1491. # reset polyline
  1492. self.polycoords = []
  1493. digitClass.driver.SetSelected([])
  1494. self.UpdateMap(render=False)
  1495. def OnMouseMoving(self, event):
  1496. """Motion event and no mouse buttons were pressed"""
  1497. digitToolbar = self.parent.digittoolbar
  1498. if self.mouse["use"] == "pointer" and digitToolbar:
  1499. digitClass = self.parent.digit
  1500. self.mouse['end'] = event.GetPositionTuple()[:]
  1501. Debug.msg (5, "BufferedWindow.OnMouseMoving(): coords=%f,%f" % \
  1502. (self.mouse['end'][0], self.mouse['end'][1]))
  1503. if digitToolbar.action == "addLine" and digitToolbar.type in ["line", "boundary"]:
  1504. if len(self.polycoords) > 0:
  1505. self.MouseDraw(pdc=self.pdcTmp, begin=self.Cell2Pixel(self.polycoords[-1]))
  1506. elif digitToolbar.action in ["moveLine", "moveVertex", "editLine"] \
  1507. and hasattr(self, "moveBegin"):
  1508. dx = self.mouse['end'][0] - self.mouse['begin'][0]
  1509. dy = self.mouse['end'][1] - self.mouse['begin'][1]
  1510. self.moveBegin[0] += dx
  1511. self.moveBegin[1] += dy
  1512. if len(self.moveIds) > 0:
  1513. # draw lines on new position
  1514. if digitToolbar.action == "moveLine":
  1515. # move line
  1516. for id in self.moveIds:
  1517. self.pdcVector.TranslateId(id, dx, dy)
  1518. elif digitToolbar.action in ["moveVertex", "editLine"]:
  1519. # move vertex ->
  1520. # (vertex, left vertex, left line,
  1521. # right vertex, right line)
  1522. # do not draw static lines
  1523. if digitToolbar.action == "moveVertex":
  1524. self.polycoords = []
  1525. self.pdcVector.TranslateId(self.moveIds[0], dx, dy)
  1526. if self.moveIds[1] > 0: # previous vertex
  1527. x, y = self.Pixel2Cell(self.pdcVector.GetIdBounds(self.moveIds[1])[0:2])
  1528. self.pdcVector.RemoveId(self.moveIds[1]+1)
  1529. self.polycoords.append((x, y))
  1530. x, y = self.Pixel2Cell(self.pdcVector.GetIdBounds(self.moveIds[0])[0:2])
  1531. self.polycoords.append((x, y))
  1532. if self.moveIds[2] > 0: # next vertex
  1533. x, y = self.Pixel2Cell(self.pdcVector.GetIdBounds(self.moveIds[2])[0:2])
  1534. self.pdcVector.RemoveId(self.moveIds[2]-1)
  1535. self.polycoords.append((x, y))
  1536. self.ClearLines(pdc=self.pdcTmp)
  1537. self.DrawLines(pdc=self.pdcTmp)
  1538. else: # edit line
  1539. try:
  1540. if self.moveIds[-1] > 0: # previous vertex
  1541. self.MouseDraw(pdc=self.pdcTmp,
  1542. begin=self.Cell2Pixel(self.polycoords[-1]))
  1543. except: # no line
  1544. self.moveIds = []
  1545. self.polycoords = []
  1546. self.Refresh() # TODO: use RefreshRect()
  1547. self.mouse['begin'] = self.mouse['end']
  1548. elif digitToolbar.action == "zbulkLine":
  1549. if len(self.polycoords) == 1:
  1550. # draw mouse moving
  1551. self.MouseDraw(self.pdcTmp)
  1552. event.Skip()
  1553. def ClearLines(self, pdc=None):
  1554. """
  1555. Clears temporary drawn lines from PseudoDC
  1556. """
  1557. if not pdc:
  1558. return
  1559. exit = True
  1560. try:
  1561. pdc.ClearId(self.lineid)
  1562. pdc.RemoveId(self.lineid)
  1563. except:
  1564. exit = False
  1565. try:
  1566. pdc.ClearId(self.plineid)
  1567. pdc.RemoveId(self.plineid)
  1568. except:
  1569. exit = False
  1570. Debug.msg(4, "BufferedWindow.ClearLines(): lineid=%s, plineid=%s" %
  1571. (self.lineid, self.plineid))
  1572. self.Refresh()
  1573. return exit
  1574. def Pixel2Cell(self, (x, y)):
  1575. """
  1576. Convert image coordinates to real word coordinates
  1577. Input : int x, int y
  1578. Output: float x, float y
  1579. """
  1580. try:
  1581. x = int(x)
  1582. y = int(y)
  1583. except:
  1584. return None
  1585. if self.Map.region["ewres"] > self.Map.region["nsres"]:
  1586. res = self.Map.region["ewres"]
  1587. else:
  1588. res = self.Map.region["nsres"]
  1589. w = self.Map.region["center_easting"] - (self.Map.width / 2) * res
  1590. n = self.Map.region["center_northing"] + (self.Map.height / 2) * res
  1591. east = w + x * res
  1592. north = n - y * res
  1593. # extent does not correspond with whole map canvas area...
  1594. # east = self.Map.region['w'] + x * self.Map.region["ewres"]
  1595. # north = self.Map.region['n'] - y * self.Map.region["nsres"]
  1596. return (east, north)
  1597. def Cell2Pixel(self, (east, north)):
  1598. """
  1599. Convert real word coordinates to image coordinates
  1600. """
  1601. try:
  1602. east = float(east)
  1603. north = float(north)
  1604. except:
  1605. return None
  1606. if self.Map.region["ewres"] > self.Map.region["nsres"]:
  1607. res = self.Map.region["ewres"]
  1608. else:
  1609. res = self.Map.region["nsres"]
  1610. w = self.Map.region["center_easting"] - (self.Map.width / 2) * res
  1611. n = self.Map.region["center_northing"] + (self.Map.height / 2) * res
  1612. # x = int((east - w) / res)
  1613. # y = int((n - north) / res)
  1614. x = (east - w) / res
  1615. y = (n - north) / res
  1616. return (x, y)
  1617. def Zoom(self, begin, end, zoomtype):
  1618. """
  1619. Calculates new region while (un)zoom/pan-ing
  1620. """
  1621. x1, y1 = begin
  1622. x2, y2 = end
  1623. newreg = {}
  1624. # threshold - too small squares do not make sense
  1625. # can only zoom to windows of > 5x5 screen pixels
  1626. if abs(x2-x1) > 5 and abs(y2-y1) > 5 and zoomtype != 0:
  1627. if x1 > x2:
  1628. x1, x2 = x2, x1
  1629. if y1 > y2:
  1630. y1, y2 = y2, y1
  1631. # zoom in
  1632. if zoomtype > 0:
  1633. newreg['w'], newreg['n'] = self.Pixel2Cell((x1, y1))
  1634. newreg['e'], newreg['s'] = self.Pixel2Cell((x2, y2))
  1635. # zoom out
  1636. elif zoomtype < 0:
  1637. newreg['w'], newreg['n'] = self.Pixel2Cell((-x1 * 2, -y1 * 2))
  1638. newreg['e'], newreg['s'] = self.Pixel2Cell((self.Map.width + 2 * \
  1639. (self.Map.width - x2),
  1640. self.Map.height + 2 * \
  1641. (self.Map.height - y2)))
  1642. # pan
  1643. elif zoomtype == 0:
  1644. dx = x1 - x2
  1645. dy = y1 - y2
  1646. newreg['w'], newreg['n'] = self.Pixel2Cell((dx, dy))
  1647. newreg['e'], newreg['s'] = self.Pixel2Cell((self.Map.width + dx,
  1648. self.Map.height + dy))
  1649. # if new region has been calculated, set the values
  1650. if newreg != {}:
  1651. # calculate new center point and display resolution
  1652. self.Map.region['center_easting'] = newreg['w'] + \
  1653. (newreg['e'] - newreg['w']) / 2
  1654. self.Map.region['center_northing'] = newreg['s'] + \
  1655. (newreg['n'] - newreg['s']) / 2
  1656. self.Map.region["ewres"] = (newreg['e'] - newreg['w']) / self.Map.width
  1657. self.Map.region["nsres"] = (newreg['n'] - newreg['s']) / self.Map.height
  1658. self.Map.AlignExtentFromDisplay()
  1659. self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
  1660. self.Map.region['e'], self.Map.region['w'])
  1661. if self.redrawAll is False:
  1662. self.redrawAll = True
  1663. def ZoomBack(self):
  1664. """
  1665. Zoom to previous extents in zoomhistory list
  1666. """
  1667. zoom = []
  1668. if len(self.zoomhistory) > 1:
  1669. self.zoomhistory.pop()
  1670. zoom = self.zoomhistory[len(self.zoomhistory)-1]
  1671. # (n, s, e, w)
  1672. if zoom:
  1673. # zoom to selected region
  1674. self.Map.region['center_easting'] = zoom[3] + \
  1675. (zoom[2] - zoom[3]) / 2
  1676. self.Map.region['center_northing'] = zoom[1] + \
  1677. (zoom[0] - zoom[1]) / 2
  1678. self.Map.region["ewres"] = (zoom[2] - zoom[3]) / self.Map.width
  1679. self.Map.region["nsres"] = (zoom[0] - zoom[1]) / self.Map.height
  1680. self.Map.AlignExtentFromDisplay()
  1681. # update map
  1682. self.UpdateMap()
  1683. # update statusbar
  1684. self.parent.StatusbarUpdate()
  1685. def ZoomHistory(self, n, s, e, w):
  1686. """
  1687. Manages a list of last 10 zoom extents
  1688. Return removed history item if exists
  1689. """
  1690. removed = None
  1691. self.zoomhistory.append((n,s,e,w))
  1692. if len(self.zoomhistory) > 10:
  1693. removed = self.zoomhistory.pop(0)
  1694. if removed:
  1695. Debug.msg(4, "BufferedWindow.ZoomHistory(): hist=%s, removed=%s" %
  1696. (self.zoomhistory, removed))
  1697. else:
  1698. Debug.msg(4, "BufferedWindow.ZoomHistory(): hist=%s" %
  1699. (self.zoomhistory))
  1700. return removed
  1701. def ZoomToMap(self, event):
  1702. """
  1703. Set display extents to match selected raster
  1704. or vector map.
  1705. """
  1706. zoomreg = {}
  1707. # find selected map
  1708. if not self.tree or not self.tree.GetSelection():
  1709. return
  1710. item = self.tree.GetSelection()
  1711. try:
  1712. layer = self.tree.GetPyData(item)[0]['maplayer']
  1713. except:
  1714. layer = None
  1715. if layer is None:
  1716. return
  1717. Debug.msg (3, "BufferedWindow.ZoomToMap(): layer=%s, type=%s" % \
  1718. (layer.name, layer.type))
  1719. # selected layer must be a valid map
  1720. if layer.type in ('raster', 'rgb', 'his', 'shaded', 'arrow'):
  1721. self.Map.region = self.Map.GetRegion(rast="%s" % layer.name)
  1722. elif layer.type in ('vector', 'thememap', 'themechart'):
  1723. if self.parent.digit and layer.name == self.parent.digit.map and \
  1724. self.parent.digit.type == 'vdigit':
  1725. w, s, b, e, n, t = self.parent.digit.driver.GetMapBoundingBox()
  1726. self.Map.region = self.Map.GetRegion(n=n, s=s, w=w, e=e)
  1727. else:
  1728. self.Map.region = self.Map.GetRegion(vect="%s" % layer.name)
  1729. else:
  1730. return
  1731. ### self.Map.SetRegion()
  1732. ### self.Map.AlignExtentFromDisplay()
  1733. self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
  1734. self.Map.region['e'], self.Map.region['w'])
  1735. self.UpdateMap()
  1736. self.parent.StatusbarUpdate()
  1737. def ZoomToWind(self, event):
  1738. """
  1739. Set display geometry to match computational
  1740. region settings (set with g.region)
  1741. """
  1742. self.Map.region = self.Map.GetRegion()
  1743. ### self.Map.SetRegion(windres=True)
  1744. self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
  1745. self.Map.region['e'], self.Map.region['w'])
  1746. self.UpdateMap()
  1747. self.parent.StatusbarUpdate()
  1748. def ZoomToDefault(self, event):
  1749. """Set display geometry to match default
  1750. region settings"""
  1751. self.Map.region = self.Map.GetRegion(default=True)
  1752. self.Map.AdjustRegion() # aling region extent to the display
  1753. self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
  1754. self.Map.region['e'], self.Map.region['w'])
  1755. self.UpdateMap()
  1756. self.parent.StatusbarUpdate()
  1757. def DisplayToWind(self, event):
  1758. """
  1759. Set computational region (WIND file) to
  1760. match display extents
  1761. """
  1762. tmpreg = os.getenv("GRASS_REGION")
  1763. os.unsetenv("GRASS_REGION")
  1764. # We ONLY want to set extents here. Don't mess with resolution. Leave that
  1765. # for user to set explicitly with g.region
  1766. new = self.Map.AlignResolution()
  1767. cmdRegion = ["g.region", "--o",
  1768. "n=%f" % new['n'],
  1769. "s=%f" % new['s'],
  1770. "e=%f" % new['e'],
  1771. "w=%f" % new['w'],
  1772. "rows=%f" % new['rows'],
  1773. "cols=%f" % new['cols']]
  1774. p = gcmd.Command(cmdRegion)
  1775. if tmpreg:
  1776. os.environ["GRASS_REGION"] = tmpreg
  1777. def ZoomToSaved(self, event):
  1778. """
  1779. Set display geometry to match extents in
  1780. saved region file
  1781. """
  1782. zoomreg = {}
  1783. dlg = gdialogs.SavedRegion(self, wx.ID_ANY, _("Zoom to saved region extents"),
  1784. pos=wx.DefaultPosition, size=wx.DefaultSize,
  1785. style=wx.DEFAULT_DIALOG_STYLE,
  1786. loadsave='load')
  1787. if dlg.ShowModal() == wx.ID_CANCEL:
  1788. dlg.Destroy()
  1789. return
  1790. wind = dlg.wind
  1791. p = gcmd.Command (["g.region",
  1792. "-ugp", "region=%s" % wind])
  1793. if p.returncode == 0:
  1794. output = p.ReadStdOutput()
  1795. for line in output:
  1796. line = line.strip()
  1797. if '=' in line: key,val = line.split('=')
  1798. zoomreg[key] = float(val)
  1799. self.Map.region['n'] = zoomreg['n']
  1800. self.Map.region['s'] = zoomreg['s']
  1801. self.Map.region['e'] = zoomreg['e']
  1802. self.Map.region['w'] = zoomreg['w']
  1803. self.ZoomHistory(self.Map.region['n'],self.Map.region['s'],self.Map.region['e'],self.Map.region['w'])
  1804. self.UpdateMap()
  1805. dlg.Destroy()
  1806. def SaveDisplayRegion(self, event):
  1807. """
  1808. Save display extents to named region file.
  1809. """
  1810. dlg = gdialogs.SavedRegion(self, wx.ID_ANY, "Save display extents to region file",
  1811. pos=wx.DefaultPosition, size=wx.DefaultSize,
  1812. style=wx.DEFAULT_DIALOG_STYLE,
  1813. loadsave='save')
  1814. if dlg.ShowModal() == wx.ID_CANCEL:
  1815. dlg.Destroy()
  1816. return
  1817. wind = dlg.wind
  1818. # test to see if it already exists and ask permission to overwrite
  1819. windpath = os.path.join(self.Map.env["GISDBASE"], self.Map.env["LOCATION_NAME"],
  1820. self.Map.env["MAPSET"],"windows",wind)
  1821. if windpath and not os.path.exists(windpath):
  1822. self.SaveRegion(wind)
  1823. elif windpath and os.path.exists(windpath):
  1824. overwrite = wx.MessageBox(_("Region file <%s> already exists. "
  1825. "Do you want to overwrite it?") % (wind),
  1826. _("Warning"), wx.YES_NO)
  1827. if (overwrite == wx.YES):
  1828. self.SaveRegion(wind)
  1829. else:
  1830. pass
  1831. dlg.Destroy()
  1832. def SaveRegion(self, wind):
  1833. """Save region settings"""
  1834. new = self.Map.AlignResolution()
  1835. cmdRegion = ["g.region",
  1836. "-u",
  1837. "n=%f" % new['n'],
  1838. "s=%f" % new['s'],
  1839. "e=%f" % new['e'],
  1840. "w=%f" % new['w'],
  1841. "rows=%d" % new['rows'],
  1842. "cols=%d" % new['cols'],
  1843. "save=%s" % wind,
  1844. "--o"]
  1845. tmpreg = os.getenv("GRASS_REGION")
  1846. os.unsetenv("GRASS_REGION")
  1847. p = gcmd.Command(cmdRegion)
  1848. if tmpreg:
  1849. os.environ["GRASS_REGION"] = tmpreg
  1850. def Distance(self, beginpt, endpt, screen=True):
  1851. """Calculete distance
  1852. LL-locations not supported
  1853. @todo Use m.distance
  1854. @param beginpt first point
  1855. @param endpt second point
  1856. @param screen True for screen coordinates otherwise EN
  1857. """
  1858. x1, y1 = beginpt
  1859. x2, y2 = endpt
  1860. if screen:
  1861. dEast = (x2 - x1) * self.Map.region["ewres"]
  1862. dNorth = (y2 - y1) * self.Map.region["nsres"]
  1863. else:
  1864. dEast = (x2 - x1)
  1865. dNorth = (y2 - y1)
  1866. return (math.sqrt(math.pow((dEast),2) + math.pow((dNorth),2)), (dEast, dNorth))
  1867. class MapFrame(wx.Frame):
  1868. """
  1869. Main frame for map display window. Drawing takes place in child double buffered
  1870. drawing window.
  1871. """
  1872. def __init__(self, parent=None, id=wx.ID_ANY, title=_("GRASS GIS - Map display"),
  1873. pos=wx.DefaultPosition, size=wx.DefaultSize,
  1874. style=wx.DEFAULT_FRAME_STYLE, toolbars=["map"],
  1875. tree=None, notebook=None, gismgr=None, page=None,
  1876. Map=None, auimgr=None):
  1877. """
  1878. Main map display window with toolbars, statusbar and
  1879. DrawWindow
  1880. @param toolbars array of activated toolbars, e.g. ['map', 'digit']
  1881. @param tree reference to layer tree
  1882. @param notebook control book ID in Layer Manager
  1883. @param gismgr Layer Manager panel
  1884. @param page notebook page with layer tree
  1885. @param Map instance of render.Map
  1886. """
  1887. wx.Frame.__init__(self, parent, id, title, pos, size, style)
  1888. self.gismanager = gismgr # GIS Manager object
  1889. self.Map = Map # instance of render.Map
  1890. self.tree = tree # GIS Manager layer tree object
  1891. self.page = page # Notebook page holding the layer tree
  1892. self.layerbook = notebook # GIS Manager layer tree notebook
  1893. self.parent = parent
  1894. #
  1895. # available cursors
  1896. #
  1897. self.cursors = {
  1898. # default: cross
  1899. # "default" : wx.StockCursor(wx.CURSOR_DEFAULT),
  1900. "default" : wx.StockCursor(wx.CURSOR_ARROW),
  1901. "cross" : wx.StockCursor(wx.CURSOR_CROSS),
  1902. "hand" : wx.StockCursor(wx.CURSOR_HAND),
  1903. "pencil" : wx.StockCursor(wx.CURSOR_PENCIL),
  1904. "sizenwse": wx.StockCursor(wx.CURSOR_SIZENWSE)
  1905. }
  1906. #
  1907. # set the size & system icon
  1908. #
  1909. self.SetClientSize(size)
  1910. self.iconsize = (16, 16)
  1911. self.SetIcon(wx.Icon(os.path.join(globalvar.ETCDIR, 'grass_map.ico'), wx.BITMAP_TYPE_ICO))
  1912. #
  1913. # Fancy gui
  1914. #
  1915. # self._mgr = auimgr
  1916. self._mgr = wx.aui.AuiManager(self)
  1917. #
  1918. # Add toolbars
  1919. #
  1920. self.maptoolbar = None
  1921. self.digittoolbar = None
  1922. self.grtoolbar = None
  1923. for toolb in toolbars:
  1924. self.AddToolbar(toolb)
  1925. #
  1926. # Add statusbar
  1927. #
  1928. self.statusbar = self.CreateStatusBar(number=3, style=0)
  1929. self.statusbar.SetStatusWidths([-5, -2, -1])
  1930. self.toggleStatus = wx.Choice(self.statusbar, wx.ID_ANY,
  1931. choices = [_("Coordinates"),
  1932. _("Extent"),
  1933. _("Comp. region"),
  1934. _("Show comp. extent"),
  1935. _("Display mode"),
  1936. _("Display geometry"),
  1937. _("Map scale")])
  1938. self.toggleStatus.SetSelection(0)
  1939. self.statusbar.Bind(wx.EVT_CHOICE, self.OnToggleStatus, self.toggleStatus)
  1940. # auto-rendering checkbox
  1941. self.autoRender = wx.CheckBox(parent=self.statusbar, id=wx.ID_ANY,
  1942. label=_("Render"))
  1943. self.statusbar.Bind(wx.EVT_CHECKBOX, self.OnToggleRender, self.autoRender)
  1944. self.autoRender.SetValue(UserSettings.Get(group='display', key='autoRendering', subkey='enabled'))
  1945. self.autoRender.SetToolTip(wx.ToolTip (_("Enable/disable auto-rendering")))
  1946. # show region
  1947. self.showRegion = wx.CheckBox(parent=self.statusbar, id=wx.ID_ANY,
  1948. label=_("Show computational extent"))
  1949. self.statusbar.Bind(wx.EVT_CHECKBOX, self.OnToggleShowRegion, self.showRegion)
  1950. self.showRegion.SetValue(False)
  1951. self.showRegion.Hide()
  1952. self.showRegion.SetToolTip(wx.ToolTip (_("Show/hide computational "
  1953. "region extent (set with g.region). "
  1954. "Display region drawn as a blue box inside the "
  1955. "computational region, "
  1956. "computational region inside a display region "
  1957. "as a red box).")))
  1958. # set resolution
  1959. self.compResolution = wx.CheckBox(parent=self.statusbar, id=wx.ID_ANY,
  1960. label=_("Constrain display resolution to computational settings"))
  1961. self.statusbar.Bind(wx.EVT_CHECKBOX, self.OnToggleResolution, self.compResolution)
  1962. self.compResolution.SetValue(UserSettings.Get(group='display', key='compResolution', subkey='enabled'))
  1963. self.compResolution.Hide()
  1964. self.compResolution.SetToolTip(wx.ToolTip (_("Constrain display resolution "
  1965. "to computational region settings. "
  1966. "Default value for new map displays can "
  1967. "be set up in 'User GUI settings' dialog.")))
  1968. # map scale
  1969. self.mapScale = wx.TextCtrl(parent=self.statusbar, id=wx.ID_ANY,
  1970. value="", style=wx.TE_PROCESS_ENTER,
  1971. size=(150, -1))
  1972. self.mapScale.Hide()
  1973. self.statusbar.Bind(wx.EVT_TEXT_ENTER, self.OnChangeMapScale, self.mapScale)
  1974. # on-render gauge
  1975. self.onRenderGauge = wx.Gauge(parent=self.statusbar, id=wx.ID_ANY,
  1976. range=0, style=wx.GA_HORIZONTAL)
  1977. self.onRenderGauge.Hide()
  1978. self.Bind(wx.EVT_TIMER, self.TimerOnRender)
  1979. self.onRenderTimer = wx.Timer(self)
  1980. self.StatusbarReposition() # reposition statusbar
  1981. #
  1982. # Init map display (buffered DC & set default cursor)
  1983. #
  1984. self.MapWindow = BufferedWindow(self, id=wx.ID_ANY, Map=self.Map, tree=self.tree, gismgr=self.gismanager)
  1985. self.MapWindow.Bind(wx.EVT_MOTION, self.OnMotion)
  1986. self.MapWindow.SetCursor(self.cursors["default"])
  1987. #
  1988. # initialize region values
  1989. #
  1990. self.__InitDisplay()
  1991. #
  1992. # Bind various events
  1993. #
  1994. self.Bind(wx.EVT_ACTIVATE, self.OnFocus)
  1995. self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
  1996. #
  1997. # Update fancy gui style
  1998. #
  1999. self._mgr.AddPane(self.MapWindow, wx.aui.AuiPaneInfo().CentrePane().
  2000. Dockable(False).BestSize((-1,-1)).
  2001. CloseButton(False).DestroyOnClose(True).
  2002. Layer(0))
  2003. self._mgr.Update()
  2004. #
  2005. # Init print module and classes
  2006. #
  2007. self.printopt = disp_print.PrintOptions(self, self.MapWindow)
  2008. #
  2009. # Current location information
  2010. #
  2011. self.projinfo = self.Map.ProjInfo()
  2012. #
  2013. # Initialization of digitization tool
  2014. #
  2015. self.digit = None
  2016. #
  2017. # Init zoom history
  2018. #
  2019. self.MapWindow.ZoomHistory(self.Map.region['n'],
  2020. self.Map.region['s'],
  2021. self.Map.region['e'],
  2022. self.Map.region['w'])
  2023. #
  2024. # Re-use dialogs
  2025. #
  2026. self.dialogs = {}
  2027. self.dialogs['attributes'] = None
  2028. self.dialogs['category'] = None
  2029. self.dialogs['barscale'] = None
  2030. self.dialogs['legend'] = None
  2031. self.decorationDialog = None # decoration/overlays
  2032. def AddToolbar(self, name):
  2033. """
  2034. Add defined toolbar to the window
  2035. Currently known toolbars are:
  2036. - map basic map toolbar
  2037. - digit vector digitizer
  2038. - georect georectifier
  2039. """
  2040. if name == "map":
  2041. self.maptoolbar = toolbars.MapToolbar(self, self.Map)
  2042. self._mgr.AddPane(self.maptoolbar.toolbar,
  2043. wx.aui.AuiPaneInfo().
  2044. Name("maptoolbar").Caption(_("Map Toolbar")).
  2045. ToolbarPane().Top().
  2046. LeftDockable(False).RightDockable(False).
  2047. BottomDockable(False).TopDockable(True).
  2048. CloseButton(False).Layer(2))
  2049. if name == "digit":
  2050. self.digittoolbar = toolbars.VDigitToolbar(self, self.Map, self.tree)
  2051. for toolRow in range(0, self.digittoolbar.numOfRows):
  2052. self._mgr.AddPane(self.digittoolbar.toolbar[toolRow],
  2053. wx.aui.AuiPaneInfo().
  2054. Name("digittoolbar" + str(toolRow)).Caption(_("Vector digitizer toolbar")).
  2055. ToolbarPane().Top().Row(toolRow + 1).
  2056. LeftDockable(False).RightDockable(False).
  2057. BottomDockable(False).TopDockable(True).
  2058. CloseButton(False).Layer(2))
  2059. # change mouse to draw digitized line
  2060. self.MapWindow.mouse['box'] = "point"
  2061. self.MapWindow.zoomtype = 0
  2062. self.MapWindow.pen = wx.Pen(colour='red', width=2, style=wx.SOLID)
  2063. self.MapWindow.polypen = wx.Pen(colour='green', width=2, style=wx.SOLID)
  2064. if name == "georect":
  2065. self.grtoolbar = toolbars.GRToolbar(self, self.Map)
  2066. self._mgr.AddPane(self.grtoolbar.toolbar,
  2067. wx.aui.AuiPaneInfo().
  2068. Name("grtoolbar").Caption(_("Georectification Toolbar")).
  2069. ToolbarPane().Top().
  2070. LeftDockable(False).RightDockable(False).
  2071. BottomDockable(False).TopDockable(True).
  2072. CloseButton(False).Layer(2))
  2073. self._mgr.Update()
  2074. def RemoveToolbar (self, name):
  2075. """
  2076. Removes toolbar from the window
  2077. TODO: Only hide, activate by calling AddToolbar()
  2078. """
  2079. # cannot hide main toolbar
  2080. if name == "map":
  2081. return
  2082. elif name == "digit":
  2083. # TODO: not destroy only hide
  2084. for toolRow in range(0, self.digittoolbar.numOfRows):
  2085. self._mgr.DetachPane (self.digittoolbar.toolbar[toolRow])
  2086. self.digittoolbar.toolbar[toolRow].Destroy()
  2087. self.digittoolbar = None
  2088. self.maptoolbar.combo.SetValue ("Tools");
  2089. self._mgr.Update()
  2090. def __InitDisplay(self):
  2091. """
  2092. Initialize map display, set dimensions and map region
  2093. """
  2094. self.width, self.height = self.GetClientSize()
  2095. Debug.msg(2, "MapFrame.__InitDisplay():")
  2096. self.Map.ChangeMapSize(self.GetClientSize())
  2097. self.Map.region = self.Map.GetRegion() # g.region -upgc
  2098. # self.Map.SetRegion() # adjust region to match display window
  2099. def OnFocus(self, event):
  2100. """
  2101. Change choicebook page to match display.
  2102. Or set display for georectifying
  2103. """
  2104. if self.gismanager.georectifying:
  2105. # in georectifying session; display used to get get geographic
  2106. # coordinates for GCPs
  2107. self.OnPointer(event)
  2108. else:
  2109. # change bookcontrol page to page associated with display
  2110. if self.page:
  2111. pgnum = self.layerbook.GetPageIndex(self.page)
  2112. if pgnum > -1:
  2113. self.layerbook.SetSelection(pgnum)
  2114. event.Skip()
  2115. def OnMotion(self, event):
  2116. """
  2117. Mouse moved
  2118. Track mouse motion and update status bar
  2119. """
  2120. # update statusbar if required
  2121. if self.toggleStatus.GetSelection() == 0: # Coordinates
  2122. e, n = self.MapWindow.Pixel2Cell(event.GetPositionTuple())
  2123. if self.digittoolbar and \
  2124. self.digittoolbar.action == 'addLine' and \
  2125. self.digittoolbar.type in ('line', 'boundary') and \
  2126. len(self.MapWindow.polycoords) > 0:
  2127. # for linear feature show segment and total length
  2128. distance_seg = self.MapWindow.Distance(self.MapWindow.polycoords[-1],
  2129. (e, n), screen=False)[0]
  2130. distance_tot = distance_seg
  2131. for idx in range(1, len(self.MapWindow.polycoords)):
  2132. distance_tot += self.MapWindow.Distance(self.MapWindow.polycoords[idx-1],
  2133. self.MapWindow.polycoords[idx],
  2134. screen=False )[0]
  2135. self.statusbar.SetStatusText("%.2f, %.2f (seg: %.2f; tot: %.2f)" % \
  2136. (e, n, distance_seg, distance_tot), 0)
  2137. else:
  2138. self.statusbar.SetStatusText("%.2f, %.2f" % (e, n), 0)
  2139. event.Skip()
  2140. def OnDraw(self, event):
  2141. """
  2142. Re-display current map composition
  2143. """
  2144. self.MapWindow.UpdateMap(render=False)
  2145. def TimerOnRender(self, event):
  2146. """Update process bar"""
  2147. self.onRenderGauge.SetValue(self.onRenderCounter)
  2148. def OnRender(self, event):
  2149. """
  2150. Re-render map composition (each map layer)
  2151. """
  2152. # detele tmp map layers (queries)
  2153. qlayer = self.Map.GetListOfLayers(l_name=globalvar.QUERYLAYER)
  2154. for layer in qlayer:
  2155. self.Map.DeleteLayer(layer)
  2156. self.MapWindow.UpdateMap(render=True)
  2157. # update statusbar
  2158. self.StatusbarUpdate()
  2159. def OnPointer(self, event):
  2160. """Pointer button clicked"""
  2161. self.MapWindow.mouse['use'] = "pointer"
  2162. self.MapWindow.mouse['box'] = "point"
  2163. # change the cursor
  2164. if self.digittoolbar:
  2165. # digitization tool activated
  2166. self.MapWindow.SetCursor(self.cursors["cross"])
  2167. # reset mouse['box'] if needed
  2168. if self.digittoolbar.action in ['addLine']:
  2169. if self.digittoolbar.type in ['point', 'centroid']:
  2170. self.MapWindow.mouse['box'] = 'point'
  2171. else: # line, boundary
  2172. self.MapWindow.mouse['box'] = 'line'
  2173. elif self.digittoolbar.action in ['addVertex', 'removeVertex', 'splitLine',
  2174. 'editLine', 'displayCats', 'displayAttrs',
  2175. 'copyCats', 'connectLine']:
  2176. self.MapWindow.mouse['box'] = 'point'
  2177. else: # moveLine, deleteLine
  2178. self.MapWindow.mouse['box'] = 'box'
  2179. elif self.gismanager.georectifying:
  2180. self.MapWindow.SetCursor(self.cursors["cross"])
  2181. else:
  2182. self.MapWindow.SetCursor(self.cursors["default"])
  2183. def OnZoomIn(self, event):
  2184. """
  2185. Zoom in the map.
  2186. Set mouse cursor, zoombox attributes, and zoom direction
  2187. """
  2188. self.MapWindow.mouse['use'] = "zoom"
  2189. self.MapWindow.mouse['box'] = "box"
  2190. self.MapWindow.zoomtype = 1
  2191. self.MapWindow.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
  2192. # change the cursor
  2193. self.MapWindow.SetCursor(self.cursors["cross"])
  2194. def OnZoomOut(self, event):
  2195. """
  2196. Zoom out the map.
  2197. Set mouse cursor, zoombox attributes, and zoom direction
  2198. """
  2199. self.MapWindow.mouse['use'] = "zoom"
  2200. self.MapWindow.mouse['box'] = "box"
  2201. self.MapWindow.zoomtype = -1
  2202. self.MapWindow.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
  2203. # change the cursor
  2204. self.MapWindow.SetCursor(self.cursors["cross"])
  2205. def OnZoomBack(self, event):
  2206. """
  2207. Zoom last (previously stored position)
  2208. """
  2209. self.MapWindow.ZoomBack()
  2210. def OnPan(self, event):
  2211. """
  2212. Panning, set mouse to drag
  2213. """
  2214. self.MapWindow.mouse['use'] = "pan"
  2215. self.MapWindow.mouse['box'] = "pan"
  2216. self.MapWindow.zoomtype = 0
  2217. # event.Skip()
  2218. # change the cursor
  2219. self.MapWindow.SetCursor(self.cursors["hand"])
  2220. def OnErase(self, event):
  2221. """
  2222. Erase the canvas
  2223. """
  2224. self.MapWindow.EraseMap()
  2225. def OnZoomRegion(self, event):
  2226. """
  2227. Zoom to region
  2228. """
  2229. self.Map.getRegion()
  2230. self.Map.getResolution()
  2231. self.UpdateMap()
  2232. # event.Skip()
  2233. def OnAlignRegion(self, event):
  2234. """
  2235. Align region
  2236. """
  2237. if not self.Map.alignRegion:
  2238. self.Map.alignRegion = True
  2239. else:
  2240. self.Map.alignRegion = False
  2241. # event.Skip()
  2242. def OnToggleRender(self, event):
  2243. """Enable/disable auto-rendering"""
  2244. if self.autoRender.GetValue():
  2245. self.OnRender(None)
  2246. def OnToggleShowRegion(self, event):
  2247. """Show/Hide extent in map canvas"""
  2248. if self.showRegion.GetValue():
  2249. # show extent
  2250. self.MapWindow.regionCoords = []
  2251. else:
  2252. del self.MapWindow.regionCoords
  2253. # redraw map if auto-rendering is enabled
  2254. if self.autoRender.GetValue():
  2255. self.OnRender(None)
  2256. def OnToggleResolution(self, event):
  2257. """Use resolution of computation region settings
  2258. for redering image instead of display resolution"""
  2259. # redraw map if auto-rendering is enabled
  2260. if self.autoRender.GetValue():
  2261. self.OnRender(None)
  2262. def OnToggleStatus(self, event):
  2263. """Toggle status text"""
  2264. self.StatusbarUpdate()
  2265. def OnChangeMapScale(self, event):
  2266. """Map scale changed by user"""
  2267. scale = event.GetString()
  2268. try:
  2269. if scale[:2] != '1:':
  2270. raise ValueError
  2271. value = int(scale[2:])
  2272. except ValueError:
  2273. self.mapScale.SetValue('1:' + str(int(self.mapScaleValue)))
  2274. return
  2275. dEW = value * (self.Map.region['cols'] / self.ppm[0])
  2276. dNS = value * (self.Map.region['rows'] / self.ppm[1])
  2277. self.Map.region['n'] = self.Map.region['center_northing'] + dNS / 2
  2278. self.Map.region['s'] = self.Map.region['center_northing'] - dNS / 2
  2279. self.Map.region['w'] = self.Map.region['center_easting'] - dEW / 2
  2280. self.Map.region['e'] = self.Map.region['center_easting'] + dEW / 2
  2281. # add to zoom history
  2282. self.MapWindow.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
  2283. self.Map.region['e'], self.Map.region['w'])
  2284. # redraw a map
  2285. self.MapWindow.UpdateMap()
  2286. def StatusbarUpdate(self):
  2287. """Update statusbar content"""
  2288. self.showRegion.Hide()
  2289. self.compResolution.Hide()
  2290. self.mapScale.Hide()
  2291. self.mapScaleValue = self.ppm = None
  2292. if self.toggleStatus.GetSelection() == 0: # Coordinates
  2293. self.statusbar.SetStatusText("", 0)
  2294. elif self.toggleStatus.GetSelection() == 1: # Extent
  2295. self.statusbar.SetStatusText("%.2f - %.2f, %.2f - %.2f" %
  2296. (self.Map.region["w"], self.Map.region["e"],
  2297. self.Map.region["s"], self.Map.region["n"]), 0)
  2298. elif self.toggleStatus.GetSelection() == 2: # Comp. region
  2299. compregion = self.Map.GetRegion()
  2300. self.statusbar.SetStatusText("%.2f - %.2f, %.2f - %.2f (%.2f, %.2f)" %
  2301. (compregion["w"], compregion["e"],
  2302. compregion["s"], compregion["n"],
  2303. compregion["ewres"], compregion["nsres"]), 0)
  2304. elif self.toggleStatus.GetSelection() == 3: # Show comp. extent
  2305. self.statusbar.SetStatusText("", 0)
  2306. self.showRegion.Show()
  2307. elif self.toggleStatus.GetSelection() == 4: # Display mode
  2308. self.statusbar.SetStatusText("", 0)
  2309. self.compResolution.Show()
  2310. elif self.toggleStatus.GetSelection() == 5: # Display geometry
  2311. self.statusbar.SetStatusText("rows=%d; cols=%d; nsres=%.2f; ewres=%.2f" %
  2312. (self.Map.region["rows"], self.Map.region["cols"],
  2313. self.Map.region["nsres"], self.Map.region["ewres"]), 0)
  2314. elif self.toggleStatus.GetSelection() == 6: # Map scale
  2315. # TODO: need to be fixed...
  2316. ### screen X region problem
  2317. ### user should specify ppm
  2318. dc = wx.ScreenDC()
  2319. dpSizePx = wx.DisplaySize() # display size in pixels
  2320. dpSizeMM = wx.DisplaySizeMM() # display size in mm (system)
  2321. dpSizeIn = (dpSizeMM[0] / 25.4, dpSizeMM[1] / 25.4) # inches
  2322. sysPpi = dc.GetPPI()
  2323. comPpi = (dpSizePx[0] / dpSizeIn[0],
  2324. dpSizePx[1] / dpSizeIn[1])
  2325. ppi = comPpi # pixel per inch
  2326. self.ppm = ((ppi[0] / 2.54) * 100, # pixel per meter
  2327. (ppi[1] / 2.54) * 100)
  2328. Debug.msg(4, "MapFrame.StatusbarUpdate(mapscale): size: px=%d,%d mm=%f,%f "
  2329. "in=%f,%f ppi: sys=%d,%d com=%d,%d; ppm=%f,%f" % \
  2330. (dpSizePx[0], dpSizePx[1], dpSizeMM[0], dpSizeMM[1],
  2331. dpSizeIn[0], dpSizeIn[1],
  2332. sysPpi[0], sysPpi[1], comPpi[0], comPpi[1],
  2333. self.ppm[0], self.ppm[1]))
  2334. region = self.Map.region
  2335. heightCm = region['rows'] / self.ppm[1] * 100
  2336. widthCm = region['cols'] / self.ppm[0] * 100
  2337. Debug.msg(4, "MapFrame.StatusbarUpdate(mapscale): width_cm=%f, height_cm=%f" %
  2338. (widthCm, heightCm))
  2339. xscale = (region['e'] - region['w']) / (region['cols'] / self.ppm[0])
  2340. yscale = (region['n'] - region['s']) / (region['rows'] / self.ppm[1])
  2341. scale = (xscale + yscale) / 2
  2342. Debug.msg(3, "MapFrame.StatusbarUpdate(mapscale): xscale=%f, yscale=%f -> scale=%f" % \
  2343. (xscale, yscale, scale))
  2344. self.statusbar.SetStatusText("")
  2345. self.mapScale.SetValue("1:%ld" % scale)
  2346. self.mapScaleValue = scale
  2347. self.mapScale.Show()
  2348. else:
  2349. self.statusbar.SetStatusText("", 1)
  2350. def StatusbarReposition(self):
  2351. """Reposition checkbox in statusbar"""
  2352. # reposition checkbox
  2353. widgets = [(0, self.showRegion),
  2354. (0, self.compResolution),
  2355. (0, self.mapScale),
  2356. (0, self.onRenderGauge),
  2357. (1, self.toggleStatus),
  2358. (2, self.autoRender)]
  2359. for idx, win in widgets:
  2360. rect = self.statusbar.GetFieldRect(idx)
  2361. if idx == 0: # show region / mapscale / process bar
  2362. # -> size
  2363. wWin, hWin = win.GetBestSize()
  2364. if win == self.onRenderGauge:
  2365. wWin = rect.width - 6
  2366. # -> position
  2367. # if win == self.showRegion:
  2368. # x, y = rect.x + rect.width - wWin, rect.y - 1
  2369. # align left
  2370. # else:
  2371. x, y = rect.x + 3, rect.y - 1
  2372. w, h = wWin, rect.height + 2
  2373. else: # choice || auto-rendering
  2374. x, y = rect.x, rect.y - 1
  2375. w, h = rect.width, rect.height + 2
  2376. if idx == 2:
  2377. x += 5
  2378. win.SetPosition((x, y))
  2379. win.SetSize((w, h))
  2380. def SaveToFile(self, event):
  2381. """
  2382. Save image to file
  2383. """
  2384. filetype = "PNG file (*.png)|*.png|"\
  2385. "TIF file (*.tif)|*.tif|"\
  2386. "GIF file (*.gif)|*.gif"
  2387. dlg = wx.FileDialog(self, "Choose a file name to save the image as a PNG to",
  2388. defaultDir = "",
  2389. defaultFile = "",
  2390. wildcard = filetype,
  2391. style=wx.SAVE|wx.FD_OVERWRITE_PROMPT)
  2392. if dlg.ShowModal() == wx.ID_OK:
  2393. base = os.path.splitext(dlg.GetPath())[0]
  2394. ext = os.path.splitext(dlg.GetPath())[1]
  2395. if dlg.GetFilterIndex() == 0:
  2396. type = wx.BITMAP_TYPE_PNG
  2397. path = dlg.GetPath()
  2398. if ext != '.png': path = base+'.png'
  2399. elif dlg.GetFilterIndex() == 1:
  2400. type = wx.BITMAP_TYPE_TIF
  2401. if ext != '.tif': path = base+'.tif'
  2402. elif dlg.GetFilterIndex() == 2:
  2403. type = wx.BITMAP_TYPE_TIF
  2404. if ext != '.gif': path = base+'.gif'
  2405. self.MapWindow.SaveToFile(path, type)
  2406. dlg.Destroy()
  2407. def PrintMenu(self, event):
  2408. """
  2409. Print map display
  2410. """
  2411. """
  2412. Print options and output menu
  2413. """
  2414. point = wx.GetMousePosition()
  2415. printmenu = wx.Menu()
  2416. # Add items to the menu
  2417. setup = wx.MenuItem(printmenu, wx.ID_ANY,'Page setup')
  2418. printmenu.AppendItem(setup)
  2419. self.Bind(wx.EVT_MENU, self.printopt.OnPageSetup, setup)
  2420. preview = wx.MenuItem(printmenu, wx.ID_ANY,'Print preview')
  2421. printmenu.AppendItem(preview)
  2422. self.Bind(wx.EVT_MENU, self.printopt.OnPrintPreview, preview)
  2423. doprint = wx.MenuItem(printmenu, wx.ID_ANY,'Print display')
  2424. printmenu.AppendItem(doprint)
  2425. self.Bind(wx.EVT_MENU, self.printopt.OnDoPrint, doprint)
  2426. # Popup the menu. If an item is selected then its handler
  2427. # will be called before PopupMenu returns.
  2428. self.PopupMenu(printmenu)
  2429. printmenu.Destroy()
  2430. def OnCloseWindow(self, event):
  2431. """
  2432. Window closed
  2433. Also close associated layer tree page
  2434. """
  2435. pgnum = None
  2436. self.Map.Clean()
  2437. if self.page:
  2438. pgnum = self.layerbook.GetPageIndex(self.page)
  2439. if pgnum > -1:
  2440. self.layerbook.DeletePage(pgnum)
  2441. #self.Destroy()
  2442. def GetRender(self):
  2443. """
  2444. Returns the current instance of render.Map()
  2445. """
  2446. return self.Map
  2447. def OnQueryDisplay(self, event):
  2448. """
  2449. Query currrent raster/vector map layers (display mode)
  2450. """
  2451. # switch GIS Manager to output console to show query results
  2452. self.gismanager.notebook.SetSelection(1)
  2453. self.MapWindow.mouse['use'] = "query"
  2454. self.MapWindow.mouse['box'] = "point"
  2455. self.MapWindow.zoomtype = 0
  2456. # change the cursor
  2457. self.MapWindow.SetCursor(self.cursors["cross"])
  2458. def OnQueryModify(self, event):
  2459. """
  2460. Query vector map layer (edit mode)
  2461. """
  2462. self.MapWindow.mouse['use'] = "queryVector"
  2463. self.MapWindow.mouse['box'] = "point"
  2464. self.MapWindow.zoomtype = 0
  2465. # change the cursor
  2466. self.MapWindow.SetCursor(self.cursors["cross"])
  2467. def QueryMap(self, x, y):
  2468. """
  2469. Query map layer features
  2470. Currently only raster and vector map layers are supported
  2471. """
  2472. #set query snap distance for v.what at mapunit equivalent of 10 pixels
  2473. qdist = 10.0 * ((self.Map.region['e'] - self.Map.region['w']) / self.Map.width)
  2474. east, north = self.MapWindow.Pixel2Cell((x, y))
  2475. if not self.tree.layer_selected:
  2476. dlg = wx.MessageDialog(parent=self,
  2477. message=_('No map layer selected for querying.'),
  2478. caption=_('Message'),
  2479. style=wx.OK | wx.ICON_INFORMATION)
  2480. dlg.ShowModal()
  2481. dlg.Destroy()
  2482. return
  2483. mapname = None
  2484. raststr = ''
  2485. vectstr = ''
  2486. rcmd = []
  2487. vcmd = []
  2488. for layer in self.tree.GetSelections():
  2489. type = self.tree.GetPyData(layer)[0]['maplayer'].type
  2490. dcmd = self.tree.GetPyData(layer)[0]['cmd']
  2491. name = utils.GetLayerNameFromCmd(dcmd)
  2492. if name == '':
  2493. continue
  2494. if type in ('raster', 'rgb', 'his'):
  2495. raststr += "%s," % name
  2496. elif type in ('vector', 'thememap', 'themechart'):
  2497. vectstr += "%s," % name
  2498. # build query commands for any selected rasters and vectors
  2499. if raststr != '':
  2500. rcmd = ['r.what', '--q',
  2501. '-f',
  2502. 'input=%s' % raststr.rstrip(','),
  2503. 'east_north=%f,%f' % (float(east), float(north))]
  2504. if vectstr != '':
  2505. vcmd = ['v.what', '--q',
  2506. '-a',
  2507. 'map=%s' % vectstr.rstrip(','),
  2508. 'east_north=%f,%f' % (float(east), float(north)),
  2509. 'distance=%f' % qdist]
  2510. # parse query command(s)
  2511. if self.gismanager:
  2512. if rcmd:
  2513. self.gismanager.goutput.RunCmd(rcmd)
  2514. if vcmd:
  2515. self.gismanager.goutput.RunCmd(vcmd)
  2516. else:
  2517. if rcmd:
  2518. gcmd.Command(rcmd)
  2519. if vcmd:
  2520. gcmd.Command(vcmd)
  2521. def QueryVector(self, x, y):
  2522. """
  2523. Query vector map layer features
  2524. Attribute data of selected vector object are displayed in GUI dialog.
  2525. Data can be modified (On Submit)
  2526. """
  2527. if not self.tree.layer_selected or \
  2528. self.tree.GetPyData(self.tree.layer_selected)[0]['type'] != 'vector':
  2529. wx.MessageBox(parent=self,
  2530. message=_("No vector map selected for querying."),
  2531. caption=_("Message"),
  2532. style=wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
  2533. return
  2534. posWindow = self.ClientToScreen((x + self.MapWindow.dialogOffset,
  2535. y + self.MapWindow.dialogOffset))
  2536. qdist = 10.0 * ((self.Map.region['e'] - self.Map.region['w']) / \
  2537. self.Map.width)
  2538. east, north = self.MapWindow.Pixel2Cell((x, y))
  2539. mapName = self.tree.GetPyData(self.tree.layer_selected)[0]['maplayer'].name
  2540. if self.dialogs['attributes'] is None:
  2541. self.dialogs['attributes'] = dbm.DisplayAttributesDialog(parent=self.MapWindow,
  2542. map=mapName,
  2543. query=((east, north), qdist),
  2544. pos=posWindow,
  2545. action="update")
  2546. else:
  2547. # update currently open dialog
  2548. self.dialogs['attributes'].UpdateDialog(query=((east, north), qdist))
  2549. line = self.dialogs['attributes'].GetLine()
  2550. try:
  2551. qlayer = self.Map.GetListOfLayers(l_name=globalvar.QUERYLAYER)[0]
  2552. except IndexError:
  2553. qlayer = None
  2554. if self.dialogs['attributes'].mapDBInfo and line:
  2555. # highlight feature & re-draw map
  2556. if qlayer:
  2557. qlayer.SetCmd(self.AddTmpVectorMapLayer(mapName, line,
  2558. useId=True,
  2559. addLayer=False))
  2560. else:
  2561. self.AddTmpVectorMapLayer(mapName, line, useId=True)
  2562. self.MapWindow.UpdateMap(render=False, renderVector=False)
  2563. # digitClass.driver.SetSelected([line])
  2564. if not self.dialogs['attributes'].IsShown():
  2565. self.dialogs['attributes'].Show()
  2566. else:
  2567. if qlayer:
  2568. self.Map.DeleteLayer(qlayer)
  2569. self.MapWindow.UpdateMap(render=False, renderVector=False)
  2570. if self.dialogs['attributes'].IsShown():
  2571. self.dialogs['attributes'].Hide()
  2572. def OnQuery(self, event):
  2573. """Query tools menu"""
  2574. point = wx.GetMousePosition()
  2575. toolsmenu = wx.Menu()
  2576. # Add items to the menu
  2577. display = wx.MenuItem(toolsmenu, wx.ID_ANY, Icons["queryDisplay"].GetLabel())
  2578. toolsmenu.AppendItem(display)
  2579. self.Bind(wx.EVT_MENU, self.OnQueryDisplay, display)
  2580. modify = wx.MenuItem(toolsmenu, wx.ID_ANY, Icons["queryModify"].GetLabel())
  2581. toolsmenu.AppendItem(modify)
  2582. self.Bind(wx.EVT_MENU, self.OnQueryModify, modify)
  2583. self.PopupMenu(toolsmenu)
  2584. toolsmenu.Destroy()
  2585. def AddTmpVectorMapLayer(self, name, cats, useId=False, addLayer=True):
  2586. """
  2587. Add temporal vector map layer to map composition
  2588. @param name name of map layer
  2589. @param useId use feature id instead of category
  2590. """
  2591. # color settings from ATM
  2592. color = UserSettings.Get(group='atm', key='highlight', subkey='color')
  2593. colorStr = str(color[0]) + ":" + \
  2594. str(color[1]) + ":" + \
  2595. str(color[2])
  2596. cmd = ["d.vect",
  2597. "map=%s" % name,
  2598. "color=%s" % colorStr,
  2599. "fcolor=%s" % colorStr,
  2600. "width=%d" % UserSettings.Get(group='atm', key='highlight', subkey='width')]
  2601. if useId:
  2602. cmd.append('-i')
  2603. cmd.append('cats=%s' % str(cats))
  2604. else:
  2605. cmd.append("cats=%s" % utils.ListOfCatsToRange(cats))
  2606. # if self.icon:
  2607. # cmd.append("icon=%s" % (self.icon))
  2608. # if self.pointsize:
  2609. # cmd.append("size=%s" % (self.pointsize))
  2610. if addLayer:
  2611. return self.Map.AddLayer(type='vector', name=globalvar.QUERYLAYER, command=cmd,
  2612. l_active=True, l_hidden=True, l_opacity=1.0)
  2613. else:
  2614. return cmd
  2615. def OnAnalyze(self, event):
  2616. """
  2617. Analysis tools menu
  2618. """
  2619. point = wx.GetMousePosition()
  2620. toolsmenu = wx.Menu()
  2621. # Add items to the menu
  2622. measure = wx.MenuItem(toolsmenu, wx.ID_ANY, Icons["measure"].GetLabel())
  2623. measure.SetBitmap(Icons["measure"].GetBitmap(self.iconsize))
  2624. toolsmenu.AppendItem(measure)
  2625. self.Bind(wx.EVT_MENU, self.OnMeasure, measure)
  2626. profile = wx.MenuItem(toolsmenu, wx.ID_ANY, Icons["profile"].GetLabel())
  2627. profile.SetBitmap(Icons["profile"].GetBitmap(self.iconsize))
  2628. toolsmenu.AppendItem(profile)
  2629. self.Bind(wx.EVT_MENU, self.Profile, profile)
  2630. histogram = wx.MenuItem(toolsmenu, wx.ID_ANY, Icons["histogram"].GetLabel())
  2631. histogram.SetBitmap(Icons["histogram"].GetBitmap(self.iconsize))
  2632. toolsmenu.AppendItem(histogram)
  2633. self.Bind(wx.EVT_MENU, self.Histogram, histogram)
  2634. # Popup the menu. If an item is selected then its handler
  2635. # will be called before PopupMenu returns.
  2636. self.PopupMenu(toolsmenu)
  2637. toolsmenu.Destroy()
  2638. def OnMeasure(self, event):
  2639. """
  2640. Init measurement routine that calculates
  2641. map distance along transect drawn on
  2642. map display
  2643. """
  2644. self.totaldist = 0.0 # total measured distance
  2645. # switch GIS Manager to output console to show measure results
  2646. self.gismanager.notebook.SetSelection(1)
  2647. # change mouse to draw line for measurement
  2648. self.MapWindow.mouse['use'] = "measure"
  2649. self.MapWindow.mouse['box'] = "line"
  2650. self.MapWindow.zoomtype = 0
  2651. self.MapWindow.pen = wx.Pen(colour='red', width=2, style=wx.SHORT_DASH)
  2652. self.MapWindow.polypen = wx.Pen(colour='green', width=2, style=wx.SHORT_DASH)
  2653. # change the cursor
  2654. self.MapWindow.SetCursor(self.cursors["pencil"])
  2655. # initiating output
  2656. style = self.gismanager.goutput.cmd_output.StyleWarning
  2657. self.gismanager.goutput.WriteLog(_('Click and drag with left mouse button '
  2658. 'to measure.%s'
  2659. 'Double click with left button to clear.') % \
  2660. (os.linesep), style)
  2661. if self.projinfo['proj'] != 'xy':
  2662. units = self.projinfo['units']
  2663. style = self.gismanager.goutput.cmd_output.StyleCommand
  2664. self.gismanager.goutput.WriteLog(_('Measuring distance') + ' ('
  2665. + units + '):',
  2666. style)
  2667. else:
  2668. self.gismanager.goutput.WriteLog(_('Measuring distance:'),
  2669. style)
  2670. def MeasureDist(self, beginpt, endpt):
  2671. """
  2672. Calculate map distance from screen distance
  2673. and print to output window
  2674. """
  2675. if self.gismanager.notebook.GetSelection() != 1:
  2676. self.gismanager.notebook.SetSelection(1)
  2677. dist, (north, east) = self.MapWindow.Distance(beginpt, endpt)
  2678. dist = round(dist, 3)
  2679. d, dunits = self.FormatDist(dist)
  2680. self.totaldist += dist
  2681. td, tdunits = self.FormatDist(self.totaldist)
  2682. strdist = str(d)
  2683. strtotdist = str(td)
  2684. if self.projinfo['proj'] == 'xy' or 'degree' not in self.projinfo['unit']:
  2685. angle = int(math.degrees(math.atan2(north,east)) + 0.5)
  2686. angle = angle+90
  2687. if angle < 0: angle = 360+angle
  2688. mstring = 'segment = %s %s\ttotal distance = %s %s\tbearing = %d deg' \
  2689. % (strdist,dunits,strtotdist,tdunits,angle)
  2690. else:
  2691. mstring = 'segment = %s %s\ttotal distance = %s %s' \
  2692. % (strdist,dunits,strtotdist,tdunits)
  2693. self.gismanager.goutput.WriteLog(mstring)
  2694. return dist
  2695. def Profile(self, event):
  2696. """
  2697. Init profile canvas and tools
  2698. """
  2699. raster = []
  2700. if self.tree.layer_selected and \
  2701. self.tree.GetPyData(self.tree.layer_selected)[0]['type'] == 'raster':
  2702. raster.append(self.tree.GetPyData(self.tree.layer_selected)[0]['maplayer'].name)
  2703. self.profile = profile.ProfileFrame(self,
  2704. id=wx.ID_ANY, pos=wx.DefaultPosition, size=(700,300),
  2705. style=wx.DEFAULT_FRAME_STYLE, rasterList=raster)
  2706. self.profile.Show()
  2707. def FormatDist(self, dist):
  2708. """Format length numbers and units in a nice way,
  2709. as a function of length. From code by Hamish Bowman
  2710. Grass Development Team 2006"""
  2711. mapunits = self.projinfo['units']
  2712. if mapunits == 'metres': mapunits = 'meters'
  2713. outunits = mapunits
  2714. dist = float(dist)
  2715. divisor = 1.0
  2716. # figure out which units to use
  2717. if mapunits == 'meters':
  2718. if dist > 2500.0:
  2719. outunits = 'km'
  2720. divisor = 1000.0
  2721. else: outunits = 'm'
  2722. elif mapunits == 'feet':
  2723. # nano-bug: we match any "feet", but US Survey feet is really
  2724. # 5279.9894 per statute mile, or 10.6' per 1000 miles. As >1000
  2725. # miles the tick markers are rounded to the nearest 10th of a
  2726. # mile (528'), the difference in foot flavours is ignored.
  2727. if dist > 5280.0:
  2728. outunits = 'miles'
  2729. divisor = 5280.0
  2730. else:
  2731. outunits = 'ft'
  2732. elif 'degree' in mapunits:
  2733. if dist < 1:
  2734. outunits = 'min'
  2735. divisor = (1/60.0)
  2736. else:
  2737. outunits = 'deg'
  2738. # format numbers in a nice way
  2739. if (dist/divisor) >= 2500.0:
  2740. outdist = round(dist/divisor)
  2741. elif (dist/divisor) >= 1000.0:
  2742. outdist = round(dist/divisor,1)
  2743. elif (dist/divisor) > 0.0:
  2744. outdist = round(dist/divisor,int(math.ceil(3-math.log10(dist/divisor))))
  2745. else:
  2746. outdist = float(dist/divisor)
  2747. return (outdist, outunits)
  2748. def Histogram(self, event):
  2749. """
  2750. Init histogram display canvas and tools
  2751. """
  2752. self.histogram = histogram.HistFrame(self,
  2753. id=wx.ID_ANY, size=globalvar.HIST_WINDOW_SIZE,
  2754. style=wx.DEFAULT_FRAME_STYLE)
  2755. #show new display
  2756. self.histogram.Show()
  2757. self.histogram.Refresh()
  2758. self.histogram.Update()
  2759. def OnDecoration(self, event):
  2760. """
  2761. Decorations overlay menu
  2762. """
  2763. point = wx.GetMousePosition()
  2764. decmenu = wx.Menu()
  2765. # Add items to the menu
  2766. AddScale = wx.MenuItem(decmenu, wx.ID_ANY, Icons["addbarscale"].GetLabel())
  2767. AddScale.SetBitmap(Icons["addbarscale"].GetBitmap(self.iconsize))
  2768. decmenu.AppendItem(AddScale)
  2769. self.Bind(wx.EVT_MENU, self.OnAddBarscale, AddScale)
  2770. AddLegend = wx.MenuItem(decmenu, wx.ID_ANY, Icons["addlegend"].GetLabel())
  2771. AddLegend.SetBitmap(Icons["addlegend"].GetBitmap(self.iconsize))
  2772. decmenu.AppendItem(AddLegend)
  2773. self.Bind(wx.EVT_MENU, self.OnAddLegend, AddLegend)
  2774. AddText = wx.MenuItem(decmenu, wx.ID_ANY, Icons["addtext"].GetLabel())
  2775. AddText.SetBitmap(Icons["addtext"].GetBitmap(self.iconsize))
  2776. decmenu.AppendItem(AddText)
  2777. self.Bind(wx.EVT_MENU, self.OnAddText, AddText)
  2778. # Popup the menu. If an item is selected then its handler
  2779. # will be called before PopupMenu returns.
  2780. self.PopupMenu(decmenu)
  2781. decmenu.Destroy()
  2782. def OnAddBarscale(self, event):
  2783. """
  2784. Handler for scale/arrow map decoration menu selection.
  2785. """
  2786. if self.dialogs['barscale']:
  2787. return
  2788. id = 0 # unique index for overlay layer
  2789. # If location is latlon, only display north arrow (scale won't work)
  2790. # proj = self.projinfo['proj']
  2791. # if proj == 'll':
  2792. # barcmd = 'd.barscale -n'
  2793. # else:
  2794. # barcmd = 'd.barscale'
  2795. # decoration overlay control dialog
  2796. self.dialogs['barscale'] = \
  2797. gdialogs.DecorationDialog(parent=self, title=_('Scale and North arrow'),
  2798. size=(350, 200),
  2799. style=wx.DEFAULT_DIALOG_STYLE | wx.CENTRE,
  2800. cmd=['d.barscale'],
  2801. ovlId=id,
  2802. name='barscale',
  2803. checktxt = _("Show/hide scale and North arrow"),
  2804. ctrltxt = _("scale object"))
  2805. self.dialogs['barscale'].Show()
  2806. def OnAddLegend(self, event):
  2807. """
  2808. Handler for legend map decoration menu selection.
  2809. """
  2810. if self.dialogs['legend']:
  2811. return
  2812. id = 1 # index for overlay layer in render
  2813. cmd = ['d.legend']
  2814. if self.tree.layer_selected and \
  2815. self.tree.GetPyData(self.tree.layer_selected)[0]['type'] == 'raster':
  2816. cmd.append('map=%s' % self.tree.GetPyData(self.tree.layer_selected)[0]['maplayer'].name)
  2817. # Decoration overlay control dialog
  2818. self.dialogs['legend'] = \
  2819. gdialogs.DecorationDialog(parent=self, title=('Legend'),
  2820. size=(350, 200),
  2821. style=wx.DEFAULT_DIALOG_STYLE | wx.CENTRE,
  2822. cmd=cmd,
  2823. ovlId=id,
  2824. name='legend',
  2825. checktxt = _("Show/hide legend"),
  2826. ctrltxt = _("legend object"))
  2827. self.dialogs['legend'].Show()
  2828. def OnAddText(self, event):
  2829. """
  2830. Handler for text decoration menu selection.
  2831. """
  2832. id = 2 # index for overlay layer in render
  2833. # default values
  2834. text = ''
  2835. font = self.GetFont()
  2836. color = wx.BLACK
  2837. coords = [10, 10, 10, 10]
  2838. rotation = 0.0
  2839. # if self.MapWindow.currtxtid == None: # text doesn't already exist
  2840. # id = wx.NewId() + 100
  2841. # else: # text already exists
  2842. # id = self.MapWindow.currtxtid
  2843. # textcoords = self.ovlcoords[id]
  2844. dlg = gdialogs.TextLayerDialog(parent=self, ovlId=id, title=_('Add text layer'),
  2845. size=(400, 200))
  2846. dlg.CenterOnScreen()
  2847. # If OK button pressed in decoration control dialog
  2848. if dlg.ShowModal() == wx.ID_OK:
  2849. text = dlg.GetValues()[0]
  2850. coords, w, h = self.MapWindow.TextBounds(dlg.GetValues(),
  2851. coords)
  2852. # delete object if it has no text
  2853. if text == '':
  2854. try:
  2855. self.MapWindow.pdc.ClearId(id)
  2856. self.MapWindow.pdc.RemoveId(id)
  2857. del self.MapWindow.textdict[id]
  2858. # del self.ovlcoords[id]
  2859. except:
  2860. pass
  2861. return
  2862. self.MapWindow.pdc.ClearId(id)
  2863. self.MapWindow.pdc.SetId(id)
  2864. self.MapWindow.textdict[id] = (text, font, color, rotation)
  2865. self.MapWindow.Draw(self.MapWindow.pdc, img=self.MapWindow.textdict[id],
  2866. drawid=id, pdctype='text', coords=coords)
  2867. self.MapWindow.UpdateMap(render=False, renderVector=False)
  2868. def GetOptData(self, dcmd, type, params, propwin):
  2869. """
  2870. Callback method for decoration overlay command generated by
  2871. dialog created in menuform.py
  2872. """
  2873. # Reset comand and rendering options in render.Map. Always render decoration.
  2874. # Showing/hiding handled by PseudoDC
  2875. self.Map.ChangeOverlay(ovltype=type, type='overlay', name='', command=dcmd,
  2876. l_active=True, l_render=False)
  2877. self.params[type] = params
  2878. self.propwin[type] = propwin
  2879. def OnZoomMenu(self, event):
  2880. """
  2881. Zoom menu
  2882. """
  2883. point = wx.GetMousePosition()
  2884. zoommenu = wx.Menu()
  2885. # Add items to the menu
  2886. zoommap = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to selected map'))
  2887. zoommenu.AppendItem(zoommap)
  2888. self.Bind(wx.EVT_MENU, self.MapWindow.ZoomToMap, zoommap)
  2889. zoomwind = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to computational region (set with g.region)'))
  2890. zoommenu.AppendItem(zoomwind)
  2891. self.Bind(wx.EVT_MENU, self.MapWindow.ZoomToWind, zoomwind)
  2892. zoomdefault = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to default region'))
  2893. zoommenu.AppendItem(zoomdefault)
  2894. self.Bind(wx.EVT_MENU, self.MapWindow.ZoomToDefault, zoomdefault)
  2895. zoomsaved = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to saved region'))
  2896. zoommenu.AppendItem(zoomsaved)
  2897. self.Bind(wx.EVT_MENU, self.MapWindow.ZoomToSaved, zoomsaved)
  2898. savewind = wx.MenuItem(zoommenu, wx.ID_ANY, _('Set computational region from display'))
  2899. zoommenu.AppendItem(savewind)
  2900. self.Bind(wx.EVT_MENU, self.MapWindow.DisplayToWind, savewind)
  2901. savezoom = wx.MenuItem(zoommenu, wx.ID_ANY, _('Save display geometry to named region'))
  2902. zoommenu.AppendItem(savezoom)
  2903. self.Bind(wx.EVT_MENU, self.MapWindow.SaveDisplayRegion, savezoom)
  2904. # Popup the menu. If an item is selected then its handler
  2905. # will be called before PopupMenu returns.
  2906. self.PopupMenu(zoommenu)
  2907. zoommenu.Destroy()
  2908. def SetProperties(self, render=False, mode=0, showCompExtent=False):
  2909. """Set properies of map display window"""
  2910. self.autoRender.SetValue(render)
  2911. self.toggleStatus.SetSelection(mode)
  2912. self.StatusbarUpdate()
  2913. self.showRegion.SetValue(showCompExtent)
  2914. if showCompExtent:
  2915. self.MapWindow.regionCoords = []
  2916. # end of class MapFrame
  2917. class MapApp(wx.App):
  2918. """
  2919. MapApp class
  2920. """
  2921. def OnInit(self):
  2922. wx.InitAllImageHandlers()
  2923. if __name__ == "__main__":
  2924. Map = render.Map() # instance of Map class to render GRASS display output to PPM file
  2925. else:
  2926. Map = None
  2927. self.mapFrm = MapFrame(parent=None, id=wx.ID_ANY, Map=Map, size=(640,480))
  2928. #self.SetTopWindow(Map)
  2929. self.mapFrm.Show()
  2930. if __name__ == "__main__":
  2931. # redraw map, if new command appears
  2932. self.redraw = False
  2933. status = Command(self, Map)
  2934. status.start()
  2935. self.timer = wx.PyTimer(self.watcher)
  2936. # check each 0.1s
  2937. self.timer.Start(100)
  2938. return 1
  2939. def OnExit(self):
  2940. if __name__ == "__main__":
  2941. # stop the timer
  2942. self.timer.Stop()
  2943. # terminate thread (a bit ugly)
  2944. os.system("""echo "quit" >> %s""" % (cmdfilename))
  2945. def watcher(self):
  2946. """Redraw, if new layer appears"""
  2947. if self.redraw:
  2948. self.mapFrm.OnDraw(None)
  2949. self.redraw = False
  2950. return
  2951. # end of class MapApp
  2952. if __name__ == "__main__":
  2953. ###### SET command variable
  2954. if len(sys.argv) != 3:
  2955. print __doc__
  2956. sys.exit()
  2957. title = sys.argv[1]
  2958. cmdfilename = sys.argv[2]
  2959. import gettext
  2960. gettext.install("gm_map") # replace with the appropriate catalog name
  2961. print "Starting monitor <%s>" % (title)
  2962. gm_map = MapApp(0)
  2963. # set title
  2964. gm_map.mapFrm.SetTitle ("GRASS GIS - Map Display: " + title + " - Location: " + \
  2965. grassenv.GetGRASSVariable("LOCATION_NAME"))
  2966. gm_map.MainLoop()
  2967. if grassenv.env.has_key("MONITOR"):
  2968. os.system("d.mon sel=%s" % grassenv.GetGRASSVariable("MONITOR"))
  2969. os.remove(cmdfilename)
  2970. os.system("""g.gisenv set="GRASS_PYCMDFILE" """)
  2971. print "Stoping monitor <%s>" % (title)
  2972. sys.exit()