vdigit.py 109 KB

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