vdigit.py 109 KB

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