-
Notifications
You must be signed in to change notification settings - Fork 234
Wrap text #321
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Wrap text #321
Changes from all commits
93807e5
2501988
ff74e6d
7c3ba09
7a08ebd
084b1d5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,13 +2,19 @@ | |
| Base class with plot generating commands. | ||
| Does not define any special non-GMT methods (savefig, show, etc). | ||
| """ | ||
| import csv | ||
| import os | ||
| import numpy as np | ||
| import pandas as pd | ||
|
|
||
| from .clib import Session | ||
| from .exceptions import GMTInvalidInput | ||
| from .helpers import ( | ||
| build_arg_string, | ||
| dummy_context, | ||
| data_kind, | ||
| fmt_docstring, | ||
| GMTTempFile, | ||
| use_alias, | ||
| kwargs_to_strings, | ||
| ) | ||
|
|
@@ -574,3 +580,107 @@ def legend(self, spec=None, **kwargs): | |
| raise GMTInvalidInput("Unrecognized data type: {}".format(type(spec))) | ||
| arg_str = " ".join([specfile, build_arg_string(kwargs)]) | ||
| lib.call_module("legend", arg_str) | ||
|
|
||
| @fmt_docstring | ||
| @use_alias(R="region", J="projection") | ||
| @kwargs_to_strings( | ||
| R="sequence", | ||
| textfiles="sequence_space", | ||
| angle="sequence_comma", | ||
| font="sequence_comma", | ||
| justify="sequence_comma", | ||
| ) | ||
| def text( | ||
| self, | ||
| textfiles=None, | ||
| x=None, | ||
| y=None, | ||
| text=None, | ||
| angle=None, | ||
| font=None, | ||
| justify=None, | ||
| **kwargs, | ||
| ): | ||
| """ | ||
| Plot or typeset text on maps | ||
|
|
||
| Used to be pstext. | ||
|
|
||
| Takes in textfile(s) or (x,y,text) triples as input. | ||
|
|
||
| Must provide at least *textfiles* or *x*, *y*, and *text*. | ||
|
|
||
| Full option list at :gmt-docs:`text.html` | ||
|
|
||
| {aliases} | ||
|
|
||
| Parameters | ||
| ---------- | ||
| textfiles : str or list | ||
| A text data file name, or a list of filenames containing 1 or more records | ||
| with (x, y[, font, angle, justify], text). | ||
| x, y : float or 1d arrays | ||
| The x and y coordinates, or an array of x and y coordinates to plot the text | ||
| text : str or 1d array | ||
| The text string, or an array of strings to plot on the figure | ||
| angle: int/float or bool | ||
| Set the angle measured in degrees counter-clockwise from horizontal. E.g. 30 | ||
| sets the text at 30 degrees. If no angle is given then the input textfile(s) | ||
| must have this as a column. | ||
| font : str or bool | ||
| Set the font specification with format "size,font,color" where size is text | ||
| size in points, font is the font to use, and color sets the font color. E.g. | ||
| "12p,Helvetica-Bold,red" selects a 12p red Helvetica-Bold font. If no font | ||
| info is given then the input textfile(s) must have this information in one | ||
| of its columns. | ||
| justify: str or bool | ||
| Set the alignment which refers to the part of the text string that will be | ||
| mapped onto the (x,y) point. Choose a 2 character combination of L, C, R | ||
| (for left, center, or right) and T, M, B for top, middle, or bottom. E.g., | ||
| BL for lower left. If no justification is given then the input textfile(s) | ||
| must have this as a column. | ||
| {J} | ||
| {R} | ||
| """ | ||
| kwargs = self._preprocess(**kwargs) | ||
|
|
||
| kind = data_kind(textfiles, x, y, text) | ||
| if kind == "vectors" and text is None: | ||
| raise GMTInvalidInput("Must provide text with x and y.") | ||
| if kind == "file": | ||
| for textfile in textfiles.split(" "): # ensure that textfile(s) exist | ||
| if not os.path.exists(textfile): | ||
| raise GMTInvalidInput(f"Cannot find the file: {textfile}") | ||
|
|
||
| if angle is not None or font is not None or justify is not None: | ||
| if "F" not in kwargs.keys(): | ||
| kwargs.update({"F": ""}) | ||
| if angle is not None and isinstance(angle, (int, float)): | ||
| kwargs["F"] += f"+a{str(angle)}" | ||
| if font is not None and isinstance(font, str): | ||
| kwargs["F"] += f"+f{font}" | ||
| if justify is not None and isinstance(justify, str): | ||
| kwargs["F"] += f"+j{justify}" | ||
|
|
||
| with GMTTempFile(suffix=".txt") as tmpfile: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should look into passing text though virtual files, actually. I was avoiding this a while ago because the API had changed but that has been standardized now in GMT.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, I don't quite get your intention here? Do you mean passing |
||
| with Session() as lib: | ||
| if kind == "file": | ||
| fname = textfiles | ||
| elif kind == "vectors": | ||
| pd.DataFrame.from_dict( | ||
| { | ||
| "x": np.atleast_1d(x), | ||
| "y": np.atleast_1d(y), | ||
| "text": np.atleast_1d(text), | ||
| } | ||
| ).to_csv( | ||
| tmpfile.name, | ||
| sep="\t", | ||
| header=False, | ||
| index=False, | ||
| quoting=csv.QUOTE_NONE, | ||
| ) | ||
| fname = tmpfile.name | ||
|
|
||
| arg_str = " ".join([fname, build_arg_string(kwargs)]) | ||
| lib.call_module("text", arg_str) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| 105.87 21.02 LM HANOI | ||
| 282.95 -12.1 LM LIMA | ||
| 178.42 -18.13 LM SUVA | ||
| 237.67 47.58 RM SEATTLE | ||
| 28.20 -25.75 LM PRETORIA |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,173 @@ | ||
| # pylint: disable=redefined-outer-name | ||
| """ | ||
| Tests text | ||
| """ | ||
| import os | ||
|
|
||
| import pytest | ||
|
|
||
| from .. import Figure | ||
| from ..exceptions import GMTInvalidInput | ||
|
|
||
| TEST_DATA_DIR = os.path.join(os.path.dirname(__file__), "data") | ||
| POINTS_DATA = os.path.join(TEST_DATA_DIR, "points.txt") | ||
| CITIES_DATA = os.path.join(TEST_DATA_DIR, "cities.txt") | ||
|
|
||
|
|
||
| @pytest.fixture(scope="module") | ||
| def projection(): | ||
| "The projection system" | ||
| return "x4i" | ||
|
|
||
|
|
||
| @pytest.fixture(scope="module") | ||
| def region(): | ||
| "The data region" | ||
| return [0, 5, 0, 2.5] | ||
|
|
||
|
|
||
| @pytest.mark.mpl_image_compare | ||
| def test_text_single_line_of_text(region, projection): | ||
| """ | ||
| Place a single line text of text at some x, y location | ||
| """ | ||
| fig = Figure() | ||
| fig.text( | ||
| region=region, | ||
| projection=projection, | ||
| x=1.2, | ||
| y=2.4, | ||
| text="This is a line of text", | ||
| ) | ||
| return fig | ||
|
|
||
|
|
||
| @pytest.mark.mpl_image_compare | ||
| def test_text_multiple_lines_of_text(region, projection): | ||
| """ | ||
| Place multiple lines of text at their respective x, y locations | ||
| """ | ||
| fig = Figure() | ||
| fig.text( | ||
| region=region, | ||
| projection=projection, | ||
| x=[1.2, 1.6], | ||
| y=[0.6, 0.3], | ||
| text=["This is a line of text", "This is another line of text"], | ||
| ) | ||
| return fig | ||
|
|
||
|
|
||
| def test_text_without_text_input(region, projection): | ||
| """ | ||
| Run text by passing in x and y, but no text | ||
| """ | ||
| fig = Figure() | ||
| with pytest.raises(GMTInvalidInput): | ||
| fig.text(region=region, projection=projection, x=1.2, y=2.4) | ||
|
|
||
|
|
||
| @pytest.mark.mpl_image_compare | ||
| def test_text_input_single_filename(): | ||
| """ | ||
| Run text by passing in one filename to textfiles | ||
| """ | ||
| fig = Figure() | ||
| fig.text(region=[10, 70, -5, 10], textfiles=POINTS_DATA) | ||
| return fig | ||
|
|
||
|
|
||
| @pytest.mark.mpl_image_compare | ||
| def test_text_input_multiple_filenames(): | ||
| """ | ||
| Run text by passing in multiple filenames to textfiles | ||
| """ | ||
| fig = Figure() | ||
| fig.text(region=[10, 70, -30, 10], textfiles=[POINTS_DATA, CITIES_DATA]) | ||
| return fig | ||
|
|
||
|
|
||
| def test_text_nonexistent_filename(): | ||
| """ | ||
| Run text by passing in a list of filenames with one that does not exist | ||
| """ | ||
| fig = Figure() | ||
| with pytest.raises(GMTInvalidInput): | ||
| fig.text(region=[10, 70, -5, 10], textfiles=[POINTS_DATA, "notexist.txt"]) | ||
|
|
||
|
|
||
| @pytest.mark.mpl_image_compare | ||
| def test_text_angle_30(region, projection): | ||
| """ | ||
| Print text at 30 degrees counter-clockwise from horizontal | ||
| """ | ||
| fig = Figure() | ||
| fig.text( | ||
| region=region, | ||
| projection=projection, | ||
| x=1.2, | ||
| y=2.4, | ||
| text="text angle 30 degrees", | ||
| angle=30, | ||
| ) | ||
| return fig | ||
|
|
||
|
|
||
| @pytest.mark.mpl_image_compare | ||
| def test_text_font_bold(region, projection): | ||
| """ | ||
| Print text with a bold font | ||
| """ | ||
| fig = Figure() | ||
| fig.text( | ||
| region=region, | ||
| projection=projection, | ||
| x=1.2, | ||
| y=2.4, | ||
| text="text in bold", | ||
| font="Helvetica-Bold", | ||
| ) | ||
| return fig | ||
|
|
||
|
|
||
| @pytest.mark.mpl_image_compare | ||
| def test_text_justify_bottom_right_and_top_left(region, projection): | ||
| """ | ||
| Print text justified at bottom right and top left | ||
| """ | ||
| fig = Figure() | ||
| fig.text( | ||
| region=region, | ||
| projection=projection, | ||
| x=1.2, | ||
| y=0.2, | ||
| text="text justified bottom right", | ||
| justify="BR", | ||
| ) | ||
| fig.text( | ||
| region=region, | ||
| projection=projection, | ||
| x=1.2, | ||
| y=0.2, | ||
| text="text justified top left", | ||
| justify="TL", | ||
| ) | ||
| return fig | ||
|
|
||
|
|
||
| @pytest.mark.mpl_image_compare | ||
| def test_text_justify_parsed_from_textfile(): | ||
| """ | ||
| Print text justified based on a column from textfile, using justify=True boolean | ||
| operation. Loosely based on "All great-circle paths lead to Rome" gallery example at | ||
| https://gmt.soest.hawaii.edu/doc/latest/gallery/ex23.html | ||
| """ | ||
| fig = Figure() | ||
| fig.text( | ||
| region="g", | ||
| projection="H90/9i", | ||
| justify=True, | ||
| textfiles=CITIES_DATA, | ||
| D="j0.45/0+vred", # draw red-line from xy point to text label (city name) | ||
| ) | ||
| return fig |
Uh oh!
There was an error while loading. Please reload this page.