5
5
import contextlib
6
6
import os
7
7
from pathlib import Path
8
+ from typing import Literal
8
9
9
10
import pandas as pd
10
- from packaging .version import Version
11
11
from pygmt .clib import Session
12
12
from pygmt .exceptions import GMTInvalidInput
13
13
from pygmt .helpers import (
14
- GMTTempFile ,
15
14
build_arg_list ,
16
15
data_kind ,
17
16
fmt_docstring ,
18
17
kwargs_to_strings ,
19
18
unique_name ,
20
19
use_alias ,
20
+ validate_output_table_type ,
21
21
)
22
22
23
23
@@ -71,7 +71,12 @@ def tempfile_from_dftrack(track, suffix):
71
71
Z = "trackvalues" ,
72
72
)
73
73
@kwargs_to_strings (R = "sequence" )
74
- def x2sys_cross (tracks = None , outfile = None , ** kwargs ):
74
+ def x2sys_cross (
75
+ tracks = None ,
76
+ output_type : Literal ["pandas" , "numpy" , "file" ] = "pandas" ,
77
+ outfile : str | None = None ,
78
+ ** kwargs ,
79
+ ):
75
80
r"""
76
81
Calculate crossovers between track data files.
77
82
@@ -102,11 +107,8 @@ def x2sys_cross(tracks=None, outfile=None, **kwargs):
102
107
set it will default to $GMT_SHAREDIR/x2sys]. (**Note**: MGD77 files
103
108
will also be looked for via $MGD77_HOME/mgd77_paths.txt and .gmt
104
109
files will be searched for via $GMT_SHAREDIR/mgg/gmtfile_paths).
105
-
106
- outfile : str
107
- Optional. The file name for the output ASCII txt file to store the
108
- table in.
109
-
110
+ {output_type}
111
+ {outfile}
110
112
tag : str
111
113
Specify the x2sys TAG which identifies the attributes of this data
112
114
type.
@@ -183,15 +185,16 @@ def x2sys_cross(tracks=None, outfile=None, **kwargs):
183
185
184
186
Returns
185
187
-------
186
- crossover_errors : :class:`pandas.DataFrame` or None
187
- Table containing crossover error information.
188
- Return type depends on whether the ``outfile`` parameter is set:
189
-
190
- - :class:`pandas.DataFrame` with (x, y, ..., etc) if ``outfile`` is not
191
- set
192
- - None if ``outfile`` is set (track output will be stored in the set in
193
- ``outfile``)
188
+ crossover_errors
189
+ Table containing crossover error information. Return type depends on ``outfile``
190
+ and ``output_type``:
191
+
192
+ - None if ``outfile`` is set (output will be stored in file set by ``outfile``)
193
+ - :class:`pandas.DataFrame` or :class:`numpy.ndarray` if ``outfile`` is not set
194
+ (depends on ``output_type``)
194
195
"""
196
+ output_type = validate_output_table_type (output_type , outfile = outfile )
197
+
195
198
with Session () as lib :
196
199
file_contexts = []
197
200
for track in tracks :
@@ -216,35 +219,21 @@ def x2sys_cross(tracks=None, outfile=None, **kwargs):
216
219
else :
217
220
raise GMTInvalidInput (f"Unrecognized data type: { type (track )} " )
218
221
219
- with GMTTempFile ( suffix = ".txt" ) as tmpfile :
222
+ with lib . virtualfile_out ( kind = "dataset" , fname = outfile ) as vouttbl :
220
223
with contextlib .ExitStack () as stack :
221
224
fnames = [stack .enter_context (c ) for c in file_contexts ]
222
- if outfile is None :
223
- outfile = tmpfile .name
224
225
lib .call_module (
225
226
module = "x2sys_cross" ,
226
- args = build_arg_list (kwargs , infile = fnames , outfile = outfile ),
227
- )
228
-
229
- # Read temporary csv output to a pandas table
230
- if outfile == tmpfile .name : # if outfile isn't set, return pd.DataFrame
231
- # Read the tab-separated ASCII table
232
- date_format_kwarg = (
233
- {"date_format" : "ISO8601" }
234
- if Version (pd .__version__ ) >= Version ("2.0.0" )
235
- else {}
227
+ args = build_arg_list (kwargs , infile = fnames , outfile = vouttbl ),
236
228
)
237
- table = pd .read_csv (
238
- tmpfile .name ,
239
- sep = "\t " ,
240
- header = 2 , # Column names are on 2nd row
241
- comment = ">" , # Skip the 3rd row with a ">"
242
- parse_dates = [2 , 3 ], # Datetimes on 3rd and 4th column
243
- ** date_format_kwarg , # Parse dates in ISO8601 format on pandas>=2
229
+ result = lib .virtualfile_to_dataset (
230
+ vfname = vouttbl , output_type = output_type , header = 2
244
231
)
245
- # Remove the "# " from "# x" in the first column
246
- table = table .rename (columns = {table .columns [0 ]: table .columns [0 ][2 :]})
247
- elif outfile != tmpfile .name : # if outfile is set, output in outfile only
248
- table = None
249
232
250
- return table
233
+ # Convert 3rd and 4th columns to datetimes.
234
+ # These two columns have names "t_1"/"t_2" or "i_1"/"i_2".
235
+ # "t_1"/"t_2" means they are datetimes and should be converted.
236
+ # "i_1"/"i_2" means they are dummy times (i.e., floating-point values).
237
+ if output_type == "pandas" and result .columns [2 ] == "t_1" :
238
+ result .iloc [:, 2 :4 ] = result .iloc [:, 2 :4 ].apply (pd .to_datetime )
239
+ return result
0 commit comments