vdigit.py 97 KB

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