[GRASS-SVN] r33661 - grass/trunk/scripts/d.rast.edit

svn_grass at osgeo.org svn_grass at osgeo.org
Fri Oct 3 23:45:54 EDT 2008


Author: glynn
Date: 2008-10-03 23:45:54 -0400 (Fri, 03 Oct 2008)
New Revision: 33661

Added:
   grass/trunk/scripts/d.rast.edit/d.rast.edit.py
Log:
Convert d.rast.edit to Python


Added: grass/trunk/scripts/d.rast.edit/d.rast.edit.py
===================================================================
--- grass/trunk/scripts/d.rast.edit/d.rast.edit.py	                        (rev 0)
+++ grass/trunk/scripts/d.rast.edit/d.rast.edit.py	2008-10-04 03:45:54 UTC (rev 33661)
@@ -0,0 +1,714 @@
+#!/usr/bin/env python
+
+############################################################################
+#
+# MODULE:       d.rast.edit
+# AUTHOR(S):    Glynn Clements <glynn at gclements.plus.com>
+# COPYRIGHT:    (C) 2007,2008 Glynn Clements
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#############################################################################/
+
+#%Module
+#% description: Interactively edit cell values in a raster map.
+#% keywords: display, raster
+#%End
+#%Option
+#% key: input
+#% type: string
+#% required: yes
+#% multiple: no
+#% key_desc: name
+#% description: Name of input raster map
+#% gisprompt: old,cell,raster
+#%End
+#%Option
+#% key: output
+#% type: string
+#% required: yes
+#% multiple: no
+#% key_desc: name
+#% description: Name for output raster map
+#% gisprompt: new,cell,raster
+#%End
+#%Option
+#% key: aspect
+#% type: string
+#% required: no
+#% multiple: no
+#% key_desc: name
+#% description: Name of aspect raster map
+#% gisprompt: old,cell,raster
+#%End
+#%Option
+#% key: width
+#% type: integer
+#% required: no
+#% multiple: no
+#% description: Width of display canvas
+#% answer: 640
+#%End
+#%Option
+#% key: height
+#% type: integer
+#% required: no
+#% multiple: no
+#% description: Height of display canvas
+#% answer: 480
+#%End
+#%Option
+#% key: size
+#% type: integer
+#% required: no
+#% multiple: no
+#% description: Minimum size of each cell
+#% answer: 12
+#%End
+#%Option
+#% key: rows
+#% type: integer
+#% required: no
+#% multiple: no
+#% description: Maximum number of rows to load
+#% answer: 100
+#%End
+#%Option
+#% key: cols
+#% type: integer
+#% required: no
+#% multiple: no
+#% description: Maximum number of columns to load
+#% answer: 100
+#%End
+
+import wxversion
+wxversion.select(['2.8','2.6'])
+#wxversion.select(['2.6','2.8'])
+import wx
+
+import sys
+import math
+import atexit
+import grass
+
+wind_keys = {
+    'north': ('n', float),
+    'south': ('s', float),
+    'east':  ('e', float),
+    'west':  ('w', float),
+    'nsres': ('nsres', float),
+    'ewres': ('ewres', float),
+    'rows':  ('rows', int),
+    'cols':  ('cols', int),
+    }
+
+gray12_bits = '\x00\x00\x22\x22\x00\x00\x88\x88\x00\x00\x22\x22\x00\x00\x88\x88\x00\x00\x22\x22\x00\x00\x88\x88\x00\x00\x22\x22\x00\x00\x88\x88'
+
+def run(cmd, **kwargs):
+    grass.run_command(cmd, quiet = True, **kwargs)
+
+class OverviewCanvas(wx.ScrolledWindow):
+    def __init__(self, app, parent):
+	wx.ScrolledWindow.__init__(self, parent)
+	self.app = app
+
+	self.width = app.total['cols']
+	self.height = app.total['rows']
+
+	self.SetVirtualSize((self.width, self.height))
+	self.SetScrollRate(1, 1)
+
+	self.Bind(wx.EVT_LEFT_DOWN, self.OnMouse)
+	self.Bind(wx.EVT_MOTION, self.OnMouse)
+	self.Bind(wx.EVT_LEFT_UP, self.OnMouse)
+	self.Bind(wx.EVT_PAINT, self.OnPaint)
+
+	run('r.out.ppm', input = app.inmap, output = app.tempfile)
+
+	self.image = wx.BitmapFromImage(wx.Image(app.tempfile))
+	grass.try_remove(app.tempfile)
+
+	app.force_window()
+
+    def OnPaint(self, ev):
+	x0 = self.app.origin_x
+	y0 = self.app.origin_y
+	dx = self.app.cols
+	dy = self.app.rows
+
+	dc = wx.PaintDC(self)
+	self.DoPrepareDC(dc)
+
+	src = wx.MemoryDC()
+	src.SelectObjectAsSource(self.image)
+	dc.Blit(0, 0, self.width, self.height, src, 0, 0)
+	src.SelectObjectAsSource(wx.NullBitmap)
+
+	dc.SetPen(wx.Pen('red', style = wx.LONG_DASH))
+	dc.SetBrush(wx.Brush('black', style = wx.TRANSPARENT))
+	dc.DrawRectangle(x0, y0, dx, dy)
+	dc.SetBrush(wx.NullBrush)
+	dc.SetPen(wx.NullPen)
+
+    def OnMouse(self, ev):
+	if ev.Moving():
+	    return
+	x = ev.GetX()
+	y = ev.GetY()
+	(x, y) = self.CalcUnscrolledPosition(x, y)
+	self.set_window(x, y)
+	if ev.ButtonUp():
+	    self.app.change_window()
+
+    def set_window(self, x, y):
+	self.app.origin_x = x - app.cols / 2
+	self.app.origin_y = y - app.rows / 2
+	self.app.force_window()
+	self.Refresh()
+
+class OverviewWindow(wx.Frame):
+    def __init__(self, app):
+	wx.Frame.__init__(self, None, title = "d.rast.edit overview (%s)" % app.inmap)
+	self.app = app
+
+	self.canvas = OverviewCanvas(app, parent = self)
+
+	self.Bind(wx.EVT_CLOSE, self.OnClose)
+
+    def OnClose(self, ev):
+	self.app.finalize()
+
+class Canvas(wx.ScrolledWindow):
+    def __init__(self, app, parent):
+	wx.ScrolledWindow.__init__(self, parent)
+	self.app = app
+
+	self.size = app.size
+
+	w = app.cols * self.size
+	h = app.rows * self.size
+
+	self.SetVirtualSize((w, h))
+	self.SetVirtualSizeHints(50, 50)
+	self.SetScrollRate(1, 1)
+
+	self.Bind(wx.EVT_LEFT_DOWN, self.OnMouse)
+	self.Bind(wx.EVT_RIGHT_DOWN, self.OnMouse)
+	self.Bind(wx.EVT_MOTION, self.OnMouse)
+	self.Bind(wx.EVT_PAINT, self.OnPaint2)
+
+	self.row = 0
+	self.col = 0
+
+	self.gray12 = wx.BitmapFromBits(gray12_bits, 16, 16)
+
+    def OnMouse(self, ev):
+	oldrow = self.row
+	oldcol = self.col
+
+	x = ev.GetX()
+	y = ev.GetY()
+	(x, y) = self.CalcUnscrolledPosition(x, y)
+
+	col = x / self.size
+	row = y / self.size
+
+	self.row = row
+	self.col = col
+
+	if ev.Moving():
+	    self.refresh_cell(oldrow, oldcol)
+	    self.refresh_cell(row, col)
+	elif ev.ButtonDown(wx.MOUSE_BTN_LEFT):
+	    self.cell_set()
+	elif ev.ButtonDown(wx.MOUSE_BTN_RIGHT):
+	    self.cell_get()
+
+    def paint_cell(self, dc, r, c):
+	if r < 0 or r >= self.app.rows or c < 0 or c >= self.app.cols:
+	    return
+
+	val = self.app.values[r][c]
+	if val == None:
+	    fill = 'black'
+	    stipple = self.gray12 
+	else:
+	    fill = self.app.get_color(val)
+	    stipple = None
+
+	if r == self.row and c == self.col:
+	    outline = 'red'
+	elif self.app.changed[r][c]:
+	    outline = 'white'
+	else:
+	    outline = 'black'
+
+	dc.SetPen(wx.Pen(outline))
+
+	if stipple:
+	    brush = wx.Brush(fill, style = wx.STIPPLE)
+	    brush.SetStipple(stipple)
+	else:
+	    brush = wx.Brush(fill, style = wx.SOLID)
+
+	x0 = c * self.size + 1
+	x1 = x0  + self.size - 1
+	y0 = r * self.size + 1
+	y1 = y0  + self.size - 1
+
+	dc.SetBrush(brush)
+	dc.DrawRectangle(x0, y0, x1 - x0, y1 - y0)
+	dc.SetPen(wx.NullPen)
+	dc.SetBrush(wx.NullBrush)
+
+	if not self.app.angles:
+	    return
+
+	if self.app.angles[r][c] == '*':
+	    return
+
+	cx = (x0 + x1) / 2
+	cy = (y0 + y1) / 2
+
+	a = math.radians(float(self.app.angles[r][c]))
+	dx =  math.cos(a) * self.size / 2
+	dy = -math.sin(a) * self.size / 2
+
+	x0 = cx - dx
+	y0 = cy - dy
+	x1 = cx + dx
+	y1 = cy + dy
+
+	dx, dy = x1 - x0, y1 - y0
+	px, py = -dy, dx
+
+	r,g,b = wx.NamedColor(fill)
+	if r + g + b > 384:
+	    line = 'black'
+	else:
+	    line = 'white'
+
+	dc.SetPen(wx.Pen(line))
+	dc.DrawLine(x0, y0, x1, y1)
+	dc.DrawLine(x1, y1, x1 + px/6 - dx/3, y1 + py/6 - dy/3)
+	dc.DrawLine(x1, y1, x1 - px/6 - dx/3, y1 - py/6 - dy/3)
+	dc.SetPen(wx.NullPen)
+
+    def paint_rect(self, dc, x, y, w, h):
+	c0 = (x + 0) / self.size
+	c1 = (x + w + 1) / self.size
+	r0 = (y + 0) / self.size
+	r1 = (y + h + 1) / self.size
+	for r in range(r0, r1 + 1):
+	    for c in range(c0, c1 + 1):
+		self.paint_cell(dc, r, c)
+
+    def OnPaint(self, ev):
+	dc = wx.PaintDC(self)
+	self.DoPrepareDC(dc)
+	for r in range(self.app.rows):
+	    for c in range(self.app.cols):
+		self.paint_cell(dc, r, c)
+
+    def OnPaint2(self, ev):
+	dc = wx.PaintDC(self)
+	self.DoPrepareDC(dc)
+	it = wx.RegionIterator(self.GetUpdateRegion())
+	while it.HaveRects():
+	    x = it.GetX()
+	    y = it.GetY()
+	    w = it.GetW()
+	    h = it.GetH()
+	    (x, y) = self.CalcUnscrolledPosition(x, y)
+	    self.paint_rect(dc, x, y, w, h)
+	    it.Next()
+
+    def cell_enter(self):
+	if not self.row or not self.col:
+	    return
+	self.app.update_status(self.row, self.col)
+
+    def cell_leave(self):
+	self.app.clear_status()
+
+    def cell_get(self):
+	self.app.brush = self.app.values[self.row][self.col]
+	self.app.frame.brush_update()
+
+    def cell_set(self):
+	self.app.values[self.row][self.col] = self.app.brush
+	self.app.changed[self.row][self.col] = True
+	self.refresh_cell(self.row, self.col)
+
+    def refresh_cell(self, row, col):
+	x = col * self.size
+	y = row * self.size
+	(x, y) = self.CalcScrolledPosition(x, y)
+	self.RefreshRect((x, y, self.size, self.size))
+
+class ColorPanel(wx.Panel):
+    def __init__(self, **kwargs):
+	wx.Panel.__init__(self, **kwargs)
+	self.SetBackgroundStyle(wx.BG_STYLE_COLOUR)
+	self.stipple = wx.BitmapFromBits(gray12_bits, 16, 16)
+	self.null_bg = True
+	self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnErase)
+
+    def OnErase(self, ev):
+	self.ClearBackground()
+
+	if not self.null_bg:
+	    return
+
+	dc = ev.GetDC()
+	if not dc:
+	    dc = wx.ClientDC(self)
+
+	brush = wx.Brush('black', style = wx.STIPPLE)
+	brush.SetStipple(self.stipple)
+	dc.SetBackground(brush)
+	dc.Clear()
+	dc.SetBackground(wx.NullBrush)
+
+    def SetNullBackgroundColour(self):
+	wx.Panel.SetBackgroundColour(self, 'gray')
+	self.null_bg = True
+
+    def SetBackgroundColour(self, color):
+	wx.Panel.SetBackgroundColour(self, color)
+	self.null_bg = False
+
+class MainWindow(wx.Frame):
+    def __init__(self, app):
+	wx.Frame.__init__(self, None, title = "d.rast.edit (%s)" % app.inmap)
+	self.app = app
+
+	self.Bind(wx.EVT_CLOSE, self.OnClose)
+
+	filemenu = wx.Menu()
+	filemenu.Append(wx.ID_SAVE, "&Save", "Save changes")
+	filemenu.Append(wx.ID_EXIT, "E&xit", "Terminate the program")
+	menubar = wx.MenuBar()
+	menubar.Append(filemenu, "&File")
+	self.SetMenuBar(menubar)
+
+	wx.EVT_MENU(self, wx.ID_SAVE, self.OnSave)
+	wx.EVT_MENU(self, wx.ID_EXIT, self.OnExit)
+
+	sizer = wx.BoxSizer(orient = wx.VERTICAL)
+
+	self.canvas = Canvas(app, parent = self)
+	si = sizer.Add(self.canvas, proportion = 1, flag = wx.EXPAND)
+
+	tools = wx.BoxSizer(wx.HORIZONTAL)
+
+	l = wx.StaticText(parent = self, label = 'New Value:')
+	tools.Add(l, flag = wx.ALIGN_CENTER_VERTICAL)
+	tools.AddSpacer(5)
+
+	self.newval = wx.TextCtrl(parent = self, style = wx.TE_PROCESS_ENTER)
+	tools.Add(self.newval, flag = wx.ALIGN_CENTER_VERTICAL)
+
+	l = wx.StaticText(parent = self, label = 'Color:')
+	tools.Add(l, flag = wx.ALIGN_CENTER_VERTICAL)
+	tools.AddSpacer(5)
+
+	self.color = ColorPanel(parent = self, size = (30,5))
+	tools.Add(self.color, flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)
+
+	self.Bind(wx.EVT_TEXT_ENTER, self.OnReturn, source = self.newval)
+
+	sizer.AddSizer(tools, proportion = 0, flag = wx.EXPAND)
+
+	self.SetSizerAndFit(sizer)
+	self.SetSize((app.width, app.height))
+
+	self.status = self.CreateStatusBar(6)
+
+	self.brush_update()
+
+    def OnSave(self, ev):
+	self.app.save_map()
+
+    def OnExit(self, ev):
+	self.Close(True)
+
+    def OnClose(self, ev):
+	self.app.finalize()
+
+    def OnReturn(self, ev):
+	self.app.brush = self.newval.GetValue()
+	if self.app.brush != '*' and self.app.brush.strip('0123456789') != '':
+	    self.app.brush = '*'
+	self.brush_update()
+
+    def update_status(self):
+	for i, key in enumerate(['row', 'col', 'x', 'y', 'value', 'aspect']):
+	    s = "%s%s: %s" % (key[0].upper(), key[1:], self.app.status[key])
+	    self.status.SetStatusText(s, i)
+
+    def clear_status(self):
+	for key in self.status:
+	    self.status[key] = ''
+
+    def brush_update(self):
+	self.newval.ChangeValue(self.app.brush)
+	if self.app.brush == '*':
+	    self.color.SetNullBackgroundColour()
+	else:
+	    self.color.SetBackgroundColour(self.app.get_color(self.app.brush))
+	self.color.Refresh()
+
+class Application(wx.App):
+    def __init__(self, options):
+	self.options = options
+	wx.App.__init__(self)
+
+    def initialize(self):
+	grass.use_temp_region()
+
+	run('g.region', rast = self.inmap)
+
+	reg = grass.region()
+	for k, f in wind_keys.values():
+	    self.total[k] = (f)(reg[k])
+
+	if self.cols > self.total['cols']:
+	    self.cols = self.total['cols']
+	if self.rows > self.total['rows']:
+	    self.rows = self.total['rows']
+
+	tempbase = grass.tempfile()
+	grass.try_remove(tempbase)
+
+	self.tempfile = tempbase + '.ppm'
+	self.tempmap = 'tmp.d.rast.edit'
+
+	atexit.register(self.cleanup)
+
+	run('g.copy', rast = (self.inmap, self.outmap), overwrite = True)
+	run('r.colors', map = self.outmap, rast = self.inmap)
+
+    def cleanup(self):
+	grass.try_remove(self.tempfile)
+	run('g.remove', rast = self.tempmap)
+
+    def finalize(self):
+	self.save_map()
+	sys.exit(0)
+
+    def save_map(self):
+	p = grass.feed_command('r.in.ascii', input = '-', output = self.tempmap, quiet = True, overwrite = True)
+	outf = p.stdin
+	outf.write("north: %f\n" % self.wind['n'])
+	outf.write("south: %f\n" % self.wind['s'])
+	outf.write("east: %f\n"  % self.wind['e'])
+	outf.write("west: %f\n"  % self.wind['w'])
+	outf.write("rows: %d\n"  % self.wind['rows'])
+	outf.write("cols: %d\n"  % self.wind['cols'])
+	outf.write("null: *\n")
+
+	for row in range(self.wind['rows']):
+	    for col in range(self.wind['cols']):
+		if col > 0:
+		    outf.write(" ")
+		val = self.values[row][col]
+		if val and self.changed[row][col]:
+		    outf.write("%s" % val)
+		else:
+		    outf.write('*')
+	    outf.write("\n")
+
+	outf.close()
+	p.wait()
+
+	run('g.region', rast = self.inmap)
+	run('r.patch', input = (self.tempmap, self.outmap), output = self.outmap, overwrite = True)
+	run('r.colors', map = self.outmap, rast = self.inmap)
+	run('g.remove', rast = self.tempmap)
+
+    def read_header(self, infile):
+	wind = {}
+	for i in range(6):
+	    line = infile.readline().rstrip('\r\n')
+	    f = line.split(':')
+	    key = f[0]
+	    val = f[1].strip()
+	    (k, f) = wind_keys[key]
+	    wind[k] = (f)(val)
+	return wind
+
+    def read_data(self, infile):
+	values = []
+	for row in range(self.wind['rows']):
+	    line = infile.readline().rstrip('\r\n')
+	    values.append(line.split())
+	return values
+
+    def load_map(self):
+	run('g.region', **self.wind)
+
+	p = grass.pipe_command('r.out.ascii', input = self.inmap, quiet = True)
+	self.wind = self.read_header(p.stdout)
+	self.values = self.read_data(p.stdout)
+	self.changed = [[False for c in row] for row in self.values]
+	p.wait()
+
+	self.clear_changes()
+
+	run('r.out.ppm', input = self.inmap, output = self.tempfile)
+	colorimg = wx.Image(self.tempfile)
+	grass.try_remove(self.tempfile)
+
+	for row in range(self.wind['rows']):
+	    for col in range(self.wind['cols']):
+		val = self.values[row][col]
+		if val in self.colors:
+		    continue
+		r = colorimg.GetRed(col, row)
+		g = colorimg.GetGreen(col, row)
+		b = colorimg.GetBlue(col, row)
+		color = "#%02x%02x%02x" % (r, g, b)
+		self.colors[val] = color
+
+	colorimg.Destroy()
+
+    def load_aspect(self):
+	if not self.aspect:
+	    return
+
+	p = grass.pipe_command('r.out.ascii', input = self.aspect, quiet = True)
+	self.read_header(p.stdout)
+	self.angles = self.read_data(p.stdout)
+	p.wait()
+
+    def clear_changes(self):
+	for row in range(self.wind['rows']):
+	    for col in range(self.wind['cols']):
+		self.changed[row][col] = 0
+
+    def update_window(self):
+	x0 = self.origin_x
+	y0 = self.origin_y
+	x1 = x0 + self.cols
+	y1 = y0 + self.rows
+
+	self.wind['n'] = self.total['n'] - y0 * self.total['nsres']
+	self.wind['s'] = self.total['n'] - y1 * self.total['nsres']
+	self.wind['w'] = self.total['w'] + x0 * self.total['ewres']
+	self.wind['e'] = self.total['w'] + x1 * self.total['ewres']
+	self.wind['rows'] = self.rows
+	self.wind['cols'] = self.cols
+
+    def change_window(self):
+	self.save_map()
+	self.update_window()
+	self.load_map()
+	self.load_aspect()
+	self.refresh_canvas()
+
+    def force_window(self):
+	if self.origin_x < 0:
+	    self.origin_x = 0
+	if self.origin_x > self.total['cols'] - self.cols:
+	    self.origin_x = self.total['cols'] - self.cols
+	if self.origin_y < 0:
+	    self.origin_y = 0
+	if self.origin_y > self.total['rows'] - self.rows:
+	    self.origin_y = self.total['rows'] - self.rows
+
+    def update_status(self, row, col):
+	self.status['row'] = row
+	self.status['col'] = col
+	self.status['x'] = self.wind['e'] + (col + 0.5) * (self.wind['e'] - self.wind['w']) / self.wind['cols']
+	self.status['y'] = self.wind['n'] - (row + 0.5) * (self.wind['n'] - self.wind['s']) / self.wind['rows']
+	self.status['value'] = self.values[row][col]
+	if self.angles:
+	    self.status['aspect'] = self.angles[row][col]
+
+    def force_color(val):
+	run('g.region', rows = 1, cols = 1)
+	run('r.mapcalc', expression = "%s = %d" % (self.tempmap, val))
+	run('r.colors', map = self.tempmap, rast = self.inmap)
+	run('r.out.ppm', input = self.tempmap, out = self.tempfile)
+	run('g.remove', rast = self.tempmap)
+
+	tempimg = wx.Image(self.tempfile)
+	grass.try_remove(self.tempfile)
+
+	rgb = tempimg.get(0, 0)
+	color = "#%02x%02x%02x" % rgb
+	self.colors[val] = color
+	tempimg.delete()
+
+    def get_color(self, val):
+	if val not in self.colors:
+	    try:
+		self.force_color(val)
+	    except:
+		self.colors[val] = "#ffffff"
+
+	return self.colors[val]
+
+    def refresh_canvas(self):
+	self.frame.canvas.Refresh()
+
+    def make_ui(self):
+	self.frame = MainWindow(self)
+	self.SetTopWindow(self.frame)
+	self.frame.Show()
+	self.overview = OverviewWindow(self)
+	self.overview.Show()
+
+    def OnInit(self):
+	self.outmap = self.options['output']
+	self.inmap  = self.options['input']
+	self.aspect = self.options['aspect']
+	self.width  = int(self.options['width'])
+	self.height = int(self.options['height'])
+	self.size   = int(self.options['size'])
+	self.rows   = int(self.options['rows'])
+	self.cols   = int(self.options['cols'])
+
+	self.status = {
+	    'row': '',
+	    'col': '',
+	    'x': '',
+	    'y': '',
+	    'value': '',
+	    'aspect': ''
+	}
+
+	self.values = None
+	self.changed = None
+	self.angles = None
+	self.colors = {}
+	self.brush = '*'
+	self.origin_x = 0
+	self.origin_y = 0
+	self.wind = {}
+	self.total = {}
+
+	self.initialize()
+	self.update_window()
+	self.make_ui()
+	self.load_map()
+	self.load_aspect()
+	self.refresh_canvas()
+
+	return True
+
+if __name__ == "__main__":
+    options, flags = grass.parser()
+
+    app = Application(options)
+    app.MainLoop()


Property changes on: grass/trunk/scripts/d.rast.edit/d.rast.edit.py
___________________________________________________________________
Name: svn:executable
   + *



More information about the grass-commit mailing list