############################################################################################################# # RASTPROF.SML # Created by: Mark Smith # Most recent revision: 9-2001 # # This toolscript allows the user to draw a line using a line tool. It accumulates # the raster data from the active raster along the line and then creates a graph displaying # the profile of the raster along the line. # # The active layer may not be a composite raster. # # This script requires TNTmips version 6.6. # # The following symbols are predefined # class VIEW View {use to access the view the tool script is attached to} # class GROUP Group {use to access the group being viewed if the script is run from a group view} # Variable declarations class XmForm form; class LineTool tool; class GRE_LAYER rasterLayer; class Raster targetRaster; class XmDrawingArea da; class GC gc; class GRE_GROUP activegroup; string rasterName$; numeric doGraph, hasNull; array numeric value[1000000]; array numeric draw[1000000]; array numeric graphx[3], graphy[3]; numeric min, max, count, nullVal; class POINT2D startpoint; class POINT2D endpoint; numeric setDefaultWhenClose = false; # Checks layer to see if it is valid. func checkLayer() { local numeric valid = false; # Get name of active layer if it is usable. If not output an error message. if (activegroup.ActiveLayer.Type == "") { rasterName$ = "Group has no layers!"; } else { if (activegroup.ActiveLayer.Type == "Raster") { rasterLayer = activegroup.ActiveLayer; DispGetRasterFromLayer(targetRaster, rasterLayer); # Check for null values if (HasNull(targetRaster)) { hasNull = 1; nullVal = NullValue(targetRaster); } else hasNull = 0; if (targetRaster.$Info.Type != "binary" and targetRaster.$Info.Type != "4-bit unsigned" and targetRaster.$Info.Type != "8-bit signed" and targetRaster.$Info.Type != "8-bit unsigned" and targetRaster.$Info.Type != "16-bit signed" and targetRaster.$Info.Type != "16-bit unsigned" and targetRaster.$Info.Type != "32-bit signed" and targetRaster.$Info.Type != "32-bit unsigned" and targetRaster.$Info.Type != "32-bit floating-point" and targetRaster.$Info.Type != "64-bit signed" and targetRaster.$Info.Type != "64-bit unsigned" and targetRaster.$Info.Type != "64-bit floating-point") { rasterName$ = "Type not supported!"; } else { rasterName$ = rasterLayer.Name; valid = true; } } else rasterName$ = "Not a raster!"; } return valid; } # Callback for drawing area expose. Draws text and graph. proc cbRedraw() { if (gc == 0) return; ActivateGC(gc); # Clear the drawing area and redraw text. SetColorName("gray75"); FillRect(0, 0, da.width, da.height); SetColorName("black"); DrawInterfaceText(sprintf("Raster: %s", rasterName$), 0, 10); if (doGraph == 0) return; local string min$, max$; local numeric size; local class POINT2D graph1, graph2, graph3; # Draw graph axes min$ = sprintf("%d", min); max$ = sprintf("%d", max); size = strlen(max$); graphx[1] = size*6.5+5; graphy[1] = 20; graphx[2] = graphx[1]; graphy[2] = da.height - 15; graphx[3] = da.width - 5; graphy[3] = graphy[2]; DrawPolyLine(graphx, graphy, 3); DrawTextSetFont("stork"); DrawTextSetHeight(10); DrawTextSimple(max$, 0, graphy[1]+10); DrawTextSimple(min$, (size - strlen(min$))*6.5, graphy[2]); DrawTextSimple(sprintf("%d, %d", startpoint.x, startpoint.y), graphx[1], da.height-3); local string str$ = sprintf("%d, %d", endpoint.x, endpoint.y); DrawTextSimple(str$, graphx[3] - strlen(str$)*6.5, da.height-3); SetColorName("magenta"); local numeric xscale, yscale, prevnull; xscale = (graphx[3] - graphx[2] + 1) / (count - 1); yscale = (graphy[2] - graphy[1]) / (max - min); # Draw profile if (value[1] < 0 || value[1] >= 0) { if (max - min == 0) { DrawPoint(graphx[2], graphy[1] + (graphy[2]-graphy[1])*.5); } else DrawPoint(graphx[2], graphy[2]-(value[1]-min)*yscale); } local numeric i; for i = 2 to count { if (value[i] < 0 || value[i] >= 0) { if (!(value[i-1] < 0 || value[i-1] >= 0)) { if (max - min == 0) { DrawPoint(graphx[2]+(i-1)*xscale, graphy[1] + (graphy[2]-graphy[1])*.5); } else DrawPoint(graphx[2]+(i-1)*xscale, graphy[2]-(value[i]-min)*yscale); } else { if (max - min == 0) { DrawTo(graphx[2]+(i-1)*xscale, graphy[1] + (graphy[2]-graphy[1])*.5); } else DrawTo(graphx[2]+(i-1)*xscale, graphy[2]-(value[i]-min)*yscale); } } } } # Callback for when the active layer changes. proc cbLayer() { checkLayer(); cbRedraw(); } # Callback for when the active group changes. proc cbGroup() { activegroup = Layout.ActiveGroup; WidgetAddCallback(activegroup.LayerSelectedCallback, cbLayer); cbLayer(); } # Callback for when user clicks the right mouse button on the polygon tool # (or clicks apply). proc cbToolApply() { # If the selected layer is not valid, don't do anything. if (checkLayer()) { startpoint = tool.start; endpoint = tool.end; startpoint = TransPoint2D(startpoint, ViewGetTransLayerToScreen(View, rasterLayer, 1)); endpoint = TransPoint2D(endpoint, ViewGetTransLayerToScreen(View, rasterLayer, 1)); # Get x and y step values local numeric ystep, xstep; local class POINT2D cursor; cursor = startpoint; count = 0; ystep = endpoint.y - startpoint.y; xstep = endpoint.x - startpoint.x; if (xstep == 0 && ystep == 0) return; if (abs(xstep) > abs(ystep)) { ystep = ystep / abs(xstep); xstep = xstep / abs(xstep); if (ystep == 0) { count = abs(endpoint.x - startpoint.x); } else count = abs((endpoint.y - startpoint.y) / ystep); } else { xstep = xstep / abs(ystep); ystep = ystep / abs(ystep); if (xstep == 0) { count = abs(endpoint.y - startpoint.y); } else count = abs((endpoint.x - startpoint.x) / xstep); } count = count + 1; doGraph = 1; max = -1000000; min = 1000000; # Loop on line to generate point list. local numeric i; for i = 1 to count { value[i] = targetRaster[round(cursor.y), round(cursor.x)]; # If the raster value was retrieved from a null cell or a coordinate # outside the raster extents, value[i] will be 'not a number'. # Therefore testing if it is < 0 or >= 0 determines whether it is # a valid value if (value[i] < 0 || value[i] >= 0) { if (value[i] > max) max = value[i]; if (value[i] < min) min = value[i]; } cursor.x = cursor.x + xstep; cursor.y = cursor.y + ystep; } if (max == -1000000 && min == 1000000) { max = 0; min = 0; } cbRedraw(); } } # Called when the close button is pressed. Closes the dialogs. proc cbClose() { tool.Managed = 0; DialogClose(form); if (setDefaultWhenClose) { setDefaultWhenClose = false; View.SetDefaultTool(); } } # Called the first time the tool is activated. # If the tool implements a dialog it should be created (but not displayed) here. func OnInitialize () { if (Layout) { WidgetAddCallback(Layout.GroupSelectedCallback, cbGroup); activegroup = Layout.ActiveGroup; } else activegroup = Group; WidgetAddCallback(activegroup.LayerSelectedCallback, cbLayer); # Set up tool dialog. form = CreateFormDialog("Raster profile"); form.marginHeight = 2; form.marginWidth = 2; WidgetAddCallback(form.Shell.PopdownCallback, cbClose); # Drawing area for displaying text. da = CreateDrawingArea(form, 173, 301); da.topWidget = form; da.bottomWidget = form; da.leftWidget = form; da.rightWidget = form; WidgetAddCallback(da.ExposeCallback, cbRedraw); # Add sketch tool. tool = ViewCreateLineTool(View); ToolAddCallback(tool.ApplyCallback, cbToolApply); doGraph = 0; } # end of OnInitialize # Called when tool is to be destroyed, will not be called if tool was never activated. func OnDestroy () { tool.Managed = 0; DestroyGC(gc); DestroyWidget(form); } # end of OnDestroy # Called when tool is activated. func OnActivate () { checkLayer(); DialogOpen(form); tool.Managed = 1; tool.HasPosition = 0; if (gc == 0) gc = CreateGCForDrawingArea(da); cbRedraw(); setDefaultWhenClose = true; } # end of OnActivate # Called when tool is deactivated (usually when switching to another tool). func OnDeactivate () { setDefaultWhenClose = false; cbClose(); } # end of OnDeactivate