LASFile   F
last analyzed

Complexity

Total Complexity 138

Size/Duplication

Total Lines 709
Duplicated Lines 0 %

Importance

Changes 8
Bugs 5 Features 1
Metric Value
dl 0
loc 709
rs 1.891
c 8
b 5
f 1
wmc 138

39 Methods

Rating   Name   Duplication   Size   Complexity  
A add_curve_raw() 0 3 1
A itervalues() 0 2 1
A items() 0 3 2
A iteritems() 0 2 1
A iterkeys() 0 2 1
A add_curve() 0 3 1
A values() 0 3 2
A data() 0 3 1
A keys() 0 3 2
A other() 0 9 1
A set_data_from_df() 0 13 4
A version() 0 9 1
C set_data() 0 46 10
A append_curve() 0 14 1
A write() 0 24 4
A __init__() 0 15 2
A metadata() 0 13 1
A well() 0 9 1
A delete_curve() 0 13 2
F read() 0 173 34
A append_curve_item() 0 8 1
B match_raw_section() 0 26 5
A curvesdict() 0 12 2
A json() 0 10 1
A __getitem__() 0 18 4
A add_section() 0 12 3
A df() 0 7 3
A get_curve() 0 13 3
A depth_ft() 0 10 3
A params() 0 9 1
A depth_m() 0 10 3
A curves() 0 9 1
A insert_curve() 0 16 1
A insert_curve_item() 0 10 2
A header() 0 9 1
A to_excel() 0 13 1
A index() 0 6 1
A __setitem__() 0 19 3
F to_csv() 0 51 16

How to fix   Complexity   

Complex Class

Complex classes like LASFile often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
from __future__ import print_function
2
3
# Standard library packages
4
import codecs
5
import csv
6
import json
7
import logging
8
import os
9
import re
10
import textwrap
11
import traceback
12
13
# get basestring in py3
14
15
try:
16
    unicode = unicode
17
except NameError:
18
    # 'unicode' is undefined, must be Python 3
19
    unicode = str
20
    basestring = (str, bytes)
21
else:
22
    # 'unicode' exists, must be Python 2
23
    bytes = str
24
    basestring = basestring
25
26
# Required third-party packages available on PyPi:
27
28
import numpy as np
29
30
# internal lasio imports
31
32
from . import exceptions
33
from .las_items import (
34
    HeaderItem, CurveItem, SectionItems, OrderedDict)
35
from . import defaults
36
from . import reader
37
from . import writer
38
39
logger = logging.getLogger(__name__)
40
41
42
class LASFile(object):
43
44
    '''LAS file object.
45
46
    Keyword Arguments:
47
        file_ref (file-like object, str): either a filename, an open file 
48
            object, or a string containing the contents of a file.
49
50
    See these routines for additional keyword arguments you can use when
51
    reading in a LAS file:
52
53
    * :func:`lasio.reader.open_with_codecs` - manage issues relate to character
54
      encodings
55
    * :meth:`lasio.las.LASFile.read` - control how NULL values and errors are
56
      handled during parsing
57
58
    Attributes:
59
        encoding (str or None): the character encoding used when reading the
60
            file in from disk
61
62
    '''
63
64
    def __init__(self, file_ref=None, **read_kwargs):
65
        super(LASFile, self).__init__()
66
        self._text = ''
67
        self.index_unit = None
68
        default_items = defaults.get_default_items()
69
        self.sections = {
70
            'Version': default_items['Version'],
71
            'Well': default_items['Well'],
72
            'Curves': default_items['Curves'],
73
            'Parameter': default_items['Parameter'],
74
            'Other': str(default_items['Other']),
75
        }
76
77
        if not (file_ref is None):
78
            self.read(file_ref, **read_kwargs)
79
80
    def read(self, file_ref, 
81
             ignore_data=False, read_policy='default', null_policy='strict',
82
             ignore_header_errors=False, mnemonic_case='upper', 
83
             **kwargs):
84
        '''Read a LAS file.
85
86
        Arguments:
87
            file_ref (file-like object, str): either a filename, an open file 
88
                object, or a string containing the contents of a file.
89
90
        Keyword Arguments:
91
            null_policy (str or list): see 
92
                http://lasio.readthedocs.io/en/latest/data-section.html#handling-invalid-data-indicators-automatically
93
            ignore_data (bool): if True, do not read in any of the actual data, 
94
                just the header metadata. False by default.
95
            ignore_header_errors (bool): ignore LASHeaderErrors (False by 
96
                default)
97
            mnemonic_case (str): 'preserve': keep the case of HeaderItem mnemonics
98
                                 'upper': convert all HeaderItem mnemonics to uppercase
99
                                 'lower': convert all HeaderItem mnemonics to lowercase
100
101
        See :func:`lasio.reader.open_with_codecs` for additional keyword
102
        arguments which help to manage issues relate to character encodings.
103
104
        '''
105
106
        file_obj, self.encoding = reader.open_file(file_ref, **kwargs)
107
108
        regexp_subs, value_null_subs, version_NULL = reader.get_substitutions(
109
            read_policy, null_policy)
110
111
        try:
112
            self.raw_sections = reader.read_file_contents(
113
                file_obj, regexp_subs, value_null_subs, ignore_data=ignore_data, )
114
        finally:
115
            if hasattr(file_obj, "close"):
116
                file_obj.close()
117
118
        if len(self.raw_sections) == 0:
119
            raise KeyError('No ~ sections found. Is this a LAS file?')
120
121
        def add_section(pattern, name, **sect_kws):
122
            raw_section = self.match_raw_section(pattern)
123
            drop = []
124
            if raw_section:
125
                self.sections[name] = reader.parse_header_section(raw_section, 
126
                                                                  **sect_kws)
127
                drop.append(raw_section["title"])
128
            else:
129
                logger.warning("Header section %s regexp=%s was not found."
130
                               % (name, pattern))
131
            for key in drop:
132
                self.raw_sections.pop(key)
133
134
        add_section("~V", "Version", version=1.2, 
135
                    ignore_header_errors=ignore_header_errors,
136
                    mnemonic_case=mnemonic_case)
137
138
        # Establish version and wrap values if possible.
139
140
        try:
141
            version = self.version['VERS'].value
142
        except KeyError:
143
            logger.warning('VERS item not found in the ~V section.')
144
            version = None
145
146
        try:
147
            wrap = self.version['WRAP'].value
148
        except KeyError:
149
            logger.warning('WRAP item not found in the ~V section')
150
            wrap = None
151
152
        # Validate version.
153
        #
154
        # If VERS was missing and version = None, then the file will be read in
155
        # as if version were 2.0. But there will be no VERS HeaderItem, meaning
156
        # that las.write(..., version=None) will fail with a KeyError. But
157
        # las.write(..., version=1.2) will work because a new VERS HeaderItem
158
        # will be created.
159
160
        try:
161
            assert version in (1.2, 2, None)
162
        except AssertionError:
163
            if version < 2:
164
                version = 1.2
165
            else:
166
                version = 2
167
        else:
168
            if version is None:
169
                logger.info('Assuming that LAS VERS is 2.0')
170
                version = 2
171
172
        add_section("~W", "Well", version=version, 
173
                    ignore_header_errors=ignore_header_errors,
174
                    mnemonic_case=mnemonic_case)
175
176
        # Establish NULL value if possible.
177
178
        try:
179
            null = self.well['NULL'].value
180
        except KeyError:
181
            logger.warning('NULL item not found in the ~W section')
182
            null = None
183
184
        add_section("~C", "Curves", version=version, 
185
                    ignore_header_errors=ignore_header_errors,
186
                    mnemonic_case=mnemonic_case)
187
        add_section("~P", "Parameter", version=version, 
188
                    ignore_header_errors=ignore_header_errors,
189
                    mnemonic_case=mnemonic_case)
190
        s = self.match_raw_section("~O")
191
192
        drop = []
193
        if s:
194
            self.sections["Other"] = "\n".join(s["lines"])
195
            drop.append(s["title"])
196
        for key in drop:
197
            self.raw_sections.pop(key)
198
199
        # Deal with nonstandard sections that some operators and/or
200
        # service companies (eg IHS) insist on adding.
201
        drop = []
202
        for s in self.raw_sections.values():
203
            if s["section_type"] == "header":
204
                logger.warning('Found nonstandard LAS section: ' + s["title"])
205
                self.sections[s["title"][1:]] = "\n".join(s["lines"])
206
                drop.append(s["title"])
207
        for key in drop:
208
            self.raw_sections.pop(key)
209
210
        if not ignore_data:
211
            drop = []
212
            s = self.match_raw_section("~A")
213
            s_valid = True
214
            if s is None:
215
                logger.warning("No data section (regexp='~A') found")
216
                s_valid = False
217
            try:
218
                if s['ncols'] is None:
219
                    logger.warning('No numerical data found inside ~A section')
220
                    s_valid = False
221
            except:
222
                pass
223
224
            if s_valid:
225
                arr = s["array"]
226
                logger.debug('~A data.shape {}'.format(arr.shape))
227
                if version_NULL:
228
                    arr[arr == null] = np.nan
229
                logger.debug('~A after NULL replacement data.shape {}'.format(arr.shape))
230
231
                n_curves = len(self.curves)
232
                n_arr_cols = len(self.curves) # provisional pending below check
233
                logger.debug("n_curves=%d ncols=%d" % (n_curves, s["ncols"]))
234
                if wrap == "NO":
235
                    if s["ncols"] > n_curves:
236
                        n_arr_cols = s["ncols"]
237
                data = np.reshape(arr, (-1, n_arr_cols))
238
239
                self.set_data(data, truncate=False)
240
                drop.append(s["title"])
241
            for key in drop:
242
                self.raw_sections.pop(key)
243
244
        check_units_on = []
245
        for mnemonic in ('STRT', 'STOP', 'STEP'):
246
            if mnemonic in self.well:
247
                check_units_on.append(self.well[mnemonic])
248
        if len(self.curves) > 0:
249
            check_units_on.append(self.curves[0])
250
        for index_unit, possibilities in defaults.DEPTH_UNITS.items():
251
            if all(i.unit.upper() in possibilities for i in check_units_on):
252
                self.index_unit = index_unit
253
254
    def write(self, file_ref, **kwargs):
255
        '''Write LAS file to disk.
256
257
        Arguments:
258
            file_ref (open file-like object or str): a file-like object opening
259
                for writing, or a filename.
260
    
261
        All ``**kwargs`` are passed to :func:`lasio.writer.write` -- please
262
        check the docstring of that function for more keyword arguments you can
263
        use here!
264
265
        Examples:
266
267
            >>> with open('test_output.las', mode='w') as f:
268
            ...     lasfile_obj.write(f, version=2.0)   # <-- this method
269
270
        '''
271
        opened_file = False
272
        if isinstance(file_ref, basestring) and not hasattr(file_ref, "write"):
273
            opened_file = True
274
            file_ref = open(file_ref, "w")
275
        writer.write(self, file_ref, **kwargs)
276
        if opened_file:
277
            file_ref.close()
278
279
    def to_excel(self, filename):
280
        '''Export LAS file to a Microsoft Excel workbook.
281
282
        This function will raise an :exc:`ImportError` if ``openpyxl`` is not
283
        installed.
284
285
        Arguments:
286
            filename (str)
287
288
        '''
289
        from . import excel
290
        converter = excel.ExcelConverter(self)
291
        converter.write(filename)
292
293
    def to_csv(self, file_ref, mnemonics=True, units=True, units_loc='line', **kwargs):
294
        '''Export to a CSV file.
295
296
        Arguments:
297
            file_ref (open file-like object or str): a file-like object opening
298
                for writing, or a filename.
299
300
        Keyword Arguments:
301
            mnemonics (list, True, False): write mnemonics as a header line at the
302
                start. If list, use the supplied items as mnemonics. If True,
303
                use the curve mnemonics.
304
            units (list, True, False): as for mnemonics.
305
            units_loc (str or None): either 'line', '[]' or '()'. 'line' will put
306
                units on the line following the mnemonics (good for WellCAD). 
307
                '[]' and '()' will put the units in either brackets or 
308
                parentheses following the mnemonics, on the single header line
309
                (better for Excel)
310
            **kwargs: passed to :class:`csv.writer`. Note that if
311
                ``lineterminator`` is **not** specified here, then it will be
312
                sent to :class:`csv.writer` as ``lineterminator='\\n'``.
313
314
        '''
315
        opened_file = False
316
        if isinstance(file_ref, basestring) and not hasattr(file_ref, "write"):
317
            opened_file = True
318
            file_ref = open(file_ref, "w")
319
320
        if not 'lineterminator' in kwargs:
321
            kwargs['lineterminator'] = '\n'
322
        writer = csv.writer(file_ref, **kwargs)
323
        
324
        if mnemonics is True:
325
            mnemonics = [c.original_mnemonic for c in self.curves]
326
        if units is True:
327
            units = [c.unit for c in self.curves]
328
329
        if mnemonics:
330
            if units_loc in ('()', '[]') and units:
331
                mnemonics = [
332
                    m + ' ' + units_loc[0] + u + units_loc[1] 
333
                    for m, u in zip(mnemonics, units)]
334
            writer.writerow(mnemonics)
335
        if units:
336
            if units_loc == 'line':
337
                writer.writerow(units)
338
339
        for i in range(self.data.shape[0]):
340
            writer.writerow(self.data[i, :])
341
        
342
        if opened_file:
343
            file_ref.close()
344
345
    def match_raw_section(self, pattern, re_func="match", flags=re.IGNORECASE):
346
        '''Find raw section with a regular expression.
347
348
        Arguments:
349
            pattern (str): regular expression (you need to include the tilde)
350
351
        Keyword Arguments:
352
            re_func (str): either "match" or "search", see python ``re`` module.
353
            flags (int): flags for :func:`re.compile`
354
355
        Returns:
356
            dict
357
358
        Intended for internal use only.
359
360
        '''
361
        for title in self.raw_sections.keys():
362
            title = title.strip()
363
            p = re.compile(pattern, flags=flags)
364
            if re_func == "match":
365
                re_func = re.match
366
            elif re_func == "search":
367
                re_func == re.search
368
            m = re_func(p, title)
369
            if m:
370
                return self.raw_sections[title]
371
372
    def get_curve(self, mnemonic):
373
        '''Return CurveItem object.
374
375
        Arguments:
376
            mnemonic (str): the name of the curve
377
378
        Returns:
379
            :class:`lasio.las_items.CurveItem` (not just the data array)
380
381
        '''
382
        for curve in self.curves:
383
            if curve.mnemonic == mnemonic:
384
                return curve
385
386
    def __getitem__(self, key):
387
        '''Provide access to curve data.
388
389
        Arguments:
390
            key (str, int): either a curve mnemonic or the column index.
391
392
        Returns:
393
            1D :class:`numpy.ndarray` (the data for the curve)
394
395
        '''
396
        #TODO: If I implement 2D arrays, need to check here for :1 :2 :3 etc.
397
        curve_mnemonics = [c.mnemonic for c in self.curves]
398
        if isinstance(key, int):
399
            return self.curves[key].data
400
        elif key in curve_mnemonics:
401
            return self.curves[key].data
402
        else:
403
            raise KeyError('{} not found in curves ({})'.format(key, curve_mnemonics))
404
405
    def __setitem__(self, key, value):
406
        '''Append a curve.
407
408
        Arguments:
409
            key (str): the curve mnemonic
410
            value (1D data or CurveItem): either the curve data, or a CurveItem
411
412
        See :meth:`lasio.las.LASFile.append_curve_item` or 
413
        :meth:`lasio.las.LASFile.append_curve` for more details.
414
415
        '''
416
        if isinstance(value, CurveItem):
417
            if key != value.mnemonic:
418
                raise KeyError('key {} does not match value.mnemonic {}'.format(
419
                    key, value.mnemonic))
420
            self.append_curve_item(value)
421
        else:
422
            # Assume value is an ndarray
423
            self.append_curve(key, value)
424
425
    def keys(self):
426
        '''Return curve mnemonics.'''
427
        return [c.mnemonic for c in self.curves]
428
429
    def values(self):
430
        '''Return data for each curve.'''
431
        return [c.data for c in self.curves]
432
433
    def items(self):
434
        '''Return mnemonics and data for all curves.'''
435
        return [(c.mnemonic, c.data) for c in self.curves]
436
437
    def iterkeys(self):
438
        return iter(list(self.keys()))
439
440
    def itervalues(self):
441
        return iter(list(self.values()))
442
443
    def iteritems(self):
444
        return iter(list(self.items()))
445
446
    @property
447
    def version(self):
448
        '''Header information from the Version (~V) section.
449
450
        Returns:
451
            :class:`lasio.las_items.SectionItems` object.
452
453
        '''
454
        return self.sections['Version']
455
456
    @version.setter
457
    def version(self, section):
458
        self.sections['Version'] = section
459
460
    @property
461
    def well(self):
462
        '''Header information from the Well (~W) section.
463
464
        Returns:
465
            :class:`lasio.las_items.SectionItems` object.
466
467
        '''
468
        return self.sections['Well']
469
470
    @well.setter
471
    def well(self, section):
472
        self.sections['Well'] = section
473
474
    @property
475
    def curves(self):
476
        '''Curve information and data from the Curves (~C) and data section..
477
478
        Returns:
479
            :class:`lasio.las_items.SectionItems` object.
480
481
        '''
482
        return self.sections['Curves']
483
484
    @curves.setter
485
    def curves(self, section):
486
        self.sections['Curves'] = section
487
488
    @property
489
    def curvesdict(self):
490
        '''Curve information and data from the Curves (~C) and data section..
491
492
        Returns:
493
            dict
494
495
        '''
496
        d = {}
497
        for curve in self.curves:
498
            d[curve['mnemonic']] = curve
499
        return d
500
501
    @property
502
    def params(self):
503
        '''Header information from the Parameter (~P) section.
504
505
        Returns:
506
            :class:`lasio.las_items.SectionItems` object.
507
508
        '''
509
        return self.sections['Parameter']
510
511
    @params.setter
512
    def params(self, section):
513
        self.sections['Parameter'] = section
514
515
    @property
516
    def other(self):
517
        '''Header information from the Other (~O) section.
518
519
        Returns:
520
            str
521
522
        '''
523
        return self.sections['Other']
524
525
    @other.setter
526
    def other(self, section):
527
        self.sections['Other'] = section
528
529
    @property
530
    def metadata(self):
531
        '''All header information joined together.
532
533
        Returns:
534
            :class:`lasio.las_items.SectionItems` object.
535
536
        '''
537
        s = SectionItems()
538
        for section in self.sections:
539
            for item in section:
540
                s.append(item)
541
        return s
542
543
    @metadata.setter
544
    def metadata(self, value):
545
        raise NotImplementedError('Set values in the section directly')
546
547
    @property
548
    def header(self):
549
        '''All header information
550
551
        Returns:
552
            dict
553
554
        '''
555
        return self.sections
556
557
    def df(self):
558
        '''Return data as a :class:`pandas.DataFrame` structure.'''
559
        import pandas as pd
560
        df = pd.DataFrame(self.data, columns=[c.mnemonic for c in self.curves])
561
        if len(self.curves) > 0:
562
            df = df.set_index(self.curves[0].mnemonic)
563
        return df
564
565
    @property
566
    def data(self):
567
        return np.vstack([c.data for c in self.curves]).T
568
569
    @data.setter
570
    def data(self, value):
571
        return self.set_data(value)
572
573
    def set_data(self, array_like, names=None, truncate=False):
574
        '''Set the data for the LAS; actually sets data on individual curves.
575
576
        Arguments:
577
            array_like (array_like or :class:`pandas.DataFrame`): 2-D data array
578
579
        Keyword Arguments:
580
            names (list, optional): used to replace the names of the existing
581
                :class:`lasio.las_items.CurveItem` objects.
582
            truncate (bool): remove any columns which are not included in the
583
                Curves (~C) section.
584
585
        Note: you can pass a :class:`pandas.DataFrame` to this method.
586
587
        '''
588
        try:
589
            import pandas as pd
590
        except ImportError:
591
            pass
592
        else:
593
            if isinstance(array_like, pd.DataFrame):
594
                return self.set_data_from_df(
595
                    array_like, **dict(names=names, truncate=False))
596
        data = np.asarray(array_like)
597
598
        # Truncate data array if necessary.
599
        if truncate:
600
            data = data[:, len(self.curves)]
601
602
        # Extend curves list if necessary.
603
        while data.shape[1] > len(self.curves):
604
            self.curves.append(CurveItem(''))
605
606
        if not names:
607
            names = [c.original_mnemonic for c in self.curves]
608
        else:
609
            # Extend names list if necessary.
610
            while len(self.curves) > len(names):
611
                names.append('')
612
        logger.debug('set_data. names to use: {}'.format(names))
613
614
        for i, curve in enumerate(self.curves):
615
            curve.mnemonic = names[i]
616
            curve.data = data[:, i]
617
            
618
        self.curves.assign_duplicate_suffixes()
619
620
    def set_data_from_df(self, df, **kwargs):
621
        '''Set the LAS file data from a :class:`pandas.DataFrame`.
622
623
        Arguments:
624
            df (pandas.DataFrame): curve mnemonics are the column names.
625
626
        Keyword arguments are passed to :meth:`lasio.las.LASFile.set_data`.
627
628
        '''
629
        df_values = np.vstack([df.index.values, df.values.T]).T
630
        if (not 'names' in kwargs) or (not kwargs['names']):
631
            kwargs['names'] = [df.index.name] + [str(name) for name in df.columns.values]
632
        self.set_data(df_values, **kwargs)
633
634
    @property
635
    def index(self):
636
        '''Return data from the first column of the LAS file data (depth/time).
637
638
        '''
639
        return self.curves[0].data
640
641
    @property
642
    def depth_m(self):
643
        '''Return the index as metres.'''
644
        if self.index_unit == 'M':
645
            return self.index
646
        elif self.index_unit == 'FT':
647
            return self.index * 0.3048
648
        else:
649
            raise exceptions.LASUnknownUnitError(
650
                'Unit of depth index not known')
651
652
    @property
653
    def depth_ft(self):
654
        '''Return the index as feet.'''
655
        if self.index_unit == 'M':
656
            return self.index / 0.3048
657
        elif self.index_unit == 'FT':
658
            return self.index
659
        else:
660
            raise exceptions.LASUnknownUnitError(
661
                'Unit of depth index not known')
662
663
    def add_curve_raw(self, mnemonic, data, unit='', descr='', value=''):
664
        '''Deprecated. Use append_curve_item() or insert_curve_item() instead.'''
665
        return self.append_curve_item(self, mnemonic, data, unit, descr, value)
666
667
    def append_curve_item(self, curve_item):
668
        '''Add a CurveItem.
669
670
        Args:
671
            curve_item (lasio.CurveItem)
672
673
        '''
674
        self.insert_curve_item(len(self.curves), curve_item)
675
676
    def insert_curve_item(self, ix, curve_item):
677
        '''Insert a CurveItem.
678
679
        Args:
680
            ix (int): position to insert CurveItem i.e. 0 for start
681
            curve_item (lasio.CurveItem)
682
683
        '''
684
        assert isinstance(curve_item, CurveItem)
685
        self.curves.insert(ix, curve_item)
686
687
    def add_curve(self, *args, **kwargs):
688
        '''Deprecated. Use append_curve() or insert_curve() instead.'''
689
        return self.append_curve(*args, **kwargs)
690
691
    def append_curve(self, mnemonic, data, unit='', descr='', value=''):
692
        '''Add a curve.
693
694
        Arguments:
695
            mnemonic (str): the curve mnemonic
696
            data (1D ndarray): the curve data
697
698
        Keyword Arguments:
699
            unit (str): curve unit
700
            descr (str): curve description
701
            value (int/float/str): value e.g. API code.
702
703
        '''
704
        return self.insert_curve(len(self.curves), mnemonic, data, unit, descr, value)
705
706
    def insert_curve(self, ix, mnemonic, data, unit='', descr='', value=''):
707
        '''Insert a curve.
708
709
        Arguments:
710
            ix (int): position to insert curve at i.e. 0 for start.
711
            mnemonic (str): the curve mnemonic
712
            data (1D ndarray): the curve data
713
714
        Keyword Arguments:
715
            unit (str): curve unit
716
            descr (str): curve description
717
            value (int/float/str): value e.g. API code.
718
719
        '''
720
        curve = CurveItem(mnemonic, unit, value, descr, data)
721
        self.insert_curve_item(ix, curve)
722
723
    def delete_curve(self, mnemonic=None, ix=None):
724
        '''Delete a curve.
725
726
        Keyword Arguments:
727
            ix (int): index of curve in LASFile.curves.
728
            mnemonic (str): mnemonic of curve.
729
730
        The index takes precedence over the mnemonic.
731
732
        '''
733
        if ix is None:
734
            ix = self.curves.keys().index(mnemonic)
735
        self.curves.pop(ix)
736
737
    @property
738
    def json(self):
739
        '''Return object contents as a JSON string.'''
740
        obj = OrderedDict()
741
        for name, section in self.sections.items():
742
            try:
743
                obj[name] = section.json
744
            except AttributeError:
745
                obj[name] = json.dumps(section)
746
        return json.dumps(obj)
747
748
    @json.setter
749
    def json(self, value):
750
        raise Exception('Cannot set objects from JSON')
751
752
753
754
class Las(LASFile):
755
756
    '''LAS file object.
757
758
    Retained for backwards compatibility.
759
760
    '''
761
    pass
762
763
764
class JSONEncoder(json.JSONEncoder):
765
766
    def default(self, obj):
767
        if isinstance(obj, LASFile):
768
            d = {'metadata': {},
769
                 'data': {}}
770
            for name, section in obj.sections.items():
771
                if isinstance(section, basestring):
772
                    d['metadata'][name] = section
773
                else:
774
                    d['metadata'][name] = []
775
                    for item in section:
776
                        d['metadata'][name].append(dict(item))
777
            for curve in obj.curves:
778
                d['data'][curve.mnemonic] = list(curve.data)
779
            return d
780