GLODAP 0.2__tar.gz → 0.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,30 +1,30 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: glodap
3
- Version: 0.2
3
+ Version: 0.4
4
4
  Summary: GLODAP downloader and importer in Python
5
5
  Classifier: Development Status :: 4 - Beta
6
- Classifier: Programming Language :: Python :: 3.10
7
6
  Classifier: Programming Language :: Python :: 3.11
8
7
  Classifier: Programming Language :: Python :: 3.12
9
8
  Classifier: Programming Language :: Python :: 3.13
9
+ Classifier: Programming Language :: Python :: 3.14
10
10
  Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
11
11
  Classifier: Operating System :: OS Independent
12
12
  Classifier: Natural Language :: English
13
13
  Classifier: Intended Audience :: Science/Research
14
14
  Classifier: Topic :: Scientific/Engineering :: Chemistry
15
15
  Classifier: Topic :: Scientific/Engineering :: Oceanography
16
- Requires-Python: >=3.10
16
+ Requires-Python: >=3.11
17
17
  Description-Content-Type: text/markdown
18
18
  License-File: LICENSE
19
- Requires-Dist: pandas
20
- Requires-Dist: requests
21
- Requires-Dist: scipy
19
+ Requires-Dist: pandas<4,>=3.0.3
20
+ Requires-Dist: pooch<2,>=1.9.0
21
+ Requires-Dist: scipy<2,>=1.17.1
22
22
  Dynamic: license-file
23
23
 
24
24
  # glodap
25
25
 
26
26
  [![Tests](https://github.com/mvdh7/glodap/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/mvdh7/glodap/actions)
27
- [![pypi badge](https://img.shields.io/pypi/v/GLODAP.svg?style=popout)](https://pypi.org/project/GLODAP/)
27
+ [![pypi badge](https://img.shields.io/pypi/v/glodap.svg?style=popout)](https://pypi.org/project/glodap/)
28
28
  [![Conda Version](https://img.shields.io/conda/vn/conda-forge/glodap.svg)](https://anaconda.org/conda-forge/glodap)
29
29
  [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
30
30
  [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
@@ -48,34 +48,41 @@ df_atlantic = glodap.atlantic()
48
48
  Files are saved by default at `"~/.glodap"`, but this can be controlled with the
49
49
  kwarg `gpath`. See the function docstrings for more information.
50
50
 
51
- The columns of the imported DataFrames are renamed so that they can be passed
52
- directly into [PyCO2SYS v2](https://github.com/mvdh7/PyCO2SYS):
51
+ The columns of the imported DataFrames can be passed
52
+ directly into [PyCO2SYS v2](https://mvdh.xyz/PyCO2SYS):
53
53
 
54
54
  ```python
55
55
  import PyCO2SYS as pyco2
56
- co2s_atlantic = pyco2.sys(data=df_atlantic, nitrite=None)
56
+ co2s_atlantic = pyco2.sys(data=df_atlantic.drop(columns="fco2"), nitrite=0)
57
57
  ```
58
58
 
59
- Note `nitrite=None` - this means PyCO2SYS will ignore the `"nitrite"` column,
60
- which is necessary because PyCO2SYS includes the nitrite-nitrous acid
61
- equilibrium but its equilibrium constant is valid only under lab conditions.
59
+ Note `nitrite=0` - this means PyCO2SYS will ignore the `"nitrite"` column,
60
+ which is necessary because while PyCO2SYS includes the nitrite-nitrous acid
61
+ equilibrium, its equilibrium constant is valid only under lab conditions.
62
+
63
+ Because of how the columns are named, and because we dropped the `"fco2"` column, when passing the DataFrame directly to
64
+ PyCO2SYS as above, the system will be solved from DIC and alkalinity, not pH nor fCO<sub>2</sub>.
62
65
 
63
66
  The columns are the same as in the original GLODAP .mat files available from [glodap.info](https://glodap.info), except:
64
67
  * The `"G2"` at the start of each parameter has been removed.
65
68
  * Flags end with `"_f"` instead of just `"f"`.
66
69
  * There is a `"datetime"` column, which combines the `"year"`, `"month"` and `"day"` but NOT the `"hour"` and `"minute"` (because some of these are missing).
67
- * For compatibility with PyCO2SYS:
68
- - `"tco2"` => `"dic"`
69
- - `"talk"` => `"alkalinity"`
70
- - `"phts25p0"` => `"ph_lab"`
71
- - `"phtsinsitutp"` => `"ph_insitu"`
72
-
73
- Therefore when passing the DataFrame directly to PyCO2SYS as in the example
74
- above, the system will be solved from DIC and alkalinity, not pH.
75
70
 
76
71
  The functions `download` and `read` can also be used for finer control, such as
77
72
  specifying a particular GLODAP version rather than using the latest one. See
78
73
  their function docstrings for more information.
79
74
 
80
- The SHA256 checksum of each downloaded file is checked before the file is
81
- written to disk.
75
+ The .mat files from the GEOMAR mirrors are downloaded, and the SHA256 checksum
76
+ of each downloaded file is checked before the file is written to disk.
77
+
78
+ # Citation
79
+
80
+ If using GLODAP data, please cite the paper corresponding to the version you used:
81
+
82
+ * `v3.2026`: TBC (submitted to *Earth System Science Data*)
83
+ * `v2.2023`: https://doi.org/10.5194/essd-16-2047-2024
84
+ * `v2.2022`: https://doi.org/10.5194/essd-14-5543-2022
85
+ * `v2.2021`: https://doi.org/10.5194/essd-13-5565-2021
86
+ * `v2.2020`: https://doi.org/10.5194/essd-12-3653-2020
87
+ * `v2.2019`: https://doi.org/10.5194/essd-11-1437-2019
88
+ * `v2.2016`: https://doi.org/10.5194/essd-8-297-2016
@@ -12,6 +12,5 @@ glodap.egg-info/SOURCES.txt
12
12
  glodap.egg-info/dependency_links.txt
13
13
  glodap.egg-info/requires.txt
14
14
  glodap.egg-info/top_level.txt
15
- tests/test_import.py
16
- tests/test_regions.py
17
- tests/test_response.py
15
+ tests/test_download_and_read.py
16
+ tests/test_regions.py
@@ -0,0 +1,3 @@
1
+ pandas<4,>=3.0.3
2
+ pooch<2,>=1.9.0
3
+ scipy<2,>=1.17.1
@@ -1,30 +1,30 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: glodap
3
- Version: 0.2
3
+ Version: 0.4
4
4
  Summary: GLODAP downloader and importer in Python
5
5
  Classifier: Development Status :: 4 - Beta
6
- Classifier: Programming Language :: Python :: 3.10
7
6
  Classifier: Programming Language :: Python :: 3.11
8
7
  Classifier: Programming Language :: Python :: 3.12
9
8
  Classifier: Programming Language :: Python :: 3.13
9
+ Classifier: Programming Language :: Python :: 3.14
10
10
  Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
11
11
  Classifier: Operating System :: OS Independent
12
12
  Classifier: Natural Language :: English
13
13
  Classifier: Intended Audience :: Science/Research
14
14
  Classifier: Topic :: Scientific/Engineering :: Chemistry
15
15
  Classifier: Topic :: Scientific/Engineering :: Oceanography
16
- Requires-Python: >=3.10
16
+ Requires-Python: >=3.11
17
17
  Description-Content-Type: text/markdown
18
18
  License-File: LICENSE
19
- Requires-Dist: pandas
20
- Requires-Dist: requests
21
- Requires-Dist: scipy
19
+ Requires-Dist: pandas<4,>=3.0.3
20
+ Requires-Dist: pooch<2,>=1.9.0
21
+ Requires-Dist: scipy<2,>=1.17.1
22
22
  Dynamic: license-file
23
23
 
24
24
  # glodap
25
25
 
26
26
  [![Tests](https://github.com/mvdh7/glodap/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/mvdh7/glodap/actions)
27
- [![pypi badge](https://img.shields.io/pypi/v/GLODAP.svg?style=popout)](https://pypi.org/project/GLODAP/)
27
+ [![pypi badge](https://img.shields.io/pypi/v/glodap.svg?style=popout)](https://pypi.org/project/glodap/)
28
28
  [![Conda Version](https://img.shields.io/conda/vn/conda-forge/glodap.svg)](https://anaconda.org/conda-forge/glodap)
29
29
  [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
30
30
  [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
@@ -48,34 +48,41 @@ df_atlantic = glodap.atlantic()
48
48
  Files are saved by default at `"~/.glodap"`, but this can be controlled with the
49
49
  kwarg `gpath`. See the function docstrings for more information.
50
50
 
51
- The columns of the imported DataFrames are renamed so that they can be passed
52
- directly into [PyCO2SYS v2](https://github.com/mvdh7/PyCO2SYS):
51
+ The columns of the imported DataFrames can be passed
52
+ directly into [PyCO2SYS v2](https://mvdh.xyz/PyCO2SYS):
53
53
 
54
54
  ```python
55
55
  import PyCO2SYS as pyco2
56
- co2s_atlantic = pyco2.sys(data=df_atlantic, nitrite=None)
56
+ co2s_atlantic = pyco2.sys(data=df_atlantic.drop(columns="fco2"), nitrite=0)
57
57
  ```
58
58
 
59
- Note `nitrite=None` - this means PyCO2SYS will ignore the `"nitrite"` column,
60
- which is necessary because PyCO2SYS includes the nitrite-nitrous acid
61
- equilibrium but its equilibrium constant is valid only under lab conditions.
59
+ Note `nitrite=0` - this means PyCO2SYS will ignore the `"nitrite"` column,
60
+ which is necessary because while PyCO2SYS includes the nitrite-nitrous acid
61
+ equilibrium, its equilibrium constant is valid only under lab conditions.
62
+
63
+ Because of how the columns are named, and because we dropped the `"fco2"` column, when passing the DataFrame directly to
64
+ PyCO2SYS as above, the system will be solved from DIC and alkalinity, not pH nor fCO<sub>2</sub>.
62
65
 
63
66
  The columns are the same as in the original GLODAP .mat files available from [glodap.info](https://glodap.info), except:
64
67
  * The `"G2"` at the start of each parameter has been removed.
65
68
  * Flags end with `"_f"` instead of just `"f"`.
66
69
  * There is a `"datetime"` column, which combines the `"year"`, `"month"` and `"day"` but NOT the `"hour"` and `"minute"` (because some of these are missing).
67
- * For compatibility with PyCO2SYS:
68
- - `"tco2"` => `"dic"`
69
- - `"talk"` => `"alkalinity"`
70
- - `"phts25p0"` => `"ph_lab"`
71
- - `"phtsinsitutp"` => `"ph_insitu"`
72
-
73
- Therefore when passing the DataFrame directly to PyCO2SYS as in the example
74
- above, the system will be solved from DIC and alkalinity, not pH.
75
70
 
76
71
  The functions `download` and `read` can also be used for finer control, such as
77
72
  specifying a particular GLODAP version rather than using the latest one. See
78
73
  their function docstrings for more information.
79
74
 
80
- The SHA256 checksum of each downloaded file is checked before the file is
81
- written to disk.
75
+ The .mat files from the GEOMAR mirrors are downloaded, and the SHA256 checksum
76
+ of each downloaded file is checked before the file is written to disk.
77
+
78
+ # Citation
79
+
80
+ If using GLODAP data, please cite the paper corresponding to the version you used:
81
+
82
+ * `v3.2026`: TBC (submitted to *Earth System Science Data*)
83
+ * `v2.2023`: https://doi.org/10.5194/essd-16-2047-2024
84
+ * `v2.2022`: https://doi.org/10.5194/essd-14-5543-2022
85
+ * `v2.2021`: https://doi.org/10.5194/essd-13-5565-2021
86
+ * `v2.2020`: https://doi.org/10.5194/essd-12-3653-2020
87
+ * `v2.2019`: https://doi.org/10.5194/essd-11-1437-2019
88
+ * `v2.2016`: https://doi.org/10.5194/essd-8-297-2016
@@ -1,7 +1,7 @@
1
1
  # glodap
2
2
 
3
3
  [![Tests](https://github.com/mvdh7/glodap/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/mvdh7/glodap/actions)
4
- [![pypi badge](https://img.shields.io/pypi/v/GLODAP.svg?style=popout)](https://pypi.org/project/GLODAP/)
4
+ [![pypi badge](https://img.shields.io/pypi/v/glodap.svg?style=popout)](https://pypi.org/project/glodap/)
5
5
  [![Conda Version](https://img.shields.io/conda/vn/conda-forge/glodap.svg)](https://anaconda.org/conda-forge/glodap)
6
6
  [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
7
7
  [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
@@ -25,34 +25,41 @@ df_atlantic = glodap.atlantic()
25
25
  Files are saved by default at `"~/.glodap"`, but this can be controlled with the
26
26
  kwarg `gpath`. See the function docstrings for more information.
27
27
 
28
- The columns of the imported DataFrames are renamed so that they can be passed
29
- directly into [PyCO2SYS v2](https://github.com/mvdh7/PyCO2SYS):
28
+ The columns of the imported DataFrames can be passed
29
+ directly into [PyCO2SYS v2](https://mvdh.xyz/PyCO2SYS):
30
30
 
31
31
  ```python
32
32
  import PyCO2SYS as pyco2
33
- co2s_atlantic = pyco2.sys(data=df_atlantic, nitrite=None)
33
+ co2s_atlantic = pyco2.sys(data=df_atlantic.drop(columns="fco2"), nitrite=0)
34
34
  ```
35
35
 
36
- Note `nitrite=None` - this means PyCO2SYS will ignore the `"nitrite"` column,
37
- which is necessary because PyCO2SYS includes the nitrite-nitrous acid
38
- equilibrium but its equilibrium constant is valid only under lab conditions.
36
+ Note `nitrite=0` - this means PyCO2SYS will ignore the `"nitrite"` column,
37
+ which is necessary because while PyCO2SYS includes the nitrite-nitrous acid
38
+ equilibrium, its equilibrium constant is valid only under lab conditions.
39
+
40
+ Because of how the columns are named, and because we dropped the `"fco2"` column, when passing the DataFrame directly to
41
+ PyCO2SYS as above, the system will be solved from DIC and alkalinity, not pH nor fCO<sub>2</sub>.
39
42
 
40
43
  The columns are the same as in the original GLODAP .mat files available from [glodap.info](https://glodap.info), except:
41
44
  * The `"G2"` at the start of each parameter has been removed.
42
45
  * Flags end with `"_f"` instead of just `"f"`.
43
46
  * There is a `"datetime"` column, which combines the `"year"`, `"month"` and `"day"` but NOT the `"hour"` and `"minute"` (because some of these are missing).
44
- * For compatibility with PyCO2SYS:
45
- - `"tco2"` => `"dic"`
46
- - `"talk"` => `"alkalinity"`
47
- - `"phts25p0"` => `"ph_lab"`
48
- - `"phtsinsitutp"` => `"ph_insitu"`
49
-
50
- Therefore when passing the DataFrame directly to PyCO2SYS as in the example
51
- above, the system will be solved from DIC and alkalinity, not pH.
52
47
 
53
48
  The functions `download` and `read` can also be used for finer control, such as
54
49
  specifying a particular GLODAP version rather than using the latest one. See
55
50
  their function docstrings for more information.
56
51
 
57
- The SHA256 checksum of each downloaded file is checked before the file is
58
- written to disk.
52
+ The .mat files from the GEOMAR mirrors are downloaded, and the SHA256 checksum
53
+ of each downloaded file is checked before the file is written to disk.
54
+
55
+ # Citation
56
+
57
+ If using GLODAP data, please cite the paper corresponding to the version you used:
58
+
59
+ * `v3.2026`: TBC (submitted to *Earth System Science Data*)
60
+ * `v2.2023`: https://doi.org/10.5194/essd-16-2047-2024
61
+ * `v2.2022`: https://doi.org/10.5194/essd-14-5543-2022
62
+ * `v2.2021`: https://doi.org/10.5194/essd-13-5565-2021
63
+ * `v2.2020`: https://doi.org/10.5194/essd-12-3653-2020
64
+ * `v2.2019`: https://doi.org/10.5194/essd-11-1437-2019
65
+ * `v2.2016`: https://doi.org/10.5194/essd-8-297-2016
@@ -1,8 +1,8 @@
1
1
  """
2
2
  GLODAP
3
3
  ======
4
- Download GLODAP (https://glodap.info) datasets and import them as pandas
5
- DataFrames.
4
+ Download Global Ocean Data Analysis Project (GLODAP; https://glodap.info)
5
+ datasets and import them as pandas DataFrames.
6
6
 
7
7
  The functions `arctic`, `atlantic`, `indian`, `pacific` and `world` import the
8
8
  latest version of the GLODAP dataset for the corresponding region, first
@@ -14,54 +14,58 @@ downloading the file if it's not already saved locally. For example:
14
14
  Files are saved by default at "~/.glodap", but this can be controlled with the
15
15
  kwarg `gpath`. See the function docstrings for more information.
16
16
 
17
- The columns of the imported DataFrames are renamed so that they can be passed
18
- directly into PyCO2SYS v2:
17
+ The columns of the imported DataFrames can be passed directly into PyCO2SYS v2:
19
18
 
20
19
  >>> import PyCO2SYS as pyco2
21
20
  >>> co2s_atlantic = pyco2.sys(data=df_atlantic, nitrite=None)
22
21
 
23
22
  Note `nitrite=None` - this means PyCO2SYS will ignore the "nitrite" column,
24
- which is necessary because PyCO2SYS includes the nitrite-nitrous acid
25
- equilibrium but its equilibrium constant is valid only under lab conditions.
23
+ which is necessary because while PyCO2SYS includes the nitrite-nitrous acid
24
+ equilibrium, its equilibrium constant is valid only under lab conditions.
25
+
26
+ Because of how the columns are named, when passing the DataFrame directly to
27
+ PyCO2SYS as above, the system will be solved from DIC and alkalinity, not pH.
26
28
 
27
29
  The columns are the same as in the original GLODAP .mat files, except:
28
30
  * The "G2" at the start of each parameter has been removed.
29
31
  * Flags end with "_f" instead of just "f".
30
32
  * There is a "datetime" column, which combines the "year", "month" and "day"
31
33
  but NOT the "hour" and "minute" (because some of these are missing).
32
- * For compatibility with PyCO2SYS:
33
- "tco2" => "dic"
34
- "talk" => "alkalinity"
35
- "phts25p0" => "ph_lab"
36
- "phtsinsitutp" => "ph_insitu"
37
- Therefore when passing the DataFrame directly to PyCO2SYS as in the example
38
- above, the system will be solved from DIC and alkalinity, not pH.
39
34
 
40
35
  The functions `download` and `read` can also be used for finer control, such as
41
36
  specifying a particular GLODAP version rather than using the latest one. See
42
37
  their function docstrings for more information.
43
38
 
44
- The SHA256 checksum of each downloaded file is checked before the file is
45
- written to disk.
39
+ The .mat files from the GEOMAR mirrors are downloaded, and the SHA256 checksum
40
+ of each downloaded file is checked before the file is written to disk.
46
41
  """
47
42
 
48
- import hashlib
49
43
  import os
44
+ import shutil
50
45
  import tempfile
51
- from warnings import warn
46
+ from pathlib import Path
52
47
 
48
+ import numpy as np
53
49
  import pandas as pd
54
- import requests
50
+ import pooch
55
51
  from scipy.io import loadmat
56
52
 
57
53
 
58
54
  # Package metadata
59
55
  __author__ = "Humphreys, Matthew P."
60
- __version__ = "0.2"
56
+ __version__ = "0.4"
61
57
 
62
58
  # GLODAP metadata
63
- version_latest = "v2.2023"
64
- versions = ["v2.2023", "v2.2022", "v2.2021", "v2.2020", "v2.2019"]
59
+ version_latest = "v3.2026"
60
+ versions = [
61
+ "v3.2026",
62
+ "v2.2023",
63
+ "v2.2022",
64
+ "v2.2021",
65
+ "v2.2020",
66
+ "v2.2019",
67
+ "v2.2016",
68
+ ]
65
69
  regions = {
66
70
  "arctic": "Arctic_Ocean",
67
71
  "atlantic": "Atlantic_Ocean",
@@ -72,7 +76,15 @@ regions = {
72
76
  regions_full = regions.copy()
73
77
  for k, v in regions.items():
74
78
  regions_full[k[:3]] = v
79
+ regions_short_to_long = {k[:3]: k for k in regions}
75
80
  checksums = {
81
+ "v3.2026": {
82
+ "arc": "03dfcf59525931065247e142439d97c0ed88d8f572e87df919476deebdc57d5c",
83
+ "atl": "a4577bbb676c30327b185fc5bf0ddc0e302464d5332b6d630922faf536dbe68c",
84
+ "ind": "79d655bd203e4579007b7224fc53b87ee8a5d70bb733550f0acac260a6baa0d8",
85
+ "pac": "1a5afbb6be199773e00772ca3b1b84241dfc52a591b3f6b85786180f5711a9b4",
86
+ "wor": "214aeb49c618474bf92622a37d2795a10160954ab49a94f9052cff1dd41947fd",
87
+ },
76
88
  "v2.2023": {
77
89
  "arc": "a998a8d99db84e9357b21a7e345bbfde62eae77870e651119b4c1929a2f1e2fd",
78
90
  "atl": "a3ff7bc4f4ff3a1b0f540886a0eb7c9b3ec52aa4eae2dea57d47ae61f56da56f",
@@ -108,6 +120,13 @@ checksums = {
108
120
  "pac": "88df995e272dd63db55421b911be1f99bfec4cac6f97a73e017c9e275bf29db2",
109
121
  "wor": "4637ce5eb560fe020e9fccf0370156bfe19498fc74c2e8b66dee190cc44cff63",
110
122
  },
123
+ "v2.2016": {
124
+ "arc": "f2214409e0526052903da2427f3f8c6fd433b1759ba2a1be3a61181b4b0d04b7",
125
+ "atl": "fb26a86ca1135a0afc94157ba7963bc1cb8138523bcba36360595641175f8da1",
126
+ "ind": "ba162aa6f1fa7209f586e503581d3cc05cdffbfa6f72bdc535824f7a78d694f9",
127
+ "pac": "0d1666de27ad6853533651027459d2525f75c6681d540f129df705b018d3c931",
128
+ "wor": "aa202e330d5e77b98d0062e40a40665b8397b83ebbfa00424532139c53814f5e",
129
+ },
111
130
  }
112
131
 
113
132
 
@@ -118,13 +137,16 @@ def _get_paths(region, version, gpath):
118
137
  version = version.lower()
119
138
  assert version in versions, "`version` not valid!"
120
139
  if gpath is None:
121
- gpath = os.path.join(os.path.expanduser("~"), ".glodap")
140
+ gpath = Path(Path.home(), ".glodap")
122
141
  fileregion = regions_full[region.lower()]
123
- filename = f"{fileregion}_{version}.mat"
142
+ if version.startswith("v2"):
143
+ filename = f"{fileregion}_{version}.mat"
144
+ elif version.startswith("v3"):
145
+ filename = f"{fileregion}_{version}.csv.zip"
124
146
  return gpath, fileregion, filename, version
125
147
 
126
148
 
127
- def download(region="world", version=None, gpath=None, chunk_size=8192):
149
+ def download(region="world", version=None, gpath=None):
128
150
  """Download a GLODAP data file and save it locally, after checking that it
129
151
  has the expected checksum (SHA256).
130
152
 
@@ -141,7 +163,13 @@ def download(region="world", version=None, gpath=None, chunk_size=8192):
141
163
  version : str or None, optional
142
164
  Which GLODAP version to download, by default `None`, in which case the
143
165
  most recent version is downloaded. The options are:
144
- "v2.2023", "v2.2022", "v2.2021", "v2.2020", "v2.2019"
166
+ "v3.2026" (the original GLODAPv3)
167
+ "v2.2023",
168
+ "v2.2022",
169
+ "v2.2021",
170
+ "v2.2020",
171
+ "v2.2019",
172
+ "v2.2016" (the original GLODAPv2).
145
173
  gpath : str of None, optional
146
174
  Where to save the downloaded file, by default `None`, in which case the
147
175
  file is saved at "~/.glodap".
@@ -151,27 +179,32 @@ def download(region="world", version=None, gpath=None, chunk_size=8192):
151
179
  gpath, fileregion, filename, version = _get_paths(region, version, gpath)
152
180
  if not os.path.isdir(gpath):
153
181
  os.makedirs(gpath)
154
- url = (
155
- f"https://glodap.info/glodap_files/{version}/"
156
- + f"GLODAP{version}_{fileregion}.mat"
157
- )
158
- hasher = hashlib.new("sha256")
182
+ if version == "v2.2016":
183
+ fr = fileregion.replace("_", " ")
184
+ url = (
185
+ f"https://glodap.info/glodap_files/{version}/"
186
+ + f"GLODAPv2 {fr}.mat"
187
+ )
188
+ elif version.startswith("v2"):
189
+ url = (
190
+ f"https://glodap.info/glodap_files/{version}/"
191
+ + f"GLODAP{version}_{fileregion}.mat"
192
+ )
193
+ elif version.startswith("v3"):
194
+ url = (
195
+ "https://glodap.info/glodap_files/v3/"
196
+ + f"GLODAPv3_{fileregion}.csv.zip"
197
+ )
159
198
  checksum = checksums[version][region[:3].lower()]
160
- with tempfile.TemporaryDirectory() as tdir:
161
- temp_path = os.path.join(tdir, filename)
162
- with requests.get(url, stream=True) as r:
163
- r.raise_for_status()
164
- with open(temp_path, "wb") as f:
165
- for chunk in r.iter_content(chunk_size=chunk_size):
166
- f.write(chunk)
167
- hasher.update(chunk)
168
- if hasher.hexdigest() == checksum:
169
- os.rename(temp_path, os.path.join(gpath, filename))
170
- else:
171
- warn("File checksum not as expected - download failed.")
172
-
173
-
174
- def read(region="world", version=None, gpath=None, rename_pyco2=True):
199
+ pooch.retrieve(
200
+ url,
201
+ known_hash=f"sha256:{checksum}",
202
+ fname=filename,
203
+ path=gpath,
204
+ )
205
+
206
+
207
+ def read(region="world", version=None, gpath=None):
175
208
  """Import a GLODAP data file as a pandas DataFrame, downloading it first
176
209
  if it's not already available locally.
177
210
 
@@ -188,31 +221,46 @@ def read(region="world", version=None, gpath=None, rename_pyco2=True):
188
221
  version : str or None, optional
189
222
  Which GLODAP version to import, by default `None`, in which case the
190
223
  most recent version is imported. The options are:
191
- "v2.2023", "v2.2022", "v2.2021", "v2.2020", "v2.2019"
224
+ "v3.2026" (the original GLODAPv3)
225
+ "v2.2023",
226
+ "v2.2022",
227
+ "v2.2021",
228
+ "v2.2020",
229
+ "v2.2019",
230
+ "v2.2016" (the original GLODAPv2).
192
231
  gpath : str of None, optional
193
232
  Where to the downloaded file is or should be saved, by default `None`,
194
233
  in which case the file is imported from or saved at "~/.glodap".
195
- rename_pyco2 : bool, optional
196
- Whether to rename the columns to work with PyCO2SYS, by default `True`.
197
234
 
198
235
  Returns
199
236
  -------
200
237
  pd.DataFrame
201
238
  The GLODAP dataset as a pandas DataFrame.
202
239
  """
240
+ if region.lower() in regions_short_to_long:
241
+ region = regions_short_to_long[region.lower()]
203
242
  gpath, _, filename, version = _get_paths(region, version, gpath)
204
- try:
205
- df = loadmat(os.path.join(gpath, filename))
206
- except FileNotFoundError:
207
- download(region=region, version=version, gpath=gpath)
208
- df = loadmat(os.path.join(gpath, filename))
209
- df = pd.DataFrame(
210
- {
211
- k[2:]: [w[0][0] for w in v] if v.dtype == "O" else v.ravel()
212
- for k, v in df.items()
213
- if k.startswith("G2")
214
- }
215
- )
243
+ download(region=region, version=version, gpath=gpath)
244
+ if version.startswith("v2"):
245
+ df = loadmat(Path(gpath, filename))
246
+ df = pd.DataFrame(
247
+ {
248
+ k[2:]: [w[0][0] for w in v] if v.dtype == "O" else v.ravel()
249
+ for k, v in df.items()
250
+ if k.startswith("G2")
251
+ }
252
+ )
253
+ elif version.startswith("v3"):
254
+ with tempfile.TemporaryDirectory() as tdir:
255
+ shutil.unpack_archive(Path(gpath, filename), tdir)
256
+ tfilename = [f for f in os.listdir(tdir) if f.endswith(".csv")][0]
257
+ df = pd.read_csv(
258
+ Path(tdir, tfilename),
259
+ dtype={
260
+ "expocode": str,
261
+ "ph_scale": str,
262
+ },
263
+ ).replace([-9999, "-9999"], np.nan)
216
264
  # Convert columns that should be integers into integers
217
265
  # Can't convert cast, hour, minute to integers because they have missing
218
266
  # values
@@ -258,23 +306,26 @@ def read(region="world", version=None, gpath=None, rename_pyco2=True):
258
306
  "tdnf",
259
307
  "chlaf",
260
308
  ]
309
+ if region in ["indian", "world"] and version == "v2.2016":
310
+ for k in keys_flags:
311
+ if k in df:
312
+ df.loc[df[k].isnull(), k] = 9
261
313
  for k in keys_integers + keys_flags:
262
- df[k] = df[k].astype(int)
314
+ if k in df:
315
+ df[k] = df[k].astype(int)
263
316
  # Rename columns for PyCO2SYS, if requested
264
- if rename_pyco2:
265
- renamer_pyco2 = {
266
- "tco2": "dic",
267
- "talk": "alkalinity",
268
- "phts25p0": "ph_lab",
269
- "phtsinsitutp": "ph_insitu",
270
- }
271
- renamer_flags = {
272
- k: renamer_pyco2[k[:-1]] + "_f"
273
- if k[:-1] in renamer_pyco2
274
- else k[:-1] + "_f"
275
- for k in keys_flags
276
- }
277
- df = df.rename(columns=renamer_pyco2).rename(columns=renamer_flags)
317
+ renamer_flags = {k: k[:-1] + "_f" for k in keys_flags}
318
+ df = df.rename(columns=renamer_flags)
319
+ # Correct fatal datetime errors in v2.2016
320
+ if version == "v2.2016":
321
+ if region in ["indian", "world"]:
322
+ L = (df.cruise == 696) & (df.station == 15537)
323
+ df.loc[L, "month"] = 12
324
+ df.loc[L, "day"] = 1
325
+ if region in ["pacific", "world"]:
326
+ L = (df.cruise == 270) & (df.station == 113)
327
+ df.loc[L, "month"] = 4
328
+ df.loc[L, "day"] = 30
278
329
  # Calculate datetime for convenience - don't include hour and minute
279
330
  # because some are missing
280
331
  df["datetime"] = pd.to_datetime(
@@ -291,7 +342,7 @@ def read(region="world", version=None, gpath=None, rename_pyco2=True):
291
342
  return df
292
343
 
293
344
 
294
- def arctic(version=None, gpath=None, rename_pyco2=True):
345
+ def arctic(version=None, gpath=None):
295
346
  """Import the latest version of the GLODAP Arctic Ocean dataset,
296
347
  downloading it first if it's not already available locally.
297
348
 
@@ -300,12 +351,16 @@ def arctic(version=None, gpath=None, rename_pyco2=True):
300
351
  version : str or None, optional
301
352
  Which GLODAP version to import, by default `None`, in which case the
302
353
  most recent version is imported. The options are:
303
- "v2.2023", "v2.2022", "v2.2021", "v2.2020", "v2.2019"
354
+ "v3.2026" (the original GLODAPv3)
355
+ "v2.2023",
356
+ "v2.2022",
357
+ "v2.2021",
358
+ "v2.2020",
359
+ "v2.2019",
360
+ "v2.2016" (the original GLODAPv2).
304
361
  gpath : str of None, optional
305
362
  Where to the downloaded file is or should be saved, by default `None`,
306
363
  in which case the file is imported from or saved at "~/.glodap".
307
- rename_pyco2 : bool, optional
308
- Whether to rename the columns to work with PyCO2SYS, by default `True`.
309
364
 
310
365
  Returns
311
366
  -------
@@ -316,11 +371,10 @@ def arctic(version=None, gpath=None, rename_pyco2=True):
316
371
  region="arctic",
317
372
  version=version,
318
373
  gpath=gpath,
319
- rename_pyco2=rename_pyco2,
320
374
  )
321
375
 
322
376
 
323
- def atlantic(version=None, gpath=None, rename_pyco2=True):
377
+ def atlantic(version=None, gpath=None):
324
378
  """Import the latest version of the GLODAP Atlantic Ocean dataset,
325
379
  downloading it first if it's not already available locally.
326
380
 
@@ -329,12 +383,16 @@ def atlantic(version=None, gpath=None, rename_pyco2=True):
329
383
  version : str or None, optional
330
384
  Which GLODAP version to import, by default `None`, in which case the
331
385
  most recent version is imported. The options are:
332
- "v2.2023", "v2.2022", "v2.2021", "v2.2020", "v2.2019"
386
+ "v3.2026" (the original GLODAPv3)
387
+ "v2.2023",
388
+ "v2.2022",
389
+ "v2.2021",
390
+ "v2.2020",
391
+ "v2.2019",
392
+ "v2.2016" (the original GLODAPv2).
333
393
  gpath : str of None, optional
334
394
  Where to the downloaded file is or should be saved, by default `None`,
335
395
  in which case the file is imported from or saved at "~/.glodap".
336
- rename_pyco2 : bool, optional
337
- Whether to rename the columns to work with PyCO2SYS, by default `True`.
338
396
 
339
397
  Returns
340
398
  -------
@@ -345,11 +403,10 @@ def atlantic(version=None, gpath=None, rename_pyco2=True):
345
403
  region="atlantic",
346
404
  version=version,
347
405
  gpath=gpath,
348
- rename_pyco2=rename_pyco2,
349
406
  )
350
407
 
351
408
 
352
- def indian(version=None, gpath=None, rename_pyco2=True):
409
+ def indian(version=None, gpath=None):
353
410
  """Import the latest version of the GLODAP Indian Ocean dataset,
354
411
  downloading it first if it's not already available locally.
355
412
 
@@ -358,12 +415,16 @@ def indian(version=None, gpath=None, rename_pyco2=True):
358
415
  version : str or None, optional
359
416
  Which GLODAP version to import, by default `None`, in which case the
360
417
  most recent version is imported. The options are:
361
- "v2.2023", "v2.2022", "v2.2021", "v2.2020", "v2.2019"
418
+ "v3.2026" (the original GLODAPv3)
419
+ "v2.2023",
420
+ "v2.2022",
421
+ "v2.2021",
422
+ "v2.2020",
423
+ "v2.2019",
424
+ "v2.2016" (the original GLODAPv2).
362
425
  gpath : str of None, optional
363
426
  Where to the downloaded file is or should be saved, by default `None`,
364
427
  in which case the file is imported from or saved at "~/.glodap".
365
- rename_pyco2 : bool, optional
366
- Whether to rename the columns to work with PyCO2SYS, by default `True`.
367
428
 
368
429
  Returns
369
430
  -------
@@ -374,11 +435,10 @@ def indian(version=None, gpath=None, rename_pyco2=True):
374
435
  region="indian",
375
436
  version=version,
376
437
  gpath=gpath,
377
- rename_pyco2=rename_pyco2,
378
438
  )
379
439
 
380
440
 
381
- def pacific(version=None, gpath=None, rename_pyco2=True):
441
+ def pacific(version=None, gpath=None):
382
442
  """Import the latest version of the GLODAP Pacific Ocean dataset,
383
443
  downloading it first if it's not already available locally.
384
444
 
@@ -387,12 +447,16 @@ def pacific(version=None, gpath=None, rename_pyco2=True):
387
447
  version : str or None, optional
388
448
  Which GLODAP version to import, by default `None`, in which case the
389
449
  most recent version is imported. The options are:
390
- "v2.2023", "v2.2022", "v2.2021", "v2.2020", "v2.2019"
450
+ "v3.2026" (the original GLODAPv3)
451
+ "v2.2023",
452
+ "v2.2022",
453
+ "v2.2021",
454
+ "v2.2020",
455
+ "v2.2019",
456
+ "v2.2016" (the original GLODAPv2).
391
457
  gpath : str of None, optional
392
458
  Where to the downloaded file is or should be saved, by default `None`,
393
459
  in which case the file is imported from or saved at "~/.glodap".
394
- rename_pyco2 : bool, optional
395
- Whether to rename the columns to work with PyCO2SYS, by default `True`.
396
460
 
397
461
  Returns
398
462
  -------
@@ -403,11 +467,10 @@ def pacific(version=None, gpath=None, rename_pyco2=True):
403
467
  region="pacific",
404
468
  version=version,
405
469
  gpath=gpath,
406
- rename_pyco2=rename_pyco2,
407
470
  )
408
471
 
409
472
 
410
- def world(version=None, gpath=None, rename_pyco2=True):
473
+ def world(version=None, gpath=None):
411
474
  """Import the latest version of the GLODAP Merged Master File dataset,
412
475
  downloading it first if it's not already available locally.
413
476
 
@@ -416,12 +479,16 @@ def world(version=None, gpath=None, rename_pyco2=True):
416
479
  version : str or None, optional
417
480
  Which GLODAP version to import, by default `None`, in which case the
418
481
  most recent version is imported. The options are:
419
- "v2.2023", "v2.2022", "v2.2021", "v2.2020", "v2.2019"
482
+ "v3.2026" (the original GLODAPv3)
483
+ "v2.2023",
484
+ "v2.2022",
485
+ "v2.2021",
486
+ "v2.2020",
487
+ "v2.2019",
488
+ "v2.2016" (the original GLODAPv2).
420
489
  gpath : str of None, optional
421
490
  Where to the downloaded file is or should be saved, by default `None`,
422
491
  in which case the file is imported from or saved at "~/.glodap".
423
- rename_pyco2 : bool, optional
424
- Whether to rename the columns to work with PyCO2SYS, by default `True`.
425
492
 
426
493
  Returns
427
494
  -------
@@ -432,5 +499,4 @@ def world(version=None, gpath=None, rename_pyco2=True):
432
499
  region="world",
433
500
  version=version,
434
501
  gpath=gpath,
435
- rename_pyco2=rename_pyco2,
436
502
  )
@@ -8,18 +8,18 @@ build-backend = "setuptools.build_meta"
8
8
  name = "glodap"
9
9
  description = "GLODAP downloader and importer in Python"
10
10
  readme = "README.md"
11
- requires-python = ">= 3.10"
11
+ requires-python = ">= 3.11"
12
12
  dependencies = [
13
- "pandas",
14
- "requests",
15
- "scipy",
13
+ "pandas>=3.0.3,<4",
14
+ "pooch>=1.9.0,<2",
15
+ "scipy>=1.17.1,<2",
16
16
  ]
17
17
  classifiers = [
18
18
  "Development Status :: 4 - Beta",
19
- "Programming Language :: Python :: 3.10",
20
19
  "Programming Language :: Python :: 3.11",
21
20
  "Programming Language :: Python :: 3.12",
22
21
  "Programming Language :: Python :: 3.13",
22
+ "Programming Language :: Python :: 3.14",
23
23
  "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
24
24
  "Operating System :: OS Independent",
25
25
  "Natural Language :: English",
@@ -34,3 +34,16 @@ include = ["glodap*"]
34
34
 
35
35
  [tool.setuptools.dynamic]
36
36
  version = {attr = "glodap.__version__"}
37
+
38
+ [tool.ruff]
39
+ line-length = 79
40
+
41
+ [tool.ruff.lint.isort]
42
+ combine-as-imports = true
43
+ force-single-line = false
44
+ lines-after-imports = 2
45
+
46
+ [dependency-groups]
47
+ dev = [
48
+ "ipykernel>=7.3.0",
49
+ ]
@@ -2,16 +2,18 @@
2
2
  import itertools
3
3
  import tempfile
4
4
 
5
+ import pandas as pd
6
+
5
7
  import glodap
6
8
 
7
9
 
8
- def test_response():
10
+ def test_download_and_read():
9
11
  with tempfile.TemporaryDirectory() as tdir:
10
12
  for region, version in itertools.product(
11
13
  glodap.regions, glodap.versions
12
14
  ):
13
- print(region, version)
14
- glodap.download(region=region, version=version, gpath=tdir)
15
+ df = glodap.read(region=region, version=version, gpath=tdir)
16
+ assert isinstance(df, pd.DataFrame)
15
17
 
16
18
 
17
- # test_response()
19
+ # test_download_and_read()
@@ -0,0 +1,27 @@
1
+ # %%
2
+ import pandas as pd
3
+
4
+ import glodap
5
+
6
+
7
+ def test_region_names():
8
+ # Check that the first 3 letters of each region name are unique
9
+ regions_short = {k[:3] for k in glodap.regions}
10
+ assert len(regions_short) == len(glodap.regions)
11
+
12
+
13
+ def test_regions_latest():
14
+ df = glodap.arctic()
15
+ assert isinstance(df, pd.DataFrame)
16
+ df = glodap.atlantic()
17
+ assert isinstance(df, pd.DataFrame)
18
+ df = glodap.indian()
19
+ assert isinstance(df, pd.DataFrame)
20
+ df = glodap.pacific()
21
+ assert isinstance(df, pd.DataFrame)
22
+ df = glodap.world()
23
+ assert isinstance(df, pd.DataFrame)
24
+
25
+
26
+ # test_region_names()
27
+ # test_regions_latest()
@@ -1,3 +0,0 @@
1
- pandas
2
- requests
3
- scipy
@@ -1,12 +0,0 @@
1
- # %%
2
- import pandas as pd
3
-
4
- import glodap
5
-
6
-
7
- def test_import():
8
- df = glodap.arctic(gpath="tests/data")
9
- assert isinstance(df, pd.DataFrame)
10
-
11
-
12
- # test_import()
@@ -1,11 +0,0 @@
1
- # %%
2
- import glodap
3
-
4
-
5
- def test_region_names():
6
- # Check that the first 3 letters of each region name are unique
7
- regions_short = {k[:3] for k in glodap.regions}
8
- assert len(regions_short) == len(glodap.regions)
9
-
10
-
11
- # test_region_names()
File without changes
File without changes
File without changes