vdigit.py 90 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396
  1. """
  2. @package vdigit
  3. @brief Vector digitizer extension
  4. Progress:
  5. (1) v.edit called on the background (class VEdit) (removed in r?)
  6. (2) Reimplentation of v.digit (VDigit)
  7. Import:
  8. from vdigit import VDigit as VDigit
  9. Classes:
  10. - AbstractDigit
  11. - VDigit
  12. - AbstractDisplayDriver
  13. - CDisplayDriver
  14. - VDigitSettingsDialog
  15. - VDigitCategoryDialog
  16. - VDigitZBulkDialog
  17. - VDigitDuplicatesDialog
  18. - VDigitVBuildDialog
  19. (C) 2007-2008 by the GRASS Development Team
  20. This program is free software under the GNU General Public
  21. License (>=v2). Read the file COPYING that comes with GRASS
  22. for details.
  23. @author Martin Landa <landa.martin gmail.com>
  24. """
  25. import os
  26. import sys
  27. import string
  28. import copy
  29. from threading import Thread
  30. import wx
  31. import wx.lib.colourselect as csel
  32. import wx.lib.mixins.listctrl as listmix
  33. import gcmd
  34. import dbm
  35. from debug import Debug as Debug
  36. import gselect
  37. import globalvar
  38. from preferences import globalSettings as UserSettings
  39. try:
  40. digitPath = os.path.join(globalvar.ETCWXDIR, "vdigit")
  41. sys.path.append(digitPath)
  42. import grass7_wxvdigit as wxvdigit
  43. GV_LINES = wxvdigit.GV_LINES
  44. digitErr = ''
  45. except ImportError, err:
  46. GV_LINES = None
  47. digitErr = err
  48. # print >> sys.stderr, "%sWARNING: Digitization tool is disabled (%s). " \
  49. # "Detailed information in README file." % \
  50. # (os.linesep, err)
  51. class AbstractDigit:
  52. """
  53. Abstract digitization class
  54. """
  55. def __init__(self, mapwindow):
  56. """Initialization
  57. @param mapwindow reference to mapwindow (MapFrame) instance
  58. @param settings initial settings of digitization tool
  59. """
  60. self.map = None
  61. self.mapWindow = mapwindow
  62. Debug.msg (3, "AbstractDigit.__init__(): map=%s" % \
  63. self.map)
  64. #self.SetCategory()
  65. self.driver = CDisplayDriver(self, mapwindow)
  66. def __del__(self):
  67. pass
  68. def SetCategoryNextToUse(self):
  69. """Find maximum category number in the map layer
  70. and update Digit.settings['category']
  71. @return 'True' on success, 'False' on failure
  72. """
  73. # vector map layer without categories, reset to '1'
  74. UserSettings.Set(group='vdigit', key='category', subkey='value', value=1)
  75. if self.map:
  76. cat = self.digit.GetCategory(UserSettings.Get(group='vdigit', key='layer', subkey='value'))
  77. cat += 1
  78. UserSettings.Set(group='vdigit', key='category', subkey='value',
  79. value=cat)
  80. def SetCategory(self):
  81. """Return category number to use (according Settings)"""
  82. if UserSettings.Get(group='vdigit', key="categoryMode", subkey='selection') == 0:
  83. self.SetCategoryNextToUse()
  84. return UserSettings.Get(group='vdigit', key="category", subkey='value')
  85. def SetMapName(self, map):
  86. """Set map name
  87. @param map map name to be set up or None (will close currently edited map)
  88. """
  89. Debug.msg (3, "AbstractDigit.SetMapName map=%s" % map)
  90. self.map = map
  91. try:
  92. ret = self.driver.Reset(self.map)
  93. except StandardError, e:
  94. raise gcmd.DigitError(parent=self.mapWindow.parent,
  95. message="%s %s (%s)" % (_('Unable to initialize display driver, '
  96. 'see README file for more information.\n\n'
  97. 'Details:'), e, digitErr))
  98. if map and ret == -1:
  99. raise gcmd.DigitError(parent=self.mapWindow.parent,
  100. message=_('Unable to open vector map <%s> for editing.\n\n'
  101. 'Data are probably corrupted, '
  102. 'try to run v.build to rebuild '
  103. 'the topology (Vector->Develop vector map->'
  104. 'Create/rebuild topology).') % map)
  105. if not map and ret != 0:
  106. raise gcmd.DigitError(parent=self.mapWindow.parent,
  107. message=_('Unable to open vector map <%s> for editing.\n\n'
  108. 'Data are probably corrupted, '
  109. 'try to run v.build to rebuild '
  110. 'the topology (Vector->Develop vector map->'
  111. 'Create/rebuild topology).') % map)
  112. self.digit.InitCats()
  113. def SelectLinesByQueryThresh(self):
  114. """Generic method used for SelectLinesByQuery()
  115. -- to get threshold value"""
  116. thresh = 0.0
  117. if UserSettings.Get(group='vdigit', key='query', subkey='selection') == 0:
  118. thresh = UserSettings.Get(group='vdigit', key='queryLength', subkey='thresh')
  119. if UserSettings.Get(group='vdigit', key="queryLength", subkey='than-selection') == 0:
  120. thresh = -1 * thresh
  121. else:
  122. thresh = UserSettings.Get(group='vdigit', key='queryDangle', subkey='thresh')
  123. if UserSettings.Get(group='vdigit', key="queryDangle", subkey='than-selection') == 0:
  124. thresh = -1 * thresh
  125. return thresh
  126. def GetSelectType(self):
  127. """Get type(s) to be selected
  128. Used by SelectLinesByBox() and SelectLinesByPoint()"""
  129. type = 0
  130. for feature in (('point', wxvdigit.GV_POINT),
  131. ('line', wxvdigit.GV_LINE),
  132. ('centroid', wxvdigit.GV_CENTROID),
  133. ('boundary', wxvdigit.GV_BOUNDARY)):
  134. if UserSettings.Get(group='vdigit', key='selectType',
  135. subkey=[feature[0], 'enabled']) is True:
  136. type |= feature[1]
  137. return type
  138. def SelectLinesFromBackgroundMap(self, pos1, pos2):
  139. """Select features from background map
  140. @param pos1,pos2 bounding box defifinition
  141. """
  142. bgmap = str(UserSettings.Get(group='vdigit', key='bgmap', subkey='value',
  143. internal=True))
  144. if bgmap == '':
  145. Debug.msg(4, "VEdit.SelectLinesFromBackgroundMap(): []")
  146. return []
  147. x1, y1 = pos1
  148. x2, y2 = pos2
  149. vEditCmd = gcmd.Command(['v.edit',
  150. '--q',
  151. 'map=%s' % bgmap,
  152. 'tool=select',
  153. 'bbox=%f,%f,%f,%f' % (pos1[0], pos1[1], pos2[0], pos2[1])])
  154. #'polygon=%f,%f,%f,%f,%f,%f,%f,%f,%f,%f' % \
  155. # (x1, y1, x2, y1, x2, y2, x1, y2, x1, y1)])
  156. output = vEditCmd.ReadStdOutput()[0] # first line
  157. ids = output.split(',')
  158. ids = map(int, ids) # str -> int
  159. Debug.msg(4, "VEdit.SelectLinesFromBackgroundMap(): %s" % \
  160. ",".join(["%d" % v for v in ids]))
  161. return ids
  162. class VDigit(AbstractDigit):
  163. """
  164. Prototype of digitization class based on v.digit reimplementation
  165. Under development (wxWidgets C/C++ background)
  166. """
  167. def __init__(self, mapwindow):
  168. """Initialization
  169. @param mapwindow reference to mapwindow (MapFrame) instance
  170. @param settings initial settings of digitization tool
  171. """
  172. AbstractDigit.__init__(self, mapwindow)
  173. try:
  174. self.digit = wxvdigit.Digit(self.driver.GetDevice(),
  175. mapwindow)
  176. except (ImportError, NameError):
  177. self.digit = None
  178. self.toolbar = mapwindow.parent.toolbars['vdigit']
  179. self.UpdateSettings()
  180. def __del__(self):
  181. del self.digit
  182. def AddPoint (self, map, point, x, y, z=None):
  183. """Add new point/centroid
  184. @param map map name (unused, for compatability with VEdit)
  185. @param point feature type (if true point otherwise centroid)
  186. @param x,y,z coordinates
  187. """
  188. if UserSettings.Get(group='vdigit', key="categoryMode", subkey='selection') == 2:
  189. layer = -1 # -> no category
  190. cat = -1
  191. else:
  192. layer = UserSettings.Get(group='vdigit', key="layer", subkey='value')
  193. cat = self.SetCategory()
  194. if point:
  195. type = wxvdigit.GV_POINT
  196. else:
  197. type = wxvdigit.GV_CENTROID
  198. snap, thresh = self.__getSnapThreshold()
  199. bgmap = str(UserSettings.Get(group='vdigit', key="bgmap",
  200. subkey='value', internal=True))
  201. if z:
  202. ret = self.digit.AddLine(type, [x, y, z], layer, cat,
  203. bgmap, snap, thresh)
  204. else:
  205. ret = self.digit.AddLine(type, [x, y], layer, cat,
  206. bgmap, snap, thresh)
  207. self.toolbar.EnableUndo()
  208. return ret
  209. def AddLine (self, map, line, coords):
  210. """Add line/boundary
  211. @param map map name (unused, for compatability with VEdit)
  212. @param line feature type (if True line, otherwise boundary)
  213. @param coords list of coordinates
  214. """
  215. if len(coords) < 2:
  216. return
  217. if UserSettings.Get(group='vdigit', key="categoryMode", subkey='selection') == 2:
  218. layer = -1 # -> no category
  219. cat = -1
  220. else:
  221. layer = UserSettings.Get(group='vdigit', key="layer", subkey='value')
  222. cat = self.SetCategory()
  223. if line:
  224. type = wxvdigit.GV_LINE
  225. else:
  226. type = wxvdigit.GV_BOUNDARY
  227. listCoords = []
  228. for c in coords:
  229. for x in c:
  230. listCoords.append(x)
  231. snap, thresh = self.__getSnapThreshold()
  232. bgmap = str(UserSettings.Get(group='vdigit', key="bgmap",
  233. subkey='value', internal=True))
  234. ret = self.digit.AddLine(type, listCoords, layer, cat,
  235. bgmap, snap, thresh)
  236. self.toolbar.EnableUndo()
  237. return ret
  238. def DeleteSelectedLines(self):
  239. """Delete selected features
  240. @return number of deleted lines
  241. """
  242. nlines = self.digit.DeleteLines(UserSettings.Get(group='vdigit', key='delRecord', subkey='enabled'))
  243. if nlines > 0:
  244. self.toolbar.EnableUndo()
  245. return nlines
  246. def MoveSelectedLines(self, move):
  247. """Move selected features
  248. @param move direction (x, y)
  249. """
  250. snap, thresh = self.__getSnapThreshold()
  251. bgmap = str(UserSettings.Get(group='vdigit', key="bgmap",
  252. subkey='value', internal=True))
  253. try:
  254. nlines = self.digit.MoveLines(move[0], move[1], 0.0, # TODO 3D
  255. bgmap, snap, thresh)
  256. except SystemExit:
  257. pass
  258. if nlines > 0:
  259. self.toolbar.EnableUndo()
  260. return nlines
  261. def MoveSelectedVertex(self, coords, move):
  262. """Move selected vertex of the line
  263. @param coords click coordinates
  264. @param move X,Y direction
  265. @return 1 vertex moved
  266. @return 0 vertex not moved (not found, line is not selected)
  267. """
  268. snap, thresh = self.__getSnapThreshold()
  269. bgmap = str(UserSettings.Get(group='vdigit', key="bgmap",
  270. subkey='value', internal=True))
  271. moved = self.digit.MoveVertex(coords[0], coords[1], 0.0, # TODO 3D
  272. move[0], move[1], 0.0,
  273. bgmap, snap,
  274. self.driver.GetThreshold(type='selectThresh'), thresh)
  275. if moved:
  276. self.toolbar.EnableUndo()
  277. return moved
  278. def AddVertex(self, coords):
  279. """Add new vertex to the selected line/boundary on position 'coords'
  280. @param coords coordinates to add vertex
  281. @return 1 vertex added
  282. @return 0 nothing changed
  283. @return -1 on failure
  284. """
  285. added = self.digit.ModifyLineVertex(1, coords[0], coords[1], 0.0, # TODO 3D
  286. self.driver.GetThreshold(type='selectThresh'))
  287. if added > 0:
  288. self.toolbar.EnableUndo()
  289. return added
  290. def RemoveVertex(self, coords):
  291. """Remove vertex from the selected line/boundary on position 'coords'
  292. @param coords coordinates to remove vertex
  293. @return 1 vertex removed
  294. @return 0 nothing changed
  295. @return -1 on failure
  296. """
  297. deleted = self.digit.ModifyLineVertex(0, coords[0], coords[1], 0.0, # TODO 3D
  298. self.driver.GetThreshold(type='selectThresh'))
  299. if deleted > 0:
  300. self.toolbar.EnableUndo()
  301. return deleted
  302. def SplitLine(self, coords):
  303. """Split selected line/boundary on position 'coords'
  304. @param coords coordinates to split line
  305. @return 1 line modified
  306. @return 0 nothing changed
  307. @return -1 error
  308. """
  309. ret = self.digit.SplitLine(coords[0], coords[1], 0.0, # TODO 3D
  310. self.driver.GetThreshold('selectThresh'))
  311. if ret > 0:
  312. self.toolbar.EnableUndo()
  313. return ret
  314. def EditLine(self, line, coords):
  315. """Edit existing line/boundary
  316. @param line id of line to be modified
  317. @param coords list of coordinates of modified line
  318. @return feature id of new line
  319. @return -1 on error
  320. """
  321. try:
  322. lineid = line[0]
  323. except:
  324. lineid = -1
  325. if len(coords) < 2:
  326. self.DeleteSelectedLines()
  327. return 0
  328. listCoords = []
  329. for c in coords:
  330. for x in c:
  331. listCoords.append(x)
  332. snap, thresh = self.__getSnapThreshold()
  333. bgmap = str(UserSettings.Get(group='vdigit', key="bgmap",
  334. subkey='value', internal=True))
  335. try:
  336. ret = self.digit.RewriteLine(lineid, listCoords,
  337. bgmap, snap, thresh)
  338. except SystemExit:
  339. pass
  340. if ret > 0:
  341. self.toolbar.EnableUndo()
  342. return ret
  343. def FlipLine(self):
  344. """Flip selected lines/boundaries
  345. @return number of modified lines
  346. @return -1 on error
  347. """
  348. ret = self.digit.FlipLines()
  349. if ret > 0:
  350. self.toolbar.EnableUndo()
  351. return ret
  352. def MergeLine(self):
  353. """Merge selected lines/boundaries
  354. @return number of modified lines
  355. @return -1 on error
  356. """
  357. ret = self.digit.MergeLines()
  358. if ret > 0:
  359. self.toolbar.EnableUndo()
  360. return ret
  361. def BreakLine(self):
  362. """Break selected lines/boundaries
  363. @return number of modified lines
  364. @return -1 on error
  365. """
  366. ret = self.digit.BreakLines()
  367. if ret > 0:
  368. self.toolbar.EnableUndo()
  369. return ret
  370. def SnapLine(self):
  371. """Snap selected lines/boundaries
  372. @return on success
  373. @return -1 on error
  374. """
  375. snap, thresh = self.__getSnapThreshold()
  376. ret = self.digit.SnapLines(thresh)
  377. if ret == 0:
  378. self.toolbar.EnableUndo()
  379. return ret
  380. def ConnectLine(self):
  381. """Connect selected lines/boundaries
  382. @return 1 lines connected
  383. @return 0 lines not connected
  384. @return -1 on error
  385. """
  386. snap, thresh = self.__getSnapThreshold()
  387. ret = self.digit.ConnectLines(thresh)
  388. if ret > 0:
  389. self.toolbar.EnableUndo()
  390. return ret
  391. def CopyLine(self, ids=[]):
  392. """Copy features from (background) vector map
  393. @param ids list of line ids to be copied
  394. @return number of copied features
  395. @return -1 on error
  396. """
  397. bgmap = str(UserSettings.Get(group='vdigit', key='bgmap',
  398. subkey='value', internal=True))
  399. if len(bgmap) > 0:
  400. ret = self.digit.CopyLines(ids, bgmap)
  401. else:
  402. ret = self.digit.CopyLines(ids, None)
  403. if ret > 0:
  404. self.toolbar.EnableUndo()
  405. return ret
  406. def CopyCats(self, fromId, toId, copyAttrb=False):
  407. """Copy given categories to objects with id listed in ids
  408. @param cats ids of 'from' feature
  409. @param ids ids of 'to' feature(s)
  410. @return number of modified features
  411. @return -1 on error
  412. """
  413. if len(fromId) == 0 or len(toId) == 0:
  414. return 0
  415. ret = self.digit.CopyCats(fromId, toId, copyAttrb)
  416. if ret > 0:
  417. self.toolbar.EnableUndo()
  418. return ret
  419. def SelectLinesByQuery(self, pos1, pos2):
  420. """Select features by query
  421. @param pos1, pos2 bounding box definition
  422. """
  423. thresh = self.SelectLinesByQueryThresh()
  424. w, n = pos1
  425. e, s = pos2
  426. query = wxvdigit.QUERY_UNKNOWN
  427. if UserSettings.Get(group='vdigit', key='query', subkey='selection') == 0:
  428. query = wxvdigit.QUERY_LENGTH
  429. else:
  430. query = wxvdigit.QUERY_DANGLE
  431. type = wxvdigit.GV_POINTS | wxvdigit.GV_LINES # TODO: 3D
  432. ids = self.digit.SelectLinesByQuery(w, n, 0.0, e, s, 1000.0,
  433. UserSettings.Get(group='vdigit', key='query', subkey='box'),
  434. query, type, thresh)
  435. Debug.msg(4, "VDigit.SelectLinesByQuery(): %s" % \
  436. ",".join(["%d" % v for v in ids]))
  437. return ids
  438. def GetLineCats(self, line=-1):
  439. """Get layer/category pairs from given (selected) line
  440. @param line feature id (-1 for first selected line)
  441. """
  442. return dict(self.digit.GetLineCats(line))
  443. def SetLineCats(self, line, layer, cats, add=True):
  444. """Set categories for given line and layer
  445. @param line feature id
  446. @param layer layer number (-1 for first selected line)
  447. @param cats list of categories
  448. @param add if True to add, otherwise do delete categories
  449. @return new feature id (feature need to be rewritten)
  450. @return -1 on error
  451. """
  452. ret = self.digit.SetLineCats(line, layer, cats, add)
  453. if ret > 0:
  454. self.toolbar.EnableUndo()
  455. return ret
  456. def GetLayers(self):
  457. """Get list of layers"""
  458. return self.digit.GetLayers()
  459. def TypeConvForSelectedLines(self):
  460. """Feature type conversion for selected objects.
  461. Supported conversions:
  462. - point <-> centroid
  463. - line <-> boundary
  464. @return number of modified features
  465. @return -1 on error
  466. """
  467. ret = self.digit.TypeConvLines()
  468. if ret > 0:
  469. self.toolbar.EnableUndo()
  470. return ret
  471. def Undo(self, level=-1):
  472. """Undo action
  473. @param level levels to undo (0 to revert all)
  474. @return id of current changeset
  475. """
  476. try:
  477. ret = self.digit.Undo(level)
  478. except SystemExit:
  479. ret = -2
  480. if ret == -2:
  481. raise gcmd.DigitError, _("Undo failed, data corrupted.")
  482. self.mapWindow.UpdateMap(render=False)
  483. if ret < 0: # disable undo tool
  484. self.toolbar.EnableUndo(False)
  485. def GetUndoLevel(self):
  486. """Get undo level (number of active changesets)"""
  487. return self.digit.GetUndoLevel()
  488. def UpdateSettings(self):
  489. """Update digit settigs"""
  490. if self.digit:
  491. self.digit.UpdateSettings(UserSettings.Get(group='vdigit', key='breakLines',
  492. subkey='enabled'))
  493. def __getSnapThreshold(self):
  494. """Get snap mode and threshold value
  495. @return (snap, thresh)
  496. """
  497. thresh = self.driver.GetThreshold()
  498. if thresh > 0.0:
  499. if UserSettings.Get(group='vdigit', key='snapToVertex', subkey='enabled') is True:
  500. snap = wxvdigit.SNAPVERTEX
  501. else:
  502. snap = wxvdigit.SNAP
  503. else:
  504. snap = wxvdigit.NO_SNAP
  505. return (snap, thresh)
  506. class Digit(VDigit):
  507. """Default digit class"""
  508. def __init__(self, mapwindow):
  509. VDigit.__init__(self, mapwindow)
  510. self.type = 'vdigit'
  511. def __del__(self):
  512. VDigit.__del__(self)
  513. class AbstractDisplayDriver:
  514. """Abstract classs for display driver"""
  515. def __init__(self, parent, mapwindow):
  516. """Initialization
  517. @param parent
  518. @param mapwindow reference to mapwindow (MFrame)
  519. """
  520. self.parent = parent
  521. self.mapwindow = mapwindow
  522. self.ids = {} # dict[g6id] = [pdcId]
  523. self.selected = [] # list of selected objects (grassId!)
  524. def GetThreshold(self, type='snapping', value=None, units=None):
  525. """Return threshold in map units
  526. @param value threshold to be set up
  527. @param units units (map, screen)
  528. """
  529. if value is None:
  530. value = UserSettings.Get(group='vdigit', key=type, subkey='value')
  531. if units is None:
  532. units = UserSettings.Get(group='vdigit', key=type, subkey='units')
  533. reg = self.mapwindow.Map.region
  534. if value < 0:
  535. value = (reg['nsres'] + reg['ewres']) / 2.
  536. if units == "screen pixels":
  537. # pixel -> cell
  538. if reg['nsres'] > reg['ewres']:
  539. res = reg['nsres']
  540. else:
  541. res = reg['ewres']
  542. threshold = value * res
  543. else:
  544. threshold = value
  545. Debug.msg(4, "AbstractDisplayDriver.GetThreshold(): type=%s, thresh=%f" % (type, threshold))
  546. return threshold
  547. class CDisplayDriver(AbstractDisplayDriver):
  548. """
  549. Display driver using grass7_wxdriver module
  550. """
  551. def __init__(self, parent, mapwindow):
  552. """Initialization
  553. @param parent
  554. @param mapwindow reference to mapwindow (MFrame)
  555. """
  556. AbstractDisplayDriver.__init__(self, parent, mapwindow)
  557. self.mapWindow = mapwindow
  558. # initialize wx display driver
  559. try:
  560. self.__display = wxvdigit.DisplayDriver(mapwindow.pdcVector,
  561. mapwindow.pdcTmp)
  562. except:
  563. self.__display = None
  564. self.UpdateSettings()
  565. def GetDevice(self):
  566. """Get device"""
  567. return self.__display
  568. def SetDevice(self, pdc):
  569. """Set device for driver
  570. @param pdc wx.PseudoDC instance
  571. """
  572. self.__display.SetDevice(pdc)
  573. def Reset(self, map):
  574. """Reset map
  575. Open or close the vector map by driver.
  576. @param map map name or None to close the map
  577. @return 0 on success (close map)
  578. @return topo level on success (open map)
  579. @return non-zero (close map)
  580. @return -1 on error (open map)
  581. """
  582. if map:
  583. name, mapset = map.split('@')
  584. try:
  585. ret = self.__display.OpenMap(str(name), str(mapset), True)
  586. except SystemExit:
  587. ret = -1
  588. else:
  589. ret = self.__display.CloseMap()
  590. return ret
  591. def ReloadMap(self):
  592. """Reload map (close and re-open).
  593. Needed for v.edit, TODO: get rid of that..."""
  594. Debug.msg(4, "CDisplayDriver.ReloadMap():")
  595. self.__display.ReloadMap()
  596. def DrawMap(self):
  597. """Draw vector map layer content
  598. @return wx.Image instance
  599. """
  600. nlines = self.__display.DrawMap(True) # force
  601. Debug.msg(3, "CDisplayDriver.DrawMap(): nlines=%d" % nlines)
  602. return nlines
  603. def SelectLinesByBox(self, begin, end, type=0, drawSeg=False):
  604. """Select vector features by given bounding box.
  605. If type is given, only vector features of given type are selected.
  606. @param begin,end bounding box definition
  607. @param type select only objects of given type
  608. """
  609. x1, y1 = begin
  610. x2, y2 = end
  611. inBox = UserSettings.Get(group='vdigit', key='selectInside', subkey='enabled')
  612. nselected = self.__display.SelectLinesByBox(x1, y1, -1.0 * wxvdigit.PORT_DOUBLE_MAX,
  613. x2, y2, wxvdigit.PORT_DOUBLE_MAX,
  614. type, inBox, drawSeg)
  615. Debug.msg(4, "CDisplayDriver.SelectLinesByBox(): selected=%d" % \
  616. nselected)
  617. return nselected
  618. def SelectLineByPoint(self, point, type=0):
  619. """Select vector feature by coordinates of click point (in given threshold).
  620. If type is given, only vector features of given type are selected.
  621. @param point click coordinates (bounding box given by threshold)
  622. @param type select only objects of given type
  623. """
  624. pointOnLine = self.__display.SelectLineByPoint(point[0], point[1], 0.0,
  625. self.GetThreshold(type='selectThresh'),
  626. type, 0); # without_z
  627. if len(pointOnLine) > 0:
  628. Debug.msg(4, "CDisplayDriver.SelectLineByPoint(): pointOnLine=%f,%f" % \
  629. (pointOnLine[0], pointOnLine[1]))
  630. return pointOnLine
  631. else:
  632. Debug.msg(4, "CDisplayDriver.SelectLineByPoint(): no line found")
  633. return None
  634. def GetSelected(self, grassId=True):
  635. """Return ids of selected vector features
  636. @param grassId if grassId is True returns GRASS ids, otherwise
  637. internal ids of objects drawn in PseudoDC"""
  638. if grassId:
  639. selected = self.__display.GetSelected(True)
  640. else:
  641. selected = self.__display.GetSelected(False)
  642. Debug.msg(4, "CDisplayDriver.GetSelected(): grassId=%d, ids=%s" % \
  643. (grassId, (",".join(["%d" % v for v in selected]))))
  644. return selected
  645. def GetSelectedCoord(self):
  646. """Return ids of selected vector features and their coordinates"""
  647. return dict(self.__display.GetSelectedCoord())
  648. def GetRegionSelected(self):
  649. """Get minimal region extent of selected features (ids/cats)"""
  650. return self.__display.GetRegionSelected()
  651. def GetDuplicates(self):
  652. """Return ids of (selected) duplicated vector features
  653. """
  654. # -> id : (list of ids)
  655. dupl = dict(self.__display.GetDuplicates())
  656. # -> id : ((id, cat), ...)
  657. dupl_full = {}
  658. for key in dupl.keys():
  659. dupl_full[key] = []
  660. for id in dupl[key]:
  661. catStr = ''
  662. cats = self.parent.GetLineCats(line=id)
  663. for layer in cats.keys():
  664. if len(cats[layer]) > 0:
  665. catStr = "%d: (" % layer
  666. for cat in cats[layer]:
  667. catStr += "%d," % cat
  668. catStr = catStr.rstrip(',')
  669. catStr += ')'
  670. dupl_full[key].append([id, catStr])
  671. return dupl_full
  672. def GetSelectedVertex(self, coords):
  673. """Get PseudoDC id(s) of vertex (of selected line)
  674. on position 'coords'
  675. @param coords click position
  676. """
  677. x, y = coords
  678. id = self.__display.GetSelectedVertex(x, y, self.GetThreshold(type='selectThresh'))
  679. Debug.msg(4, "CDisplayDriver.GetSelectedVertex(): id=%s" % \
  680. (",".join(["%d" % v for v in id])))
  681. return id
  682. def SetSelected(self, id, cats=False):
  683. """Set selected vector features
  684. @param id line id to be selected
  685. @param cats if True expect categories instead of feature ids
  686. """
  687. Debug.msg(4, "CDisplayDriver.SetSelected(): id=%s" % \
  688. id)
  689. self.__display.SetSelected(id, cats)
  690. def UnSelect(self, id):
  691. """Unselect vector features
  692. @param id list of feature id(s)
  693. """
  694. Debug.msg(4, "CDisplayDriver.UnSelect(): id=%s" % \
  695. ",".join(["%d" % v for v in id]))
  696. self.__display.UnSelect(id)
  697. def UpdateRegion(self):
  698. """Set geographical region
  699. Needed for 'cell2pixel' conversion"""
  700. map = self.mapwindow.Map
  701. reg = map.region
  702. self.__display.SetRegion(reg['n'],
  703. reg['s'],
  704. reg['e'],
  705. reg['w'],
  706. reg['nsres'],
  707. reg['ewres'],
  708. reg['center_easting'],
  709. reg['center_northing'],
  710. map.width, map.height)
  711. def GetMapBoundingBox(self):
  712. """Return bounding box of given vector map layer
  713. @return (w,s,b,e,n,t)
  714. """
  715. return self.__display.GetMapBoundingBox()
  716. def DrawSelected(self, draw=True):
  717. """Show/hide selected features"""
  718. self.__display.DrawSelected(draw)
  719. def UpdateSettings(self, alpha=255):
  720. """Update display driver settings"""
  721. # TODO map units
  722. if not self.__display:
  723. return
  724. color = {}
  725. for symbol in ("highlight",
  726. "highlightDupl",
  727. "point",
  728. "line",
  729. "boundaryNo",
  730. "boundaryOne",
  731. "boundaryTwo",
  732. "centroidIn",
  733. "centroidOut",
  734. "centroidDup",
  735. "nodeOne",
  736. "nodeTwo",
  737. "vertex",
  738. "area",
  739. "direction"):
  740. color[symbol] = wx.Color(UserSettings.Get(group='vdigit', key='symbol',
  741. subkey=[symbol, 'color'])[0],
  742. UserSettings.Get(group='vdigit', key='symbol',
  743. subkey=[symbol, 'color'])[1],
  744. UserSettings.Get(group='vdigit', key='symbol',
  745. subkey=[symbol, 'color'])[2]).GetRGB()
  746. self.__display.UpdateSettings (color['highlight'],
  747. UserSettings.Get(group='vdigit', key='checkForDupl',
  748. subkey='enabled'),
  749. color['highlightDupl'],
  750. UserSettings.Get(group='vdigit', key='symbol',
  751. subkey=['point', 'enabled']),
  752. color['point'],
  753. UserSettings.Get(group='vdigit', key='symbol',
  754. subkey=['line', 'enabled']),
  755. color['line'],
  756. UserSettings.Get(group='vdigit', key='symbol',
  757. subkey=['boundaryNo', 'enabled']),
  758. color['boundaryNo'],
  759. UserSettings.Get(group='vdigit', key='symbol',
  760. subkey=['boundaryOne', 'enabled']),
  761. color['boundaryOne'],
  762. UserSettings.Get(group='vdigit', key='symbol',
  763. subkey=['boundaryTwo', 'enabled']),
  764. color['boundaryTwo'],
  765. UserSettings.Get(group='vdigit', key='symbol',
  766. subkey=['centroidIn', 'enabled']),
  767. color['centroidIn'],
  768. UserSettings.Get(group='vdigit', key='symbol',
  769. subkey=['centroidOut', 'enabled']),
  770. color['centroidOut'],
  771. UserSettings.Get(group='vdigit', key='symbol',
  772. subkey=['centroidDup', 'enabled']),
  773. color['centroidDup'],
  774. UserSettings.Get(group='vdigit', key='symbol',
  775. subkey=['nodeOne', 'enabled']),
  776. color['nodeOne'],
  777. UserSettings.Get(group='vdigit', key='symbol',
  778. subkey=['nodeTwo', 'enabled']),
  779. color['nodeTwo'],
  780. UserSettings.Get(group='vdigit', key='symbol',
  781. subkey=['vertex', 'enabled']),
  782. color['vertex'],
  783. UserSettings.Get(group='vdigit', key='symbol',
  784. subkey=['area', 'enabled']),
  785. color['area'],
  786. UserSettings.Get(group='vdigit', key='symbol',
  787. subkey=['direction', 'enabled']),
  788. color['direction'],
  789. UserSettings.Get(group='vdigit', key='lineWidth',
  790. subkey='value'),
  791. alpha)
  792. class VDigitSettingsDialog(wx.Dialog):
  793. """
  794. Standard settings dialog for digitization purposes
  795. """
  796. def __init__(self, parent, title, style=wx.DEFAULT_DIALOG_STYLE):
  797. wx.Dialog.__init__(self, parent=parent, id=wx.ID_ANY, title=title, style=style)
  798. self.parent = parent # mapdisplay.BufferedWindow class instance
  799. # notebook
  800. notebook = wx.Notebook(parent=self, id=wx.ID_ANY, style=wx.BK_DEFAULT)
  801. self.__CreateSymbologyPage(notebook)
  802. parent.digit.SetCategory() # update category number (next to use)
  803. self.__CreateGeneralPage(notebook)
  804. self.__CreateAttributesPage(notebook)
  805. self.__CreateQueryPage(notebook)
  806. # buttons
  807. btnApply = wx.Button(self, wx.ID_APPLY)
  808. btnCancel = wx.Button(self, wx.ID_CANCEL)
  809. btnSave = wx.Button(self, wx.ID_SAVE)
  810. btnSave.SetDefault()
  811. # bindigs
  812. btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
  813. btnApply.SetToolTipString(_("Apply changes for this session"))
  814. btnApply.SetDefault()
  815. btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
  816. btnSave.SetToolTipString(_("Close dialog and save changes to user settings file"))
  817. btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
  818. btnCancel.SetToolTipString(_("Close dialog and ignore changes"))
  819. # sizers
  820. btnSizer = wx.StdDialogButtonSizer()
  821. btnSizer.AddButton(btnCancel)
  822. btnSizer.AddButton(btnApply)
  823. btnSizer.AddButton(btnSave)
  824. btnSizer.Realize()
  825. mainSizer = wx.BoxSizer(wx.VERTICAL)
  826. mainSizer.Add(item=notebook, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
  827. mainSizer.Add(item=btnSizer, proportion=0,
  828. flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
  829. self.Bind(wx.EVT_CLOSE, self.OnCancel)
  830. self.SetSizer(mainSizer)
  831. mainSizer.Fit(self)
  832. def __CreateSymbologyPage(self, notebook):
  833. """Create notebook page concerning with symbology settings"""
  834. panel = wx.Panel(parent=notebook, id=wx.ID_ANY)
  835. notebook.AddPage(page=panel, text=_("Symbology"))
  836. sizer = wx.BoxSizer(wx.VERTICAL)
  837. flexSizer = wx.FlexGridSizer (cols=3, hgap=5, vgap=5)
  838. flexSizer.AddGrowableCol(0)
  839. self.symbology = {}
  840. for label, key in self.__SymbologyData():
  841. textLabel = wx.StaticText(panel, wx.ID_ANY, label)
  842. color = csel.ColourSelect(panel, id=wx.ID_ANY,
  843. colour=UserSettings.Get(group='vdigit', key='symbol',
  844. subkey=[key, 'color']), size=(25, 25))
  845. isEnabled = UserSettings.Get(group='vdigit', key='symbol',
  846. subkey=[key, 'enabled'])
  847. if isEnabled is not None:
  848. enabled = wx.CheckBox(panel, id=wx.ID_ANY, label="")
  849. enabled.SetValue(isEnabled)
  850. self.symbology[key] = (enabled, color)
  851. else:
  852. enabled = (1, 1)
  853. self.symbology[key] = (None, color)
  854. flexSizer.Add(textLabel, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
  855. flexSizer.Add(enabled, proportion=0, flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
  856. flexSizer.Add(color, proportion=0, flag=wx.ALIGN_RIGHT | wx.FIXED_MINSIZE)
  857. color.SetName("GetColour")
  858. sizer.Add(item=flexSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=10)
  859. panel.SetSizer(sizer)
  860. return panel
  861. def __CreateGeneralPage(self, notebook):
  862. """Create notebook page concerning with symbology settings"""
  863. panel = wx.Panel(parent=notebook, id=wx.ID_ANY)
  864. notebook.AddPage(page=panel, text=_("General"))
  865. border = wx.BoxSizer(wx.VERTICAL)
  866. #
  867. # display section
  868. #
  869. box = wx.StaticBox (parent=panel, id=wx.ID_ANY, label=" %s " % _("Display"))
  870. sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
  871. flexSizer = wx.FlexGridSizer (cols=3, hgap=5, vgap=5)
  872. flexSizer.AddGrowableCol(0)
  873. # line width
  874. text = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("Line width"))
  875. self.lineWidthValue = wx.SpinCtrl(parent=panel, id=wx.ID_ANY, size=(75, -1),
  876. initial=UserSettings.Get(group='vdigit', key="lineWidth", subkey='value'),
  877. min=1, max=1e6)
  878. units = wx.StaticText(parent=panel, id=wx.ID_ANY, size=(115, -1),
  879. label=UserSettings.Get(group='vdigit', key="lineWidth", subkey='units'),
  880. style=wx.ALIGN_LEFT)
  881. flexSizer.Add(text, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
  882. flexSizer.Add(self.lineWidthValue, proportion=0, flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
  883. flexSizer.Add(units, proportion=0, flag=wx.ALIGN_RIGHT | wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL | wx.LEFT,
  884. border=10)
  885. sizer.Add(item=flexSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=1)
  886. border.Add(item=sizer, proportion=0, flag=wx.ALL | wx.EXPAND, border=5)
  887. #
  888. # snapping section
  889. #
  890. box = wx.StaticBox (parent=panel, id=wx.ID_ANY, label=" %s " % _("Snapping"))
  891. sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
  892. flexSizer = wx.FlexGridSizer (cols=3, hgap=5, vgap=5)
  893. flexSizer.AddGrowableCol(0)
  894. # snapping
  895. text = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("Snapping threshold"))
  896. self.snappingValue = wx.SpinCtrl(parent=panel, id=wx.ID_ANY, size=(75, -1),
  897. initial=UserSettings.Get(group='vdigit', key="snapping", subkey='value'),
  898. min=-1, max=1e6)
  899. self.snappingValue.Bind(wx.EVT_SPINCTRL, self.OnChangeSnappingValue)
  900. self.snappingValue.Bind(wx.EVT_TEXT, self.OnChangeSnappingValue)
  901. self.snappingUnit = wx.Choice(parent=panel, id=wx.ID_ANY, size=(125, -1),
  902. choices=["screen pixels", "map units"])
  903. self.snappingUnit.SetStringSelection(UserSettings.Get(group='vdigit', key="snapping", subkey='units'))
  904. self.snappingUnit.Bind(wx.EVT_CHOICE, self.OnChangeSnappingUnits)
  905. flexSizer.Add(text, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
  906. flexSizer.Add(self.snappingValue, proportion=0, flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
  907. flexSizer.Add(self.snappingUnit, proportion=0, flag=wx.ALIGN_RIGHT | wx.FIXED_MINSIZE)
  908. vertexSizer = wx.BoxSizer(wx.VERTICAL)
  909. self.snapVertex = wx.CheckBox(parent=panel, id=wx.ID_ANY,
  910. label=_("Snap also to vertex"))
  911. self.snapVertex.SetValue(UserSettings.Get(group='vdigit', key="snapToVertex", subkey='enabled'))
  912. vertexSizer.Add(item=self.snapVertex, proportion=0, flag=wx.EXPAND)
  913. self.mapUnits = self.parent.MapWindow.Map.ProjInfo()['units']
  914. self.snappingInfo = wx.StaticText(parent=panel, id=wx.ID_ANY,
  915. label=_("Snapping threshold is %(value).1f %(units)s") % \
  916. {'value' : self.parent.digit.driver.GetThreshold(),
  917. 'units' : self.mapUnits})
  918. vertexSizer.Add(item=self.snappingInfo, proportion=0,
  919. flag=wx.ALL | wx.EXPAND, border=1)
  920. sizer.Add(item=flexSizer, proportion=1, flag=wx.EXPAND)
  921. sizer.Add(item=vertexSizer, proportion=1, flag=wx.EXPAND)
  922. border.Add(item=sizer, proportion=0, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=5)
  923. #
  924. # select box
  925. #
  926. box = wx.StaticBox (parent=panel, id=wx.ID_ANY, label=" %s " % _("Select vector features"))
  927. # feature type
  928. sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
  929. inSizer = wx.BoxSizer(wx.HORIZONTAL)
  930. self.selectFeature = {}
  931. for feature in ('point', 'line',
  932. 'centroid', 'boundary'):
  933. chkbox = wx.CheckBox(parent=panel, label=feature)
  934. self.selectFeature[feature] = chkbox.GetId()
  935. chkbox.SetValue(UserSettings.Get(group='vdigit', key='selectType',
  936. subkey=[feature, 'enabled']))
  937. inSizer.Add(item=chkbox, proportion=0,
  938. flag=wx.EXPAND | wx.ALL, border=5)
  939. sizer.Add(item=inSizer, proportion=0, flag=wx.EXPAND)
  940. # threshold
  941. flexSizer = wx.FlexGridSizer (cols=3, hgap=5, vgap=5)
  942. flexSizer.AddGrowableCol(0)
  943. text = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("Select threshold"))
  944. self.selectThreshValue = wx.SpinCtrl(parent=panel, id=wx.ID_ANY, size=(75, -1),
  945. initial=UserSettings.Get(group='vdigit', key="selectThresh", subkey='value'),
  946. min=1, max=1e6)
  947. units = wx.StaticText(parent=panel, id=wx.ID_ANY, size=(115, -1),
  948. label=UserSettings.Get(group='vdigit', key="lineWidth", subkey='units'),
  949. style=wx.ALIGN_LEFT)
  950. flexSizer.Add(text, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
  951. flexSizer.Add(self.selectThreshValue, proportion=0, flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
  952. flexSizer.Add(units, proportion=0, flag=wx.ALIGN_RIGHT | wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL | wx.LEFT,
  953. border=10)
  954. self.selectIn = wx.CheckBox(parent=panel, id=wx.ID_ANY,
  955. label=_("Select only features inside of selection bounding box"))
  956. self.selectIn.SetValue(UserSettings.Get(group='vdigit', key="selectInside", subkey='enabled'))
  957. self.selectIn.SetToolTipString(_("By default are selected all features overlapping selection bounding box "))
  958. self.checkForDupl = wx.CheckBox(parent=panel, id=wx.ID_ANY,
  959. label=_("Check for duplicates"))
  960. self.checkForDupl.SetValue(UserSettings.Get(group='vdigit', key="checkForDupl", subkey='enabled'))
  961. sizer.Add(item=flexSizer, proportion=0, flag=wx.EXPAND)
  962. sizer.Add(item=self.selectIn, proportion=0, flag=wx.EXPAND | wx.ALL, border=1)
  963. sizer.Add(item=self.checkForDupl, proportion=0, flag=wx.EXPAND | wx.ALL, border=1)
  964. border.Add(item=sizer, proportion=0, flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)
  965. #
  966. # digitize lines box
  967. #
  968. box = wx.StaticBox (parent=panel, id=wx.ID_ANY, label=" %s " % _("Digitize line features"))
  969. sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
  970. self.intersect = wx.CheckBox(parent=panel, label=_("Break lines at intersection"))
  971. self.intersect.SetValue(UserSettings.Get(group='vdigit', key='breakLines', subkey='enabled'))
  972. sizer.Add(item=self.intersect, proportion=0, flag=wx.ALL | wx.EXPAND, border=1)
  973. border.Add(item=sizer, proportion=0, flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)
  974. #
  975. # save-on-exit box
  976. #
  977. box = wx.StaticBox (parent=panel, id=wx.ID_ANY, label=" %s " % _("Save changes"))
  978. # save changes on exit?
  979. sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
  980. self.save = wx.CheckBox(parent=panel, label=_("Save changes on exit"))
  981. self.save.SetValue(UserSettings.Get(group='vdigit', key='saveOnExit', subkey='enabled'))
  982. sizer.Add(item=self.save, proportion=0, flag=wx.ALL | wx.EXPAND, border=1)
  983. border.Add(item=sizer, proportion=0, flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)
  984. panel.SetSizer(border)
  985. return panel
  986. def __CreateQueryPage(self, notebook):
  987. """Create notebook page for query tool"""
  988. panel = wx.Panel(parent=notebook, id=wx.ID_ANY)
  989. notebook.AddPage(page=panel, text=_("Query tool"))
  990. border = wx.BoxSizer(wx.VERTICAL)
  991. #
  992. # query tool box
  993. #
  994. box = wx.StaticBox (parent=panel, id=wx.ID_ANY, label=" %s " % _("Choose query tool"))
  995. sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
  996. LocUnits = self.parent.MapWindow.Map.ProjInfo()['units']
  997. self.queryBox = wx.CheckBox(parent=panel, id=wx.ID_ANY, label=_("Select by box"))
  998. self.queryBox.SetValue(UserSettings.Get(group='vdigit', key="query", subkey='box'))
  999. sizer.Add(item=self.queryBox, proportion=0, flag=wx.ALL | wx.EXPAND, border=1)
  1000. sizer.Add((0, 5))
  1001. #
  1002. # length
  1003. #
  1004. self.queryLength = wx.RadioButton(parent=panel, id=wx.ID_ANY, label=_("length"))
  1005. self.queryLength.Bind(wx.EVT_RADIOBUTTON, self.OnChangeQuery)
  1006. sizer.Add(item=self.queryLength, proportion=0, flag=wx.ALL | wx.EXPAND, border=1)
  1007. flexSizer = wx.FlexGridSizer (cols=4, hgap=5, vgap=5)
  1008. flexSizer.AddGrowableCol(0)
  1009. txt = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("Select lines"))
  1010. self.queryLengthSL = wx.Choice (parent=panel, id=wx.ID_ANY,
  1011. choices = [_("shorter than"), _("longer than")])
  1012. self.queryLengthSL.SetSelection(UserSettings.Get(group='vdigit', key="queryLength", subkey='than-selection'))
  1013. self.queryLengthValue = wx.SpinCtrl(parent=panel, id=wx.ID_ANY, size=(100, -1),
  1014. initial=1,
  1015. min=0, max=1e6)
  1016. self.queryLengthValue.SetValue(UserSettings.Get(group='vdigit', key="queryLength", subkey='thresh'))
  1017. units = wx.StaticText(parent=panel, id=wx.ID_ANY, label="%s" % LocUnits)
  1018. flexSizer.Add(txt, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
  1019. flexSizer.Add(self.queryLengthSL, proportion=0, flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
  1020. flexSizer.Add(self.queryLengthValue, proportion=0, flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
  1021. flexSizer.Add(units, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
  1022. sizer.Add(item=flexSizer, proportion=0, flag=wx.ALL | wx.EXPAND, border=1)
  1023. #
  1024. # dangle
  1025. #
  1026. self.queryDangle = wx.RadioButton(parent=panel, id=wx.ID_ANY, label=_("dangle"))
  1027. self.queryDangle.Bind(wx.EVT_RADIOBUTTON, self.OnChangeQuery)
  1028. sizer.Add(item=self.queryDangle, proportion=0, flag=wx.ALL | wx.EXPAND, border=1)
  1029. flexSizer = wx.FlexGridSizer (cols=4, hgap=5, vgap=5)
  1030. flexSizer.AddGrowableCol(0)
  1031. txt = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("Select dangles"))
  1032. self.queryDangleSL = wx.Choice (parent=panel, id=wx.ID_ANY,
  1033. choices = [_("shorter than"), _("longer than")])
  1034. self.queryDangleSL.SetSelection(UserSettings.Get(group='vdigit', key="queryDangle", subkey='than-selection'))
  1035. self.queryDangleValue = wx.SpinCtrl(parent=panel, id=wx.ID_ANY, size=(100, -1),
  1036. initial=1,
  1037. min=0, max=1e6)
  1038. self.queryDangleValue.SetValue(UserSettings.Get(group='vdigit', key="queryDangle", subkey='thresh'))
  1039. units = wx.StaticText(parent=panel, id=wx.ID_ANY, label="%s" % LocUnits)
  1040. flexSizer.Add(txt, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
  1041. flexSizer.Add(self.queryDangleSL, proportion=0, flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
  1042. flexSizer.Add(self.queryDangleValue, proportion=0, flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
  1043. flexSizer.Add(units, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
  1044. sizer.Add(item=flexSizer, proportion=0, flag=wx.ALL | wx.EXPAND, border=1)
  1045. if UserSettings.Get(group='vdigit', key="query", subkey='selection') == 0:
  1046. self.queryLength.SetValue(True)
  1047. else:
  1048. self.queryDangle.SetValue(True)
  1049. # enable & disable items
  1050. self.OnChangeQuery(None)
  1051. border.Add(item=sizer, proportion=0, flag=wx.ALL | wx.EXPAND, border=5)
  1052. panel.SetSizer(border)
  1053. return panel
  1054. def __CreateAttributesPage(self, notebook):
  1055. """Create notebook page for query tool"""
  1056. panel = wx.Panel(parent=notebook, id=wx.ID_ANY)
  1057. notebook.AddPage(page=panel, text=_("Attributes"))
  1058. border = wx.BoxSizer(wx.VERTICAL)
  1059. #
  1060. # add new record
  1061. #
  1062. box = wx.StaticBox (parent=panel, id=wx.ID_ANY, label=" %s " % _("Digitize new feature"))
  1063. sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
  1064. # checkbox
  1065. self.addRecord = wx.CheckBox(parent=panel, id=wx.ID_ANY,
  1066. label=_("Add new record into table"))
  1067. self.addRecord.SetValue(UserSettings.Get(group='vdigit', key="addRecord", subkey='enabled'))
  1068. sizer.Add(item=self.addRecord, proportion=0, flag=wx.ALL | wx.EXPAND, border=1)
  1069. # settings
  1070. flexSizer = wx.FlexGridSizer(cols=2, hgap=3, vgap=3)
  1071. flexSizer.AddGrowableCol(0)
  1072. settings = ((_("Layer"), 1), (_("Category"), 1), (_("Mode"), _("Next to use")))
  1073. # layer
  1074. text = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("Layer"))
  1075. if self.parent.digit.map:
  1076. layers = map(str, self.parent.digit.GetLayers())
  1077. if len(layers) == 0:
  1078. layers = [str(UserSettings.Get(group='vdigit', key="layer", subkey='value')), ]
  1079. else:
  1080. layers = [str(UserSettings.Get(group='vdigit', key="layer", subkey='value')), ]
  1081. self.layer = wx.Choice(parent=panel, id=wx.ID_ANY, size=(125, -1),
  1082. choices=layers)
  1083. self.layer.SetStringSelection(str(UserSettings.Get(group='vdigit', key="layer", subkey='value')))
  1084. flexSizer.Add(item=text, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
  1085. flexSizer.Add(item=self.layer, proportion=0,
  1086. flag=wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
  1087. # category number
  1088. text = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("Category number"))
  1089. self.category = wx.SpinCtrl(parent=panel, id=wx.ID_ANY, size=(125, -1),
  1090. initial=UserSettings.Get(group='vdigit', key="category", subkey='value'),
  1091. min=-1e9, max=1e9)
  1092. if UserSettings.Get(group='vdigit', key="categoryMode", subkey='selection') != 1:
  1093. self.category.Enable(False)
  1094. flexSizer.Add(item=text, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
  1095. flexSizer.Add(item=self.category, proportion=0,
  1096. flag=wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
  1097. # category mode
  1098. text = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("Category mode"))
  1099. self.categoryMode = wx.Choice(parent=panel, id=wx.ID_ANY, size=(125, -1),
  1100. choices=[_("Next to use"), _("Manual entry"), _("No category")])
  1101. self.categoryMode.SetSelection(UserSettings.Get(group='vdigit', key="categoryMode", subkey='selection'))
  1102. flexSizer.Add(item=text, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
  1103. flexSizer.Add(item=self.categoryMode, proportion=0,
  1104. flag=wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
  1105. sizer.Add(item=flexSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=1)
  1106. border.Add(item=sizer, proportion=0,
  1107. flag=wx.ALL | wx.EXPAND, border=5)
  1108. #
  1109. # delete existing record
  1110. #
  1111. box = wx.StaticBox (parent=panel, id=wx.ID_ANY, label=" %s " % _("Delete existing feature(s)"))
  1112. sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
  1113. # checkbox
  1114. self.deleteRecord = wx.CheckBox(parent=panel, id=wx.ID_ANY,
  1115. label=_("Delete record from table"))
  1116. self.deleteRecord.SetValue(UserSettings.Get(group='vdigit', key="delRecord", subkey='enabled'))
  1117. sizer.Add(item=self.deleteRecord, proportion=0, flag=wx.ALL | wx.EXPAND, border=1)
  1118. border.Add(item=sizer, proportion=0,
  1119. flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=5)
  1120. # bindings
  1121. self.Bind(wx.EVT_CHECKBOX, self.OnChangeAddRecord, self.addRecord)
  1122. self.Bind(wx.EVT_CHOICE, self.OnChangeCategoryMode, self.categoryMode)
  1123. self.Bind(wx.EVT_CHOICE, self.OnChangeLayer, self.layer)
  1124. panel.SetSizer(border)
  1125. return panel
  1126. def __SymbologyData(self):
  1127. """
  1128. Data for __CreateSymbologyPage()
  1129. label | checkbox | color
  1130. """
  1131. return (
  1132. # ("Background", "symbolBackground"),
  1133. (_("Highlight"), "highlight"),
  1134. (_("Highlight (duplicates)"), "highlightDupl"),
  1135. (_("Point"), "point"),
  1136. (_("Line"), "line"),
  1137. (_("Boundary (no area)"), "boundaryNo"),
  1138. (_("Boundary (one area)"), "boundaryOne"),
  1139. (_("Boundary (two areas)"), "boundaryTwo"),
  1140. (_("Centroid (in area)"), "centroidIn"),
  1141. (_("Centroid (outside area)"), "centroidOut"),
  1142. (_("Centroid (duplicate in area)"), "centroidDup"),
  1143. (_("Node (one line)"), "nodeOne"),
  1144. (_("Node (two lines)"), "nodeTwo"),
  1145. (_("Vertex"), "vertex"),
  1146. (_("Area (closed boundary + centroid)"), "area"),
  1147. (_("Direction"), "direction"),)
  1148. def OnChangeCategoryMode(self, event):
  1149. """Change category mode"""
  1150. mode = event.GetSelection()
  1151. UserSettings.Set(group='vdigit', key="categoryMode", subkey='selection', value=mode)
  1152. if mode == 1: # manual entry
  1153. self.category.Enable(True)
  1154. elif self.category.IsEnabled(): # disable
  1155. self.category.Enable(False)
  1156. if mode == 2 and self.addRecord.IsChecked(): # no category
  1157. self.addRecord.SetValue(False)
  1158. self.parent.digit.SetCategory()
  1159. self.category.SetValue(UserSettings.Get(group='vdigit', key='category', subkey='value'))
  1160. def OnChangeLayer(self, event):
  1161. """Layer changed"""
  1162. layer = int(event.GetString())
  1163. if layer > 0:
  1164. UserSettings.Set(group='vdigit', key='layer', subkey='value', value=layer)
  1165. self.parent.digit.SetCategory()
  1166. self.category.SetValue(UserSettings.Get(group='vdigit', key='category', subkey='value'))
  1167. event.Skip()
  1168. def OnChangeAddRecord(self, event):
  1169. """Checkbox 'Add new record' status changed"""
  1170. self.category.SetValue(self.parent.digit.SetCategory())
  1171. def OnChangeSnappingValue(self, event):
  1172. """Change snapping value - update static text"""
  1173. value = self.snappingValue.GetValue()
  1174. if value < 0:
  1175. region = self.parent.MapWindow.Map.GetRegion()
  1176. res = (region['nsres'] + region['ewres']) / 2.
  1177. threshold = self.parent.digit.driver.GetThreshold(value=res)
  1178. else:
  1179. if self.snappingUnit.GetStringSelection() == "map units":
  1180. threshold = value
  1181. else:
  1182. threshold = self.parent.digit.driver.GetThreshold(value=value)
  1183. if value == 0:
  1184. self.snappingInfo.SetLabel(_("Snapping disabled"))
  1185. elif value < 0:
  1186. self.snappingInfo.SetLabel(_("Snapping threshold is %(value).1f %(units)s "
  1187. "(based on comp. resolution)") %
  1188. {'value' : threshold,
  1189. 'units' : self.mapUnits.lower()})
  1190. else:
  1191. self.snappingInfo.SetLabel(_("Snapping threshold is %(value).1f %(units)s") %
  1192. {'value' : threshold,
  1193. 'units' : self.mapUnits.lower()})
  1194. event.Skip()
  1195. def OnChangeSnappingUnits(self, event):
  1196. """Snapping units change -> update static text"""
  1197. value = self.snappingValue.GetValue()
  1198. units = self.snappingUnit.GetStringSelection()
  1199. threshold = self.parent.digit.driver.GetThreshold(value=value, units=units)
  1200. if units == "map units":
  1201. self.snappingInfo.SetLabel(_("Snapping threshold is %(value).1f %(units)s") %
  1202. {'value' : value,
  1203. 'units' : self.mapUnits})
  1204. else:
  1205. self.snappingInfo.SetLabel(_("Snapping threshold is %(value).1f %(units)s") %
  1206. {'value' : threshold,
  1207. 'units' : self.mapUnits})
  1208. event.Skip()
  1209. def OnChangeQuery(self, event):
  1210. """Change query"""
  1211. if self.queryLength.GetValue():
  1212. # length
  1213. self.queryLengthSL.Enable(True)
  1214. self.queryLengthValue.Enable(True)
  1215. self.queryDangleSL.Enable(False)
  1216. self.queryDangleValue.Enable(False)
  1217. else:
  1218. # dangle
  1219. self.queryLengthSL.Enable(False)
  1220. self.queryLengthValue.Enable(False)
  1221. self.queryDangleSL.Enable(True)
  1222. self.queryDangleValue.Enable(True)
  1223. def OnSave(self, event):
  1224. """Button 'Save' clicked"""
  1225. self.UpdateSettings()
  1226. self.parent.toolbars['vdigit'].settingsDialog = None
  1227. fileSettings = {}
  1228. UserSettings.ReadSettingsFile(settings=fileSettings)
  1229. fileSettings['vdigit'] = UserSettings.Get(group='vdigit')
  1230. file = UserSettings.SaveToFile(fileSettings)
  1231. self.parent.gismanager.goutput.WriteLog(_('Vector digitizer settings saved to file <%s>.') % file)
  1232. self.Destroy()
  1233. event.Skip()
  1234. def OnApply(self, event):
  1235. """Button 'Apply' clicked"""
  1236. self.UpdateSettings()
  1237. def OnCancel(self, event):
  1238. """Button 'Cancel' clicked"""
  1239. self.parent.toolbars['vdigit'].settingsDialog = None
  1240. self.Destroy()
  1241. if event:
  1242. event.Skip()
  1243. def UpdateSettings(self):
  1244. """Update UserSettings"""
  1245. # symbology
  1246. for key, (enabled, color) in self.symbology.iteritems():
  1247. if enabled:
  1248. UserSettings.Set(group='vdigit', key='symbol',
  1249. subkey=[key, 'enabled'],
  1250. value=enabled.IsChecked())
  1251. UserSettings.Set(group='vdigit', key='symbol',
  1252. subkey=[key, 'color'],
  1253. value=tuple(color.GetColour()))
  1254. else:
  1255. UserSettings.Set(group='vdigit', key='symbol',
  1256. subkey=[key, 'color'],
  1257. value=tuple(color.GetColour()))
  1258. # display
  1259. UserSettings.Set(group='vdigit', key="lineWidth", subkey='value',
  1260. value=int(self.lineWidthValue.GetValue()))
  1261. # snapping
  1262. UserSettings.Set(group='vdigit', key="snapping", subkey='value',
  1263. value=int(self.snappingValue.GetValue()))
  1264. UserSettings.Set(group='vdigit', key="snapping", subkey='units',
  1265. value=self.snappingUnit.GetStringSelection())
  1266. UserSettings.Set(group='vdigit', key="snapToVertex", subkey='enabled',
  1267. value=self.snapVertex.IsChecked())
  1268. # digitize new feature
  1269. UserSettings.Set(group='vdigit', key="addRecord", subkey='enabled',
  1270. value=self.addRecord.IsChecked())
  1271. UserSettings.Set(group='vdigit', key="layer", subkey='value',
  1272. value=int(self.layer.GetStringSelection()))
  1273. UserSettings.Set(group='vdigit', key="category", subkey='value',
  1274. value=int(self.category.GetValue()))
  1275. UserSettings.Set(group='vdigit', key="categoryMode", subkey='selection',
  1276. value=self.categoryMode.GetSelection())
  1277. # delete existing feature
  1278. UserSettings.Set(group='vdigit', key="delRecord", subkey='enabled',
  1279. value=self.deleteRecord.IsChecked())
  1280. # snapping threshold
  1281. self.parent.digit.threshold = self.parent.digit.driver.GetThreshold()
  1282. # query tool
  1283. if self.queryLength.GetValue():
  1284. UserSettings.Set(group='vdigit', key="query", subkey='selection',
  1285. value=0)
  1286. else:
  1287. UserSettings.Set(group='vdigit', key="query", subkey='type',
  1288. value=1)
  1289. UserSettings.Set(group='vdigit', key="query", subkey='box',
  1290. value=self.queryBox.IsChecked())
  1291. UserSettings.Set(group='vdigit', key="queryLength", subkey='than-selection',
  1292. value=self.queryLengthSL.GetSelection())
  1293. UserSettings.Set(group='vdigit', key="queryLength", subkey='thresh',
  1294. value=int(self.queryLengthValue.GetValue()))
  1295. UserSettings.Set(group='vdigit', key="queryDangle", subkey='than-selection',
  1296. value=self.queryDangleSL.GetSelection())
  1297. UserSettings.Set(group='vdigit', key="queryDangle", subkey='thresh',
  1298. value=int(self.queryDangleValue.GetValue()))
  1299. # select features
  1300. for feature in ('point', 'line',
  1301. 'centroid', 'boundary'):
  1302. UserSettings.Set(group='vdigit', key='selectType',
  1303. subkey=[feature, 'enabled'],
  1304. value=self.FindWindowById(self.selectFeature[feature]).IsChecked())
  1305. UserSettings.Set(group='vdigit', key="selectThresh", subkey='value',
  1306. value=int(self.selectThreshValue.GetValue()))
  1307. UserSettings.Set(group='vdigit', key="checkForDupl", subkey='enabled',
  1308. value=self.checkForDupl.IsChecked())
  1309. UserSettings.Set(group='vdigit', key="selectInside", subkey='enabled',
  1310. value=self.selectIn.IsChecked())
  1311. # on-exit
  1312. UserSettings.Set(group='vdigit', key="saveOnExit", subkey='enabled',
  1313. value=self.save.IsChecked())
  1314. # break lines
  1315. UserSettings.Set(group='vdigit', key="breakLines", subkey='enabled',
  1316. value=self.intersect.IsChecked())
  1317. # update driver settings
  1318. self.parent.digit.driver.UpdateSettings()
  1319. # update digit settings
  1320. self.parent.digit.UpdateSettings()
  1321. # redraw map if auto-rendering is enabled
  1322. if self.parent.autoRender.GetValue():
  1323. self.parent.OnRender(None)
  1324. class VDigitCategoryDialog(wx.Dialog, listmix.ColumnSorterMixin):
  1325. """
  1326. Dialog used to display/modify categories of vector objects
  1327. @param parent
  1328. @param title dialog title
  1329. @param query {coordinates, qdist} - used by v.edit/v.what
  1330. @param cats directory of lines (layer/categories) - used by vdigit
  1331. @param pos
  1332. @param style
  1333. """
  1334. def __init__(self, parent, title,
  1335. map, query=None, cats=None,
  1336. pos=wx.DefaultPosition,
  1337. style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER):
  1338. # parent
  1339. self.parent = parent # mapdisplay.BufferedWindow class instance
  1340. # map name
  1341. self.map = map
  1342. # line : {layer: [categories]}
  1343. self.cats = {}
  1344. # do not display dialog if no line is found (-> self.cats)
  1345. if cats is None:
  1346. if self.__GetCategories(query[0], query[1]) == 0 or not self.line:
  1347. Debug.msg(3, "VDigitCategoryDialog(): nothing found!")
  1348. else:
  1349. self.cats = cats
  1350. for line in cats.keys():
  1351. for layer in cats[line].keys():
  1352. self.cats[line][layer] = list(cats[line][layer])
  1353. layers = []
  1354. for layer in self.parent.parent.digit.GetLayers():
  1355. layers.append(str(layer))
  1356. # make copy of cats (used for 'reload')
  1357. self.cats_orig = copy.deepcopy(self.cats)
  1358. wx.Dialog.__init__(self, parent=self.parent, id=wx.ID_ANY, title=title,
  1359. style=style, pos=pos)
  1360. # list of categories
  1361. box = wx.StaticBox(parent=self, id=wx.ID_ANY,
  1362. label=" %s " % _("List of categories - right-click to delete"))
  1363. listSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
  1364. self.list = CategoryListCtrl(parent=self, id=wx.ID_ANY,
  1365. style=wx.LC_REPORT |
  1366. wx.BORDER_NONE |
  1367. wx.LC_SORT_ASCENDING |
  1368. wx.LC_HRULES |
  1369. wx.LC_VRULES)
  1370. # sorter
  1371. self.fid = self.cats.keys()[0]
  1372. self.itemDataMap = self.list.Populate(self.cats[self.fid])
  1373. listmix.ColumnSorterMixin.__init__(self, 2)
  1374. self.fidMulti = wx.Choice(parent=self, id=wx.ID_ANY,
  1375. size=(150, -1))
  1376. self.fidMulti.Bind(wx.EVT_CHOICE, self.OnFeature)
  1377. self.fidText = wx.StaticText(parent=self, id=wx.ID_ANY)
  1378. if len(self.cats.keys()) == 1:
  1379. self.fidMulti.Show(False)
  1380. self.fidText.SetLabel(str(self.fid))
  1381. else:
  1382. self.fidText.Show(False)
  1383. choices = []
  1384. for fid in self.cats.keys():
  1385. choices.append(str(fid))
  1386. self.fidMulti.SetItems(choices)
  1387. self.fidMulti.SetSelection(0)
  1388. listSizer.Add(item=self.list, proportion=1, flag=wx.EXPAND)
  1389. # add new category
  1390. box = wx.StaticBox(parent=self, id=wx.ID_ANY,
  1391. label=" %s " % _("Add new category"))
  1392. addSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
  1393. flexSizer = wx.FlexGridSizer (cols=5, hgap=5, vgap=5)
  1394. flexSizer.AddGrowableCol(3)
  1395. layerNewTxt = wx.StaticText(parent=self, id=wx.ID_ANY,
  1396. label="%s:" % _("Layer"))
  1397. self.layerNew = wx.Choice(parent=self, id=wx.ID_ANY, size=(75, -1),
  1398. choices=layers)
  1399. if len(layers) > 0:
  1400. self.layerNew.SetSelection(0)
  1401. catNewTxt = wx.StaticText(parent=self, id=wx.ID_ANY,
  1402. label="%s:" % _("Category"))
  1403. try:
  1404. newCat = max(self.cats[self.fid][1]) + 1
  1405. except KeyError:
  1406. newCat = 1
  1407. self.catNew = wx.SpinCtrl(parent=self, id=wx.ID_ANY, size=(75, -1),
  1408. initial=newCat, min=0, max=1e9)
  1409. btnAddCat = wx.Button(self, wx.ID_ADD)
  1410. flexSizer.Add(item=layerNewTxt, proportion=0,
  1411. flag=wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
  1412. flexSizer.Add(item=self.layerNew, proportion=0,
  1413. flag=wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
  1414. flexSizer.Add(item=catNewTxt, proportion=0,
  1415. flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT,
  1416. border=10)
  1417. flexSizer.Add(item=self.catNew, proportion=0,
  1418. flag=wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
  1419. flexSizer.Add(item=btnAddCat, proportion=0,
  1420. flag=wx.EXPAND | wx.ALIGN_RIGHT | wx.FIXED_MINSIZE)
  1421. addSizer.Add(item=flexSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)
  1422. # buttons
  1423. btnApply = wx.Button(self, wx.ID_APPLY)
  1424. btnApply.SetToolTipString(_("Apply changes"))
  1425. btnCancel = wx.Button(self, wx.ID_CANCEL)
  1426. btnCancel.SetToolTipString(_("Ignore changes and close dialog"))
  1427. btnOk = wx.Button(self, wx.ID_OK)
  1428. btnOk.SetToolTipString(_("Apply changes and close dialog"))
  1429. btnOk.SetDefault()
  1430. # sizers
  1431. btnSizer = wx.StdDialogButtonSizer()
  1432. btnSizer.AddButton(btnCancel)
  1433. #btnSizer.AddButton(btnReload)
  1434. #btnSizer.SetNegativeButton(btnReload)
  1435. btnSizer.AddButton(btnApply)
  1436. btnSizer.AddButton(btnOk)
  1437. btnSizer.Realize()
  1438. mainSizer = wx.BoxSizer(wx.VERTICAL)
  1439. mainSizer.Add(item=listSizer, proportion=1,
  1440. flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
  1441. mainSizer.Add(item=addSizer, proportion=0,
  1442. flag=wx.EXPAND | wx.ALIGN_CENTER |
  1443. wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)
  1444. fidSizer = wx.BoxSizer(wx.HORIZONTAL)
  1445. fidSizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY,
  1446. label=_("Feature id:")),
  1447. proportion=0, border=5,
  1448. flag=wx.ALIGN_CENTER_VERTICAL)
  1449. fidSizer.Add(item=self.fidMulti, proportion=0,
  1450. flag=wx.EXPAND | wx.ALL, border=5)
  1451. fidSizer.Add(item=self.fidText, proportion=0,
  1452. flag=wx.EXPAND | wx.ALL, border=5)
  1453. mainSizer.Add(item=fidSizer, proportion=0,
  1454. flag=wx.EXPAND | wx.ALL, border=5)
  1455. mainSizer.Add(item=btnSizer, proportion=0,
  1456. flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
  1457. self.SetSizer(mainSizer)
  1458. mainSizer.Fit(self)
  1459. self.SetAutoLayout(True)
  1460. # set min size for dialog
  1461. self.SetMinSize(self.GetBestSize())
  1462. # bindings
  1463. # buttons
  1464. #btnReload.Bind(wx.EVT_BUTTON, self.OnReload)
  1465. btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
  1466. btnOk.Bind(wx.EVT_BUTTON, self.OnOK)
  1467. btnAddCat.Bind(wx.EVT_BUTTON, self.OnAddCat)
  1468. btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
  1469. # list
  1470. # self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected, self.list)
  1471. # self.list.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
  1472. self.list.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnRightUp) #wxMSW
  1473. self.list.Bind(wx.EVT_RIGHT_UP, self.OnRightUp) #wxGTK
  1474. self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnBeginEdit, self.list)
  1475. self.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.OnEndEdit, self.list)
  1476. self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick, self.list)
  1477. def GetListCtrl(self):
  1478. """Used by ColumnSorterMixin"""
  1479. return self.list
  1480. def OnColClick(self, event):
  1481. """Click on column header (order by)"""
  1482. event.Skip()
  1483. def OnBeginEdit(self, event):
  1484. """Editing of item started"""
  1485. event.Allow()
  1486. def OnEndEdit(self, event):
  1487. """Finish editing of item"""
  1488. itemIndex = event.GetIndex()
  1489. layerOld = int (self.list.GetItem(itemIndex, 0).GetText())
  1490. catOld = int (self.list.GetItem(itemIndex, 1).GetText())
  1491. if event.GetColumn() == 0:
  1492. layerNew = int(event.GetLabel())
  1493. catNew = catOld
  1494. else:
  1495. layerNew = layerOld
  1496. catNew = int(event.GetLabel())
  1497. try:
  1498. if layerNew not in self.cats[self.fid].keys():
  1499. self.cats[layerNew] = []
  1500. self.cats[layerNew].append(catNew)
  1501. self.cats[layerOld].remove(catOld)
  1502. except:
  1503. event.Veto()
  1504. self.list.SetStringItem(itemIndex, 0, str(layerNew))
  1505. self.list.SetStringItem(itemIndex, 1, str(catNew))
  1506. dlg = wx.MessageDialog(self, _("Unable to add new layer/category <%(layer)s/%(category)s>.\n"
  1507. "Layer and category number must be integer.\n"
  1508. "Layer number must be greater then zero.") %
  1509. { 'layer': self.layerNew.GetStringSelection(),
  1510. 'category' : str(self.catNew.GetValue()) },
  1511. _("Error"), wx.OK | wx.ICON_ERROR)
  1512. dlg.ShowModal()
  1513. dlg.Destroy()
  1514. return False
  1515. def OnRightDown(self, event):
  1516. """Mouse right button down"""
  1517. x = event.GetX()
  1518. y = event.GetY()
  1519. item, flags = self.list.HitTest((x, y))
  1520. if item != wx.NOT_FOUND and \
  1521. flags & wx.LIST_HITTEST_ONITEM:
  1522. self.list.Select(item)
  1523. event.Skip()
  1524. def OnRightUp(self, event):
  1525. """Mouse right button up"""
  1526. if not hasattr(self, "popupID1"):
  1527. self.popupID1 = wx.NewId()
  1528. self.popupID2 = wx.NewId()
  1529. self.popupID3 = wx.NewId()
  1530. self.Bind(wx.EVT_MENU, self.OnItemDelete, id=self.popupID1)
  1531. self.Bind(wx.EVT_MENU, self.OnItemDeleteAll, id=self.popupID2)
  1532. self.Bind(wx.EVT_MENU, self.OnReload, id=self.popupID3)
  1533. # generate popup-menu
  1534. menu = wx.Menu()
  1535. menu.Append(self.popupID1, _("Delete selected"))
  1536. if self.list.GetFirstSelected() == -1:
  1537. menu.Enable(self.popupID1, False)
  1538. menu.Append(self.popupID2, _("Delete all"))
  1539. menu.AppendSeparator()
  1540. menu.Append(self.popupID3, _("Reload"))
  1541. self.PopupMenu(menu)
  1542. menu.Destroy()
  1543. def OnItemSelected(self, event):
  1544. """Item selected"""
  1545. event.Skip()
  1546. def OnItemDelete(self, event):
  1547. """Delete selected item(s) from the list (layer/category pair)"""
  1548. item = self.list.GetFirstSelected()
  1549. while item != -1:
  1550. layer = int (self.list.GetItem(item, 0).GetText())
  1551. cat = int (self.list.GetItem(item, 1).GetText())
  1552. self.list.DeleteItem(item)
  1553. self.cats[self.fid][layer].remove(cat)
  1554. item = self.list.GetFirstSelected()
  1555. event.Skip()
  1556. def OnItemDeleteAll(self, event):
  1557. """Delete all items from the list"""
  1558. self.list.DeleteAllItems()
  1559. self.cats[self.fid] = {}
  1560. event.Skip()
  1561. def OnFeature(self, event):
  1562. """Feature id changed (on duplicates)"""
  1563. self.fid = int(event.GetString())
  1564. self.itemDataMap = self.list.Populate(self.cats[self.fid],
  1565. update=True)
  1566. try:
  1567. newCat = max(self.cats[self.fid][1]) + 1
  1568. except KeyError:
  1569. newCat = 1
  1570. self.catNew.SetValue(newCat)
  1571. event.Skip()
  1572. def __GetCategories(self, coords, qdist):
  1573. """Get layer/category pairs for all available
  1574. layers
  1575. Return True line found or False if not found"""
  1576. cmdWhat = gcmd.Command(cmd=['v.what',
  1577. '--q',
  1578. 'map=%s' % self.map,
  1579. 'east_north=%f,%f' % \
  1580. (float(coords[0]), float(coords[1])),
  1581. 'distance=%f' % qdist])
  1582. if cmdWhat.returncode != 0:
  1583. return False
  1584. for item in cmdWhat.ReadStdOutput():
  1585. litem = item.lower()
  1586. if "id:" in litem: # get line id
  1587. self.line = int(item.split(':')[1].strip())
  1588. elif "layer:" in litem: # add layer
  1589. layer = int(item.split(':')[1].strip())
  1590. if layer not in self.cats.keys():
  1591. self.cats[layer] = []
  1592. elif "category:" in litem: # add category
  1593. self.cats[layer].append(int(item.split(':')[1].strip()))
  1594. return True
  1595. def OnReload(self, event):
  1596. """Reload button pressed"""
  1597. # restore original list
  1598. self.cats = copy.deepcopy(self.cats_orig)
  1599. # polulate list
  1600. self.itemDataMap = self.list.Populate(self.cats[self.fid],
  1601. update=True)
  1602. event.Skip()
  1603. def OnCancel(self, event):
  1604. """Cancel button pressed"""
  1605. self.parent.parent.dialogs['category'] = None
  1606. if self.parent.parent.digit:
  1607. self.parent.parent.digit.driver.SetSelected([])
  1608. self.parent.UpdateMap(render=False)
  1609. else:
  1610. self.parent.parent.OnRender(None)
  1611. self.Close()
  1612. def OnApply(self, event):
  1613. """Apply button pressed"""
  1614. for fid in self.cats.keys():
  1615. newfid = self.ApplyChanges(fid)
  1616. if fid == self.fid:
  1617. self.fid = newfid
  1618. def ApplyChanges(self, fid):
  1619. cats = self.cats[fid]
  1620. cats_orig = self.cats_orig[fid]
  1621. # action : (catsFrom, catsTo)
  1622. check = {'catadd': (cats, cats_orig),
  1623. 'catdel': (cats_orig, cats)}
  1624. newfid = -1
  1625. # add/delete new category
  1626. for action, catsCurr in check.iteritems():
  1627. for layer in catsCurr[0].keys():
  1628. catList = []
  1629. for cat in catsCurr[0][layer]:
  1630. if layer not in catsCurr[1].keys() or \
  1631. cat not in catsCurr[1][layer]:
  1632. catList.append(cat)
  1633. if catList != []:
  1634. if action == 'catadd':
  1635. add = True
  1636. else:
  1637. add = False
  1638. newfid = self.parent.parent.digit.SetLineCats(fid, layer,
  1639. catList, add)
  1640. if len(self.cats.keys()) == 1:
  1641. self.fidText.SetLabel("%d" % newfid)
  1642. else:
  1643. choices = self.fidMulti.GetItems()
  1644. choices[choices.index(str(fid))] = str(newfid)
  1645. self.fidMulti.SetItems(choices)
  1646. self.fidMulti.SetStringSelection(str(newfid))
  1647. self.cats[newfid] = self.cats[fid]
  1648. del self.cats[fid]
  1649. fid = newfid
  1650. if self.fid < 0:
  1651. wx.MessageBox(parent=self, message=_("Unable to update vector map."),
  1652. caption=_("Error"), style=wx.OK | wx.ICON_ERROR)
  1653. self.cats_orig[fid] = copy.deepcopy(cats)
  1654. return newfid
  1655. def OnOK(self, event):
  1656. """OK button pressed"""
  1657. self.OnApply(event)
  1658. self.OnCancel(event)
  1659. def OnAddCat(self, event):
  1660. """Button 'Add' new category pressed"""
  1661. try:
  1662. layer = int(self.layerNew.GetStringSelection())
  1663. cat = int(self.catNew.GetValue())
  1664. if layer <= 0:
  1665. raise ValueError
  1666. except ValueError:
  1667. dlg = wx.MessageDialog(self, _("Unable to add new layer/category <%(layer)s/%(category)s>.\n"
  1668. "Layer and category number must be integer.\n"
  1669. "Layer number must be greater then zero.") %
  1670. {'layer' : str(self.layerNew.GetValue()),
  1671. 'category' : str(self.catNew.GetValue())},
  1672. _("Error"), wx.OK | wx.ICON_ERROR)
  1673. dlg.ShowModal()
  1674. dlg.Destroy()
  1675. return False
  1676. if layer not in self.cats[self.fid].keys():
  1677. self.cats[self.fid][layer] = []
  1678. self.cats[self.fid][layer].append(cat)
  1679. # reload list
  1680. self.itemDataMap = self.list.Populate(self.cats[self.fid],
  1681. update=True)
  1682. # update category number for add
  1683. self.catNew.SetValue(cat + 1)
  1684. event.Skip()
  1685. return True
  1686. def GetLine(self):
  1687. """Get id of selected line of 'None' if no line is selected"""
  1688. return self.cats.keys()
  1689. def UpdateDialog(self, query=None, cats=None):
  1690. """Update dialog
  1691. @param query {coordinates, distance} - v.edit/v.what
  1692. @param cats directory layer/cats - vdigit
  1693. Return True if updated otherwise False
  1694. """
  1695. # line: {layer: [categories]}
  1696. self.cats = {}
  1697. # do not display dialog if no line is found (-> self.cats)
  1698. if cats is None:
  1699. ret = self.__GetCategories(query[0], query[1])
  1700. else:
  1701. self.cats = cats
  1702. for line in cats.keys():
  1703. for layer in cats[line].keys():
  1704. self.cats[line][layer] = list(cats[line][layer])
  1705. ret = 1
  1706. if ret == 0 or len(self.cats.keys()) < 1:
  1707. Debug.msg(3, "VDigitCategoryDialog(): nothing found!")
  1708. return False
  1709. # make copy of cats (used for 'reload')
  1710. self.cats_orig = copy.deepcopy(self.cats)
  1711. # polulate list
  1712. self.fid = self.cats.keys()[0]
  1713. self.itemDataMap = self.list.Populate(self.cats[self.fid],
  1714. update=True)
  1715. try:
  1716. newCat = max(self.cats[self.fid][1]) + 1
  1717. except KeyError:
  1718. newCat = 1
  1719. self.catNew.SetValue(newCat)
  1720. if len(self.cats.keys()) == 1:
  1721. self.fidText.Show(True)
  1722. self.fidMulti.Show(False)
  1723. self.fidText.SetLabel("%d" % self.fid)
  1724. else:
  1725. self.fidText.Show(False)
  1726. self.fidMulti.Show(True)
  1727. choices = []
  1728. for fid in self.cats.keys():
  1729. choices.append(str(fid))
  1730. self.fidMulti.SetItems(choices)
  1731. self.fidMulti.SetSelection(0)
  1732. self.Layout()
  1733. return True
  1734. class CategoryListCtrl(wx.ListCtrl,
  1735. listmix.ListCtrlAutoWidthMixin,
  1736. listmix.TextEditMixin):
  1737. """List of layers/categories"""
  1738. def __init__(self, parent, id, pos=wx.DefaultPosition,
  1739. size=wx.DefaultSize, style=0):
  1740. self.parent = parent
  1741. wx.ListCtrl.__init__(self, parent, id, pos, size, style)
  1742. listmix.ListCtrlAutoWidthMixin.__init__(self)
  1743. listmix.TextEditMixin.__init__(self)
  1744. def Populate(self, cats, update=False):
  1745. """Populate the list"""
  1746. itemData = {} # requested by sorter
  1747. if not update:
  1748. self.InsertColumn(0, _("Layer"))
  1749. self.InsertColumn(1, _("Category"))
  1750. else:
  1751. self.DeleteAllItems()
  1752. i = 1
  1753. for layer in cats.keys():
  1754. catsList = cats[layer]
  1755. for cat in catsList:
  1756. index = self.InsertStringItem(sys.maxint, str(catsList[0]))
  1757. self.SetStringItem(index, 0, str(layer))
  1758. self.SetStringItem(index, 1, str(cat))
  1759. self.SetItemData(index, i)
  1760. itemData[i] = (str(layer), str(cat))
  1761. i = i + 1
  1762. if not update:
  1763. self.SetColumnWidth(0, 100)
  1764. self.SetColumnWidth(1, wx.LIST_AUTOSIZE)
  1765. self.currentItem = 0
  1766. return itemData
  1767. class VDigitZBulkDialog(wx.Dialog):
  1768. """
  1769. Dialog used for Z bulk-labeling tool
  1770. """
  1771. def __init__(self, parent, title, nselected, style=wx.DEFAULT_DIALOG_STYLE):
  1772. wx.Dialog.__init__(self, parent=parent, id=wx.ID_ANY, title=title, style=style)
  1773. self.parent = parent # mapdisplay.BufferedWindow class instance
  1774. # panel = wx.Panel(parent=self, id=wx.ID_ANY)
  1775. border = wx.BoxSizer(wx.VERTICAL)
  1776. txt = wx.StaticText(parent=self,
  1777. label=_("%d lines selected for z bulk-labeling") % nselected);
  1778. border.Add(item=txt, proportion=0, flag=wx.ALL | wx.EXPAND, border=5)
  1779. box = wx.StaticBox (parent=self, id=wx.ID_ANY, label=" %s " % _("Set value"))
  1780. sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
  1781. flexSizer = wx.FlexGridSizer (cols=2, hgap=5, vgap=5)
  1782. flexSizer.AddGrowableCol(0)
  1783. # starting value
  1784. txt = wx.StaticText(parent=self,
  1785. label=_("Starting value"));
  1786. self.value = wx.SpinCtrl(parent=self, id=wx.ID_ANY, size=(150, -1),
  1787. initial=0,
  1788. min=-1e6, max=1e6)
  1789. flexSizer.Add(txt, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
  1790. flexSizer.Add(self.value, proportion=0, flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
  1791. # step
  1792. txt = wx.StaticText(parent=self,
  1793. label=_("Step"))
  1794. self.step = wx.SpinCtrl(parent=self, id=wx.ID_ANY, size=(150, -1),
  1795. initial=0,
  1796. min=0, max=1e6)
  1797. flexSizer.Add(txt, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
  1798. flexSizer.Add(self.step, proportion=0, flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
  1799. sizer.Add(item=flexSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=1)
  1800. border.Add(item=sizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)
  1801. # buttons
  1802. btnCancel = wx.Button(self, wx.ID_CANCEL)
  1803. btnOk = wx.Button(self, wx.ID_OK)
  1804. btnOk.SetDefault()
  1805. # sizers
  1806. btnSizer = wx.StdDialogButtonSizer()
  1807. btnSizer.AddButton(btnCancel)
  1808. btnSizer.AddButton(btnOk)
  1809. btnSizer.Realize()
  1810. mainSizer = wx.BoxSizer(wx.VERTICAL)
  1811. mainSizer.Add(item=border, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
  1812. mainSizer.Add(item=btnSizer, proportion=0,
  1813. flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
  1814. self.SetSizer(mainSizer)
  1815. mainSizer.Fit(self)
  1816. class VDigitDuplicatesDialog(wx.Dialog):
  1817. """
  1818. Show duplicated feature ids
  1819. """
  1820. def __init__(self, parent, data, title=_("List of duplicates"),
  1821. style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
  1822. pos=wx.DefaultPosition):
  1823. wx.Dialog.__init__(self, parent=parent, id=wx.ID_ANY, title=title, style=style,
  1824. pos=pos)
  1825. self.parent = parent # BufferedWindow
  1826. self.data = data
  1827. self.winList = []
  1828. # panel = wx.Panel(parent=self, id=wx.ID_ANY)
  1829. # notebook
  1830. self.notebook = wx.Notebook(parent=self, id=wx.ID_ANY, style=wx.BK_DEFAULT)
  1831. id = 1
  1832. for key in self.data.keys():
  1833. panel = wx.Panel(parent=self.notebook, id=wx.ID_ANY)
  1834. self.notebook.AddPage(page=panel, text=" %d " % (id))
  1835. # notebook body
  1836. border = wx.BoxSizer(wx.VERTICAL)
  1837. win = CheckListFeature(parent=panel, data=list(self.data[key]))
  1838. self.winList.append(win.GetId())
  1839. border.Add(item=win, proportion=1,
  1840. flag=wx.ALL | wx.EXPAND, border=5)
  1841. panel.SetSizer(border)
  1842. id += 1
  1843. # buttons
  1844. btnCancel = wx.Button(self, wx.ID_CANCEL)
  1845. btnOk = wx.Button(self, wx.ID_OK)
  1846. btnOk.SetDefault()
  1847. # sizers
  1848. btnSizer = wx.StdDialogButtonSizer()
  1849. btnSizer.AddButton(btnCancel)
  1850. btnSizer.AddButton(btnOk)
  1851. btnSizer.Realize()
  1852. mainSizer = wx.BoxSizer(wx.VERTICAL)
  1853. mainSizer.Add(item=self.notebook, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
  1854. mainSizer.Add(item=btnSizer, proportion=0,
  1855. flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
  1856. self.SetSizer(mainSizer)
  1857. mainSizer.Fit(self)
  1858. self.SetAutoLayout(True)
  1859. # set min size for dialog
  1860. self.SetMinSize((250, 180))
  1861. def GetUnSelected(self):
  1862. """Get unselected items (feature id)
  1863. @return list of ids
  1864. """
  1865. ids = []
  1866. for id in self.winList:
  1867. wlist = self.FindWindowById(id)
  1868. for item in range(wlist.GetItemCount()):
  1869. if not wlist.IsChecked(item):
  1870. ids.append(int(wlist.GetItem(item, 0).GetText()))
  1871. return ids
  1872. class CheckListFeature(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.CheckListCtrlMixin):
  1873. """List of mapset/owner/group"""
  1874. def __init__(self, parent, data,
  1875. pos=wx.DefaultPosition, log=None):
  1876. self.parent = parent
  1877. self.data = data
  1878. wx.ListCtrl.__init__(self, parent, wx.ID_ANY,
  1879. style=wx.LC_REPORT)
  1880. listmix.CheckListCtrlMixin.__init__(self)
  1881. self.log = log
  1882. # setup mixins
  1883. listmix.ListCtrlAutoWidthMixin.__init__(self)
  1884. self.LoadData(self.data)
  1885. def LoadData(self, data):
  1886. """Load data into list"""
  1887. self.InsertColumn(0, _('Feature id'))
  1888. self.InsertColumn(1, _('Layer (Categories)'))
  1889. for item in data:
  1890. index = self.InsertStringItem(sys.maxint, str(item[0]))
  1891. self.SetStringItem(index, 1, str(item[1]))
  1892. # enable all items by default
  1893. for item in range(self.GetItemCount()):
  1894. self.CheckItem(item, True)
  1895. self.SetColumnWidth(col=0, width=wx.LIST_AUTOSIZE_USEHEADER)
  1896. self.SetColumnWidth(col=1, width=wx.LIST_AUTOSIZE_USEHEADER)
  1897. def OnCheckItem(self, index, flag):
  1898. """Mapset checked/unchecked"""
  1899. pass