# doqq7.sml
#
#
##############################################################################
# global variables that need declaring
##############################################################################
class TEXTSTYLE textstyle, labelstyle;
class GRE_GROUP group, mapgroup, indexgroup;
class GRE_LAYER_RASTER rastlayer;
class GRE_LAYER_VECTOR vectlayer;
class GRE_LAYER_MAPGRID gridlayer, neatlayer;
class GRE_LAYER_TEXT textlayer;
class GRE_LAYER layer;
class RECT extents; # Extents of DOQQ in lat/lon
# for step 0
class OBJECTINFO layoutObject;
raster DOQQ;
vector WetlandsVector;
string layoutFileName$, layoutObjectName$;
string doqqFileName$, doqqObjectName$, doqqObjectDesc$, templateDir$;
numeric layoutObjectNumber, doqqObjectNumber;
# for steps 1-3
class FILE inFile;
string workDir$, name$, prffile$, currentDate$, inFile$, ccs755$;
string newname$, oldname$, objname$;
numeric numLines;
# for step 4
class DATABASE db;
class DBTABLEINFO table;
numeric photodate, dftcont, dftbright, wantNeatLine, ntokens, contrast, brightness;
string photodate$, quadname$, reqfilen$, direct$, a$, imgdate$, maptitle$;
class XmSeparator sep;
class XmForm br;
# for step 5-10
class GRE_LAYOUT layout;
class DBTABLEINFO tinfo;
# for step 11
class Georef georef;
numeric xmin, ymin, xmax, ymax;
numeric halfsize, twosec, cx, cy;
##############################################################################
# initialize global variables
##############################################################################
# This is the map projection we will use everywhere
class MAPPROJ projection;
projection.System = "United States State Plane 1983";
projection.Zone = "Maryland";
# This is the projection used for clipping
class MAPPROJ latlon;
latlon.System = "Latitude / Longitude";
latlon.Datum = "North American 1983";
latlon.Ellipsoid = "GRS 1980";
#=============================================================================
clear();
##############################################################################
# function to count number of lines in a text file
##############################################################################
func CountLines(string fileName$) {
class file f;
numeric c;
string line$;
f = fopen(fileName$, "r");
c = 0;
line$ = fgetline$(f);
while (line$ != "" ) {
c = c + 1;
line$ = fgetline$(f);
}
fclose(f);
return(c);
}
##############################################################################
# step 0:
# Prompt for Input objects
##############################################################################
layoutFileName$ = IniReadString("DOQQSML", "Layout");
layoutObject = SelectInputObject("D2DWIN", "Select the template layout", layoutFileName$);
layoutFileName$ = layoutObject.Filename;
layoutObjectName$ = layoutObject.Name;
layoutObjectNumber = layoutObject.Number;
IniWriteString("DOQQSML", "Layout", layoutFileName$);
GetInputRaster(DOQQ);
GetInputVector(WetlandsVector);
doqqFileName$ = DOQQ.$INFO.Filename;
doqqObjectName$ = DOQQ.$INFO.Name;
doqqObjectDesc$ = DOQQ.$INFO.Desc;
doqqObjectNumber = DOQQ.$INFO.Number;
IniWriteString("DOQQSML", "DOQQRaster", doqqFileName$);
templateDir$ = FileNameGetPath(layoutFileName$);
##############################################################################
# step 1:
# Prompt for the output *.prf file. Use the directory the user puts this
# file into as the "workDir". Prevent it from being the same directory as
# the template for sanity's sake.
##############################################################################
# XXX The work directory name should be derived from the DOQQ raster
# name, but The GetFile dialog won't default to a nonexistant
# directory.
workDir$ = IniReadString("DOQQSML", "WorkDir");
# PRF file has same name as DOQQ file, but change the "1" to a "5"
# example: brand1sw.rvc -> brand5sw.prf
name$ = FileNameGetName(doqqFileName$);
if (NumberTokens(name$, "1") == 2) {
name$ = GetToken(name$, "1", 1) + "5" + GetToken(name$, "1", 2);
}
prffile$ = workDir$ + "/" + name$ + ".prf";
prffile$ = GetOutputFileName(prffile$,
"Select output print file. The layout and other\n"
+ "files will be placed in this directory as well.",
"prf");
workDir$ = FileNameGetPath(prffile$);
# Keep the user from puting the workDir in the same place as the
# tempDir, as this would be bad. ("I'm a little fuzzy on this whole
# good-bad thing.")
while (workDir$ == templateDir$) {
PopupMessage(
"You can't create the new layout in the same directory as\n"
+ "the template layout. Please choose a different directory."
);
prffile$ = GetOutputFileName(prffile$,
"Select output print file. The layout and other\n"
+ "files will be placed in this directory as well.",
"prf");
workDir$ = FileNameGetPath(prffile$);
}
IniWriteString("DOQQSML", "WorkDir", workDir$);
##############################################################################
# step 3:
# edit ccs755.txt - change date on last line to current date
# Note we don't actually write out any file here. The text is just
# held in a string variable and placed into the text layer directly.
##############################################################################
# The format string (2nd parameter) to DateToString can contain the following
# codes which it will replace with the information shown
#
# %a abbreviated weekday name
# %A Full weekday name
# %b abbreviated month name
# %B Full month name
# %d Day of the month ( 01 - 31 )
# %e Day of the month ( 1 - 32; single digits preceded by a space)
# %m month number (01 - 12)
# %y year within century (00-99)
# %Y four digit year
currentDate$ = DateToString(Date(), "%e %B %Y");
inFile$ = templateDir$ + "/ccs755.txt";
numLines = CountLines(inFile$);
ccs755$ = "";
inFile = fopen(inFile$, "r");
numeric i;
for i = 1 to (numLines - 1) {
line$ = fgetline$(inFile);
ccs755$ = ccs755$ + line$ + "\n";
}
line$ = fgetline$(inFile);
line$ = left$(line$, 6) + currentDate$;
ccs755$ = ccs755$ + line$ + "\n";
fclose(inFile);
##############################################################################
# step 4:
# get date of photography from DOQQNAMEs metadata
# change IMG_DATE.TXT to this date
##############################################################################
db = OpenDatabase(templateDir$ + "/doqqmeta.rvc", "Database");
table = DatabaseGetTableInfo(db, "DOQQMETA");
photodate = 0;
quadname$ = "";
name$ = FileNameGetName(doqqFileName$);
for i = 1 to table.NumRecords {
reqfilen$ = TableReadFieldStr(table, "REQFILEN", i);
direct$ = TableReadFieldStr(table, "DIRECT", i);
a$ = reqfilen$ + "1" + direct$;
if (name$ == a$) {
photodate = TableReadFieldNum(table, "PHOTODATE", i);
quadname$ = TableReadFieldStr(table, "QUADNAME", i);
dftcont = TableReadFieldNum(table, "CONTRAST", i);
dftbright = TableReadFieldNum(table, "BRIGHTNESS", i);
}
}
if (!photodate) {
photodate$ = PopupString(
'The metadata for this DOQQ (' + name$ + ') is empty or missing.
Please type the date of the photography as you wish it to
appear on the map here (mm/dd/yy):');
}
else {
#photodate$ = DateToString(photodate, "%e %B %Y"); # 5 January 1996
photodate$ = DateToString(photodate, "%m/%d/%y"); # 01/05/96
}
CloseRaster(DOQQ);
imgdate$ = "Date of photography: " + photodate$ + "\n";
wantNeatLine = IniReadNumber("DOQQSML", "WantNeatLine", false);
wantNeatLine = PopupYesNo("Do you want a neatline around the map image area?", wantNeatLine);
IniWriteNumber("DOQQSML", "WantNeatLine", wantNeatLine);
##############################################################################
# step 4b:
# We got the title from the metadata too (assuming we found it)
##############################################################################
if (quadname$ == "") {
# Didn't find one in the metadata. Default to doqq object description
maptitle$ = doqqObjectDesc$;
}
else {
ntokens = NumberTokens(quadname$, " ");
maptitle$ = GetToken(quadname$, " ", 1);
for i = 2 to (ntokens - 1) {
maptitle$ = maptitle$ + " " + GetToken(quadname$, " ", i);
}
# The last token will be the direction. Insert a comma after it.
if (ntokens > 1) {
maptitle$ = maptitle$ + ", " + GetToken(quadname$, " ", ntokens);
}
}
contrast = 50;
brightness = 100;
if (dftcont) contrast = dftcont;
if (dftbright) brightness = dftbright;
##############################################################################
# step 4d:
# Prompt for title, contrast and brightness values
##############################################################################
class PromptStr promptTitle;
class PromptNum promptCont, promptBright;
class XmForm dlg;
proc CB_Close() {
maptitle$ = promptTitle.value;
contrast = promptCont.value;
brightness = promptBright.value;
DialogClose(dlg);
}
dlg = CreateModalFormDialog("Settings");
promptTitle = CreatePromptStr(dlg, "Title:", 64, maptitle$);
promptTitle.TopWidget = dlg;
promptTitle.LeftWidget = dlg;
promptTitle.RightWidget = dlg;
promptCont = CreatePromptNum(dlg, "Contrast:", 4, 0, contrast, 0, 100);
promptCont.TopWidget = promptTitle;
promptCont.LeftWidget = dlg;
promptBright = CreatePromptNum(dlg, "Brightness:", 4, 0, brightness, 0, 200);
promptBright.TopWidget = promptTitle;
promptBright.LeftWidget = promptCont;
sep = CreateHorizontalSeparator(dlg);
sep.TopWidget = promptBright;
sep.LeftWidget = dlg;
sep.RightWidget = dlg;
br = CreateButtonRow(dlg, CreatePushButtonItem("ok", CB_Close));
br.TopWidget = sep;
br.LeftWidget = dlg;
br.RightWidget = dlg;
br.BottomWidget = dlg;
DialogWaitForClose(dlg);
DestroyWidget(dlg);
##############################################################################
# step 2: (moved)
# copy DOQQNAME from MERLIN data servers (in /RASTERS/DOQQ)
# Copy DNR Wetland vector from MERLIN data server (in VECTORS/DNRWET)
#
# Make sure they're not already there. We might want to prompt the
# user and ask them if they want to do this. If the source is already on
# a local harddrive or the network connection is reasonably fast, there's
# no reason to copy them. But there's no way we can tell if this is the case.
##############################################################################
if (PopupYesNo("Do you wish to copy the DOQQ raster and Wetlands\n vector to the work directory?", true)) {
if (FileNameGetPath(doqqFileName$) != workDir$) {
CloseRaster(DOQQ);
newname$ = workDir$ + "/" + FileNameGetName(doqqFileName$) + ".rvc";
CopyFile(doqqFileName$, newname$);
# Now need to reopen the object because we use that instead of the
OpenRaster(DOQQ, doqqFileName$, doqqObjectName$);
CloseRaster(DOQQ);
}
oldname$ = WetlandsVector.$INFO.Filename;
objname$ = WetlandsVector.$INFO.Name;
if (FileNameGetPath(oldname$) != workDir$) {
newname$ = workDir$ + "/" + FileNameGetName(oldname$) + ".rvc";
CopyFile(oldname$, newname$);
OpenVector(WetlandsVector, newname$, objname$);
CloseVector(WetlandsVector);
}
}
##############################################################################
# step 5:
# Open LAYOUT.RVC/DOQQ_Wetland_v - change description to DOQQNAME
##############################################################################
layout = LayoutCreate("layout", 1);
LayoutRead(layout, layoutFileName$, layoutObjectName$);
##############################################################################
# step 6:
# Verify print settings
##############################################################################
#layout.Hardcopy.Printer = "HP DesignJet 650C";
layout.Hardcopy.Printer = "HP DesignJet Series Color 300dpi";
layout.Hardcopy.MapScale = 8400;
layout.Hardcopy.RasterDitherPattern = "Stucki";
layout.Hardcopy.VectorDitherPattern = "weave"; # Internal name of "vector"
layout.Hardcopy.FullPage = true;
layout.Hardcopy.dpi = 300;
layout.Hardcopy.Contrast = contrast;
layout.Hardcopy.Brightness = brightness;
IniWriteString("PRINTERS", "PortType", "file");
IniWriteString("PRINTERS", "PortName", prffile$);
IniWriteString("PRINTERS", "PRFFile", prffile$);
##############################################################################
# step 7:
# Change the title
##############################################################################
group = LayoutGetGroupByName(layout, "Title");
textlayer = group.FirstLayer;
textstyle.Font = "times.ttf";
textstyle.Height = 1.25; # inches
textstyle.LineSpace = .3228; # Inches
textstyle.MapScale = layout.Hardcopy.MapScale;
textstyle.UseLayoutScale = 1;
textstyle.Foreground.Name = "black";
textstyle.Background.Name = "white";
textlayer.Text = maptitle$;
textlayer.Style = textstyle;
##############################################################################
# step 8:
# Remove the DOQQ raster and wetlands vector
# (Dont actually do this here. Well replace the objects in the
# existing layers instead)
# Instead, find the actual layers
##############################################################################
mapgroup = LayoutGetGroupByName(layout, "Map Data");
layer = mapgroup.FirstLayer;
while (layer) {
if (layer.Type == "Raster") rastlayer = layer;
if (layer.Type == "Vector") vectlayer = layer;
if (layer.Type == "Map Grid") gridlayer = layer;
layer = layer.NextLayer;
}
##############################################################################
# step 9:
# Re add the new DOQQ
##############################################################################
RasterLayerSetObject(rastlayer, DOQQ);
rastlayer.NullCellsTransparent = false;
##############################################################################
# step 10a:
# Re add the new Wetlands vector.
##############################################################################
VectorLayerSetObject(vectlayer, WetlandsVector);
vectlayer.Point.Select.Mode = "None";
vectlayer.Node.Draw = false;
vectlayer.StyleObject = OpenStyleObject(templateDir$ + "/styles.rvc", "STYLES");
##############################################################################
# step 10b:
# Set the line styles
##############################################################################
vectlayer.Line.Select.Mode = "ByAttribute";
vectlayer.Line.StyleMode = "ByAttribute";
vectlayer.DrawLinesFirst = false; # Don't draw lines before polygons
tinfo = TableGetInfo(WetlandsVector.line.ClassStyle);
tinfo.StyleObjName = "";
numeric num = NumRecords(WetlandsVector.line.ClassStyle);
string cl$, l$;
for i = 1 to num {
cl$ = WetlandsVector.line.ClassStyle[@i].Class$;
if (cl$ == "Unclassified") {
WetlandsVector.line.ClassStyle[@i]._DrawFlag_ = 0;
}
else {
WetlandsVector.line.ClassStyle[@i]._DrawFlag_ = 1;
l$ = left$(cl$, 1);
if (l$ == "E") {
WetlandsVector.line.ClassStyle[@i]._StyleName_$ = "Estuarine_Lin";
WetlandsVector.line.ClassStyle[@i]._StyleIndex_ = 1;
}
else if (l$ == "P") {
WetlandsVector.line.ClassStyle[@i]._StyleName_$ = "Palustrine_Lin";
WetlandsVector.line.ClassStyle[@i]._StyleIndex_ = 4;
}
else if (l$ == "R") {
WetlandsVector.line.ClassStyle[@i]._StyleName_$ = "Riverine_Lin";
WetlandsVector.line.ClassStyle[@i]._StyleIndex_ = 2;
}
else {
# Assume "break line" or "lead line"
# Instructions say "or similar", but can't tell
WetlandsVector.line.ClassStyle[@i]._StyleName_$ = "Break_Lead_Line";
WetlandsVector.line.ClassStyle[@i]._StyleIndex_ = 0;
}
}
}
##############################################################################
# step 10c:
# Set the polygon styles
##############################################################################
vectlayer.Poly.Select.Mode = "ByAttribute";
vectlayer.Poly.NoFill = false; # Don't disable polygon filling
tinfo = TableGetInfo(WetlandsVector.poly.ClassStyle);
tinfo.StyleObjName = "";
num = NumRecords(WetlandsVector.poly.ClassStyle);
for i = 1 to num {
WetlandsVector.poly.ClassStyle[@i]._DrawFlag_ = 1;
WetlandsVector.poly.ClassStyle[@i]._StyleName_$ = "Wetland";
WetlandsVector.poly.ClassStyle[@i]._StyleIndex_ = 0;
}
##############################################################################
# step 10d:
# Set the Label styles
##############################################################################
vectlayer.Label.Draw = true;
labelstyle.Font = "mallard.of";
labelstyle.Foreground.Name = "yellow";
labelstyle.Background.Name = "black";
labelstyle.Enhance = true;
labelstyle.UseElemHeight = true;
vectlayer.Label.NormalStyle = labelstyle;
##############################################################################
# step 11:
# Change group settings for the "Map Data" group
##############################################################################
# Need the extents of the DOQQ raster layer in degrees
georef = GeorefAlloc();
GeorefSetProjection(georef, latlon);
GetObjectExtents(DOQQ, xmin, ymin, xmax, ymax, georef);
GeorefFree(georef);
# Set the ClipRegion to this rect + a 2 second buffer
halfsize = (7.5 / 4) / 60; # Half the height of a quarter quad in degrees
twosec = 2.0 / 3600; # Two seconds converted to degrees
cx = (xmin + xmax) / 2;
cy = (ymin + ymax) / 2;
# Force center to fall on a the correct interval
# Floor rounds to -infinity.
cx = floor(cx / halfsize + .5) * halfsize;
cy = floor(cy / halfsize + .5) * halfsize;
extents.x1 = cx - halfsize - twosec;
extents.x2 = cx + halfsize + twosec;
extents.y1 = cy - halfsize - twosec;
extents.y2 = cy + halfsize + twosec;
# Need to set clip region to this rectangle
mapgroup.ClipRegion.SetFromRect(extents);
mapgroup.ClipRegion.Projection = latlon;
mapgroup.Clip = true;
mapgroup.X.AttachTo = "Margin";
mapgroup.X.AttachThis = "Left";
mapgroup.X.AttachRef = "Left";
mapgroup.X.Offset = .2; # Page inches
mapgroup.Y.AttachTo = "Margin";
mapgroup.Y.AttachThis = "Center";
mapgroup.Y.AttachRef = "Center";
mapgroup.Y.Offset = -.5; # Page inches
##############################################################################
# step 12:
# Modify the mapgrid
##############################################################################
if (gridlayer) LayerDestroy(gridlayer);
gridlayer = GroupAddMapGridLayer(mapgroup, projection, 5000, 5000, "feet", latlon, extents);
LayerRaise(vectlayer); # Move vector layer back on top
gridlayer.Name = projection.System;
gridlayer.CoordFormat = "Comma";
gridlayer.BorderRelative = false; # Starting At: Origin
gridlayer.ShowBorder = false;
gridlayer.LayoutScale = true;
gridlayer.ShowCoordText2D = true;
gridlayer.CoordLabelStyle = textstyle;
gridlayer.CoordLabelStyle.Foreground.name = "black";
gridlayer.CoordLabelStyle.Height = .1; # Inches, approx 2.5 mm
gridlayer.ShowGrid = true;
gridlayer.GridStyle.Color.name = "white";
gridlayer.GridWidthUnits = "mm";
gridlayer.GridWidth = .5;
# Border ticks are off. To offset text by a bit, turn them
# on, but keep them white so they don't show up
gridlayer.ShowBorderTicksExt = false;
gridlayer.ShowBorderTicksExt = false;
gridlayer.BorderTickWidthUnits = "mm";
gridlayer.BorderTickWidth = .5;
gridlayer.BorderTickOutsideUnits = "mm";
gridlayer.BorderTickOutside = 2.0;
gridlayer.BorderTickStyle.Color.name = "white";
##############################################################################
# step 13:
# Modify the CCWS Text group
##############################################################################
group = LayoutGetGroupByName(layout, "CCWS Text");
textlayer = group.FirstLayer;
textstyle.Height = .315; # approx 8 mm
textlayer.Style = textstyle;
textlayer.Text = ccs755$;
##############################################################################
# step 14:
# Modify the Photo Date text layer
##############################################################################
group = LayoutGetGroupByName(layout, "Photo Date");
textlayer = group.FirstLayer;
textstyle.Height = .157; # approx 4 mm
textlayer.Style = textstyle;
textlayer.Text = imgdate$;
##############################################################################
# step 16: (Changed the order here on purpose)
# Use object editor to edit the neatline vector. Id change this to use
# an SML layer to autogenerate a neatline. (This is an option Maryland
# didnt have when they started this project)
#
# Heres an even easier way. Create a mapgrid but only show the border
# The neatline will then be exactly on the quarter quad border.
##############################################################################
# We had expanded the extents out by two seconds for clipping. Now put them
# back to the actual quarter quad bounds.
extents.x1 = extents.x1 + twosec;
extents.x2 = extents.x2 - twosec;
extents.y1 = extents.y1 + twosec;
extents.y2 = extents.y2 - twosec;
# Neatline was done with a vector. Delete it
layer = GroupGetLayerByName(mapgroup, "NEATLINE / Neatline");
if (layer) LayerDestroy(layer);
if (wantNeatLine) {
neatlayer = GroupAddMapGridLayer(mapgroup, latlon, 1, 1, "degrees", latlon, extents);
neatlayer.Name = "NEATLINE / Neatline";
neatlayer.ShowGrid = false;
neatlayer.ShowCoordText2D = false;
neatlayer.ShowBorderTicksExt = false;
neatlayer.ShowBorderTicksInt = false;
neatlayer.ShowBorder = true;
neatlayer.LayoutScale = true;
# XXX Change the color of the neatline in the main map here...
neatlayer.BorderStyle.Color.name = "red"; # name from RGB.txt
neatlayer.BorderWidthUnits = "mm";
neatlayer.BorderWidth = .5;
}
##############################################################################
# Step 16a: There's a neatline in the index map too. Same drill
##############################################################################
indexgroup = LayoutGetGroupByName(layout, "Index Map");
# Neatline was done with a vector. Delete it
layer = GroupGetLayerByName(indexgroup, "NEATLINE / Neatline");
if (layer) LayerDestroy(layer);
neatlayer = GroupAddMapGridLayer(indexgroup, latlon, 1, 1, "degrees", latlon, extents);
neatlayer.Name = "NEATLINE / Neatline";
neatlayer.ShowGrid = false;
neatlayer.ShowCoordText2D = false;
neatlayer.ShowBorderTicksExt = false;
neatlayer.ShowBorderTicksInt = false;
neatlayer.ShowBorder = true;
neatlayer.LayoutScale = true;
# XXX This is the color of the rectangle in the index map
neatlayer.BorderStyle.Color.name = "red";
neatlayer.BorderWidthUnits = "mm";
neatlayer.BorderWidth = .5;
neatlayer.SizeRelative = true;
neatlayer.LayoutScale = false;
neatlayer.MapScale = layout.MapScale / indexgroup.RelScale;
##############################################################################
# step 15: (Changed the order here on purpose)
# Save the layout
##############################################################################
string newFileName$ = workDir$ + "/layout.rvc";
CopyFile(layoutFileName$, newFileName$);
layoutFileName$ = newFileName$;
DeleteObject(layoutFileName$, layoutObjectNumber); # Remove old layout
LayoutWrite(layout, layoutFileName$, layoutObjectName$, doqqObjectDesc$);
##############################################################################
# step 17:
# View / Print to generate print file, then save and exit
##############################################################################
numeric err = LayoutPageSetupDialog(layout);
#define SML_DEFINE_SML_MDISP_FUNC_LIST
if (!err && PopupYesNo("Do you want to print the layout now?", true)) {
LayoutPrint(layout);
}
# If we don't destroy the layout, it will prevent view from being able to
# exit.
LayoutDestroy(layout);
#
# rvcmain 4.131 4618
# rvcdbase.c 4.273 1260
# qqobject.c 1.138 1196
# doqq.sml --- 165
#
# Need better error if the database isn't found
#
# setting style object on vector layer seems to fail if it had no style
# assigned to begin with. May be that if vector was originally set to
# "From vector" it ignores the style object I set
#