wxdigit.py 60 KB

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