vdigit.py 107 KB

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