wxdigit.py 60 KB

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