CropTiffsWithShapefiles.sml

  Download

More scripts: Pipeline

Syntax Highlighing:

comments, key words, predefined symbols, class members & methods, functions & classes
            
## CropTiffsWithShapefiles.sml
#		Script to resample georeferenced airphoto TIFF files to
#		NAD83 / New Jersey State Plane Feet and crop to
#		a rectangular area in that coordinate system.  The
#		rectangular area for each  photo TIFF is defined by a 
#		rectangle in an accompanying shapefile.
#		The output for each input TIFF is a resampled and cropped
#		TIFF file (uncompressed, with no GeoTIFF tags) with
#		Arc World (.tfw) and .prj files.  A log file (text) is
#		created in the same directory as the output files to
#		record the processing parameters and result for each
#		TIFF file.
#		The script creates and opens a dialog window to allow the user
#		to create input and output directories and to provide status
#		information on the processing.
#		Assumptions and Conditions:
#		1) The user selects a single input directory containing both
#				TIFF and matching shape files
#		2) Input TIFF filenames begin with a unique 4-digit photo number.
#		3) The shapefile that matches a particular input TIFF has a filename that 
#				begins with the same 4-digit identifier as that TIFF file.
#		4) TIFF files that do not have a matching shape file in the input directory
#				are skipped during processing and reported in the script dialog and log file.
#		5) A TIFF file whose rectangle in the matching shape file is not fully contained
#				within the map extents of the TIFF file is skipped during processing and
#				reported in the log file.
#		5) Extra nonmatching shape files in the input directory are ignored.
#		6) All output TIFF files are placed in a single directory selected by the user.
#		7) Georeferenced input TIFFs need not be georeferenced to the same
#				coordinate reference system.  (i.e, TIFFs georeferenced to New Jersey
#				State Plane feet or meters will both work).
#		 Version 26 March 2009
#		 Requires TNT 2008:74 dated 30 December 2008 or later 
###############   Global Variables   #################
class STRING inDir$, outDir$;
numeric err;																		# error flag
class STRINGLIST inTiffList, inShapeList;			# lists of input TIFF and shape files
class STRING logfilename$;										# name of log file
class FILE logfile;
class GUI_DLG dlgwin;
class GUI_CTRL_PUSHBUTTON outDirBtn;
class GUI_CTRL_EDIT_STRING indirtext, outdirtext;
class GUI_CTRL_EDIT_NUMBER numTiff, numShape, numProc;
class GUI_CTRL_LISTBOX TIFFlistbox, Shapelistbox, Proclistbox;
class GUI_CTRL_EDIT_STRING statustext;
class GUI_CTRL_PUSHBUTTON runBtn, cancelBtn, closeBtn;
class STATUSDIALOG statusD;
class STATUSCONTEXT statusC;
###############   User-defined Procedures   #############
##########################################
# error checking procedure
proc ReportError(numeric linenum, numeric err) {
	printf("FAILED -line: %d, error: %d\n", linenum - 1, err);
	PopupError(err); 
	}
###########################################
### function to find matching shapefile and return its path string
func string findShapefile(class STRINGLIST list, class STRING id$)
	{
	local numeric j;
	local numeric shapefound = 0;
	local string foundShapename$;
	local class FILEPATH shapeName;			# filepath for the current shapefile
	local class STRING shapename$;			# shape filename
	local class STRING test$;							# first nine characters of the shape filename
	for j = 1 to list.GetNumItems()
		{
		shapeName = list[j-1];
		shapename$ = shapeName.GetName();
		test$ = shapename$.substr(0, 9);
		if (test$ == id$)
			{
			shapefound = 1;
			foundShapename$ = shapename$;
			}
		}
		if (shapefound)
			return foundShapename$;
		else
			return "failed";
	}	 # end findShapefile
############################
### procedure to get the output directory
proc GetOutDirectory()
	{
	outDir$ = GetDirectory("", "Select output directory:");
	class FILEPATH outDirPath(outDir$);
	outdirtext.SetValueStr(outDir$);		# write output directory name to dialog
	class DATETIME currentDT;
	currentDT.SetCurrent();
	# open log file for this run
	logfilename$ = sprintf("%s/%s.log", outDir$, outDirPath.GetNameOnly() );
	print(logfilename$);
	logfile = fopen(logfilename$, "a");
	fprint(logfile, "Log for TIFF file extraction and resampling.");
	fprintf(logfile, "\nProcessing initiated %s Local Time\n", currentDT);
	fprintf(logfile, "Input directory = %s\n", inDir$);
	fprintf(logfile, "Output directory = %s\n", outDir$);
	fprintf(logfile, "Number of tiff files found = %d\n", inTiffList.GetNumItems() );
	fprintf(logfile, "Number of shape files found = %d\n", inShapeList.GetNumItems() );
	# enable Run button
	runBtn.SetEnabled(1);
	}
############################
### procedure to get the input directory
proc GetInDirectory()
	{
	local numeric i, j;
	local numeric shapecount;
	inDir$ = GetDirectory("", "Select directory with TIFF and SHAPE files:");
	class FILEPATH inDirPath(inDir$);
	indirtext.SetValueStr(inDir$);				# write input directory name to dialog
	# clear dialog listboxes if they are still populated from a previous run
	if (TIFFlistbox.GetCount() > 0) then
		TIFFlistbox.DeleteAllItems();
	if (Shapelistbox.GetCount() > 0) then
			Shapelistbox.DeleteAllItems();
	if (Proclistbox.GetCount() > 0)
			{ 
			Proclistbox.DeleteAllItems();
			numProc.SetValueNum(0);
			}
	outDir$ = "";
	outdirtext.SetValueStr("");		# clear output directory text field 
	statustext.SetValueStr("");		# clear status message text field
	local class STRING tiffName$;			# filename of current TIFF file
	local class STRING id$;							# id$ = first nine characters of TIFF filename
	local class STRING shapefileName$;
	inTiffList = inDirPath.GetFileList("*.tif");
	inShapeList = inDirPath.GetFileList("*.shp");
	for i = 1 to 	inTiffList.GetNumItems()
		{
		TIFFlistbox.AddItem( inTiffList[i-1] );		# add TIFF filename to listbox in dialog
		# get first nine characters of TIFF filename as id to check for matching shapefile
		tiffName$ = inTiffList[i-1];
		id$ = tiffName$.substr(0, 9);
		shapefileName$ = findShapefile(inShapeList, id$);
		if (shapefileName$ <> "failed")		# matching shape file found
			{
			Shapelistbox.AddItem(shapefileName$);
			++shapecount;
			}
		else
			{
			statustext.SetValueStr( sprintf("No matching shapefile found for %s\n",   tiffName$) );
			}
		}
	# write number of files found to the dialog
	numTiff.SetValueNum(inTiffList.GetNumItems() );
	numShape.SetValueNum(shapecount);
	# enable Output Directory and Cancel buttons
	outDirBtn.SetEnabled(1);
	cancelBtn.SetEnabled(1);
	}
############################
### procedure called by the Close button
proc onClose()
	{
	fclose(logfile);
	dlgwin.Close(0);
	Exit();
	}
############################
### procedure called by the Close button
proc onCancel()
	{
	fclose(logfile);
	dlgwin.Close(0);
	Exit();
	}
############################
### procedure called by the Run button
proc onRun()
	{
	local numeric success;
	statusD.Create();
	statusD.SetSelfAsDefault();
	statusC = statusD.CreateContext();
	### set output Coordinate Reference System 
	### NAD83 / New Jersey State Plain ftUS
	class SR_DATUM datum;
	datum.Assign("2347");
	class SR_COORDSYS coordsys;
	coordsys.Assign("1202");
	class SR_COORDOPDEF projection;
	projection.Assign("25196");
	class SR_COORDREFSYS crsNJSPFT;
	crsNJSPFT.Create(coordsys, datum, projection);
	fprintf(logfile, "Output coordinate reference system = %s\n", crsNJSPFT.Name);
	###########################################
	### loop through the list of input TIFF files to perform extraction
	local numeric i;   # counter
	statusC.BarInit(inShapeList.GetNumItems(), 0);
	for i = 1 to inTiffList.GetNumItems()
		{
		fprintf(logfile, "\nTiff file %d: %s\n", i, inTiffList[i-1] );
		string shapepath$;			# full path string for the matching shapefile
		class STRING tiffName$ = inTiffList[i-1];	# filename of current TIFF file
		class STRING id$ = tiffName$.substr(0, 9);				# id$ = first four characters of TIFF filename
		# call custom function to find shapefile name with first 4 characters matching those of TIFF file
		shapepath$ = findShapefile(inShapeList, id$);
		if (shapepath$ == "failed")		# no matching shape file found
			{
			fprintf(logfile, "No matching shapefile found for %s\n",   tiffName$);
			statusC.Message = sprintf("No matching shapefile found for %s\n",   tiffName$);
			statustext.SetValueStr( sprintf("No matching shapefile found for %s\n",   tiffName$) );
			}
		else			# a shape file matches the current TIFF file
			{
			statusC.Message = sprintf("Processing %s...", tiffName$);
			statusC.BarIncrement(1, 0);
			statustext.SetValueStr(sprintf("Processing %s...", tiffName$) );
			++ success;
			# construct full filepath for matching shapefile 
			class FILEPATH shapeFilepath;
			shapeFilepath = inDirPath;
			shapeFilepath.Append(shapepath$);
			fprintf(logfile, "matching shapefile = %s\n", shapeFilepath);
			# construct full path for current TIFF file
			class FILEPATH tiffFilepath;
			tiffFilepath = inDirPath;
			tiffFilepath.Append(tiffName$);
			# Set up pipeline source for TIFF file
			class IMAGE_PIPELINE_SOURCE_TIFF sourceTIFF(tiffFilepath);
			err = sourceTIFF.Initialize();
			# get pipeline georeference from the source image
			class IMAGE_PIPELINE_GEOREFERENCE georefSource;		# georeference from source
			georefSource = sourceTIFF.GetGeoreference();
			# get coordinate reference system from source TIFF image
			class SR_COORDREFSYS crsSource;
			crsSource = georefSource.GetCRS(); 
			fprintf(logfile, "source TIFF coordinate reference system = %s\n", crsSource.Name);
			# get extents of the source image as a region
			class REGION2D sourceRegion;
			sourceTIFF.ComputeGeoreferenceRegion(sourceRegion);
			# get centroid of the extents region to use to compute source cell size
			class POINT2D centerPt;
			centerPt = sourceRegion.GetCentroid();
			fprintf(logfile, "Center point of TIFF: x = %.2f, y = %.2f\n", centerPt.x, centerPt.y);
			# get coordinate transformation from source image to its defined CRS
			class TRANS2D_MAPGEN transImgToCRS = georefSource.GetTransGen();
			# compute center point of source TIFF image in image coordinates
			class POINT2D centerPtImg;
			centerPtImg = transImgToCRS.ConvertPoint2DInv(centerPt);
			fprintf(logfile, "Center point of TIFF in image coordinates: x = %.1f, y = %.1f\n", centerPtImg.x, centerPtImg.y); 
			# compute cell sizes (scales) in meters at center of TIFF image from its georeference
			class POINT2D rastScale;
			georefSource.ComputeScale(centerPtImg, rastScale, 1);
			fprintf(logfile, "TIFF cell size (m): x = %.1f, y = %.1f\n", rastScale.x, rastScale.y);
			# convert cell sizes to US survey feet to use to find dimensions of output image from
			# the extents of the rectangle in the shape file
			numeric metersToFeet = GetUnitConvDist("m", "ft");
			rastScale.x = rastScale.x * metersToFeet;
			rastScale.y = rastScale.y * metersToFeet;
			fprintf(logfile, "TIFF cell size (ft): x = %.1f, y = %.1f\n", rastScale.x, rastScale.y);
			# convert source region to New Jersey State Plane feet if necessary
			if (sourceRegion.CoordRefSys.Name <> crsNJSPFT.Name)
				{
				fprintf(logfile, "Converting TIFF extents region %s to %s to test containment.\n", tiffName$, crsNJSPFT.Name);
				sourceRegion.ConvertTo(crsNJSPFT);
				}
			# open the shapefile
			class RVC_SHAPE shape;
			err = shape.OpenByName(shapeFilepath, shapeFilepath.GetName(), "Read");
			# get the georeference from the shapefile
			class RVC_GEOREFERENCE shapeGeoref;
			shape.GetDefaultGeoref(shapeGeoref);
			fprintf(logfile, "Shapefile coordinate reference system = %s\n", shapeGeoref.GetCoordRefSys().Name);
			# get the rectangle from the shapefile as a region and set its CRS
			class REGION2D clipRegion;
			clipRegion = shape.ReadPolygons(0);
			clipRegion.CoordRefSys =shapeGeoref.GetCoordRefSys();
			# convert extraction region to New Jersey State Plane feet if necessary
			if (clipRegion.CoordRefSys <> crsNJSPFT.Name)
				{
				fprintf(logfile, "Converting clip region for %s to %s\n", tiffName$, crsNJSPFT.Name);
				clipRegion.ConvertTo(crsNJSPFT);
				}
			### check extraction area
			if (sourceRegion.TestRegion(clipRegion, "FullInside") )
				{
				fprintf(logfile, "Source image %s contains the extraction area.\n", tiffName$);
				fprintf(logfile, "clipRegion minX = %.4f, maxX = %.4f\n", clipRegion.Extents.x1, clipRegion.Extents.x2);
				fprintf(logfile, "clipRegion minY = %.4f, maxY = %.4f\n", clipRegion.Extents.y1, clipRegion.Extents.y2);
				# compute image extract dimensions in raster lines and columns
				numeric numLins, numCols;
				numLins = abs(round( (clipRegion.Extents.y2 - clipRegion.Extents.y1) / rastScale.y));
				numCols = round( (clipRegion.Extents.x2 - clipRegion.Extents.x1) / rastScale.x);
				fprintf(logfile, "Image dimensions of extraction area: %d lines, %d columns\n", numLins, numCols);
				class IMAGE_PIPELINE_DIMENSIONS dimensions;
				dimensions.SetTotalDimensions(numCols, numLins);
				# set up affine georeference for target image
				class TRANS2D_AFFINE transAffine;
				transAffine.ApplyScale(rastScale.x, rastScale.y);
				transAffine.ApplyOffset(clipRegion.Extents.x1, clipRegion.Extents.y2);
				class IMAGE_PIPELINE_GEOREFERENCE targetGeoref;
				targetGeoref.SetTransAffine(crsNJSPFT, transAffine);
				# set up pipeline filter to resample & extract the image
				class IMAGE_PIPELINE_FILTER_RESAMPLE filterResample(sourceTIFF, dimensions, targetGeoref, "Nearest");
				err = filterResample.Initialize();
				if (err < 0) then ReportError(_context.CurrentLineNum, err);
				# construct filepath for output TIFF file
				class FILEPATH outFilepath(outDir$);
				outFilepath.Append(id$ + "crp.tif");
				fprintf(logfile, "Filepath for the output TIFF file = %s\n", outFilepath);
				# set up pipeline filter target and process
				class IMAGE_PIPELINE_TARGET_TIFF tiffTarget(filterResample, outFilepath, "ArcWorld");
				class IMAGE_PIPELINE_TARGET_TIFF_SETTINGS settings;
				settings.SetGeoTag("None");			# no GeoTiff tags
				tiffTarget.SetParms(settings);
				err = tiffTarget.Initialize();
				if (err < 0) then ReportError(_context.CurrentLineNum, err);
				err = tiffTarget.Process();
				if (err < 0) then ReportError(_context.CurrentLineNum, err);
				fprintf(logfile, "File %s successfully extracted.\n", tiffName$);
				numProc.SetValueNum(success);
				Proclistbox.AddItem(	outFilepath.GetName() );
				} # end: if (sourceRegion.TestRegion(clipRegion, "FullInside") )
			else
				{
				fprintf(logfile, "Extraction area for %s is outside the extents of the source image.  No extracted image created.\n", tiffName$); 
				}
			} # end: else a shapefile matches current tiff file
		} # end: 	for i = 1 to inTiffList.GetNumItems()
	statusD.Destroy();
	runBtn.SetEnabled(0);
	cancelBtn.SetEnabled(0);
	statustext.SetValueStr("Processing complete.");
	} # end of onRun()
##################################################
#################   Main Program   #####################
clear();
class STRING xml$='<?xml version="1.0"?>
<!DOCTYPE root SYSTEM "smlforms.dtd">
<root>
	<dialog id="dlgxtiff" Title="Crop/Resample TIFFs Using Shapefiles" Buttons="">
		<pane Orientation="Horizontal" HorizResize="Fixed">
			<pushbutton Name=" Input Directory " OnPressed="GetInDirectory()"/>
			<edittext id="indirtext" width="30" ReadOnly="true"/>
		</pane>
		<pane Orientation="Horizontal" HorizResize="Fixed">
			<pushbutton id="outDirBtn" Name=" Output Directory " Enabled="false" OnPressed="GetOutDirectory()"/>
			<edittext id="outdirtext" Width="30" ReadOnly="true"/>
		</pane>
		<groupbox ExtraBorder="3">
			<pane Orientation="Horizontal" HorizResize="Fixed">
				<pane Orientation="Vertical" HorizResize="Fixed" VertResize="Fixed">
					<listbox id="TIFFlistbox" Width="10"/>
					<pane Orientation="Horizontal" HorizResize="Fixed">
						<editnumber id="numTiff" Width="2" Precision="0" BlankZero="true" ReadOnly="true"/>
						<label>  TIFF files   </label>
					</pane>
				</pane>
				<pane Orientation="Vertical" HorizResize="Fixed" VertResize="Fixed">
					<listbox id="Shapelistbox" Width="15"/>
					<pane Orientation="Horizontal" HorizResize="Fixed">
						<editnumber id="numShape" Width="2" Precision="0" BlankZero="true" ReadOnly="true"/>
						<label>  matching shapefiles   </label>
					</pane>
				</pane>
				<pane Orientation="Vertical" HorizResize="Fixed" VertResize="Fixed">
					<listbox id="Proclistbox" Width="10"/>
					<pane Orientation="Horizontal" HorizResize="Fixed">
						<editnumber id="numProc" Width="2" Precision="0" BlankZero="true" ReadOnly="true"/>
						<label>  successfully processed</label>
					</pane>
				</pane>
			</pane>
		</groupbox>
		<pane Orientation="Horizontal" HorizAlign="left">
			<edittext id="statustext" Width="20" ReadOnly="true"/>
		</pane>
		<pane Orientation="Horizontal" HorizAlign="right">
			<pushbutton id="runBtn" Name=" Run " Enabled="false" OnPressed="onRun()"/>
			<pushbutton id="cancelBtn" Name=" Cancel " Enabled="false" OnPressed="onCancel()"/>
			<pushbutton id="closeBtn" Name=" Close " OnPressed="onClose()"/>
		</pane>
	</dialog>
</root>';
### parse XML text for the dialog into memory
class XMLDOC dlgdoc;
err = dlgdoc.Parse(xml$); 
### get the dialog element from the parsed XML document
class XMLNODE dlgnode;
dlgnode = dlgdoc.GetElementByID("dlgxtiff");
### set the dialog XML element as the source for the GUI_DLG class instance
dlgwin.SetXMLNode(dlgnode);
dlgwin.CreateModeless();
### get handles for dialog controls
outDirBtn = dlgwin.GetCtrlByID("outDirBtn");
indirtext = dlgwin.GetCtrlByID("indirtext");
outdirtext = dlgwin.GetCtrlByID("outdirtext");
numTiff = dlgwin.GetCtrlByID("numTiff");
numShape = dlgwin.GetCtrlByID("numShape");
numProc = dlgwin.GetCtrlByID("numProc");
TIFFlistbox = dlgwin.GetCtrlByID("TIFFlistbox");
Shapelistbox = dlgwin.GetCtrlByID("Shapelistbox");
Proclistbox = dlgwin.GetCtrlByID("Proclistbox");
statustext = dlgwin.GetCtrlByID("statustext");
runBtn = dlgwin.GetCtrlByID("runBtn");
cancelBtn = dlgwin.GetCtrlByID("cancelBtn");
closeBtn = dlgwin.GetCtrlByID("closeBtn");
dlgwin.Open();
WaitForExit();