-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathLAS.py
1124 lines (968 loc) · 39.5 KB
/
LAS.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# -*- coding: utf-8 -*-
"""
LAS
===
This file defines the classes and functions necessary to read and write LAS
Version 2.0 [1]_ files.
References
----------
.. [1] LAS Version 2.0:
http://www.cwls.org/wp-content/uploads/2014/09/LAS_20_Update_Jan2014.pdf
"""
from __future__ import print_function
import numpy as np
from collections import OrderedDict
try:
import builtins
except ImportError:
import __builtin__ as builtins
_VERBOSE = False
class LASFile(object):
"""
A LAS 2.0 file object.
Parameters
----------
filename : str
File name
Attributes
----------
filename : str
File name.
header : OrderedDict
Header of the file. Each section of the LAS header is a value in the
dictionary. The key of each section is its capitalized first letter
(e.g. "V" for VERSION section). Each section is also an OrderedDict, in
which the keys are the mnems of the lines and the lines themselves are
the values. The lines are dicts with 4 keys: "MNEM", "UNIT", "DATA" and
"DESC".
headersectionnames : dict
Names of the header sections. The keys are the capitalized first
letters of each section and the values are the full section names.
headerlayout : dict
Layout of the header. Similar to `header`, but instead of the lines,
contains the layout of the line. The layout of a line is a list of 6
ints, representing the number of whitespaces in the line. The elements
of the list are, respectively, the number of whitespaces between the
beginning of the line and the beginning of the mnem, the end of mnem
and the first dot, the end of the unit and the beginning of the data,
the end of the data and the colon, the colon and the beginning of the
description and the end of the description and the end of the line.
headercomments : dict
Comments in the LAS header. The keys are the linenumber of the comment
and the values are the comments themselves.
data : numpy.ndarray
The data present in the ASCII section of the LAS file. The data shape
is nc x ns, where nc is the number of curves and ns is the
number of samples.
"""
def __init__(self, filename):
self.filename = filename
self.header = OrderedDict()
self.headersectionnames = {}
self.headerlayout = {}
self.headercomments = {}
self.data = np.empty(0)
class LASReader(LASFile):
"""
A specialization of `LASFile` for reading files.
Attributes
----------
wellname : str
Name of the well.
curvesnames : list
Name of each curve.
curvesunits : list
Unit of each curve.
Notes
-----
When creating a `LASReader` object the file is not read immediately. The
`read` method must be called after the creation. Once it is called, all
of its attributes will be read from the file and the file will be closed.
Examples
--------
>>> lasfile = LASReader("filename.las")
>>> lasfile.read()
>>> lasfile.wellname
'wellname'
>>> lasfile.curvesnames
['curve01name', 'curve02name', ...]
>>> lasfile.header["V"]["VERSION"]["DATA"]
'2.0'
>>> lasfile.data[0]
array([1500.0, 1500.2, ...])
"""
def __init__(self, filename):
super(LASReader, self).__init__(filename)
@property
def wellname(self):
return self.header["W"]["WELL"]["DATA"]
@property
def curvesnames(self):
return [line['MNEM'] for line in self.header["C"].values()]
@property
def curvesunits(self):
return [line['UNIT'] for line in self.header["C"].values()]
@staticmethod
def _splitline(line):
"""
Split a LAS line in MNEM, UNITS, DATA and DESCRIPTION.
Parameters
----------
line : str
A non-comment LAS line.
Returns
-------
mnem : str
Mnemoic part of the line
unit : str
Unit part of the line
data : str
Data part of the line
desc : str
Description part of the line
Notes
-----
This method doesn't remove whitespaces from the line parts.
Examples
--------
>>> LASReader._splitline(' DEPTH.M : MEASURED DEPTH ')
(' DEPTH', 'M', ' ', ' MEASURED DEPTH ')
"""
# if ":" not in line:
# desc = ''
# else:
# line, desc = line.rsplit(":", 1)
# desc = desc.strip()
# line = line.strip()
# if " " not in line:
# data = ''
# else:
# line, data = line.rsplit(" ", 1)
# data = data.strip()
# line = line.strip()
# if "." not in line:
# unit = ''
# mnem = line
# else:
# mnem, unit = line.split(".", 1)
# return mnem, unit, data, desc
rest, desc = line.rsplit(":", 1)
mnem, rest = rest.split(".", 1)
unit, data = rest.split(" ", 1)
return mnem, unit, data, desc
@staticmethod
def _getlinelayout(splittedline):
"""
Obtain the layout, i.e. the whitespace structure, of a splitted line.
Parameters
----------
splittedline : list
Contains the four parts of a LAS line with the whitespaces, i.e.
the return of the `_splitline` method
Returns
-------
layout : list
A list of 6 ints, in which each element correspond to the lenght of
a string of whitespaces in the line. The elements of the list are,
respectively, the number of whitespaces between the beginning of
the line and the beginning of the mnem, the end of mnem and the
first dot, the end of the unit and the beginning of the data, the
end of the data and the colon, the colon and the beginning of the
description and the end of the description and the end of the line.
Examples
--------
>>> splittedline = LASReader._splitline(
' DEPTH.M : MEASURED DEPTH ')
>>> LASReader._getlinelayout(splittedline)
[2, 0, 7, 0, 1, 2]
"""
mnem, unit, data, desc = splittedline
layout = []
lmnem = mnem.lstrip()
ldata = data.lstrip()
ldesc = desc.lstrip()
layout.append(len(mnem) - len(lmnem))
layout.append(len(lmnem) - len(lmnem.rstrip()))
layout.append(len(data) - len(ldata))
layout.append(len(ldata) - len(ldata.rstrip()))
layout.append(len(desc) - len(ldesc))
layout.append(len(ldesc) - len(ldesc.rstrip()))
return layout
@staticmethod
def _parseline(line, withlayout=False):
"""
Parse a LAS line in its components and, if specified, its layout.
Parameters
----------
line : str
A non-comment LAS line.
withlayout : bool, optional
Whether the layout must be returned.
Returns
-------
parsedline : dict
A dictionary consisting of the 4 elements of a LAS line, with keys
"MENM", "UNIT", "DATA" and "DESC".
layout : list
A list of 6 ints, in which each element correspond to the lenght of
a string of whitespaces in the line.
Examples
--------
>>> parsedline, layout = LASReader._parseline(
' DEPTH.M : MEASURED DEPTH ', True)
>>> parsedline
{'DATA': '', 'DESC': 'MEASURED DEPTH', 'MNEM': 'DEPTH', 'UNIT': 'M'}
>>> layout
[2, 0, 7, 0, 1, 2]
"""
mnem, unit, data, desc = LASReader._splitline(line)
parsedline = {}
parsedline["MNEM"] = mnem.strip()
parsedline["UNIT"] = unit.strip()
parsedline["DATA"] = data.strip()
parsedline["DESC"] = desc.strip()
if not withlayout:
return parsedline
else:
layout = LASReader._getlinelayout((mnem, unit, data, desc))
return parsedline, layout
@staticmethod
def _getheaderlines(fileobject):
"""
Obtain the LAS header lines from a file object.
Parameters
----------
fileobject : file-like object
The file object from which the header lines will be obtained.
Returns
-------
headerlines : list
A list containing the lines that belong to a LAS file header.
"""
fileobject.seek(0)
headerlines = []
line = fileobject.readline()
while not line.lstrip().startswith('~A'):
headerlines.append(line.replace('\t', ' ')) # TODO: Suportar vários tipos de separadores
line = fileobject.readline()
headerlines.append(line)
return headerlines
@staticmethod
def _getheader(headerlines, withsectionnames=False, withlayout=False, withcomments=False):
"""
Obtain the LAS header from a list of lines.
Parameters
----------
headerlines : list
A list containing the lines that belong to a LAS file header, i.e.
the return of `_getheaderlines` method.
withsectionnames : bool, optional
Whether to return the LAS section names.
withlayout : bool, optional
Whether to return the LAS header layout.
withcomments : bool, optional
Whether to return the LAS header comments.
Returns
-------
header : OrderedDict
Header of a LAS file. Each section of the header is a value in the
dictionary. The key of each section is its capitalized first letter
(e.g. "V" for VERSION section). Each section is also an
OrderedDict, in which the keys are the mnems of the lines and the
lines themselves are the values. The lines are dicts with 4 keys:
"MNEM", "UNIT", "DATA" and "DESC".
sectionnames : dict
Names of the header sections. The keys are the capitalized first
letters of each section and the values are the full section names.
layout : dict
Layout of the header. Similar to `header`, but instead of the
lines, contains the layout of the line.
comments : dict
Comments in the LAS header. The keys are the linenumber of the
comment and the values are the comments themselves.
See Also
--------
_getlinelayout : Obtain the line layout.
"""
global _VERBOSE
header = OrderedDict()
sectionnames = {}
comments = {}
layout = {}
currentsection = None
linecount = 0
for line in headerlines:
if not line:
continue
elif line.lstrip().startswith('#'):
comments[linecount] = line.split('\n')[0]
elif line.lstrip().startswith('~'):
currentsection = []
sectionname = line.split('\n')[0]
sectionkey = sectionname.split('~')[1][0].upper()
header[sectionkey] = currentsection
sectionnames[sectionkey] = sectionname
else:
currentsection.append(line.split('\n')[0])
linecount += 1
for sectionkey, lines in header.items():
try:
section = OrderedDict()
sectionlayout = {}
for line in lines:
parsedline, linelayout = LASReader._parseline(line, True)
# if parsedline['MNEM'] in section:
# print "Curva repetida:", parsedline['MNEM'] # TODO: Fazer algo
# section[parsedline['MNEM']] = parsedline
# sectionlayout[parsedline['MNEM']] = linelayout
# TODO: Melhorar e ver se funcionou
old_mnem = parsedline['MNEM']
new_mnem = old_mnem
count = 0
while new_mnem in section:
count += 1
new_mnem = old_mnem + '_{:0>4}'.format(count)
if _VERBOSE and count:
print("Nome de curva repetido:", old_mnem)
print("Substituindo por:", new_mnem)
parsedline['MNEM'] = new_mnem
section[new_mnem] = parsedline
sectionlayout[new_mnem] = linelayout
if not section:
header[sectionkey] = ''
else:
header[sectionkey] = section
layout[sectionkey] = sectionlayout
except:
header[sectionkey] = '\n'.join(lines)
if (not withsectionnames) and (not withlayout) and (not withcomments):
return header
else:
returns = (header,)
if withsectionnames:
returns += (sectionnames,)
if withlayout:
returns += (layout,)
if withcomments:
returns += (comments,)
return returns
@staticmethod
def _getdatalines(fileobject):
"""
Obtain the LAS ASCII section lines from a file object.
Parameters
----------
fileobject : file-like object
The file object from which the data lines will be obtained.
Returns
-------
datalines : list
A list containing the lines that belong to a LAS file ASCII
section.
"""
fileobject.seek(0)
line = fileobject.readline()
while not line.lstrip().startswith('~A'):
line = fileobject.readline()
datalines = fileobject.readlines()
return datalines
@staticmethod
def _getflatdata(datalines):
"""
Obtain a flat `numpy.ndarray` from a list of data lines.
Concatenate the lines; split the resulting string, convert each element
to float and convert to a `numpy.ndarray`.
Parameters
----------
datalines : list
A list containing the lines that belong to a LAS file ASCII
section.
Returns
-------
flatdata : numpy.ndarray
A flat (i.e. one-dimensional) array containing data from
`datalines`.
"""
flatdata = np.asarray([float(a) for a in ' '.join(datalines).split()])
return flatdata
@staticmethod
def _reshapeflatdata(flatdata, ncurves):
"""
Reshape the flat data into a 2-dimensional data.
The reshaped data will have the same number of elements as `flatdata`
and first dimension with length `ncurves`. This way, `data[0]` will
be the data from the first curve in the file.
Parameters
----------
flatdata : numpy.ndarray
A flat (i.e. one-dimensional) array containing data from a LAS
file.
ncurves : int
Number of existing curves in the same file
Returns
-------
data : numpy.ndarray
Reshaped data with first dimension lenght equal to `ncurves`
"""
data = np.reshape(flatdata, (-1, ncurves)).T
return data
@staticmethod
def _replacenullvalues(data, nullvalue, copy=False):
"""
Replace null values in an array with `np.nan`.
Parameters
----------
data : np.ndarray
Array containing null values to be replaced.
nullvalue : float
The value that will be replaced by `np.nan`.
copy : bool, optional
Whether the operation will be performed in a copy of the data or
in the data itself.
Returns
-------
newdata : np.ndarray
A array with `nullvalue` replaced with `np.nan`.
"""
if copy:
newdata = np.copy(data)
else:
newdata = data
where = (newdata == nullvalue)
newdata[where] = np.nan
return newdata
@staticmethod
def _reorderdata(data, copy=False):
"""
Reorder the data so that the first line is in ascending order.
This method suposes that the first line of `data` is already sorted
in descending order. It will invert the order of the rows in the array,
i.e. the last row will become the first, the second last will become
the second and so on.
Parameters
----------
data : np.ndarray
The array that will be reordered.
copy : bool, optional
Whether the operation will be performed in a copy of the data or
in the data itself.
Returns
-------
newdata : np.ndarray
A array with the rows in reverse order.
"""
if copy:
newdata = np.copy(data)
else:
newdata = data
return newdata[:, ::-1]
def read(self):
"""
Read the file.
Notes
-----
When creating a `LASReader` object the file is not read immediately.
This method must be called after the creation. Once it is called, all
of the instance's attributes will be read from the file and the file
will be closed.
"""
fileobject = builtins.open(self.filename, 'r')
headerlines = LASReader._getheaderlines(fileobject)
datalines = LASReader._getdatalines(fileobject)
fileobject.close()
self.header, self.headersectionnames, self.headerlayout, self.headercomments = LASReader._getheader(headerlines, True, True, True)
ncurves = len(self.header["C"])
nullvalue = float(self.header["W"]["NULL"]["DATA"])
stepvalue = float(self.header["W"]["STEP"]["DATA"])
flattendata = LASReader._getflatdata(datalines)
nandata = LASReader._replacenullvalues(flattendata, nullvalue)
self.data = LASReader._reshapeflatdata(nandata, ncurves)
if (stepvalue == nullvalue) or (stepvalue == 0.0):
stepvalue = self.data[0][1] - self.data[0][0]
if stepvalue < 0:
self.data = LASReader._reorderdata(self.data)
class LASWriter(LASFile):
"""
A specialization of `LASFile` for writing files.
Notes
-----
When creating a `LASReader` object the file is not written immediately. The
`header` and `data` attributes must be defined before calling the `write`
method. The other attributes (`headersectionnames`, `headerlayout` and
`headercomments`) are optional.
No verification is done to guarantee that the header and data are
compatible (i.e. have the same number of curves and the same depth range).
There are two methods that can be used for this: `correctwellsection` and
`correctcurvesection`.
In order to get a better layout for the header, the method
`getprettyheaderlayout` may be used.
Examples
--------
>>> lasfile = LASWriter("filename.las")
>>> lasfile.header = existing_header
>>> lasfile.data = existing_data
>>> lasfile.write()
"""
DEFAULTMNEMSTYLE = {'leftmargin': 1, 'rightmargin': 0, 'allign': 'left'}
DEFAULTDATASTYLE = {'leftmargin': 1, 'rightmargin': 1, 'allign': 'left'}
DEFAULTDESCSTYLE = {'leftmargin': 1, 'righmargin': 0, 'allign': 'left'}
DEFAULTUNIFORMSECTIONS = True
MINIMALLINELAYOUT = [0,0,1,0,0,0]
LASLINEPATTERN = "{0[0]}{MNEM}{0[1]}.{UNIT}{0[2]}{DATA}{0[3]}:{0[4]}{DESC}{0[5]}"
def __init__(self, filename):
super(LASWriter, self).__init__(filename)
@staticmethod
def _composeline(parsedline, linelayout=None):
"""
Turn a LAS parsed line into a one-string-line.
Parameters
----------
parsedline : dict
A LAS parsed line, i.e. a dict with keys "MNEM", "UNIT", "DATA"
and "DESC" which values are the respective LAS line parts.
linelayout : list, optional
A list containing 6 ints, each representing the number of
whitespaces in a portion of the LAS line. If not provided a minimal
layout will be used.
Returns
-------
line : str
A line composed using the `parsedline` parts and `linelayout`
whitespaces.
Examples
--------
>>> parsedline = {'DATA': '', 'DESC': 'MEASURED DEPTH',
'MNEM': 'DEPTH','UNIT': 'M'}
>>> layout = [2, 0, 7, 0, 1, 2]
>>> LASWriter._composeline(parsedline, linelayout)
' DEPTH.M : MEASURED DEPTH '
"""
if not linelayout:
linelayout = LASWriter.MINIMALLINELAYOUT
line = LASWriter.LASLINEPATTERN.format([" "*n for n in linelayout], **parsedline)
return line
@staticmethod
def _getspaces(style, spaces):
"""
Return the number of left and right whitespaces in a LAS line element.
Here LAS line element refers to either the MNEM, DATA or DESCRIPTION
part of a LAS line (the UNIT part cannot have whitespaces). The
distribution of whitespaces will be done according to `style`.
Parameters
----------
style : dict
A dictionary contaning the style parameters of a LAS line
element. The possible style parameters are 'allign',
'leftmargin' and 'rightmargin'. All of them are optional. If
'allign' is not provided, the other parameters are not used.
'allign' can be 'left', 'center' or 'right' and describes the
allignment of the LAS line element. 'leftmargin' and
'rightmargin' are the number of extra whitespaces to the left
or to the right of the line element, respectively.
spaces : int
Number of whitespaces to be distributed between `right` and
`left`.
Returns
-------
left : int
Number of whitespaces to the left of the LAS line element.
right : int
Number of whitespaces to the right of the LAS line element.
"""
if style.get('allign') == 'left':
left = style.get('leftmargin', 0)
right = style.get('rightmargin', 0) + spaces
elif style.get('allign') == 'center':
left = style.get('leftmargin', 0) + spaces//2
right = style.get('rightmargin', 0) + (spaces + 1)//2
elif style.get('allign') == 'right':
left = style.get('leftmargin', 0) + spaces
right = style.get('rightmargin', 0)
else:
left = style.get('leftmargin', 0)
right = style.get('rightmargin', 0)
return left, right
@staticmethod
def getprettyheaderlayout(header, mnemstyle=None, datastyle=None, descstyle=None, uniformsections=None):
"""
Obtain a 'pretty' header layout from a header and style parameters.
The layout will be constructed using the lenghts of the line elements
of each line in each section of the header.
Parameters
----------
header : OrderedDict
The LAS header for which the layout will be created.
mnemstyle : dict, optional
A dictionary containing the style parameters for the MNEM part of
the LAS line. The possible style parameters are 'allign',
'leftmargin' and 'rightmargin'. If not given, a default style will
be used.
datastyle : dict, optional
Same as `mnemstyle`, but for the DATA part instead.
descstyle : dict, optional
Same as `mnemstyle`, but for the DESCRIPTION part instead.
uniformsections : bool, optional
If True, the line elements will have the same lenght across all
sections of the header.
Returns
-------
headerlayout : dict
The pretty header layout that will fit the provided header.
"""
if mnemstyle is None:
mnemstyle = LASWriter.DEFAULTMNEMSTYLE
if datastyle is None:
datastyle = LASWriter.DEFAULTDATASTYLE
if descstyle is None:
descstyle = LASWriter.DEFAULTDESCSTYLE
if uniformsections is None:
uniformsections = LASWriter.DEFAULTUNIFORMSECTIONS
style = {}
style["MNEM"] = mnemstyle
style["DATA"] = datastyle
style["DESC"] = descstyle
allignmnem = bool(style["MNEM"].get('allign'))
alligndata = bool(style["DATA"].get('allign'))
alligndesc = bool(style["DESC"].get('allign'))
sizearrays = {}
for sectionkey, section in header.items():
if isinstance(section, basestring) or not section:
continue
sizearray = {}
for key in ("MNEM", "UNIT", "DATA", "DESC"):
sizearray[key] = np.array([len(line[key]) for line in section.values()])
sizearrays[sectionkey] = sizearray
usizearray = {}
for key in ("MNEM", "UNIT", "DATA", "DESC"):
usizearray[key] = np.hstack(sizearray[key] for sizearray in sizearrays.values())
leftpositions = {}
for sectionkey, section in header.items():
if isinstance(section, basestring) or not section:
continue
leftposition = {}
sizearray = sizearrays[sectionkey]
if uniformsections:
msizearray = usizearray
else:
msizearray = sizearray
mnemleft = np.zeros(len(section))
if not alligndata:
dataleft = np.zeros(len(section))
else:
if allignmnem:
size = sizearray['UNIT']
maxsize = np.max(msizearray['UNIT'])
else:
size = sizearray['MNEM'] + sizearray['UNIT']
maxsize = np.max(msizearray['MNEM'] + msizearray['UNIT'])
dataleft = maxsize - size
if not alligndesc or alligndata:
descleft = np.zeros(len(section))
else:
if allignmnem:
size = sizearray['UNIT'] + sizearray['DATA']
maxsize = np.max(msizearray['UNIT'] + msizearray['DATA'])
else:
size = sizearray['MNEM'] + sizearray['UNIT'] + sizearray['DATA']
maxsize = np.max(msizearray['MNEM'] + msizearray['UNIT'] + msizearray['DATA'])
descleft = maxsize - size
leftposition["MNEM"] = mnemleft
leftposition["DATA"] = dataleft
leftposition["DESC"] = descleft
leftpositions[sectionkey] = leftposition
headerlayout = {}
for sectionkey, section in header.items():
if isinstance(section, basestring) or not section:
continue
sectionlayout = {}
if uniformsections:
msizearray = usizearray
else:
msizearray = sizearrays[sectionkey]
for i, line in enumerate(section.values()):
linelayout = []
for key in ("MNEM", "DATA", "DESC"):
spaces = np.max(msizearray[key]) - len(line[key])
left, right = LASWriter._getspaces(style[key], spaces)
left += leftpositions[sectionkey][key][i]
linelayout.append(left)
linelayout.append(right)
sectionlayout[line["MNEM"]] = linelayout
headerlayout[sectionkey] = sectionlayout
return headerlayout
@staticmethod
def getemptyheader():
"""
Return an empty LAS header.
Returns
-------
emptyheader : OrderedDict
An empty LAS header.
"""
pass # TODO: implementar
@staticmethod
def correctwellsection(header, depthdata, depthunit, copy=False):
"""
Correct the Well Info section of the header based on the depth data.
The correction consists basically in obtaining the correct STRT, STOP
and STEP parameters from the provided depth data.
Parameters
----------
header : OrderedDict
The LAS header which Well Info section will be corrected.
depthdata : numpy.ndarray
The data of the depth "curve".
depthunit : str
The unit in which `depthdata` was measured.
copy : bool, optional
Whether the correction will be done in a copy of `header` or in
`header` itself.
Returns
-------
hdr : OrderedDict
A LAS header with corrected Well Info section.
"""
if copy:
hdr = header.copy()
else:
hdr = header
start = depthdata[0]
stop = depthdata[-1]
steps = depthdata[1:] - depthdata[:-1]
if np.equal(steps, steps[0]).all():
step = steps[0]
else:
step = 0
hdr["W"]["STRT"]["UNIT"] = depthunit
hdr["W"]["STRT"]["DATA"] = "{:g}".format(start)
hdr["W"]["STOP"]["UNIT"] = depthunit
hdr["W"]["STOP"]["DATA"] = "{:g}".format(stop)
hdr["W"]["STEP"]["UNIT"] = depthunit
hdr["W"]["STEP"]["DATA"] = "{:g}".format(step)
return hdr
@staticmethod
def correctcurvesection(header, mnems, units, keep=False, copy=False):
"""
Correct the Curve Info section of the header.
After the creation and deletion of curves the information existing in
the header will unlikely be suitable to the data. This method will
correct the Curve Info section, so that the header will use the
provided mnems and units.
Parameters
----------
header : OrderedDict
The LAS header which Curve Info section will be corrected.
mnems : list
The mnems of the curves that will exist in the header Curve Info
section.
units : list
The units of the curves that will exist in the header Curve Info
section.
keep : bool, optional
Whether only the necessary parts of the header will be corrected
or it will be built from the ground up.
copy : bool, optional
Whether the correction will be done in a copy of `header` or in
`header` itself.
Returns
-------
hdr : OrderedDict
A LAS header with corrected Curve Info section.
"""
if copy:
hdr = header.copy()
else:
hdr = header
if keep:
toremove = [key for key in hdr if key not in mnems]
for key in toremove:
del hdr[key]
for name, unit in zip(mnems, units):
if name in hdr["C"]:
hdr["C"][name]["UNIT"] = unit
hdr["C"][name] = hdr["C"].pop(name) # Para manter a mesma ordem de names
else:
hdr["C"][name] = {"MNEM": name, "UNIT": unit, "DATA": "", "DESC": ""}
else:
hdr["C"].clear()
for name, unit in zip(mnems, units):
hdr["C"][name] = {"MNEM": name, "UNIT": unit, "DATA": "", "DESC": ""}
return hdr
@staticmethod
def _headertostring(header, sectionnames=None, layout=None, comments=None):
"""
Convert the given LAS header to a string ready for writing into a file.
Parameters
----------
header : OrderedDict
The header to convert into a string.
sectionnames : dict, optional
A dictionary with section names capitalized first letter as keys
and full section names as values. For example, ``sectionnames["V"]
= "VERSION INFORMATION SECTION"``. If not provided the capitalized
first letter will be used as the full section name.
layout : dict, optional
Similar to header, but instead of the lines themselves it contains
the layout of each line. If not given a minimal layout will be
used.
comments : dict, optional
A dictinary which keys are the line numbers of the comment lines
and values are the comment lines themselves. If not given no
comment will be placed in the header string.
Returns
-------
string : str
A string containing all the header lines separated by '\\n'.
"""
lines = []
if not sectionnames:
sectionnames = {}
for key in header.keys():
sectionnames[key] = '~' + key
if not layout:
layout = {}
for key in header.keys():
layout[key] = {}.fromkeys(header[key])
if not comments:
comments = {}
for sectionkey, section in header.items():
while len(lines) in comments:
lines.append(comments[len(lines)])
if not section:
continue
lines.append(sectionnames[sectionkey])
if isinstance(section, basestring):
for line in section.split('\n'):
while len(lines) in comments:
lines.append(comments[len(lines)])
lines.append(line)
else:
for key, line in section.items():
while len(lines) in comments:
lines.append(comments[len(lines)])
linelayout = layout[sectionkey][key]
lines.append(LASWriter._composeline(line, linelayout))
while len(lines) in comments:
lines.append(comments[len(lines)])
lines.append(sectionnames["A"])
string = '\n'.join(lines)
return string
@staticmethod
def _datatostring(data, nullvalue=-999.25, revertorder=False, wrap=False, allign='right', collumnwidth=10, maxprecision=8, copy=False): # TODO: rever (alinhar pontos e etc)
"""
Convert the given LAS data to a string ready for writing into a file.
The data is formatted in collumns, such that the first line of `data`
will be placed in the first column, the second line in the second
column and so on.
Parameters
----------
data : np.ndarray
The data to convert into a string.
nullvalue : float, optional
A value that will replace the np.nan in the returned string.
wrap : bool, optional
Whether the output should be wrapped. If True, the each entry of
the first line of `data` will be alone in its line and subsequent