4
4
"""
5
5
import contextlib
6
6
import csv
7
- import os
8
7
import numpy as np
9
8
import pandas as pd
10
9
@@ -813,7 +812,15 @@ def legend(self, spec=None, position="JTR+jTR+o0.2c", box="+gwhite+p1p", **kwarg
813
812
lib .call_module ("legend" , arg_str )
814
813
815
814
@fmt_docstring
816
- @use_alias (R = "region" , J = "projection" , B = "frame" )
815
+ @use_alias (
816
+ R = "region" ,
817
+ J = "projection" ,
818
+ B = "frame" ,
819
+ C = "clearance" ,
820
+ D = "offset" ,
821
+ G = "fill" ,
822
+ W = "pen" ,
823
+ )
817
824
@kwargs_to_strings (
818
825
R = "sequence" ,
819
826
textfiles = "sequence_space" ,
@@ -826,20 +833,22 @@ def text(
826
833
textfiles = None ,
827
834
x = None ,
828
835
y = None ,
836
+ position = None ,
829
837
text = None ,
830
838
angle = None ,
831
839
font = None ,
832
840
justify = None ,
833
841
** kwargs ,
834
842
):
835
843
"""
836
- Plot or typeset text on maps
844
+ Plot or typeset text strings of variable size, font type, and
845
+ orientation.
837
846
838
- Used to be pstext.
847
+ Must provide at least one of the following combinations as input:
839
848
840
- Takes in textfile(s) or (x,y,text) triples as input.
841
-
842
- Must provide at least *textfiles* or *x*, *y*, and *text*.
849
+ - *textfiles*
850
+ - *x*, *y*, and *text*
851
+ - *position* and *text*
843
852
844
853
Full option list at :gmt-docs:`text.html`
845
854
@@ -849,70 +858,128 @@ def text(
849
858
----------
850
859
textfiles : str or list
851
860
A text data file name, or a list of filenames containing 1 or more
852
- records with (x, y[, font, angle , justify], text).
861
+ records with (x, y[, angle, font , justify], text).
853
862
x/y : float or 1d arrays
854
863
The x and y coordinates, or an array of x and y coordinates to plot
855
864
the text
865
+ position : str
866
+ Sets reference point on the map for the text by using x,y
867
+ coordinates extracted from *region* instead of providing them
868
+ through *x* and *y*. Specify with a two letter (order independent)
869
+ code, chosen from:
870
+
871
+ * Horizontal: L(eft), C(entre), R(ight)
872
+ * Vertical: T(op), M(iddle), B(ottom)
873
+
874
+ For example, position="TL" plots the text at the Upper Left corner
875
+ of the map.
856
876
text : str or 1d array
857
877
The text string, or an array of strings to plot on the figure
858
- angle: int, float or bool
878
+ angle: int, float, str or bool
859
879
Set the angle measured in degrees counter-clockwise from
860
880
horizontal. E.g. 30 sets the text at 30 degrees. If no angle is
861
- given then the input textfile(s) must have this as a column.
881
+ explicitly given (i.e. angle=True) then the input textfile(s) must
882
+ have this as a column.
862
883
font : str or bool
863
884
Set the font specification with format "size,font,color" where size
864
885
is text size in points, font is the font to use, and color sets the
865
886
font color. E.g. "12p,Helvetica-Bold,red" selects a 12p red
866
- Helvetica-Bold font. If no font info is given then the input
867
- textfile(s) must have this information in one of its columns.
868
- justify: str or bool
887
+ Helvetica-Bold font. If no font info is explicitly given (i.e.
888
+ font=True), then the input textfile(s) must have this information
889
+ in one of its columns.
890
+ justify : str or bool
869
891
Set the alignment which refers to the part of the text string that
870
892
will be mapped onto the (x,y) point. Choose a 2 character
871
893
combination of L, C, R (for left, center, or right) and T, M, B for
872
894
top, middle, or bottom. E.g., BL for lower left. If no
873
- justification is given then the input textfile(s) must have this as
874
- a column.
895
+ justification is explicitly given (i.e. justify=True), then the
896
+ input textfile(s) must have this as a column.
875
897
{J}
876
898
{R}
899
+ clearance : str
900
+ ``[dx/dy][+to|O|c|C]``
901
+ Adjust the clearance between the text and the surrounding box
902
+ [15%]. Only used if *pen* or *fill* are specified. Append the unit
903
+ you want ('c' for cm, 'i' for inch, or 'p' for point; if not given
904
+ we consult 'PROJ_LENGTH_UNIT') or '%' for a percentage of the
905
+ font size. Optionally, use modifier '+t' to set the shape of the
906
+ textbox when using *fill* and/or *pen*. Append lower case 'o' to
907
+ get a straight rectangle [Default]. Append upper case 'O' to get a
908
+ rounded rectangle. In paragraph mode (*paragraph*) you can also
909
+ append lower case 'c' to get a concave rectangle or append upper
910
+ case 'C' to get a convex rectangle.
911
+ fill : str
912
+ Sets the shade or color used for filling the text box [Default is
913
+ no fill].
914
+ offset : str
915
+ ``[j|J]dx[/dy][+v[pen]]``
916
+ Offsets the text from the projected (x,y) point by dx,dy [0/0]. If
917
+ dy is not specified then it is set equal to dx. Use offset='j' to
918
+ offset the text away from the point instead (i.e., the text
919
+ justification will determine the direction of the shift). Using
920
+ offset='J' will shorten diagonal offsets at corners by sqrt(2).
921
+ Optionally, append '+v' which will draw a line from the original
922
+ point to the shifted point; append a pen to change the attributes
923
+ for this line.
924
+ pen : str
925
+ Sets the pen used to draw a rectangle around the text string
926
+ (see *clearance*) [Default is width = default, color = black,
927
+ style = solid].
877
928
"""
878
929
kwargs = self ._preprocess (** kwargs )
879
930
880
- kind = data_kind (textfiles , x , y , text )
881
- if kind == "vectors" and text is None :
882
- raise GMTInvalidInput ("Must provide text with x and y." )
883
- if kind == "file" :
884
- for textfile in textfiles .split (" " ): # ensure that textfile(s) exist
885
- if not os .path .exists (textfile ):
886
- raise GMTInvalidInput (f"Cannot find the file: { textfile } " )
931
+ # Ensure inputs are either textfiles, x/y/text, or position/text
932
+ if position is None :
933
+ kind = data_kind (textfiles , x , y , text )
934
+ elif position is not None :
935
+ if x is not None or y is not None :
936
+ raise GMTInvalidInput (
937
+ "Provide either position only, or x/y pairs, not both"
938
+ )
939
+ kind = "vectors"
887
940
888
- if angle is not None or font is not None or justify is not None :
941
+ if kind == "vectors" and text is None :
942
+ raise GMTInvalidInput ("Must provide text with x/y pairs or position" )
943
+
944
+ # Build the `-F` argument in gmt text.
945
+ if (
946
+ position is not None
947
+ or angle is not None
948
+ or font is not None
949
+ or justify is not None
950
+ ):
889
951
if "F" not in kwargs .keys ():
890
952
kwargs .update ({"F" : "" })
891
- if angle is not None and isinstance (angle , (int , float )):
953
+ if angle is not None and isinstance (angle , (int , float , str )):
892
954
kwargs ["F" ] += f"+a{ str (angle )} "
893
955
if font is not None and isinstance (font , str ):
894
956
kwargs ["F" ] += f"+f{ font } "
895
957
if justify is not None and isinstance (justify , str ):
896
958
kwargs ["F" ] += f"+j{ justify } "
959
+ if position is not None and isinstance (position , str ):
960
+ kwargs ["F" ] += f'+c{ position } +t"{ text } "'
897
961
898
962
with GMTTempFile (suffix = ".txt" ) as tmpfile :
899
963
with Session () as lib :
900
964
if kind == "file" :
901
965
fname = textfiles
902
966
elif kind == "vectors" :
903
- pd .DataFrame .from_dict (
904
- {
905
- "x" : np .atleast_1d (x ),
906
- "y" : np .atleast_1d (y ),
907
- "text" : np .atleast_1d (text ),
908
- }
909
- ).to_csv (
910
- tmpfile .name ,
911
- sep = "\t " ,
912
- header = False ,
913
- index = False ,
914
- quoting = csv .QUOTE_NONE ,
915
- )
967
+ if position is not None :
968
+ fname = ""
969
+ else :
970
+ pd .DataFrame .from_dict (
971
+ {
972
+ "x" : np .atleast_1d (x ),
973
+ "y" : np .atleast_1d (y ),
974
+ "text" : np .atleast_1d (text ),
975
+ }
976
+ ).to_csv (
977
+ tmpfile .name ,
978
+ sep = "\t " ,
979
+ header = False ,
980
+ index = False ,
981
+ quoting = csv .QUOTE_NONE ,
982
+ )
916
983
fname = tmpfile .name
917
984
918
985
arg_str = " " .join ([fname , build_arg_string (kwargs )])
0 commit comments