mapdisp_window.py 101 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761
  1. """!
  2. @package mapdisp_window.py
  3. @brief GIS map display canvas, buffered window.
  4. Classes:
  5. - MapWindow
  6. - BufferedWindow
  7. (C) 2006-2009 by the GRASS Development Team
  8. This program is free software under the GNU General Public
  9. License (>=v2). Read the file COPYING that comes with GRASS
  10. for details.
  11. @author Michael Barton
  12. @author Jachym Cepicky
  13. @author Martin Landa <landa.martin gmail.com>
  14. """
  15. import os
  16. import time
  17. import math
  18. import sys
  19. import tempfile
  20. import wx
  21. import grass.script as grass
  22. import dbm
  23. import dbm_dialogs
  24. import gdialogs
  25. import gcmd
  26. import utils
  27. import globalvar
  28. import gselect
  29. from debug import Debug
  30. from preferences import globalSettings as UserSettings
  31. from units import ConvertValue as UnitsConvertValue
  32. from vdigit import GV_LINES as VDigit_Lines_Type
  33. from vdigit import VDigitCategoryDialog
  34. from vdigit import VDigitZBulkDialog
  35. from vdigit import VDigitDuplicatesDialog
  36. from vdigit import PseudoDC as VDigitPseudoDC
  37. class MapWindow(object):
  38. """!
  39. Abstract map window class
  40. Parent for BufferedWindow class (2D display mode) and
  41. GLWindow (3D display mode)
  42. """
  43. def __init__(self, parent, id=wx.ID_ANY,
  44. pos=wx.DefaultPosition,
  45. size=wx.DefaultSize,
  46. style=wx.NO_FULL_REPAINT_ON_RESIZE,
  47. Map=None, tree=None, lmgr=None):
  48. self.parent = parent # MapFrame
  49. #
  50. # mouse attributes -- position on the screen, begin and end of
  51. # dragging, and type of drawing
  52. #
  53. self.mouse = {
  54. 'begin': [0, 0], # screen coordinates
  55. 'end' : [0, 0],
  56. 'use' : "pointer",
  57. 'box' : "point"
  58. }
  59. def EraseMap(self):
  60. """!
  61. Erase the canvas (virtual method)
  62. """
  63. pass
  64. def UpdateMap(self):
  65. """!
  66. Updates the canvas anytime there is a change to the
  67. underlaying images or to the geometry of the canvas.
  68. """
  69. pass
  70. def OnLeftDown(self, event):
  71. pass
  72. def OnLeftUp(self, event):
  73. pass
  74. def OnMouseMotion(self, event):
  75. pass
  76. def OnZoomToMap(self, event):
  77. pass
  78. def OnZoomToRaster(self, event):
  79. pass
  80. def GetSelectedLayer(self, type = 'layer', multi = False):
  81. """!
  82. Get selected layer from layer tree
  83. @param type 'item' / 'layer' / 'nviz'
  84. @param multi return first selected layer or all
  85. @return layer / map layer properties / nviz properties
  86. @return None / [] on failure
  87. """
  88. ret = []
  89. if not self.tree or \
  90. not self.tree.GetSelection():
  91. if multi:
  92. return []
  93. else:
  94. return None
  95. if multi and \
  96. type == 'item':
  97. return self.tree.GetSelections()
  98. for item in self.tree.GetSelections():
  99. if not item.IsChecked():
  100. if multi:
  101. continue
  102. else:
  103. return None
  104. if type == 'item': # -> multi = False
  105. return item
  106. try:
  107. if type == 'nviz':
  108. layer = self.tree.GetPyData(item)[0]['nviz']
  109. else:
  110. layer = self.tree.GetPyData(item)[0]['maplayer']
  111. except:
  112. layer = None
  113. if multi:
  114. ret.append(layer)
  115. else:
  116. return layer
  117. return ret
  118. class BufferedWindow(MapWindow, wx.Window):
  119. """!
  120. A Buffered window class.
  121. When the drawing needs to change, you app needs to call the
  122. UpdateMap() method. Since the drawing is stored in a bitmap, you
  123. can also save the drawing to file by calling the
  124. SaveToFile(self,file_name,file_type) method.
  125. """
  126. def __init__(self, parent, id,
  127. pos = wx.DefaultPosition,
  128. size = wx.DefaultSize,
  129. style=wx.NO_FULL_REPAINT_ON_RESIZE,
  130. Map=None, tree=None, lmgr=None):
  131. MapWindow.__init__(self, parent, id, pos, size, style,
  132. Map, tree, lmgr)
  133. wx.Window.__init__(self, parent, id, pos, size, style)
  134. self.Map = Map
  135. self.tree = tree
  136. self.lmgr = lmgr # Layer Manager
  137. #
  138. # Flags
  139. #
  140. self.resize = False # indicates whether or not a resize event has taken place
  141. self.dragimg = None # initialize variable for map panning
  142. #
  143. # Variable for drawing on DC
  144. #
  145. self.pen = None # pen for drawing zoom boxes, etc.
  146. self.polypen = None # pen for drawing polylines (measurements, profiles, etc)
  147. # List of wx.Point tuples defining a polyline (geographical coordinates)
  148. self.polycoords = []
  149. # ID of rubber band line
  150. self.lineid = None
  151. # ID of poly line resulting from cumulative rubber band lines (e.g. measurement)
  152. self.plineid = None
  153. #
  154. # Event bindings
  155. #
  156. self.Bind(wx.EVT_PAINT, self.OnPaint)
  157. self.Bind(wx.EVT_SIZE, self.OnSize)
  158. self.Bind(wx.EVT_IDLE, self.OnIdle)
  159. self.Bind(wx.EVT_MOTION, self.MouseActions)
  160. self.Bind(wx.EVT_MOUSE_EVENTS, self.MouseActions)
  161. self.processMouse = True
  162. #
  163. # Render output objects
  164. #
  165. self.mapfile = None # image file to be rendered
  166. self.img = "" # wx.Image object (self.mapfile)
  167. # used in digitization tool (do not redraw vector map)
  168. self.imgVectorMap = None
  169. # decoration overlays
  170. self.overlays = {}
  171. # images and their PseudoDC ID's for painting and dragging
  172. self.imagedict = {}
  173. self.select = {} # selecting/unselecting decorations for dragging
  174. self.textdict = {} # text, font, and color indexed by id
  175. self.currtxtid = None # PseudoDC id for currently selected text
  176. #
  177. # Zoom objects
  178. #
  179. self.zoomhistory = [] # list of past zoom extents
  180. self.currzoom = 0 # current set of extents in zoom history being used
  181. self.zoomtype = 1 # 1 zoom in, 0 no zoom, -1 zoom out
  182. self.hitradius = 10 # distance for selecting map decorations
  183. self.dialogOffset = 5 # offset for dialog (e.g. DisplayAttributesDialog)
  184. # OnSize called to make sure the buffer is initialized.
  185. # This might result in OnSize getting called twice on some
  186. # platforms at initialization, but little harm done.
  187. ### self.OnSize(None)
  188. self.DefinePseudoDC()
  189. # redraw all pdc's, pdcTmp layer is redrawn always (speed issue)
  190. self.redrawAll = True
  191. # will store an off screen empty bitmap for saving to file
  192. self._buffer = ''
  193. self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x:None)
  194. # vars for handling mouse clicks
  195. self.dragid = -1
  196. self.lastpos = (0, 0)
  197. def DefinePseudoDC(self, vdigit = False):
  198. """!Define PseudoDC class to use
  199. @vdigit True to use PseudoDC from vdigit
  200. """
  201. # create PseudoDC used for background map, map decorations like scales and legends
  202. self.pdc = self.PseudoDC(vdigit)
  203. # used for digitization tool
  204. self.pdcVector = None
  205. # decorations (region box, etc.)
  206. self.pdcDec = self.PseudoDC(vdigit)
  207. # pseudoDC for temporal objects (select box, measurement tool, etc.)
  208. self.pdcTmp = self.PseudoDC(vdigit)
  209. def PseudoDC(self, vdigit = False):
  210. """!Create PseudoDC instance"""
  211. if vdigit:
  212. PseudoDC = VDigitPseudoDC
  213. else:
  214. PseudoDC = wx.PseudoDC
  215. return PseudoDC()
  216. def CheckPseudoDC(self):
  217. """!Try to draw background
  218. @return True on success
  219. @return False on failure
  220. """
  221. try:
  222. self.pdc.BeginDrawing()
  223. self.pdc.SetBackground(wx.Brush(self.GetBackgroundColour()))
  224. self.pdc.BeginDrawing()
  225. except:
  226. return False
  227. return True
  228. def Draw(self, pdc, img=None, drawid=None, pdctype='image', coords=[0, 0, 0, 0]):
  229. """!
  230. Draws map and overlay decorations
  231. """
  232. if drawid == None:
  233. if pdctype == 'image' and img:
  234. drawid = self.imagedict[img]
  235. elif pdctype == 'clear':
  236. drawid == None
  237. else:
  238. drawid = wx.NewId()
  239. if img and pdctype == 'image':
  240. # self.imagedict[img]['coords'] = coords
  241. self.select[self.imagedict[img]['id']] = False # ?
  242. pdc.BeginDrawing()
  243. if drawid != 99:
  244. bg = wx.TRANSPARENT_BRUSH
  245. else:
  246. bg = wx.Brush(self.GetBackgroundColour())
  247. pdc.SetBackground(bg)
  248. ### pdc.Clear()
  249. Debug.msg (5, "BufferedWindow.Draw(): id=%s, pdctype=%s, coord=%s" % \
  250. (drawid, pdctype, coords))
  251. # set PseudoDC id
  252. if drawid is not None:
  253. pdc.SetId(drawid)
  254. if pdctype == 'clear': # erase the display
  255. bg = wx.WHITE_BRUSH
  256. # bg = wx.Brush(self.GetBackgroundColour())
  257. pdc.SetBackground(bg)
  258. pdc.RemoveAll()
  259. pdc.Clear()
  260. pdc.EndDrawing()
  261. self.Refresh()
  262. return
  263. if pdctype == 'image': # draw selected image
  264. bitmap = wx.BitmapFromImage(img)
  265. w,h = bitmap.GetSize()
  266. pdc.DrawBitmap(bitmap, coords[0], coords[1], True) # draw the composite map
  267. pdc.SetIdBounds(drawid, wx.Rect(coords[0],coords[1], w, h))
  268. elif pdctype == 'box': # draw a box on top of the map
  269. if self.pen:
  270. pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
  271. pdc.SetPen(self.pen)
  272. x2 = max(coords[0],coords[2])
  273. x1 = min(coords[0],coords[2])
  274. y2 = max(coords[1],coords[3])
  275. y1 = min(coords[1],coords[3])
  276. rwidth = x2-x1
  277. rheight = y2-y1
  278. rect = wx.Rect(x1, y1, rwidth, rheight)
  279. pdc.DrawRectangleRect(rect)
  280. pdc.SetIdBounds(drawid, rect)
  281. elif pdctype == 'line': # draw a line on top of the map
  282. if self.pen:
  283. pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
  284. pdc.SetPen(self.pen)
  285. pdc.DrawLine(wx.Point(coords[0], coords[1]),
  286. wx.Point(coords[2], coords[3]))
  287. pdc.SetIdBounds(drawid, wx.Rect(coords[0], coords[1], coords[2], coords[3]))
  288. # self.ovlcoords[drawid] = coords
  289. elif pdctype == 'polyline': # draw a polyline on top of the map
  290. if self.polypen:
  291. pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
  292. pdc.SetPen(self.polypen)
  293. ### pdc.DrawLines(coords)
  294. if (len(coords) < 2):
  295. return
  296. i = 1
  297. while i < len(coords):
  298. pdc.DrawLine(wx.Point(coords[i-1][0], coords[i-1][1]),
  299. wx.Point(coords[i][0], coords[i][1]))
  300. i += 1
  301. # get bounding rectangle for polyline
  302. xlist = []
  303. ylist = []
  304. if len(coords) > 0:
  305. for point in coords:
  306. x,y = point
  307. xlist.append(x)
  308. ylist.append(y)
  309. x1=min(xlist)
  310. x2=max(xlist)
  311. y1=min(ylist)
  312. y2=max(ylist)
  313. pdc.SetIdBounds(drawid, wx.Rect(x1,y1,x2,y2))
  314. # self.ovlcoords[drawid] = [x1,y1,x2,y2]
  315. elif pdctype == 'point': # draw point
  316. if self.pen:
  317. pdc.SetPen(self.pen)
  318. pdc.DrawPoint(coords[0], coords[1])
  319. coordsBound = (coords[0] - 5,
  320. coords[1] - 5,
  321. coords[0] + 5,
  322. coords[1] + 5)
  323. pdc.SetIdBounds(drawid, wx.Rect(coordsBound))
  324. # self.ovlcoords[drawid] = coords
  325. elif pdctype == 'text': # draw text on top of map
  326. if not img['active']:
  327. return #only draw active text
  328. if img.has_key('rotation'):
  329. rotation = float(img['rotation'])
  330. else:
  331. rotation = 0.0
  332. w, h = self.GetFullTextExtent(img['text'])[0:2]
  333. pdc.SetFont(img['font'])
  334. pdc.SetTextForeground(img['color'])
  335. coords, w, h = self.TextBounds(img)
  336. if rotation == 0:
  337. pdc.DrawText(img['text'], coords[0], coords[1])
  338. else:
  339. pdc.DrawRotatedText(img['text'], coords[0], coords[1], rotation)
  340. pdc.SetIdBounds(drawid, wx.Rect(coords[0], coords[1], w, h))
  341. pdc.EndDrawing()
  342. self.Refresh()
  343. return drawid
  344. def TextBounds(self, textinfo):
  345. """!
  346. Return text boundary data
  347. @param textinfo text metadata (text, font, color, rotation)
  348. @param coords reference point
  349. """
  350. if textinfo.has_key('rotation'):
  351. rotation = float(textinfo['rotation'])
  352. else:
  353. rotation = 0.0
  354. coords = textinfo['coords']
  355. Debug.msg (4, "BufferedWindow.TextBounds(): text=%s, rotation=%f" % \
  356. (textinfo['text'], rotation))
  357. self.Update()
  358. ### self.Refresh()
  359. self.SetFont(textinfo['font'])
  360. w, h = self.GetTextExtent(textinfo['text'])
  361. if rotation == 0:
  362. coords[2], coords[3] = coords[0] + w, coords[1] + h
  363. return coords, w, h
  364. boxh = math.fabs(math.sin(math.radians(rotation)) * w) + h
  365. boxw = math.fabs(math.cos(math.radians(rotation)) * w) + h
  366. coords[2] = coords[0] + boxw
  367. coords[3] = coords[1] + boxh
  368. return coords, boxw, boxh
  369. def OnPaint(self, event):
  370. """!
  371. Draw PseudoDC's to buffered paint DC
  372. self.pdc for background and decorations
  373. self.pdcVector for vector map which is edited
  374. self.pdcTmp for temporaly drawn objects (self.polycoords)
  375. If self.redrawAll is False on self.pdcTmp content is re-drawn
  376. """
  377. Debug.msg(4, "BufferedWindow.OnPaint(): redrawAll=%s" % self.redrawAll)
  378. dc = wx.BufferedPaintDC(self, self.buffer)
  379. ### dc.SetBackground(wx.Brush("White"))
  380. dc.Clear()
  381. # use PrepareDC to set position correctly
  382. self.PrepareDC(dc)
  383. # create a clipping rect from our position and size
  384. # and update region
  385. rgn = self.GetUpdateRegion().GetBox()
  386. dc.SetClippingRect(rgn)
  387. switchDraw = False
  388. if self.redrawAll is None:
  389. self.redrawAll = True
  390. switchDraw = True
  391. if self.redrawAll: # redraw pdc and pdcVector
  392. # draw to the dc using the calculated clipping rect
  393. self.pdc.DrawToDCClipped(dc, rgn)
  394. # draw vector map layer
  395. if self.pdcVector:
  396. # decorate with GDDC (transparency)
  397. try:
  398. gcdc = wx.GCDC(dc)
  399. self.pdcVector.DrawToDCClipped(gcdc, rgn)
  400. except NotImplementedError, e:
  401. print >> sys.stderr, e
  402. self.pdcVector.DrawToDCClipped(dc, rgn)
  403. self.bufferLast = None
  404. else: # do not redraw pdc and pdcVector
  405. if self.bufferLast is None:
  406. # draw to the dc
  407. self.pdc.DrawToDC(dc)
  408. if self.pdcVector:
  409. # decorate with GDDC (transparency)
  410. try:
  411. gcdc = wx.GCDC(dc)
  412. self.pdcVector.DrawToDC(gcdc)
  413. except NotImplementedError, e:
  414. print >> sys.stderr, e
  415. self.pdcVector.DrawToDC(dc)
  416. # store buffered image
  417. # self.bufferLast = wx.BitmapFromImage(self.buffer.ConvertToImage())
  418. self.bufferLast = dc.GetAsBitmap(wx.Rect(0, 0, self.Map.width, self.Map.height))
  419. pdcLast = self.PseudoDC(vdigit = False)
  420. pdcLast.DrawBitmap(self.bufferLast, 0, 0, False)
  421. pdcLast.DrawToDC(dc)
  422. # draw decorations (e.g. region box)
  423. try:
  424. gcdc = wx.GCDC(dc)
  425. self.pdcDec.DrawToDC(gcdc)
  426. except NotImplementedError, e:
  427. print >> sys.stderr, e
  428. self.pdcDec.DrawToDC(dc)
  429. # draw temporary object on the foreground
  430. ### self.pdcTmp.DrawToDCClipped(dc, rgn)
  431. self.pdcTmp.DrawToDC(dc)
  432. if switchDraw:
  433. self.redrawAll = False
  434. def OnSize(self, event):
  435. """!
  436. Scale map image so that it is
  437. the same size as the Window
  438. """
  439. Debug.msg(3, "BufferedWindow.OnSize():")
  440. # set size of the input image
  441. self.Map.ChangeMapSize(self.GetClientSize())
  442. # align extent based on center point and display resolution
  443. # this causes that image is not resized when display windows is resized
  444. # self.Map.AlignExtentFromDisplay()
  445. # Make new off screen bitmap: this bitmap will always have the
  446. # current drawing in it, so it can be used to save the image to
  447. # a file, or whatever.
  448. self.buffer = wx.EmptyBitmap(max(1, self.Map.width), max(1, self.Map.height))
  449. # get the image to be rendered
  450. self.img = self.GetImage()
  451. # update map display
  452. if self.img and self.Map.width + self.Map.height > 0: # scale image during resize
  453. self.img = self.img.Scale(self.Map.width, self.Map.height)
  454. if len(self.Map.GetListOfLayers()) > 0:
  455. self.UpdateMap()
  456. # re-render image on idle
  457. self.resize = True
  458. # reposition checkbox in statusbar
  459. self.parent.StatusbarReposition()
  460. # update statusbar
  461. self.parent.StatusbarUpdate()
  462. def OnIdle(self, event):
  463. """!
  464. Only re-render a composite map image from GRASS during
  465. idle time instead of multiple times during resizing.
  466. """
  467. if self.resize:
  468. self.UpdateMap(render=True)
  469. event.Skip()
  470. def SaveToFile(self, FileName, FileType):
  471. """!
  472. This draws the psuedo DC to a buffer that
  473. can be saved to a file.
  474. """
  475. dc = wx.BufferedPaintDC(self, self.buffer)
  476. self.pdc.DrawToDC(dc)
  477. if self.pdcVector:
  478. self.pdcVector.DrawToDC(dc)
  479. self.buffer.SaveFile(FileName, FileType)
  480. def GetOverlay(self):
  481. """!
  482. Converts rendered overlay files to wx.Image
  483. Updates self.imagedict
  484. @return list of images
  485. """
  486. imgs = []
  487. for overlay in self.Map.GetListOfLayers(l_type="overlay", l_active=True):
  488. if os.path.isfile(overlay.mapfile) and os.path.getsize(overlay.mapfile):
  489. img = wx.Image(overlay.mapfile, wx.BITMAP_TYPE_ANY)
  490. self.imagedict[img] = { 'id' : overlay.id,
  491. 'layer' : overlay }
  492. imgs.append(img)
  493. return imgs
  494. def GetImage(self):
  495. """!
  496. Converts redered map files to wx.Image
  497. Updates self.imagedict (id=99)
  498. @return wx.Image instance (map composition)
  499. """
  500. imgId = 99
  501. if self.Map.mapfile and os.path.isfile(self.Map.mapfile) and \
  502. os.path.getsize(self.Map.mapfile):
  503. img = wx.Image(self.Map.mapfile, wx.BITMAP_TYPE_ANY)
  504. else:
  505. img = None
  506. self.imagedict[img] = { 'id': imgId }
  507. return img
  508. def UpdateMap(self, render=True, renderVector=True):
  509. """!
  510. Updates the canvas anytime there is a change to the
  511. underlaying images or to the geometry of the canvas.
  512. @param render re-render map composition
  513. @param renderVector re-render vector map layer enabled for editing (used for digitizer)
  514. """
  515. start = time.clock()
  516. self.resize = False
  517. # if len(self.Map.GetListOfLayers()) == 0:
  518. # return False
  519. if self.img is None:
  520. render = True
  521. #
  522. # initialize process bar (only on 'render')
  523. #
  524. if render is True or renderVector is True:
  525. self.parent.statusbarWin['progress'].Show()
  526. if self.parent.statusbarWin['progress'].GetRange() > 0:
  527. self.parent.statusbarWin['progress'].SetValue(1)
  528. #
  529. # render background image if needed
  530. #
  531. # update layer dictionary if there has been a change in layers
  532. if self.tree and self.tree.reorder == True:
  533. self.tree.ReorderLayers()
  534. # reset flag for auto-rendering
  535. if self.tree:
  536. self.tree.rerender = False
  537. if render:
  538. # update display size
  539. self.Map.ChangeMapSize(self.GetClientSize())
  540. if self.parent.statusbarWin['resolution'].IsChecked():
  541. # use computation region resolution for rendering
  542. windres = True
  543. else:
  544. windres = False
  545. self.mapfile = self.Map.Render(force=True, mapWindow=self.parent,
  546. windres=windres)
  547. else:
  548. self.mapfile = self.Map.Render(force=False, mapWindow=self.parent)
  549. self.img = self.GetImage() # id=99
  550. #
  551. # clear pseudoDcs
  552. #
  553. for pdc in (self.pdc,
  554. self.pdcDec,
  555. self.pdcTmp):
  556. pdc.Clear()
  557. pdc.RemoveAll()
  558. #
  559. # draw background map image to PseudoDC
  560. #
  561. if not self.img:
  562. self.Draw(self.pdc, pdctype='clear')
  563. else:
  564. try:
  565. id = self.imagedict[self.img]['id']
  566. except:
  567. return False
  568. self.Draw(self.pdc, self.img, drawid=id)
  569. #
  570. # render vector map layer
  571. #
  572. digitToolbar = self.parent.toolbars['vdigit']
  573. if renderVector and digitToolbar and \
  574. digitToolbar.GetLayer():
  575. # set region
  576. self.parent.digit.driver.UpdateRegion()
  577. # re-calculate threshold for digitization tool
  578. self.parent.digit.driver.GetThreshold()
  579. # draw map
  580. if self.pdcVector:
  581. self.pdcVector.Clear()
  582. self.pdcVector.RemoveAll()
  583. try:
  584. item = self.tree.FindItemByData('maplayer', digitToolbar.GetLayer())
  585. except TypeError:
  586. item = None
  587. if item and self.tree.IsItemChecked(item):
  588. self.parent.digit.driver.DrawMap()
  589. # translate tmp objects (pointer position)
  590. if digitToolbar.GetAction() == 'moveLine':
  591. if hasattr(self, "vdigitMove") and \
  592. self.vdigitMove.has_key('beginDiff'):
  593. # move line
  594. for id in self.vdigitMove['id']:
  595. self.pdcTmp.TranslateId(id,
  596. self.vdigitMove['beginDiff'][0],
  597. self.vdigitMove['beginDiff'][1])
  598. del self.vdigitMove['beginDiff']
  599. #
  600. # render overlays
  601. #
  602. for img in self.GetOverlay():
  603. # draw any active and defined overlays
  604. if self.imagedict[img]['layer'].IsActive():
  605. id = self.imagedict[img]['id']
  606. self.Draw(self.pdc, img=img, drawid=id,
  607. pdctype=self.overlays[id]['pdcType'], coords=self.overlays[id]['coords'])
  608. for id in self.textdict.keys():
  609. self.Draw(self.pdc, img=self.textdict[id], drawid=id,
  610. pdctype='text', coords=[10, 10, 10, 10])
  611. # optionally draw computational extent box
  612. self.DrawCompRegionExtent()
  613. #
  614. # redraw pdcTmp if needed
  615. #
  616. if len(self.polycoords) > 0:
  617. self.DrawLines(self.pdcTmp)
  618. if not self.parent.IsStandalone() and \
  619. self.parent.GetLayerManager().georectifying:
  620. # -> georectifier (redraw GCPs)
  621. if self.parent.toolbars['georect']:
  622. coordtype = 'gcpcoord'
  623. else:
  624. coordtype = 'mapcoord'
  625. self.parent.GetLayerManager().georectifying.DrawGCP(coordtype)
  626. #
  627. # clear measurement
  628. #
  629. if self.mouse["use"] == "measure":
  630. self.ClearLines(pdc=self.pdcTmp)
  631. self.polycoords = []
  632. self.mouse['use'] = 'pointer'
  633. self.mouse['box'] = 'point'
  634. self.mouse['end'] = [0, 0]
  635. self.SetCursor(self.parent.cursors["default"])
  636. stop = time.clock()
  637. #
  638. # hide process bar
  639. #
  640. self.parent.statusbarWin['progress'].Hide()
  641. #
  642. # update statusbar
  643. #
  644. ### self.Map.SetRegion()
  645. self.parent.StatusbarUpdate()
  646. if grass.find_file(name = 'MASK', element = 'cell')['name']:
  647. # mask found
  648. self.parent.statusbarWin['mask'].SetLabel(_('MASK'))
  649. else:
  650. self.parent.statusbarWin['mask'].SetLabel('')
  651. Debug.msg (2, "BufferedWindow.UpdateMap(): render=%s, renderVector=%s -> time=%g" % \
  652. (render, renderVector, (stop-start)))
  653. return True
  654. def DrawCompRegionExtent(self):
  655. """!
  656. Draw computational region extent in the display
  657. Display region is drawn as a blue box inside the computational region,
  658. computational region inside a display region as a red box).
  659. """
  660. if hasattr(self, "regionCoords"):
  661. compReg = self.Map.GetRegion()
  662. dispReg = self.Map.GetCurrentRegion()
  663. reg = None
  664. if self.IsInRegion(dispReg, compReg):
  665. self.polypen = wx.Pen(colour=wx.Colour(0, 0, 255, 128), width=3, style=wx.SOLID)
  666. reg = dispReg
  667. else:
  668. self.polypen = wx.Pen(colour=wx.Colour(255, 0, 0, 128),
  669. width=3, style=wx.SOLID)
  670. reg = compReg
  671. self.regionCoords = []
  672. self.regionCoords.append((reg['w'], reg['n']))
  673. self.regionCoords.append((reg['e'], reg['n']))
  674. self.regionCoords.append((reg['e'], reg['s']))
  675. self.regionCoords.append((reg['w'], reg['s']))
  676. self.regionCoords.append((reg['w'], reg['n']))
  677. # draw region extent
  678. self.DrawLines(pdc=self.pdcDec, polycoords=self.regionCoords)
  679. def IsInRegion(self, region, refRegion):
  680. """!
  681. Test if 'region' is inside of 'refRegion'
  682. @param region input region
  683. @param refRegion reference region (e.g. computational region)
  684. @return True if region is inside of refRegion
  685. @return False
  686. """
  687. if region['s'] >= refRegion['s'] and \
  688. region['n'] <= refRegion['n'] and \
  689. region['w'] >= refRegion['w'] and \
  690. region['e'] <= refRegion['e']:
  691. return True
  692. return False
  693. def EraseMap(self):
  694. """!
  695. Erase the canvas
  696. """
  697. self.Draw(self.pdc, pdctype='clear')
  698. if self.pdcVector:
  699. self.Draw(self.pdcVector, pdctype='clear')
  700. self.Draw(self.pdcDec, pdctype='clear')
  701. self.Draw(self.pdcTmp, pdctype='clear')
  702. def DragMap(self, moveto):
  703. """!
  704. Drag the entire map image for panning.
  705. """
  706. dc = wx.BufferedDC(wx.ClientDC(self))
  707. dc.SetBackground(wx.Brush("White"))
  708. dc.Clear()
  709. self.dragimg = wx.DragImage(self.buffer)
  710. self.dragimg.BeginDrag((0, 0), self)
  711. self.dragimg.GetImageRect(moveto)
  712. self.dragimg.Move(moveto)
  713. self.dragimg.DoDrawImage(dc, moveto)
  714. self.dragimg.EndDrag()
  715. return True
  716. def DragItem(self, id, event):
  717. """!
  718. Drag an overlay decoration item
  719. """
  720. if id == 99 or id == '' or id == None: return
  721. Debug.msg (5, "BufferedWindow.DragItem(): id=%d" % id)
  722. x, y = self.lastpos
  723. dx = event.GetX() - x
  724. dy = event.GetY() - y
  725. self.pdc.SetBackground(wx.Brush(self.GetBackgroundColour()))
  726. r = self.pdc.GetIdBounds(id)
  727. ### FIXME in vdigit/pseudodc.i
  728. if type(r) is list:
  729. r = wx.Rect(r[0], r[1], r[2], r[3])
  730. if id > 100: # text dragging
  731. rtop = (r[0],r[1]-r[3],r[2],r[3])
  732. r = r.Union(rtop)
  733. rleft = (r[0]-r[2],r[1],r[2],r[3])
  734. r = r.Union(rleft)
  735. self.pdc.TranslateId(id, dx, dy)
  736. r2 = self.pdc.GetIdBounds(id)
  737. if type(r2) is list:
  738. r2 = wx.Rect(r[0], r[1], r[2], r[3])
  739. if id > 100: # text
  740. self.textdict[id]['coords'] = r2
  741. r = r.Union(r2)
  742. r.Inflate(4,4)
  743. self.RefreshRect(r, False)
  744. self.lastpos = (event.GetX(), event.GetY())
  745. def MouseDraw(self, pdc=None, begin=None, end=None):
  746. """!
  747. Mouse box or line from 'begin' to 'end'
  748. If not given from self.mouse['begin'] to self.mouse['end'].
  749. """
  750. self.redrawAll = False
  751. if not pdc:
  752. return
  753. if begin is None:
  754. begin = self.mouse['begin']
  755. if end is None:
  756. end = self.mouse['end']
  757. Debug.msg (5, "BufferedWindow.MouseDraw(): use=%s, box=%s, begin=%f,%f, end=%f,%f" % \
  758. (self.mouse['use'], self.mouse['box'],
  759. begin[0], begin[1], end[0], end[1]))
  760. if self.mouse['box'] == "box":
  761. boxid = wx.ID_NEW
  762. mousecoords = [begin[0], begin[1],
  763. end[0], end[1]]
  764. r = pdc.GetIdBounds(boxid)
  765. if type(r) is list:
  766. r = wx.Rect(r[0], r[1], r[2], r[3])
  767. r.Inflate(4, 4)
  768. try:
  769. pdc.ClearId(boxid)
  770. except:
  771. pass
  772. self.RefreshRect(r, False)
  773. pdc.SetId(boxid)
  774. self.Draw(pdc, drawid=boxid, pdctype='box', coords=mousecoords)
  775. elif self.mouse['box'] == "line" or self.mouse['box'] == 'point':
  776. self.lineid = wx.ID_NEW
  777. mousecoords = [begin[0], begin[1], \
  778. end[0], end[1]]
  779. x1=min(begin[0],end[0])
  780. x2=max(begin[0],end[0])
  781. y1=min(begin[1],end[1])
  782. y2=max(begin[1],end[1])
  783. r = wx.Rect(x1,y1,x2-x1,y2-y1)
  784. r.Inflate(4,4)
  785. try:
  786. pdc.ClearId(self.lineid)
  787. except:
  788. pass
  789. self.RefreshRect(r, False)
  790. pdc.SetId(self.lineid)
  791. self.Draw(pdc, drawid=self.lineid, pdctype='line', coords=mousecoords)
  792. def DrawLines(self, pdc=None, polycoords=None):
  793. """!
  794. Draw polyline in PseudoDC
  795. Set self.pline to wx.NEW_ID + 1
  796. polycoords - list of polyline vertices, geographical coordinates
  797. (if not given, self.polycoords is used)
  798. """
  799. if not pdc:
  800. pdc = self.pdcTmp
  801. if not polycoords:
  802. polycoords = self.polycoords
  803. if len(polycoords) > 0:
  804. self.plineid = wx.ID_NEW + 1
  805. # convert from EN to XY
  806. coords = []
  807. for p in polycoords:
  808. coords.append(self.Cell2Pixel(p))
  809. self.Draw(pdc, drawid=self.plineid, pdctype='polyline', coords=coords)
  810. Debug.msg (4, "BufferedWindow.DrawLines(): coords=%s, id=%s" % \
  811. (coords, self.plineid))
  812. return self.plineid
  813. return -1
  814. def DrawCross(self, pdc, coords, size, rotation=0,
  815. text=None, textAlign='lr', textOffset=(5, 5)):
  816. """!Draw cross in PseudoDC
  817. @todo implement rotation
  818. @param pdc PseudoDC
  819. @param coord center coordinates
  820. @param rotation rotate symbol
  821. @param text draw also text (text, font, color, rotation)
  822. @param textAlign alignment (default 'lower-right')
  823. @textOffset offset for text (from center point)
  824. """
  825. Debug.msg(4, "BufferedWindow.DrawCross(): pdc=%s, coords=%s, size=%d" % \
  826. (pdc, coords, size))
  827. coordsCross = ((coords[0] - size, coords[1], coords[0] + size, coords[1]),
  828. (coords[0], coords[1] - size, coords[0], coords[1] + size))
  829. self.lineid = wx.NewId()
  830. for lineCoords in coordsCross:
  831. self.Draw(pdc, drawid=self.lineid, pdctype='line', coords=lineCoords)
  832. if not text:
  833. return self.lineid
  834. if textAlign == 'ul':
  835. coord = [coords[0] - textOffset[0], coords[1] - textOffset[1], 0, 0]
  836. elif textAlign == 'ur':
  837. coord = [coords[0] + textOffset[0], coords[1] - textOffset[1], 0, 0]
  838. elif textAlign == 'lr':
  839. coord = [coords[0] + textOffset[0], coords[1] + textOffset[1], 0, 0]
  840. else:
  841. coord = [coords[0] - textOffset[0], coords[1] + textOffset[1], 0, 0]
  842. self.Draw(pdc, img=text,
  843. pdctype='text', coords=coord)
  844. return self.lineid
  845. def MouseActions(self, event):
  846. """!
  847. Mouse motion and button click notifier
  848. """
  849. if not self.processMouse:
  850. return
  851. ### if self.redrawAll is False:
  852. ### self.redrawAll = True
  853. # zoom with mouse wheel
  854. if event.GetWheelRotation() != 0:
  855. self.OnMouseWheel(event)
  856. # left mouse button pressed
  857. elif event.LeftDown():
  858. self.OnLeftDown(event)
  859. # left mouse button released
  860. elif event.LeftUp():
  861. self.OnLeftUp(event)
  862. # dragging
  863. elif event.Dragging():
  864. self.OnDragging(event)
  865. # double click
  866. elif event.ButtonDClick():
  867. self.OnButtonDClick(event)
  868. # middle mouse button pressed
  869. elif event.MiddleDown():
  870. self.OnMiddleDown(event)
  871. # right mouse button pressed
  872. elif event.RightDown():
  873. self.OnRightDown(event)
  874. # right mouse button released
  875. elif event.RightUp():
  876. self.OnRightUp(event)
  877. elif event.Moving():
  878. self.OnMouseMoving(event)
  879. # event.Skip()
  880. def OnMouseWheel(self, event):
  881. """!
  882. Mouse wheel moved
  883. """
  884. self.processMouse = False
  885. current = event.GetPositionTuple()[:]
  886. wheel = event.GetWheelRotation()
  887. Debug.msg (5, "BufferedWindow.MouseAction(): wheel=%d" % wheel)
  888. # zoom 1/2 of the screen, centered to current mouse position (TODO: settings)
  889. begin = (current[0] - self.Map.width / 4,
  890. current[1] - self.Map.height / 4)
  891. end = (current[0] + self.Map.width / 4,
  892. current[1] + self.Map.height / 4)
  893. if wheel > 0:
  894. zoomtype = 1
  895. else:
  896. zoomtype = -1
  897. # zoom
  898. self.Zoom(begin, end, zoomtype)
  899. # redraw map
  900. self.UpdateMap()
  901. ### self.OnPaint(None)
  902. # update statusbar
  903. self.parent.StatusbarUpdate()
  904. self.Refresh()
  905. self.processMouse = True
  906. # event.Skip()
  907. def OnDragging(self, event):
  908. """!
  909. Mouse dragging with left button down
  910. """
  911. Debug.msg (5, "BufferedWindow.MouseAction(): Dragging")
  912. current = event.GetPositionTuple()[:]
  913. previous = self.mouse['begin']
  914. move = (current[0] - previous[0],
  915. current[1] - previous[1])
  916. digitToolbar = self.parent.toolbars['vdigit']
  917. # dragging or drawing box with left button
  918. if self.mouse['use'] == 'pan':
  919. self.DragMap(move)
  920. # dragging decoration overlay item
  921. elif (self.mouse['use'] == 'pointer' and
  922. not digitToolbar and
  923. self.dragid != None):
  924. self.DragItem(self.dragid, event)
  925. # dragging anything else - rubber band box or line
  926. else:
  927. if (self.mouse['use'] == 'pointer' and
  928. not digitToolbar): return
  929. self.mouse['end'] = event.GetPositionTuple()[:]
  930. digitClass = self.parent.digit
  931. if (event.LeftIsDown() and
  932. not (digitToolbar and
  933. digitToolbar.GetAction() in ("moveLine",) and
  934. digitClass.driver.GetSelected() > 0)):
  935. # draw box only when left mouse button is pressed
  936. self.MouseDraw(pdc=self.pdcTmp)
  937. # event.Skip()
  938. def OnLeftDownVDigitAddLine(self, event):
  939. """!
  940. Left mouse button down - vector digitizer add new line
  941. action
  942. """
  943. digitToolbar = self.parent.toolbars['vdigit']
  944. digitClass = self.parent.digit
  945. try:
  946. mapLayer = digitToolbar.GetLayer().GetName()
  947. except:
  948. return
  949. if digitToolbar.GetAction('type') in ["point", "centroid"]:
  950. # add new point
  951. if digitToolbar.GetAction('type') == 'point':
  952. point = True
  953. else:
  954. point = False
  955. east, north = self.Pixel2Cell(self.mouse['begin'])
  956. fid = digitClass.AddPoint(mapLayer, point, east, north)
  957. if fid < 0:
  958. return
  959. self.UpdateMap(render=False) # redraw map
  960. # add new record into atribute table
  961. if UserSettings.Get(group='vdigit', key="addRecord", subkey='enabled') is True:
  962. # select attributes based on layer and category
  963. cats = { fid : {
  964. UserSettings.Get(group='vdigit', key="layer", subkey='value') :
  965. (UserSettings.Get(group='vdigit', key="category", subkey='value'), )
  966. }}
  967. posWindow = self.ClientToScreen((self.mouse['end'][0] + self.dialogOffset,
  968. self.mouse['end'][1] + self.dialogOffset))
  969. addRecordDlg = dbm_dialogs.DisplayAttributesDialog(parent=self, map=mapLayer,
  970. cats=cats,
  971. pos=posWindow,
  972. action="add")
  973. if not point:
  974. self.__geomAttrb(fid, addRecordDlg, 'area', digitClass,
  975. digitToolbar.GetLayer())
  976. self.__geomAttrb(fid, addRecordDlg, 'perimeter', digitClass,
  977. digitToolbar.GetLayer())
  978. if addRecordDlg.mapDBInfo and \
  979. addRecordDlg.ShowModal() == wx.ID_OK:
  980. sqlfile = tempfile.NamedTemporaryFile(mode="w")
  981. for sql in addRecordDlg.GetSQLString():
  982. sqlfile.file.write(sql + ";\n")
  983. sqlfile.file.flush()
  984. gcmd.RunCommand('db.execute',
  985. parent = self,
  986. quiet = True,
  987. input = sqlfile.name)
  988. if addRecordDlg.mapDBInfo:
  989. self.__updateATM()
  990. elif digitToolbar.GetAction('type') in ["line", "boundary"]:
  991. # add new point to the line
  992. self.polycoords.append(self.Pixel2Cell(event.GetPositionTuple()[:]))
  993. self.DrawLines(pdc=self.pdcTmp)
  994. def __geomAttrb(self, fid, dialog, attrb, digit, mapLayer):
  995. """!Trac geometry attributes?"""
  996. item = self.tree.FindItemByData('maplayer', mapLayer)
  997. vdigit = self.tree.GetPyData(item)[0]['vdigit']
  998. if vdigit and \
  999. vdigit.has_key('geomAttr') and \
  1000. vdigit['geomAttr'].has_key(attrb):
  1001. val = -1
  1002. if attrb == 'length':
  1003. val = digit.GetLineLength(fid)
  1004. type = attrb
  1005. elif attrb == 'area':
  1006. val = digit.GetAreaSize(fid)
  1007. type = attrb
  1008. elif attrb == 'perimeter':
  1009. val = digit.GetAreaPerimeter(fid)
  1010. type = 'length'
  1011. if val > 0:
  1012. layer = int(UserSettings.Get(group='vdigit', key="layer", subkey='value'))
  1013. column = vdigit['geomAttr'][attrb]['column']
  1014. val = UnitsConvertValue(val, type, vdigit['geomAttr'][attrb]['units'])
  1015. dialog.SetColumnValue(layer, column, val)
  1016. dialog.OnReset()
  1017. def __geomAttrbUpdate(self, fids):
  1018. """!Update geometry atrributes of currently selected features
  1019. @param fid list feature id
  1020. """
  1021. mapLayer = self.parent.toolbars['vdigit'].GetLayer()
  1022. vectorName = mapLayer.GetName()
  1023. digit = self.parent.digit
  1024. item = self.tree.FindItemByData('maplayer', mapLayer)
  1025. vdigit = self.tree.GetPyData(item)[0]['vdigit']
  1026. if vdigit is None or not vdigit.has_key('geomAttr'):
  1027. return
  1028. dbInfo = gselect.VectorDBInfo(vectorName)
  1029. sqlfile = tempfile.NamedTemporaryFile(mode="w")
  1030. for fid in fids:
  1031. for layer, cats in digit.GetLineCats(fid).iteritems():
  1032. table = dbInfo.GetTable(layer)
  1033. for attrb, item in vdigit['geomAttr'].iteritems():
  1034. val = -1
  1035. if attrb == 'length':
  1036. val = digit.GetLineLength(fid)
  1037. type = attrb
  1038. elif attrb == 'area':
  1039. val = digit.GetAreaSize(fid)
  1040. type = attrb
  1041. elif attrb == 'perimeter':
  1042. val = digit.GetAreaPerimeter(fid)
  1043. type = 'length'
  1044. if val < 0:
  1045. continue
  1046. val = UnitsConvertValue(val, type, item['units'])
  1047. for cat in cats:
  1048. sqlfile.write('UPDATE %s SET %s = %f WHERE %s = %d;\n' % \
  1049. (table, item['column'], val,
  1050. dbInfo.GetKeyColumn(layer), cat))
  1051. sqlfile.file.flush()
  1052. gcmd.RunCommand('db.execute',
  1053. parent = True,
  1054. quiet = True,
  1055. input = sqlfile.name)
  1056. def __updateATM(self):
  1057. """!Update open Attribute Table Manager
  1058. @todo: use AddDataRow() instead
  1059. """
  1060. # update ATM
  1061. digitToolbar = self.parent.toolbars['vdigit']
  1062. digitVector = digitToolbar.GetLayer().GetName()
  1063. for atm in self.lmgr.dialogs['atm']:
  1064. atmVector = atm.GetVectorName()
  1065. if atmVector == digitVector:
  1066. layer = UserSettings.Get(group='vdigit', key="layer", subkey='value')
  1067. # TODO: use AddDataRow instead
  1068. atm.LoadData(layer)
  1069. def OnLeftDownVDigitEditLine(self, event):
  1070. """!
  1071. Left mouse button down - vector digitizer edit linear feature
  1072. - add new vertex.
  1073. """
  1074. digitToolbar = self.parent.toolbars['vdigit']
  1075. digitClass = self.parent.digit
  1076. self.polycoords.append(self.Pixel2Cell(self.mouse['begin']))
  1077. self.vdigitMove['id'].append(wx.NewId())
  1078. self.DrawLines(pdc=self.pdcTmp)
  1079. def OnLeftDownVDigitMoveLine(self, event):
  1080. """!
  1081. Left mouse button down - vector digitizer move feature/vertex,
  1082. edit linear feature
  1083. """
  1084. digitToolbar = self.parent.toolbars['vdigit']
  1085. digitClass = self.parent.digit
  1086. self.vdigitMove = {}
  1087. # geographic coordinates of initial position (left-down)
  1088. self.vdigitMove['begin'] = None
  1089. # list of ids to modify
  1090. self.vdigitMove['id'] = []
  1091. # ids geographic coordinates
  1092. self.vdigitMove['coord'] = {}
  1093. if digitToolbar.GetAction() in ["moveVertex", "editLine"]:
  1094. # set pen
  1095. pcolor = UserSettings.Get(group='vdigit', key="symbol",
  1096. subkey=["highlight", "color"])
  1097. self.pen = self.polypen = wx.Pen(colour=pcolor,
  1098. width=2, style=wx.SHORT_DASH)
  1099. self.pdcTmp.SetPen(self.polypen)
  1100. def OnLeftDownVDigitDisplayCA(self, event):
  1101. """!
  1102. Left mouse button down - vector digitizer display categories
  1103. or attributes action
  1104. """
  1105. digitToolbar = self.parent.toolbars['vdigit']
  1106. digitClass = self.parent.digit
  1107. try:
  1108. mapLayer = digitToolbar.GetLayer().GetName()
  1109. except:
  1110. return
  1111. coords = self.Pixel2Cell(self.mouse['begin'])
  1112. # unselect
  1113. digitClass.driver.SetSelected([])
  1114. # select feature by point
  1115. cats = {}
  1116. if digitClass.driver.SelectLineByPoint(coords,
  1117. digitClass.GetSelectType()) is None:
  1118. return
  1119. if UserSettings.Get(group='vdigit', key='checkForDupl',
  1120. subkey='enabled'):
  1121. lines = digitClass.driver.GetSelected()
  1122. else:
  1123. lines = (digitClass.driver.GetSelected()[0],) # only first found
  1124. for line in lines:
  1125. cats[line] = digitClass.GetLineCats(line)
  1126. posWindow = self.ClientToScreen((self.mouse['end'][0] + self.dialogOffset,
  1127. self.mouse['end'][1] + self.dialogOffset))
  1128. if digitToolbar.GetAction() == "displayAttrs":
  1129. # select attributes based on coordinates (all layers)
  1130. if self.parent.dialogs['attributes'] is None:
  1131. self.parent.dialogs['attributes'] = \
  1132. dbm_dialogs.DisplayAttributesDialog(parent=self, map=mapLayer,
  1133. cats=cats,
  1134. action="update")
  1135. else:
  1136. # upgrade dialog
  1137. self.parent.dialogs['attributes'].UpdateDialog(cats=cats)
  1138. if self.parent.dialogs['attributes']:
  1139. if len(cats.keys()) > 0:
  1140. # highlight feature & re-draw map
  1141. if not self.parent.dialogs['attributes'].IsShown():
  1142. self.parent.dialogs['attributes'].Show()
  1143. else:
  1144. if self.parent.dialogs['attributes'] and \
  1145. self.parent.dialogs['attributes'].IsShown():
  1146. self.parent.dialogs['attributes'].Hide()
  1147. else: # displayCats
  1148. if self.parent.dialogs['category'] is None:
  1149. # open new dialog
  1150. dlg = VDigitCategoryDialog(parent=self,
  1151. map=mapLayer,
  1152. cats=cats,
  1153. pos=posWindow,
  1154. title=_("Update categories"))
  1155. self.parent.dialogs['category'] = dlg
  1156. else:
  1157. # update currently open dialog
  1158. self.parent.dialogs['category'].UpdateDialog(cats=cats)
  1159. if self.parent.dialogs['category']:
  1160. if len(cats.keys()) > 0:
  1161. # highlight feature & re-draw map
  1162. if not self.parent.dialogs['category'].IsShown():
  1163. self.parent.dialogs['category'].Show()
  1164. else:
  1165. if self.parent.dialogs['category'].IsShown():
  1166. self.parent.dialogs['category'].Hide()
  1167. self.UpdateMap(render=False)
  1168. def OnLeftDownVDigitCopyCA(self, event):
  1169. """!
  1170. Left mouse button down - vector digitizer copy categories
  1171. or attributes action
  1172. """
  1173. digitToolbar = self.parent.toolbars['vdigit']
  1174. digitClass = self.parent.digit
  1175. if not hasattr(self, "copyCatsList"):
  1176. self.copyCatsList = []
  1177. else:
  1178. self.copyCatsIds = []
  1179. self.mouse['box'] = 'box'
  1180. def OnLeftDownVDigitCopyLine(self, event):
  1181. """!
  1182. Left mouse button down - vector digitizer copy lines action
  1183. """
  1184. digitToolbar = self.parent.toolbars['vdigit']
  1185. digitClass = self.parent.digit
  1186. if not hasattr(self, "copyIds"):
  1187. self.copyIds = []
  1188. self.layerTmp = None
  1189. def OnLeftDownVDigitBulkLine(self, event):
  1190. """!
  1191. Left mouse button down - vector digitizer label 3d vector
  1192. lines
  1193. """
  1194. digitToolbar = self.parent.toolbars['vdigit']
  1195. digitClass = self.parent.digit
  1196. if len(self.polycoords) > 1: # start new line
  1197. self.polycoords = []
  1198. self.ClearLines(pdc=self.pdcTmp)
  1199. self.polycoords.append(self.Pixel2Cell(event.GetPositionTuple()[:]))
  1200. if len(self.polycoords) == 1:
  1201. begin = self.Pixel2Cell(self.polycoords[-1])
  1202. end = self.Pixel2Cell(self.mouse['end'])
  1203. else:
  1204. end = self.Pixel2Cell(self.polycoords[-1])
  1205. begin = self.Pixel2Cell(self.mouse['begin'])
  1206. self.DrawLines(self.pdcTmp, polycoords = (begin, end))
  1207. def OnLeftDown(self, event):
  1208. """!
  1209. Left mouse button pressed
  1210. """
  1211. Debug.msg (5, "BufferedWindow.OnLeftDown(): use=%s" % \
  1212. self.mouse["use"])
  1213. self.mouse['begin'] = event.GetPositionTuple()[:]
  1214. if self.mouse["use"] in ["measure", "profile"]:
  1215. # measure or profile
  1216. if len(self.polycoords) == 0:
  1217. self.mouse['end'] = self.mouse['begin']
  1218. self.polycoords.append(self.Pixel2Cell(self.mouse['begin']))
  1219. self.ClearLines(pdc=self.pdcTmp)
  1220. else:
  1221. self.mouse['begin'] = self.mouse['end']
  1222. elif self.mouse['use'] == 'zoom':
  1223. pass
  1224. #
  1225. # vector digizer
  1226. #
  1227. elif self.mouse["use"] == "pointer" and \
  1228. self.parent.toolbars['vdigit']:
  1229. digitToolbar = self.parent.toolbars['vdigit']
  1230. digitClass = self.parent.digit
  1231. try:
  1232. mapLayer = digitToolbar.GetLayer().GetName()
  1233. except:
  1234. wx.MessageBox(parent=self,
  1235. message=_("No vector map selected for editing."),
  1236. caption=_("Vector digitizer"),
  1237. style=wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
  1238. event.Skip()
  1239. return
  1240. if digitToolbar.GetAction() not in ("moveVertex",
  1241. "addVertex",
  1242. "removeVertex",
  1243. "editLine"):
  1244. # set pen
  1245. self.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
  1246. self.polypen = wx.Pen(colour='dark green', width=2, style=wx.SOLID)
  1247. if digitToolbar.GetAction() in ("addVertex",
  1248. "removeVertex",
  1249. "splitLines"):
  1250. # unselect
  1251. digitClass.driver.SetSelected([])
  1252. if digitToolbar.GetAction() == "addLine":
  1253. self.OnLeftDownVDigitAddLine(event)
  1254. elif digitToolbar.GetAction() == "editLine" and \
  1255. hasattr(self, "vdigitMove"):
  1256. self.OnLeftDownVDigitEditLine(event)
  1257. elif digitToolbar.GetAction() in ("moveLine",
  1258. "moveVertex",
  1259. "editLine") and \
  1260. not hasattr(self, "vdigitMove"):
  1261. self.OnLeftDownVDigitMoveLine(event)
  1262. elif digitToolbar.GetAction() in ("displayAttrs"
  1263. "displayCats"):
  1264. self.OnLeftDownVDigitDisplayCA(event)
  1265. elif digitToolbar.GetAction() in ("copyCats",
  1266. "copyAttrs"):
  1267. self.OnLeftDownVDigitCopyCA(event)
  1268. elif digitToolbar.GetAction() == "copyLine":
  1269. self.OnLeftDownVDigitCopyLine(event)
  1270. elif digitToolbar.GetAction() == "zbulkLine":
  1271. self.OnLeftDownVDigitBulkLine(event)
  1272. elif self.mouse['use'] == 'pointer':
  1273. # get decoration or text id
  1274. self.idlist = []
  1275. self.dragid = ''
  1276. self.lastpos = self.mouse['begin']
  1277. idlist = self.pdc.FindObjects(self.lastpos[0], self.lastpos[1],
  1278. self.hitradius)
  1279. if 99 in idlist:
  1280. idlist.remove(99)
  1281. if idlist != []:
  1282. self.dragid = idlist[0] #drag whatever is on top
  1283. else:
  1284. pass
  1285. event.Skip()
  1286. def OnLeftUpVDigitVarious(self, event):
  1287. """!
  1288. Left mouse button up - vector digitizer various actions
  1289. """
  1290. digitToolbar = self.parent.toolbars['vdigit']
  1291. digitClass = self.parent.digit
  1292. pos1 = self.Pixel2Cell(self.mouse['begin'])
  1293. pos2 = self.Pixel2Cell(self.mouse['end'])
  1294. nselected = 0
  1295. # -> delete line || move line || move vertex
  1296. if digitToolbar.GetAction() in ("moveVertex",
  1297. "editLine"):
  1298. if len(digitClass.driver.GetSelected()) == 0:
  1299. nselected = digitClass.driver.SelectLineByPoint(pos1, type=VDigit_Lines_Type)
  1300. if digitToolbar.GetAction() == "editLine":
  1301. try:
  1302. selVertex = digitClass.driver.GetSelectedVertex(pos1)[0]
  1303. except IndexError:
  1304. selVertex = None
  1305. if selVertex:
  1306. # self.UpdateMap(render=False)
  1307. ids = digitClass.driver.GetSelected(grassId=False)
  1308. # move this line to tmp layer
  1309. self.polycoords = []
  1310. for id in ids:
  1311. if id % 2: # register only vertices
  1312. e, n = self.Pixel2Cell(self.pdcVector.GetIdBounds(id)[0:2])
  1313. self.polycoords.append((e, n))
  1314. digitClass.driver.DrawSelected(False)
  1315. if selVertex < ids[-1] / 2:
  1316. # choose first or last node of line
  1317. self.vdigitMove['id'].reverse()
  1318. self.polycoords.reverse()
  1319. else:
  1320. # unselect
  1321. digitClass.driver.SetSelected([])
  1322. del self.vdigitMove
  1323. self.UpdateMap(render=False)
  1324. elif digitToolbar.GetAction() in ("copyCats",
  1325. "copyAttrs"):
  1326. if not hasattr(self, "copyCatsIds"):
  1327. # 'from' -> select by point
  1328. nselected = digitClass.driver.SelectLineByPoint(pos1, digitClass.GetSelectType())
  1329. if nselected:
  1330. self.copyCatsList = digitClass.driver.GetSelected()
  1331. else:
  1332. # -> 'to' -> select by bbox
  1333. digitClass.driver.SetSelected([])
  1334. # return number of selected features (by box/point)
  1335. nselected = digitClass.driver.SelectLinesByBox(pos1, pos2,
  1336. digitClass.GetSelectType())
  1337. if nselected == 0:
  1338. if digitClass.driver.SelectLineByPoint(pos1,
  1339. digitClass.GetSelectType()) is not None:
  1340. nselected = 1
  1341. if nselected > 0:
  1342. self.copyCatsIds = digitClass.driver.GetSelected()
  1343. elif digitToolbar.GetAction() == "queryLine":
  1344. selected = digitClass.SelectLinesByQuery(pos1, pos2)
  1345. nselected = len(selected)
  1346. if nselected > 0:
  1347. digitClass.driver.SetSelected(selected)
  1348. else:
  1349. # -> moveLine || deleteLine, etc. (select by point/box)
  1350. if digitToolbar.GetAction() == 'moveLine' and \
  1351. len(digitClass.driver.GetSelected()) > 0:
  1352. nselected = 0
  1353. else:
  1354. if digitToolbar.GetAction() == 'moveLine':
  1355. drawSeg = True
  1356. else:
  1357. drawSeg = False
  1358. nselected = digitClass.driver.SelectLinesByBox(pos1, pos2,
  1359. digitClass.GetSelectType(),
  1360. drawSeg)
  1361. if nselected == 0:
  1362. if digitClass.driver.SelectLineByPoint(pos1,
  1363. digitClass.GetSelectType()) is not None:
  1364. nselected = 1
  1365. if nselected > 0:
  1366. if digitToolbar.GetAction() in ("moveLine",
  1367. "moveVertex"):
  1368. # get pseudoDC id of objects which should be redrawn
  1369. if digitToolbar.GetAction() == "moveLine":
  1370. # -> move line
  1371. self.vdigitMove['id'] = digitClass.driver.GetSelected(grassId=False)
  1372. self.vdigitMove['coord'] = digitClass.driver.GetSelectedCoord()
  1373. else: # moveVertex
  1374. self.vdigitMove['id'] = digitClass.driver.GetSelectedVertex(pos1)
  1375. if len(self.vdigitMove['id']) == 0: # no vertex found
  1376. digitClass.driver.SetSelected([])
  1377. #
  1378. # check for duplicates
  1379. #
  1380. if UserSettings.Get(group='vdigit', key='checkForDupl', subkey='enabled') is True:
  1381. dupl = digitClass.driver.GetDuplicates()
  1382. self.UpdateMap(render=False)
  1383. if dupl:
  1384. posWindow = self.ClientToScreen((self.mouse['end'][0] + self.dialogOffset,
  1385. self.mouse['end'][1] + self.dialogOffset))
  1386. dlg = VDigitDuplicatesDialog(parent=self, data=dupl, pos=posWindow)
  1387. if dlg.ShowModal() == wx.ID_OK:
  1388. digitClass.driver.UnSelect(dlg.GetUnSelected())
  1389. # update selected
  1390. self.UpdateMap(render=False)
  1391. if digitToolbar.GetAction() != "editLine":
  1392. # -> move line || move vertex
  1393. self.UpdateMap(render=False)
  1394. else: # no vector object found
  1395. if not (digitToolbar.GetAction() in ("moveLine",
  1396. "moveVertex") and \
  1397. len(self.vdigitMove['id']) > 0):
  1398. # avoid left-click when features are already selected
  1399. self.UpdateMap(render=False, renderVector=False)
  1400. def OnLeftUpVDigitModifyLine(self, event):
  1401. """!
  1402. Left mouse button up - vector digitizer split line, add/remove
  1403. vertex action
  1404. """
  1405. digitToolbar = self.parent.toolbars['vdigit']
  1406. digitClass = self.parent.digit
  1407. pos1 = self.Pixel2Cell(self.mouse['begin'])
  1408. pointOnLine = digitClass.driver.SelectLineByPoint(pos1,
  1409. type=VDigit_Lines_Type)
  1410. if not pointOnLine:
  1411. return
  1412. if digitToolbar.GetAction() in ["splitLine", "addVertex"]:
  1413. self.UpdateMap(render=False) # highlight object
  1414. self.DrawCross(pdc=self.pdcTmp, coords=self.Cell2Pixel(pointOnLine),
  1415. size=5)
  1416. else: # removeVertex
  1417. # get only id of vertex
  1418. try:
  1419. id = digitClass.driver.GetSelectedVertex(pos1)[0]
  1420. except IndexError:
  1421. id = None
  1422. if id:
  1423. x, y = self.pdcVector.GetIdBounds(id)[0:2]
  1424. self.pdcVector.RemoveId(id)
  1425. self.UpdateMap(render=False) # highlight object
  1426. self.DrawCross(pdc=self.pdcTmp, coords=(x, y),
  1427. size=5)
  1428. else:
  1429. # unselect
  1430. digitClass.driver.SetSelected([])
  1431. self.UpdateMap(render=False)
  1432. def OnLeftUpVDigitCopyLine(self, event):
  1433. """!
  1434. Left mouse button up - vector digitizer copy feature action
  1435. """
  1436. digitToolbar = self.parent.toolbars['vdigit']
  1437. digitClass = self.parent.digit
  1438. pos1 = self.Pixel2Cell(self.mouse['begin'])
  1439. pos2 = self.Pixel2Cell(self.mouse['end'])
  1440. if UserSettings.Get(group='vdigit', key='bgmap',
  1441. subkey='value', internal=True) == '':
  1442. # no background map -> copy from current vector map layer
  1443. nselected = digitClass.driver.SelectLinesByBox(pos1, pos2,
  1444. digitClass.GetSelectType())
  1445. if nselected > 0:
  1446. # highlight selected features
  1447. self.UpdateMap(render=False)
  1448. else:
  1449. self.UpdateMap(render=False, renderVector=False)
  1450. else:
  1451. # copy features from background map
  1452. self.copyIds += digitClass.SelectLinesFromBackgroundMap(pos1, pos2)
  1453. if len(self.copyIds) > 0:
  1454. color = UserSettings.Get(group='vdigit', key='symbol',
  1455. subkey=['highlight', 'color'])
  1456. colorStr = str(color[0]) + ":" + \
  1457. str(color[1]) + ":" + \
  1458. str(color[2])
  1459. dVectTmp = ['d.vect',
  1460. 'map=%s' % UserSettings.Get(group='vdigit', key='bgmap',
  1461. subkey='value', internal=True),
  1462. 'cats=%s' % utils.ListOfCatsToRange(self.copyIds),
  1463. '-i',
  1464. 'color=%s' % colorStr,
  1465. 'fcolor=%s' % colorStr,
  1466. 'type=point,line,boundary,centroid',
  1467. 'width=2']
  1468. if not self.layerTmp:
  1469. self.layerTmp = self.Map.AddLayer(type='vector',
  1470. name=globalvar.QUERYLAYER,
  1471. command=dVectTmp)
  1472. else:
  1473. self.layerTmp.SetCmd(dVectTmp)
  1474. self.UpdateMap(render=True, renderVector=False)
  1475. else:
  1476. self.UpdateMap(render=False, renderVector=False)
  1477. self.redrawAll = None
  1478. def OnLeftUpVDigitBulkLine(self, event):
  1479. """!
  1480. Left mouse button up - vector digitizer z-bulk line action
  1481. """
  1482. digitToolbar = self.parent.toolbars['vdigit']
  1483. digitClass = self.parent.digit
  1484. # select lines to be labeled
  1485. pos1 = self.polycoords[0]
  1486. pos2 = self.polycoords[1]
  1487. nselected = digitClass.driver.SelectLinesByBox(pos1, pos2,
  1488. digitClass.GetSelectType())
  1489. if nselected > 0:
  1490. # highlight selected features
  1491. self.UpdateMap(render=False)
  1492. self.DrawLines(pdc=self.pdcTmp) # redraw temp line
  1493. else:
  1494. self.UpdateMap(render=False, renderVector=False)
  1495. def OnLeftUpVDigitConnectLine(self, event):
  1496. """!
  1497. Left mouse button up - vector digitizer connect line action
  1498. """
  1499. digitToolbar = self.parent.toolbars['vdigit']
  1500. digitClass = self.parent.digit
  1501. if len(digitClass.driver.GetSelected()) > 0:
  1502. self.UpdateMap(render=False)
  1503. def OnLeftUp(self, event):
  1504. """!
  1505. Left mouse button released
  1506. """
  1507. Debug.msg (5, "BufferedWindow.OnLeftUp(): use=%s" % \
  1508. self.mouse["use"])
  1509. self.mouse['end'] = event.GetPositionTuple()[:]
  1510. if self.mouse['use'] in ["zoom", "pan"]:
  1511. # set region in zoom or pan
  1512. begin = self.mouse['begin']
  1513. end = self.mouse['end']
  1514. if self.mouse['use'] == 'zoom':
  1515. # set region for click (zero-width box)
  1516. if begin[0] - end[0] == 0 or \
  1517. begin[1] - end[1] == 0:
  1518. # zoom 1/2 of the screen (TODO: settings)
  1519. begin = (end[0] - self.Map.width / 4,
  1520. end[1] - self.Map.height / 4)
  1521. end = (end[0] + self.Map.width / 4,
  1522. end[1] + self.Map.height / 4)
  1523. self.Zoom(begin, end, self.zoomtype)
  1524. # redraw map
  1525. self.UpdateMap(render=True)
  1526. # update statusbar
  1527. self.parent.StatusbarUpdate()
  1528. elif self.mouse["use"] == "query":
  1529. # querying
  1530. self.parent.QueryMap(self.mouse['begin'][0],self.mouse['begin'][1])
  1531. elif self.mouse["use"] == "queryVector":
  1532. # editable mode for vector map layers
  1533. self.parent.QueryVector(self.mouse['begin'][0], self.mouse['begin'][1])
  1534. # clear temp canvas
  1535. self.UpdateMap(render=False, renderVector=False)
  1536. elif self.mouse["use"] in ["measure", "profile"]:
  1537. # measure or profile
  1538. if self.mouse["use"] == "measure":
  1539. self.parent.MeasureDist(self.mouse['begin'], self.mouse['end'])
  1540. self.polycoords.append(self.Pixel2Cell(self.mouse['end']))
  1541. self.ClearLines(pdc=self.pdcTmp)
  1542. self.DrawLines(pdc=self.pdcTmp)
  1543. elif self.mouse["use"] == "pointer" and \
  1544. self.parent.GetLayerManager().georectifying:
  1545. # -> georectifying
  1546. coord = self.Pixel2Cell(self.mouse['end'])
  1547. if self.parent.toolbars['georect']:
  1548. coordtype = 'gcpcoord'
  1549. else:
  1550. coordtype = 'mapcoord'
  1551. self.parent.GetLayerManager().georectifying.SetGCPData(coordtype, coord, self)
  1552. self.UpdateMap(render=False, renderVector=False)
  1553. elif self.mouse["use"] == "pointer" and self.parent.toolbars['vdigit']:
  1554. # digitization tool active
  1555. digitToolbar = self.parent.toolbars['vdigit']
  1556. digitClass = self.parent.digit
  1557. if hasattr(self, "vdigitMove"):
  1558. if len(digitClass.driver.GetSelected()) == 0:
  1559. self.vdigitMove['begin'] = self.Pixel2Cell(self.mouse['begin']) # left down
  1560. # eliminate initial mouse moving efect
  1561. self.mouse['begin'] = self.mouse['end']
  1562. if digitToolbar.GetAction() in ("deleteLine",
  1563. "moveLine",
  1564. "moveVertex",
  1565. "copyCats",
  1566. "copyAttrs",
  1567. "editLine",
  1568. "flipLine",
  1569. "mergeLine",
  1570. "snapLine",
  1571. "queryLine",
  1572. "breakLine",
  1573. "typeConv",
  1574. "connectLine"):
  1575. self.OnLeftUpVDigitVarious(event)
  1576. elif digitToolbar.GetAction() in ("splitLine",
  1577. "addVertex",
  1578. "removeVertex"):
  1579. self.OnLeftUpVDigitModifyLine(event)
  1580. elif digitToolbar.GetAction() == "copyLine":
  1581. self.OnLeftUpVDigitCopyLine(event)
  1582. elif digitToolbar.GetAction() == "zbulkLine" and \
  1583. len(self.polycoords) == 2:
  1584. self.OnLeftUpVDigitBulkLine(event)
  1585. elif digitToolbar.GetAction() == "connectLine":
  1586. self.OnLeftUpConnectLine(event)
  1587. if len(digitClass.driver.GetSelected()) > 0:
  1588. self.redrawAll = None
  1589. elif (self.mouse['use'] == 'pointer' and
  1590. self.dragid >= 0):
  1591. # end drag of overlay decoration
  1592. if self.dragid < 99 and self.overlays.has_key(self.dragid):
  1593. self.overlays[self.dragid]['coords'] = self.pdc.GetIdBounds(self.dragid)
  1594. elif self.dragid > 100 and self.textdict.has_key(self.dragid):
  1595. self.textdict[self.dragid]['coords'] = self.pdc.GetIdBounds(self.dragid)
  1596. else:
  1597. pass
  1598. self.dragid = None
  1599. self.currtxtid = None
  1600. def OnButtonDClick(self, event):
  1601. """!
  1602. Mouse button double click
  1603. """
  1604. Debug.msg (5, "BufferedWindow.OnButtonDClick(): use=%s" % \
  1605. self.mouse["use"])
  1606. if self.mouse["use"] == "measure":
  1607. # measure
  1608. self.ClearLines(pdc=self.pdcTmp)
  1609. self.polycoords = []
  1610. self.mouse['use'] = 'pointer'
  1611. self.mouse['box'] = 'point'
  1612. self.mouse['end'] = [0, 0]
  1613. self.Refresh()
  1614. self.SetCursor(self.parent.cursors["default"])
  1615. elif self.mouse["use"] == "profile":
  1616. pass
  1617. elif self.mouse['use'] == 'pointer' and \
  1618. self.parent.toolbars['vdigit']:
  1619. # vector digitizer
  1620. pass
  1621. else:
  1622. # select overlay decoration options dialog
  1623. clickposition = event.GetPositionTuple()[:]
  1624. idlist = self.pdc.FindObjects(clickposition[0], clickposition[1], self.hitradius)
  1625. if idlist == []:
  1626. return
  1627. self.dragid = idlist[0]
  1628. # self.ovlcoords[self.dragid] = self.pdc.GetIdBounds(self.dragid)
  1629. if self.dragid > 100:
  1630. self.currtxtid = self.dragid
  1631. self.parent.OnAddText(None)
  1632. elif self.dragid == 0:
  1633. self.parent.OnAddBarscale(None)
  1634. elif self.dragid == 1:
  1635. self.parent.OnAddLegend(None)
  1636. def OnRightDown(self, event):
  1637. """!
  1638. Right mouse button pressed
  1639. """
  1640. Debug.msg (5, "BufferedWindow.OnRightDown(): use=%s" % \
  1641. self.mouse["use"])
  1642. digitToolbar = self.parent.toolbars['vdigit']
  1643. if digitToolbar:
  1644. digitClass = self.parent.digit
  1645. # digitization tool (confirm action)
  1646. if digitToolbar.GetAction() in ("moveLine",
  1647. "moveVertex") and \
  1648. hasattr(self, "vdigitMove"):
  1649. pFrom = self.vdigitMove['begin']
  1650. pTo = self.Pixel2Cell(event.GetPositionTuple())
  1651. move = (pTo[0] - pFrom[0],
  1652. pTo[1] - pFrom[1])
  1653. if digitToolbar.GetAction() == "moveLine":
  1654. # move line
  1655. if digitClass.MoveSelectedLines(move) < 0:
  1656. return
  1657. elif digitToolbar.GetAction() == "moveVertex":
  1658. # move vertex
  1659. fid = digitClass.MoveSelectedVertex(pFrom, move)
  1660. if fid < 0:
  1661. return
  1662. self.__geomAttrbUpdate([fid,])
  1663. del self.vdigitMove
  1664. event.Skip()
  1665. def OnRightUp(self, event):
  1666. """!
  1667. Right mouse button released
  1668. """
  1669. Debug.msg (5, "BufferedWindow.OnRightUp(): use=%s" % \
  1670. self.mouse["use"])
  1671. digitToolbar = self.parent.toolbars['vdigit']
  1672. if digitToolbar:
  1673. digitClass = self.parent.digit
  1674. # digitization tool (confirm action)
  1675. if digitToolbar.GetAction() == "addLine" and \
  1676. digitToolbar.GetAction('type') in ["line", "boundary"]:
  1677. # -> add new line / boundary
  1678. try:
  1679. map = digitToolbar.GetLayer().GetName()
  1680. except:
  1681. map = None
  1682. wx.MessageBox(parent=self,
  1683. message=_("No vector map selected for editing."),
  1684. caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
  1685. if map:
  1686. # mapcoords = []
  1687. # xy -> EN
  1688. # for coord in self.polycoords:
  1689. # mapcoords.append(self.Pixel2Cell(coord))
  1690. if digitToolbar.GetAction('type') == 'line':
  1691. line = True
  1692. else:
  1693. line = False
  1694. if len(self.polycoords) < 2: # ignore 'one-point' lines
  1695. return
  1696. fid = digitClass.AddLine(map, line, self.polycoords)
  1697. if fid < 0:
  1698. return
  1699. position = self.Cell2Pixel(self.polycoords[-1])
  1700. self.polycoords = []
  1701. self.UpdateMap(render=False)
  1702. self.redrawAll = True
  1703. self.Refresh()
  1704. # add new record into atribute table
  1705. if UserSettings.Get(group='vdigit', key="addRecord", subkey='enabled') and \
  1706. (line is True or \
  1707. (not line and fid > 0)):
  1708. posWindow = self.ClientToScreen((position[0] + self.dialogOffset,
  1709. position[1] + self.dialogOffset))
  1710. # select attributes based on layer and category
  1711. cats = { fid : {
  1712. UserSettings.Get(group='vdigit', key="layer", subkey='value') :
  1713. (UserSettings.Get(group='vdigit', key="category", subkey='value'), )
  1714. }}
  1715. addRecordDlg = dbm_dialogs.DisplayAttributesDialog(parent=self, map=map,
  1716. cats=cats,
  1717. pos=posWindow,
  1718. action="add")
  1719. self.__geomAttrb(fid, addRecordDlg, 'length', digitClass,
  1720. digitToolbar.GetLayer())
  1721. # auto-placing centroid
  1722. self.__geomAttrb(fid, addRecordDlg, 'area', digitClass,
  1723. digitToolbar.GetLayer())
  1724. self.__geomAttrb(fid, addRecordDlg, 'perimeter', digitClass,
  1725. digitToolbar.GetLayer())
  1726. if addRecordDlg.mapDBInfo and \
  1727. addRecordDlg.ShowModal() == wx.ID_OK:
  1728. sqlfile = tempfile.NamedTemporaryFile(mode="w")
  1729. for sql in addRecordDlg.GetSQLString():
  1730. sqlfile.file.write(sql + ";\n")
  1731. sqlfile.file.flush()
  1732. gcmd.RunCommand('db.execute',
  1733. parent = True,
  1734. quiet = True,
  1735. input = sqlfile.name)
  1736. if addRecordDlg.mapDBInfo:
  1737. self.__updateATM()
  1738. elif digitToolbar.GetAction() == "deleteLine":
  1739. # -> delete selected vector features
  1740. if digitClass.DeleteSelectedLines() < 0:
  1741. return
  1742. self.__updateATM()
  1743. elif digitToolbar.GetAction() == "splitLine":
  1744. # split line
  1745. if digitClass.SplitLine(self.Pixel2Cell(self.mouse['begin'])) < 0:
  1746. return
  1747. elif digitToolbar.GetAction() == "addVertex":
  1748. # add vertex
  1749. fid = digitClass.AddVertex(self.Pixel2Cell(self.mouse['begin']))
  1750. if fid < 0:
  1751. return
  1752. elif digitToolbar.GetAction() == "removeVertex":
  1753. # remove vertex
  1754. fid = digitClass.RemoveVertex(self.Pixel2Cell(self.mouse['begin']))
  1755. if fid < 0:
  1756. return
  1757. self.__geomAttrbUpdate([fid,])
  1758. elif digitToolbar.GetAction() in ("copyCats", "copyAttrs"):
  1759. try:
  1760. if digitToolbar.GetAction() == 'copyCats':
  1761. if digitClass.CopyCats(self.copyCatsList,
  1762. self.copyCatsIds, copyAttrb=False) < 0:
  1763. return
  1764. else:
  1765. if digitClass.CopyCats(self.copyCatsList,
  1766. self.copyCatsIds, copyAttrb=True) < 0:
  1767. return
  1768. del self.copyCatsList
  1769. del self.copyCatsIds
  1770. except AttributeError:
  1771. pass
  1772. self.__updateATM()
  1773. elif digitToolbar.GetAction() == "editLine" and \
  1774. hasattr(self, "vdigitMove"):
  1775. line = digitClass.driver.GetSelected()
  1776. if digitClass.EditLine(line, self.polycoords) < 0:
  1777. return
  1778. del self.vdigitMove
  1779. elif digitToolbar.GetAction() == "flipLine":
  1780. if digitClass.FlipLine() < 0:
  1781. return
  1782. elif digitToolbar.GetAction() == "mergeLine":
  1783. if digitClass.MergeLine() < 0:
  1784. return
  1785. elif digitToolbar.GetAction() == "breakLine":
  1786. if digitClass.BreakLine() < 0:
  1787. return
  1788. elif digitToolbar.GetAction() == "snapLine":
  1789. if digitClass.SnapLine() < 0:
  1790. return
  1791. elif digitToolbar.GetAction() == "connectLine":
  1792. if len(digitClass.driver.GetSelected()) > 1:
  1793. if digitClass.ConnectLine() < 0:
  1794. return
  1795. elif digitToolbar.GetAction() == "copyLine":
  1796. if digitClass.CopyLine(self.copyIds) < 0:
  1797. return
  1798. del self.copyIds
  1799. if self.layerTmp:
  1800. self.Map.DeleteLayer(self.layerTmp)
  1801. self.UpdateMap(render=True, renderVector=False)
  1802. del self.layerTmp
  1803. elif digitToolbar.GetAction() == "zbulkLine" and len(self.polycoords) == 2:
  1804. pos1 = self.polycoords[0]
  1805. pos2 = self.polycoords[1]
  1806. selected = digitClass.driver.GetSelected()
  1807. dlg = VDigitZBulkDialog(parent=self, title=_("Z bulk-labeling dialog"),
  1808. nselected=len(selected))
  1809. if dlg.ShowModal() == wx.ID_OK:
  1810. if digitClass.ZBulkLines(pos1, pos2, dlg.value.GetValue(),
  1811. dlg.step.GetValue()) < 0:
  1812. return
  1813. self.UpdateMap(render=False, renderVector=True)
  1814. elif digitToolbar.GetAction() == "typeConv":
  1815. # -> feature type conversion
  1816. # - point <-> centroid
  1817. # - line <-> boundary
  1818. if digitClass.TypeConvForSelectedLines() < 0:
  1819. return
  1820. if digitToolbar.GetAction() != "addLine":
  1821. # unselect and re-render
  1822. digitClass.driver.SetSelected([])
  1823. self.polycoords = []
  1824. self.UpdateMap(render=False)
  1825. self.redrawAll = True
  1826. self.Refresh()
  1827. event.Skip()
  1828. def OnMiddleDown(self, event):
  1829. """!
  1830. Middle mouse button pressed
  1831. """
  1832. digitToolbar = self.parent.toolbars['vdigit']
  1833. # digitization tool
  1834. if self.mouse["use"] == "pointer" and digitToolbar:
  1835. digitClass = self.parent.digit
  1836. if (digitToolbar.GetAction() == "addLine" and \
  1837. digitToolbar.GetAction('type') in ["line", "boundary"]) or \
  1838. digitToolbar.GetAction() == "editLine":
  1839. # add line or boundary -> remove last point from the line
  1840. try:
  1841. removed = self.polycoords.pop()
  1842. Debug.msg(4, "BufferedWindow.OnMiddleDown(): polycoords_poped=%s" % \
  1843. [removed,])
  1844. self.mouse['begin'] = self.Cell2Pixel(self.polycoords[-1])
  1845. except:
  1846. pass
  1847. if digitToolbar.GetAction() == "editLine":
  1848. # remove last vertex & line
  1849. if len(self.vdigitMove['id']) > 1:
  1850. self.vdigitMove['id'].pop()
  1851. self.UpdateMap(render=False, renderVector=False)
  1852. elif digitToolbar.GetAction() in ["deleteLine", "moveLine", "splitLine",
  1853. "addVertex", "removeVertex", "moveVertex",
  1854. "copyCats", "flipLine", "mergeLine",
  1855. "snapLine", "connectLine", "copyLine",
  1856. "queryLine", "breakLine", "typeConv"]:
  1857. # varios tools -> unselected selected features
  1858. digitClass.driver.SetSelected([])
  1859. if digitToolbar.GetAction() in ["moveLine", "moveVertex", "editLine"] and \
  1860. hasattr(self, "vdigitMove"):
  1861. del self.vdigitMove
  1862. elif digitToolbar.GetAction() == "copyCats":
  1863. try:
  1864. del self.copyCatsList
  1865. del self.copyCatsIds
  1866. except AttributeError:
  1867. pass
  1868. elif digitToolbar.GetAction() == "copyLine":
  1869. del self.copyIds
  1870. if self.layerTmp:
  1871. self.Map.DeleteLayer(self.layerTmp)
  1872. self.UpdateMap(render=True, renderVector=False)
  1873. del self.layerTmp
  1874. self.polycoords = []
  1875. self.UpdateMap(render=False) # render vector
  1876. elif digitToolbar.GetAction() == "zbulkLine":
  1877. # reset polyline
  1878. self.polycoords = []
  1879. digitClass.driver.SetSelected([])
  1880. self.UpdateMap(render=False)
  1881. self.redrawAll = True
  1882. def OnMouseMoving(self, event):
  1883. """!
  1884. Motion event and no mouse buttons were pressed
  1885. """
  1886. digitToolbar = self.parent.toolbars['vdigit']
  1887. if self.mouse["use"] == "pointer" and digitToolbar:
  1888. digitClass = self.parent.digit
  1889. self.mouse['end'] = event.GetPositionTuple()[:]
  1890. Debug.msg (5, "BufferedWindow.OnMouseMoving(): coords=%f,%f" % \
  1891. (self.mouse['end'][0], self.mouse['end'][1]))
  1892. if digitToolbar.GetAction() == "addLine" and digitToolbar.GetAction('type') in ["line", "boundary"]:
  1893. if len(self.polycoords) > 0:
  1894. self.MouseDraw(pdc=self.pdcTmp, begin=self.Cell2Pixel(self.polycoords[-1]))
  1895. elif digitToolbar.GetAction() in ["moveLine", "moveVertex", "editLine"] \
  1896. and hasattr(self, "vdigitMove"):
  1897. dx = self.mouse['end'][0] - self.mouse['begin'][0]
  1898. dy = self.mouse['end'][1] - self.mouse['begin'][1]
  1899. if len(self.vdigitMove['id']) > 0:
  1900. # draw lines on new position
  1901. if digitToolbar.GetAction() == "moveLine":
  1902. # move line
  1903. for id in self.vdigitMove['id']:
  1904. self.pdcTmp.TranslateId(id, dx, dy)
  1905. elif digitToolbar.GetAction() in ["moveVertex", "editLine"]:
  1906. # move vertex ->
  1907. # (vertex, left vertex, left line,
  1908. # right vertex, right line)
  1909. # do not draw static lines
  1910. if digitToolbar.GetAction() == "moveVertex":
  1911. self.polycoords = []
  1912. ### self.pdcTmp.TranslateId(self.vdigitMove['id'][0], dx, dy)
  1913. self.pdcTmp.RemoveId(self.vdigitMove['id'][0])
  1914. if self.vdigitMove['id'][1] > 0: # previous vertex
  1915. x, y = self.Pixel2Cell(self.pdcTmp.GetIdBounds(self.vdigitMove['id'][1])[0:2])
  1916. self.pdcTmp.RemoveId(self.vdigitMove['id'][1]+1)
  1917. self.polycoords.append((x, y))
  1918. ### x, y = self.Pixel2Cell(self.pdcTmp.GetIdBounds(self.vdigitMove['id'][0])[0:2])
  1919. self.polycoords.append(self.Pixel2Cell(self.mouse['end']))
  1920. if self.vdigitMove['id'][2] > 0: # next vertex
  1921. x, y = self.Pixel2Cell(self.pdcTmp.GetIdBounds(self.vdigitMove['id'][2])[0:2])
  1922. self.pdcTmp.RemoveId(self.vdigitMove['id'][2]-1)
  1923. self.polycoords.append((x, y))
  1924. self.ClearLines(pdc=self.pdcTmp)
  1925. self.DrawLines(pdc=self.pdcTmp)
  1926. else: # edit line
  1927. try:
  1928. if self.vdigitMove['id'][-1] > 0: # previous vertex
  1929. self.MouseDraw(pdc=self.pdcTmp,
  1930. begin=self.Cell2Pixel(self.polycoords[-1]))
  1931. except: # no line
  1932. self.vdigitMove['id'] = []
  1933. self.polycoords = []
  1934. self.Refresh() # TODO: use RefreshRect()
  1935. self.mouse['begin'] = self.mouse['end']
  1936. elif digitToolbar.GetAction() == "zbulkLine":
  1937. if len(self.polycoords) == 1:
  1938. # draw mouse moving
  1939. self.MouseDraw(self.pdcTmp)
  1940. event.Skip()
  1941. def ClearLines(self, pdc=None):
  1942. """!
  1943. Clears temporary drawn lines from PseudoDC
  1944. """
  1945. if not pdc:
  1946. pdc=self.pdcTmp
  1947. try:
  1948. pdc.ClearId(self.lineid)
  1949. pdc.RemoveId(self.lineid)
  1950. except:
  1951. pass
  1952. try:
  1953. pdc.ClearId(self.plineid)
  1954. pdc.RemoveId(self.plineid)
  1955. except:
  1956. pass
  1957. Debug.msg(4, "BufferedWindow.ClearLines(): lineid=%s, plineid=%s" %
  1958. (self.lineid, self.plineid))
  1959. ### self.Refresh()
  1960. return True
  1961. def Pixel2Cell(self, (x, y)):
  1962. """!
  1963. Convert image coordinates to real word coordinates
  1964. Input : int x, int y
  1965. Output: float x, float y
  1966. """
  1967. try:
  1968. x = int(x)
  1969. y = int(y)
  1970. except:
  1971. return None
  1972. if self.Map.region["ewres"] > self.Map.region["nsres"]:
  1973. res = self.Map.region["ewres"]
  1974. else:
  1975. res = self.Map.region["nsres"]
  1976. w = self.Map.region["center_easting"] - (self.Map.width / 2) * res
  1977. n = self.Map.region["center_northing"] + (self.Map.height / 2) * res
  1978. east = w + x * res
  1979. north = n - y * res
  1980. # extent does not correspond with whole map canvas area...
  1981. # east = self.Map.region['w'] + x * self.Map.region["ewres"]
  1982. # north = self.Map.region['n'] - y * self.Map.region["nsres"]
  1983. return (east, north)
  1984. def Cell2Pixel(self, (east, north)):
  1985. """!
  1986. Convert real word coordinates to image coordinates
  1987. """
  1988. try:
  1989. east = float(east)
  1990. north = float(north)
  1991. except:
  1992. return None
  1993. if self.Map.region["ewres"] > self.Map.region["nsres"]:
  1994. res = self.Map.region["ewres"]
  1995. else:
  1996. res = self.Map.region["nsres"]
  1997. w = self.Map.region["center_easting"] - (self.Map.width / 2) * res
  1998. n = self.Map.region["center_northing"] + (self.Map.height / 2) * res
  1999. # x = int((east - w) / res)
  2000. # y = int((n - north) / res)
  2001. x = (east - w) / res
  2002. y = (n - north) / res
  2003. return (x, y)
  2004. def Zoom(self, begin, end, zoomtype):
  2005. """!
  2006. Calculates new region while (un)zoom/pan-ing
  2007. """
  2008. x1, y1 = begin
  2009. x2, y2 = end
  2010. newreg = {}
  2011. # threshold - too small squares do not make sense
  2012. # can only zoom to windows of > 5x5 screen pixels
  2013. if abs(x2-x1) > 5 and abs(y2-y1) > 5 and zoomtype != 0:
  2014. if x1 > x2:
  2015. x1, x2 = x2, x1
  2016. if y1 > y2:
  2017. y1, y2 = y2, y1
  2018. # zoom in
  2019. if zoomtype > 0:
  2020. newreg['w'], newreg['n'] = self.Pixel2Cell((x1, y1))
  2021. newreg['e'], newreg['s'] = self.Pixel2Cell((x2, y2))
  2022. # zoom out
  2023. elif zoomtype < 0:
  2024. newreg['w'], newreg['n'] = self.Pixel2Cell((-x1 * 2, -y1 * 2))
  2025. newreg['e'], newreg['s'] = self.Pixel2Cell((self.Map.width + 2 * \
  2026. (self.Map.width - x2),
  2027. self.Map.height + 2 * \
  2028. (self.Map.height - y2)))
  2029. # pan
  2030. elif zoomtype == 0:
  2031. dx = x1 - x2
  2032. dy = y1 - y2
  2033. newreg['w'], newreg['n'] = self.Pixel2Cell((dx, dy))
  2034. newreg['e'], newreg['s'] = self.Pixel2Cell((self.Map.width + dx,
  2035. self.Map.height + dy))
  2036. # if new region has been calculated, set the values
  2037. if newreg != {}:
  2038. # LL locations
  2039. if self.parent.Map.projinfo['proj'] == 'll':
  2040. if newreg['n'] > 90.0:
  2041. newreg['n'] = 90.0
  2042. if newreg['s'] < -90.0:
  2043. newreg['s'] = -90.0
  2044. ce = newreg['w'] + (newreg['e'] - newreg['w']) / 2
  2045. cn = newreg['s'] + (newreg['n'] - newreg['s']) / 2
  2046. if hasattr(self, "vdigitMove"):
  2047. # xo = self.Cell2Pixel((self.Map.region['center_easting'], self.Map.region['center_northing']))
  2048. # xn = self.Cell2Pixel(ce, cn))
  2049. tmp = self.Pixel2Cell(self.mouse['end'])
  2050. # calculate new center point and display resolution
  2051. self.Map.region['center_easting'] = ce
  2052. self.Map.region['center_northing'] = cn
  2053. self.Map.region["ewres"] = (newreg['e'] - newreg['w']) / self.Map.width
  2054. self.Map.region["nsres"] = (newreg['n'] - newreg['s']) / self.Map.height
  2055. self.Map.AlignExtentFromDisplay()
  2056. if hasattr(self, "vdigitMove"):
  2057. tmp1 = self.mouse['end']
  2058. tmp2 = self.Cell2Pixel(self.vdigitMove['begin'])
  2059. dx = tmp1[0] - tmp2[0]
  2060. dy = tmp1[1] - tmp2[1]
  2061. self.vdigitMove['beginDiff'] = (dx, dy)
  2062. for id in self.vdigitMove['id']:
  2063. self.pdcTmp.RemoveId(id)
  2064. self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
  2065. self.Map.region['e'], self.Map.region['w'])
  2066. if self.redrawAll is False:
  2067. self.redrawAll = True
  2068. def ZoomBack(self):
  2069. """!
  2070. Zoom to previous extents in zoomhistory list
  2071. """
  2072. zoom = list()
  2073. if len(self.zoomhistory) > 1:
  2074. self.zoomhistory.pop()
  2075. zoom = self.zoomhistory[-1]
  2076. if len(self.zoomhistory) < 2: # disable tool
  2077. self.parent.toolbars['map'].Enable('zoomback', enable = False)
  2078. # zoom to selected region
  2079. self.Map.GetRegion(n = zoom[0], s = zoom[1],
  2080. e = zoom[2], w = zoom[3],
  2081. update = True)
  2082. # update map
  2083. self.UpdateMap()
  2084. # update statusbar
  2085. self.parent.StatusbarUpdate()
  2086. def ZoomHistory(self, n, s, e, w):
  2087. """!
  2088. Manages a list of last 10 zoom extents
  2089. Return removed history item if exists
  2090. """
  2091. removed = None
  2092. self.zoomhistory.append((n,s,e,w))
  2093. if len(self.zoomhistory) > 10:
  2094. removed = self.zoomhistory.pop(0)
  2095. if removed:
  2096. Debug.msg(4, "BufferedWindow.ZoomHistory(): hist=%s, removed=%s" %
  2097. (self.zoomhistory, removed))
  2098. else:
  2099. Debug.msg(4, "BufferedWindow.ZoomHistory(): hist=%s" %
  2100. (self.zoomhistory))
  2101. if len(self.zoomhistory) > 1:
  2102. self.parent.toolbars['map'].Enable('zoomback')
  2103. return removed
  2104. def OnZoomToMap(self, event):
  2105. """!
  2106. Set display extents to match selected raster (including NULLs)
  2107. or vector map.
  2108. """
  2109. self.ZoomToMap()
  2110. def OnZoomToRaster(self, event):
  2111. """!
  2112. Set display extents to match selected raster map (ignore NULLs)
  2113. """
  2114. self.ZoomToMap(ignoreNulls = True)
  2115. def ZoomToMap(self, layers = None, ignoreNulls = False, render = True):
  2116. """!
  2117. Set display extents to match selected raster
  2118. or vector map(s).
  2119. @param layer list of layers to be zoom to
  2120. @param ignoreNulls True to ignore null-values
  2121. @param render True to re-render display
  2122. """
  2123. zoomreg = {}
  2124. if not layers:
  2125. layers = self.GetSelectedLayer(multi = True)
  2126. if not layers:
  2127. return
  2128. rast = []
  2129. vect = []
  2130. updated = False
  2131. for l in layers:
  2132. # only raster/vector layers are currently supported
  2133. if l.type == 'raster':
  2134. rast.append(l.name)
  2135. elif l.type == 'vector':
  2136. digitToolbar = self.parent.toolbars['vdigit']
  2137. if digitToolbar and digitToolbar.GetLayer() == l.name:
  2138. w, s, b, e, n, t = self.parent.digit.driver.GetMapBoundingBox()
  2139. self.Map.GetRegion(n=n, s=s, w=w, e=e,
  2140. update=True)
  2141. updated = True
  2142. else:
  2143. vect.append(l.name)
  2144. if not updated:
  2145. self.Map.GetRegion(rast = rast,
  2146. vect = vect,
  2147. update = True)
  2148. self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
  2149. self.Map.region['e'], self.Map.region['w'])
  2150. if render:
  2151. self.UpdateMap()
  2152. self.parent.StatusbarUpdate()
  2153. def ZoomToWind(self, event):
  2154. """!
  2155. Set display geometry to match computational
  2156. region settings (set with g.region)
  2157. """
  2158. self.Map.region = self.Map.GetRegion()
  2159. ### self.Map.SetRegion(windres=True)
  2160. self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
  2161. self.Map.region['e'], self.Map.region['w'])
  2162. self.UpdateMap()
  2163. self.parent.StatusbarUpdate()
  2164. def ZoomToDefault(self, event):
  2165. """!
  2166. Set display geometry to match default region settings
  2167. """
  2168. self.Map.region = self.Map.GetRegion(default=True)
  2169. self.Map.AdjustRegion() # aling region extent to the display
  2170. self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
  2171. self.Map.region['e'], self.Map.region['w'])
  2172. self.UpdateMap()
  2173. self.parent.StatusbarUpdate()
  2174. def DisplayToWind(self, event):
  2175. """!
  2176. Set computational region (WIND file) to
  2177. match display extents
  2178. """
  2179. tmpreg = os.getenv("GRASS_REGION")
  2180. if tmpreg:
  2181. del os.environ["GRASS_REGION"]
  2182. # We ONLY want to set extents here. Don't mess with resolution. Leave that
  2183. # for user to set explicitly with g.region
  2184. new = self.Map.AlignResolution()
  2185. gcmd.RunCommand('g.region',
  2186. parent = self,
  2187. overwrite = True,
  2188. n = new['n'],
  2189. s = new['s'],
  2190. e = new['e'],
  2191. w = new['w'],
  2192. rows = int(new['rows']),
  2193. cols = int(new['cols']))
  2194. if tmpreg:
  2195. os.environ["GRASS_REGION"] = tmpreg
  2196. def ZoomToSaved(self, event):
  2197. """!Set display geometry to match extents in
  2198. saved region file
  2199. """
  2200. dlg = gdialogs.SavedRegion(parent = self,
  2201. title = _("Zoom to saved region extents"),
  2202. loadsave='load')
  2203. if dlg.ShowModal() == wx.ID_CANCEL or not dlg.wind:
  2204. dlg.Destroy()
  2205. return
  2206. if not grass.find_file(name = dlg.wind, element = 'windows')['name']:
  2207. wx.MessageBox(parent = self,
  2208. message = _("Region <%s> not found. Operation canceled.") % dlg.wind,
  2209. caption = _("Error"), style = wx.ICON_ERROR | wx.OK | wx.CENTRE)
  2210. dlg.Destroy()
  2211. return
  2212. self.Map.GetRegion(regionName = dlg.wind,
  2213. update = True)
  2214. dlg.Destroy()
  2215. self.ZoomHistory(self.Map.region['n'],
  2216. self.Map.region['s'],
  2217. self.Map.region['e'],
  2218. self.Map.region['w'])
  2219. self.UpdateMap()
  2220. def SaveDisplayRegion(self, event):
  2221. """!
  2222. Save display extents to named region file.
  2223. """
  2224. dlg = gdialogs.SavedRegion(parent = self,
  2225. title = _("Save display extents to region file"),
  2226. loadsave='save')
  2227. if dlg.ShowModal() == wx.ID_CANCEL or not dlg.wind:
  2228. dlg.Destroy()
  2229. return
  2230. # test to see if it already exists and ask permission to overwrite
  2231. if grass.find_file(name = dlg.wind, element = 'windows')['name']:
  2232. overwrite = wx.MessageBox(parent = self,
  2233. message = _("Region file <%s> already exists. "
  2234. "Do you want to overwrite it?") % (dlg.wind),
  2235. caption = _("Warning"), style = wx.YES_NO | wx.CENTRE)
  2236. if (overwrite == wx.YES):
  2237. self.SaveRegion(dlg.wind)
  2238. else:
  2239. self.SaveRegion(dlg.wind)
  2240. dlg.Destroy()
  2241. def SaveRegion(self, wind):
  2242. """!Save region settings
  2243. @param wind region name
  2244. """
  2245. new = self.Map.GetCurrentRegion()
  2246. tmpreg = os.getenv("GRASS_REGION")
  2247. if tmpreg:
  2248. del os.environ["GRASS_REGION"]
  2249. gcmd.RunCommand('g.region',
  2250. overwrite = True,
  2251. parent = self,
  2252. flags = 'u',
  2253. n = new['n'],
  2254. s = new['s'],
  2255. e = new['e'],
  2256. w = new['w'],
  2257. rows = int(new['rows']),
  2258. cols = int(new['cols']),
  2259. save = wind)
  2260. if tmpreg:
  2261. os.environ["GRASS_REGION"] = tmpreg
  2262. def Distance(self, beginpt, endpt, screen=True):
  2263. """!Calculete distance
  2264. LL-locations not supported
  2265. @todo Use m.distance
  2266. @param beginpt first point
  2267. @param endpt second point
  2268. @param screen True for screen coordinates otherwise EN
  2269. """
  2270. x1, y1 = beginpt
  2271. x2, y2 = endpt
  2272. if screen:
  2273. dEast = (x2 - x1) * self.Map.region["ewres"]
  2274. dNorth = (y2 - y1) * self.Map.region["nsres"]
  2275. else:
  2276. dEast = (x2 - x1)
  2277. dNorth = (y2 - y1)
  2278. return (math.sqrt(math.pow((dEast),2) + math.pow((dNorth),2)), (dEast, dNorth))