wxvdigit.py 59 KB

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