vdigit.py 92 KB

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