mapdisp_window.py 102 KB

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