wxdigit.py 62 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852
  1. """!
  2. @package vdigit.wxdigit
  3. @brief wxGUI vector digitizer (base class)
  4. Code based on wxVdigit C++ component from GRASS 6.4.0
  5. (gui/wxpython/vdigit). Converted to Python in 2010/12-2011/01.
  6. List of classes:
  7. - wxdigit::VDigitError
  8. - wxdigit::IVDigit
  9. @todo Read large amounts of data from Vlib into arrays, which could
  10. then be processed using NumPy and rendered using glDrawArrays or
  11. glDrawElements, so no per-line/per-vertex processing in Python. Bulk
  12. data processing with NumPy is much faster than iterating in Python
  13. (and NumPy would be an excellent candidate for acceleration via
  14. e.g. OpenCL or CUDA; I'm surprised it hasn't happened already).
  15. (C) 2007-2011, 2013 by the GRASS Development Team
  16. This program is free software under the GNU General Public License
  17. (>=v2). Read the file COPYING that comes with GRASS for details.
  18. @author Martin Landa <landa.martin gmail.com>
  19. """
  20. import grass.script.core as grass
  21. from grass.pydispatch.signal import Signal
  22. from core.gcmd import GError
  23. from core.debug import Debug
  24. from core.settings import UserSettings
  25. from core.utils import _
  26. from vdigit.wxdisplay import DisplayDriver, GetLastError
  27. try:
  28. from grass.lib.gis import *
  29. from grass.lib.vector import *
  30. from grass.lib.vedit import *
  31. from grass.lib.dbmi import *
  32. except ImportError:
  33. pass
  34. class VDigitError:
  35. def __init__(self, parent):
  36. """!Class for managing error messages of vector digitizer
  37. @param parent parent window for dialogs
  38. """
  39. self.parent = parent
  40. self.caption = _('Digitization Error')
  41. def NoMap(self, name = None):
  42. """!No map for editing"""
  43. if name:
  44. message = _('Unable to open vector map <%s>.') % name
  45. else:
  46. message = _('No vector map open for editing.')
  47. GError(message + ' ' + _('Operation canceled.'),
  48. parent = self.parent,
  49. caption = self.caption)
  50. def WriteLine(self):
  51. """!Writing line failed
  52. """
  53. GError(message = _('Writing new feature failed. '
  54. 'Operation canceled.\n\n'
  55. 'Reason: %s') % GetLastError(),
  56. parent = self.parent,
  57. caption = self.caption)
  58. def ReadLine(self, line):
  59. """!Reading line failed
  60. """
  61. GError(message = _('Reading feature id %d failed. '
  62. 'Operation canceled.') % line,
  63. parent = self.parent,
  64. caption = self.caption)
  65. def DbLink(self, dblink):
  66. """!No dblink available
  67. """
  68. GError(message = _('Database link %d not available. '
  69. 'Operation canceled.') % dblink,
  70. parent = self.parent,
  71. caption = self.caption)
  72. def Driver(self, driver):
  73. """!Staring driver failed
  74. """
  75. GError(message = _('Unable to start database driver <%s>. '
  76. 'Operation canceled.') % driver,
  77. parent = self.parent,
  78. caption = self.caption)
  79. def Database(self, driver, database):
  80. """!Opening database failed
  81. """
  82. GError(message = _('Unable to open database <%(db)s> by driver <%(driver)s>. '
  83. 'Operation canceled.') % { 'db' : database, 'driver' : driver},
  84. parent = self.parent,
  85. caption = self.caption)
  86. def DbExecute(self, sql):
  87. """!Sql query failed
  88. """
  89. GError(message = _("Unable to execute SQL query '%s'. "
  90. "Operation canceled.") % sql,
  91. parent = self.parent,
  92. caption = self.caption)
  93. def DeadLine(self, line):
  94. """!Dead line
  95. """
  96. GError(message = _("Feature id %d is marked as dead. "
  97. "Operation canceled.") % line,
  98. parent = self.parent,
  99. caption = self.caption)
  100. def FeatureType(self, ftype):
  101. """!Unknown feature type
  102. """
  103. GError(message = _("Unsupported feature type %d. "
  104. "Operation canceled.") % ftype,
  105. parent = self.parent,
  106. caption = self.caption)
  107. class IVDigit:
  108. def __init__(self, mapwindow, driver = DisplayDriver):
  109. """!Base class for vector digitizer (ctypes interface)
  110. @param mapwindow reference for map window (BufferedWindow)
  111. """
  112. self.poMapInfo = None # pointer to Map_info
  113. self.mapWindow = mapwindow
  114. # background map
  115. self.bgMapInfo = Map_info()
  116. self.poBgMapInfo = self.popoBgMapInfo = None
  117. # TODO: replace this by using giface
  118. if not mapwindow.parent.IsStandalone():
  119. goutput = mapwindow.parent.GetLayerManager().GetLogWindow()
  120. log = goutput.GetLog(err = True)
  121. progress = mapwindow.parent._giface.GetProgress()
  122. else:
  123. log = sys.stderr
  124. progress = None
  125. self.toolbar = mapwindow.parent.toolbars['vdigit']
  126. self._error = VDigitError(parent = self.mapWindow)
  127. self._display = driver(device = mapwindow.pdcVector,
  128. deviceTmp = mapwindow.pdcTmp,
  129. mapObj = mapwindow.Map,
  130. window = mapwindow,
  131. glog = log,
  132. gprogress = progress)
  133. # GRASS lib
  134. self.poPoints = Vect_new_line_struct()
  135. self.poCats = Vect_new_cats_struct()
  136. # self.SetCategory()
  137. # layer / max category
  138. self.cats = dict()
  139. self._settings = dict()
  140. self.UpdateSettings() # -> self._settings
  141. # undo/redo
  142. self.changesets = list()
  143. self.changesetCurrent = -1 # first changeset to apply
  144. if self.poMapInfo:
  145. self.InitCats()
  146. #TODO signal for errors?
  147. self.featureAdded = Signal('IVDigit.featureAdded')
  148. self.areasDeleted = Signal('IVDigit.areasDeleted')
  149. self.vertexMoved = Signal('IVDigit.vertexMoved')
  150. self.vertexAdded = Signal('IVDigit.vertexAdded')
  151. self.vertexRemoved = Signal('IVDigit.vertexRemoved')
  152. self.featuresDeleted = Signal('IVDigit.featuresDeleted')
  153. self.featuresMoved = Signal('IVDigit.featuresMoved')
  154. self.lineEdited = Signal('IVDigit.lineEdited')
  155. def __del__(self):
  156. Debug.msg(1, "IVDigit.__del__()")
  157. Vect_destroy_line_struct(self.poPoints)
  158. self.poPoints = None
  159. Vect_destroy_cats_struct(self.poCats)
  160. self.poCats = None
  161. if self.poBgMapInfo:
  162. Vect_close(self.poBgMapInfo)
  163. self.poBgMapInfo = self.popoBgMapInfo = None
  164. del self.bgMapInfo
  165. def CloseBackgroundMap(self):
  166. """!Close background vector map"""
  167. if not self.poBgMapInfo:
  168. return
  169. Vect_close(self.poBgMapInfo)
  170. self.poBgMapInfo = self.popoBgMapInfo = None
  171. def OpenBackgroundMap(self, bgmap):
  172. """!Open background vector map
  173. @todo support more background maps then only one
  174. @param bgmap name of vector map to be opened
  175. @return pointer to map_info
  176. @return None on error
  177. """
  178. name = create_string_buffer(GNAME_MAX)
  179. mapset = create_string_buffer(GMAPSET_MAX)
  180. if not G_name_is_fully_qualified(bgmap, name, mapset):
  181. name = str(bgmap)
  182. mapset = str(G_find_vector2(bgmap, ''))
  183. else:
  184. name = str(name.value)
  185. mapset = str(mapset.value)
  186. if (name == Vect_get_name(self.poMapInfo) and \
  187. mapset == Vect_get_mapset(self.poMapInfo)):
  188. self.poBgMapInfo = self.popoBgMapInfo = None
  189. self._error.NoMap(bgmap)
  190. return
  191. self.poBgMapInfo = pointer(self.bgMapInfo)
  192. self.popoBgMapInfo = pointer(self.poBgMapInfo)
  193. if Vect_open_old(self.poBgMapInfo, name, mapset) == -1:
  194. self.poBgMapInfo = self.popoBgMapInfo = None
  195. self._error.NoMap(bgmap)
  196. return
  197. def _getSnapMode(self):
  198. """!Get snapping mode
  199. - snap to vertex
  200. - snap to nodes
  201. - no snapping
  202. @return snap mode
  203. """
  204. threshold = self._display.GetThreshold()
  205. if threshold > 0.0:
  206. if UserSettings.Get(group = 'vdigit', key = 'snapToVertex', subkey = 'enabled'):
  207. return SNAPVERTEX
  208. else:
  209. return SNAP
  210. else:
  211. return NO_SNAP
  212. def _getNewFeaturesLayer(self):
  213. """!Returns layer of new feature (from settings)"""
  214. if UserSettings.Get(group = 'vdigit', key = "categoryMode", subkey = 'selection') == 2:
  215. layer = -1 # -> no category
  216. else:
  217. layer = UserSettings.Get(group = 'vdigit', key = "layer", subkey = 'value')
  218. return layer
  219. def _getNewFeaturesCat(self):
  220. """!Returns category of new feature (from settings)"""
  221. if UserSettings.Get(group = 'vdigit', key = "categoryMode", subkey = 'selection') == 2:
  222. cat = -1
  223. else:
  224. cat = self.SetCategory()
  225. return cat
  226. def _breakLineAtIntersection(self, line, pointsLine):
  227. """!Break given line at intersection
  228. \param line line id
  229. \param pointsLine line geometry
  230. \return number of modified lines
  231. """
  232. if not self._checkMap():
  233. return -1
  234. if not Vect_line_alive(self.poMapInfo, line):
  235. return 0
  236. if not pointsLine:
  237. if Vect_read_line(self.poMapInfo, self.poPoints, None, line) < 0:
  238. self._error.ReadLine(line)
  239. return -1
  240. points = self.poPoints
  241. else:
  242. points = pointsLine
  243. listLine = Vect_new_boxlist(0)
  244. listRef = Vect_new_list()
  245. listBreak = Vect_new_list()
  246. pointsCheck = Vect_new_line_struct()
  247. lineBox = bound_box()
  248. # find all relevant lines
  249. Vect_get_line_box(self.poMapInfo, line, byref(lineBox))
  250. Vect_select_lines_by_box(self.poMapInfo, byref(lineBox),
  251. GV_LINES, listLine)
  252. # check for intersection
  253. Vect_list_append(listBreak, line)
  254. Vect_list_append(listRef, line)
  255. for i in range(listLine.contents.n_values):
  256. lineBreak = listLine.contents.id[i]
  257. if lineBreak == line:
  258. continue
  259. ltype = Vect_read_line(self.poMapInfo, pointsCheck, None, lineBreak)
  260. if not (ltype & GV_LINES):
  261. continue
  262. if Vect_line_check_intersection(self.poPoints, pointsCheck,
  263. WITHOUT_Z):
  264. Vect_list_append(listBreak, lineBreak)
  265. ret = Vect_break_lines_list(self.poMapInfo, listBreak, listRef,
  266. GV_LINES, None)
  267. Vect_destroy_line_struct(pointsCheck)
  268. Vect_destroy_boxlist(listLine)
  269. Vect_destroy_list(listBreak)
  270. Vect_destroy_list(listRef)
  271. return ret
  272. def _addChangeset(self):
  273. data = list()
  274. for i in range(Vect_get_num_updated_lines(self.poMapInfo) - 1, -1, -1):
  275. line = Vect_get_updated_line(self.poMapInfo, i)
  276. offset = Vect_get_updated_line_offset(self.poMapInfo, i)
  277. data.append({ 'line' : line,
  278. 'offset' : offset })
  279. self.changesetCurrent += 1
  280. self.changesets.insert(self.changesetCurrent, data)
  281. Vect_reset_updated(self.poMapInfo)
  282. def _applyChangeset(self, changeset, undo):
  283. """!Apply changeset (undo/redo changeset)
  284. @param changeset changeset id
  285. @param undo True for undo otherwise redo
  286. @return 1 changeset applied
  287. @return 0 changeset not applied
  288. @return -1 on error
  289. """
  290. if changeset < 0 or changeset >= len(self.changesets):
  291. return -1
  292. ret = 0
  293. actions = self.changesets[changeset]
  294. for action in actions:
  295. line = action['line']
  296. if action['offset'] > 0:
  297. if Vect_line_alive(self.poMapInfo, line):
  298. Debug.msg(3, "IVDigit._applyChangeset(): changeset=%d, action=add, line=%d -> deleted",
  299. changeset, line)
  300. Vect_delete_line(self.poMapInfo, line)
  301. ret = 1
  302. else:
  303. Debug.msg(3, "Digit.ApplyChangeset(): changeset=%d, action=add, line=%d dead",
  304. changeset, line)
  305. else: # delete
  306. offset = abs(action['offset'])
  307. if not Vect_line_alive(self.poMapInfo, line):
  308. Debug.msg(3, "Digit.ApplyChangeset(): changeset=%d, action=delete, line=%d -> added",
  309. changeset, line)
  310. if Vect_restore_line(self.poMapInfo, line, offset) < 0:
  311. return -1
  312. ret = 1
  313. else:
  314. Debug.msg(3, "Digit.ApplyChangeset(): changeset=%d, action=delete, line=%d alive",
  315. changeset, line)
  316. action['offset'] *= -1
  317. Vect_reset_updated(self.poMapInfo)
  318. return ret
  319. def AddFeature(self, ftype, points):
  320. """!Add new feature
  321. @param ftype feature type (point, line, centroid, boundary)
  322. @param points tuple of points ((x, y), (x, y), ...)
  323. @return tuple (number of added features, feature ids)
  324. """
  325. layer = self._getNewFeaturesLayer()
  326. cat = self._getNewFeaturesCat()
  327. if ftype == 'point':
  328. vtype = GV_POINT
  329. elif ftype == 'line':
  330. vtype = GV_LINE
  331. elif ftype == 'centroid':
  332. vtype = GV_CENTROID
  333. elif ftype == 'boundary':
  334. vtype = GV_BOUNDARY
  335. elif ftype == 'area':
  336. vtype = GV_AREA
  337. else:
  338. GError(parent = self.mapWindow,
  339. message = _("Unknown feature type '%s'") % ftype)
  340. return (-1, None)
  341. if vtype & GV_LINES and len(points) < 2:
  342. GError(parent = self.mapWindow,
  343. message = _("Not enough points for line"))
  344. return (-1, None)
  345. self.toolbar.EnableUndo()
  346. ret = self._addFeature(vtype, points, layer, cat,
  347. self._getSnapMode(), self._display.GetThreshold())
  348. if ret[0] > -1:
  349. self.featureAdded.emit(new_geom = points, cat = {layer : [cat]})
  350. return ret
  351. def DeleteSelectedLines(self):
  352. """!Delete selected features
  353. @return number of deleted features
  354. """
  355. if not self._checkMap():
  356. return -1
  357. # colect categories for delete if requested
  358. deleteRec = UserSettings.Get(group = 'vdigit', key = 'delRecord', subkey = 'enabled')
  359. catDict = dict()
  360. old_geoms = []
  361. old_areas_cats = []
  362. if deleteRec:
  363. for i in self._display.selected['ids']:
  364. if Vect_read_line(self.poMapInfo, None, self.poCats, i) < 0:
  365. self._error.ReadLine(i)
  366. old_geoms.append(self._getGeom(i))
  367. old_areas_cats.append(self._getLineAreasCategories(i))
  368. cats = self.poCats.contents
  369. # catDict was not used -> put into comment
  370. #for j in range(cats.n_cats):
  371. # if cats.field[j] not in catDict.keys():
  372. # catDict[cats.field[j]] = list()
  373. # catDict[cats.field[j]].append(cats.cat[j])
  374. poList = self._display.GetSelectedIList()
  375. nlines = Vedit_delete_lines(self.poMapInfo, poList)
  376. Vect_destroy_list(poList)
  377. self._display.selected['ids'] = list()
  378. if nlines > 0:
  379. if deleteRec:
  380. self._deleteRecords(cats)
  381. self._addChangeset()
  382. self.toolbar.EnableUndo()
  383. self.featuresDeleted.emit(old_geoms = old_geoms, old_areas_cats = old_areas_cats)
  384. return nlines
  385. def _deleteRecords(self, cats):
  386. """!Delete records from attribute table
  387. @param cats directory field/list of cats
  388. """
  389. handle = dbHandle()
  390. poHandle = pointer(handle)
  391. stmt = dbString()
  392. poStmt = pointer(stmt)
  393. for dblink in range(Vect_get_num_dblinks(self.poMapInfo)):
  394. poFi = Vect_get_dblink(self.poMapInfo, dblink)
  395. if poFi is None:
  396. self._error.DbLink(dblink)
  397. return -1
  398. Fi = poFi.contents
  399. if Fi.table not in cats.keys():
  400. continue
  401. poDriver = db_start_driver(Fi.driver)
  402. if poDriver is None:
  403. self._error.Driver(Fi.driver)
  404. return -1
  405. db_init_handle(poHandle)
  406. db_set_handle(poHandle, Fi.database, None)
  407. if db_open_database(poDriver, poHandle) != DB_OK:
  408. self._error.Database(Fi.driver, Fi.database)
  409. return -1
  410. db_init_string(poStmt)
  411. db_set_string(poStmt, "DELETE FROM %s WHERE" % Fi.table)
  412. n_cats = 0
  413. for cat in cats[Fi.table]:
  414. if n_cats > 0:
  415. db_append_string(poStmt, " or")
  416. db_append_string(poStmt, " %s = %d" % (Fi.key, cats))
  417. n_cats += 1
  418. if n_cats > 0 and \
  419. db_execute_immediate(poDriver, poStmt) != DB_OK:
  420. self._error.DbExecute(db_get_string(poStmt))
  421. return -1
  422. db_close_shutdown_database(poDriver)
  423. def DeleteSelectedAreas(self):
  424. """!Delete selected areas (centroid+boundaries)
  425. @return number of deleted
  426. """
  427. if len(self._display.selected['ids']) < 1:
  428. return 0
  429. poList = self._display.GetSelectedIList()
  430. cList = poList.contents
  431. nareas = 0
  432. old_geoms = []
  433. old_areas_cats = []
  434. for i in range(cList.n_values):
  435. if Vect_get_line_type(self.poMapInfo, cList.value[i]) != GV_CENTROID:
  436. continue
  437. area = Vect_get_centroid_area(self.poMapInfo, cList.value[i]);
  438. if area > 0:
  439. geoms, cats = self._getaAreaGeomsCats(area)
  440. old_geoms += geoms
  441. old_areas_cats += cats
  442. nareas += Vedit_delete_area_centroid(self.poMapInfo, cList.value[i])
  443. if nareas > 0:
  444. self._addChangeset()
  445. self.toolbar.EnableUndo()
  446. self.areasDeleted.emit(old_geoms = old_geoms, old_areas_cats = old_areas_cats)
  447. return nareas
  448. def _getaAreaGeomsCats(self, area):
  449. po_b_list = Vect_new_list()
  450. Vect_get_area_boundaries(self.poMapInfo, area, po_b_list);
  451. b_list = po_b_list.contents
  452. geoms = []
  453. areas_cats = []
  454. if b_list.n_values > 0:
  455. for i_line in range(b_list.n_values):
  456. line = b_list.value[i_line];
  457. geoms.append(self._getGeom(abs(line)))
  458. areas_cats.append(self._getLineAreasCategories(abs(line)))
  459. Vect_destroy_list(po_b_list);
  460. return geoms, areas_cats
  461. def _getLineAreasCategories(self, ln_id):
  462. ltype = Vect_read_line(self.poMapInfo, None, None, ln_id)
  463. if ltype != GV_BOUNDARY:
  464. return []
  465. cats = [None, None]
  466. left = c_int()
  467. right = c_int()
  468. if Vect_get_line_areas(self.poMapInfo, ln_id, pointer(left), pointer(right)) == 1:
  469. areas = [left.value, right.value]
  470. for i, a in enumerate(areas):
  471. if a > 0:
  472. centroid = Vect_get_area_centroid(self.poMapInfo, a)
  473. if centroid <= 0:
  474. continue
  475. c = self._getCategories(centroid)
  476. if c:
  477. cats[i] = c
  478. return cats
  479. def _getCategories(self, ln_id):
  480. poCats = Vect_new_cats_struct()
  481. if Vect_read_line(self.poMapInfo, None, poCats, ln_id) < 0:
  482. Vect_destroy_cats_struct(poCats)
  483. return None
  484. cCats = poCats.contents
  485. cats = {}
  486. for j in range(cCats.n_cats):
  487. if cats.has_key(cCats.field[j]):
  488. cats[cCats.field[j]].append(cCats.cat[j])
  489. else:
  490. cats[cCats.field[j]] = [cCats.cat[j]]
  491. Vect_destroy_cats_struct(poCats)
  492. return cats
  493. def _getGeom(self, ln_id):
  494. poPoints = Vect_new_line_struct()
  495. if Vect_read_line(self.poMapInfo, poPoints, None, ln_id) < 0:
  496. Vect_destroy_line_struct(poPoints)
  497. return None
  498. geom = self._convertGeom(poPoints)
  499. Vect_destroy_line_struct(poPoints)
  500. return geom
  501. def _convertGeom(self, poPoints):
  502. Points = poPoints.contents
  503. pts_geom = []
  504. for j in range(Points.n_points):
  505. pts_geom.append((Points.x[j], Points.y[j]))
  506. return pts_geom
  507. def MoveSelectedLines(self, move):
  508. """!Move selected features
  509. @param move direction (x, y)
  510. """
  511. if not self._checkMap():
  512. return -1
  513. nsel = len(self._display.selected['ids'])
  514. if nsel < 1:
  515. return -1
  516. thresh = self._display.GetThreshold()
  517. snap = self._getSnapMode()
  518. poList = self._display.GetSelectedIList()
  519. old_geoms = []
  520. old_areas_cats = []
  521. for sel_id in self._display.selected['ids']:
  522. old_geoms.append(self._getGeom(sel_id))
  523. old_areas_cats.append(self._getLineAreasCategories(sel_id))
  524. poNewIds = Vect_new_list()
  525. nlines = Vedit_move_lines(self.poMapInfo, self.popoBgMapInfo, int(self.poBgMapInfo is not None),
  526. poList,
  527. move[0], move[1], 0,
  528. snap, thresh, poNewIds)
  529. Vect_destroy_list(poList)
  530. cList = poNewIds.contents
  531. if nlines > 0:
  532. new_areas_cats = []
  533. for i in range(cList.n_values):
  534. new_id = cList.value[i]
  535. new_areas_cats.append(self._getLineAreasCategories(new_id))
  536. Vect_destroy_list(poNewIds)
  537. if nlines > 0 and self._settings['breakLines']:
  538. for i in range(1, nlines):
  539. self._breakLineAtIntersection(nlines + i, None, changeset)
  540. if nlines > 0:
  541. self._addChangeset()
  542. self.toolbar.EnableUndo()
  543. self.featuresMoved.emit(move = move,
  544. old_geoms = old_geoms,
  545. old_areas_cats = old_areas_cats,
  546. new_areas_cats = new_areas_cats)
  547. return nlines
  548. def MoveSelectedVertex(self, point, move):
  549. """!Move selected vertex of the line
  550. @param point location point
  551. @param move x,y direction
  552. @return id of new feature
  553. @return 0 vertex not moved (not found, line is not selected)
  554. @return -1 on error
  555. """
  556. if not self._checkMap():
  557. return -1
  558. if len(self._display.selected['ids']) != 1:
  559. return -1
  560. # move only first found vertex in bbox
  561. poList = self._display.GetSelectedIList()
  562. cList = poList.contents
  563. old_geom = self._getGeom(cList.value[0])
  564. old_areas_cats = self._getLineAreasCategories(cList.value[0])
  565. Vect_reset_line(self.poPoints)
  566. Vect_append_point(self.poPoints, point[0], point[1], 0.0)
  567. poNewIds = Vect_new_list()
  568. moved = Vedit_move_vertex(self.poMapInfo, self.popoBgMapInfo, int(self.poBgMapInfo is not None),
  569. poList, self.poPoints,
  570. self._display.GetThreshold(type = 'selectThresh'),
  571. self._display.GetThreshold(),
  572. move[0], move[1], 0.0,
  573. 1, self._getSnapMode(), poNewIds)
  574. Vect_destroy_list(poList)
  575. new_id = poNewIds.contents.value[0]
  576. Vect_destroy_list(poNewIds)
  577. if new_id > -1:
  578. new_geom = self._getGeom(new_id)
  579. new_areas_cats = self._getLineAreasCategories(new_id)
  580. if moved > 0 and self._settings['breakLines']:
  581. self._breakLineAtIntersection(Vect_get_num_lines(self.poMapInfo),
  582. None)
  583. if moved > 0:
  584. self._addChangeset()
  585. self.toolbar.EnableUndo()
  586. if new_id > -1:
  587. self.vertexMoved.emit(new_geom = new_geom,
  588. new_areas_cats = new_areas_cats,
  589. old_areas_cats = old_areas_cats,
  590. old_geom = old_geom)
  591. return moved
  592. def AddVertex(self, coords):
  593. """!Add new vertex to the selected line/boundary on position 'coords'
  594. @param coords coordinates to add vertex
  595. @return id of new feature
  596. @return 0 nothing changed
  597. @return -1 on failure
  598. """
  599. added = self._ModifyLineVertex(coords, add = True)
  600. if added > 0:
  601. self.toolbar.EnableUndo()
  602. return added
  603. def RemoveVertex(self, coords):
  604. """!Remove vertex from the selected line/boundary on position 'coords'
  605. @param coords coordinates to remove vertex
  606. @return id of new feature
  607. @return 0 nothing changed
  608. @return -1 on failure
  609. """
  610. deleted = self._ModifyLineVertex(coords, add = False)
  611. if deleted > 0:
  612. self.toolbar.EnableUndo()
  613. return deleted
  614. def SplitLine(self, point):
  615. """!Split/break selected line/boundary on given position
  616. @param point point where to split line
  617. @return 1 line modified
  618. @return 0 nothing changed
  619. @return -1 error
  620. """
  621. thresh = self._display.GetThreshold('selectThresh')
  622. if not self._checkMap():
  623. return -1
  624. poList = self._display.GetSelectedIList()
  625. Vect_reset_line(self.poPoints)
  626. Vect_append_point(self.poPoints, point[0], point[1], 0.0)
  627. ret = Vedit_split_lines(self.poMapInfo, poList,
  628. self.poPoints, thresh, None)
  629. Vect_destroy_list(poList)
  630. if ret > 0:
  631. self.addChangeset()
  632. self.toolbar.EnableUndo()
  633. return ret
  634. def EditLine(self, line, coords):
  635. """!Edit existing line/boundary
  636. @param line feature id to be modified
  637. @param coords list of coordinates of modified line
  638. @return feature id of new line
  639. @return -1 on error
  640. """
  641. if not self._checkMap():
  642. return -1
  643. if len(coords) < 2:
  644. self.DeleteSelectedLines()
  645. return 0
  646. if not Vect_line_alive(self.poMapInfo, line):
  647. self._error.DeadLine(line)
  648. return -1
  649. # read original feature
  650. ltype = Vect_read_line(self.poMapInfo, None, self.poCats, line)
  651. if ltype < 0:
  652. self._error.ReadLine(line)
  653. return -1
  654. old_geom = self._getGeom(line)
  655. old_areas_cats = self._getLineAreasCategories(line)
  656. # build feature geometry
  657. Vect_reset_line(self.poPoints)
  658. for p in coords:
  659. Vect_append_point(self.poPoints, p[0], p[1], 0.0)
  660. # apply snapping (node or vertex)
  661. snap = self._getSnapMode()
  662. if snap != NO_SNAP:
  663. modeSnap = not (snap == SNAP)
  664. Vedit_snap_line(self.poMapInfo, self.popoBgMapInfo,
  665. int(self.poBgMapInfo is not None),
  666. -1, self.poPoints, self._display.GetThreshold(), modeSnap)
  667. newline = Vect_rewrite_line(self.poMapInfo, line, ltype,
  668. self.poPoints, self.poCats)
  669. if newline > 0 and self._settings['breakLines']:
  670. self._breakLineAtIntersection(newline, None)
  671. if newline > 0:
  672. self._addChangeset()
  673. self.toolbar.EnableUndo()
  674. new_geom = self._getGeom(newline)
  675. new_areas_cats = self._getLineAreasCategories(newline)
  676. self.lineEdited.emit(old_geom = old_geom,
  677. old_areas_cats = old_areas_cats,
  678. new_geom = new_geom,
  679. new_areas_cats = new_areas_cats)
  680. return newline
  681. def FlipLine(self):
  682. """!Flip selected lines/boundaries
  683. @return number of modified lines
  684. @return -1 on error
  685. """
  686. if not self._checkMap():
  687. return -1
  688. nlines = Vect_get_num_lines(self.poMapInfo)
  689. poList = self._display.GetSelectedIList()
  690. ret = Vedit_flip_lines(self.poMapInfo, poList)
  691. Vect_destroy_list(poList)
  692. if ret > 0:
  693. self._addChangeset()
  694. self.toolbar.EnableUndo()
  695. return ret
  696. def MergeLine(self):
  697. """!Merge selected lines/boundaries
  698. @return number of modified lines
  699. @return -1 on error
  700. """
  701. if not self._checkMap():
  702. return -1
  703. poList = self._display.GetSelectedIList()
  704. ret = Vedit_merge_lines(self.poMapInfo, poList)
  705. Vect_destroy_list(poList)
  706. if ret > 0:
  707. self._addChangeset()
  708. self.toolbar.EnableUndo()
  709. return ret
  710. def BreakLine(self):
  711. """!Break selected lines/boundaries
  712. @return number of modified lines
  713. @return -1 on error
  714. """
  715. if not self._checkMap():
  716. return -1
  717. poList = self._display.GetSelectedIList()
  718. ret = Vect_break_lines_list(self.poMapInfo, poList, None,
  719. GV_LINES, None)
  720. Vect_destroy_list(poList)
  721. if ret > 0:
  722. self._addChangeset()
  723. self.toolbar.EnableUndo()
  724. return ret
  725. def SnapLine(self):
  726. """!Snap selected lines/boundaries
  727. @return on success
  728. @return -1 on error
  729. """
  730. if not self._checkMap():
  731. return -1
  732. nlines = Vect_get_num_lines(self.poMapInfo)
  733. poList = self._display.GetSelectedIList()
  734. Vect_snap_lines_list(self.poMapInfo, poList,
  735. self._display.GetThreshold(), None)
  736. Vect_destroy_list(poList)
  737. if nlines < Vect_get_num_lines(self.poMapInfo):
  738. self._addChangeset()
  739. self.toolbar.EnableUndo()
  740. def ConnectLine(self):
  741. """!Connect selected lines/boundaries
  742. @return 1 lines connected
  743. @return 0 lines not connected
  744. @return -1 on error
  745. """
  746. if not self._checkMap():
  747. return -1
  748. poList = self._display.GetSelectedIList()
  749. ret = Vedit_connect_lines(self.poMapInfo, poList,
  750. self._display.GetThreshold())
  751. Vect_destroy_list(poList)
  752. if ret > 0:
  753. self._addChangeset()
  754. self.toolbar.EnableUndo()
  755. return ret
  756. def CopyLine(self, ids = []):
  757. """!Copy features from (background) vector map
  758. @param ids list of line ids to be copied
  759. @return number of copied features
  760. @return -1 on error
  761. """
  762. if not self._checkMap():
  763. return -1
  764. nlines = Vect_get_num_lines(self.poMapInfo)
  765. poList = self._display.GetSelectedIList(ids)
  766. ret = Vedit_copy_lines(self.poMapInfo, self.poBgMapInfo,
  767. poList)
  768. Vect_destroy_list(poList)
  769. if ret > 0 and self.poBgMapInfo and self._settings['breakLines']:
  770. for i in range(1, ret):
  771. self._breakLineAtIntersection(nlines + i, None)
  772. if ret > 0:
  773. self._addChangeset()
  774. self.toolbar.EnableUndo()
  775. return ret
  776. def CopyCats(self, fromId, toId, copyAttrb = False):
  777. """!Copy given categories to objects with id listed in ids
  778. @param cats ids of 'from' feature
  779. @param ids ids of 'to' feature(s)
  780. @return number of modified features
  781. @return -1 on error
  782. """
  783. if len(fromId) < 1 or len(toId) < 1:
  784. return 0
  785. poCatsFrom = self.poCats
  786. poCatsTo = Vect_new_cats_struct();
  787. nlines = 0
  788. for fline in fromId:
  789. if not Vect_line_alive(self.poMapInfo, fline):
  790. continue
  791. if Vect_read_line(self.poMapInfo, None, poCatsFrom, fline) < 0:
  792. self._error.ReadLine(fline)
  793. return -1
  794. for tline in toId:
  795. if not Vect_line_alive(self.poMapInfo, tline):
  796. continue
  797. ltype = Vect_read_line(self.poMapInfo, self.poPoints, poCatsTo, tline)
  798. if ltype < 0:
  799. self._error.ReadLine(fline)
  800. return -1
  801. catsFrom = poCatsFrom.contents
  802. for i in range(catsFrom.n_cats):
  803. if not copyAttrb:
  804. # duplicate category
  805. cat = catsFrom.cat[i]
  806. else:
  807. # duplicate attributes
  808. cat = self.cats[catsFrom.field[i]] + 1
  809. self.cats[catsFrom.field[i]] = cat
  810. poFi = Vect_get_field(self.poMapInfo, catsFrom.field[i])
  811. if not poFi:
  812. self._error.DbLink(i)
  813. return -1
  814. fi = poFi.contents
  815. driver = db_start_driver(fi.driver)
  816. if not driver:
  817. self._error.Driver(fi.driver)
  818. return -1
  819. handle = dbHandle()
  820. db_init_handle(byref(handle))
  821. db_set_handle(byref(handle), fi.database, None)
  822. if db_open_database(driver, byref(handle)) != DB_OK:
  823. db_shutdown_driver(driver)
  824. self._error.Database(fi.driver, fi.database)
  825. return -1
  826. stmt = dbString()
  827. db_init_string(byref(stmt))
  828. db_set_string(byref(stmt),
  829. "SELECT * FROM %s WHERE %s=%d" % (fi.table, fi.key,
  830. catsFrom.cat[i]))
  831. cursor = dbCursor()
  832. if db_open_select_cursor(driver, byref(stmt), byref(cursor),
  833. DB_SEQUENTIAL) != DB_OK:
  834. db_close_database_shutdown_driver(driver)
  835. return -1
  836. table = db_get_cursor_table(byref(cursor))
  837. ncols = db_get_table_number_of_columns(table)
  838. sql = "INSERT INTO %s VALUES (" % fi.table
  839. # fetch the data
  840. more = c_int()
  841. while True:
  842. if db_fetch(byref(cursor), DB_NEXT, byref(more)) != DB_OK:
  843. db_close_database_shutdown_driver(driver)
  844. return -1
  845. if not more.value:
  846. break
  847. value_string = dbString()
  848. for col in range(ncols):
  849. if col > 0:
  850. sql += ","
  851. column = db_get_table_column(table, col)
  852. if db_get_column_name(column) == fi.key:
  853. sql += "%d" % cat
  854. continue
  855. value = db_get_column_value(column)
  856. db_convert_column_value_to_string(column, byref(value_string))
  857. if db_test_value_isnull(value):
  858. sql += "NULL"
  859. else:
  860. ctype = db_sqltype_to_Ctype(db_get_column_sqltype(column))
  861. if ctype != DB_C_TYPE_STRING:
  862. sql += db_get_string(byref(value_string))
  863. else:
  864. sql += "'%s'" % db_get_string(byref(value_string))
  865. sql += ")"
  866. db_set_string(byref(stmt), sql)
  867. if db_execute_immediate(driver, byref(stmt)) != DB_OK:
  868. db_close_database_shutdown_driver(driver)
  869. return -1
  870. db_close_database_shutdown_driver(driver)
  871. G_free(poFi)
  872. if Vect_cat_set(poCatsTo, catsFrom.field[i], cat) < 1:
  873. continue
  874. if Vect_rewrite_line(self.poMapInfo, tline, ltype, self.poPoints, poCatsTo) < 0:
  875. self._error.WriteLine()
  876. return -1
  877. nlines +=1
  878. Vect_destroy_cats_struct(poCatsTo)
  879. if nlines > 0:
  880. self.toolbar.EnableUndo()
  881. return nlines
  882. def _selectLinesByQueryThresh(self):
  883. """!Generic method used for SelectLinesByQuery() -- to get
  884. threshold value"""
  885. thresh = 0.0
  886. if UserSettings.Get(group = 'vdigit', key = 'query', subkey = 'selection') == 0:
  887. thresh = UserSettings.Get(group = 'vdigit', key = 'queryLength', subkey = 'thresh')
  888. if UserSettings.Get(group = 'vdigit', key = "queryLength", subkey = 'than-selection') == 0:
  889. thresh = -1 * thresh
  890. else:
  891. thresh = UserSettings.Get(group = 'vdigit', key = 'queryDangle', subkey = 'thresh')
  892. if UserSettings.Get(group = 'vdigit', key = "queryDangle", subkey = 'than-selection') == 0:
  893. thresh = -1 * thresh
  894. return thresh
  895. def SelectLinesByQuery(self, bbox):
  896. """!Select features by query
  897. @todo layer / 3D
  898. @param bbox bounding box definition
  899. """
  900. if not self._checkMap():
  901. return -1
  902. thresh = self._selectLinesByQueryThresh()
  903. query = QUERY_UNKNOWN
  904. if UserSettings.Get(group = 'vdigit', key = 'query', subkey = 'selection') == 0:
  905. query = QUERY_LENGTH
  906. else:
  907. query = QUERY_DANGLE
  908. ftype = GV_POINTS | GV_LINES # TODO: 3D
  909. layer = 1 # TODO
  910. ids = list()
  911. poList = Vect_new_list()
  912. coList = poList.contents
  913. if UserSettings.Get(group = 'vdigit', key = 'query', subkey = 'box'):
  914. Vect_reset_line(self.poPoints)
  915. x1, y1 = bbox[0]
  916. x2, y2 = bbox[1]
  917. z1 = z2 = 0.0
  918. Vect_append_point(self.poPoints, x1, y1, z1)
  919. Vect_append_point(self.poPoints, x2, y1, z2)
  920. Vect_append_point(self.poPoints, x2, y2, z1)
  921. Vect_append_point(self.poPoints, x1, y2, z2)
  922. Vect_append_point(self.poPoints, x1, y1, z1)
  923. Vect_select_lines_by_polygon(self.poMapInfo, self.poPoints, 0, None,
  924. ftype, poList)
  925. if coList.n_values == 0:
  926. return ids
  927. Vedit_select_by_query(self.poMapInfo,
  928. ftype, layer, thresh, query,
  929. poList)
  930. for i in range(coList.n_values):
  931. ids.append(int(coList.value[i]))
  932. Debug.msg(3, "IVDigit.SelectLinesByQuery(): lines=%d", coList.n_values)
  933. Vect_destroy_list(poList)
  934. return ids
  935. def IsVector3D(self):
  936. """!Check if open vector map is 3D
  937. """
  938. if not self._checkMap():
  939. return False
  940. return Vect_is_3d(self.poMapInfo)
  941. def GetLineLength(self, line):
  942. """!Get line length
  943. @param line feature id
  944. @return line length
  945. @return -1 on error
  946. """
  947. if not self._checkMap():
  948. return -1
  949. if not Vect_line_alive(self.poMapInfo, line):
  950. return -1
  951. ltype = Vect_read_line(self.poMapInfo, self.poPoints, None, line)
  952. if ltype < 0:
  953. self._error.ReadLine(line)
  954. return ret
  955. length = -1
  956. if ltype & GV_LINES: # lines & boundaries
  957. length = Vect_line_length(self.poPoints)
  958. return length
  959. def GetAreaSize(self, centroid):
  960. """!Get area size
  961. @param centroid centroid id
  962. @return area size
  963. @return -1 on error
  964. """
  965. if not self._checkMap():
  966. return -1
  967. ltype = Vect_read_line(self.poMapInfo, None, None, centroid)
  968. if ltype < 0:
  969. self._error.ReadLine(line)
  970. return ret
  971. if ltype != GV_CENTROID:
  972. return -1
  973. area = Vect_get_centroid_area(self.poMapInfo, centroid)
  974. size = -1
  975. if area > 0:
  976. if not Vect_area_alive(self.poMapInfo, area):
  977. return size
  978. size = Vect_get_area_area(self.poMapInfo, area)
  979. return size
  980. def GetAreaPerimeter(self, centroid):
  981. """!Get area perimeter
  982. @param centroid centroid id
  983. @return area size
  984. @return -1 on error
  985. """
  986. if not self._checkMap():
  987. return -1
  988. ltype = Vect_read_line(self.poMapInfo, None, None, centroid)
  989. if ltype < 0:
  990. self._error.ReadLine(line)
  991. return ret
  992. if ltype != GV_CENTROID:
  993. return -1
  994. area = Vect_get_centroid_area(self.poMapInfo, centroid)
  995. perimeter = -1
  996. if area > 0:
  997. if not Vect_area_alive(self.poMapInfo, area):
  998. return -1
  999. Vect_get_area_points(self.poMapInfo, area, self.poPoints)
  1000. perimeter = Vect_area_perimeter(self.poPoints)
  1001. return perimeter
  1002. def SetLineCats(self, line, layer, cats, add = True):
  1003. """!Set categories for given line and layer
  1004. @param line feature id
  1005. @param layer layer number (-1 for first selected line)
  1006. @param cats list of categories
  1007. @param add if True to add, otherwise do delete categories
  1008. @return new feature id (feature need to be rewritten)
  1009. @return -1 on error
  1010. """
  1011. if not self._checkMap():
  1012. return -1
  1013. if line < 1 and len(self._display.selected['ids']) < 1:
  1014. return -1
  1015. update = False
  1016. if line == -1:
  1017. update = True
  1018. line = self._display.selected['ids'][0]
  1019. if not Vect_line_alive(self.poMapInfo, line):
  1020. return -1
  1021. ltype = Vect_read_line(self.poMapInfo, self.poPoints, self.poCats, line)
  1022. if ltype < 0:
  1023. self._error.ReadLine(line)
  1024. return -1
  1025. for c in cats:
  1026. if add:
  1027. Vect_cat_set(self.poCats, layer, c)
  1028. else:
  1029. Vect_field_cat_del(self.poCats, layer, c)
  1030. newline = Vect_rewrite_line(self.poMapInfo, line, ltype,
  1031. self.poPoints, self.poCats)
  1032. if newline > 0:
  1033. self._addChangeset()
  1034. self.toolbar.EnableUndo()
  1035. if update:
  1036. # update line id since the line was rewritten
  1037. self._display.selected['ids'][0] = newline
  1038. return newline
  1039. def TypeConvForSelectedLines(self):
  1040. """!Feature type conversion for selected objects.
  1041. Supported conversions:
  1042. - point <-> centroid
  1043. - line <-> boundary
  1044. @return number of modified features
  1045. @return -1 on error
  1046. """
  1047. if not self._checkMap():
  1048. return -1
  1049. poList = self._display.GetSelectedIList()
  1050. ret = Vedit_chtype_lines(self.poMapInfo, poList)
  1051. Vect_destroy_list(poList)
  1052. if ret > 0:
  1053. self._addChangeset()
  1054. self.toolbar.EnableUndo()
  1055. return ret
  1056. def Undo(self, level = -1):
  1057. """!Undo action
  1058. @param level levels to undo (0 to revert all)
  1059. @return id of current changeset
  1060. """
  1061. changesetLast = len(self.changesets) - 1
  1062. if changesetLast < 0:
  1063. return changesetLast
  1064. if level > 0 and self.changesetCurrent < 0:
  1065. self.changesetCurrent = 0
  1066. elif level < 0 and self.changesetCurrent > changesetLast:
  1067. self.changesetCurrent = changesetLast
  1068. elif level == 0:
  1069. # 0 -> undo all
  1070. level = -1 * changesetLast + 1
  1071. Debug.msg(2, "Digit.Undo(): changeset_last=%d, changeset_current=%d, level=%d",
  1072. changesetLast, self.changesetCurrent, level)
  1073. if level < 0: # undo
  1074. if self.changesetCurrent + level < -1:
  1075. return self.changesetCurrent
  1076. for changeset in range(self.changesetCurrent, self.changesetCurrent + level, -1):
  1077. self._applyChangeset(changeset, undo = True)
  1078. elif level > 0: # redo
  1079. if self.changesetCurrent + level > len(self.changesets):
  1080. return self.changesetCurrent
  1081. for changeset in range(self.changesetCurrent, self.changesetCurrent + level):
  1082. self._applyChangeset(changeset, undo = False)
  1083. self.changesetCurrent += level
  1084. Debug.msg(2, "Digit.Undo(): changeset_current=%d, changeset_last=%d",
  1085. self.changesetCurrent, changesetLast)
  1086. self.mapWindow.UpdateMap(render = False)
  1087. if self.changesetCurrent < 0: # disable undo tool
  1088. self.toolbar.EnableUndo(False)
  1089. else:
  1090. self.toolbar.EnableUndo(True)
  1091. if self.changesetCurrent <= changesetLast:
  1092. self.toolbar.EnableRedo(True)
  1093. else:
  1094. self.toolbar.EnableRedo(False)
  1095. def ZBulkLines(self, pos1, pos2, start, step):
  1096. """!Z-bulk labeling
  1097. @param pos1 reference line (start point)
  1098. @param pos1 reference line (end point)
  1099. @param start starting value
  1100. @param step step value
  1101. @return number of modified lines
  1102. @return -1 on error
  1103. """
  1104. if not self._checkMap():
  1105. return -1
  1106. poList = self._display.GetSelectedIList()
  1107. ret = Vedit_bulk_labeling(self.poMapInfo, poList,
  1108. pos1[0], pos1[1], pos2[0], pos2[1],
  1109. start, step)
  1110. Vect_destroy_list(poList)
  1111. if ret > 0:
  1112. self._addChangeset()
  1113. self.toolbar.EnableUndo()
  1114. return ret
  1115. def GetDisplay(self):
  1116. """!Get display driver instance"""
  1117. return self._display
  1118. def OpenMap(self, name):
  1119. """!Open vector map for editing
  1120. @param map name of vector map to be set up
  1121. """
  1122. Debug.msg (3, "AbstractDigit.SetMapName map=%s" % name)
  1123. if '@' in name:
  1124. name, mapset = name.split('@')
  1125. else:
  1126. mapset = grass.gisenv()['MAPSET']
  1127. self.poMapInfo = self._display.OpenMap(str(name), str(mapset), True)
  1128. if self.poMapInfo:
  1129. self.InitCats()
  1130. return self.poMapInfo
  1131. def CloseMap(self):
  1132. """!Close currently open vector map
  1133. """
  1134. if not self._checkMap():
  1135. return
  1136. self._display.CloseMap()
  1137. def InitCats(self):
  1138. """!Initialize categories information
  1139. @return 0 on success
  1140. @return -1 on error
  1141. """
  1142. self.cats.clear()
  1143. if not self._checkMap():
  1144. return -1
  1145. ndblinks = Vect_get_num_dblinks(self.poMapInfo)
  1146. for i in range(ndblinks):
  1147. fi = Vect_get_dblink(self.poMapInfo, i).contents
  1148. if fi:
  1149. self.cats[fi.number] = None
  1150. # find max category
  1151. nfields = Vect_cidx_get_num_fields(self.poMapInfo)
  1152. Debug.msg(2, "wxDigit.InitCats(): nfields=%d", nfields)
  1153. for i in range(nfields):
  1154. field = Vect_cidx_get_field_number(self.poMapInfo, i)
  1155. ncats = Vect_cidx_get_num_cats_by_index(self.poMapInfo, i)
  1156. if field <= 0:
  1157. continue
  1158. for j in range(ncats):
  1159. cat = c_int()
  1160. type = c_int()
  1161. id = c_int()
  1162. Vect_cidx_get_cat_by_index(self.poMapInfo, i, j,
  1163. byref(cat), byref(type), byref(id))
  1164. if field in self.cats:
  1165. if cat > self.cats[field]:
  1166. self.cats[field] = cat.value
  1167. else:
  1168. self.cats[field] = cat.value
  1169. Debug.msg(3, "wxDigit.InitCats(): layer=%d, cat=%d", field, self.cats[field])
  1170. # set default values
  1171. for field, cat in self.cats.iteritems():
  1172. if cat == None:
  1173. self.cats[field] = 0 # first category 1
  1174. Debug.msg(3, "wxDigit.InitCats(): layer=%d, cat=%d", field, self.cats[field])
  1175. def _checkMap(self):
  1176. """!Check if map is open
  1177. """
  1178. if not self.poMapInfo:
  1179. self._error.NoMap()
  1180. return False
  1181. return True
  1182. def _addFeature(self, ftype, coords, layer, cat, snap, threshold):
  1183. """!Add new feature(s) to the vector map
  1184. @param ftype feature type (GV_POINT, GV_LINE, GV_BOUNDARY, ...)
  1185. @coords tuple of coordinates ((x, y), (x, y), ...)
  1186. @param layer layer number (-1 for no cat)
  1187. @param cat category number
  1188. @param snap snap to node/vertex
  1189. @param threshold threshold for snapping
  1190. @return tuple (number of added features, list of fids)
  1191. @return number of features -1 on error
  1192. """
  1193. fids = list()
  1194. if not self._checkMap():
  1195. return (-1, None)
  1196. is3D = bool(Vect_is_3d(self.poMapInfo))
  1197. Debug.msg(2, "IVDigit._addFeature(): npoints=%d, layer=%d, cat=%d, snap=%d",
  1198. len(coords), layer, cat, snap)
  1199. if not (ftype & (GV_POINTS | GV_LINES | GV_AREA)): # TODO: 3D
  1200. self._error.FeatureType(ftype)
  1201. return (-1, None)
  1202. # set category
  1203. Vect_reset_cats(self.poCats)
  1204. if layer > 0 and ftype != GV_AREA:
  1205. Vect_cat_set(self.poCats, layer, cat)
  1206. self.cats[layer] = max(cat, self.cats.get(layer, 1))
  1207. # append points
  1208. Vect_reset_line(self.poPoints)
  1209. for c in coords:
  1210. Vect_append_point(self.poPoints, c[0], c[1], 0.0)
  1211. if ftype & (GV_BOUNDARY | GV_AREA):
  1212. # close boundary
  1213. points = self.poPoints.contents
  1214. last = points.n_points - 1
  1215. if self._settings['closeBoundary']:
  1216. Vect_append_point(self.poPoints, points.x[0], points.y[0], points.z[0])
  1217. elif Vect_points_distance(points.x[0], points.y[0], points.z[0],
  1218. points.x[last], points.y[last], points.z[last],
  1219. is3D) <= threshold:
  1220. points.x[last] = points.x[0]
  1221. points.y[last] = points.y[0]
  1222. points.z[last] = points.z[0]
  1223. if snap != NO_SNAP:
  1224. # apply snapping (node or vertex)
  1225. modeSnap = not (snap == SNAP)
  1226. Vedit_snap_line(self.poMapInfo, self.popoBgMapInfo, int(self.poBgMapInfo is not None),
  1227. -1, self.poPoints, threshold, modeSnap)
  1228. if ftype == GV_AREA:
  1229. ltype = GV_BOUNDARY
  1230. else:
  1231. ltype = ftype
  1232. newline = Vect_write_line(self.poMapInfo, ltype, self.poPoints, self.poCats)
  1233. if newline < 0:
  1234. self._error.WriteLine()
  1235. return (-1, None)
  1236. else:
  1237. fids.append(newline)
  1238. left = right = -1
  1239. if ftype & GV_AREA:
  1240. # add centroids for left/right area
  1241. bpoints = Vect_new_line_struct()
  1242. cleft = c_int()
  1243. cright = c_int()
  1244. Vect_get_line_areas(self.poMapInfo, newline,
  1245. byref(cleft), byref(cright))
  1246. left = cleft.value
  1247. right = cright.value
  1248. Debug.msg(3, "IVDigit._addFeature(): area - left=%d right=%d",
  1249. left, right)
  1250. # check if area exists and has no centroid inside
  1251. if layer > 0 and (left > 0 or right > 0):
  1252. Vect_cat_set(self.poCats, layer, cat)
  1253. self.cats[layer] = max(cat, self.cats.get(layer, 0))
  1254. x = c_double()
  1255. y = c_double()
  1256. if left > 0 and \
  1257. Vect_get_area_centroid(self.poMapInfo, left) == 0:
  1258. # if Vect_get_area_points(self.poMapInfo, left, bpoints) > 0 and
  1259. # Vect_find_poly_centroid(bpoints, byref(x), byref(y)) == 0:
  1260. if Vect_get_point_in_area(self.poMapInfo, left, byref(x), byref(y)) == 0:
  1261. Vect_reset_line(bpoints)
  1262. Vect_append_point(bpoints, x.value, y.value, 0.0)
  1263. newline = Vect_write_line(self.poMapInfo, GV_CENTROID,
  1264. bpoints, self.poCats)
  1265. if newline < 0:
  1266. self._error.WriteLine()
  1267. return (len(fids), fids)
  1268. else:
  1269. fids.append(newline)
  1270. if right > 0 and \
  1271. Vect_get_area_centroid(self.poMapInfo, right) == 0:
  1272. # if Vect_get_area_points(byref(self.poMapInfo), right, bpoints) > 0 and
  1273. # Vect_find_poly_centroid(bpoints, byref(x), byref(y)) == 0:
  1274. if Vect_get_point_in_area(self.poMapInfo, right, byref(x), byref(y)) == 0:
  1275. Vect_reset_line(bpoints)
  1276. Vect_append_point(bpoints, x.value, y.value, 0.0)
  1277. newline = Vect_write_line(self.poMapInfo, GV_CENTROID,
  1278. bpoints, self.poCats)
  1279. if newline < 0:
  1280. self._error.WriteLine()
  1281. return (len(fids, fids))
  1282. else:
  1283. fids.append(newline)
  1284. Vect_destroy_line_struct(bpoints)
  1285. # break at intersection
  1286. if self._settings['breakLines']:
  1287. self._breakLineAtIntersection(newline, self.poPoints)
  1288. self._addChangeset()
  1289. return (len(fids), fids)
  1290. def _ModifyLineVertex(self, coords, add = True):
  1291. """!Add or remove vertex
  1292. Shape of line/boundary is not changed when adding new vertex.
  1293. @param coords coordinates of point
  1294. @param add True to add, False to remove
  1295. @return 1 on success
  1296. @return 0 nothing changed
  1297. @return -1 error
  1298. """
  1299. if not self._checkMap():
  1300. return -1
  1301. selected = self._display.selected
  1302. if len(selected['ids']) != 1:
  1303. return 0
  1304. poList = self._display.GetSelectedIList()
  1305. cList = poList.contents
  1306. old_geom = self._getGeom(cList.value[0])
  1307. old_areas_cats = self._getLineAreasCategories(cList.value[0])
  1308. Vect_reset_line(self.poPoints)
  1309. Vect_append_point(self.poPoints, coords[0], coords[1], 0.0)
  1310. thresh = self._display.GetThreshold(type = 'selectThresh')
  1311. poNewIds = Vect_new_list()
  1312. if add:
  1313. ret = Vedit_add_vertex(self.poMapInfo, poList,
  1314. self.poPoints, thresh, poNewIds)
  1315. else:
  1316. ret = Vedit_remove_vertex(self.poMapInfo, poList,
  1317. self.poPoints, thresh, poNewIds)
  1318. new_id = poNewIds.contents.value[0]
  1319. Vect_destroy_list(poNewIds)
  1320. Vect_destroy_list(poList)
  1321. if new_id > -1:
  1322. new_geom = self._getGeom(new_id)
  1323. new_areas_cats = self._getLineAreasCategories(new_id)
  1324. if not add and ret > 0 and self._settings['breakLines']:
  1325. self._breakLineAtIntersection(Vect_get_num_lines(self.poMapInfo),
  1326. None)
  1327. if ret > 0:
  1328. self._addChangeset()
  1329. if new_id > -1:
  1330. if add:
  1331. self.vertexAdded.emit(old_geom = old_geom, new_geom = new_geom)
  1332. else:
  1333. self.vertexRemoved.emit(old_geom = old_geom,
  1334. new_geom = new_geom,
  1335. old_areas_cats = old_areas_cats,
  1336. new_areas_cats = new_areas_cats)
  1337. return 1
  1338. def GetLineCats(self, line):
  1339. """!Get list of layer/category(ies) for selected feature.
  1340. @param line feature id (-1 for first selected feature)
  1341. @return list of layer/cats
  1342. """
  1343. ret = dict()
  1344. if not self._checkMap():
  1345. return ret
  1346. if line == -1 and len(self._display.selected['ids']) < 1:
  1347. return ret
  1348. if line == -1:
  1349. line = self._display.selected['ids'][0]
  1350. if not Vect_line_alive(self.poMapInfo, line):
  1351. self._error.DeadLine(line)
  1352. return ret
  1353. if Vect_read_line(self.poMapInfo, None, self.poCats, line) < 0:
  1354. self._error.ReadLine(line)
  1355. return ret
  1356. cats = self.poCats.contents
  1357. for i in range(cats.n_cats):
  1358. field = cats.field[i]
  1359. if field not in ret:
  1360. ret[field] = list()
  1361. ret[field].append(cats.cat[i])
  1362. return ret
  1363. def GetLayers(self):
  1364. """!Get list of layers
  1365. Requires self.InitCats() to be called.
  1366. @return list of layers
  1367. """
  1368. return self.cats.keys()
  1369. def UpdateSettings(self):
  1370. """!Update digit (and display) settings
  1371. """
  1372. self._display.UpdateSettings()
  1373. self._settings['breakLines'] = bool(UserSettings.Get(group = 'vdigit', key = "breakLines",
  1374. subkey = 'enabled'))
  1375. self._settings['closeBoundary'] = bool(UserSettings.Get(group = 'vdigit', key = "closeBoundary",
  1376. subkey = 'enabled'))
  1377. def SetCategory(self):
  1378. """!Update self.cats based on settings"""
  1379. sel = UserSettings.Get(group = 'vdigit', key = 'categoryMode', subkey = 'selection')
  1380. cat = None
  1381. if sel == 0: # next to usep
  1382. cat = self._setCategoryNextToUse()
  1383. elif sel == 1:
  1384. cat = UserSettings.Get(group = 'vdigit', key = 'category', subkey = 'value')
  1385. if cat:
  1386. layer = UserSettings.Get(group = 'vdigit', key = 'layer', subkey = 'value')
  1387. self.cats[layer] = cat
  1388. return cat
  1389. def _setCategoryNextToUse(self):
  1390. """!Find maximum category number for the given layer and
  1391. update the settings
  1392. @return category to be used
  1393. """
  1394. # get max category number for given layer and update the settings
  1395. layer = UserSettings.Get(group = 'vdigit', key = 'layer', subkey = 'value')
  1396. cat = self.cats.get(layer, 0) + 1
  1397. UserSettings.Set(group = 'vdigit', key = 'category', subkey = 'value',
  1398. value = cat)
  1399. Debug.msg(1, "IVDigit._setCategoryNextToUse(): cat=%d", cat)
  1400. return cat
  1401. def SelectLinesFromBackgroundMap(self, bbox):
  1402. """!Select features from background map
  1403. @param bbox bounding box definition
  1404. @return list of selected feature ids
  1405. """
  1406. # try select features by box first
  1407. if self._display.SelectLinesByBox(bbox, poMapInfo = self.poBgMapInfo) < 1:
  1408. self._display.SelectLineByPoint(bbox[0], poMapInfo = self.poBgMapInfo)['line']
  1409. return self._display.selected['ids']
  1410. def GetUndoLevel(self):
  1411. """!Get undo level (number of active changesets)
  1412. Note: Changesets starts wiht 0
  1413. """
  1414. return self.changesetCurrent
  1415. def GetFeatureType(self):
  1416. """!Get feature type for OGR layers
  1417. @return feature type as string (point, linestring, polygon)
  1418. @return None for native format
  1419. """
  1420. return Vect_get_finfo_geometry_type(self.poMapInfo)