vdigit.py 100 KB

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