mapdisp_window.py 94 KB

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