Reading XPS Header and Data From the Same File

This snippet is a solution for an Igor user who wanted to use header information to set the scaling of a matrix loaded from an XPS file. This solution uses the technique of storing the header information in a list of keyword-value pairs and later querying that list for specific fields.

This simplified solution stems from https://www.wavemetrics.com/forum/general/loading-2d-data-matrix-its-fi…

For background information and links to other solutions, see https://www.wavemetrics.com/code-snippet/reading-header-and-data-same-f…

#pragma TextEncoding = "UTF-8"
#pragma rtGlobals=3				// Use modern global access method and strict wave access
#pragma DefaultTab={3,20,4}		// Set default tab width in Igor Pro 9 and later

// Reads header information and numeric data from a text file with a format like this:
//		# 
//		#  ImageWidth: 1376 pixel
//		#  ImageHeight: 1040 pixel
//		#  KineticEnergy: 14 eV
//		#  PassEnergy: 160 eV
//		#  EnergyScalingOffset: 3.44767 eV
//		#  EnergyScalingFactor: 0.0153488 eV/pixel
//		#  AzimuthalScalingOffset: 6.99327 deg
//		#  AzimuthalScalingFactor: -0.0134615 deg/pixel
//		#  
//		00003800	00002400	00001200	00003800	00002400	... (start of numeric data)

// The numeric data is matrix data to be loaded into a 2D wave.
// The user wanted to use the AzimuthalScalingOffset and AzimuthalScalingFactor
// fields to set the matrix X scaling and the EnergyScalingOffset and EnergyScalingFactor
// fields to set the matrix Y scaling. He also wanted to store all header information
// in the wave note.
// 
// To understand this file it is best to read it from the bottom (high-level) up.

// GetFieldNameAndValueStrFromHeaderLine(headerLine, fieldName, valueStr)
// headerLine is a possible line of header from the file.
static Function GetFieldNameAndValueStrFromHeaderLine(headerLine, fieldName, valueStr)
	String headerLine		// Input
	String& fieldName		// Output
	String& valueStr		// Output
	
	int lineLength = strlen(headerLine)
	
	String prefix = "#  "		// Header lines start with # followed by two spaces
	
	if (CmpStr(headerLine[0,2],prefix) != 0)
		return -1					// Not a header line
	endif
	headerLine = headerLine[3,lineLength]		// Strip prefix
	lineLength -= 3
	
	// Header lines after prefix start with "<Field Name>: "
	int pos = strsearch(headerLine, ": ", 0)
	if (pos < 0)
		// Did not find ": "
		return -1					// Failure
	endif
	
	fieldName = headerLine[0,pos-1]
	valueStr = headerLine[pos+2,lineLength-2]

	return 0					// Success
End

// ReadHeaderInfo(pathName, fileName, maxHeaderLines)
// Returns a list of field names and values as colon-separated keyword-value pairs.
static Function/S ReadHeaderInfo(pathName, fileName, maxHeaderLines)
	String pathName			// Name of symbolic path. Ignored if filePath is full path.
	String fileName			// File name, relative path or full path
	int maxHeaderLines		// Maximum lines of header at start of file

	Variable refNum
	Open/R/P=$pathName refNum as fileName
	if (refNum == 0)		// File not opened - bad pathName or fileName
		return ""
	endif
	
	String resultStr = ""
	
	int lineNumber = 0
	do
		String line
		FReadLine refNum, line
		if (strlen(line) == 0)
			break					// No more text in file
		endif
		
		String fieldName		// e.g., "EnergyScalingOffset"
		String valueStr			// e.g., "3.44767 eV"
		if (GetFieldNameAndValueStrFromHeaderLine(line, fieldName, valueStr) != 0)
			// No field on this line
			lineNumber += 1
			continue
		endif
		
		resultStr += fieldName + ":" + valueStr + ";"
		
		lineNumber += 1
		if (lineNumber >= maxHeaderLines)
			// There are no more header lines to examine
			break
		endif
	while(1)
	
	Close refNum
	
	return resultStr
End

// DemoReadXPSHeaderAndData(pathName, fileName)
// Demonstrates reading header information and data from a file like the "XPS Sample Data.txt" file.
// In this case, the file contains about 30 header lines followed by one column
// of numeric data. The user wants to use certain header fields to form the wave name.
//
// To display an Open File dialog, execute:
// 		DemoReadXPSHeaderAndData("", "")
//
// To load a file without an Open File dialog dialog, execute:
//		Create a symbolic path named "XPSData" and then execute
// 		DemoReadXPSHeaderAndData("XPSData", "XPS Sample Data.txt")
// If you are not familiar with Igor's "symbolic path" concept, execute this:
//		DisplayHelpTopic "Symbolic Paths"
// 
// If the wave already exists, this routine overwrites it.
Function DemoReadXPSHeaderAndData(pathName, fileName)
	String pathName		// Name of an Igor symbolic path or "" to get an Open File dialog
	String fileName		// Name of file or full path to file or "" to get an Open File dialog

	// If necessary, display an Open File dialog to get a valid reference to a file
	if ((strlen(pathName)==0) || (strlen(fileName)==0))
		// Display dialog looking for file
		Variable refNum
		String fileFilters = "Data Files (*.txt,*.dat,*.csv):.txt,.dat,.csv;"
		fileFilters += "All Files:.*;"
		Open /D /R  /F=fileFilters /P=$pathName refNum as fileName
		fileName = S_fileName				// S_fileName is set by Open/D
		if (strlen(fileName) == 0)		// User cancelled?
			return -1
		endif
	endif
	
	// Read header information
	String headerInfo = ReadHeaderInfo(pathName, fileName, 100)
	// Printf "headerInfo=%s\r"		// For debugging only
	
	// Load matrix wave
	LoadWave/G/P=$pathName/A=image/O/M/Q fileName
	if (V_Flag != 1)		// We expect 1 wave to be created
		return -1				// Failure
	endif
	
	// Create wave reference
	String name = StringFromList(0, S_waveNames)	// S_waveNames is created by LoadWave
	WAVE w = $name
	
	// Store header information in wave note
	String noteText = ReplaceString(";", headerInfo, "\r")	// Each field on separate line
	Note w, noteText
	
	// Set X scaling
	double x0 = NumberByKey("AzimuthalScalingOffset", headerInfo)
	double dx = NumberByKey("AzimuthalScalingFactor", headerInfo)
	SetScale/P x x0, dx, "eV", w
	
	// Set Y scaling
	double y0 = NumberByKey("EnergyScalingOffset", headerInfo)
	double dy = NumberByKey("EnergyScalingFactor", headerInfo)
	SetScale/P y y0, dy, "°", w
	
	return 0					// Success
End

 

Forum

Support

Gallery

Igor Pro 10

Learn More

Igor XOP Toolkit

Learn More

Igor NIDAQ Tools MX

Learn More