jerlov 0.2.1__tar.gz → 0.2.2__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.
Files changed (39) hide show
  1. {jerlov-0.2.1/jerlov.egg-info → jerlov-0.2.2}/PKG-INFO +10 -9
  2. {jerlov-0.2.1 → jerlov-0.2.2}/README.md +9 -8
  3. {jerlov-0.2.1 → jerlov-0.2.2}/jerlov/__init__.py +1 -1
  4. {jerlov-0.2.1 → jerlov-0.2.2}/jerlov/colour.py +10 -7
  5. {jerlov-0.2.1 → jerlov-0.2.2/jerlov.egg-info}/PKG-INFO +10 -9
  6. {jerlov-0.2.1 → jerlov-0.2.2}/pyproject.toml +1 -1
  7. {jerlov-0.2.1 → jerlov-0.2.2}/tests/test_colour.py +8 -3
  8. {jerlov-0.2.1 → jerlov-0.2.2}/tests/test_packaging.py +64 -0
  9. {jerlov-0.2.1 → jerlov-0.2.2}/LICENSE +0 -0
  10. {jerlov-0.2.1 → jerlov-0.2.2}/NOTICE +0 -0
  11. {jerlov-0.2.1 → jerlov-0.2.2}/jerlov/_data.py +0 -0
  12. {jerlov-0.2.1 → jerlov-0.2.2}/jerlov/data/__init__.py +0 -0
  13. {jerlov-0.2.1 → jerlov-0.2.2}/jerlov/data/austin1986_kd.csv +0 -0
  14. {jerlov-0.2.1 → jerlov-0.2.2}/jerlov/data/austin1986_model.csv +0 -0
  15. {jerlov-0.2.1 → jerlov-0.2.2}/jerlov/data/cie1931_2deg_cmf.csv +0 -0
  16. {jerlov-0.2.1 → jerlov-0.2.2}/jerlov/data/cie_d65.csv +0 -0
  17. {jerlov-0.2.1 → jerlov-0.2.2}/jerlov/data/jerlov1968_kd.csv +0 -0
  18. {jerlov-0.2.1 → jerlov-0.2.2}/jerlov/data/jerlov1968_total_irradiance.csv +0 -0
  19. {jerlov-0.2.1 → jerlov-0.2.2}/jerlov/data/jerlov1976_kd.csv +0 -0
  20. {jerlov-0.2.1 → jerlov-0.2.2}/jerlov/data/paulson1977_shortwave.csv +0 -0
  21. {jerlov-0.2.1 → jerlov-0.2.2}/jerlov/data/smart2007_b_from_c.csv +0 -0
  22. {jerlov-0.2.1 → jerlov-0.2.2}/jerlov/data/solonenko2015_iop.csv +0 -0
  23. {jerlov-0.2.1 → jerlov-0.2.2}/jerlov/data/williamson2022_iop.csv +0 -0
  24. {jerlov-0.2.1 → jerlov-0.2.2}/jerlov/data/williamson2022_measured.csv +0 -0
  25. {jerlov-0.2.1 → jerlov-0.2.2}/jerlov/data/williamson2023_depth.csv +0 -0
  26. {jerlov-0.2.1 → jerlov-0.2.2}/jerlov/scene.py +0 -0
  27. {jerlov-0.2.1 → jerlov-0.2.2}/jerlov/shortwave.py +0 -0
  28. {jerlov-0.2.1 → jerlov-0.2.2}/jerlov/sources.py +0 -0
  29. {jerlov-0.2.1 → jerlov-0.2.2}/jerlov/water.py +0 -0
  30. {jerlov-0.2.1 → jerlov-0.2.2}/jerlov.egg-info/SOURCES.txt +0 -0
  31. {jerlov-0.2.1 → jerlov-0.2.2}/jerlov.egg-info/dependency_links.txt +0 -0
  32. {jerlov-0.2.1 → jerlov-0.2.2}/jerlov.egg-info/requires.txt +0 -0
  33. {jerlov-0.2.1 → jerlov-0.2.2}/jerlov.egg-info/top_level.txt +0 -0
  34. {jerlov-0.2.1 → jerlov-0.2.2}/setup.cfg +0 -0
  35. {jerlov-0.2.1 → jerlov-0.2.2}/tests/test_api.py +0 -0
  36. {jerlov-0.2.1 → jerlov-0.2.2}/tests/test_depth.py +0 -0
  37. {jerlov-0.2.1 → jerlov-0.2.2}/tests/test_reproduces_papers.py +0 -0
  38. {jerlov-0.2.1 → jerlov-0.2.2}/tests/test_scene.py +0 -0
  39. {jerlov-0.2.1 → jerlov-0.2.2}/tests/test_shortwave.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jerlov
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Inherent optical properties of Jerlov water types, with provenance
5
5
  Author: T. Ishibashi
6
6
  License-Expression: Apache-2.0
@@ -257,16 +257,17 @@ rather than substituting a neighbour.
257
257
  ## Examples
258
258
 
259
259
  ```
260
- python examples/sources_disagree.py
261
- python examples/synthetic_underwater_images.py
260
+ python examples/sources_disagree.py why the source is an argument
261
+ python examples/from_one_measurement.py from an instrument reading
262
+ python examples/synthetic_underwater_images.py appearance at range and depth
263
+ python examples/solar_heating.py for ocean circulation models
264
+ python examples/what_an_eye_sees.py any spectral sensitivity
262
265
  ```
263
266
 
264
- The first shows why the source is an argument: at 510 nm the scattering
265
- coefficient of Jerlov III differs by a factor of 3.8 between the two published
266
- sets. The second synthesises how reflectance patches appear at range and
267
- depth, white-balanced two ways, and ends with a list of everything it assumed.
268
-
269
- Both are run by CI, so an example that has stopped working is a failed build.
267
+ Five scripts, each aimed at a different reader; `examples/README.md` says
268
+ which to start with. CI runs all of them on every push, so an example that has
269
+ stopped working is a failed build. Every one ends with the assumptions it
270
+ made.
270
271
 
271
272
  ## Provenance and design
272
273
 
@@ -232,16 +232,17 @@ rather than substituting a neighbour.
232
232
  ## Examples
233
233
 
234
234
  ```
235
- python examples/sources_disagree.py
236
- python examples/synthetic_underwater_images.py
235
+ python examples/sources_disagree.py why the source is an argument
236
+ python examples/from_one_measurement.py from an instrument reading
237
+ python examples/synthetic_underwater_images.py appearance at range and depth
238
+ python examples/solar_heating.py for ocean circulation models
239
+ python examples/what_an_eye_sees.py any spectral sensitivity
237
240
  ```
238
241
 
239
- The first shows why the source is an argument: at 510 nm the scattering
240
- coefficient of Jerlov III differs by a factor of 3.8 between the two published
241
- sets. The second synthesises how reflectance patches appear at range and
242
- depth, white-balanced two ways, and ends with a list of everything it assumed.
243
-
244
- Both are run by CI, so an example that has stopped working is a failed build.
242
+ Five scripts, each aimed at a different reader; `examples/README.md` says
243
+ which to start with. CI runs all of them on every push, so an example that has
244
+ stopped working is a failed build. Every one ends with the assumptions it
245
+ made.
245
246
 
246
247
  ## Provenance and design
247
248
 
@@ -64,4 +64,4 @@ __all__ = [
64
64
  "MissingQuantityError",
65
65
  ]
66
66
 
67
- __version__ = "0.2.1"
67
+ __version__ = "0.2.2"
@@ -151,15 +151,18 @@ def integrate_response(spectrum, wavelengths, response, response_wavelengths,
151
151
  if wavelengths.size < 2:
152
152
  raise ValueError("at least two wavelengths are needed to integrate")
153
153
 
154
- worst = min(
155
- _coverage(wavelengths, response_wavelengths, response[:, k])
156
- for k in range(response.shape[1])
157
- )
154
+ covered = [_coverage(wavelengths, response_wavelengths, response[:, k])
155
+ for k in range(response.shape[1])]
156
+ worst = min(covered)
158
157
  if worst < 0.999:
158
+ which = (f"channel {covered.index(worst)} of {len(covered)}"
159
+ if len(covered) > 1 else "it")
159
160
  warnings.warn(
160
- f"the spectrum spans {wavelengths[0]:g}-{wavelengths[-1]:g} nm and "
161
- f"covers only {worst:.1%} of the {name}; the integral is over the "
162
- "overlap and is biased by what was left out",
161
+ f"the spectrum spans {wavelengths[0]:g}-{wavelengths[-1]:g} nm; "
162
+ f"of the {name}, {which} is only {worst:.1%} covered "
163
+ f"(all channels: {', '.join(f'{c:.1%}' for c in covered)}). "
164
+ "The integral is over the overlap and is biased by what was left "
165
+ "out",
163
166
  CoverageWarning,
164
167
  stacklevel=2,
165
168
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jerlov
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Inherent optical properties of Jerlov water types, with provenance
5
5
  Author: T. Ishibashi
6
6
  License-Expression: Apache-2.0
@@ -257,16 +257,17 @@ rather than substituting a neighbour.
257
257
  ## Examples
258
258
 
259
259
  ```
260
- python examples/sources_disagree.py
261
- python examples/synthetic_underwater_images.py
260
+ python examples/sources_disagree.py why the source is an argument
261
+ python examples/from_one_measurement.py from an instrument reading
262
+ python examples/synthetic_underwater_images.py appearance at range and depth
263
+ python examples/solar_heating.py for ocean circulation models
264
+ python examples/what_an_eye_sees.py any spectral sensitivity
262
265
  ```
263
266
 
264
- The first shows why the source is an argument: at 510 nm the scattering
265
- coefficient of Jerlov III differs by a factor of 3.8 between the two published
266
- sets. The second synthesises how reflectance patches appear at range and
267
- depth, white-balanced two ways, and ends with a list of everything it assumed.
268
-
269
- Both are run by CI, so an example that has stopped working is a failed build.
267
+ Five scripts, each aimed at a different reader; `examples/README.md` says
268
+ which to start with. CI runs all of them on every push, so an example that has
269
+ stopped working is a failed build. Every one ends with the assumptions it
270
+ made.
270
271
 
271
272
  ## Provenance and design
272
273
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "jerlov"
7
- version = "0.2.1"
7
+ version = "0.2.2"
8
8
  description = "Inherent optical properties of Jerlov water types, with provenance"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -73,7 +73,7 @@ def test_each_primary_lights_only_its_own_channel():
73
73
  def test_a_narrow_spectrum_warns():
74
74
  """450-650 nm looks like a full spectrum but is not."""
75
75
  wl = np.arange(450.0, 651.0, 1.0)
76
- with pytest.warns(CoverageWarning, match="covers only"):
76
+ with pytest.warns(CoverageWarning, match="covered"):
77
77
  spectrum_to_xyz(np.ones_like(wl), wl)
78
78
 
79
79
 
@@ -88,8 +88,13 @@ def test_the_warning_states_what_was_missed():
88
88
  with pytest.warns(CoverageWarning) as caught:
89
89
  spectrum_to_xyz(np.ones_like(wl), wl)
90
90
  message = str(caught[0].message)
91
- assert "500-600 nm" in message
92
- assert "CIE 1931" in message
91
+ assert "500-600 nm" in message # the span it actually had
92
+ assert "CIE 1931" in message # what it was integrated against
93
+ assert "all channels:" in message # not just the worst one
94
+ # z-bar peaks at 445 nm, so 500-600 nm barely touches it. Naming the
95
+ # channel matters: "4.1% covered" alone reads as if the whole observer
96
+ # were 4.1% covered, which would be wrong.
97
+ assert "channel 2 of 3" in message
93
98
 
94
99
 
95
100
  # -- white, the other one ------------------------------------------------
@@ -76,3 +76,67 @@ def test_every_shipped_table_has_a_build_script_or_is_explained():
76
76
  scripts = " ".join(p.read_text() for p in (ROOT / "tools").glob("*.py"))
77
77
  for csv in sorted((ROOT / "jerlov" / "data").glob("*.csv")):
78
78
  assert csv.name in scripts, f"no build script writes {csv.name}"
79
+
80
+
81
+ # -- the documentation counts itself -------------------------------------
82
+
83
+ _SPELLED = ["zero", "one", "two", "three", "four", "five", "six", "seven",
84
+ "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen",
85
+ "fifteen", "sixteen", "seventeen", "eighteen", "nineteen",
86
+ "twenty", "twenty-one", "twenty-two", "twenty-three",
87
+ "twenty-four", "twenty-five"]
88
+ WORDS = {word: value for value, word in enumerate(_SPELLED)}
89
+ WORDS.update({word.capitalize(): value
90
+ for value, word in enumerate(_SPELLED)})
91
+
92
+
93
+ def _numbered_sections(text: str) -> int:
94
+ return len(re.findall(r"^## \d+\. ", text, re.MULTILINE))
95
+
96
+
97
+ def _confirmed_sections(text: str) -> int:
98
+ return len(re.findall(r"^## \d+\..*\(confirmed", text, re.MULTILINE))
99
+
100
+
101
+ @source_tree
102
+ def test_DATA_md_counts_its_own_sections():
103
+ """The opening summary is written by hand and has drifted three times."""
104
+ text = (ROOT / "DATA.md").read_text()
105
+ match = re.search(r"^(\w+) entries are recorded below\. (\w+) are confirmed",
106
+ text, re.MULTILINE)
107
+ assert match, "DATA.md no longer opens with a countable summary"
108
+ claimed_total, claimed_confirmed = (WORDS[g] for g in match.groups())
109
+ assert claimed_total == _numbered_sections(text)
110
+ assert claimed_confirmed == _confirmed_sections(text)
111
+
112
+
113
+ @source_tree
114
+ def test_the_zenodo_record_will_carry_what_it_should():
115
+ """`.zenodo.json` is only read at archive time, so nothing else checks it."""
116
+ import json
117
+
118
+ record = json.loads((ROOT / ".zenodo.json").read_text())
119
+ assert record["upload_type"] == "software"
120
+ assert record["license"] == "Apache-2.0"
121
+ assert record["creators"], "a record with no author is not citable"
122
+ for creator in record["creators"]:
123
+ assert creator.get("orcid"), (
124
+ f"{creator['name']} has no ORCID, so the record will not attach "
125
+ "to their publication list"
126
+ )
127
+ # Every source the package ships data from should be reachable from the
128
+ # record, not only from DATA.md.
129
+ derived = [r["identifier"] for r in record["related_identifiers"]
130
+ if r["relation"] == "isDerivedFrom"]
131
+ assert len(derived) >= 8, derived
132
+
133
+
134
+ @source_tree
135
+ def test_the_README_agrees_with_DATA_md_on_the_count():
136
+ data = (ROOT / "DATA.md").read_text()
137
+ readme = (ROOT / "README.md").read_text()
138
+ match = re.search(r"(\w+) entries are\s+documented there: (\w+) confirmed",
139
+ readme)
140
+ assert match, "the README no longer states the count"
141
+ assert WORDS[match.group(1)] == _numbered_sections(data)
142
+ assert WORDS[match.group(2)] == _confirmed_sections(data)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes