wxdigit.py 69 KB

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