TNTmips

HOME

FREE PRODUCTS
  TNTlite
  TNTatlas
  TNTsim3D

DOWNLOADS
  Release Version
  Development Version
  FTP
  Language Kits
  Sample Geodata
  Reseller Resources
  Promotional

DOCUMENTATION
  Tutorials
  Technical Guides
  Quick Guides

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. 2008 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

12 May 2008

page update: 9 Aug 07