vdigit.py 92 KB

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