vdigit.py 99 KB

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