Skip to content

Commit 40bc7f9

Browse files
committed
Merge branch 'master' into grid_registration
2 parents 5a685b2 + f401f85 commit 40bc7f9

File tree

11 files changed

+190
-87
lines changed

11 files changed

+190
-87
lines changed

.azure-pipelines.yml

Lines changed: 0 additions & 65 deletions
This file was deleted.

.github/workflows/ci_tests.yaml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,30 @@ on:
1212
- cron: '0 0 * * *'
1313

1414
jobs:
15+
style_check:
16+
name: Style Checks
17+
runs-on: ubuntu-latest
18+
19+
steps:
20+
# Checkout current git repository
21+
- name: Checkout
22+
uses: actions/[email protected]
23+
24+
# Setup Miniconda
25+
- name: Set up Python
26+
uses: actions/setup-python@v2
27+
with:
28+
python-version: 3.7
29+
30+
- name: Install packages
31+
run: pip install black flake8 pylint
32+
33+
- name: Formatting check (black and flake8)
34+
run: make check
35+
36+
- name: Linting (pylint)
37+
run: make lint
38+
1539
test:
1640
name: ${{ matrix.os }} - Python ${{ matrix.python-version }}
1741
runs-on: ${{ matrix.os }}

MAINTENANCE.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,11 @@ The main advantages of this are:
4040

4141
## Continuous Integration
4242

43-
We use TravisCI continuous integration (CI) services to build and test the
44-
project on Linux, and Mac (Windows is still a work in progress).
45-
The configuration file for this service is `.travis.yml`.
46-
It relies on the `requirements.txt` file to install the required dependencies using
43+
We use Github Actions and TravisCI continuous integration (CI) services to
44+
build and test the project on Linux, macOS and Windows.
45+
The configuration file for these services are in
46+
`.github/workflows/ci_tests.yaml` and `.travis.yml`.
47+
They rely on the `requirements.txt` file to install required dependencies using
4748
conda and the `Makefile` to run the tests and checks.
4849

4950
Travis also handles all of our deployments automatically:

README.rst

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@ PyGMT
1212
.. image:: http://img.shields.io/travis/GenericMappingTools/pygmt/master.svg?style=flat-square&label=Linux
1313
:alt: Travis CI build status
1414
:target: https://travis-ci.org/GenericMappingTools/pygmt
15-
.. image:: https://img.shields.io/azure-devops/build/GenericMappingTools/7682ad4e-76bb-4775-849e-7c4f8dce4e1a/3/master.svg?label=Mac|Windows&style=flat-square
16-
:alt: Azure Pipelines build status
17-
:target: https://dev.azure.com/GenericMappingTools/PyGMT/_build
1815
.. image:: https://github.com/GenericMappingTools/pygmt/workflows/Tests/badge.svg
1916
:alt: GitHub Actions build status
2017
:target: https://github.com/GenericMappingTools/pygmt/actions

examples/gallery/grid/grdview_surface.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
elevation angle of the view. We provide a list of two arguments to ``frame`` — the
99
second argument, prepended with ``"z"``, specifies the :math:`z`-axis frame attributes.
1010
Specifying the same scale for the ``projection`` and ``zcale`` arguments ensures equal
11-
axis scaling.
11+
axis scaling. The ``shading`` argument specifies illumination; here we choose an azimuth of
12+
45° with ``shading="+a45"``.
1213
"""
1314

1415
import pygmt
@@ -44,7 +45,8 @@ def ackley(x, y):
4445
zscale=f"{SCALE}i",
4546
surftype="s",
4647
cmap="roma",
47-
perspective="135/30",
48+
perspective=[135, 30], # Azimuth southeast (135°), at elevation 30°
49+
shading="+a45",
4850
)
4951

5052
fig.show()

pygmt/base_plotting.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,7 @@ def grdimage(self, grid, **kwargs):
336336
Wm="meshpen",
337337
Wf="facadepen",
338338
p="perspective",
339+
I="shading",
339340
)
340341
@kwargs_to_strings(R="sequence", p="sequence")
341342
def grdview(self, grid, **kwargs):
@@ -403,6 +404,15 @@ def grdview(self, grid, **kwargs):
403404
``'[x|y|z]azim[/elev[/zlevel]][+wlon0/lat0[/z0]][+vx0/y0]'``.
404405
Select perspective view.
405406
407+
shading : str
408+
Provide the name of a grid file with intensities in the (-1,+1)
409+
range, or a constant intensity to apply everywhere (affects the
410+
ambient light). Alternatively, derive an intensity grid from the
411+
input data grid reliefgrid via a call to ``grdgradient``; append
412+
``+aazimuth``, ``+nargs``, and ``+mambient`` to specify azimuth,
413+
intensity, and ambient arguments for that module, or just give
414+
``+d`` to select the default arguments (``+a-45+nt1+m0``).
415+
406416
"""
407417
kwargs = self._preprocess(**kwargs)
408418
kind = data_kind(grid, None, None)

pygmt/clib/conversion.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Functions to convert data types into ctypes friendly formats.
33
"""
44
import numpy as np
5+
import pandas as pd
56

67
from ..exceptions import GMTInvalidInput
78

@@ -242,3 +243,82 @@ def kwargs_to_ctypes_array(argument, kwargs, dtype):
242243
if argument in kwargs:
243244
return dtype(*kwargs[argument])
244245
return None
246+
247+
248+
def array_to_datetime(array):
249+
"""
250+
Convert an 1d datetime array from various types into pandas.DatetimeIndex
251+
(i.e., numpy.datetime64).
252+
253+
If the input array is not in legal datetime formats, raise a "ParseError"
254+
exception.
255+
256+
Parameters
257+
----------
258+
array : list or 1d array
259+
The input datetime array in various formats.
260+
261+
Supported types:
262+
263+
- str
264+
- numpy.datetime64
265+
- pandas.DateTimeIndex
266+
- datetime.datetime and datetime.date
267+
268+
Returns
269+
-------
270+
array : 1d datetime array in pandas.DatetimeIndex (i.e., numpy.datetime64)
271+
272+
Examples
273+
--------
274+
>>> import datetime
275+
>>> # numpy.datetime64 array
276+
>>> x = np.array(
277+
... ["2010-06-01", "2011-06-01T12", "2012-01-01T12:34:56"],
278+
... dtype="datetime64",
279+
... )
280+
>>> array_to_datetime(x)
281+
DatetimeIndex(['2010-06-01 00:00:00', '2011-06-01 12:00:00',
282+
'2012-01-01 12:34:56'],
283+
dtype='datetime64[ns]', freq=None)
284+
285+
>>> # pandas.DateTimeIndex array
286+
>>> x = pd.date_range("2013", freq="YS", periods=3)
287+
>>> array_to_datetime(x) # doctest: +NORMALIZE_WHITESPACE
288+
DatetimeIndex(['2013-01-01', '2014-01-01', '2015-01-01'],
289+
dtype='datetime64[ns]', freq='AS-JAN')
290+
291+
>>> # Python's built-in date and datetime
292+
>>> x = [datetime.date(2018, 1, 1), datetime.datetime(2019, 1, 1)]
293+
>>> array_to_datetime(x) # doctest: +NORMALIZE_WHITESPACE
294+
DatetimeIndex(['2018-01-01', '2019-01-01'],
295+
dtype='datetime64[ns]', freq=None)
296+
297+
>>> # Raw datetime strings in various format
298+
>>> x = [
299+
... "2018",
300+
... "2018-02",
301+
... "2018-03-01",
302+
... "2018-04-01T01:02:03",
303+
... "5/1/2018",
304+
... "Jun 05, 2018",
305+
... "2018/07/02",
306+
... ]
307+
>>> array_to_datetime(x)
308+
DatetimeIndex(['2018-01-01 00:00:00', '2018-02-01 00:00:00',
309+
'2018-03-01 00:00:00', '2018-04-01 01:02:03',
310+
'2018-05-01 00:00:00', '2018-06-05 00:00:00',
311+
'2018-07-02 00:00:00'],
312+
dtype='datetime64[ns]', freq=None)
313+
314+
>>> # Mixed datetime types
315+
>>> x = [
316+
... "2018-01-01",
317+
... np.datetime64("2018-01-01"),
318+
... datetime.datetime(2018, 1, 1),
319+
... ]
320+
>>> array_to_datetime(x) # doctest: +NORMALIZE_WHITESPACE
321+
DatetimeIndex(['2018-01-01', '2018-01-01', '2018-01-01'],
322+
dtype='datetime64[ns]', freq=None)
323+
"""
324+
return pd.to_datetime(array)

pygmt/clib/session.py

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
vectors_to_arrays,
2323
dataarray_to_matrix,
2424
as_c_contiguous,
25+
array_to_datetime,
2526
)
2627

2728
FAMILIES = [
@@ -48,12 +49,13 @@
4849
REGISTRATIONS = ["GMT_GRID_PIXEL_REG", "GMT_GRID_NODE_REG"]
4950

5051
DTYPES = {
51-
"float64": "GMT_DOUBLE",
52-
"float32": "GMT_FLOAT",
53-
"int64": "GMT_LONG",
54-
"int32": "GMT_INT",
55-
"uint64": "GMT_ULONG",
56-
"uint32": "GMT_UINT",
52+
np.float64: "GMT_DOUBLE",
53+
np.float32: "GMT_FLOAT",
54+
np.int64: "GMT_LONG",
55+
np.int32: "GMT_INT",
56+
np.uint64: "GMT_ULONG",
57+
np.uint32: "GMT_UINT",
58+
np.datetime64: "GMT_DATETIME",
5759
}
5860

5961

@@ -710,15 +712,22 @@ def _check_dtype_and_dim(self, array, ndim):
710712
True
711713
712714
"""
713-
if array.dtype.name not in DTYPES:
714-
raise GMTInvalidInput(
715-
"Unsupported numpy data type '{}'.".format(array.dtype.name)
716-
)
715+
# check the array has the given dimension
717716
if array.ndim != ndim:
718717
raise GMTInvalidInput(
719718
"Expected a numpy 1d array, got {}d.".format(array.ndim)
720719
)
721-
return self[DTYPES[array.dtype.name]]
720+
721+
# check the array has a valid/known data type
722+
if array.dtype.type not in DTYPES:
723+
try:
724+
# Try to convert any unknown numpy data types to np.datetime64
725+
array = np.asarray(array, dtype=np.datetime64)
726+
except ValueError:
727+
raise GMTInvalidInput(
728+
"Unsupported numpy data type '{}'.".format(array.dtype.type)
729+
)
730+
return self[DTYPES[array.dtype.type]]
722731

723732
def put_vector(self, dataset, column, vector):
724733
"""
@@ -764,7 +773,13 @@ def put_vector(self, dataset, column, vector):
764773
)
765774

766775
gmt_type = self._check_dtype_and_dim(vector, ndim=1)
767-
vector_pointer = vector.ctypes.data_as(ctp.c_void_p)
776+
if gmt_type == self["GMT_DATETIME"]:
777+
vector_pointer = (ctp.c_char_p * len(vector))()
778+
vector_pointer[:] = np.char.encode(
779+
np.datetime_as_string(array_to_datetime(vector))
780+
)
781+
else:
782+
vector_pointer = vector.ctypes.data_as(ctp.c_void_p)
768783
status = c_put_vector(
769784
self.session_pointer, dataset, column, gmt_type, vector_pointer
770785
)
17.8 KB
Loading

pygmt/tests/test_clib.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ def test_put_vector_invalid_dtype():
349349
mode="GMT_CONTAINER_ONLY",
350350
dim=[2, 3, 1, 0], # columns, rows, layers, dtype
351351
)
352-
data = np.array([37, 12, 556], dtype="complex128")
352+
data = np.array([37, 12, 556], dtype="object")
353353
with pytest.raises(GMTInvalidInput):
354354
lib.put_vector(dataset, column=1, vector=data)
355355

pygmt/tests/test_plot.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22
"""
33
Tests plot.
44
"""
5+
import datetime
56
import os
67

78
import numpy as np
9+
import pandas as pd
10+
import xarray as xr
11+
812
import pytest
913

1014
from .. import Figure
@@ -284,3 +288,38 @@ def test_plot_scalar_xy():
284288
fig.plot(x=0, y=0, style="t1c")
285289
fig.plot(x=1.5, y=-1.5, style="s1c")
286290
return fig
291+
292+
293+
@pytest.mark.mpl_image_compare
294+
def test_plot_datetime():
295+
"""Test various datetime input data"""
296+
fig = Figure()
297+
fig.basemap(projection="X15c/5c", region="2010-01-01/2020-01-01/0/10", frame=True)
298+
299+
# numpy.datetime64 types
300+
x = np.array(
301+
["2010-06-01", "2011-06-01T12", "2012-01-01T12:34:56"], dtype="datetime64"
302+
)
303+
y = [1.0, 2.0, 3.0]
304+
fig.plot(x, y, style="c0.2c", pen="1p")
305+
306+
# pandas.DatetimeIndex
307+
x = pd.date_range("2013", freq="YS", periods=3)
308+
y = [4, 5, 6]
309+
fig.plot(x, y, style="t0.2c", pen="1p")
310+
311+
# xarray.DataArray
312+
x = xr.DataArray(data=pd.date_range(start="2015-03", freq="QS", periods=3))
313+
y = [7.5, 6, 4.5]
314+
fig.plot(x, y, style="s0.2c", pen="1p")
315+
316+
# raw datetime strings
317+
x = ["2016-02-01", "2017-03-04T00:00"]
318+
y = [7, 8]
319+
fig.plot(x, y, style="a0.2c", pen="1p")
320+
321+
# the Python built-in datetime and date
322+
x = [datetime.date(2018, 1, 1), datetime.datetime(2019, 1, 1)]
323+
y = [8.5, 9.5]
324+
fig.plot(x, y, style="i0.2c", pen="1p")
325+
return fig

0 commit comments

Comments
 (0)