vdigit.py 100 KB

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