vdigit.py 100 KB

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