Skip to content
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

Add support to Pyspectral NIRReflectance masking limit #1390

Merged
merged 5 commits into from Oct 19, 2020

Conversation

pnuu
Copy link
Member

@pnuu pnuu commented Oct 5, 2020

This is adds support to masking_limit keyword argument for NIRReflectance modifier added in pytroll/pyspectral#113

  • Tests added
  • Passes flake8 satpy
  • Fully documented

I'm not sure where to document this feature, but the usage shown below.

This disables the masking altogether. See pytroll/pyspectral#113 for an example image. This is the old default behaviour, which was changed in Pyspectral May (pytroll/pyspectral@e9a234f#diff-2a5232e6ece84bcd6cc7c97d82a7a574).

  nir_reflectance_no_masking:
    compositor: !!python/name:satpy.composites.NIRReflectance
    prerequisites:
      - name: IR_108
    optional_prerequisites:
    - solar_zenith_angle
    - IR_134
    masking_limit: null

And definition of a specific masking limit:

  nir_reflectance_no_masking:
    compositor: !!python/name:satpy.composites.NIRReflectance
    prerequisites:
      - name: IR_108
    optional_prerequisites:
    - solar_zenith_angle
    - IR_134
    masking_limit: 88.0

The default is to mask with the default limit defined in pyspectral.near_infrared_reflectance.TERMINATOR_LIMIT, which is also the current behaviour.

@pnuu pnuu added enhancement code enhancements, features, improvements component:compositors labels Oct 5, 2020
@pnuu pnuu requested review from mraspaud and djhoese October 5, 2020 08:28
@pnuu pnuu self-assigned this Oct 5, 2020
@codecov
Copy link

codecov bot commented Oct 5, 2020

Codecov Report

❗ No coverage uploaded for pull request base (master@40fb438). Click here to learn what that means.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff            @@
##             master    #1390   +/-   ##
=========================================
  Coverage          ?   90.57%           
=========================================
  Files             ?      228           
  Lines             ?    33430           
  Branches          ?        0           
=========================================
  Hits              ?    30278           
  Misses            ?     3152           
  Partials          ?        0           
Impacted Files Coverage Δ
satpy/composites/__init__.py 82.80% <100.00%> (ø)
satpy/tests/test_composites.py 99.89% <100.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 40fb438...2e20102. Read the comment docs.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.001%) to 90.566% when pulling 3230f85 on pnuu:feature-pyspectral-masking-limit into 40fb438 on pytroll:master.

@coveralls
Copy link

coveralls commented Oct 5, 2020

Coverage Status

Coverage increased (+0.007%) to 90.571% when pulling 2e20102 on pnuu:feature-pyspectral-masking-limit into 40fb438 on pytroll:master.

@pnuu
Copy link
Member Author

pnuu commented Oct 5, 2020

Before I forget. This test tested that the default keyword argument should not be None, but at the same time explicitly passed None as the value. I changed this to do what the method name said.

@mraspaud
Copy link
Member

mraspaud commented Oct 5, 2020

@pnuu this is the reason for that test: 53620fd#diff-c3f713f166b7940f26ca41873bcd1890R650-R655
So the objective was to check that the default value from the calculator was use if None was passed.

Comment on lines -703 to +708
if self.sun_zenith_threshold is not None:
reflectance_3x_calculator = Calculator(metadata['platform_name'], metadata['sensor'], metadata['name'],
sunz_threshold=self.sun_zenith_threshold)
else:
reflectance_3x_calculator = Calculator(metadata['platform_name'], metadata['sensor'], metadata['name'])
self.sun_zenith_threshold = reflectance_3x_calculator.sunz_threshold
reflectance_3x_calculator = Calculator(metadata['platform_name'], metadata['sensor'], metadata['name'],
sunz_threshold=self.sun_zenith_threshold,
masking_limit=self.masking_limit)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this is right. When self.sun_zenith_threshold is None, we want to replace it with the default value from the calculator.
This was what #1319 was about.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh. That sounds wrong. I'd expect None to mean "do not apply SZA correction in the Calculator". I'd just fix the documentation to say If not explicitly specified, the default value defined in Pyspectral will be used.

But anyway, the behaviour on the Satpy side doesn't change: the default value is used if the user doesn't override it. If the user decides to override the default with None, they'll probably know what they are doing (which is, crash the code 😁).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, just change the __init__ docstring then :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docstring updated. Good thing, as I didn't remember to add the new kwarg there before.

@pnuu
Copy link
Member Author

pnuu commented Oct 5, 2020

@pnuu this is the reason for that test: 53620fd#diff-c3f713f166b7940f26ca41873bcd1890R650-R655
So the objective was to check that the default value from the calculator was use if None was passed.

... which was because Satpy didn't respect the default and overrode it with None. This PR also fixes that by importing the defaults from Pyspectral.

@mraspaud
Copy link
Member

mraspaud commented Oct 5, 2020

Last question (maybe :) ): Should we use the pyspectral default or prevent potential problems if pyspectral is modified and hardcode a satpy-specific set of limits?

@pnuu
Copy link
Member Author

pnuu commented Oct 5, 2020

I thought about that. Now that you raise the point again, I think that would make more sense than importing from Pyspectral. It would also make testing more explicit. I'll add Satpy-internal thresholds for these two arguments. Any suggestions what the default for masking_limit should be? Based on my experience with Day/Night composites I'd say masking_limit=None (no masking) is the best choice, but for "standalone" composites the "strict" limit might be better 🤔

@mraspaud
Copy link
Member

mraspaud commented Oct 5, 2020

ok, what if the masking limit is beyond the sunz_threshold, would that have the same effect as masking_limit set to None for Day/Night composites?

@pnuu
Copy link
Member Author

pnuu commented Oct 5, 2020

It behaves the same as long as there's overlap between the two composites. It seems the default blending in DayNightCompositor is from 85.0 to 88.0 degrees SZA. So beyond 88.0 the NIRReflectance shouldn't matter at all. I'll try that limit and use it if seems to work as expected.

@pnuu
Copy link
Member Author

pnuu commented Oct 5, 2020

Yep, The results are identical. Below are images with the 88.0 degree limit, first the single composite and then with night_microphysical on the night side. I'll update the tests and push the updates in a bit.

day_microphysical_20200320_180000

day_microphysical_with_night_microphysical_20200320_180000_88

Copy link
Member

@mraspaud mraspaud left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just one stylistic question

Comment on lines 63 to 64
NIR_REFLECTANCE_TERMINATOR_LIMIT = 85.0
NIR_REFLECTANCE_MASKING_LIMIT = 88.0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to keep things gathered in the same place, would it be worth have these at class attributes to NIRReflectance?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do. Update follows in a minute or three.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code and tests updated, tested with real data.

@pnuu
Copy link
Member Author

pnuu commented Oct 5, 2020

Just as a reminder: the Pyspectral PR pytroll/pyspectral#113 needs to be merged (and released) first for this to be usable.

@mraspaud mraspaud added the blocked Waiting on an external action (eg release of other package) label Oct 5, 2020
@mraspaud
Copy link
Member

mraspaud commented Oct 6, 2020

Probably one more thing to do here is to set the minimal version of pyspectral in setup.py

@pnuu
Copy link
Member Author

pnuu commented Oct 6, 2020

Agreed. That'll need the new Pyspectral release to see the actual version number.

@pnuu
Copy link
Member Author

pnuu commented Oct 7, 2020

Pyspectral version requirement updated to the release made yesterday.

@pnuu pnuu removed the blocked Waiting on an external action (eg release of other package) label Oct 19, 2020
@pnuu
Copy link
Member Author

pnuu commented Oct 19, 2020

This PR isn't blocked anymore by the Pyspectral PR which has been merged and released.

@mraspaud mraspaud merged commit 2948936 into pytroll:master Oct 19, 2020
@pnuu pnuu deleted the feature-pyspectral-masking-limit branch October 21, 2020 04:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component:compositors enhancement code enhancements, features, improvements
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants