|
@@ -0,0 +1,714 @@
|
|
|
+#!/usr/bin/env python
|
|
|
+
|
|
|
+############################################################################
|
|
|
+#
|
|
|
+# MODULE: d.rast.edit
|
|
|
+# AUTHOR(S): Glynn Clements <glynn@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()
|