4 @brief wxGUI vector digitizer (display driver)
6 Code based on wxVdigit C++ component from GRASS 6.4.0
7 (gui/wxpython/vdigit). Converted to Python in 2010/12-2011/01.
12 (C) 2007-2011 by the GRASS Development Team
14 This program is free software under the GNU General Public License
15 (>=v2). Read the file COPYING that comes with GRASS for details.
17 @author Martin Landa <landa.martin gmail.com>
25 from debug
import Debug
26 from preferences
import globalSettings
as UserSettings
36 """!Redirect stderr"""
46 """!Redirect progress info"""
49 progress.SetValue(value)
55 errtype = CFUNCTYPE(UNCHECKED(c_int), String, c_int)
57 pertype = CFUNCTYPE(UNCHECKED(c_int), c_int)
61 def __init__(self, device, deviceTmp, mapObj, window, glog, gprogress):
62 """!Display driver used by vector digitizer
64 @param device wx.PseudoDC device where to draw vector objects
65 @param deviceTmp wx.PseudoDC device where to draw temporary vector objects
66 @param mapOng Map Object (render.Map)
67 @param windiow parent window for dialogs
68 @param glog logging device (None to discard messages)
69 @param gprogress progress bar device (None to discard message)
71 global errfunc, perfunc, log, progress
76 locale.setlocale(locale.LC_NUMERIC,
'C')
87 self.
region = mapObj.GetCurrentRegion()
109 'highlightDupl' : {
'enabled' :
False,
111 'point' : {
'enabled' :
False,
113 'line' : {
'enabled' :
False,
115 'boundaryNo' : {
'enabled' :
False,
117 'boundaryOne' : {
'enabled' :
False,
119 'boundaryTwo' : {
'enabled' :
False,
121 'centroidIn' : {
'enabled' :
False,
123 'centroidOut' : {
'enabled' :
False,
125 'centroidDup' : {
'enabled' :
False,
127 'nodeOne' : {
'enabled' :
False,
129 'nodeTwo' : {
'enabled' :
False,
131 'vertex' : {
'enabled' :
False,
133 'area' : {
'enabled' :
False,
135 'direction' : {
'enabled' :
False,
149 """!Close currently open vector map"""
159 def _resetTopology(self):
160 """!Reset topology dict
177 def _cell2Pixel(self, east, north, elev):
178 """!Conversion from geographic coordinates (east, north)
183 @param east, north, elev geographical coordinates
185 @return x, y screen coordinates (integer)
188 w = self.
region[
'center_easting'] - (self.mapObj.width / 2) * map_res
189 n = self.
region[
'center_northing'] + (self.mapObj.height / 2) * map_res
191 return int((east - w) / map_res), int((n - north) / map_res)
193 def _drawCross(self, pdc, point, size = 5):
194 """!Draw cross symbol of given size to device content
196 Used for points, nodes, vertices
198 @param[in,out] PseudoDC where to draw
199 @param point coordinates of center
200 @param size size of the cross symbol
203 @return -1 on failure
205 if not pdc
or not point:
208 pdc.DrawLine(point.x - size, point.y, point.x + size, point.y)
209 pdc.DrawLine(point.x, point.y - size, point.x, point.y + size)
213 def _drawObject(self, robj):
214 """!Draw given object to the device
216 The object is defined as robject() from vedit.h.
218 @param robj object to be rendered
221 @return -1 on failure (vector feature marked as dead, etc.)
223 if not self.
dc or not self.
dcTmp:
226 Debug.msg(3,
"_drawObject(): type=%d npoints=%d", robj.type, robj.npoints)
231 pen = wx.Pen(self.
settings[
'highlightDupl'][
'color'], self.
settings[
'lineWidth'], wx.SOLID)
233 pen = wx.Pen(self.
settings[
'highlight'], self.
settings[
'lineWidth'], wx.SOLID)
248 if robj.type & (TYPE_POINT | TYPE_CENTROIDIN | TYPE_CENTROIDOUT | TYPE_CENTROIDDUP |
249 TYPE_NODEONE | TYPE_NODETWO | TYPE_VERTEX):
251 if robj.type == TYPE_VERTEX:
253 elif robj.type & (TYPE_NODEONE | TYPE_NODETWO):
260 for i
in range(robj.npoints):
272 while i < robj.npoints - 1:
273 point_beg = wx.Point(robj.point[i].x, robj.point[i].y)
274 point_end = wx.Point(robj.point[i+1].x, robj.point[i+1].y)
277 pdc.SetIdBounds(dcId - 1, wx.Rect(point_beg.x, point_beg.y, 0, 0))
278 pdc.SetIdBounds(dcId, wx.RectPP(point_beg, point_end))
279 pdc.DrawLine(point_beg.x, point_beg.y,
280 point_end.x, point_end.y)
283 pdc.SetIdBounds(dcId - 1, wx.Rect(robj.point[robj.npoints - 1].x,
284 robj.point[robj.npoints - 1].y,
288 for i
in range(robj.npoints):
290 points.append(wx.Point(p.x, p.y))
292 if robj.type == TYPE_AREA:
293 pdc.DrawPolygon(points)
295 pdc.DrawLines(points)
297 def _definePen(self, rtype):
298 """!Define pen/brush based on rendered object)
300 Updates also self.topology dict
304 if rtype == TYPE_POINT:
306 elif rtype == TYPE_LINE:
308 elif rtype == TYPE_BOUNDARYNO:
310 elif rtype == TYPE_BOUNDARYTWO:
312 elif rtype == TYPE_BOUNDARYONE:
314 elif rtype == TYPE_CENTROIDIN:
316 elif rtype == TYPE_CENTROIDOUT:
318 elif rtype == TYPE_CENTROIDDUP:
320 elif rtype == TYPE_NODEONE:
322 elif rtype == TYPE_NODETWO:
324 elif rtype == TYPE_VERTEX:
326 elif rtype == TYPE_AREA:
328 elif rtype == TYPE_ISLE:
330 elif rtype == TYPE_DIRECTION:
333 if key
not in (
'direction',
'area',
'isle'):
336 if key
in (
'area',
'isle'):
337 pen = wx.TRANSPARENT_PEN
339 brush = wx.Brush(self.
settings[key][
'color'], wx.SOLID)
341 brush = wx.TRANSPARENT_BRUSH
343 pen = wx.Pen(self.
settings[key][
'color'], self.
settings[
'lineWidth'], wx.SOLID)
348 def _getDrawFlag(self):
349 """!Get draw flag from the settings
351 See vedit.h for list of draw flags.
353 @return draw flag (int)
356 if self.
settings[
'point'][
'enabled']:
358 if self.
settings[
'line'][
'enabled']:
360 if self.
settings[
'boundaryNo'][
'enabled']:
361 ret |= DRAW_BOUNDARYNO
362 if self.
settings[
'boundaryTwo'][
'enabled']:
363 ret |= DRAW_BOUNDARYTWO
364 if self.
settings[
'boundaryOne'][
'enabled']:
365 ret |= DRAW_BOUNDARYONE
366 if self.
settings[
'centroidIn'][
'enabled']:
367 ret |= DRAW_CENTROIDIN
368 if self.
settings[
'centroidOut'][
'enabled']:
369 ret |= DRAW_CENTROIDOUT
370 if self.
settings[
'centroidDup'][
'enabled']:
371 ret |= DRAW_CENTROIDDUP
372 if self.
settings[
'nodeOne'][
'enabled']:
374 if self.
settings[
'nodeTwo'][
'enabled']:
376 if self.
settings[
'vertex'][
'enabled']:
378 if self.
settings[
'area'][
'enabled']:
380 if self.
settings[
'direction'][
'enabled']:
381 ret |= DRAW_DIRECTION
385 def _isSelected(self, line, force = False):
386 """!Check if vector object selected?
388 @param line feature id
390 @return True if vector object is selected
391 @return False if vector object is not selected
393 if len(self.
selected[
'cats']) < 1
or force:
399 cats = self.poCats.contents
400 for i
in range(cats.n_cats):
401 if cats.field[i] == self.
selected[
'field']
and \
402 cats.cat[i]
in self.
selected[
'cats']:
410 def _isDuplicated(self, line):
411 """!Check for already marked duplicates
413 @param line feature id
415 @return True line already marked as duplicated
416 @return False not duplicated
418 return line
in self.
selected[
'idsDupl']
420 def _getRegionBox(self):
421 """!Get bound_box() from current region
431 box.T = PORT_DOUBLE_MAX
432 box.B = -PORT_DOUBLE_MAX
437 """!Draw content of the vector map to the device
439 @param force force drawing
440 @return number of drawn features
443 Debug.msg(1,
"DisplayDriver.DrawMap(): force=%d", force)
449 self.
region[
'center_easting'], self.
region[
'center_northing'],
450 self.mapObj.width, self.mapObj.height,
455 self.dc.BeginDrawing()
456 self.dcTmp.BeginDrawing()
459 for i
in range(rlist.nitems):
460 robj = rlist.item[i].contents
464 self.dcTmp.EndDrawing()
471 def _getSelectType(self):
472 """!Get type(s) to be selected
474 Used by SelectLinesByBox() and SelectLineByPoint()
477 for feature
in ((
'point', GV_POINT),
479 (
'centroid', GV_CENTROID),
480 (
'boundary', GV_BOUNDARY)):
481 if UserSettings.Get(group =
'vdigit', key =
'selectType',
482 subkey = [feature[0],
'enabled']):
487 def _validLine(self, line):
488 """!Check if feature id is valid
490 @param line feature id
492 @return True valid feature id
493 @return False invalid
501 """!Select vector objects by given bounding box
503 If line id is already in the list of selected lines, then it will
504 be excluded from this list.
506 @param bbox bounding box definition
507 @param drawSeg True to draw segments of line
508 @param poMapInfo use external Map_info, None for self.poMapInfo
510 @return number of selected features
511 @return None on error
513 thisMapInfo = poMapInfo
is None
541 flist = poList.contents
542 nlines = flist.n_values
543 Debug.msg(1,
"DisplayDriver.SelectLinesByBox() num = %d", nlines)
544 for i
in range(nlines):
545 line = flist.value[i]
546 if UserSettings.Get(group =
'vdigit', key =
'selectInside',
552 points = self.poPoints.contents
553 for p
in range(points.n_points):
573 """!Select vector feature by given point in given
576 Only one vector object can be selected. Bounding boxes of
577 all segments are stores.
579 @param point points coordinates (x, y)
580 @param poMapInfo use external Map_info, None for self.poMapInfo
582 @return dict {'line' : feature id, 'point' : point on line}
584 thisMapInfo = poMapInfo
is None
589 return {
'line' : -1,
'point':
None }
601 Debug.msg(1,
"DisplayDriver.SelectLineByPoint() found = %d", lineNearest)
605 self.
selected[
'ids'].append(lineNearest)
607 self.
selected[
'ids'].remove(lineNearest)
613 return {
'line' : -1,
'point':
None }
615 Vect_line_distance (self.
poPoints, point[0], point[1], 0.0, self.
is3D,
616 byref(px), byref(py), byref(pz),
620 if self.
settings[
'highlightDupl'][
'enabled']:
621 found = poFound.contents
622 for i
in range(found.n_values):
623 line = found.value[i]
624 if line != lineNearest:
629 for i
in range(found.n_values):
630 line = found.value[i]
641 return {
'line' : lineNearest,
642 'point' : (px.value, py.value, pz.value) }
644 def _listToIList(self, plist):
645 """!Generate from list struct_ilist
654 """!Get list of selected objects as struct_ilist
656 Returned IList must be freed by Vect_destroy_list().
666 """!Get ids of selected objects
668 @param grassId True for feature id, False for PseudoDC id
670 @return list of ids of selected vector objects
683 points = self.poPoints.contents
685 for i
in range(1, 2 * points.n_points):
691 """!Set selected vector objects
693 @param list of ids (None to unselect features)
694 @param layer layer number for features selected based on category number
702 selected.field = layer
709 """!Get PseudoDC vertex id of selected line
711 Set bounding box for vertices of line.
715 @return id of center, left and right vertex
716 @return 0 no line found
735 points = self.poPoints.contents
736 for idx
in range(points.n_points):
738 points.x[idx], points.y[idx], points.z[idx], 0)
748 vx, vy = self.
_cell2Pixel(points.x[idx], points.y[idx], points.z[idx])
749 rect = wx.Rect(vx, vy, 0, 0)
750 self.dc.SetIdBounds(DCid, rect)
760 returnId.append(DCid)
765 returnId.append(DCid - 2)
767 if DCid == (points.n_points - 1) * 2 + startId:
770 returnId.append(DCid + 2)
775 """!Draw selected features
777 @param flag True to draw selected features
785 @return non-zero on error
800 def OpenMap(self, name, mapset, update = True):
801 """!Open vector map by the driver
803 @param name name of vector map to be open
804 @param mapset name of mapset where the vector map lives
807 @return None on error
809 Debug.msg(
"DisplayDriver.OpenMap(): name=%s mapset=%s updated=%d",
810 name, mapset, update)
826 dlg = wx.MessageDialog(parent = self.
window,
827 message = _(
"Topology for vector map <%s> is not available. "
828 "Topology is required by digitizer. Do you want to "
829 "rebuild topology (takes some time) and open the vector map "
830 "for editing?") % name,
831 caption=_(
"Topology missing"),
832 style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION | wx.CENTRE)
833 ret = dlg.ShowModal()
843 """!Get bounding box of (opened) vector map layer
845 @return (w,s,b,e,n,t)
853 return bbox.W, bbox.S, bbox.B, \
854 bbox.E, bbox.N, bbox.T
857 """!Update display driver settings
861 @alpha color value for aplha channel
864 for key
in self.settings.keys():
865 if key ==
'lineWidth':
866 self.
settings[key] = int(UserSettings.Get(group =
'vdigit', key =
'lineWidth',
870 color = wx.Color(UserSettings.Get(group =
'vdigit', key =
'symbol',
871 subkey = [key,
'color'])[0],
872 UserSettings.Get(group =
'vdigit', key =
'symbol',
873 subkey = [key,
'color'])[1],
874 UserSettings.Get(group =
'vdigit', key =
'symbol',
875 subkey = [key,
'color'])[2],
878 if key ==
'highlight':
882 if key ==
'highlightDupl':
883 self.
settings[key][
'enabled'] = bool(UserSettings.Get(group =
'vdigit', key =
'checkForDupl',
886 self.
settings[key][
'enabled'] = bool(UserSettings.Get(group =
'vdigit', key =
'symbol',
887 subkey = [key,
'enabled']))
892 """!Update geographical region used by display driver
894 self.
region = self.mapObj.GetCurrentRegion()
897 """!Return threshold value in map units
899 @param type snapping mode (node, vertex)
900 @param value threshold to be set up
901 @param units units (map, screen)
903 @return threshold value
906 value = UserSettings.Get(group =
'vdigit', key = type, subkey =
'value')
909 units = UserSettings.Get(group =
'vdigit', key = type, subkey =
'units')
912 value = (self.
region[
'nsres'] + self.
region[
'ewres']) / 2.0
914 if units ==
"screen pixels":
922 """!Return ids of (selected) duplicated vector features
933 for i
in range(len(self.
selected[
'ids'])):
950 self.
selected[
'idsDupl'].append(line1)
953 self.
selected[
'idsDupl'].append(line2)
960 def _getCatString(self, line):
963 cats = self.poCats.contents
965 for i
in range(cats.n_cats):
966 layer = cats.field[i]
967 if layer
not in catsDict:
968 catsDict[layer] = list()
969 catsDict[layer].append(cats.cat[i])
972 for l, c
in catsDict.iteritems():
973 catsStr =
'%d: (%s)' % (l,
','.join(
map(str, c)))
978 """!Unselect vector features
980 @param lines list of feature id(s)