wxdigit.py 68 KB

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