home products news downloads documentation support gallery online maps resellers search
TNTmips Downloads Menu

HOME

CONTACT US

CURRENT RELEASE
  TNT 2013

DEVELOPMENT VERSION
  TNT 2014

TNTmips Pro
PRIOR RELEASES
  TNT 2012

FREE SOFTWARE
  TNTmips Free
  TNTatlas
  TNTsdk

MORE DOWNLOADS
  HASP Key Driver
  Screen Recorder
  TNT Language Kits
  Sample Geodata
  TNT Scripts

DOCUMENTATION
  TNTmips Tutorials
  Tutorial Datasets
  Technical Guides
  Scripts
  Quick Guides

MORE INFO
  Download FAQs
  FTP
  Download Managers
  Find Reseller

SITE MAP


GeotagDbase.sml


### GeotagDbase.sml

### Requires version 2006:73 of the TNT products

### Randy Smith
### MicroImages, Inc.
### 19 January 2007

### Sample script to demonstrate geotagging a database table using a single GPS log file.

### Source table must contain a string field with date in form "month/day/year"
### and a string field with time in form "hour:min:sec".  User-defined functions
### getDate() and getTime() can be modified to accommodate other date or time formats.

### Coordinate fields "Latitude", "Longitude", and "Elevation" are added to a copy of 
### the input database table that is written to the same database object as the source table.


############## Global variable declarations ####################
class DATABASE dbSource;		# source database object
class DBTABLEINFO tblSource, tblDest;	# source and destination tables

class GPSDBASE gpsdbase;		# class for determining location based on Date/Time from
										# any source and one or more GPS track logs.

string srctablename$;			# name of selected source database table
string srcdatefld$, srctimefld$;		# names of selected date and time fields in the source table
string desttablename$;			# name designated for the new database table
string gpsfilename$;				# name of the selected GPS log file

class DBEDITOR dbSourceView, dbDestView;
numeric dbOpen;								# flag to indicate if source database has been opened
numeric destTblOpen, srcTblOpen;			# flags to indicate if tabular view of table has been opened

# variables related to the script dialog
string xml$;						# string containing the dialog specification in XML
class XMLDOC dlgdoc;				# class instance for the XML document specifying the dialog
class XMLNODE gpsdlgnode;		# class instance for the dialog's node in the parsed XML structure
numeric err;						# error value checked at stages of processing the dialog specification

class GUI_DLG gpsdialog;		# internal class instance for the script dialog

# handles for the various controls in the script dialog
class GUI_CTRL_LABEL tblLabel;
class GUI_CTRL_PUSHBUTTON openSrcBtn, tblBtn, dateBtn, timeBtn, logBtn, newTblBtn;
class GUI_CTRL_PUSHBUTTON runBtn, testBtn, exitBtn;
class GUI_CTRL_EDIT_STRING dbText, tblText, dateText, timeText, logText, newTblText, statusText;


################################################################## 
############### Procedures and Functions #########################
##################################################################


########################################################
# Function to get date string from a field in the source database
# and parse it to get year, month, and day. 
# Returns the Julian date (numeric value).
#
# Modify this function to fit the date format in your source table.
func getDate(numeric recnum, class DBTABLEINFO tblSource, string srcdatefld$) {
	local class DATETIME dateDT;	# structure to store and convert date/time information
	local string date$;
	local numeric year, month, day;

	# get string from designated date field in source table
	date$ = TableReadFieldStr(tblSource, srcdatefld$, recnum);

	# parse date string to get year, month, and day
	year = StrToNum( GetToken(date$, "/", 3) );
	month = StrToNum( GetToken(date$, "/", 1) );
	day = StrToNum( GetToken(date$, "/", 2) );

	# set date in DATETIME class and return value
	dateDT.SetDate(year, month, day);
	return dateDT.GetDateJulian();
	} # end of func getDate

##########################################################
# Function to get time string from a field in the source database
# and parse it to get hour, min, and sec.
# Returns the time as seconds since midnight (numeric valus).
#
# Modify this function to fit the time format in your source table.
func getTime(numeric recnum, var class DBTABLEINFO tblSource, string srctimefld$) {
	local class DATETIME timeDT;		# structure to store and convert date/time information
	local string time$;
	local numeric hour, min, tsec;

	# get string from designated time field in source table
	time$ = TableReadFieldStr(tblSource, srctimefld$, recnum);

	# parse time string to get hour, min, and seconds
	hour = StrToNum( GetToken(time$, ":", 1) );
	min = StrToNum( GetToken(time$, ":", 2) );
	tsec = StrToNum( GetToken(time$, ":", 3) );

	# set time in DATETIME class to be returned
	timeDT.SetTime(hour, min, tsec);
	return timeDT.GetTimeSecondsSinceMidnight(1);
	} # end of func getTime


##################################################################
# Procedure called when exiting.  Called by Exit button on dialog.
proc ExitScript() { 

	if (srcTblOpen) then
		DBEditorCloseTable(dbSourceView, srctablename$);

	if (destTblOpen) then
		DBEditorCloseTable(dbDestView, desttablename$);

	if (dbOpen) then
		CloseDatabase(dbSource);

	print("Closing...");
 
	gpsdialog.Close(0);
	}

#############################################################
# Procedure called when the dialog opens; sets status prompt.
proc OnDlgOpen() {
	statusText = gpsdialog.GetCtrlByID("statusText");
	statusText.SetValueStr("Select source database.");
	}

###################################################################
# Procedure to get source database object.
# Called by Source Database button on dialog
proc GetSourceDatabase () { 

	local class RVC_OBJITEM dbItem;				# RVC item for source database object
	local class FILEPATH dbFilepath;				# filepath of the source database object
	local class RVC_DESCRIPTOR dbDescript;		# RVC descriptor of source database (works with long object names)

	local string srcfilename$;				# name of the Project File containing the source database object
	local string srcobjname$;				# name of source database object
	local numeric err;

	#####################################################################
	statusText = gpsdialog.GetCtrlByID("statusText");	############### delete then when OnOpen() is fixed. 

	# open pop-up dialog to select the source database object
	err = DlgGetObject("Choose standalone database object:", "Database", dbItem, "ExistingOnly");

	if (err < 0) 	# if not database object selected
		{
		statusText.SetValueStr("No source database selected; please select one.");
		}
	else 		# database object selected
		{
		dbFilepath = dbItem.GetFilePath();			# get filepath from database's RVC_OBJITEM
		srcfilename$ = dbFilepath;

		dbDescript = dbItem.GetDescriptor();		# get RVC descriptor of the database object
		srcobjname$ = dbDescript.GetFullName();	# get name of source database object (works with long names) 

		# get handle for text control used to show database object name and write name to it 
		dbText = gpsdialog.GetCtrlByID("dbText");
		dbText.SetValueStr(sprintf("%s\%s\n", srcfilename$, srcobjname$) );

		dbSource = OpenDatabase(srcfilename$, srcobjname$);	# open the source database
		dbOpen = 1;															# set flag to indicate database has been opened

		# get handle for next pushbutton control and enable it
		tblBtn = gpsdialog.GetCtrlByID("tblBtn");
		tblBtn.SetEnabled(1);

		# update status prompt
		statusText.SetValueStr("Select source table.");
		}


	} # end proc GetSourceDatabase


##################################################
# Procedure to get source table from open database
# Called by Source Table button on dialog
proc GetSourceTable () {

	local numeric err;

	# open popup dialog to select the source table from the database
	err = PopupSelectTable(dbSource, srctablename$);

	if (err < 1)	# no table was selected
		{
		statusText.SetValueStr("No table selected; please select a source table.");
		}
	else	# table was selected
		{
		# get handle for text control used to show source table name and write name to it
		tblText = gpsdialog.GetCtrlByID("tblText");
		tblText.SetValueStr(srctablename$);

		tblSource = DatabaseGetTableInfo(dbSource, srctablename$);	# get DBTABLEINFO from source table

		# get handle for the View Table pushbutton control and enable it 
		openSrcBtn = gpsdialog.GetCtrlByID("openSrcTblView");
		openSrcBtn.SetEnabled(1);

		# get handle for next pushbutton control and enable it
		dateBtn = gpsdialog.GetCtrlByID("dateBtn");
		dateBtn.SetEnabled(1);

		# update status prompt
		statusText.SetValueStr("Select field containing date string.");
		}

	} # end func GetSourceTable

#######################################################
# Procedure to open a tabular view of the source table
# Called by Open View button on dialog
proc OpenSourceTblView () {
	dbSourceView = DBEditorCreate(dbSource);
	srcTblOpen = 1;
	DBEditorOpenTabularView(dbSourceView, srctablename$);
	}

####################################################
# Procedure to get Date field from source table
# Called by Date Field button on dialog
proc GetDateField () {
	numeric err;
	local class DBFIELDINFO dateFld;

	# open popup dialog to select field in source table
	err = PopupSelectTableField(dbSource, srctablename$, srcdatefld$);

	if (err < 1) then	# no field was selected
		statusText.SetValueStr("No date field selected.");

	else		# field was selected
		{
		dateFld = FieldGetInfoByName(tblSource, srcdatefld$); # get info for selected field to check type

		if (dateFld.Type <> "string") then		# if not a string field
			statusText.SetValueStr("Selected field must be type string.");

		else		# selected field is a string field
			{
			# get handle for text control used to show field name and write name to it
			dateText = gpsdialog.GetCtrlByID("dateText");
			dateText.SetValueStr(srcdatefld$);

			# get handle for next pushbutton control and enable it
			timeBtn = gpsdialog.GetCtrlByID("timeBtn");
			timeBtn.SetEnabled(1);

			# update status prompt
			statusText.SetValueStr("Select field containing time string.");
			}
		}
	}	# end proc GetDateField


####################################################
# Procedure to get Time field from source table
# Called by Time Field button on dialog
proc GetTimeField () {
	numeric err;
	local class DBFIELDINFO timeFld;

	# open popup dialog to select field in source table
	err = PopupSelectTableField(dbSource, srctablename$, srctimefld$);

	if (err < 1) then		# no field was selected
		statusText.SetValueStr("No time field selected.");

	else			# field was selected
		timeFld = FieldGetInfoByName(tblSource, srctimefld$);	# get info for selected field to check type

		if (timeFld.Type <> "string")		# if not a string field
			statusText.SetValueStr("Selected field must be type string.");

		else		# selected field is a string field
			{
			# get handle for text control used to show field name and write name to it
			timeText = gpsdialog.GetCtrlByID("timeText");
			timeText.SetValueStr(srctimefld$);

			# get handle for next pushbutton control and enable it
			logBtn =gpsdialog.GetCtrlByID("logBtn");
			logBtn.SetEnabled(1);

			# update status prompt
			statusText.SetValueStr("Select GPS log file.");
			}
	} # end proc GetTimeField


#######################################################
# Procedure to get GPS log
# Called by GPS Log button on dialog
proc GetGPSlog () {

	# open popup dialog to choose GPS log file
	gpsfilename$ = GetInputFileName("", "Select GPS log file in NMEA or MicroImages format:", "");

	if (gpsfilename$ == "") then		# no log file selected
		statusText.SetValueStr("No GPS log file selected.");

	else		# a log file was selected
		{
		if (gpsdbase.ReadLog(gpsfilename$) < 1) then 	# log file not read 
			statusText.SetValueStr("Log file must be text in NMEA or MicroImages format.");

		else	# log file is successfully read
			{
			# get handle for text control used to show log file name and write name to it
			logText = gpsdialog.GetCtrlByID("logText");
			logText.SetValueStr(gpsfilename$);

			# get handles for label and text control for new table name and enable them
			tblLabel = gpsdialog.GetCtrlByID("tblLabel");
			tblLabel.SetEnabled(1);

			newTblText = gpsdialog.GetCtrlByID("newTblText");
			newTblText.SetEnabled(1);

			# update status prompt
			statusText.SetValueStr("Enter name for output table and press [Tab] or [Enter].");
			}
		}
	} # end proc GetGPSlog


#######################################################
# Procedure to set name for new output table.
# Called when the New Table Name editstring control is activated by Return/Enter key.
proc SetOutputTableName () {

	# get handles for controls to be enabled if non-empty string was entered
	runBtn = gpsdialog.GetCtrlByID("runBtn");
	testBtn = gpsdialog.GetCtrlByID("testBtn");

	# get handle for the text control and get the string from it to be checked and used
	newTblText = gpsdialog.GetCtrlByID("newTblText");
	desttablename$ = newTblText.GetValueStr();

	if (desttablename$ <> "")	# table name string not empty
		{
		runBtn.SetEnabled(1);		# enable Run button
		testBtn.SetEnabled(1);		# enable Test button
		# update status prompt
		statusText.SetValueStr("Set options and press [Test] or [Run].");
		}
	else	# string is empty
		statusText.SetValueStr("Please enter name for output table.");

	} # end proc SetOutputTableName


#######################################################
# Procedure to get processing options from the dialog.
# Called by Run() and Test() procedures
proc GetOptions (var numeric gpsTimeOffset, var numeric maxTimeDiff, var string method$) {

	# get gps offset time from dialog
	local numeric adjHour = gpsdialog.GetCtrlByID("adjHour").GetValueNum();
	local numeric adjMin = gpsdialog.GetCtrlByID("adjMin").GetValueNum();
	local numeric adjSec = gpsdialog.GetCtrlByID("adjSec").GetValueNum();

	if (adjHour < 0)	# check if hour is negative value and change sign for min and sec
		{
		adjMin = adjMin * -1;
		adjSec = adjSec * -1;
		}

	# convert gps offset time in H:M:S to seconds for GPSDBASE class
	local numeric gpsTimeOffset = adjHour * 3600 + adjMin * 60 + adjSec;

	# get maximum time difference; used as parameter to GPSDBASE.Compute()
	local numeric maxTimeDiff = gpsdialog.GetCtrlByID("maxDiff").GetValueNum();

	# get method (Interpolate or Closest); used as parameter to GPSDBASE.Compute()
	local string method$ = gpsdialog.GetCtrlByID("method").GetValueStr();
	} # end proc GetOptions


########################################################
# Procedure to check how many records get geotagged with current settings.
# Called by Test button on dialog.
proc Test () {

	# get GPS log processing options from dialog using user-defined function
	local numeric gpsTimeOffset, maxTimeDiff;
	local string method$;
	GetOptions(gpsTimeOffset, maxTimeDiff, method$);
	gpsdbase.SetOffset(gpsTimeOffset);		# set GPS time offset

	### loop through records in table to compute and add coordinates
	local numeric j;					# loop counter
	local numeric numTagged = 0;	# count of number of records succesfully geotagged

	local class GPSDATA gpsdata;		# current GPS location to be passed to GBSDBASE
	local class DATETIME datetime;	# date time info to be passed to GPSDBASE
	local class POINT3D position;		# point location returned by GPSDATA

	for j = 1 to tblSource.NumRecords 
		{
		# set date and time in DATETIME class to be passed to GPSDBASE
		# by calling user-defined functions that get them from the source database
		# and return Julian date and time in seconds since midnight 
		datetime.SetDateJulian( getDate(j, tblSource, srcdatefld$) );
		datetime.SetTimeSecondsSinceMidnight( getTime(j, tblSource, srctimefld$) );

		# compute coordinates; method returns 0 if time match found and -1 if not
		if (gpsdbase.Compute(datetime, gpsdata, method$, maxTimeDiff) == 0) # time match found
			{
			position = gpsdata.position;	# get position from GPSDATA as 3D point

			++ numTagged;		# increment success counter
#			printf("latitude= %.6f, longitude = %.6f, elevation = %.2f \n", position.y, position.x, position.z);
			}
		} # end for

	# update status prompt
	statusText.SetValueStr( sprintf("Current options would geotag %d of %d records.", numTagged, tblSource.NumRecords) );

	} # end proc Test

#######################################################
# Procedure called when Run button is pressed.
# Copies source table to destination, adds position fields,
# and loops through records to compute position and write
# values to the new fields. 
proc Run () {
 
	# get GPS log processing options from dialog using user-defined function
	local numeric gpsTimeOffset, maxTimeDiff;
	local string method$;
	GetOptions(gpsTimeOffset, maxTimeDiff, method$);
	gpsdbase.SetOffset(gpsTimeOffset);		# set GPS time offset

	### copy source table and rename with name previously chosen by user
	TableCopy(dbSource, tblSource, dbSource);		# copy table; name gets incremented by adding "1"
	local string ctablename$ = srctablename$ + "1";		# make string for name of copied table 
	tblDest = DatabaseGetTableInfo(dbSource, ctablename$);	# get table info for copied table
	tblDest.Name = desttablename$;		# rename table

	### add fields for Latitude, Longitude, and Elevation to the copied table
	local class DBFIELDINFO elevInfo;
	TableAddFieldFloat(tblDest, "Latitude", 10, 6);
	TableAddFieldFloat(tblDest, "Longitude", 10, 6);
	elevInfo = TableAddFieldFloat(tblDest, "Elevation", 10, 6);
	elevInfo.UnitType = "length";
	elevInfo.Units = "meters";

	### loop through records in destination table to compute and add coordinates
	local numeric j;					# loop counter
	local numeric numTagged = 0;	# count of number of records succesfully geotagged

	local class GPSDATA gpsdata;		# current GPS location to be passed to GPSDBASE
	local class DATETIME datetime;	# date time info to be passed to GPSDBASE
	local class POINT3D position;		# point location returned by GPSDATA

	for j = 1 to tblDest.NumRecords 
		{
		# set date and time in DATETIME class to be passed to GPSDBASE
		# by calling user-defined functions that get them from the destination database
		# and return Julian date and time in seconds since midnight 
		datetime.SetDateJulian( getDate(j, tblDest, srcdatefld$) );
		datetime.SetTimeSecondsSinceMidnight( getTime(j, tblDest, srctimefld$) );

		# compute coordinates; method returns 0 if time match found and -1 if not
		if (gpsdbase.Compute(datetime, gpsdata, method$, maxTimeDiff) == 0) # if time match found
			{
			position = gpsdata.position;		# get position for record from GPSDATA as 3D point
			TableWriteField(tblDest, j, "Latitude", position.y);		# write coordinates to table
			TableWriteField(tblDest, j, "Longitude", position.x);
			TableWriteField(tblDest, j, "Elevation", position.z);
			++ numTagged;			# increment success counter

#			printf("latitude= %.6f, longitude = %.6f, elevation = %.2f \n", position.y, position.x, position.z);
			}
		} # end for

	# update status prompt
	statusText.SetValueStr( sprintf("Successfully geotagged %d of %d records.", numTagged, tblDest.NumRecords) );

	dbDestView = DBEditorCreate(dbSource);
	DBEditorOpenTabularView(dbDestView, desttablename$);	# open tabular view of result table
	destTblOpen = 1;

	} # end proc Run


################################################################
################### Main Program ###############################
################################################################
clear();

xml$='

	
		
			
				
				
			
			
				
				
				
			
			
				
				
				
			
			
				
				
				
			
			
				
				
			
			
				
				
			
		
		
			
				
				
				
				
				
				
				
			
			
				
				
				
			
			
				
					
					
				
			
		
		
			
			
			
			
		
	
';

### parse XML text for the dialog into memory; 
### return an error code (number < 0 ) if there are syntax errorsi
err = dlgdoc.Parse(xml$);

if ( err < 0 ) {
	PopupError( err ); 	# Popup an error dialog. "Details" button shows syntax errors.
	Exit();
}

# get the dialog element from the parsed XML document and
# show error message if the dialog element can't be found
gpsdlgnode = dlgdoc.GetElementByID("gps");

if ( gpsdlgnode == 0 ) {
	PopupMessage("Could not find dialog node in XML document");
	Exit();
}

# Set the XML dialog element as the source for the GUI_DLG class instance
# we are using for the dialog window.
gpsdialog.SetXMLNode(gpsdlgnode);
err = gpsdialog.DoModal();

if ( err == -1 ) {	# exit script if Cancel button on dialog is pressed
	Exit();
	}








Back Home ©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

25 March 2009

page update: 26 May 11