vdigit.py 113 KB

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