Syntax Highlighing:
comments, key words, predefined symbols, class members & methods, functions & classes
# VPTOOL.sml
# View ToolScript "View Marks"
# Provides a dialog that allows recording of view positions (center of current
# View) in WGS84/Geographic coordinates along with current view scale. When
# adding a View Mark, user is prompted to name it; default name includes the
# scale and lat/lon position. The name of the View Mark is shown in a Listbox
# in the control dialog and can be selected there.
# Actions controlled by icon buttons in dialog:
# New: clear all view marks from the list
# Open: open saved view mark text file
# Save: save current view mark list to a text file with .pos extension
# Add: add a view mark to the list
# Remove: remove the selected view mark from the list
# Zoom: pan/zoom the view to the selected view mark
# Close: close the dialog and switch to default tool
### Revision 24 June 2008.
### Requires version 2007:73 or later of the TNT products
#
# The following symbols are predefined
# class GRE_VIEW View {use to access the view the tool script is attached to}
# class GRE_GROUP Group {use to access the group being viewed if the script is run from a group view}
# class GRE_LAYOUT Layout {use to access the layout being viewed if the script is run from a layout view}
# numeric ToolIsActive Will be 0 if tool is inactive or 1 if tool is active
#
# The following values are also predefined and are valid when the various On...()
# functions are called which deal with pointer and keyboard events.
# numeric PointerX Pointer X coordinate within view in pixels
# numeric PointerY Pointer Y coordinate within view in pixels
# numeric ShiftPressed 1 if <shift> key being pressed or 0 if not
# numeric CtrlPressed 1 if <ctrl> key being pressed or 0 if not
# numeric LeftButtonPressed 1 if left pointer button pressed or 0 if not
# numeric RightButtonPressed 1 if right pointer button pressed or 0 if not
# numeric MiddleButtonPressed 1 if middle pointer button pressed or 0 if not
#
### GLOBAL VARIABLES
class SR_COORDREFSYS crsLatLon; # Latitude/longitude CRS for defining View Mark positions
class TRANS2D_MAPGEN transLatLonView; # coordinate transformation between View coordinates and latitude/longitude
class FILE posfile; # handle for text file containing position list
numeric ischanged;
numeric setDefaultWhenClose;
numeric numpos; # number of positions in list
array numeric posX[1]; # arrays for x and y positions of view marks in list
array numeric posY[1];
array numeric posScale[1]; # array for current view scale value for each view mark
class STRINGLIST nameList; # stringlist to hold View Mark names
# variables for dialog and its controls
class GUI_DLG dlgwin;
class GUI_CTRL_LISTBOX vpList;
###################################################################################
############## USER-DEFINED FUNCTIONS AND PROCEDURES ##########################
###################################################################################
# Save the list to a file.
func DoSave ()
{
if (numpos == 0) return false;
local class STRING posfilename$ = GetOutputFileName("", "Select position file to save as:", "pos");
posfile = fopen(posfilename$,"w", "UTF8");
if (posfile == 0) return (false);
# loop through stringlist and arrays to write View Mark list to text file
local numeric i;
for i = 1 to numpos {
fprintf(posfile, "%s,%f,%f,%f\n", nameList[i - 1], posX[i], posY[i], posScale[i]);
}
fclose(posfile);
ischanged = false;
return (true);
}
func AskSave ()
{
if (!ischanged || numpos == 0) return (true);
local numeric answer;
answer = PopupYesNoCancel("Save current point list?", 1);
if (answer < 0) return (false);
if (answer == 0) return (true);
return ( DoSave() );
}
# Zoom to selected position
proc DoZoom ()
{
local numeric selpos;
if (numpos == 0) return;
if (vpList.GetSelectedItemCount() > 0)
{
selpos = vpList.GetSelectedItemIndex();
# Listbox indexing begins with 0, whereas array indexing begins with 1.
# Increment recorded index of selected item to align it with the arrays
++selpos;
# get coordinate transformation from Latitude/Longitude to View coordinates
transLatLonView = View.GetTransMapToView(crsLatLon)
if (transLatLonView == 0)
{
PopupMessage("Cannot obtain map/view transformation.");
return;
}
local class POINT2D zpoint; # point to hold position coordinates
# read lat/lon coordinates for the selected position from the arrays
zpoint.x = posX[selpos];
zpoint.y = posY[selpos];
# convert point to curent View coordinates
zpoint = transLatLonView.ConvertPoint2dFwd(zpoint); # forward transformation, from Lat/Lon to View
# check if point is within the extents of the data currently in the View
if ( !View.Extents.ContainsPoint(zpoint) )
{
PopupMessage("Point is outside extents of objects being viewed.");
return;
}
View.DisableRedraw = true;
View.CurrentMapScale = posScale[selpos];
View.Center = zpoint;
View.DisableRedraw = false;
View.Redraw();
}
}
# Add current viewpoint to list
proc DoAdd ()
{
# get coordinate transformation from Latitude/Longitude to View coordinates
transLatLonView = View.GetTransMapToView(crsLatLon);
if (transLatLonView == 0)
{
PopupMessage("Cannot obtain map/view transformation.");
return;
}
local class POINT2D cpoint; # point to record View center coordinates
cpoint = transLatLonView.ConvertPoint2DInv(View.Center); # inverse transformation, from View to Lat/Lon
++numpos; # increment list counter
# resize arrays and add coordinates and scale for current View Mark
ResizeArrayPreserve(posX, numpos);
ResizeArrayPreserve(posY, numpos);
ResizeArrayPreserve(posScale, numpos);
posX[numpos] = cpoint.x;
posY[numpos] = cpoint.y;
posScale[numpos] = View.CurrentMapScale;
local string namestr$ = sprintf("%d: %.0f %f %f", numpos, posScale[numpos], posX[numpos], posY[numpos]);
namestr$ = PopupString("Enter view position name:",namestr$);
local numeric i;
local numeric foundDup = 1;
local numeric dup;
while (foundDup == 1)
{
dup = 0;
for i = 0 to nameList.GetNumItems() - 1
{
if (nameList[i] == namestr$) then ++dup;
}
if (dup == 0) then foundDup = 0;
else
namestr$ = PopupString("Name already used.\nEnter view position name:", namestr$);
}
nameList.AddToEnd(namestr$); # add name of View Mark to stringlist
vpList.AddItem(namestr$, namestr$); # add name to Listbox control in dialog
ischanged = true;
}
# Remove selected item from list
proc DoRemove ()
{
local numeric selpos, i;
if (numpos == 0) return;
if (vpList.GetSelectedItemCount() > 0)
{
selpos = vpList.GetSelectedItemIndex();
vpList.DeleteItemIndex(selpos); # delete entry from listbox
nameList.Remove(selpos); # delete View Mark name from stringlist
# Listbox & stringlist indexing begin with 0, whereas array indexing begins with 1.
# Increment recorded index of removed item to align it with the arrays
++selpos;
# Copy next highest value in each array to current index, beginning with
# index corresponding to the deleted item.
for i = selpos to numpos - 1
{
posX[i] = posX[i + 1];
posY[i] = posY[i + 1];
posScale[i] = posScale[i + 1];
}
--numpos; # decrement position counter
ischanged = true;
}
}
# Clear the list
proc DoNew ()
{
if ( !AskSave() ) return;
numpos = 0;
vpList.DeleteAllItems();
ischanged = false;
}
# Open file containing list.
proc DoOpen ()
{
if ( !AskSave() ) return;
posfile = GetInputTextFile("","Select positions file to open:","pos", "UTF8");
if (posfile == 0) return;
numpos = 0;
vpList.DeleteAllItems(); # clear the current list in View Mark listbox
nameList.Clear(); # clear the View Mark name stringlist
ischanged = false;
# read the values from the text file
local class STRING filestr$, name$;
while (!feof(posfile)) { # while not at end of text file
filestr$ = fgetline$(posfile); # get next line of text
if (NumberTokens(filestr$,",") < 4) continue; # jump to next iteration if less than 4 tokens in text line
++numpos;
ResizeArrayPreserve(posX, numpos); # resize the coordinate and scale arrays
ResizeArrayPreserve(posY, numpos);
ResizeArrayPreserve(posScale, numpos);
name$ = GetToken(filestr$,",",1);
nameList.AddToEnd(name$); # add View Mark name to stringlist
vpList.AddItem(name$); # add View Mark name to listbox control
posX[numpos] = StrToNum(GetToken(filestr$,",",2)); # add values to numeric arrays
posY[numpos] = StrToNum(GetToken(filestr$,",",3));
posScale[numpos] = StrToNum(GetToken(filestr$,",",4));
}
fclose(posfile);
}
# Close the window, switching to default tool
proc DoClose ()
{
if (setDefaultWhenClose)
{
setDefaultWhenClose = false;
View.SetDefaultTool();
}
}
##################################################################################
# The following script functions will be called (if used in the script) when
# the appropriate action or event occurs as described in the comments before each.
# To use a function, uncomment the lines containing the 'func' definition
# and ending brace '}' by removing the leftmost '#' on the line and add the
# function code between the two lines.
# Called the first time the tool is activated.
# If the tool implements a dialog it should be created (but not displayed) here.
proc OnInitialize ()
{
local string xml$;
local numeric err;
# set up geographic coordinate reference system for recording View Mark positions
crsLatLon.Assign("Geographic2D_WGS84_Deg");
# string with dialog specification
xml$ = '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root SYSTEM "smlforms.dtd">
<root>
<dialog id="vptool" Title="Viewpoint List" Buttons="">
<pane Orientation="vertical">
<pane Orientation="horizontal" ChildSpacing="8" HorizontalResize="Expand">
<pane Orientation="horizontal" ChildSpacing="2">
<pushbutton id="newBtn" Icon="FILE_NEW" ToolTip="New" OnPressed="DoNew()"/>
<pushbutton id="openBtn" Icon="FILE_OPEN" ToolTip="Open.." OnPressed="DoOpen()"/>
<pushbutton id="saveBtn" Icon="FILE_SAVE" ToolTip="Save..." OnPressed="DoSave()"/>
</pane>
<pane Orientation="horizontal" ChildSpacing="2">
<pushbutton id="addBtn" Icon="CONTROL_ADD_CYAN" ToolTip="Add" OnPressed="DoAdd()"/>
<pushbutton id="subtractBtn" Icon="CONTROL_SUBTRACT_CYAN" ToolTip="Remove" OnPressed="DoRemove()"/>
</pane>
<pane Orientation="horizontal" ChildSpacing="2">
<pushbutton id="zoomBtn" Icon="EDIT_APPLY_RED" ToolTip="Zoom" OnPressed="DoZoom()"/>
<pushbutton id="closeBtn" Icon="EDIT_DELETE_RED" ToolTip="Close" OnPressed="DoClose()"/>
</pane>
</pane>
<listbox id="vpList" Width="30" Height="5"/>
</pane>
</dialog>
</root>';
### parse XML string; returns an error code (number < 0 ) if there are syntax errors
local class XMLDOC doc;
err = doc.Parse(xml$);
if (err < 0)
{
PopupError(err); # Popup an error dialog. "Details" button shows syntax errors.
Exit();
}
### declare class instance for the dialog element in the XML structure
### and get the dialog handle from the XML structure.
### Pop up an error dialog and exit if the dialog ID can't be found in the XML.
class XMLNODE dlgnode;
dlgnode = doc.GetElementByID("vptool");
if (dlgnode == 0)
{
PopupMessage("Could not find dialog node in XML document");
Exit();
}
### set the XML structure in memory as the source for the dialog.
dlgwin.SetXMLNode(dlgnode);
err = dlgwin.CreateModeless();
if (err < 0)
{
PopupError(err); # Popup an error dialog. "Details" button shows syntax errors.
Exit();
}
# get handles for dialog controls
vpList = dlgwin.GetCtrlByID("vpList");
} # end of OnInitialize
# Called when tool is to be destroyed, will not be called if tool was never activated.
# If the tool implements a dialog it should be destroyed here.
# proc OnDestroy () {
# } # end of OnDestroy
# Called when tool is activated.
# If the tool implements a dialog it should be "managed" (displayed) here.
proc OnActivate ()
{
dlgwin.Open();
setDefaultWhenClose = true;
} # end of OnActivate
# Called when tool is deactivated (usually when switching to another tool).
# If the tool implements a dialog it should be "unmanaged" (hidden) here.
proc OnDeactivate ()
{
dlgwin.Close(0);
setDefaultWhenClose = false;
} # end of OnDeactivate
# Called when tool is to be 'suspended' during a redraw operation.
# proc OnSuspend () {
# } # end of OnSuspend
# Called when tool is to be 'resumed' after a redraw operation.
# If the tool displays any graphics they should be updated by this function.
# proc OnResume () {
# } # end of OnResume
# Called when user presses 'left' pointer/mouse button.
# proc OnLeftButtonPress () {
# } # end of OnLeftButtonPress
# Called when user presses 'right' pointer/mouse button.
# proc OnRightButtonPress () {
# } # end of OnRightButtonPress
# Called when user presses 'middle' pointer/mouse button.
# proc OnMiddleButtonPress () {
# } # end of OnMiddleButtonPress
# Called when user releases 'left' pointer/mouse button.
# proc OnLeftButtonRelease () {
# } # end of OnLeftButtonRelease
# Called when user releases 'right' pointer/mouse button.
# proc OnRightButtonRelease () {
# } # end of OnRightButtonRelease
# Called when user releases 'middle' pointer/mouse button.
# proc OnMiddleButtonRelease () {
# } # end of OnMiddleButtonRelease
# Called when user moves cursor if no button being pressed
# proc OnPointerMoveNoButton () {
# } # end of OnPointerMoveNoButton
# Called when user moves cursor while holding down button
# proc OnPointerMoveWithButton () {
# } # end of OnPointerMoveWithButton
# Called when cursor enters window associated with view.
# proc OnEnterWindow () {
# } # end of OnEnterWindow
# Called when cursor leaves window associated with view.
# proc OnLeaveWindow () {
# } # end of OnLeaveWindow
# Called when user presses 'key' on keyboard.
# proc OnKeyPress (key) {
# } # end of OnKeyPress