wxdigit.py 67 KB

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