vdigit.py 106 KB

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