vdigit.py 100 KB

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