biomass2Extract.sml

  Download

More scripts: Advanced

Syntax Highlighing:

comments, key words, predefined symbols, class members & methods, functions & classes
            
#
#  Biomass.sml  (SML object within the biomass2.sml Applidat)
#
#	This is how to declare a global variable so that
#	they can be used in functions before it's assigned.
#	Without the explicit declaration, the variable will
#	be assumed to be numeric or a Raster (depending on
#	the case of the first letter).
#
numeric sample_height, sample_width, sample_space, sample_text_x, num_ranges;
sample_height = 15;		# Height of sample boxes
sample_width = 40;		# Width of sample boxes
sample_space = 5;		# Space between boxes
sample_text_x = 37;		# Where to start the right justified text.
num_ranges = 10;		# Number of ranges to break histogram into.
class ColorMap cmap;
class Color black, white, c0;
class XmDrawingArea da;
class XmForm form;			# The main form dialog
class GC gc;	# The context for drawing into the view
class RASTER Composite, BiomassRaster, FilteredBiomassRaster;
class VECTOR BiomassVector;
class GRE_VIEW view;
class GRE_GROUP group;
class GRE_LAYER_RASTER layer, overlay;			# Our base layer
class GRE_LAYER_VECTOR voverlay;
array numeric histo[256];
numeric cellsize, unitconv;
class GUI_GADGET_POLYLINE tool;
class GUI_CTRL_TOGGLEBUTTON plusbutton;
class GUI_CTRL_TOGGLEBUTTON minusbutton;
class XmToggleButton drawhisto;
class PushButtonItem buttonConvertToVector, buttonFiltVector, buttonFiltRaster;
class PushButtonItem buttonClearOverlay, buttonExit;
class XmForm button_row;
class XmFrame frame;
numeric height, width;
class REGION2D MyRgn;
class ToolTip tip, tip2, tip3;
string str$ = "";
numeric firstpass, minusregion;
firstpass = 1;
minusregion = 0;
#unitconv = GetUnitConvArea("square meters", "acres");
# Note, GetUnitConvArea() fails if running on a system with localized
#  resources.  So just hard code the conversion
unitconv = 1.0 / 4046.873;
# If the script is in an RVC file, assume all the data
# is in that file.  If it's not, assume I'm still working
# on it and get the data from my data directory
if (_context.ScriptIsRVCObject) {
	string datafile$ = _context.Filename;
	}
else {
	datafile$ = "e:/data/toolbar/smldemo.rvc";
	}
string outputfile$ = FileNameGetPath(datafile$) + "/biomass.rvc";
CreateProjectFile(outputfile$, "Biomass");
################################################################
#
#	This is the callback function which will be called
#	when the exit button is pressed. 
#
proc cbQuit(class widget widget) {
	DialogClose(form);
	}
################################################################
#
#	This is the callback function which will be called
#	when the dialog goes away.  This can happen either because
#	we called DialogClose() from our Quit callback or because
#	the user closed the dialog using the [X] icon on the title bar.
#
proc cbPopdown(class widget widget) {
	ViewDestroy(view);
	GroupDestroy(group);
	DestroyWidget(form);
	Exit();		# Nothing else from here on!
	}
################################################################
#
#	Draw the bar graph
#
proc drawGraph(class XmDrawingArea da) {
	local numeric maxval, total, i, scale, w, y;
	# Clear the drawing area
	if (!gc) return;
	ActivateGC(gc);
	SetColorRGB(0,0,0);
	FillRect(0, 0, da.width, da.height);
	maxval = 0;
	total = 0;
	for i = 1 to num_ranges {
		maxval = SetMax(maxval, histo[i]);
		total += histo[i];
		}
	if (total) {	# Don't draw bars if nothing selected
		w = da.width - 2 * sample_space;
		scale = w / maxval;
		if (drawhisto.set) {
			for i = 1 to num_ranges {
				y = (num_ranges - i + 1) * (sample_height + sample_space) + sample_space;
				SetColor(ColorMapGetColor(cmap, i));
				w = scale * histo[i];
				if (w < 1 && histo[i] != 0) w = 1;
				FillRect(sample_space, y, w, sample_height);
				}
			}
		else {
			DrawTextSetColors(white, black);
			y = sample_space;
			gc.TextStyle.JustifyRight = 1;
			DrawTextSetColors(black, white);
			for i = 1 to num_ranges {
				y = (num_ranges - i) * (sample_height + sample_space) + sample_space;
				SetColor(ColorMapGetColor(cmap, i));
				FillRect(sample_space, y, sample_width, sample_height);
				SetColorName("white");
				DrawRect(sample_space, y, sample_width, sample_height);
				str$ = sprintf("%.2f", histo[i] * cellsize * unitconv);
				DrawTextSimple(str$, sample_text_x, y + sample_height - 4);
				}
			y = (num_ranges) * (sample_height + sample_space) + sample_space;
			SetColorName("white");
			MoveTo(2, y);
			DrawTo(da.width - 2, y);
			str$ = sprintf("%.2f", total * cellsize * unitconv);
			DrawTextSetColors(white, black);
			y += (sample_height - 3);
			DrawTextSimple(str$, sample_text_x, y);
			DrawTextSimple("TOTAL", sample_text_x, y + 11);
			DrawTextSimple("ACRES", sample_text_x, y + 22);
			}
		}
	}
################################################################
#
#	Callback for when user clicks the right mouse button
#	on the polygon tool (or hits apply)
#
proc cbToolApply(class GUI_GADGET_POLYLINE tool) {
	local numeric i, val, scale, minval, maxval, ir, red, total, offset, lo, hi;
	local class REGION2D reg, reg2;
	class POLYLINE poly = tool.GetPolygon();
	poly.SetClosed(1);
	reg.UnionPolyLine(poly);
	if (firstpass) { 
		MyRgn = CopyRegion(reg);
		firstpass = 0;
		}
	else {
		if (minusregion) {
			reg2 = RegionSubtract(MyRgn, reg);
			MyRgn = CopyRegion(reg2);
			reg = CopyRegion(reg2);
			}
		else {
			# reg = RegionOR(MyRgn, tool.Region);
			reg2 = RegionOR(MyRgn, reg);
			MyRgn = CopyRegion(reg2);
			reg = CopyRegion(reg2);
			}
		}
	PopupMessage(NumToStr(reg.Extents.x1) + " " + NumToStr(reg.Extents.x2));
	reg = RegionTrans(reg,ViewGetTransLayerToScreen(view, layer, 1));
	StatusSetDefaultHandle(view.StatusHandle);
	ViewSetMessage(view, "Filling raster with 0");
	BiomassRaster = 0;
	if (1) {
		ViewSetMessage(view, "Computing scale");
		for i=0 to 200 histo[i] = 0;
      for each Composite in reg {
			ir = Composite.red;
			red = Composite.green;
			val = ((ir - red) / (ir + red) + 1) * 100;
			histo[val] = histo[val] + 1;
			}
		total = 0;
		for i=0 to 200 total += histo[i];
		lo = total * .02;
		hi = total * .98;
		minval = -100;
		maxval = -100;
		total = 0;
		for i = 0 to 200 {
			if (total > lo && minval < -1) {
				minval = (i / 100) - 1;
				}
			total += histo[i];
			if (total > hi && maxval < -1) {
				maxval = (i / 100) - 1;
				}
			}
      scale = num_ranges / (maxval - minval);
      offset = -minval;
		}
	else {
		scale = num_ranges / 2.0;
		offset =  1.0;
		}
	PopupMessage(NumToStr(reg.Extents.x1) + " " + NumToStr(reg.Extents.x2));
	ViewSetMessage(view, "Processing region");
	for i = 1 to num_ranges histo[i] = 0;
	for each Composite in reg {
		ir = Composite.red;
		red = Composite.green;
		val = (ir - red) / (ir + red);
		val = Bound((val + offset) * scale + 1, 1, num_ranges);
		histo[val] = histo[val] + 1;
		BiomassRaster = val;
		}
	CloseRaster(BiomassRaster);
	LayerShow(overlay, view);
	ViewRedrawLayer(view, overlay);
	ViewRedraw(view);
	drawGraph(da);
	# Do something with the region here
	tool.HasPosition = 0;	# Clears the current position
	buttonConvertToVector.Disabled = 0;
	buttonFiltRaster.Disabled = 0;
	}
################################################################
#
#	Callback for the "Filter Biomass" button
#
proc cbFiltRast () {
	local class REGION2D reg;
	reg = RegionTrans(MyRgn, ViewGetTransLayerToView(view, layer, 1));
	FilteredBiomassRaster = 0;
	ViewSetMessage(view, "Median filter");
	StatusSetDefaultHandle(view.StatusHandle);
	foreach BiomassRaster in reg {
		FilteredBiomassRaster = FocalMajority(BiomassRaster, 5, 5);
		}
	foreach BiomassRaster in reg {
		#BiomassRaster = FocalMajority(FilteredBiomassRaster, 3, 3);
		BiomassRaster = (FilteredBiomassRaster);
		}
	CloseRaster(BiomassRaster);
	ViewRedraw(view);
	drawGraph(da);
	}
################################################################
#
#	Callback for the "Convert To Vector" button
#
proc cbConvToVector () {
	local numeric i, numPolys, numToDelete, minsize, inode;
	minsize = IniReadNumber("biomass", "minsize", 0);
	minsize = PopupNum("Enter minimum polygon size in acres:", minsize, 0, 640, 1);
	IniWriteNumber("biomass", "minsize", minsize);
	minsize /= unitconv;	# Convert to sq meters
	if (VectorExists(outputfile$, "Vector")) {
		inode = ObjectNumber(outputfile$, "Vector", "VECTOR");
		DeleteObject(outputfile$, inode);
		}
	CreateVector(BiomassVector, outputfile$, "Vector", "Vector boundaries");
	FilteredBiomassRaster = 0;
	ViewSetMessage(view, "Median filter");
	foreach BiomassRaster in MyRgn {
		FilteredBiomassRaster = FocalMedian(BiomassRaster, 5, 5);
		}
	StatusSetDefaultHandle(view.StatusHandle);
	ViewSetMessage(view, "Converting Raster to Vector");
	BiomassVector = RasterToVectorBound(BiomassRaster);
	ViewSetMessage(view, "Generating std stats table");
	VectorToolkitInit(BiomassVector);
	VectorUpdateStdAttributes(BiomassVector);		# Compute std stats table
	numToDelete = 0;
	numPolys = NumVectorPolys(BiomassVector);
	array numeric deleteList[numPolys];
	ViewSetMessage(view, "Looking for polygons to delete");
	for i=1 to numPolys {
		if (BiomassVector.poly[i].POLYSTATS.Area < minsize) {
			numToDelete += 1;
			deleteList[numToDelete] = i;
			}
		}
	if (numToDelete) {
		ViewSetMessage(view, "Deleting tiny polygons");
		VectorDeletePolys(BiomassVector, deleteList, numToDelete);
		}
	ViewSetMessage(view, "Closing vector");
	CloseVector(BiomassVector);
	if (!voverlay) {
		voverlay = GroupQuickAddVectorVar(group, BiomassVector);
		voverlay.Poly.NormalStyle.BorderColor.name = "black";
		voverlay.Line.NormalStyle.Color.name = "black";
		voverlay.Line.NormalStyle.width = 1;
		voverlay.Line.NormalStyle.MapScale = 2000;
		voverlay.Line.NormalStyle.ScaleToMap = 1;
		}
	LayerShow(voverlay, view);
	ViewRedraw(view);
	drawGraph(da);
	}
proc cbHideOverlay(class widget widget) {
	local numeric i;
	tool.HasPosition = 0;	# Clears the current position
	buttonConvertToVector.Disabled = 1;
	firstpass = 1;
	buttonFiltRaster.Disabled = 1;
	LayerHide(overlay, view);
	if (voverlay) {
		LayerHide(voverlay, view);
		}
	for i = 0 to 10 histo[i] = 0;
	ViewRedraw(view);
	drawGraph(da);
	}
################################################################
#
#	A generic callback to redraw the graph.  Will be called
#	after every redraw because the colormap may have changed
#	out from under us.
#
proc cbRedrawViewBegin(class GRE_VIEW view) {
	tip.Disabled = 1;
	tip2.Disabled = 1;
	tip3.Disabled = 1;
	}
proc cbRedrawViewEnd(class GRE_VIEW view) {
	# Force all the tips timers to restart
	tip.Disabled = 1;
	tip2.Disabled = 1;
	tip3.Disabled = 1;
	tip.Disabled = 0;
	tip2.Disabled = 0;
	tip3.Disabled = 0;
	drawGraph(da);
	}
proc cbRedrawGraph(class widget button) {
	drawGraph(da);
	}
proc cbMinus(class GUI_CTRL_TOGGLEBUTTON this) {
	if (minusbutton.GetValue()) plusbutton.SetValue(0, false);
	else plusbutton.SetValue(1, false);
	minusregion = minusbutton.GetValue();
	}
proc cbPlus(class GUI_CTRL_TOGGLEBUTTON this) {
	if (plusbutton.GetValue()) minusbutton.SetValue(0, false);
	else minusbutton.SetValue(1, false);
	minusregion = minusbutton.GetValue();
	}
#####################################################
#
#	MAIN PROGRAM
#
# clear()
# Get an input raster.  We will use it's size to determine 
# 	 how big to make our window
OpenRaster(Composite, datafile$, "CIR_Raster");
#GetInputRaster(Composite);
#-----------------------------------------------------
#
#	Create the Biomass Raster that we'll use for the overlay
#
#CreateTempRaster(BiomassRaster, NumLins(Composite), NumCols(Composite), "8-bit unsigned", 1);
string lokfile$ = FileNameGetPath(outputfile$) +"/"+ FileNameGetName(outputfile$) + ".lok";
#DeleteFile(lokfile$);
DeleteFile(outputfile$);
CreateRaster(BiomassRaster, outputfile$, "biomass", "biomass raster", NumLins(Composite), NumCols(Composite), "8-bit unsigned");
CopySubobjects(Composite, BiomassRaster, "georef");
cellsize = LinScale(Composite) * ColScale(Composite); 
#ini = IniOpenObject(datafile$, "Prefs");
IniWriteString("BiomassRaster", "Filename", BiomassRaster.$Info.Filename);
IniWriteString("BiomassRaster", "ObjectName", BiomassRaster.$Info.Name);
#IniClose(ini);
c0 = ColorMapGetColor(cmap, 0);
c0.Name = "black";
c0.transp = 75;		# Make it 75% transparent
ColorMapSetColor(cmap, 0, c0);
ColorMapSetColorHIS(cmap, 10, 230, 50, 100);	# Green
ColorMapSetColorHIS(cmap, 9, 180, 70, 100);		# Yellow
ColorMapSetColorHIS(cmap, 8, 150, 70, 100);		# Lt Orange
ColorMapSetColorHIS(cmap, 7, 144, 50, 100);		# Dk Orange
ColorMapSetColorHIS(cmap, 6, 120, 50, 100);		# Red
ColorMapSetColorHIS(cmap, 5, 72, 50, 100);		# Magenta
ColorMapSetColorHIS(cmap, 4, 48, 50, 100);		# Purple
ColorMapSetColorHIS(cmap, 3, 10, 50, 100);		# Blue
ColorMapSetColorHIS(cmap, 2, 342, 60, 100);		# Lt Blue
ColorMapSetColorHIS(cmap, 1, 320, 70, 100);		# Cyan
#for i = 0 to 10 {
#	c0 = ColorMapGetColor(cmap, i);
#	c0.transp = 75;		# Make it 75% transparent
#	ColorMapSetColor(cmap, i, c0);
#	}
ColorMapWriteToRastVar(BiomassRaster, cmap, "ColorMap");
SetNull(BiomassRaster, 0);
CreateHistogram(BiomassRaster);
CreateTempRaster(FilteredBiomassRaster, NumLins(Composite), NumCols(Composite), "8-bit unsigned");
CopySubobjects(Composite, FilteredBiomassRaster, "georef");
#-----------------------------------------------------
#
#	Create a temp vector for the biomass->vector conversion
#
#CreateTempVector(BiomassVector);
#CreateVector(BiomassVector, outputfile$, "Vector", "Vector boundaries");
# We don't create it until we actually need it now 
#-----------------------------------------------------
#  Create a dialog (aka "window").  
#
#	This function returns a form that can be used as a
#	parent for other widgets
#
#	If we had another dialog which we wanted to use as
#	a "parent" to this one, we would pass it as the
#	2nd parameter to CreateModalFormDialog().
form = CreateFormDialog("Biomass Mapping");
WidgetAddCallback(form.Shell.PopdownCallback, cbPopdown);
#-----------------------------------------------------
#
#	Create a push button on the form.  Set its attachments
#	so that sets in the lower left of the form.  To attach
#	an edge to the form rather than another widget, just
#	set it to the form.  In Motif, this would be done by
#	setting the attachment to XmATTACH_FORM, which I plan
#	to implement here too.  Setting the RightPosition to 50
#	ties the right edge of the button to a spot 50% of the
#	width of the form, (assuming form.FractionBase still
#	has the default value of 100) no matter how wide the 
#	form gets
#
buttonClearOverlay = CreatePushButtonItem("Clear Overlay", cbHideOverlay);
buttonFiltRaster = CreatePushButtonItem("Filter Biomass", cbFiltRast);
buttonConvertToVector = CreatePushButtonItem("Convert To Vector", cbConvToVector);
#buttonFiltVector = CreatePushButtonItem("Filter Vector", cbFilterVector);
buttonExit = CreatePushButtonItem("Exit", cbQuit);
buttonConvertToVector.Disabled = 1;
buttonFiltRaster.Disabled = 1;
#buttonFiltVector.Disabled = 1;
button_row = CreateButtonRow(form, buttonClearOverlay, 
											  buttonFiltRaster, 
											  buttonConvertToVector, 
											  buttonExit
											  );
button_row.BottomWidget = form;
drawhisto = CreateIconToggleButton(form, "RVCobjects", "histo", "Show as histogram");
drawhisto.LeftWidget = form;
drawhisto.TopWidget = form;
WidgetAddCallback(drawhisto.ValueChangedCallback, cbRedrawGraph);
frame = CreateFrame(form);
frame.TopWidget = drawhisto;
frame.BottomWidget = button_row;
frame.LeftWidget = form;
da = CreateDrawingArea(frame, 180, sample_width + 2 * sample_space + 2);
WidgetAddCallback(da.ExposeCallback, cbRedrawGraph);
#-----------------------------------------------------
#
#	Create a 2D group to display the raster in and quick-add it.
#
group = DispCreate2DGroup();
#-----------------------------------------------------
#
#	Create a view for this group in our form.  Default its size to
#	the size of the raster.  For a real program, you'd want to
#	limit this to something reasonable.  
#
height = NumLins(Composite);
width = NumCols(Composite);
while (height > 700 or width > 1000) {
	height /= 2;
	width /= 2;
	}
view = GroupCreateView(group, form, "view", height, width);
view.DisableRedraw = 1; #Prevent open from doing a redraw because
DispAddCallback(view.DrawBeginCallback, cbRedrawViewBegin);
DispAddCallback(view.DrawEndCallback, cbRedrawViewEnd);
DispAddCallback(view.RestoreCallback, cbRedrawViewEnd);
#-----------------------------------------------------
#
#	Make widget attachments for the view.  Connect it to the form
#	on all edges except the bottom.  There, attach it to one of the
#	buttons.  
#	Some notes about making attachment:
#		1) If you attach widget A to Widget B, don't attach B to A
#		   or anything else which would cause a circular reference.
#		2) The widget created and attached last is what will resize
#		   with the window.  If I had created the view first and then
#		   the buttons, resizing the window would make the buttons
#		   bigger.
form.TopWidget = form;
form.LeftWidget = frame;
form.RightWidget = form;
form.BottomWidget = button_row;
view.ShowDataTips = 0;
#-----------------------------------------------------
#
#	Create a region tool on the view
#
tool = ViewCreatePolyLineTool(view, "My Tool", "draw_polygon", "polyline_edit");
ToolAddCallback(tool.ActivateCallback, cbToolApply);
tool.DialogPosition = "RightCenter";
#-----------------------------------------------------
#
#	Add the standard graphic tools to the view
#
ViewAddStandardTools(view);
#-----------------------------------------------------
#
#	Have to call this function after adding any tools to 
#	create icon buttons for the tools.
#
ViewAddToolIcons(view);
#-----------------------------------------------------
#
# add region mode (+,-) region icons
#
#plusicon = CreateToggleButtonItem("Add to region", cbPlusRegion);
#minusicon = CreateToggleButtonItem("Subtract from region", cbMinusRegion);
# set the icons to initial state - plus region
#plusicon.IconClass = "standard";
#plusicon.IconName = "add_select";
#plusicon.Selected = true;
#plusicon.OneOfMany = true;
#minusicon.IconClass = "standard";
#minusicon.IconName = "minus_select";
#minusicon.OneOfMany = true;
plusbutton.CreateIcon(view.ToolBarPane, "CONTROL_ADD_CYAN", "Add to region");
plusbutton.SetOnPressed(cbPlus);
plusbutton.SetValue(1, false);
minusbutton.CreateIcon(view.ToolBarPane, "CONTROL_SUBTRACT_CYAN", "Subtract from region");
minusbutton.SetOnPressed(cbMinus);
#-----------------------------------------------------
#
#	Create the layers
#
layer = GroupQuickAddRasterVar(group, Composite);
layer.NullCellsTransparent = 1;
overlay = GroupQuickAddRasterVar(group, BiomassRaster);
overlay.NullCellsTransparent = 0;
overlay.UsesTransparency = 1;
overlay.DoBlendMask = 1;
LayerHide(overlay, view);
#-----------------------------------------------------
#
#	Open the dialog Need to do this before creating the GC
#	because until then the drawing area has no window.
#	Would like a way around this.  Could my CreateGC
#	realize it first if necessary?
#
DialogOpen(form);			# we're just going to do a resize right away
DialogFullScreen(form);
view.DisableRedraw = 0;	# and that will make us redraw again.
ViewRedrawIfNeeded(view);
#ViewActivateTool(view, tool);
tool.Managed = 1;
#
#	Create a GC (Graphics Context) for the drawing area
#	You can have multiple gc's per drawing area.  Drawing
#	functions will always use the active GC.  
#
gc = CreateGCForDrawingArea(da);
gc.TextStyle.JustifyRight = 1;
# Clear the drawing area
ActivateGC(gc);
SetColorRGB(0,0,0);
black.name = "black";
white.name = "white";
FillRect(0, 0, da.width, da.height);
DrawTextSetFont("stork.of");
DrawTextSetColors(black, white);
DrawTextSetHeightPixels(9);
# Add the layers after we've had a chance to notice the resize
# Don't do the following. Need the tool window to show.
#DialogToTop(form);	# Pull our window to the top
#-----------------------------------------------------
#
#	Add the data tips.
#
ViewSetMessage(view, 'Draw around a field and press "Apply"');
tip = CreateToolTip(view.DrawingArea, 
'(1) Press the left mouse button to draw around the 
field whose canopy biomass is of interest.
Click on "apply" to close your field boundary'
);
tip.Disabled = 1;
tip.Delay = 5000;		# pop up after 3000 ms
tip.priority = -3;	# Less than DataTips
tip2 = CreateToolTip(view.DrawingArea, 
'(2) Press the left mouse button to draw around your field.
If you want to add another field or add area to a field 
just draw it now.  If you want to omit any area within a 
field use left mouse button to select the "-"  (minus) 
icon in the upper right of the menu toolbar.  You will 
then be in the subtract area mode.  Next simply draw 
around the area to be deleted.'
);
tip2.Disabled = 1;
tip2.Delay = tip.Delay + 8000;		# pop up after another 6000 ms
tip2.priority = -2;	# Less than DataTips
tip3 = CreateToolTip(view.DrawingArea, 
'(3) To redraw, zoom, pan... use the button icons on the left 
of the menu toolbar.  For more help use the "Instruction" 
icon on the product toolbar.'
);
tip3.Disabled = 1;
tip3.Delay = tip2.Delay + 18000;		# pop up after another 10 sec
tip3.priority = -1;	# Less than DataTips
tip.Disabled = 0;
tip2.Disabled = 0;
tip3.Disabled = 0;
#-----------------------------------------------------
#
#	New and improved method!
#	Rather than having the script wait for the dialog
#	to close, setup an OnExit callback.  If the script
#	is exited for any reason (including clicking the
#	exit icon on the tool bar), this function will be
#	called before we're terminated.
#	Then just call WaitForExit().  This will put the
#	script into a suspended state such that it's only
#	waiting for callbacks (including the OnExit one)
#	The only way out of this state is to call Exit()
#
OnExit(cbQuit);	#Call this when the script exits
WaitForExit();