ftir-OMNIC-map-reader 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ftir_map/LoadOmnicArrayMAP.py +409 -0
- ftir_map/LoadOmnicMAP.py +97 -0
- ftir_map/PyMca_Array_OmnicMap.py +419 -0
- ftir_map/PyMca_OmnicMap.py +357 -0
- ftir_map/__about__.py +4 -0
- ftir_map/__init__.py +3 -0
- ftir_map/testpoint.py +45 -0
- ftir_omnic_map_reader-1.0.0.dist-info/METADATA +93 -0
- ftir_omnic_map_reader-1.0.0.dist-info/RECORD +12 -0
- ftir_omnic_map_reader-1.0.0.dist-info/WHEEL +5 -0
- ftir_omnic_map_reader-1.0.0.dist-info/licenses/LICENSE.txt +9 -0
- ftir_omnic_map_reader-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
# %%
|
|
2
|
+
# /*##########################################################################
|
|
3
|
+
# Modifed by Henry Towbin June 2023 to convert OMNIC .MAP files to Xarray datasets
|
|
4
|
+
# This allows the code work outside of the PyMca software package and enables
|
|
5
|
+
# analysis of the data in a wider set of python libraries.
|
|
6
|
+
|
|
7
|
+
# The main function Load_Omnic_Map(dir_path: str) takes a path to an omnic .MAP file
|
|
8
|
+
# and returns an xarray dataset object with the x,y coordiantes and spectral range
|
|
9
|
+
# of the map as dimensions.
|
|
10
|
+
#
|
|
11
|
+
# ============================================================================
|
|
12
|
+
# Original License information:
|
|
13
|
+
# The PyMca X-Ray Fluorescence Toolkit
|
|
14
|
+
#
|
|
15
|
+
# Copyright (c) 2004-2023 European Synchrotron Radiation Facility
|
|
16
|
+
#
|
|
17
|
+
# This file is part of the PyMca X-ray Fluorescence Toolkit developed at
|
|
18
|
+
# the ESRF.
|
|
19
|
+
#
|
|
20
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
21
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
22
|
+
# in the Software without restriction, including without limitation the rights
|
|
23
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
24
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
25
|
+
# furnished to do so, subject to the following conditions:
|
|
26
|
+
#
|
|
27
|
+
# The above copyright notice and this permission notice shall be included in
|
|
28
|
+
# all copies or substantial portions of the Software.
|
|
29
|
+
#
|
|
30
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
31
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
32
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
33
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
34
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
35
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
36
|
+
# THE SOFTWARE.
|
|
37
|
+
#
|
|
38
|
+
#############################################################################*/
|
|
39
|
+
# %%
|
|
40
|
+
__author__ = "V.A. Sole - ESRF"
|
|
41
|
+
__contact__ = "sole@esrf.fr"
|
|
42
|
+
__license__ = "MIT"
|
|
43
|
+
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
|
|
44
|
+
import os
|
|
45
|
+
import sys
|
|
46
|
+
import re
|
|
47
|
+
import struct
|
|
48
|
+
import numpy
|
|
49
|
+
import copy
|
|
50
|
+
import logging
|
|
51
|
+
import xarray as xr
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
_logger = logging.getLogger(__name__)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class DataObject:
|
|
58
|
+
"""
|
|
59
|
+
Simple container of an array and associated information.
|
|
60
|
+
Basically it has the members:
|
|
61
|
+
info: A dictionary
|
|
62
|
+
data: An array, usually 2D, 3D, ...
|
|
63
|
+
|
|
64
|
+
In the past also incorporated selection methods.
|
|
65
|
+
Now each different data source implements its selection methods.
|
|
66
|
+
|
|
67
|
+
Plotting routines may add additional members
|
|
68
|
+
|
|
69
|
+
x: A list containing arrays to be considered axes
|
|
70
|
+
y: A list of data to be considered as signals
|
|
71
|
+
m: A list containing the monitor data
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
def __init__(self):
|
|
75
|
+
"""
|
|
76
|
+
Default Constructor
|
|
77
|
+
"""
|
|
78
|
+
self.info = {}
|
|
79
|
+
self.data = numpy.array([])
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class OmnicMap(DataObject):
|
|
83
|
+
"""
|
|
84
|
+
Class to read OMNIC .map files
|
|
85
|
+
|
|
86
|
+
It reads the spectra into a DataObject instance.
|
|
87
|
+
This class info member contains all the parsed information.
|
|
88
|
+
This class data member contains the map itself as a 3D array.
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
def __init__(self, filename):
|
|
92
|
+
"""
|
|
93
|
+
Parameters:
|
|
94
|
+
-----------
|
|
95
|
+
filename : str
|
|
96
|
+
Name of the .map file.
|
|
97
|
+
It is expected to work with OMNIC versions 7.x and 8.x
|
|
98
|
+
"""
|
|
99
|
+
DataObject.__init__(self)
|
|
100
|
+
fid = open(filename, "rb")
|
|
101
|
+
data = fid.read()
|
|
102
|
+
fid.close()
|
|
103
|
+
fid = None
|
|
104
|
+
|
|
105
|
+
try:
|
|
106
|
+
omnicInfo = self._getOmnicInfo(data)
|
|
107
|
+
except Exception:
|
|
108
|
+
omnicInfo = None
|
|
109
|
+
self.sourceName = [filename]
|
|
110
|
+
if sys.version < "3.0":
|
|
111
|
+
searchedChain1 = ", XPos="
|
|
112
|
+
searchedChain2 = "Pixel "
|
|
113
|
+
else:
|
|
114
|
+
searchedChain1 = bytes(", XPos=", "utf-8")
|
|
115
|
+
searchedChain2 = bytes("Pixel ", "utf-8")
|
|
116
|
+
|
|
117
|
+
byteAnchor = data.index(searchedChain1)
|
|
118
|
+
firstByte = byteAnchor - (100-data[byteAnchor-100:byteAnchor+100].index(searchedChain2))
|
|
119
|
+
s = data[firstByte : (firstByte + 100 - 16)]
|
|
120
|
+
if sys.version >= "3.0":
|
|
121
|
+
s = str(s)
|
|
122
|
+
_logger.debug("firstByte = %d", firstByte)
|
|
123
|
+
_logger.debug("s1 = %s ", s)
|
|
124
|
+
exp = re.compile(r"(-?[0-9]+\.?[0-9]*)")
|
|
125
|
+
tmpValues = exp.findall(s)
|
|
126
|
+
pixelNumber = int(tmpValues[0])
|
|
127
|
+
# self.nSpectra = int(tmpValues[1]) # this doesnt exist in the array detector format
|
|
128
|
+
# if "XPos = " in s:
|
|
129
|
+
xPosition = float(tmpValues[1])
|
|
130
|
+
yPosition = float(tmpValues[2])
|
|
131
|
+
X_um = float(tmpValues[3])
|
|
132
|
+
Y_um = float(tmpValues[4])
|
|
133
|
+
|
|
134
|
+
# else:
|
|
135
|
+
# # I have to calculate them from the scan
|
|
136
|
+
# xPosition, yPosition = self.getPositionFromIndexAndInfo(0, omnicInfo)
|
|
137
|
+
_logger.debug(
|
|
138
|
+
"pixelNumber, nSpectra, xPosition, yPosition = %d %d %f %f",
|
|
139
|
+
pixelNumber,
|
|
140
|
+
# self.nSpectra,
|
|
141
|
+
xPosition,
|
|
142
|
+
yPosition,
|
|
143
|
+
)
|
|
144
|
+
if sys.version < "3.0":
|
|
145
|
+
chain = "Pixel"
|
|
146
|
+
else:
|
|
147
|
+
chain = bytes("Pixel", "utf-8")
|
|
148
|
+
secondByte = data[(firstByte + 1) :].index(chain)
|
|
149
|
+
secondByte += firstByte + 1
|
|
150
|
+
_logger.debug("secondByte = %s", secondByte)
|
|
151
|
+
self.nChannels = int((secondByte - firstByte - 100) / 4)
|
|
152
|
+
_logger.debug("nChannels = %d", self.nChannels)
|
|
153
|
+
self.firstSpectrumOffset = firstByte - 16
|
|
154
|
+
|
|
155
|
+
# fill the header
|
|
156
|
+
self.header = []
|
|
157
|
+
oldXPosition = xPosition
|
|
158
|
+
oldYPosition = yPosition
|
|
159
|
+
self.nRows = 0
|
|
160
|
+
xPosition_list = []
|
|
161
|
+
yPosition_list = []
|
|
162
|
+
x_list = []
|
|
163
|
+
y_list = []
|
|
164
|
+
pixelNumber_list = []
|
|
165
|
+
|
|
166
|
+
# xPositions = numpy.zeros(self.nSpectra)
|
|
167
|
+
# yPositions = numpy.zeros(self.nSpectra)
|
|
168
|
+
calculating = True
|
|
169
|
+
# offset = int(firstByte + i * (100 + self.nChannels * 4))
|
|
170
|
+
|
|
171
|
+
# for i in range(self.nSpectra):
|
|
172
|
+
i = 0
|
|
173
|
+
offset = 0
|
|
174
|
+
# Length of spectral data is not known
|
|
175
|
+
# so I have to read until the end of the file
|
|
176
|
+
# or until I find a problem
|
|
177
|
+
while offset < len(data):
|
|
178
|
+
try:
|
|
179
|
+
offset = int(firstByte + i * (100 + self.nChannels * 4))
|
|
180
|
+
if sys.version < "3.0":
|
|
181
|
+
s = data[offset : (offset + 100 - 16)]
|
|
182
|
+
else:
|
|
183
|
+
s = str(data[offset : (offset + 100 - 16)])
|
|
184
|
+
tmpValues = exp.findall(s)
|
|
185
|
+
pixelNumber = int(tmpValues[0])
|
|
186
|
+
if "XPos = " in s:
|
|
187
|
+
xPosition = float(tmpValues[1])
|
|
188
|
+
yPosition = float(tmpValues[2])
|
|
189
|
+
X_um = float(tmpValues[3])
|
|
190
|
+
Y_um = float(tmpValues[4])
|
|
191
|
+
else:
|
|
192
|
+
# I have to calculate them from the scan
|
|
193
|
+
xPosition, yPosition = self.getPositionFromIndexAndInfo(i, omnicInfo)
|
|
194
|
+
xPosition_list.append(xPosition)
|
|
195
|
+
yPosition_list.append(yPosition)
|
|
196
|
+
x_list.append(X_um)
|
|
197
|
+
y_list.append(Y_um)
|
|
198
|
+
pixelNumber_list.append(pixelNumber)
|
|
199
|
+
|
|
200
|
+
i += 1
|
|
201
|
+
|
|
202
|
+
if calculating:
|
|
203
|
+
if (abs(yPosition - oldYPosition) > 1.0e-6) and (
|
|
204
|
+
abs(xPosition - oldXPosition) < 1.0e-6
|
|
205
|
+
):
|
|
206
|
+
calculating = False
|
|
207
|
+
continue
|
|
208
|
+
self.nRows += 1
|
|
209
|
+
except Exception:
|
|
210
|
+
break
|
|
211
|
+
|
|
212
|
+
self.nSpectra = i
|
|
213
|
+
xPositions = numpy.array(xPosition_list)
|
|
214
|
+
yPositions = numpy.array(yPosition_list)
|
|
215
|
+
x_list = numpy.array(x_list)
|
|
216
|
+
y_list = numpy.array(y_list)
|
|
217
|
+
self.xPositions = xPositions
|
|
218
|
+
self.yPositions = yPositions
|
|
219
|
+
self.x_list = x_list
|
|
220
|
+
self.y_list = y_list
|
|
221
|
+
self.pixelNumber_list = numpy.array(pixelNumber_list)
|
|
222
|
+
|
|
223
|
+
_logger.debug(
|
|
224
|
+
"DIMENSIONS X = %f Y=%d", self.nSpectra * 1.0 / self.nRows, self.nRows
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
# arrange as an EDF Stack
|
|
228
|
+
self.info = {}
|
|
229
|
+
self.__nFiles = int(self.nSpectra / self.nRows)
|
|
230
|
+
try:
|
|
231
|
+
deltaX = (xPositions[-1] - xPositions[0]) / (self.nRows - 1)
|
|
232
|
+
deltaY = (yPositions[-1] - yPositions[0]) / (self.__nFiles - 1)
|
|
233
|
+
except Exception:
|
|
234
|
+
deltaX = None
|
|
235
|
+
deltaY = None
|
|
236
|
+
_logger.warning("Cannot calculate scales")
|
|
237
|
+
self.data = numpy.zeros(
|
|
238
|
+
(self.__nFiles, self.nRows, self.nChannels), dtype=numpy.float32
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
self.__nImagesPerFile = 1
|
|
242
|
+
offset = firstByte - 16 + 100 # starting position of the data
|
|
243
|
+
delta = 100 + self.nChannels * 4
|
|
244
|
+
fmt = "%df" % self.nChannels
|
|
245
|
+
for i in range(self.__nFiles):
|
|
246
|
+
for j in range(self.nRows):
|
|
247
|
+
# this approach is inneficient when compared to a direct
|
|
248
|
+
# data readout, but it allows to deal with nan at the source
|
|
249
|
+
tmpData = numpy.zeros((self.nChannels,), dtype=numpy.float32)
|
|
250
|
+
tmpData[:] = struct.unpack(fmt, data[offset : (offset + delta - 100)])
|
|
251
|
+
finiteData = numpy.isfinite(tmpData)
|
|
252
|
+
self.data[i, j, finiteData] = tmpData[finiteData]
|
|
253
|
+
offset = int(offset + delta)
|
|
254
|
+
shape = self.data.shape
|
|
255
|
+
for i in range(len(shape)):
|
|
256
|
+
key = "Dim_%d" % (i + 1,)
|
|
257
|
+
self.info[key] = shape[i]
|
|
258
|
+
|
|
259
|
+
# self.info["SourceType"] = SOURCE_TYPE
|
|
260
|
+
self.info["SourceName"] = self.sourceName
|
|
261
|
+
self.info["Size"] = self.__nFiles * self.__nImagesPerFile
|
|
262
|
+
self.info["NumberOfFiles"] = self.__nFiles * 1
|
|
263
|
+
self.info["FileIndex"] = 0
|
|
264
|
+
self.info["Channel0"] = 0.0
|
|
265
|
+
if omnicInfo is not None:
|
|
266
|
+
self.info["McaCalib"] = [
|
|
267
|
+
omnicInfo["First X value"] * 1.0,
|
|
268
|
+
omnicInfo["Data spacing"] * 1.0,
|
|
269
|
+
0.0,
|
|
270
|
+
]
|
|
271
|
+
else:
|
|
272
|
+
self.info["McaCalib"] = [0.0, 1.0, 0.0]
|
|
273
|
+
self.info["OmnicInfo"] = omnicInfo
|
|
274
|
+
if deltaX and deltaY:
|
|
275
|
+
if (deltaX > 0.0) and (deltaY > 0.0):
|
|
276
|
+
self.info["xScale"] = [oldXPosition, deltaX]
|
|
277
|
+
self.info["yScale"] = [oldYPosition, deltaY]
|
|
278
|
+
self.info["positioners"] = {"X": xPositions, "Y": yPositions}
|
|
279
|
+
|
|
280
|
+
def _getOmnicInfo(self, data):
|
|
281
|
+
"""
|
|
282
|
+
Parameters:
|
|
283
|
+
-----------
|
|
284
|
+
data : The contents of the .map file
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
--------
|
|
288
|
+
A dictionary with acquisition information
|
|
289
|
+
"""
|
|
290
|
+
# additional information
|
|
291
|
+
fmt = "I" # unsigned long in 32-bit
|
|
292
|
+
offset = 372 # 93*4 unsigned integers
|
|
293
|
+
infoBlockIndex = (
|
|
294
|
+
struct.unpack(fmt, data[offset : (offset + 4)])[0] - 204
|
|
295
|
+
) / 4.0
|
|
296
|
+
infoBlockIndex = int(infoBlockIndex)
|
|
297
|
+
# infoblock is the position of the information block
|
|
298
|
+
offset = infoBlockIndex * 4
|
|
299
|
+
# read 13 unsigned integers
|
|
300
|
+
nValues = 13
|
|
301
|
+
fmt = "%dI" % nValues
|
|
302
|
+
values = struct.unpack(fmt, data[offset : (offset + 4 * nValues)])
|
|
303
|
+
ddict = {}
|
|
304
|
+
ddict["Number of points"] = values[0]
|
|
305
|
+
ddict["Number of scan points"] = values[6]
|
|
306
|
+
ddict["Interferogram peak position"] = values[7]
|
|
307
|
+
ddict["Number of sample scans"] = values[8]
|
|
308
|
+
ddict["Number of FFT points"] = values[10]
|
|
309
|
+
ddict["Number of background scans"] = values[12]
|
|
310
|
+
offset = (infoBlockIndex + 3) * 4
|
|
311
|
+
nFloats = 47
|
|
312
|
+
fmt = "%df" % nFloats
|
|
313
|
+
vFloats = struct.unpack(fmt, data[offset : (offset + 4 * nFloats)])
|
|
314
|
+
lastX = vFloats[0]
|
|
315
|
+
firstX = vFloats[1]
|
|
316
|
+
ddict["First X value"] = firstX
|
|
317
|
+
ddict["Last X value"] = lastX
|
|
318
|
+
ddict["Identifier for start indices of spectra"] = vFloats[14]
|
|
319
|
+
ddict["Laser frequency"] = vFloats[16]
|
|
320
|
+
ddict["Data spacing"] = (lastX - firstX) / (ddict["Number of points"] - 1.0)
|
|
321
|
+
ddict["Background gain"] = vFloats[10]
|
|
322
|
+
for key in ddict.keys():
|
|
323
|
+
_logger.debug("%s: %s", key, ddict[key])
|
|
324
|
+
ddict.update(self.getMapInformation(data))
|
|
325
|
+
return ddict
|
|
326
|
+
|
|
327
|
+
def getMapInformation(self, data):
|
|
328
|
+
"""
|
|
329
|
+
Internal method to help finding spectra coordinates
|
|
330
|
+
Parameters:
|
|
331
|
+
-----------
|
|
332
|
+
data : Contents of the .map file
|
|
333
|
+
|
|
334
|
+
Returns:
|
|
335
|
+
--------
|
|
336
|
+
Dictionary with map gemoetrical acquisition parameters
|
|
337
|
+
"""
|
|
338
|
+
# look for the chain 'Position'
|
|
339
|
+
if sys.version < "3.0":
|
|
340
|
+
chain = "Position"
|
|
341
|
+
else:
|
|
342
|
+
chain = bytes("Position", "utf-8")
|
|
343
|
+
offset = data.index(chain)
|
|
344
|
+
positions = [offset]
|
|
345
|
+
while True:
|
|
346
|
+
try:
|
|
347
|
+
a = data[(offset + 1) :].index(chain)
|
|
348
|
+
offset = a + offset + 1
|
|
349
|
+
positions.append(offset)
|
|
350
|
+
except ValueError:
|
|
351
|
+
break
|
|
352
|
+
|
|
353
|
+
ddict = {}
|
|
354
|
+
# map description position
|
|
355
|
+
if (positions[1] - positions[0]) == 66: # reverse engineered magic number :-)
|
|
356
|
+
mapDescriptionOffset = positions[0] - 90
|
|
357
|
+
mapDescription = struct.unpack(
|
|
358
|
+
"6f", data[mapDescriptionOffset : mapDescriptionOffset + 24]
|
|
359
|
+
)
|
|
360
|
+
y0, y1, deltaY, x0, x1, deltaX = mapDescription
|
|
361
|
+
ddict["First map location"] = [x0, y0]
|
|
362
|
+
ddict["Last map location"] = [x1, y1]
|
|
363
|
+
ddict["Mapping stage X step size"] = deltaX
|
|
364
|
+
ddict["Mapping stage Y step size"] = deltaY
|
|
365
|
+
ddict["Number of spectra"] = abs(
|
|
366
|
+
(1 + ((y1 - y0) / deltaY)) * (1 + ((x1 - x0) / deltaX))
|
|
367
|
+
)
|
|
368
|
+
for key in ddict.keys():
|
|
369
|
+
_logger.debug("%s: %s", key, ddict[key])
|
|
370
|
+
return ddict
|
|
371
|
+
|
|
372
|
+
def getOmnicInfo(self):
|
|
373
|
+
"""
|
|
374
|
+
Returns a dictionary with the parsed OMNIC information
|
|
375
|
+
"""
|
|
376
|
+
return copy.deepcopy(self.info["OmnicInfo"])
|
|
377
|
+
|
|
378
|
+
def getPositionFromIndexAndInfo(self, index, info=None):
|
|
379
|
+
"""
|
|
380
|
+
Internal method to obtain the position at which a spectrum
|
|
381
|
+
was acquired
|
|
382
|
+
Parameters:
|
|
383
|
+
-----------
|
|
384
|
+
index : int
|
|
385
|
+
Index of spectrum
|
|
386
|
+
info : Dictionary
|
|
387
|
+
Information recovered with _getOmnicInfo
|
|
388
|
+
Returns:
|
|
389
|
+
--------
|
|
390
|
+
x, y : floats
|
|
391
|
+
Position at which the spectrum was acquired.
|
|
392
|
+
"""
|
|
393
|
+
if info is None:
|
|
394
|
+
return 0.0, 0.0
|
|
395
|
+
ddict = info
|
|
396
|
+
# first variation on X and then on Y
|
|
397
|
+
try:
|
|
398
|
+
x0, y0 = ddict["First map location"]
|
|
399
|
+
except KeyError:
|
|
400
|
+
return 0.0, 0.0
|
|
401
|
+
x1, y1 = ddict["Last map location"]
|
|
402
|
+
deltaX = ddict["Mapping stage X step size"]
|
|
403
|
+
deltaY = ddict["Mapping stage Y step size"]
|
|
404
|
+
nX = int(1.5 + ((x1 - x0) / deltaX))
|
|
405
|
+
nY = int(1.5 + ((y1 - y0) / deltaY))
|
|
406
|
+
x = x0 + (index % nX) * deltaX
|
|
407
|
+
y = y0 + int(index / nX) * deltaY
|
|
408
|
+
|
|
409
|
+
return x, y
|
ftir_map/LoadOmnicMAP.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#%%
|
|
2
|
+
import numpy
|
|
3
|
+
import xarray as xr
|
|
4
|
+
# try:
|
|
5
|
+
# from .PyMca_Array_OmnicMap import OmnicArrayMap
|
|
6
|
+
# from .PyMca_OmnicMap import OmnicMap
|
|
7
|
+
# except ImportError:
|
|
8
|
+
# from PyMca_Array_OmnicMap import OmnicArrayMap
|
|
9
|
+
# from PyMca_OmnicMap import OmnicMap
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
from . import PyMca_Array_OmnicMap
|
|
13
|
+
from . import PyMca_OmnicMap
|
|
14
|
+
except ImportError:
|
|
15
|
+
import PyMca_Array_OmnicMap
|
|
16
|
+
import PyMca_OmnicMap
|
|
17
|
+
|
|
18
|
+
#%%
|
|
19
|
+
def Load_Omnic_Map(dir_path: str, format: str = "Xarray-Dataset") -> xr.Dataset:
|
|
20
|
+
"""_summary_
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
dir_path (str): _description_
|
|
24
|
+
format (str, optional): _description_. Defaults to "Xarray-Dataset". Other Options are "Xarray-DataArray" and "Dictionary".
|
|
25
|
+
|
|
26
|
+
Raises:
|
|
27
|
+
ValueError: Errors if the format is not supported.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
xr.Dataset: Dataset containing the spectral data, coordinates, and metadata.
|
|
31
|
+
xr.DataArray: DataArray containing the spectral data.
|
|
32
|
+
dict: Dictionary containing the spectral data, coordinates, and metadata.
|
|
33
|
+
"""
|
|
34
|
+
try:
|
|
35
|
+
mapfile = PyMca_OmnicMap.OmnicMap(dir_path)
|
|
36
|
+
except Exception as e:
|
|
37
|
+
print(f"Error loading map file: {e}")
|
|
38
|
+
print("Trying to load as OmnicArrayMap...")
|
|
39
|
+
try:
|
|
40
|
+
mapfile = PyMca_Array_OmnicMap.OmnicArrayMap(dir_path)
|
|
41
|
+
print("Loaded as OmnicArrayMap.")
|
|
42
|
+
except Exception as e:
|
|
43
|
+
print(f"Error loading map file as OmnicArrayMap: {e}")
|
|
44
|
+
print("Unable to load the file.")
|
|
45
|
+
return None
|
|
46
|
+
|
|
47
|
+
wn0 = mapfile.info["OmnicInfo"]['First X value'] # cm^-1
|
|
48
|
+
wn1 = mapfile.info["OmnicInfo"]['Last X value'] # cm^-1
|
|
49
|
+
|
|
50
|
+
unique_x = numpy.unique(mapfile.info["positioners"]["X"])
|
|
51
|
+
unique_y = numpy.unique(mapfile.info["positioners"]["Y"])
|
|
52
|
+
|
|
53
|
+
x_coords = unique_x - unique_x[0] #µm
|
|
54
|
+
y_coords = unique_y - unique_y[0] #µm
|
|
55
|
+
|
|
56
|
+
len_wn = mapfile.data.shape[2]
|
|
57
|
+
wn = numpy.linspace(wn0, wn1, len_wn) #cm^-1
|
|
58
|
+
|
|
59
|
+
unit_names = {"x": "um", "y": "um", "wn": "cm^-1", "data": "absorbance"}
|
|
60
|
+
unit_long_names = {"x": "microns", "y": "microns", "wn": "wavenumbers", "data": "absorbance"}
|
|
61
|
+
metadata = {"unit_names": unit_names, "unit_long_names": unit_long_names, **mapfile.info}
|
|
62
|
+
|
|
63
|
+
# Reshape the data to match the unique x and y coordinates
|
|
64
|
+
# This assumes that the data is structured in a way that corresponds to the unique x and y coordinates.
|
|
65
|
+
lenX = len(unique_x)
|
|
66
|
+
lenY = len(unique_y)
|
|
67
|
+
reshaped = mapfile.data.reshape(lenY, lenX, len_wn)
|
|
68
|
+
data = {"spectra": (["y", "x", "wn"], reshaped)}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
coords = {
|
|
72
|
+
"x": x_coords,
|
|
73
|
+
"y": y_coords,
|
|
74
|
+
"wn": wn,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
dataset = xr.Dataset(
|
|
78
|
+
data_vars= data,
|
|
79
|
+
coords= coords,
|
|
80
|
+
attrs= metadata
|
|
81
|
+
)
|
|
82
|
+
if format == "Xarray-Dataset":
|
|
83
|
+
return dataset
|
|
84
|
+
elif format == "Xarray-DataArray":
|
|
85
|
+
return dataset["spectra"]
|
|
86
|
+
elif format == "Dictionary":
|
|
87
|
+
return {
|
|
88
|
+
"data": dataset["spectra"].values,
|
|
89
|
+
"x": dataset["x"].values,
|
|
90
|
+
"y": dataset["y"].values,
|
|
91
|
+
"wn": dataset["wn"].values,
|
|
92
|
+
"metadata": metadata
|
|
93
|
+
}
|
|
94
|
+
else:
|
|
95
|
+
raise ValueError(f"Unsupported format: {format}. Supported formats are 'Xarray-Dataset', 'Xarray-DataArray', and 'Dictionary'.")
|
|
96
|
+
|
|
97
|
+
# %%
|