vdigit.py 99 KB

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