wxdigit.py 67 KB

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