44
44
"GMT_IS_SURFACE" ,
45
45
]
46
46
47
+ METHODS = ["GMT_IS_DUPLICATE" , "GMT_IS_REFERENCE" ]
48
+
47
49
MODES = ["GMT_CONTAINER_ONLY" , "GMT_IS_OUTPUT" ]
48
50
49
51
REGISTRATIONS = ["GMT_GRID_PIXEL_REG" , "GMT_GRID_NODE_REG" ]
@@ -235,7 +237,7 @@ def __getitem__(self, name):
235
237
value = c_get_enum (session , name .encode ())
236
238
237
239
if value is None or value == - 99999 :
238
- raise GMTCLibError ("Constant '{}' doesn't exits in libgmt." . format ( name ) )
240
+ raise GMTCLibError (f "Constant '{ name } ' doesn't exist in libgmt." )
239
241
240
242
return value
241
243
@@ -733,7 +735,7 @@ def put_vector(self, dataset, column, vector):
733
735
"""
734
736
Attach a numpy 1D array as a column on a GMT dataset.
735
737
736
- Use this functions to attach numpy array data to a GMT dataset and pass
738
+ Use this function to attach numpy array data to a GMT dataset and pass
737
739
it to GMT modules. Wraps ``GMT_Put_Vector``.
738
740
739
741
The dataset must be created by :meth:`~gmt.clib.Session.create_data`
@@ -793,11 +795,72 @@ def put_vector(self, dataset, column, vector):
793
795
)
794
796
)
795
797
798
+ def put_strings (self , dataset , family , strings ):
799
+ """
800
+ Attach a numpy 1D array of dtype str as a column on a GMT dataset.
801
+
802
+ Use this function to attach string type numpy array data to a GMT
803
+ dataset and pass it to GMT modules. Wraps ``GMT_Put_Strings``.
804
+
805
+ The dataset must be created by :meth:`~gmt.clib.Session.create_data`
806
+ first.
807
+
808
+ .. warning::
809
+ The numpy array must be C contiguous in memory. If it comes from a
810
+ column slice of a 2d array, for example, you will have to make a
811
+ copy. Use :func:`numpy.ascontiguousarray` to make sure your vector
812
+ is contiguous (it won't copy if it already is).
813
+
814
+ Parameters
815
+ ----------
816
+ dataset : :class:`ctypes.c_void_p`
817
+ The ctypes void pointer to a ``GMT_Dataset``. Create it with
818
+ :meth:`~gmt.clib.Session.create_data`.
819
+ family : str
820
+ The family type of the dataset. Can be either ``GMT_IS_VECTOR`` or
821
+ ``GMT_IS_MATRIX``.
822
+ strings : numpy 1d-array
823
+ The array that will be attached to the dataset. Must be a 1d C
824
+ contiguous array.
825
+
826
+ Raises
827
+ ------
828
+ GMTCLibError
829
+ If given invalid input or ``GMT_Put_Strings`` exits with status !=
830
+ 0.
831
+
832
+ """
833
+ c_put_strings = self .get_libgmt_func (
834
+ "GMT_Put_Strings" ,
835
+ argtypes = [
836
+ ctp .c_void_p ,
837
+ ctp .c_uint ,
838
+ ctp .c_void_p ,
839
+ ctp .POINTER (ctp .c_char_p ),
840
+ ],
841
+ restype = ctp .c_int ,
842
+ )
843
+
844
+ strings_pointer = (ctp .c_char_p * len (strings ))()
845
+ strings_pointer [:] = np .char .encode (strings )
846
+
847
+ family_int = self ._parse_constant (
848
+ family , valid = FAMILIES , valid_modifiers = METHODS
849
+ )
850
+
851
+ status = c_put_strings (
852
+ self .session_pointer , family_int , dataset , strings_pointer
853
+ )
854
+ if status != 0 :
855
+ raise GMTCLibError (
856
+ f"Failed to put strings of type { strings .dtype } into dataset"
857
+ )
858
+
796
859
def put_matrix (self , dataset , matrix , pad = 0 ):
797
860
"""
798
861
Attach a numpy 2D array to a GMT dataset.
799
862
800
- Use this functions to attach numpy array data to a GMT dataset and pass
863
+ Use this function to attach numpy array data to a GMT dataset and pass
801
864
it to GMT modules. Wraps ``GMT_Put_Matrix``.
802
865
803
866
The dataset must be created by :meth:`~gmt.clib.Session.create_data`
@@ -1002,9 +1065,7 @@ def open_virtual_file(self, family, geometry, direction, data):
1002
1065
family_int = self ._parse_constant (family , valid = FAMILIES , valid_modifiers = VIAS )
1003
1066
geometry_int = self ._parse_constant (geometry , valid = GEOMETRIES )
1004
1067
direction_int = self ._parse_constant (
1005
- direction ,
1006
- valid = ["GMT_IN" , "GMT_OUT" ],
1007
- valid_modifiers = ["GMT_IS_REFERENCE" , "GMT_IS_DUPLICATE" ],
1068
+ direction , valid = ["GMT_IN" , "GMT_OUT" ], valid_modifiers = METHODS ,
1008
1069
)
1009
1070
1010
1071
buff = ctp .create_string_buffer (self ["GMT_VF_LEN" ])
@@ -1079,14 +1140,23 @@ def virtualfile_from_vectors(self, *vectors):
1079
1140
1080
1141
"""
1081
1142
# Conversion to a C-contiguous array needs to be done here and not in
1082
- # put_matrix because we need to maintain a reference to the copy while
1083
- # it is being used by the C API. Otherwise, the array would be garbage
1084
- # collected and the memory freed. Creating it in this context manager
1085
- # guarantees that the copy will be around until the virtual file is
1086
- # closed. The conversion is implicit in vectors_to_arrays.
1143
+ # put_vector or put_strings because we need to maintain a reference to
1144
+ # the copy while it is being used by the C API. Otherwise, the array
1145
+ # would be garbage collected and the memory freed. Creating it in this
1146
+ # context manager guarantees that the copy will be around until the
1147
+ # virtual file is closed. The conversion is implicit in
1148
+ # vectors_to_arrays.
1087
1149
arrays = vectors_to_arrays (vectors )
1088
1150
1089
1151
columns = len (arrays )
1152
+ # Find arrays that are of string dtype from column 3 onwards
1153
+ # Assumes that first 2 columns contains coordinates like longitude
1154
+ # latitude, or datetime string types.
1155
+ for col , array in enumerate (arrays [2 :]):
1156
+ if np .issubdtype (array .dtype , np .str_ ):
1157
+ columns = col + 2
1158
+ break
1159
+
1090
1160
rows = len (arrays [0 ])
1091
1161
if not all (len (i ) == rows for i in arrays ):
1092
1162
raise GMTInvalidInput ("All arrays must have same size." )
@@ -1098,9 +1168,24 @@ def virtualfile_from_vectors(self, *vectors):
1098
1168
family , geometry , mode = "GMT_CONTAINER_ONLY" , dim = [columns , rows , 1 , 0 ]
1099
1169
)
1100
1170
1101
- for col , array in enumerate (arrays ):
1171
+ # Use put_vector for columns with numerical type data
1172
+ for col , array in enumerate (arrays [:columns ]):
1102
1173
self .put_vector (dataset , column = col , vector = array )
1103
1174
1175
+ # Use put_strings for last column(s) with string type data
1176
+ # Have to use modifier "GMT_IS_DUPLICATE" to duplicate the strings
1177
+ string_arrays = arrays [columns :]
1178
+ if string_arrays :
1179
+ if len (string_arrays ) == 1 :
1180
+ strings = string_arrays [0 ]
1181
+ elif len (string_arrays ) > 1 :
1182
+ strings = np .apply_along_axis (
1183
+ func1d = " " .join , axis = 0 , arr = string_arrays
1184
+ )
1185
+ self .put_strings (
1186
+ dataset , family = "GMT_IS_VECTOR|GMT_IS_DUPLICATE" , strings = strings
1187
+ )
1188
+
1104
1189
with self .open_virtual_file (
1105
1190
family , geometry , "GMT_IN|GMT_IS_REFERENCE" , dataset
1106
1191
) as vfile :
0 commit comments