vdigit.py 90 KB

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