vdigit.py 109 KB

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