vdigit.py 100 KB

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