Extent JPK driver to also read .jpk-data1D-file files.
authorW. Trevor King <wking@drexel.edu>
Thu, 9 Sep 2010 11:37:03 +0000 (07:37 -0400)
committerW. Trevor King <wking@drexel.edu>
Thu, 9 Sep 2010 11:37:03 +0000 (07:37 -0400)
Also:
* Lowercase most JPK exception messages.
* Decode integer types to floats as specified by 'encoder'.
* Give the path to the old file when suggeesting an `out2jpk-force` upgrade.

hooke/driver/jpk.py

index 71af831b0be25e1872c807008344e7d210e18e19..6fe84f01d3860e7b0df45a8b0e212448168ce228 100644 (file)
@@ -74,6 +74,12 @@ class JPKDriver (Driver):
             f.path = path
             zip_info = self._zip_info(f)
             version = zip_info['file-format-version']
+            if zip_info['jpk-data-file'] == 'jpk-data1D-file':
+                return self._zip_read_1d(
+                    f, path, info, zip_info, version)
+            elif zip_info['jpk-data-file'] != 'spm-forcefile':
+                raise ValueError('unrecognized JPK file type "%s"'
+                                 % zip_info['jpk-data-file'])
             segments = []
             for i in range(len([p for p in f.namelist()
                                 if p.endswith('segment-header.properties')])):
@@ -121,7 +127,7 @@ class JPKDriver (Driver):
                     zipfile, index, chan, chan_info))
             if channels[-1].shape != expected_shape:
                 raise NotImplementedError(
-                    'Channel %d:%s in %s has strange shape %s != %s'
+                    'channel %d:%s in %s has strange shape %s != %s'
                     % (index, chan, zipfile.path,
                        channels[-1].shape, expected_shape))
         if len(channels) > 0:
@@ -145,11 +151,13 @@ class JPKDriver (Driver):
                     'segments', str(segment_index),
                     chan_info['data']['file']['name']), 'r')) as f:
             assert chan_info['data']['file']['format'] == 'raw', \
-                'Non-raw data format:\n%s' % pprint.pformat(chan_info)
+                'non-raw data format:\n%s' % pprint.pformat(chan_info)
             dtype = self._zip_channel_dtype(chan_info)
             data = numpy.frombuffer(
                 buffer(f.read()),
                 dtype=dtype,)
+        if dtype.kind in ['i', 'u']:
+            data = self._zip_channel_decode(data, chan_info)
         return data
 
     def _zip_calculate_channel(self, chan_info):
@@ -168,7 +176,7 @@ class JPKDriver (Driver):
                 step=step,
                 dtype=numpy.float32)
         else:
-            raise ValueError('Unrecognized data format "%s"' % type_)
+            raise ValueError('unrecognized data format "%s"' % type_)
 
     def _zip_channel_dtype(self, chan_info):
         type_ = chan_info['data']['type']
@@ -181,7 +189,7 @@ class JPKDriver (Driver):
             elif encoder in ['unsignedinteger', 'unsignedinteger-limited']:
                 dtype = numpy.dtype(numpy.uint32)
             else:
-                raise ValueError('Unrecognized encoder type "%s" for "%s" data'
+                raise ValueError('unrecognized encoder type "%s" for "%s" data'
                                  % (encoder, type_))
         elif type_ in ['short-data', 'short', 'memory-short-data']:
             encoder = chan_info['data']['encoder']['type']
@@ -190,10 +198,10 @@ class JPKDriver (Driver):
             elif encoder in ['unsignedshort', 'unsignedshort-limited']:
                 dtype = numpy.dtype(numpy.uint16)
             else:
-                raise ValueError('Unrecognized encoder type "%s" for "%s" data'
+                raise ValueError('unrecognized encoder type "%s" for "%s" data'
                                  % (encoder, type_))
         else:
-            raise ValueError('Unrecognized data format "%s"' % type_)
+            raise ValueError('unrecognized data format "%s"' % type_)
         byte_order = '>'
         # '>' (big endian) byte order.
         # From version 0.3 of JPKForceSpec.txt in the "Binary data" section:
@@ -206,6 +214,10 @@ class JPKDriver (Driver):
         #    float (4 bytes, IEEE format).
         return dtype.newbyteorder(byte_order)
 
+    def _zip_channel_decode(self, data, encoder_info):
+        return self._zip_apply_channel_scaling(
+            data, encoder_info['data']['encoder'])
+
     def _zip_translate_params(self, params, chan_info, version):
         info = {
             'raw info':params,
@@ -328,16 +340,20 @@ class JPKDriver (Driver):
             pass  # Fall through to 'simple' conversion processing.
         else:
             assert conversion_info['type'] == 'simple', conversion_info['type']
+        segment[:,channel] = self._zip_apply_channel_scaling(
+            segment[:,channel], conversion_info)
+        unit = self._zip_unit(conversion_info['scaling'], version)
+        segment.info['columns'][channel] = join_data_label(channel_name, unit)
+        return segment
+
+    def _zip_apply_channel_scaling(self, channel_data, conversion_info):
         assert conversion_info['scaling']['type'] == 'linear', \
             conversion_info['scaling']['type']
         assert conversion_info['scaling']['style'] == 'offsetmultiplier', \
             conversion_info['scaling']['style']
         multiplier = float(conversion_info['scaling']['multiplier'])
         offset = float(conversion_info['scaling']['offset'])
-        unit = self._zip_unit(conversion_info['scaling'], version)
-        segment[:,channel] = segment[:,channel] * multiplier + offset
-        segment.info['columns'][channel] = join_data_label(channel_name, unit)
-        return segment
+        return channel_data * multiplier + offset
 
     def _zip_unit(self, conversion_info, version):
         if version in ['0.%d' % i for i in range(3)]:
@@ -370,8 +386,34 @@ class JPKDriver (Driver):
                     sub_info[setting[-1]] = fields[1]
         return info
 
+    def _zip_read_1d(self, zipfile, path, info, zip_info, version):
+        expected_shape = (int(zip_info['data']['num-points']),)
+        if zip_info['data']['type'] in ['constant-data', 'raster-data']:
+            return self._zip_calculate_channel(zip_info)
+        with Closing(zipfile.open(
+                zip_info['data']['file']['name'], 'r')) as f:
+            assert zip_info['data']['file']['format'] == 'raw', \
+                'non-raw data format:\n%s' % pprint.pformat(chan_info)
+            dtype = self._zip_channel_dtype(zip_info)
+            data = numpy.frombuffer(
+                buffer(f.read()),
+                dtype=dtype,)
+            if dtype.kind in ['i', 'u']:
+                data = self._zip_channel_decode(data, zip_info)
+        if data.shape != expected_shape:
+            raise NotImplementedError(
+                'channel %s has strange shape %s != %s'
+                % (path, data.shape, expected_shape))
+        d = curve.Data(
+            shape=data.shape,
+            dtype=data.dtype,
+            info=zip_info)
+        d[:] = data
+        return d
+
     def _read_old(self, path, info):
         raise NotImplementedError(
             "Early JPK files (pre-zip) are not supported by Hooke.  Please "
             "use JPK's `out2jpk-force` script to convert your old files "
-            "to a more recent format before loading them with Hooke.")
+            "(%s) to a more recent format before loading them with Hooke."
+            % path)