vdigit.py 108 KB

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