coastal.sml
Applidat: .SML file includes both script
and data
See other
examples of APPLIDATs ...
########################################################
#
# Transect and Rates SML script
#
#
# Written 8 Mar 1998
# David Williss
# MicroImages, Inc
#
# Revised 16 November 2010
# Requires TNT version 2009:75
# Now declares all variables including function/procedure arguments
#
# Based on a pair of DSAS programs.
#
########################################################
#
# Limits --
# A few limits which are hard-coded.
# Increase as necessary
#
numeric maxTrans = 10000; # Maximum number of transects
numeric maxShores = 20; # Maximum number of shoreline vectors
########################################################
#
# Defaults --
# These are initial default values.
# They will be read out of the tntproc.ini file. The
# last parameter to IniReadNumber is the default value
# to use if not found in the ini file
#
numeric transInterval, maxdist;
string outfilebase$;
transInterval = IniReadNumber("transect", "interval", 100);
maxdist = IniReadNumber("transect", "maxdist", 250);
outfilebase$ = FileNameGetPath(_context.Filename);
outfilebase$ = IniReadString("transect", "outfilebase", outfilebase$);
########################################################
#
# Global Variables
#
array numeric transx[maxTrans];
array numeric transy[maxTrans];
array numeric transa[maxTrans];
numeric numtrans, numshores;
array numeric shorex[maxShores], shorey[maxShores];
array numeric vectdate[maxShores], juldate[maxShores], years[maxShores];
array numeric distToBase[maxShores], distFromShore1[maxShores], years[maxShores];
array numeric basex[10], basey[10]; # Automatically resized anyway
class FILE locfile, julfile, distfile, ratefile;
class GRE_GROUP group;
class GRE_LAYER_VECTOR voverlay, layer;
class GRE_LAYER translayer;
class GRE_VIEW view;
class GRE_LAYER_RASTER bglayer;
class XmForm form;
class PUSHBUTTONITEM okItem, cancelItem;
class XmForm button_row;
class XmSeparator sep;
class PromptNum pinterval, pmaxdist;
class STATUSCONTEXT status;
class DATABASE database;
class DBTABLEINFO ratetable;
class DBFIELDINFO transfield, field;
class GRE_LAYERMANAGER controls;
#class XmPushButton transButton;
class GUI_CTRL_PUSHBUTTON transButton;
vector Transects;
raster BackgroundRaster;
vector BaseLineVector;
string prompt$, fname$, name$, lastdate$;
numeric inode, year, month, date;
numeric line, numbasepts, flipbaseline;
numeric x1, y1, x2, y2;
numeric hits, misses, numint, sx, sy, rates;
########################################################
proc DoTransSeg(numeric x0, numeric y0, numeric x1, numeric y1)
{
local numeric theta, theta90, r, dx, dy, x, y, dist;
dx = x1 - x0;
dy = y1 - y0;
theta = atan2(dy, dx); # Angle of this segment
theta90 = theta + pi/2; # angle perpendicular to segment
dist = sqrt(dx*dx + dy*dy);
for r = 0 to dist step transInterval {
numtrans += 1;
transx[numtrans] = x0 + r*cos(theta);
transy[numtrans] = y0 + r*sin(theta);
transa[numtrans] = theta90;
}
if (r != dist) {
numtrans += 1;
transx[numtrans] = x1;
transy[numtrans] = y1;
transa[numtrans] = theta90;
}
return;
}
########################################################
#
# Do the corner between two segments.
# Segment A is from (x0,y0) to (x1,y1)
# Segment B is from (x1,y1) to (x2,y2)
#
# The transect from the end of segment A is already in the
# list. The transect from the beginning of B will be added
# by DoTransSeg
# Compute a transect at point (x1,y1) which bisects the
# angle betweent the two transects.
#
proc DoCorner(numeric x0, numeric y0, numeric x1, numeric y1, numeric x2, numeric y2) {
local numeric dxA, dxB, dyA, dyB, thetaA, thetaB, theta90A, theta90B, theta90AB;
dxA = x1 - x0;
dyA = y1 - y0;
dxB = x2 - x1;
dyB = y2 - y1;
thetaA = atan2(dyA, dxA); # Angle of segment A
thetaB = atan2(dyB, dxB); # Angle of segment B
theta90A = thetaA + pi/2;
theta90B = thetaB + pi/2;
# Angles greater than 2*pi screw up the bisection
if (theta90A > 2 * pi) {
theta90A -= 2 * pi;
}
if (theta90B > 2 * pi) {
theta90B -= 2 * pi;
}
if (abs(theta90A - theta90B) > pi) {
theta90A -= 2 * pi; # Doesn't really matter which one we subtract from
}
theta90AB = (theta90A + theta90B) / 2;
numtrans += 1;
transx[numtrans] = x1;
transy[numtrans] = y1;
transa[numtrans] = theta90AB;
return;
}
########################################################
#
# Compute the endpoint rate dr/dt.
# The dates are assumed to be julian dates
# return value is in meters/year.
#
func EndPointRate(numeric dist0, numeric dist1, numeric date0, numeric date1) {
return ((dist1 - dist0) / ((date1 - date0) / 365.25));
}
# Called when user closes layer controls.
# Close the view window. This will cause
# the popdown callback to be called
proc cbQuit(class widget widget) {
DialogClose(form);
}
# Called when the dialog actually pops down. Free
# and destroy everything.
proc cbPopdown(class widget widget) {
ViewDestroy(view);
GroupDestroy(group);
DestroyWidget(form);
Exit();
}
#######################################################
# MAIN
#######################################################
#
# Lets assume that the baseline vector contains lines whos
# verticies are the segment points.
#
if (_context.ScriptIsRVCObject) {
# Script is being run from an RVC file which has all the data it needs
# This is for demo purposes
OpenVector(BaseLineVector, _context.Filename, "BaseLine");
numshores = OpenInputVectorList(ShoreVectorList,
_context.Filename, "_1844",
_context.Filename, "_1942",
_context.Filename, "_1976"
);
}
else {
GetInputVector(BaseLineVector);
numshores = GetInputVectorList(ShoreVectorList, "Select shoreline vectors in chronological order");
}
class XmForm pform;
numeric cancel;
proc cbPromptsOK() {
DialogClose(pform);
cancel = 0;
}
proc cbPromptsCancel() {
DialogClose(pform);
cancel = 1;
}
proc PromptForValues() {
pform = CreateModalFormDialog("Parameters");
okItem = CreatePushButtonItem("OK", cbPromptsOK);
cancelItem = CreatePushButtonItem("Cancel", cbPromptsCancel);
button_row = CreateButtonRow(pform, okItem, cancelItem);
button_row.BottomWidget = pform;
sep = CreateHorizontalSeparator(pform);
sep.LeftWidget = pform;
sep.RightWidget = pform;
sep.BottomWidget = button_row;
pinterval = CreatePromptNum(pform, "Interval:", 5, 0, transInterval, 0);
pinterval.BottomWidget = sep;
pmaxdist = CreatePromptNum(pform, "Max Dist:", 5, 0, maxdist, 0);
pmaxdist.BottomWidget = pinterval;
AlignWidgets(pinterval.label, pmaxdist.label);
DialogOpen(pform);
DialogWaitForClose(pform);
if (!cancel) {
transInterval = pinterval.value;
maxdist = pmaxdist.value;
# Write them back out to the ini file for next time
IniWriteNumber("transect", "interval", transInterval);
IniWriteNumber("transect", "maxdist", maxdist);
}
DestroyWidget(pform);
}
proc cbComputeTransects() {
local numeric i;
PromptForValues();
if (cancel) {
return;
}
#------------------------------------------------------------
# Prompt for output file location
# I'd like to make this use the standard get file
# dialog but I don't have that callable from within
# SML yet.
#
prompt$ = "Select output .loc file. Other files will\n"
+ "be created in the same directory by changing\n"
+ "the file extention";
GetOutputVector(Transects, "Network,VectorToolkit");
fname$ = Transects.$info.Filename;
#fname$ = GetOutputFileName(outfilebase$ + ".loc", prompt$, ".loc");
outfilebase$ = FileNameGetPath(fname$) + FileNameGetName(fname$);
IniWriteString("transect", "outfilebase", outfilebase$); # remember it for next time
#------------------------------------------------------------
# Prompt operator for the dates of the shoreline vectors.
# Genreate the default by assuming the vector names are of
# the form _YYYY_MM
#
numeric j;
for j = 1 to numshores {
fname$ = GetObjectFileName(ShoreVectorList[j]);
inode = GetObjectNumber(ShoreVectorList[j]);
name$ = GetObjectName(fname$, inode);
year = StrToNum(mid$(name$, 2, 4));
month = StrToNum(mid$(name$, 7, 2));
if (month < 1) month = 1;
date = year * 10000 + month * 100 + 1;
if (! _context.ScriptIsRVCObject) {
# Don't bother prompting if this is only a demo. Just assume Jan 1
prompt$ = "Enter date for vector " + name$ + " as YYYYMMDD";
date = PopupNum(prompt$, date);
}
vectdate[j] = DateToJulian(date);
}
status = StatusContextCreate(view.StatusHandle);
#------------------------------------------------------------
#
# Initialize vector toolkit routines for intersection
# finding on the shoreline vectors
#
for j = 1 to numshores {
status.Message = sprintf("Preparing vector %d for finding intersections\n", j);
VectorToolkitInit(ShoreVectorList[j]);
}
#------------------------------------------------------------
#
# Compute the transects
#
numtrans = 0;
clear();
status.Message = "Computing transects...";
for line = 1 to NumVectorLines(BaseLineVector) {
numbasepts = GetVectorLinePointList(BaseLineVector, basex, basey, line);
if (flipbaseline) {
for i = 1 to numbasepts - 1 {
if (i > 1) {
DoCorner(basex[i+1], basey[i+1], basex[i], basey[i], basex[i-1], basey[i-1]);
}
DoTransSeg(basex[i+1], basey[i+1], basex[i], basey[i]);
}
}
else {
for i = 1 to numbasepts - 1 {
if (i > 1) {
DoCorner(basex[i-1], basey[i-1], basex[i], basey[i], basex[i+1], basey[i+1]);
}
DoTransSeg(basex[i], basey[i], basex[i+1], basey[i+1]);
}
}
}
#------------------------------------------------------------
#
# Create a vector object with the trancects so we can overlay them
# Would like to make this use network topology. That way, transects
# which cross each other won't be broken up into multiple lines. This
# insures that transect 1000 is line 1000 and will make it easier to
# attach database records later
#
status.Message = "Creating transect vector...";
#DeleteFile(outfilebase$ + ".rvc");
#CreateVector(Transects, outfilebase$ + ".rvc", "Transects", "Generated by SML", "Network");
for i = 1 to numtrans {
StatusSetBar(status, i, numtrans);
x1 = transx[i];
y1 = transy[i];
x2 = x1 + maxdist * cos(transa[i]);
y2 = y1 + maxdist * sin(transa[i]);
VectorAddTwoPointLine(Transects, x1, y1, x2, y2);
}
StatusSetBar(status);
status.Message = "Updating transect vector topology...";
CopySubobjects(BaseLineVector, Transects, "georef");
VectorValidate(Transects);
CloseVector(Transects);
database = OpenVectorLineDatabase(Transects);
ratetable = TableCreate(database, "Rates", "Erosion rates");
ratetable.ImpliedAttachment = 1;
transfield = TableAddFieldInteger(Transects.line.Rates, "Transect", 5);
field = TableAddFieldFloat(Transects.line.Rates, "EPR", 8, 2);
field.UnitType = "velocity";
field.Units = "m/yr";
field = TableAddFieldFloat(Transects.line.Rates, "Mean", 8, 2);
field.UnitType = "velocity";
field.Units = "m/yr";
TableAddFieldFloat(Transects.line.Rates, "StdDev", 8, 2);
TableAddFieldFloat(Transects.line.Rates, "Variance", 8, 2);
TableAddFieldFloat(Transects.line.Rates, "Regression", 8, 2);
TableAddFieldFloat(Transects.line.Rates, "Jackknife", 8, 2);
if (! _context.ScriptIsRVCObject) {
# Don't create text files for the demo version
# If we don't open the files then fprintf or close,
# SML is kind enough to just ignore us (C programs
# die if we did that)
julfile = fopen(outfilebase$ + ".jul", "w");
distfile = fopen(outfilebase$ + ".dis", "w");
ratefile = fopen(outfilebase$ + ".rat", "w");
locfile = fopen(outfilebase$ + ".loc", "w");
}
status.Message = "Creating .loc file...";
#------------------------------------------------------------
#
# Output the .loc file
#
for i = 1 to numtrans {
StatusSetBar(status, i, numtrans);
fprintf(locfile, "%f,%f,%d\n", transx[i], transy[i], i);
}
#------------------------------------------------------------
# This is the biggie! Find the intersections and compute
# the rates.
#
status.Message = "Finding shoreline-transect intersections";
hits = 0;
misses = 0;
for i = 1 to numtrans {
StatusSetBar(status, i, numtrans);
#------------------------------------------------------------
# Find intersection of vector transx[i], transy[i], transa[i]
# with each line.
#
# Write the intersections to the .jul file.
#
# Point to ask Randy: do you want this as one line per transect like
# the original or one line per intersection as requested for the .dis
# file?
#
numint = 0;
for j = 1 to numshores {
if (VectorLineRayIntersection(ShoreVectorList[j], transx[i], transy[i], transa[i], maxdist, sx, sy)) {
fprintf(julfile, "%4d %10.2f %10.2f %10.2f %10.2f %7d\n", i, transx[i], transy[i], sx, sy, juldate[j]);
numint += 1;
shorex[numint] = sx;
shorey[numint] = sy;
distToBase[numint] = sqrt((transx[i] - sx) ^ 2 + (transy[i] - sy) ^ 2);
juldate[numint] = vectdate[j];
}
}
if (numint) {
hits += 1;
}
else {
misses += 1;
}
#------------------------------------------------------------
# Output the line in the .dist file
#
# For each intersection, also compute the distance from this
# intersection and the first intersection, and also the
# difference in time (in years). These will obviously be
# 0 for the first intersection.
#
string thisdate$, lastdate;
numeric lastdist = 0, delta;
for j = 1 to numint {
thisdate$ = DateToString(JulianToDate(juldate[j]), "%m/%Y");
if (j > 1) {
delta = distToBase[j] - lastdist;
fprintf(distfile, "%d,%s,%s,%f\n", i, lastdate$, thisdate$, delta);
}
distFromShore1[j] = sqrt((shorex[j] - shorex[1])^2 + (shorey[j] - shorey[1])^2);
if (distToBase[1] > distToBase[j]) distFromShore1[j] = -distFromShore1[j];
years[j] = (juldate[j] - juldate[1]) / 365.25;
lastdist = distToBase[j];
lastdate$ = thisdate$;
}
#------------------------------------------------------------
#
# Compute endpoint rate
#
fprintf(ratefile, "%d,", i); # transect number
Transects.line[i].Rates.Transect = i;
if (numint > 1) {
numeric epr = EndPointRate(distToBase[numint], distToBase[1], juldate[numint], juldate[1]);
fprintf(ratefile, "%.2f", epr);
Transects.line[i].Rates.EPR = epr;
}
#------------------------------------------------------------
#
# Compute average of rates. Need at least 3 points to do this
# This uses a feature of SML that's been there since the beginning
# but I don't know if it's documented. The rates variable is set to
# the empty set ( { } ). Then each endpoint rate is computed and
# the is added to the set. When the loops are done, we have a set
# of all the rates, which can be passed to SetMean, SetSD, SetVariance
# or any of the other Set functions.
#
numeric k;
if (numint >= 2) {
rates = {};
for j = 1 to numint - 1 {
for k = j+1 to numint {
epr = EndPointRate(distToBase[j], distToBase[k], juldate[j], juldate[k]);
rates = rates + { epr };
}
}
Transects.line[i].Rates.Mean = SetMean(rates);
fprintf(ratefile, ",%.2f", SetMean(rates));
if (numint > 2) {
fprintf(ratefile, ",%.2f,%.2f", SetSD(rates), SetVariance(rates));
Transects.line[i].Rates.StdDev = SetSD(rates);
Transects.line[i].Rates.Variance = SetVariance(rates);
}
else {
fprintf(ratefile, ",,");
}
}
else {
fprintf(ratefile, ",,,");
}
#------------------------------------------------------------
#
# Standard linear regression. Compute slope where x is years and
# y is distance in meters.
#
numeric slope, intercept;
if (numint > 1) {
LinearRegression(years, distFromShore1, numint, slope, intercept);
fprintf(ratefile, ",%.2f", slope);
Transects.line[i].Rates.Regression = slope;
}
else {
fprintf(ratefile, ",");
}
#------------------------------------------------------------
#
# Compute jackknife regression. The jackknife regression is the
# mean of the regression run numint times, each time omitting
# one of the points. The LinearRegression function has an
# optional parameter that makes this easier. The parameter
# after the intercept tells it which point to ignore.
#
numeric sum;
if (numint > 2) {
sum = 0;
for j = 1 to numint {
LinearRegression(years, distFromShore1, numint, slope, intercept, j);
sum += slope;
}
fprintf(ratefile, ",%.2f", sum/numint);
Transects.line[i].Rates.Jackknife = sum/numint;
}
else {
fprintf(ratefile, ",");
}
fprintf(ratefile, "\n");
}
StatusSetBar(status); # Clear it
status.Message = "Cleaning up...";
for i = 1 to numshores {
CloseVector(ShoreVectorList[i]);
}
fclose(julfile);
fclose(distfile);
fclose(ratefile);
fclose(locfile);
StatusContextDestroy(status);
if (voverlay) {
LayerDestroy(voverlay);
}
voverlay = GroupQuickAddVectorVar(group, Transects);
voverlay.Line.NormalStyle.Color.name = "blue";
voverlay.Line.DataTip.Field = FieldGetInfoByName(Transects.line.Rates, "Mean");
ViewRedraw(view);
}
#############################################################
############################ MAIN ###########################
#############################################################
numeric height = .8 * _context.DisplayInfo.Height;
numeric width = .75 * height;
form = CreateFormDialog("Transect intersections");
WidgetAddCallback(form.Shell.PopdownCallback, cbPopdown);
group = DispCreate2DGroup();
if (_context.ScriptIsRVCObject) {
if (RasterExists(_context.Filename, "KENTIS")) {
OpenRaster(BackgroundRaster, _context.Filename, "KENTIS");
bglayer = GroupQuickAddRasterVar(group, BackgroundRaster);
bglayer.ShowDataTips = 0;
width = height * (BackgroundRaster.$info.NumCols /
BackgroundRaster.$info.NumLins);
}
}
numeric i;
for i = 1 to numshores {
layer = GroupQuickAddVectorVar(group, ShoreVectorList[i]);
}
layer = GroupQuickAddVectorVar(group, BaseLineVector);
layer.Line.NormalStyle.Color.name = "blue";
view = GroupCreateView(group, form, "view", height, width);
view.BackgroundColor.Red = 80;
view.BackgroundColor.Green = 80;
view.BackgroundColor.Blue = 80;
ViewCreateSelectTool(view);
ViewCreateMeasureTool(view);
ViewAddStandardTools(view);
ViewAddToolIcons(view);
# transButton = CreatePushButton(view.ToolBarPane, "Create Transects");
# WidgetAddCallback(transButton.ActivateCallback, cbComputeTransects);
transButton.Create(view.ToolBarPane, "Create Transects");
transButton.SetOnPressed(cbComputeTransects);
DialogOpen(form);
controls = GroupOpenLayerManagerWindow(form, group);
DispAddCallback(controls.CloseCallback, cbQuit);
ViewRedraw(view);
WaitForExit();
©MicroImages, Inc. 2013 Published in the United States of America
11th Floor - Sharp Tower, 206 South 13th Street, Lincoln NE 68508-2010 USA
Business & Sales: (402)477-9554 Support: (402)477-9562 Fax: (402)477-9559
Business info@microimages.com
Support support@microimages.com
Web webmaster@microimages.com
|