@@ -1324,37 +1324,36 @@ def open_virtual_file(self, family, geometry, direction, data):
1324
1324
return self .open_virtualfile (family , geometry , direction , data )
1325
1325
1326
1326
@contextlib .contextmanager
1327
- def virtualfile_from_vectors (self , * vectors ):
1327
+ def virtualfile_from_vectors (
1328
+ self , vectors : Sequence , * args
1329
+ ) -> Generator [str , None , None ]:
1328
1330
"""
1329
- Store 1-D arrays as columns of a table inside a virtual file.
1331
+ Store a sequence of 1-D vectors as columns of a dataset inside a virtual file.
1330
1332
1331
- Use the virtual file name to pass in the data in your vectors to a GMT
1332
- module.
1333
+ Use the virtual file name to pass the dataset with your vectors to a GMT module.
1333
1334
1334
- Context manager (use in a ``with`` block). Yields the virtual file name
1335
- that you can pass as an argument to a GMT module call. Closes the
1336
- virtual file upon exit of the ``with`` block.
1335
+ Context manager (use in a ``with`` block). Yields the virtual file name that you
1336
+ can pass as an argument to a GMT module call. Closes the virtual file upon exit
1337
+ of the ``with`` block.
1337
1338
1338
- Use this instead of creating the data container and virtual file by
1339
- hand with :meth:`pygmt.clib.Session.create_data`,
1340
- :meth:`pygmt.clib.Session.put_vector`, and
1341
- :meth:`pygmt.clib.Session.open_virtualfile`.
1339
+ Use this instead of creating the data container and virtual file by hand with
1340
+ :meth:`pygmt.clib.Session.create_data`, :meth:`pygmt.clib.Session.put_vector`,
1341
+ and :meth:`pygmt.clib.Session.open_virtualfile`.
1342
1342
1343
- If the arrays are C contiguous blocks of memory, they will be passed
1344
- without copying to GMT. If they are not (e.g., they are columns of a
1345
- 2-D array), they will need to be copied to a contiguous block.
1343
+ If the arrays are C contiguous blocks of memory, they will be passed without
1344
+ copying to GMT. If they are not (e.g., they are columns of a 2-D array), they
1345
+ will need to be copied to a contiguous block.
1346
1346
1347
1347
Parameters
1348
1348
----------
1349
- vectors : 1-D arrays
1350
- The vectors that will be included in the array . All must be of the
1349
+ vectors
1350
+ A sequence of vectors that will be stored in the dataset . All must be of the
1351
1351
same size.
1352
1352
1353
1353
Yields
1354
1354
------
1355
- fname : str
1356
- The name of virtual file. Pass this as a file name argument to a
1357
- GMT module.
1355
+ fname
1356
+ The name of virtual file. Pass this as a file name argument to a GMT module.
1358
1357
1359
1358
Examples
1360
1359
--------
@@ -1366,34 +1365,49 @@ def virtualfile_from_vectors(self, *vectors):
1366
1365
>>> y = np.array([4, 5, 6])
1367
1366
>>> z = pd.Series([7, 8, 9])
1368
1367
>>> with Session() as ses:
1369
- ... with ses.virtualfile_from_vectors(x, y, z) as fin:
1368
+ ... with ses.virtualfile_from_vectors(( x, y, z) ) as fin:
1370
1369
... # Send the output to a file so that we can read it
1371
1370
... with GMTTempFile() as fout:
1372
1371
... ses.call_module("info", [fin, f"->{fout.name}"])
1373
1372
... print(fout.read().strip())
1374
1373
<vector memory>: N = 3 <1/3> <4/6> <7/9>
1375
1374
"""
1376
- # Conversion to a C-contiguous array needs to be done here and not in
1377
- # put_vector or put_strings because we need to maintain a reference to
1378
- # the copy while it is being used by the C API. Otherwise, the array
1379
- # would be garbage collected and the memory freed. Creating it in this
1380
- # context manager guarantees that the copy will be around until the
1381
- # virtual file is closed. The conversion is implicit in
1375
+ # "*args" is added in v0.14.0 for backward-compatibility with the deprecated
1376
+ # syntax of passing multiple vectors as positional arguments.
1377
+ # Remove it in v0.16.0.
1378
+ if len (args ) > 0 :
1379
+ msg = (
1380
+ "Passing multiple arguments to Session.virtualfile_from_vectors is "
1381
+ "deprecated since v0.14.0 and will be unsupported in v0.16.0. "
1382
+ "Put all vectors in a sequence (a tuple or a list) instead and pass "
1383
+ "the sequence as the single argument to this function. "
1384
+ "E.g., use `with lib.virtualfile_from_vectors((x, y, z)) as vfile` "
1385
+ "instead of `with lib.virtualfile_from_vectors(x, y, z) as vfile`."
1386
+ )
1387
+ warnings .warn (message = msg , category = FutureWarning , stacklevel = 3 )
1388
+ vectors = (vectors , * args )
1389
+
1390
+ # Conversion to a C-contiguous array needs to be done here and not in put_vector
1391
+ # or put_strings because we need to maintain a reference to the copy while it is
1392
+ # being used by the C API. Otherwise, the array would be garbage collected and
1393
+ # the memory freed. Creating it in this context manager guarantees that the copy
1394
+ # will be around until the virtual file is closed. The conversion is implicit in
1382
1395
# vectors_to_arrays.
1383
1396
arrays = vectors_to_arrays (vectors )
1384
1397
1385
1398
columns = len (arrays )
1386
- # Find arrays that are of string dtype from column 3 onwards
1387
- # Assumes that first 2 columns contains coordinates like longitude
1388
- # latitude, or datetime string types.
1399
+ # Find arrays that are of string dtype from column 3 onwards. Assumes that first
1400
+ # 2 columns contains coordinates like longitude, latitude, or datetime string
1401
+ # types.
1389
1402
for col , array in enumerate (arrays [2 :]):
1390
1403
if pd .api .types .is_string_dtype (array .dtype ):
1391
1404
columns = col + 2
1392
1405
break
1393
1406
1394
1407
rows = len (arrays [0 ])
1395
1408
if not all (len (i ) == rows for i in arrays ):
1396
- raise GMTInvalidInput ("All arrays must have same size." )
1409
+ msg = "All arrays must have same size."
1410
+ raise GMTInvalidInput (msg )
1397
1411
1398
1412
family = "GMT_IS_DATASET|GMT_VIA_VECTOR"
1399
1413
geometry = "GMT_IS_POINT"
@@ -1406,8 +1420,8 @@ def virtualfile_from_vectors(self, *vectors):
1406
1420
for col , array in enumerate (arrays [:columns ]):
1407
1421
self .put_vector (dataset , column = col , vector = array )
1408
1422
1409
- # Use put_strings for last column(s) with string type data
1410
- # Have to use modifier "GMT_IS_DUPLICATE" to duplicate the strings
1423
+ # Use put_strings for last column(s) with string type data.
1424
+ # Have to use modifier "GMT_IS_DUPLICATE" to duplicate the strings.
1411
1425
string_arrays = arrays [columns :]
1412
1426
if string_arrays :
1413
1427
if len (string_arrays ) == 1 :
@@ -1682,7 +1696,7 @@ def virtualfile_from_stringio(
1682
1696
seg .header = None
1683
1697
seg .text = None
1684
1698
1685
- def virtualfile_in ( # noqa: PLR0912
1699
+ def virtualfile_in (
1686
1700
self ,
1687
1701
check_kind = None ,
1688
1702
data = None ,
@@ -1781,19 +1795,18 @@ def virtualfile_in( # noqa: PLR0912
1781
1795
"vectors" : self .virtualfile_from_vectors ,
1782
1796
}[kind ]
1783
1797
1784
- # Ensure the data is an iterable (Python list or tuple).
1798
+ # "_data" is the data that will be passed to the _virtualfile_from function.
1799
+ # "_data" defaults to "data" but should be adjusted for some cases.
1800
+ _data = data
1785
1801
match kind :
1786
- case "arg" | "file" | "geojson" | "grid" | "image" | "stringio" :
1787
- _data = (data ,)
1788
- if kind == "image" and data .dtype != "uint8" :
1789
- msg = (
1790
- f"Input image has dtype: { data .dtype } which is unsupported, "
1791
- "and may result in an incorrect output. Please recast image "
1792
- "to a uint8 dtype and/or scale to 0-255 range, e.g. "
1793
- "using a histogram equalization function like "
1794
- "skimage.exposure.equalize_hist."
1795
- )
1796
- warnings .warn (message = msg , category = RuntimeWarning , stacklevel = 2 )
1802
+ case "image" if data .dtype != "uint8" :
1803
+ msg = (
1804
+ f"Input image has dtype: { data .dtype } which is unsupported, and "
1805
+ "may result in an incorrect output. Please recast image to a uint8 "
1806
+ "dtype and/or scale to 0-255 range, e.g. using a histogram "
1807
+ "equalization function like skimage.exposure.equalize_hist."
1808
+ )
1809
+ warnings .warn (message = msg , category = RuntimeWarning , stacklevel = 2 )
1797
1810
case "empty" : # data is None, so data must be given via x/y/z.
1798
1811
_data = [x , y ]
1799
1812
if z is not None :
@@ -1808,19 +1821,17 @@ def virtualfile_in( # noqa: PLR0912
1808
1821
else :
1809
1822
# Python list, tuple, numpy.ndarray, and pandas.Series types
1810
1823
_data = np .atleast_2d (np .asanyarray (data ).T )
1811
- case "matrix" :
1824
+ case "matrix" if data . dtype . kind not in "iuf" :
1812
1825
# GMT can only accept a 2-D matrix which are signed integer (i),
1813
1826
# unsigned integer (u) or floating point (f) types. For other data
1814
1827
# types, we need to use virtualfile_from_vectors instead, which turns
1815
1828
# the matrix into a list of vectors and allows for better handling of
1816
1829
# non-integer/float type inputs (e.g. for string or datetime data types)
1817
- _data = (data ,)
1818
- if data .dtype .kind not in "iuf" :
1819
- _virtualfile_from = self .virtualfile_from_vectors
1820
- _data = data .T
1830
+ _virtualfile_from = self .virtualfile_from_vectors
1831
+ _data = data .T
1821
1832
1822
1833
# Finally create the virtualfile from the data, to be passed into GMT
1823
- file_context = _virtualfile_from (* _data )
1834
+ file_context = _virtualfile_from (_data )
1824
1835
return file_context
1825
1836
1826
1837
def virtualfile_from_data (
0 commit comments