wxdigit.py 55 KB

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