diff --git a/01-Tue/README.md b/01-Tue/README.md
index d29796ec..03a92450 100644
--- a/01-Tue/README.md
+++ b/01-Tue/README.md
@@ -5,7 +5,11 @@
- Presentation materials: [repo](https://github.com/yaylasezginer/ohw-tutorials/tree/OHW26/01-Tue/DataAccess)
- [YouTube recording]()
-## Inroduction to Xarray
+## Introduction to Xarray
- Presenter: Valentina Staneva, Alex Kerney
- Presentation materials: [repo](https://github.com/oceanhackweek/ohw-tutorials/tree/OHW26/01-Tue/xarray)
- [YouTube recording]()
+
+## Green ~~Eggs~~ Crabs and ~~Ham~~ OISST (using Icechunk)
+- Presenter: Alex Kerney
+- Presentation materials: [repo](https://github.com/oceanhackweek/ohw-tutorials/tree/OHW26/01-Tue/xarray)
diff --git a/01-Tue/xarray/Green_Crab_MT_points.csv b/01-Tue/xarray/Green_Crab_MT_points.csv
new file mode 100644
index 00000000..788249bb
--- /dev/null
+++ b/01-Tue/xarray/Green_Crab_MT_points.csv
@@ -0,0 +1,1269 @@
+SITES,LATITUDE,LONGITUDE
+Little John-NW,43.753894,-70.136267
+Little John-NE,43.753994,-70.135828
+Little John-SE,43.753647,-70.13635
+Mackworth- SE,43.6919,-70.228983
+Mackworth- SW,43.691739,-70.228897
+Mackworth- NW,43.692039,-70.137333
+Little John-NW,43.75405,-70.137333
+Little John-NE,43.754083,-70.137833
+Little John-SE,43.753733,-70.136267
+Mackworth- SE,43.753894,-70.135828
+Mackworth- SW,43.754083,-70.138667
+Mackworth- NW,43.753733,-70.138167
+Little John-NW,43.6919,-70.137333
+Little John-NE,43.691739,-70.138667
+Little John-SE,43.692039,-70.137833
+Mackworth- SE,43.75405,-70.138
+Mackworth- SW,43.754083,-70.138667
+Mackworth- NW,43.753733,-70.138167
+Little John-NW,43.753417,-70.1363
+Little John-NE,43.7536,-70.136367
+Little John-SE,43.753717,-70.138833
+Mackworth- SE,43.692267,-70.140417
+Mackworth- SW,43.692217,-70.218536
+Mackworth- NW,43.692383,-70.218522
+Little John-NW,43.753417,-70.1363
+Little John-NE,43.7536,-70.136367
+Little John-SE,43.753783,-70.136417
+Mackworth- SE,43.6923,-70.227583
+Mackworth- SW,43.6924,-70.227883
+Mackworth- NW,43.6925,-70.2278
+Maquoit,43.8475914,-70.0148456
+Maquoit,43.8475914,-70.0148456
+Maquoit,43.8475914,-70.0148456
+Maquoit,43.8475914,-70.0148456
+Maquoit,43.8475914,-70.0148456
+Maquoit,43.8475914,-70.0148456
+Maquoit,43.8475914,-70.0148456
+Maquoit,43.8475914,-70.0148456
+Maquoit,43.8475914,-70.0148456
+Middle Bay,43.8262905,-69.9843805
+Middle Bay,43.8262905,-69.9843805
+Middle Bay,43.8262905,-69.9843805
+Middle Bay,43.8262905,-69.9843805
+Middle Bay,43.8262905,-69.9843805
+Middle Bay ,43.8262905,-69.9843805
+Middle Bay,43.8262905,-69.9843805
+Middle Bay,43.8262905,-69.9843805
+Middle Bay,43.8262905,-69.9843805
+Middle Bay,43.8262905,-69.9843805
+Harpswell Cove,43.851516,-69.9359829
+Harpswell Cove,43.851516,-69.9359829
+Harpswell Cove,43.851516,-69.9359829
+Harpswell Cove,43.851516,-69.9359829
+Harpswell Cove,43.851516,-69.9359829
+Harpswell Cove,43.851516,-69.9359829
+Harpswell Cove,43.851516,-69.9359829
+Harpswell Cove,43.851516,-69.9359829
+Harpswell Cove,43.851516,-69.9359829
+Princes Point,43.7625802,-70.1739374
+Princes Point,43.7625802,-70.1739374
+Princes Point,43.7625802,-70.1739374
+Princes Point,43.7625802,-70.1739374
+Princes Point,43.7625802,-70.1739374
+Princes Point,43.7625802,-70.1739374
+Princes Point,43.7625802,-70.1739374
+Thomas Point Beach,43.8945242,-69.8911593
+Thomas Point Beach,43.8945242,-69.8911593
+Thomas Point Beach,43.8945242,-69.8911593
+Thomas Point Beach,43.8945242,-69.8911593
+Thomas Point Beach,43.8945242,-69.8911593
+Thomas Point Beach,43.8945242,-69.8911593
+Thomas Point Beach,43.8945242,-69.8911593
+Thomas Point Beach,43.8945242,-69.8911593
+Biddeford,43.44785,-70.3595
+Biddeford,43.44785,-70.35948
+Biddeford,43.44785,-70.35939
+Biddeford,43.44787,-70.35937
+Biddeford,43.44788,-70.35934
+Biddeford,43.44783,-70.35935
+Biddeford,43.4479,-70.35926
+Biddeford,43.44791,-70.35926
+Biddeford,43.44793,-70.35923
+Biddeford,43.44795,-70.35912
+Biddeford,43.44786,-70.35943
+Biddeford,43.44786,-70.35938
+Biddeford,43.44783,-70.35936
+Biddeford,43.4439,-70.35933
+Biddeford,43.44789,-70.3593
+Biddeford,43.44789,-70.35925
+Biddeford,43.44791,-70.35922
+Biddeford,43.44792,-70.3592
+Biddeford,43.44795,-70.35916
+Biddeford,43.44795,-70.35913
+Biddeford,43.44771,-70.35934
+Biddeford,43.44771,-70.35933
+Biddeford,43.44735,-70.35932
+Biddeford,43.44786,-70.35928
+Biddeford,43.44779,-70.35924
+Biddeford,43.44791,-70.3592
+Biddeford,43.4479,-70.35917
+Biddeford,43.44796,-70.35918
+Biddeford,43.44792,-70.35912
+Biddeford,43.44785,-70.35948
+Biddeford,43.44781,-70.35941
+Biddeford,43.44784,-70.35938
+Biddeford,43.44785,-70.35935
+Biddeford,43.44797,-70.35933
+Biddeford,43.44789,-70.35933
+Biddeford,43.44791,-70.35927
+Biddeford,43.44789,-70.35922
+Biddeford,43.44794,-70.35918
+Biddeford,43.44796,-70.35918
+Biddeford,43.4478,-70.3595
+Biddeford,43.44782,-70.35946
+Biddeford,43.44781,-70.35944
+Biddeford,43.44782,-70.35938
+Biddeford,43.44784,-70.35936
+Biddeford,43.44784,-70.35929
+Biddeford,43.44783,-70.35929
+Biddeford,43.44785,-70.35926
+Biddeford,43.44786,-70.35919
+Biddeford,43.44787,-70.35917
+Biddeford,43.4479,-70.35923
+Biddeford,43.44789,-70.35923
+Biddeford,43.44786,-70.35924
+Biddeford,43.44784,-70.35924
+Biddeford,43.44784,-70.35939
+Biddeford,43.44778,-70.35929
+Biddeford,43.44782,-70.35944
+Biddeford,43.44794,-70.35917
+Biddeford,43.44785,-70.35921
+Biddeford,43.44786,-70.35925
+Biddeford,43.44782,-70.35927
+Biddeford,43.44781,-70.35933
+Biddeford,43.44782,-70.35938
+Biddeford,43.44783,-70.3594
+Biddeford,43.44782,-70.35945
+Biddeford,43.44778,-70.35948
+Biddeford,43.44782,-70.35948
+Biddeford,43.44784,-70.35914
+Biddeford,43.44783,-70.35919
+Biddeford,43.44786,-70.35928
+Biddeford,43.44785,-70.35934
+Biddeford,43.44781,-70.35931
+Biddeford,43.44785,-70.35935
+Biddeford,43.44788,-70.35938
+Biddeford,43.44781,-70.35943
+Biddeford,43.44783,-70.35949
+Biddeford,43.44784,-70.35949
+Biddeford,43.44786,-70.35943
+Biddeford,43.44783,-70.35919
+Biddeford,43.44786,-70.35928
+Biddeford,43.44785,-70.35934
+Biddeford,43.44781,-70.35931
+Biddeford,43.44785,-70.35935
+Biddeford,43.44788,-70.35938
+Biddeford,43.44781,-70.35943
+Biddeford,43.44783,-70.35949
+Biddeford,43.44778,-70.35962
+Biddeford,43.44786,-70.35915
+Biddeford,43.44783,-70.35919
+Biddeford,43.44786,-70.35928
+Biddeford,43.44785,-70.35934
+Biddeford,43.44781,-70.35931
+Biddeford,43.44785,-70.35935
+Biddeford,43.44788,-70.35938
+Biddeford,43.44781,-70.35943
+Biddeford,43.44783,-70.35949
+Biddeford,43.4478,-70.35948
+Biddeford,43.44782,-70.35919
+Biddeford,43.44783,-70.35919
+Biddeford,43.44786,-70.35928
+Biddeford,43.44785,-70.35934
+Biddeford,43.44781,-70.35931
+Biddeford,43.44785,-70.35935
+Biddeford,43.44788,-70.35938
+Biddeford,43.44781,-70.35943
+Biddeford,43.44783,-70.35949
+Biddeford,43.44784,-70.35957
+Little John-NW,43.754033,-70.136467
+Little John-NE,43.754,-70.136167
+Little John-SE,43.7537,-70.136133
+Mackworth- SE,43.692167,-70.227883
+Mackworth- SW,43.692467,-70.228167
+Mackworth- NW,43.753983,-70.136567
+Little John-NW,43.753983,-70.136183
+Little John-NE,43.753817,-70.136183
+Little John-SE,43.6922,-70.22795
+Mackworth- SE,43.692167,-70.22825
+Mackworth- SW,43.692433,-70.228283
+Mackworth- NW,43.692433,-70.228283
+Little John-NW,43.753983,-70.13635
+Little John-NE,43.753933,-70.1362
+Little John-SE,43.753717,-70.13605
+Mackworth- SE,43. 692167,-70.227967
+Mackworth- SW,43.692233,-70.2283
+Mackworth- NW,43.692433,-70.22825
+Little John-NW,43.754,-70.136617
+Little John-NE,43.753967,-70.1362
+Little John-SE,43.753683,-70.136167
+Mackworth- SE,43.692167,-70.227983
+Mackworth- SW,43.692167,-70.228267
+Mackworth- NW,43.692417,-70.228317
+Little John-NW,43.754,-70.13615
+Little John-NE,43.753983,-70.13615
+Little John-SE,43.75375,-70.136217
+Mackworth- SE,43.692167,-70.227917
+Mackworth- SW,43.692167,-70.2283
+Mackworth- NW,43.692467,-70.2283
+Fore River- Scour Pool,43.661552,-70.3080174
+Fore River- Scour Pool,43.661552,-70.3080174
+Fore River- Scour Pool,43.661552,-70.3080174
+Fore River- Scour Pool,43.661552,-70.3080174
+Fore River- Scour Pool,43.661552,-70.3080174
+Fore River- Scour Pool,43.661552,-70.3080174
+Fore River- Scour Pool,43.661552,-70.3080174
+Fore River- Scour Pool,43.661552,-70.3080174
+Fore River- Scour Pool,43.661552,-70.3080174
+Fore River- Scour Pool,43.661552,-70.3080174
+Fore River- Marsh Pool,43.6609884,-70.3078613
+Fore River- Marsh Pool,43.6609884,-70.3078613
+Fore River- Marsh Pool,43.6609884,-70.3078613
+Fore River- Marsh Pool,43.6609884,-70.3078613
+Dolphin Lane,43.3125441,-70.5630469
+Upper Landing,43.3295791,-70.5665107
+Jones Creek ,43.545032,-70.3344664
+WinnockÊNeck,43.5564883,-70.3363646
+Harpswell Cove,43.851516,-69.9359829
+Thomas Point ,43.8898416,-69.8911862
+Atkins Flat ,43.7527277,-69.7956192
+Branch Flat,43.7254414,-69.8496689
+SamÕs Cove,43.9869865,-69.4245734
+Broad Cove,44.0300803,-69.4077721
+Little Broad Cove,44.307332,-68.899959
+Ryder Cove,44.3415648,-68.8875348
+Hatch Cove ,44.1794093,-68.6218423
+Sunshine Bar ,44.2106637,-68.6012752
+Raccoon Cove,44.4672197,-68.2834265
+Hog Bay,44.5761443,-68.2201068
+DobbinsÕ Island,44.5050784,-67.6033264
+Perio Point ,44.5215101,-67.6118721
+Sanborn Cove,44.6830785,-67.3974513
+Randall Point,44.6874811,-67.3777066
+Burnt Cove ,44.8320469,-67.1540798
+Hallowell Island ,44.8796669,-67.1578838
+Marion Cove,44.8670204,-67.1165431
+Gleason Cove,44.9675232,-67.0549885
+HalfMoonÊCove,44.9525077,-67.0432993
+Harpswell Cove,43.851516,-69.935983
+Harpswell Cove,43.851516,-69.935983
+Harpswell Cove,43.851516,-69.935983
+Harpswell Cove,43.851516,-69.935983
+Harpswell Cove,43.851516,-69.935983
+Harpswell Cove,43.851516,-69.935983
+Harpswell Cove,43.851516,-69.935983
+Harpswell Cove,43.851516,-69.935983
+Harpswell Cove,43.851516,-69.935983
+Harpswell Cove,43.851516,-69.935983
+Harpswell Cove,43.851516,-69.935983
+Harpswell Cove,43.851516,-69.935983
+Harpswell Cove,43.851516,-69.935983
+Harpswell Cove,43.851516,-69.935983
+Harpswell Cove,43.851516,-69.935983
+Penneville,43.8546344,-69.9608317
+Penneville,43.8546344,-69.9608317
+Penneville,43.8546344,-69.9608317
+Penneville,43.8546344,-69.9608317
+Penneville,43.8546344,-69.9608317
+Penneville,43.8546344,-69.9608317
+Penneville,43.8546344,-69.9608317
+Penneville,43.8546344,-69.9608317
+Penneville,43.8546344,-69.9608317
+Penneville,43.8546344,-69.9608317
+Penneville,43.8546344,-69.9608317
+Penneville,43.8546344,-69.9608317
+Penneville,43.8546344,-69.9608317
+Penneville,43.8546344,-69.9608317
+Penneville,43.8546344,-69.9608317
+Thomas Point Beach,43.8945242,-69.8911593
+Thomas Point Beach,43.8945242,-69.8911593
+Thomas Point Beach,43.8945242,-69.8911593
+Simpsons Point,43.8520251,-69.9728267
+Simpsons Point,43.8520251,-69.9728267
+Simpsons Point,43.8520251,-69.9728267
+Maquoit,43.8475914,-70.0148456
+Maquoit,43.8475914,-70.0148456
+Princes Point,43.7625802,-70.1739374
+Princes Point,43.7625802,-70.1739374
+Princes Point,43.7625802,-70.1739374
+Harpswell Cove,43.851516,-69.935983
+Harpswell Cove,43.851516,-69.935983
+Harpswell Cove,43.851516,-69.935983
+Maquoit,43.8475914,-70.0148456
+Middle Bay,43.8262905,-69.9843805
+Harpswell Cove,43.851516,-69.935983
+Princes Point,43.7625802,-70.1739374
+Thomas Point,43.8945242,-69.8911593
+Maquoit,43.8475914,-70.0148456
+Middle Bay,43.8262905,-69.9843805
+Harpswell Cove,43.851516,-69.935983
+Princes Point,43.7625802,-70.1739374
+Thomas Point,43.8945242,-69.8911593
+Maquoit,43.8475914,-70.0148456
+Middle Bay,43.8262905,-69.9843805
+Harpswell Cove,43.851516,-69.935983
+Princes Point,43.7625802,-70.1739374
+Thomas Point,43.8945242,-69.8911593
+Maquoit,43.8475914,-70.0148456
+Middle Bay,43.8262905,-69.9843805
+Harpswell Cove,43.851516,-69.935983
+Princes Point,43.7625802,-70.1739374
+Thomas Point,43.8945242,-69.8911593
+Maquoit,43.8475914,-70.0148456
+Middle Bay,43.8262905,-69.9843805
+Harpswell Cove,43.851516,-69.935983
+Princes Point,43.7625802,-70.1739374
+Thomas Point,43.8945242,-69.8911593
+Maquoit,43.8475914,-70.0148456
+Middle Bay,43.8262905,-69.9843805
+Harpswell Cove,43.851516,-69.935983
+Princes Point,43.7625802,-70.1739374
+Thomas Point,43.8945242,-69.8911593
+Maquoit,43.8475914,-70.0148456
+Middle Bay,43.8262905,-69.9843805
+Harpswell Cove,43.851516,-69.935983
+Princes Point,43.7625802,-70.1739374
+Thomas Point,43.8945242,-69.8911593
+Maquoit,43.8475914,-70.0148456
+Middle Bay,43.8262905,-69.9843805
+Harpswell Cove,43.851516,-69.935983
+Princes Point,43.7625802,-70.1739374
+Thomas Point,43.8945242,-69.8911593
+Maquoit,43.8475914,-70.0148456
+Middle Bay,43.8262905,-69.9843805
+Harpswell Cove,43.851516,-69.935983
+Princes Point,43.7625802,-70.1739374
+Thomas Point,43.8945242,-69.8911593
+Maquoit,43.8475914,-70.0148456
+Middle Bay,43.8262905,-69.9843805
+Harpswell Cove,43.851516,-69.935983
+Princes Point,43.7625802,-70.1739374
+Thomas Point,43.8945242,-69.8911593
+Thomas Point,43.8945242,-69.8911593
+Thomas Point,43.8945242,-69.8911593
+Thomas Point,43.8945242,-69.8911593
+Thomas Point,43.8945242,-69.8911593
+Thomas Point,43.8945242,-69.8911593
+Thomas Point,43.8945242,-69.8911593
+Thomas Point,43.8945242,-69.8911593
+Prince's Point,43.7625802,-70.1739374
+Prince's Point,43.7625802,-70.1739374
+Prince's Point,43.7625802,-70.1739374
+Prince's Point,43.7625802,-70.1739374
+Prince's Point,43.7625802,-70.1739374
+Prince's Point,43.7625802,-70.1739374
+Prince's Point,43.7625802,-70.1739374
+Harpswell Cove,43.851516,-69.935983
+Harpswell Cove,43.851516,-69.935983
+Harpswell Cove,43.851516,-69.935983
+Harpswell Cove,43.851516,-69.935983
+Harpswell Cove,43.851516,-69.935983
+Harpswell Cove,43.851516,-69.935983
+Harpswell Cove,43.851516,-69.935983
+Harpswell Cove,43.851516,-69.935983
+Penneville,43.8546344,-69.9608317
+Penneville,43.8546344,-69.9608317
+Penneville,43.8546344,-69.9608317
+Penneville,43.8546344,-69.9608317
+Penneville,43.8546344,-69.9608317
+Penneville,43.8546344,-69.9608317
+Penneville,43.8546344,-69.9608317
+Penneville,43.8546344,-69.9608317
+Maquoit,43.8475914,-70.0148456
+Maquoit,43.8475914,-70.0148456
+Maquoit,43.8475914,-70.0148456
+Maquoit,43.8475914,-70.0148456
+Maquoit,43.8475914,-70.0148456
+Maquoit,43.8475914,-70.0148456
+Maquoit,43.8475914,-70.0148456
+Maquoit,43.8475914,-70.0148456
+Dolphin Lane,43.3125441,-70.5630469
+Upper Landing,43.3295791,-70.5665107
+Jones Creek ,43.545032,-70.3344664
+WinnockÊNeck,43.5564883,-70.3363646
+Harpswell Cove,43.851516,-69.9359829
+Thomas Point ,43.8898416,-69.8911862
+Atkins Flat ,43.7527277,-69.7956192
+Branch Flat,43.7254414,-69.8496689
+SamÕs Cove,43.9869865,-69.4245734
+Broad Cove,44.0300803,-69.4077721
+Little Broad Cove,44.307332,-68.899959
+Ryder Cove,44.3415648,-68.8875348
+Hatch Cove ,44.1794093,-68.6218423
+Sunshine Bar ,44.2106637,-68.6012752
+Raccoon Cove,44.4672197,-68.2834265
+Hog Bay,44.5761443,-68.2201068
+DobbinsÕ Island,44.5050784,-67.6033264
+Perio Point ,44.5215101,-67.6118721
+Sanborn Cove,44.6830785,-67.3974513
+Randall Point,44.6874811,-67.3777066
+Burnt Cove ,44.8320469,-67.1540798
+Hallowell Island ,44.8796669,-67.1578838
+Marion Cove,44.8670204,-67.1165431
+Gleason Cove,44.9675232,-67.0549885
+HalfMoonÊCove,44.9525077,-67.0432993
+Dolphin Lane,43.3125441,-70.5630469
+Upper Landing,43.3295791,-70.5665107
+Jones Creek ,43.545032,-70.3344664
+WinnockÊNeck,43.5564883,-70.3363646
+Harpswell Cove,43.851516,-69.9359829
+Thomas Point ,43.8898416,-69.8911862
+Atkins Flat (Fab),43.7527277,-69.7956192
+Atkins Flat (Mes),43.7527277,-69.7956192
+Branch Flat (mes),43.7254414,-69.8496689
+Branch Flat (fab),43.7254414,-69.8496689
+SamÕs Cove,43.9869865,-69.4245734
+Broad Cove,44.0300803,-69.4077721
+Little Broad Cove,44.307332,-68.899959
+Ryder Cove,44.3415648,-68.8875348
+Hatch Cove (fab),44.1794093,-68.6218423
+Hatch Cove (Mesh),44.1794093,-68.6218423
+Sunshine Bar (fab),44.2106637,-68.6012752
+Sunshine Bar (mesh),44.2106637,-68.6012752
+Raccoon Cove,44.4672197,-68.2834265
+Hog Bay,44.5761443,-68.2201068
+DobbinsÕ Island,44.5050784,-67.6033264
+Perio Point ,44.5215101,-67.6118721
+Sanborn Cove (fab),44.6830785,-67.3974513
+Sanborn Cove (mes),44.6830785,-67.3974513
+Randall Point (fab),44.6874811,-67.3777066
+Randall Point (mes),44.6874811,-67.3777066
+Burnt Cove ,44.8320469,-67.1540798
+Hallowell Island ,44.8796669,-67.1578838
+Marion Cove,44.8670204,-67.1165431
+Gleason Cove,44.9675232,-67.0549885
+HalfMoonÊCove,44.9525077,-67.0432993
+Dolphin Lane,43.3125441,-70.5630469
+Upper Landing,43.3295791,-70.5665107
+Jones Creek ,43.545032,-70.3344664
+WinnockÊNeck,43.5564883,-70.3363646
+Harpswell Cove,43.851516,-69.9359829
+Thomas Point ,43.8898416,-69.8911862
+Cushman Cove ,43.987742,-69.667775
+Maine Yankee,43.931817,-69.724433
+SamÕs Cove,43.9869865,-69.424573
+Broad Cove,44.0300803,-69.407772
+Little Broad Cove,44.307332,-68.899959
+Ryder Cove,44.3415648,-68.887535
+Raccoon Cove,44.4672197,-68.2834265
+Hog Bay,44.5761443,-68.2201068
+DobbinsÕ Island,44.5050784,-67.6033264
+Perio Point ,44.5215101,-67.6118721
+Burnt Cove ,44.8320469,-67.1540798
+Hallowell Island ,44.8796669,-67.1578838
+Marion Cove,44.8670204,-67.1165431
+Gleason Cove,44.9675232,-67.0549885
+HalfMoonÊCove,44.9525077,-67.0432993
+Dolphin Lane,43.3125441,-70.5630469
+Upper Landing,43.3295791,-70.5665107
+Jones Creek ,43.545032,-70.3344664
+WinnockÊNeck,43.5564883,-70.3363646
+Harpswell Cove,43.851516,-69.9359829
+Thomas Point ,43.8898416,-69.8911862
+Cushman Cove ,43.987742,-69.667775
+Maine Yankee,43.931817,-69.724433
+SamÕs Cove,43.9869865,-69.424573
+Broad Cove,44.0300803,-69.407772
+Little Broad Cove,44.307332,-68.899959
+Ryder Cove,44.3415648,-68.887535
+Raccoon Cove,44.4672197,-68.2834265
+Hog Bay,44.5761443,-68.2201068
+DobbinsÕ Island,44.5050784,-67.6033264
+Perio Point ,44.5215101,-67.6118721
+Gleason Cove,44.9675232,-67.0549885
+HalfMoonÊCove,44.9525077,-67.0432993
+Bunker Harbor,44.2946831,-68.284584
+Hog Island,43.9683989,-69.4234431
+Grand Marsh Bay,44.4449817,-68.0120217
+Grand Marsh Bay,44.4449817,-68.0120217
+Grand Marsh Bay,44.4449817,-68.0120217
+Grand Marsh Bay,44.4449817,-68.0120217
+Grand Marsh Bay,44.4449817,-68.0120217
+Grand Marsh Bay,44.4449817,-68.0120217
+Grand Marsh Bay,44.4449817,-68.0120217
+Grand Marsh Bay,44.4449817,-68.0120217
+Grand Marsh Bay,44.4449817,-68.0120217
+Grand Marsh Bay,44.4449817,-68.0120217
+Grand Marsh Bay,44.4449817,-68.0120217
+Grand Marsh Bay,44.4449817,-68.0120217
+Grand Marsh Bay,44.4449817,-68.0120217
+Grand Marsh Bay,44.4449817,-68.0120217
+Grand Marsh Bay,44.4449817,-68.0120217
+Grand Marsh Bay,44.4449817,-68.0120217
+,,
+Grand Marsh Bay,44.4449817,-68.0120217
+Jones Cove,44.4681326,-68.1012783
+Widgeon Cove,43.79874101,-69.97272698
+Widgeon Cove,43.79900102,-69.97321799
+Mackworth Island,43.68477998,-70.23132596
+Mackworth Island,43.68473698,-70.23193097
+Broad Cove,43.75795299,-70.19301899
+Broad Cove,43.75756399,-70.19333297
+Broad Cove,43.75596799,-70.19477902
+Broad Cove,43.75556499,-70.195049
+Cousins Island,43.766023,-70.14663697
+Cousins Island,43.76647202,-70.146627
+Cousins Island,43.76274702,-70.14640102
+Cousins Island,43.76315002,-70.14666002
+Little Chebeague Island,43.71735097,-70.14377799
+Little Chebeague Island,43.71765196,-70.14332897
+Little Chebeague Island,43.71605404,-70.14806701
+Little Chebeague Island,43.71629803,-70.14755404
+Mackworth Island,43.68477998,-70.23132596
+Mackworth Island,43.68473698,-70.23193097
+Broad Cove,43.75795299,-70.19301899
+Broad Cove,43.75756399,-70.19333297
+Widgeon Cove,43.79874101,-69.97272698
+Widgeon Cove,43.79900102,-69.97321799
+Broad Cove,43.75596799,-70.19477902
+Broad Cove,43.75556499,-70.195049
+Cousins Island,43.766023,-70.14663697
+Cousins Island,43.76647202,-70.146627
+Cousins Island,43.76274702,-70.14640102
+Cousins Island,43.76315002,-70.14666002
+Little Chebeague Island,43.71735097,-70.14377799
+Little Chebeague Island,43.71765196,-70.14332897
+Little Chebeague Island,43.71605404,-70.14806701
+Little Chebeague Island,43.71629803,-70.14755404
+Mackworth Island,43.68477998,-70.23132596
+Mackworth Island,43.68473698,-70.23193097
+Broad Cove,43.75795299,-70.19301899
+Broad Cove,43.75756399,-70.19333297
+Broad Cove,43.75596799,-70.19477902
+Broad Cove,43.75556499,-70.195049
+Cousins Island,43.766023,-70.14663697
+Cousins Island,43.76647202,-70.146627
+Cousins Island,43.76274702,-70.14640102
+Cousins Island,43.76315002,-70.14666002
+Little Chebeague Island,43.71735097,-70.14377799
+Little Chebeague Island,43.71765196,-70.14332897
+Little Chebeague Island,43.71605404,-70.14806701
+Little Chebeague Island,43.71629803,-70.14755404
+Widgeon Cove,43.79874101,-69.97272698
+Widgeon Cove,43.79900102,-69.97321799
+Mackworth Island,43.68477998,-70.23132596
+Mackworth Island,43.68473698,-70.23193097
+Broad Cove,43.75795299,-70.19301899
+Broad Cove,43.75756399,-70.19333297
+Broad Cove,43.75596799,-70.19477902
+Broad Cove,43.75556499,-70.195049
+Cousins Island,43.766023,-70.14663697
+Cousins Island,43.76647202,-70.146627
+Cousins Island,43.76274702,-70.14640102
+Cousins Island,43.76315002,-70.14666002
+Little Chebeague Island,43.71735097,-70.14377799
+Little Chebeague Island,43.71765196,-70.14332897
+Little Chebeague Island,43.71605404,-70.14806701
+Little Chebeague Island,43.71629803,-70.14755404
+Widgeon Cove,43.79874101,-69.97272698
+Widgeon Cove,43.79900102,-69.97321799
+Mackworth Island,43.68477998,-70.23132596
+Mackworth Island,43.68473698,-70.23193097
+Broad Cove,43.75795299,-70.19301899
+Broad Cove,43.75756399,-70.19333297
+Broad Cove,43.75596799,-70.19477902
+Broad Cove,43.75556499,-70.195049
+Cousins Island,43.766023,-70.14663697
+Cousins Island,43.76647202,-70.146627
+Cousins Island,43.76274702,-70.14640102
+Cousins Island,43.76315002,-70.14666002
+Little Chebeague Island,43.71735097,-70.14377799
+Little Chebeague Island,43.71765196,-70.14332897
+Little Chebeague Island,43.71605404,-70.14806701
+Little Chebeague Island,43.71629803,-70.14755404
+Widgeon Cove,43.79874101,-69.97272698
+Widgeon Cove,43.79900102,-69.97321799
+Widgeon Cove,43.79874101,-69.97272698
+Widgeon Cove,43.79900102,-69.97321799
+Mackworth Island,43.68477998,-70.23132596
+Mackworth Island,43.68473698,-70.23193097
+Broad Cove,43.75795299,-70.19301899
+Broad Cove,43.75756399,-70.19333297
+Broad Cove,43.75596799,-70.19477902
+Broad Cove,43.75556499,-70.195049
+Cousins Island,43.766023,-70.14663697
+Cousins Island,43.76647202,-70.146627
+Cousins Island,43.76274702,-70.14640102
+Cousins Island,43.76315002,-70.14666002
+Little Chebeague Island,43.71765196,-70.14332897
+Little Chebeague Island,43.71605404,-70.14806701
+Little Chebeague Island,43.71629803,-70.14755404
+Little Chebeague Island,43.79874101,-69.97272698
+Mackworth Island,43.68477998,-70.23132596
+Mackworth Island,43.68473698,-70.23193097
+Broad Cove,43.75795299,-70.19301899
+Broad Cove,43.75756399,-70.19333297
+Broad Cove,43.75596799,-70.19477902
+Broad Cove,43.75556499,-70.195049
+Cousins Island,43.766023,-70.14663697
+Cousins Island,43.76647202,-70.146627
+Cousins Island,43.76274702,-70.14640102
+Cousins Island,43.76315002,-70.14666002
+Little Chebeague Island,43.71765196,-70.14332897
+Little Chebeague Island,43.71605404,-70.14806701
+Little Chebeague Island,43.71629803,-70.14755404
+Little Chebeague Island,43.79874101,-69.97272698
+Widgeon Cove,43.79874101,-69.97272698
+Widgeon Cove,43.79900102,-69.97321799
+Mackworth Island,43.68477998,-70.23132596
+Mackworth Island,43.68473698,-70.23193097
+Broad Cove,43.75795299,-70.19301899
+Broad Cove,43.75756399,-70.19333297
+Broad Cove,43.75596799,-70.19477902
+Broad Cove,43.75556499,-70.195049
+Cousins Island,43.766023,-70.14663697
+Cousins Island,43.76647202,-70.146627
+Cousins Island,43.76274702,-70.14640102
+Cousins Island,43.76315002,-70.14666002
+Little Chebeague Island,43.71765196,-70.14332897
+Little Chebeague Island,43.71605404,-70.14806701
+Little Chebeague Island,43.71629803,-70.14755404
+Little Chebeague Island,43.79874101,-69.97272698
+Widgeon Cove,43.79874101,-69.97272698
+Widgeon Cove,43.79900102,-69.97321799
+Mackworth Island,43.68477998,-70.23132596
+Mackworth Island,43.68473698,-70.23193097
+Broad Cove,43.75795299,-70.19301899
+Broad Cove,43.75756399,-70.19333297
+Broad Cove,43.75596799,-70.19477902
+Broad Cove,43.75556499,-70.195049
+Cousins Island,43.766023,-70.14663697
+Cousins Island,43.76647202,-70.146627
+Cousins Island,43.76274702,-70.14640102
+Cousins Island,43.76315002,-70.14666002
+Little Chebeague Island,43.71765196,-70.14332897
+Little Chebeague Island,43.71605404,-70.14806701
+Little Chebeague Island,43.71629803,-70.14755404
+Little Chebeague Island,43.79874101,-69.97272698
+Mackworth Island - North,43.69248,-70.23123
+Mussel Cove,43.71143,-70.21814
+Mackworth Island - Beach,43.68958,-70.23572
+Cushing Island,43.64294,-70.20761
+Audubon,43.711032,-70.2419147
+Back Cove,43.67912,-70.26547
+Audubon,43.711032,-70.2419147
+Mackworth Island - North,43.69248,-70.23123
+SMCC,43.649958,-70.225889
+Cushing Island,43.64294,-70.20761
+The Brothers - North,43.70047,-70.21832
+Mackworth Island - Beach,43.68958,-70.23572
+Back Cove,43.67912,-70.26547
+Audubon,43.711032,-70.2419147
+Mackworth Island - North,43.69248,-70.23123
+SMCC,43.649958,-70.225889
+Alewife Cove,43.59085,-70.21031
+Cushing Island,43.64294,-70.20761
+Back Cove,43.67912,-70.26547
+Skitterygusset,43.71458,-70.24791
+Mackworth Island - Beach,43.68958,-70.23572
+SMCC,43.649958,-70.225889
+Great Diamond Island,43.67601,-70.20443
+Mackworth Island - North,43.69248,-70.23123
+Presumpscot Moorings,43.71546,-70.25923
+Alewife Cove,43.59085,-70.21031
+Cushing Island,43.64294,-70.20761
+Mackworth Island - Beach,43.68958,-70.23572
+Skitterygusset,43.71458,-70.24791
+Back Cove,43.67912,-70.26547
+Mussel Cove,43.71143,-70.21814
+Audubon,43.711032,-70.2419147
+Mackworth Island - North,43.69248,-70.23123
+The Brothers - North,43.70047,-70.21832
+SMCC,43.649958,-70.225889
+Cushing Island,43.64294,-70.20761
+Mackworth Island - Beach,43.68958,-70.23572
+Skitterygusset,43.71458,-70.24791
+Back Cove,43.67912,-70.26547
+SMCC,43.649958,-70.225889
+Audubon,43.711032,-70.2419147
+Mackworth Island - North,43.69248,-70.23123
+Great Diamond Island,43.67601,-70.20443
+Cushing Island,43.64294,-70.20761
+Mackworth Island - Beach,43.68958,-70.23572
+Mussel Cove,43.71143,-70.21814
+Skitterygusset,43.71458,-70.24791
+Mackworth Island - Beach,43.68958,-70.23572
+SMCC,43.649958,-70.225889
+Cushing Island,43.64294,-70.20761
+Skitterygusset,43.71458,-70.24791
+Mackworth Island - North,43.69248,-70.23123
+Mackworth Island - Beach,43.68958,-70.23572
+Audubon,43.711032,-70.2419147
+SMCC,43.649958,-70.225889
+Skitterygusset,43.71458,-70.24791
+Mackworth Island - North,43.69248,-70.23123
+Cushing Island,43.64294,-70.20761
+Mussel Cove,43.71143,-70.21814
+Mackworth Island - Beach,43.68958,-70.23572
+Presumpscot Moorings,43.71546,-70.25923
+Audubon,43.711032,-70.2419147
+Cushing Island,43.64294,-70.20761
+SMCC,43.649958,-70.225889
+Mussel Cove,43.71143,-70.21814
+Mackworth Island - North,43.69248,-70.23123
+Skitterygusset,43.71458,-70.24791
+Mackworth Island - Beach,43.68958,-70.23572
+Presumpscot Moorings,43.71546,-70.25923
+Audubon,43.711032,-70.2419147
+Great Diamond Island,43.67601,-70.20443
+SMCC,43.649958,-70.225889
+Cushing Island,43.64294,-70.20761
+Mussel Cove,43.71143,-70.21814
+Mackworth Island - North,43.69248,-70.23123
+Skitterygusset,43.71458,-70.24791
+SMCC,43.649958,-70.225889
+Mackworth Island - Beach,43.68958,-70.23572
+Presumpscot Moorings,43.71546,-70.25923
+Audubon,43.711032,-70.2419147
+Alewife Cove,43.59085,-70.21031
+Cushing Island,43.64294,-70.20761
+Mackworth Island - North,43.69248,-70.23123
+Skitterygusset,43.71458,-70.24791
+SMCC,43.649958,-70.225889
+Presumpscot Moorings,43.71546,-70.25923
+Audubon,43.711032,-70.2419147
+Mackworth Island - Beach,43.68958,-70.23572
+The Brothers - North,43.70047,-70.21832
+Great Diamond Island,43.67601,-70.20443
+Mackworth Island - North,43.69248,-70.23123
+Mussel Cove,43.71143,-70.21814
+Cushing Island,43.64294,-70.20761
+Skitterygusset,43.71458,-70.24791
+SMCC,43.649958,-70.225889
+Great Diamond Island,43.67601,-70.20443
+Audubon,43.711032,-70.2419147
+Presumpscot Moorings,43.71546,-70.25923
+Mackworth Island - Beach,43.68958,-70.23572
+The Brothers - North,43.70047,-70.21832
+Cushing Island,43.64294,-70.20761
+Skitterygusset,43.71458,-70.24791
+Mackworth Island - North,43.69248,-70.23123
+Presumpscot Moorings,43.71546,-70.25923
+Mackworth Island - Beach,43.68958,-70.23572
+Audubon,43.711032,-70.2419147
+SMCC,43.649958,-70.225889
+The Brothers - North,43.70047,-70.21832
+Audubon,43.711032,-70.2419147
+Mackworth Island - North,43.69248,-70.23123
+Mussel Cove,43.71143,-70.21814
+Great Diamond Island,43.67601,-70.20443
+Cushing Island,43.64294,-70.20761
+Alewife Cove,43.59085,-70.21031
+Mackworth Island - Beach,43.68958,-70.23572
+Skitterygusset,43.71458,-70.24791
+Mackworth Island - North,43.69248,-70.23123
+Great Diamond Island,43.67601,-70.20443
+Cushing Island,43.64294,-70.20761
+Skitterygusset,43.71458,-70.24791
+Back Cove,43.67912,-70.26547
+Mackworth Island - Beach,43.68958,-70.23572
+The Brothers - North,43.70047,-70.21832
+SMCC,43.649958,-70.225889
+Great Diamond Island,43.67601,-70.20443
+Mackworth Island - North,43.69248,-70.23123
+Audubon,43.711032,-70.2419147
+Presumpscot Moorings,43.71546,-70.25923
+Mussel Cove,43.71143,-70.21814
+Cushing Island,43.64294,-70.20761
+Mackworth Island - Beach,43.68958,-70.23572
+The Brothers - North,43.70047,-70.21832
+Cushing Island,43.64294,-70.20761
+Mackworth Island - North,43.69248,-70.23123
+Mussel Cove,43.71143,-70.21814
+Audubon,43.711032,-70.2419147
+SMCC,43.649958,-70.225889
+Mackworth Island - Beach,43.68958,-70.23572
+Skitterygusset,43.71458,-70.24791
+Audubon,43.711032,-70.2419147
+Mackworth Island - North,43.69248,-70.23123
+Cushing Island,43.64294,-70.20761
+Back Cove,43.67912,-70.26547
+Great Diamond Island,43.67601,-70.20443
+SMCC,43.649958,-70.225889
+Mackworth Island - Beach,43.68958,-70.23572
+Alewife Cove,43.59085,-70.21031
+Cushing Island,43.64294,-70.20761
+Mussel Cove,43.71143,-70.21814
+Mackworth Island - North,43.69248,-70.23123
+Audubon,43.711032,-70.2419147
+SMCC,43.649958,-70.225889
+Great Diamond Island,43.67601,-70.20443
+Mackworth Island - Beach,43.68958,-70.23572
+Skitterygusset,43.71458,-70.24791
+Back Cove,43.67912,-70.26547
+Skitterygusset,43.71458,-70.24791
+Mackworth Island - North,43.69248,-70.23123
+Mussel Cove,43.71143,-70.21814
+Cushing Island,43.64294,-70.20761
+SMCC,43.649958,-70.225889
+The Brothers - North,43.70047,-70.21832
+Mackworth Island - Beach,43.68958,-70.23572
+SMCC,43.649958,-70.225889
+Skitterygusset,43.71458,-70.24791
+Great Diamond Island,43.67601,-70.20443
+Audubon,43.711032,-70.2419147
+Mackworth Island - Beach,43.68958,-70.23572
+Skitterygusset,43.71458,-70.24791
+Mackworth Island - North,43.69248,-70.23123
+Mussel Cove,43.71143,-70.21814
+Great Diamond Island,43.67601,-70.20443
+Cushing Island,43.64294,-70.20761
+SMCC,43.649958,-70.225889
+Mackworth Island - Beach,43.68958,-70.23572
+Audubon,43.711032,-70.2419147
+Skitterygusset,43.71458,-70.24791
+Mackworth Island - North,43.69248,-70.23123
+Great Diamond Island,43.67601,-70.20443
+Cushing Island,43.64294,-70.20761
+SMCC,43.649958,-70.225889
+Alewife Cove,43.59085,-70.21031
+Audubon,43.711032,-70.2419147
+Mackworth Island - Beach,43.68958,-70.23572
+Back Cove,43.67912,-70.26547
+Cushing Island,43.64294,-70.20761
+Great Diamond Island,43.67601,-70.20443
+Mussel Cove,43.71143,-70.21814
+Mackworth Island - North,43.69248,-70.23123
+Skitterygusset,43.71458,-70.24791
+Audubon,43.711032,-70.2419147
+Mackworth Island - Beach,43.68958,-70.23572
+Presumpscot Moorings,43.71546,-70.25923
+Audubon,43.711032,-70.2419147
+Alewife Cove,43.59085,-70.21031
+Skitterygusset,43.71458,-70.24791
+SMCC,43.649958,-70.225889
+Great Diamond Island,43.67601,-70.20443
+Mackworth Island - North,43.69248,-70.23123
+Audubon,43.711032,-70.2419147
+Mackworth Island - Beach,43.68958,-70.23572
+SMCC,43.649958,-70.225889
+Cushing Island,43.64294,-70.20761
+Back Cove,43.67912,-70.26547
+The Brothers - North,43.70047,-70.21832
+Mackworth Island - Beach,43.68958,-70.23572
+SMCC,43.649958,-70.225889
+Great Diamond Island,43.67601,-70.20443
+Mussel Cove,43.71143,-70.21814
+Mackworth Island - North,43.69248,-70.23123
+Audubon,43.711032,-70.2419147
+Mackworth Island - Beach,43.68958,-70.23572
+The Brothers - North,43.70047,-70.21832
+Cushing Island,43.64294,-70.20761
+Skitterygusset,43.71458,-70.24791
+Back Cove,43.67912,-70.26547
+Audubon,43.711032,-70.2419147
+Mackworth Island - North,43.69248,-70.23123
+Great Diamond Island,43.67601,-70.20443
+Mussel Cove,43.71143,-70.21814
+Mackworth Island - Beach,43.68958,-70.23572
+Cushing Island,43.64294,-70.20761
+Great Diamond Island,43.67601,-70.20443
+The Brothers - North,43.70047,-70.21832
+Mussel Cove,43.71143,-70.21814
+Mackworth Island - North,43.69248,-70.23123
+Mackworth Island - Beach,43.68958,-70.23572
+Audubon,43.711032,-70.2419147
+Skitterygusset,43.71458,-70.24791
+Mackworth Island - North,43.69248,-70.23123
+Alewife Cove,43.59085,-70.21031
+SMCC,43.649958,-70.225889
+Audubon,43.711032,-70.2419147
+Mackworth Island - North,43.69248,-70.23123
+Mackworth Island - North,43.69248,-70.23123
+Cushing Island,43.64294,-70.20761
+Mackworth Island - Beach,43.68958,-70.23572
+Skitterygusset,43.71458,-70.24791
+Cushing Island,43.64294,-70.20761
+Mackworth Island - Beach,43.68958,-70.23572
+Audubon,43.711032,-70.2419147
+The Brothers - North,43.70047,-70.21832
+Mackworth Island - North,43.69248,-70.23123
+Great Diamond Island,43.67601,-70.20443
+Cushing Island,43.64294,-70.20761
+Mackworth Island - Beach,43.68958,-70.23572
+The Brothers - North,43.70047,-70.21832
+SMCC,43.649958,-70.225889
+Audubon,43.711032,-70.2419147
+Mackworth Island - North,43.69248,-70.23123
+Mussel Cove,43.71143,-70.21814
+Mackworth Island - Beach,43.68958,-70.23572
+Presumpscot Moorings,43.71546,-70.25923
+Mackworth Island - North,43.69248,-70.23123
+SMCC,43.649958,-70.225889
+Mussel Cove,43.71143,-70.21814
+The Brothers - North,43.70047,-70.21832
+Mackworth Island - North,43.69248,-70.23123
+Alewife Cove,43.59085,-70.21031
+Audubon,43.711032,-70.2419147
+Skitterygusset,43.71458,-70.24791
+Mackworth Island - Beach,43.68958,-70.23572
+Back Cove,43.67912,-70.26547
+Audubon,43.711032,-70.2419147
+Mackworth Island - Beach,43.68958,-70.23572
+The Brothers - North,43.70047,-70.21832
+Mackworth Island - North,43.69248,-70.23123
+Great Diamond Island,43.67601,-70.20443
+Mussel Cove,43.71143,-70.21814
+SMCC,43.649958,-70.225889
+Mackworth Island - Beach,43.68958,-70.23572
+Audubon,43.711032,-70.2419147
+Cushing Island,43.64294,-70.20761
+Great Diamond Island,43.67601,-70.20443
+Mackworth Island - North,43.69248,-70.23123
+Mussel Cove,43.71143,-70.21814
+SMCC,43.649958,-70.225889
+Back Cove,43.67912,-70.26547
+Mackworth Island - Beach,43.68958,-70.23572
+Audubon,43.711032,-70.2419147
+Alewife Cove,43.59085,-70.21031
+Cushing Island,43.64294,-70.20761
+Great Diamond Island,43.67601,-70.20443
+The Brothers - North,43.70047,-70.21832
+Mackworth Island - North,43.69248,-70.23123
+Mussel Cove,43.71143,-70.21814
+Mackworth Island - Beach,43.68958,-70.23572
+Audubon,43.711032,-70.2419147
+Skitterygusset,43.71458,-70.24791
+Back Cove,43.67912,-70.26547
+SMCC,43.649958,-70.225889
+Cushing Island,43.64294,-70.20761
+Great Diamond Island,43.67601,-70.20443
+Mackworth Island - North,43.69248,-70.23123
+The Brothers - North,43.70047,-70.21832
+Mussel Cove,43.71143,-70.21814
+Skitterygusset,43.71458,-70.24791
+SMCC,43.649958,-70.225889
+Mackworth Island - Beach,43.68958,-70.23572
+Audubon,43.711032,-70.2419147
+Mussel Cove,43.71143,-70.21814
+The Brothers - North,43.70047,-70.21832
+Alewife Cove,43.59085,-70.21031
+Cushing Island,43.64294,-70.20761
+Great Diamond Island,43.67601,-70.20443
+Mackworth Island - North,43.69248,-70.23123
+Cushing Island,43.64294,-70.20761
+SMCC,43.649958,-70.225889
+The Brothers - North,43.70047,-70.21832
+Mackworth Island - North,43.69248,-70.23123
+Skitterygusset,43.71458,-70.24791
+Mussel Cove,43.71143,-70.21814
+Mackworth Island - Beach,43.68958,-70.23572
+Cushing Island,43.64294,-70.20761
+SMCC,43.649958,-70.225889
+Mackworth Island - North,43.69248,-70.23123
+The Brothers - North,43.70047,-70.21832
+Mussel Cove,43.71143,-70.21814
+Audubon,43.711032,-70.2419147
+Skitterygusset,43.71458,-70.24791
+Mackworth Island - Beach,43.68958,-70.23572
+SMCC,43.649958,-70.225889
+Cushing Island,43.64294,-70.20761
+Great Diamond Island,43.67601,-70.20443
+The Brothers - North,43.70047,-70.21832
+Mussel Cove,43.71143,-70.21814
+Mackworth Island - Beach,43.68958,-70.23572
+Audubon,43.711032,-70.2419147
+Skitterygusset,43.71458,-70.24791
+Cushing Island,43.64294,-70.20761
+SMCC,43.649958,-70.225889
+Great Diamond Island,43.67601,-70.20443
+The Brothers - North,43.70047,-70.21832
+Mackworth Island - North,43.69248,-70.23123
+Audubon,43.711032,-70.2419147
+Mussel Cove,43.71143,-70.21814
+The Brothers - North,43.70047,-70.21832
+SMCC,43.649958,-70.225889
+Mackworth Island - Beach,43.68958,-70.23572
+Audubon,43.711032,-70.2419147
+Cushing Island,43.64294,-70.20761
+Great Diamond Island,43.67601,-70.20443
+Audubon,43.711032,-70.2419147
+Mussel Cove,43.71143,-70.21814
+Audubon,43.711032,-70.2419147
+Skitterygusset,43.71458,-70.24791
+Mackworth Island - Beach,43.68958,-70.23572
+Mackworth Island - North,43.69248,-70.23123
+Great Diamond Island,43.67601,-70.20443
+Alewife Cove,43.59085,-70.21031
+Cushing Island,43.64294,-70.20761
+SMCC,43.649958,-70.225889
+The Brothers - North,43.70047,-70.21832
+SMCC,43.649958,-70.225889
+Great Diamond Island,43.67601,-70.20443
+Audubon,43.711032,-70.2419147
+Mussel Cove,43.71143,-70.21814
+Skitterygusset,43.71458,-70.24791
+Mackworth Island - Beach,43.68958,-70.23572
+Cushing Island,43.64294,-70.20761
+The Brothers - North,43.70047,-70.21832
+Mackworth Island - North,43.69248,-70.23123
+Back Cove,43.67912,-70.26547
+Audubon,43.71458,-70.24791
+Skitterygusset,43.71458,-70.24791
+Mackworth Island - Beach,43.68958,-70.23572
+Cushing Island,43.64294,-70.20761
+SMCC,43.649958,-70.225889
+Mackworth Island - Beach,43.68958,-70.23572
+Audubon,43.711032,-70.2419147
+Presumpscot Moorings,43.71546,-70.25923
+Skitterygusset,43.71458,-70.24791
+Cushing Island,43.64294,-70.20761
+SMCC,43.649958,-70.225889
+Back Cove,43.67912,-70.26547
+Mackworth Island - Beach,43.68958,-70.23572
+SMCC,43.649958,-70.225889
+Great Diamond Island,43.67601,-70.20443
+Mussel Cove,43.71143,-70.21814
+Mackworth Island - North,43.69248,-70.23123
+Audubon,43.711032,-70.2419147
+Skitterygusset,43.71458,-70.24791
+Presumpscot Moorings,43.71546,-70.25923
+Snow Island,43.8129805,-69.9090979
+Long Point Cove,43.7818491,-69.9345743
+Lowell Cove,43.7509752,-69.9831968
+Garrison Cove,43.750853,-69.9590602
+Orrs Cove,43.835471,-69.9138021
+Mackworth Island - Beach,43.68958,-70.23572
+Mackworth Island - North,43.69248,-70.23123
+Alewife Cove,43.59085,-70.21031
+Long Point Cove,43.7818491,-69.9345743
+Snow Island,43.8129805,-69.9090979
+Cushing Island,43.64294,-70.20761
+Great Diamond Island,43.67601,-70.20443
+SMCC,43.649958,-70.225889
+Audubon,43.711032,-70.2419147
+Skitterygusset,43.71458,-70.24791
+Mackworth Island - Beach,43.68958,-70.23572
+Cushing Island,43.64294,-70.20761
+SMCC,43.649958,-70.225889
+Garrison Cove,43.750853,-69.9590602
+Long Point Cove,43.7818491,-69.9345743
+Orrs Cove,43.835471,-69.9138021
+Snow Island,43.8129805,-69.9090979
+Great Diamond Island,43.67601,-70.20443
+Cushing Island,43.64294,-70.20761
+SMCC,43.649958,-70.225889
+Lowell Cove,43.7509752,-69.9831968
+Garrison Cove,43.750853,-69.9590602
+Long Point Cove,43.7818491,-69.9345743
+Orrs Cove,43.835471,-69.9138021
+Snow Island,43.8129805,-69.9090979
+Alewife Cove,43.59085,-70.21031
+Mussel Cove,43.71143,-70.21814
+The Brothers - North,43.70047,-70.21832
+Mackworth Island - North,43.69248,-70.23123
+Mackworth Island - Beach,43.68958,-70.23572
+Audubon,43.711032,-70.2419147
+Skitterygusset,43.71458,-70.24791
+Orrs Cove,43.835471,-69.9138021
+Snow Island,43.8129805,-69.9090979
+Long Point Cove,43.7818491,-69.9345743
+Lowell Cove,43.7509752,-69.9831968
+Garrison Cove,43.750853,-69.9590602
+SMCC,43.649958,-70.225889
+The Brothers - North,43.70047,-70.21832
+Great Diamond Island,43.67601,-70.20443
+Skitterygusset,43.71458,-70.24791
+Garrison Cove,43.750853,-69.9590602
+Cedar Beach,43.7437857,-69.9856095
+Lowell Cove,43.7509752,-69.9831968
+Long Point Cove,43.7818491,-69.9345743
+Snow Island,43.8129805,-69.9090979
+Mackworth Island - North,43.69248,-70.23123
+Mussel Cove,43.71143,-70.21814
+Mackworth Island - Beach,43.68958,-70.23572
+Snow Island,43.8129805,-69.9090979
+Lowell Cove,43.7509752,-69.9831968
+Long Point Cove,43.7818491,-69.9345743
+Alewife Cove,43.59085,-70.21031
+Great Diamond Island,43.67601,-70.20443
+Cushing Island,43.64294,-70.20761
+Mussel Cove,43.71143,-70.21814
+Back Cove,43.67912,-70.26547
+Mackworth Island - Beach,43.68958,-70.23572
+Audubon,43.711032,-70.2419147
+Skitterygusset,43.71458,-70.24791
+Lowell Cove,43.7509752,-69.9831968
+Long Point Cove,43.7818491,-69.9345743
+Snow Island,43.8129805,-69.9090979
+Orrs Cove,43.835471,-69.9138021
+The Brothers - North,43.70047,-70.21832
+Mackworth Island - North,43.69248,-70.23123
+Alewife Cove,43.59085,-70.21031
+Cushing Island,43.64294,-70.20761
+SMCC,43.649958,-70.225889
+Great Diamond Island,43.67601,-70.20443
+Mussel Cove,43.71143,-70.21814
+The Brothers - North,43.70047,-70.21832
+Mackworth Island - North,43.69248,-70.23123
+Back Cove,43.67912,-70.26547
+Mackworth Island - Beach,43.68958,-70.23572
+Presumpscot Moorings,43.71546,-70.25923
+Audubon,43.711032,-70.2419147
+Snow Island,43.8129805,-69.9090979
+Long Point Cove,43.7818491,-69.9345743
+Lowell Cove,43.7509752,-69.9831968
+Garrison Cove,43.750853,-69.9590602
+Lowell Cove,43.7509752,-69.9831968
+Long Point Cove,43.7818491,-69.9345743
+Great Diamond Island,43.67601,-70.20443
+Cushing Island,43.64294,-70.20761
+SMCC,43.649958,-70.225889
+Mackworth Island - North,43.69248,-70.23123
+Snow Island,43.8129805,-69.9090979
+Long Point Cove,43.7818491,-69.9345743
+Lowell Cove,43.7509752,-69.9831968
+Garrison Cove,43.750853,-69.9590602
+Stovers Point,43.7578601,-69.9981029
+Mackworth Island - Beach,43.68958,-70.23572
+Audubon,43.711032,-70.2419147
+Mussel Cove,43.71143,-70.21814
+Stovers Point,43.7578601,-69.9981029
+Garrison Cove,43.750853,-69.9590602
+Long Point Cove,43.7818491,-69.9345743
+Lowell Cove,43.7509752,-69.9831968
+Snow Island,43.8129805,-69.9090979
+SMCC,43.649958,-70.225889
+Mackworth Island - North,43.69248,-70.23123
+Mussel Cove,43.71143,-70.21814
+The Brothers - North,43.70047,-70.21832
+Great Diamond Island,43.67601,-70.20443
+Garrison Cove,43.750853,-69.9590602
+Snow Island,43.8129805,-69.9090979
+Mackworth Island - Beach,43.68958,-70.23572
+Audubon,43.711032,-70.2419147
+Skitterygusset,43.71458,-70.24791
+Stovers Point,43.7578601,-69.9981029
+Garrison Cove,43.750853,-69.9590602
+Long Point Cove,43.7818491,-69.9345743
+Snow Island,43.8129805,-69.9090979
+Orrs Cove,43.835471,-69.9138021
+Cushing Island,43.64294,-70.20761
+SMCC,43.649958,-70.225889
+Great Diamond Island,43.67601,-70.20443
+The Brothers - North,43.70047,-70.21832
+Audubon,43.711032,-70.2419147
+Snow Island,43.8129805,-69.9090979
+Long Point Cove,43.7818491,-69.9345743
+Lowell Cove,43.7509752,-69.9831968
+Garrison Cove,43.750853,-69.9590602
+Back Cove,43.67912,-70.26547
+Mackworth Island - North,43.69248,-70.23123
+Mackworth Island - Beach,43.68958,-70.23572
+Stovers Point,43.7578601,-69.9981029
+Garrison Cove,43.750853,-69.9590602
+Lowell Cove,43.7509752,-69.9831968
+Long Point Cove,43.7818491,-69.9345743
+Snow Island,43.8129805,-69.9090979
+Mussel Cove,43.71143,-70.21814
+The Brothers - North,43.70047,-70.21832
+Mackworth Island - North,43.69248,-70.23123
+Great Diamond Island,43.67601,-70.20443
+SMCC,43.649958,-70.225889
+Garrison Cove,43.750853,-69.9590602
+Long Point Cove,43.7818491,-69.9345743
+Snow Island,43.8129805,-69.9090979
+Stovers Point,43.7578601,-69.9981029
+Garrison Cove,43.750853,-69.9590602
+Lowell Cove,43.7509752,-69.9831968
+Long Point Cove,43.7818491,-69.9345743
+Snow Island,43.8129805,-69.9090979
+Snow Island,43.8129805,-69.9090979
+Long Point Cove,43.7818491,-69.9345743
+Lowell Cove,43.7509752,-69.9831968
+Garrison Cove,43.750853,-69.9590602
+Long Point Cove,43.7818491,-69.9345743
+Snow Island,43.8129805,-69.9090979
+SMCC,43.649958,-70.225889
+SMCC,43.649958,-70.225889
+Presumpscot Moorings,43.71546,-70.25923
+Skitterygusset,43.71458,-70.24791
+Audubon,43.711032,-70.2419147
+Mackworth Island - Beach,43.68958,-70.23572
+Mackworth Island - North,43.69248,-70.23123
+Great Diamond Island,43.67601,-70.20443
+Lowell Cove,43.7509752,-69.9831968
+Snow Island,43.8129805,-69.9090979
+Orrs Cove,43.835471,-69.9138021
+Long Point Cove,43.7818491,-69.9345743
+SMCC,43.649958,-70.225889
+Lowell Cove,43.7509752,-69.9831968
+Long Point Cove,43.7818491,-69.9345743
+Snow Island,43.8129805,-69.9090979
+Mussel Cove,43.71143,-70.21814
+Mackworth Island - North,43.69248,-70.23123
+Cushing Island,43.64294,-70.20761
+Lowell Cove,43.7509752,-69.9831968
+Garrison Cove,43.750853,-69.9590602
+Stovers Point,43.7578601,-69.9981029
+Orrs Cove,43.835471,-69.9138021
+Long Point Cove,43.7818491,-69.9345743
+Cushing Island,43.64294,-70.20761
+Garrison Cove,43.750853,-69.9590602
+SMCC,43.649958,-70.225889
+Lowell Cove,43.7509752,-69.9831968
+Long Point Cove,43.7818491,-69.9345743
+Snow Island,43.8129805,-69.9090979
+Orrs Cove,43.835471,-69.9138021
+Audubon,43.711032,-70.2419147
+Lowell Cove,43.7509752,-69.9831968
+Garrison Cove,43.750853,-69.9590602
+Snow Island,43.8129805,-69.9090979
+Long Point Cove,43.7818491,-69.9345743
+Garrison Cove,43.750853,-69.9590602
+Lowell Cove,43.7509752,-69.9831968
+Long Point Cove,43.7818491,-69.9345743
+Cushing Island,43.64294,-70.20761
+Skitterygusset,43.71458,-70.24791
+Presumpscot Moorings,43.71546,-70.25923
+SMCC,43.649958,-70.225889
+The Brothers - North,43.70047,-70.21832
+Garrison Cove,43.750853,-69.9590602
+Lowell Cove,43.7509752,-69.9831968
+Long Point Cove,43.7818491,-69.9345743
+Snow Island,43.8129805,-69.9090979
+Alewife Cove,43.59085,-70.21031
+Cushing Island,43.64294,-70.20761
+Mackworth Island - North,43.69248,-70.23123
+SMCC,43.649958,-70.225889
+Audubon,43.711032,-70.2419147
+Skitterygusset,43.71458,-70.24791
+Long Point Cove,43.7818491,-69.9345743
+Snow Island,43.8129805,-69.9090979
+Lowell Cove,43.7509752,-69.9831968
+The Brothers - North,43.70047,-70.21832
+Mackworth Island - North,43.69248,-70.23123
+Audubon,43.711032,-70.2419147
+Skitterygusset,43.71458,-70.24791
+SMCC,43.649958,-70.225889
+Stovers Point,43.7578601,-69.9981029
+Garrison Cove,43.750853,-69.9590602
+Long Point Cove,43.7818491,-69.9345743
+Orrs Cove,43.835471,-69.9138021
+Snow Island,43.8129805,-69.9090979
+Orrs Cove,43.835471,-69.9138021
+Snow Island,43.8129805,-69.9090979
+Long Point Cove,43.7818491,-69.9345743
+Stovers Point,43.7578601,-69.9981029
+Cushing Island,43.64294,-70.20761
+SMCC,43.649958,-70.225889
+The Brothers - North,43.70047,-70.21832
+Snow Island,43.8129805,-69.9090979
+Long Point Cove,43.7818491,-69.9345743
+Mussel Cove,43.71143,-70.21814
+Audubon,43.711032,-70.2419147
+Skitterygusset,43.71458,-70.24791
+Presumpscot Moorings,43.71546,-70.25923
+Alewife Cove,43.59085,-70.21031
+SMCC,43.649958,-70.225889
+The Brothers - North,43.70047,-70.21832
+Mackworth Island - North,43.69248,-70.23123
+Orrs Cove,43.835471,-69.9138021
+Snow Island,43.8129805,-69.9090979
+Long Point Cove,43.7818491,-69.9345743
+Lowell Cove,43.7509752,-69.9831968
+Skitterygusset,43.71458,-70.24791
+Mackworth Island - Beach,43.68958,-70.23572
+Mussel Cove,43.71143,-70.21814
\ No newline at end of file
diff --git a/01-Tue/xarray/oisst_green_crabs.ipynb b/01-Tue/xarray/oisst_green_crabs.ipynb
new file mode 100644
index 00000000..45bfbfba
--- /dev/null
+++ b/01-Tue/xarray/oisst_green_crabs.ipynb
@@ -0,0 +1,5055 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "Hbol",
+ "metadata": {
+ "marimo": {
+ "config": {
+ "hide_code": true
+ },
+ "md_prefix": "r"
+ }
+ },
+ "source": [
+ "# Extracting OISST timeseries from green crab study locations.\n",
+ "\n",
+ "First import libraries."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "MJUe",
+ "metadata": {
+ "marimo": {
+ "config": {
+ "hide_code": true
+ }
+ },
+ "tags": [
+ "remove-input"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "import re\n",
+ "from getpass import getpass\n",
+ "\n",
+ "import icechunk as ic\n",
+ "import numpy as np\n",
+ "import xarray as xr\n",
+ "import pandas as pd\n",
+ "from pydantic import BaseModel, Field\n",
+ "import geopandas as gpd"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "vblA",
+ "metadata": {
+ "marimo": {
+ "config": {
+ "hide_code": true
+ },
+ "md_prefix": "r"
+ }
+ },
+ "source": [
+ "Open and explore the source dataframe (once it was externally pruned down to just sites/location)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "bkHC",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " SITES \n",
+ " LATITUDE \n",
+ " LONGITUDE \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " 0 \n",
+ " Little John-NW \n",
+ " 43.753894 \n",
+ " -70.136267 \n",
+ " \n",
+ " \n",
+ " 1 \n",
+ " Little John-NE \n",
+ " 43.753994 \n",
+ " -70.135828 \n",
+ " \n",
+ " \n",
+ " 2 \n",
+ " Little John-SE \n",
+ " 43.753647 \n",
+ " -70.136350 \n",
+ " \n",
+ " \n",
+ " 3 \n",
+ " Mackworth- SE \n",
+ " 43.6919 \n",
+ " -70.228983 \n",
+ " \n",
+ " \n",
+ " 4 \n",
+ " Mackworth- SW \n",
+ " 43.691739 \n",
+ " -70.228897 \n",
+ " \n",
+ " \n",
+ " ... \n",
+ " ... \n",
+ " ... \n",
+ " ... \n",
+ " \n",
+ " \n",
+ " 1263 \n",
+ " Long Point Cove \n",
+ " 43.7818491 \n",
+ " -69.934574 \n",
+ " \n",
+ " \n",
+ " 1264 \n",
+ " Lowell Cove \n",
+ " 43.7509752 \n",
+ " -69.983197 \n",
+ " \n",
+ " \n",
+ " 1265 \n",
+ " Skitterygusset \n",
+ " 43.71458 \n",
+ " -70.247910 \n",
+ " \n",
+ " \n",
+ " 1266 \n",
+ " Mackworth Island - Beach \n",
+ " 43.68958 \n",
+ " -70.235720 \n",
+ " \n",
+ " \n",
+ " 1267 \n",
+ " Mussel Cove \n",
+ " 43.71143 \n",
+ " -70.218140 \n",
+ " \n",
+ " \n",
+ "
\n",
+ "
1268 rows × 3 columns
\n",
+ "
"
+ ],
+ "text/plain": [
+ " SITES LATITUDE LONGITUDE\n",
+ "0 Little John-NW 43.753894 -70.136267\n",
+ "1 Little John-NE 43.753994 -70.135828\n",
+ "2 Little John-SE 43.753647 -70.136350\n",
+ "3 Mackworth- SE 43.6919 -70.228983\n",
+ "4 Mackworth- SW 43.691739 -70.228897\n",
+ "... ... ... ...\n",
+ "1263 Long Point Cove 43.7818491 -69.934574\n",
+ "1264 Lowell Cove 43.7509752 -69.983197\n",
+ "1265 Skitterygusset 43.71458 -70.247910\n",
+ "1266 Mackworth Island - Beach 43.68958 -70.235720\n",
+ "1267 Mussel Cove 43.71143 -70.218140\n",
+ "\n",
+ "[1268 rows x 3 columns]"
+ ]
+ },
+ "execution_count": 2,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "source_df = pd.read_csv(\"./Green_Crab_MT_points.csv\", engine=\"python\")\n",
+ "source_df"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "lEQa",
+ "metadata": {
+ "marimo": {
+ "config": {
+ "hide_code": true
+ },
+ "md_prefix": "r"
+ }
+ },
+ "source": [
+ "Filter to just unique points (1,268 -> 85 points), and set the site names as the index variable (so they become the coordinate variable)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "PKri",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " LATITUDE \n",
+ " LONGITUDE \n",
+ " \n",
+ " \n",
+ " SITES \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " Little John-NW \n",
+ " 43.753894 \n",
+ " -70.136267 \n",
+ " \n",
+ " \n",
+ " Little John-NE \n",
+ " 43.753994 \n",
+ " -70.135828 \n",
+ " \n",
+ " \n",
+ " Little John-SE \n",
+ " 43.753647 \n",
+ " -70.136350 \n",
+ " \n",
+ " \n",
+ " Mackworth- SE \n",
+ " 43.6919 \n",
+ " -70.228983 \n",
+ " \n",
+ " \n",
+ " Mackworth- SW \n",
+ " 43.691739 \n",
+ " -70.228897 \n",
+ " \n",
+ " \n",
+ " ... \n",
+ " ... \n",
+ " ... \n",
+ " \n",
+ " \n",
+ " Lowell Cove \n",
+ " 43.7509752 \n",
+ " -69.983197 \n",
+ " \n",
+ " \n",
+ " Garrison Cove \n",
+ " 43.750853 \n",
+ " -69.959060 \n",
+ " \n",
+ " \n",
+ " Orrs Cove \n",
+ " 43.835471 \n",
+ " -69.913802 \n",
+ " \n",
+ " \n",
+ " Cedar Beach \n",
+ " 43.7437857 \n",
+ " -69.985609 \n",
+ " \n",
+ " \n",
+ " Stovers Point \n",
+ " 43.7578601 \n",
+ " -69.998103 \n",
+ " \n",
+ " \n",
+ "
\n",
+ "
85 rows × 2 columns
\n",
+ "
"
+ ],
+ "text/plain": [
+ " LATITUDE LONGITUDE\n",
+ "SITES \n",
+ "Little John-NW 43.753894 -70.136267\n",
+ "Little John-NE 43.753994 -70.135828\n",
+ "Little John-SE 43.753647 -70.136350\n",
+ "Mackworth- SE 43.6919 -70.228983\n",
+ "Mackworth- SW 43.691739 -70.228897\n",
+ "... ... ...\n",
+ "Lowell Cove 43.7509752 -69.983197\n",
+ "Garrison Cove 43.750853 -69.959060\n",
+ "Orrs Cove 43.835471 -69.913802\n",
+ "Cedar Beach 43.7437857 -69.985609\n",
+ "Stovers Point 43.7578601 -69.998103\n",
+ "\n",
+ "[85 rows x 2 columns]"
+ ]
+ },
+ "execution_count": 3,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "unique_df = source_df.drop_duplicates([\"SITES\"]).set_index(\"SITES\")\n",
+ "unique_df"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "Xref",
+ "metadata": {
+ "marimo": {
+ "config": {
+ "hide_code": true
+ },
+ "md_prefix": "r"
+ }
+ },
+ "source": [
+ "Convert to an Xarray dataset with the point names as a coordinate, and rename the columns to what other operations expect (points, lat, lon)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "SFPL",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ "
<xarray.Dataset> Size: 2kB\n",
+ "Dimensions: (points: 85)\n",
+ "Coordinates:\n",
+ " * points (points) object 680B 'Little John-NW' ... 'Stovers Point'\n",
+ "Data variables:\n",
+ " lat (points) object 680B '43.753894' '43.753994' ... '43.7578601'\n",
+ " lon (points) float64 680B -70.14 -70.14 -70.14 ... -69.91 -69.99 -70.0 Dimensions:
Coordinates: (1)
Data variables: (2)
Indexes: (1)
PandasIndex
PandasIndex(Index(['Little John-NW', 'Little John-NE', 'Little John-SE', 'Mackworth- SE',\n",
+ " 'Mackworth- SW', 'Mackworth- NW', 'Maquoit', 'Middle Bay',\n",
+ " 'Middle Bay ', 'Harpswell Cove', 'Princes Point', 'Thomas Point Beach',\n",
+ " 'Biddeford', 'Fore River- Scour Pool', 'Fore River- Marsh Pool',\n",
+ " 'Dolphin Lane', 'Upper Landing', 'Jones Creek ', 'WinnockÊNeck',\n",
+ " 'Thomas Point ', 'Atkins Flat ', 'Branch Flat', 'SamÕs Cove',\n",
+ " 'Broad Cove', 'Little Broad Cove', 'Ryder Cove', 'Hatch Cove ',\n",
+ " 'Sunshine Bar ', 'Raccoon Cove', 'Hog Bay', 'DobbinsÕ Island',\n",
+ " 'Perio Point ', 'Sanborn Cove', 'Randall Point', 'Burnt Cove ',\n",
+ " 'Hallowell Island ', 'Marion Cove', 'Gleason Cove', 'HalfMoonÊCove',\n",
+ " 'Penneville', 'Simpsons Point', 'Thomas Point', 'Prince's Point',\n",
+ " 'Atkins Flat (Fab)', 'Atkins Flat (Mes)', 'Branch Flat (mes)',\n",
+ " 'Branch Flat (fab)', 'Hatch Cove (fab)', 'Hatch Cove (Mesh)',\n",
+ " 'Sunshine Bar (fab)', 'Sunshine Bar (mesh)', 'Sanborn Cove (fab)',\n",
+ " 'Sanborn Cove (mes)', 'Randall Point (fab)', 'Randall Point (mes)',\n",
+ " 'Cushman Cove ', 'Maine Yankee', 'Bunker Harbor', 'Hog Island',\n",
+ " 'Grand Marsh Bay', nan, 'Jones Cove', 'Widgeon Cove',\n",
+ " 'Mackworth Island', 'Cousins Island', 'Little Chebeague Island',\n",
+ " 'Mackworth Island - North', 'Mussel Cove', 'Mackworth Island - Beach',\n",
+ " 'Cushing Island', 'Audubon', 'Back Cove', 'SMCC',\n",
+ " 'The Brothers - North', 'Alewife Cove', 'Skitterygusset',\n",
+ " 'Great Diamond Island', 'Presumpscot Moorings', 'Snow Island',\n",
+ " 'Long Point Cove', 'Lowell Cove', 'Garrison Cove', 'Orrs Cove',\n",
+ " 'Cedar Beach', 'Stovers Point'],\n",
+ " dtype='str', name='points')) Attributes: (0)
"
+ ],
+ "text/plain": [
+ " Size: 2kB\n",
+ "Dimensions: (points: 85)\n",
+ "Coordinates:\n",
+ " * points (points) object 680B 'Little John-NW' ... 'Stovers Point'\n",
+ "Data variables:\n",
+ " lat (points) object 680B '43.753894' '43.753994' ... '43.7578601'\n",
+ " lon (points) float64 680B -70.14 -70.14 -70.14 ... -69.91 -69.99 -70.0"
+ ]
+ },
+ "execution_count": 4,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "points_ds = unique_df.to_xarray().rename({\"LATITUDE\": \"lat\", \"LONGITUDE\": \"lon\", \"SITES\": \"points\"})\n",
+ "points_ds"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "BYtC",
+ "metadata": {
+ "marimo": {
+ "config": {
+ "hide_code": true
+ },
+ "md_prefix": "r"
+ }
+ },
+ "source": [
+ "Enter in AWS access credentials for the dataset. The daily dataset itself is in a public bucket, but the metadata and the monthly data is in a NERACOOS requester pays bucket."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "RGSE",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdin",
+ "output_type": "stream",
+ "text": [
+ "Enter S3 access key ID: ········\n",
+ "Enter S3 secret access key: ········\n"
+ ]
+ }
+ ],
+ "source": [
+ "access_key = getpass(\"Enter S3 access key ID: \")\n",
+ "secret_key = getpass(\"Enter S3 secret access key: \")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "Kclp",
+ "metadata": {
+ "marimo": {
+ "config": {
+ "hide_code": true
+ },
+ "md_prefix": "r"
+ }
+ },
+ "source": [
+ "Some scaffolding to help opening the dataset. The daily data lives in `noaa-cdr-sea-surface-temp-optimum-interpolation-pds`, while the metadata and monthly data is in `neracoos-data-requester-pays`, which makes the access a little convoluted."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "id": "emfo",
+ "metadata": {
+ "marimo": {
+ "config": {
+ "hide_code": true
+ }
+ },
+ "tags": [
+ "remove-input"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "BUCKET = \"noaa-cdr-sea-surface-temp-optimum-interpolation-pds\"\n",
+ "URL_PREFIX = f\"s3://{BUCKET}/\"\n",
+ "DATA_PREFIX = \"data/v2.1/avhrr\"\n",
+ "REGION = \"us-east-1\"\n",
+ "\n",
+ "# obstore / icechunk want the prefix without a trailing slash for the registry\n",
+ "# key, while icechunk's VirtualChunkContainer matches against the trailing-slash\n",
+ "# form (mirroring services/xreds dataset_spec.py).\n",
+ "STORE_PREFIX = URL_PREFIX.rstrip(\"/\")\n",
+ "\n",
+ "\n",
+ "def open_repo(prefix: str) -> ic.Repository:\n",
+ " \"\"\"Open (or create on first run) the icechunk repo for a store prefix, with\n",
+ " the NOAA virtual chunk container registered and authorized.\"\"\"\n",
+ " config = ic.RepositoryConfig.default()\n",
+ " config.set_virtual_chunk_container(\n",
+ " ic.VirtualChunkContainer(\n",
+ " url_prefix=URL_PREFIX,\n",
+ " store=ic.s3_store(region=REGION, anonymous=True),\n",
+ " ),\n",
+ " )\n",
+ "\n",
+ " return ic.Repository.open(\n",
+ " ic.s3_storage(\n",
+ " bucket=\"neracoos-data-requester-pays\",\n",
+ " prefix=prefix,\n",
+ " region=\"us-east-1\",\n",
+ " access_key_id=access_key,\n",
+ " secret_access_key=secret_key,\n",
+ " requester_pays=True,\n",
+ " ),\n",
+ " config=config,\n",
+ " authorize_virtual_chunk_access=ic.containers_credentials(\n",
+ " {URL_PREFIX: ic.s3_anonymous_credentials()}\n",
+ " ),\n",
+ " )"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "Hstk",
+ "metadata": {
+ "marimo": {
+ "config": {
+ "hide_code": true
+ },
+ "md_prefix": "r"
+ }
+ },
+ "source": [
+ "Now we can access the metadata, and open a session for access. There are a few steps here because [Icechunk](https://icechunk.io/en/stable/) uses a strong versioning system like Git, but we can use the latest data on `main`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "id": "nWHF",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ " \n",
+ "
\n",
+ " storage \n",
+ " \n",
+ "\n",
+ " \n",
+ "
type: S3 (native)
\n",
+ "
bucket: neracoos-data-requester-pays
\n",
+ "
prefix: oisst/final
\n",
+ "
region: us-east-1
\n",
+ "
requester_pays: True
\n",
+ "
\n",
+ "\n",
+ " \n",
+ "
\n",
+ " config \n",
+ " \n",
+ "\n",
+ " \n",
+ "
inline_chunk_threshold_bytes: 512 (default)
\n",
+ "
get_partial_values_concurrency: 10 (default)
\n",
+ "
max_concurrent_requests: 256 (default)
\n",
+ "
num_updates_per_repo_info_file: 1000 (default)
\n",
+ "
\n",
+ " compression \n",
+ " \n",
+ "\n",
+ " \n",
+ "
algorithm: Zstd (default)
\n",
+ "
level: 3 (default)
\n",
+ "
\n",
+ "\n",
+ " \n",
+ "
\n",
+ " caching \n",
+ " \n",
+ "\n",
+ " \n",
+ "
num_snapshot_nodes: 500000 (default)
\n",
+ "
num_chunk_refs: 15000000 (default)
\n",
+ "
num_transaction_changes: 0 (default)
\n",
+ "
num_bytes_attributes: 0 (default)
\n",
+ "
num_bytes_chunks: 0 (default)
\n",
+ "
\n",
+ "\n",
+ " \n",
+ "
\n",
+ " storage \n",
+ " \n",
+ "\n",
+ " \n",
+ "
unsafe_use_conditional_create: True (default)
\n",
+ "
unsafe_use_conditional_update: True (default)
\n",
+ "
unsafe_use_metadata: True (default)
\n",
+ "
storage_class: None
\n",
+ "
metadata_storage_class: None
\n",
+ "
chunks_storage_class: None
\n",
+ "
minimum_size_for_multipart_upload: 104857600 (default)
\n",
+ "
\n",
+ " concurrency \n",
+ " \n",
+ "\n",
+ " \n",
+ "
max_concurrent_requests_for_object: 18 (default)
\n",
+ "
ideal_concurrent_request_size: 12582912 (default)
\n",
+ "
\n",
+ "\n",
+ " \n",
+ "
\n",
+ " retries \n",
+ " \n",
+ "\n",
+ " \n",
+ "
max_tries: 10 (default)
\n",
+ "
initial_backoff_ms: 100 (default)
\n",
+ "
max_backoff_ms: 180000 (default)
\n",
+ "
\n",
+ "\n",
+ " \n",
+ "
timeouts: None
\n",
+ "
\n",
+ "\n",
+ " \n",
+ "
\n",
+ " manifest \n",
+ " \n",
+ "\n",
+ " \n",
+ "
\n",
+ " preload \n",
+ " \n",
+ "\n",
+ " \n",
+ "
max_total_refs: 10000 (default)
\n",
+ "
preload_if: None
\n",
+ "
max_arrays_to_scan: 50 (default)
\n",
+ "
\n",
+ "\n",
+ " \n",
+ "
\n",
+ " splitting \n",
+ " \n",
+ "\n",
+ " \n",
+ "
split_sizes: [(icechunk.config.ManifestSplitCondition.path_matches(\".*\"), [(icechunk.config.ManifestSplitDimCondition.any(), 4294967295)])]
\n",
+ "
\n",
+ "\n",
+ " \n",
+ "
\n",
+ " virtual_chunk_location_compression \n",
+ " \n",
+ "\n",
+ " \n",
+ "
min_num_chunks: 1000 (default)
\n",
+ "
dictionary_max_training_samples: 100 (default)
\n",
+ "
dictionary_max_size_bytes: 2048 (default)
\n",
+ "
compression_level: 3 (default)
\n",
+ "
\n",
+ "\n",
+ " \n",
+ "
max_concurrent_manifest_fetches_during_commit: 1 (default)
\n",
+ "
\n",
+ "\n",
+ " \n",
+ "
\n",
+ " repo_update_retries \n",
+ " \n",
+ "\n",
+ " \n",
+ "
\n",
+ " default \n",
+ " \n",
+ "\n",
+ " \n",
+ "
max_tries: 100
\n",
+ "
initial_backoff_ms: 50
\n",
+ "
max_backoff_ms: 30000
\n",
+ "
\n",
+ "\n",
+ " \n",
+ "
\n",
+ "\n",
+ " \n",
+ "
\n",
+ " virtual_chunk_containers \n",
+ " s3://noaa-cdr-sea-surface-temp-optimum-interpolation-pds/ \n",
+ "\n",
+ " \n",
+ "
name: None
\n",
+ "
url_prefix: \"s3://noaa-cdr-sea-surface-temp-optimum-interpolation-pds/\"
\n",
+ "
\n",
+ " store \n",
+ " \n",
+ "\n",
+ " \n",
+ "
region: \"us-east-1\"
\n",
+ "
endpoint_url: None
\n",
+ "
allow_http: False
\n",
+ "
anonymous: True
\n",
+ "
force_path_style: False
\n",
+ "
network_stream_timeout_seconds: 60
\n",
+ "
requester_pays: False
\n",
+ "
checksum_algorithm: None
\n",
+ "
\n",
+ "\n",
+ " \n",
+ "
\n",
+ " \n",
+ " \n",
+ "
\n",
+ "\n",
+ " \n",
+ "
\n"
+ ],
+ "text/plain": [
+ "\n",
+ "storage:\n",
+ " \n",
+ " type: S3 (native)\n",
+ " bucket: neracoos-data-requester-pays\n",
+ " prefix: oisst/final\n",
+ " region: us-east-1\n",
+ " requester_pays: True\n",
+ "config: "
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "final_repo = open_repo(\"oisst/final\")\n",
+ "final_repo"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "id": "iLit",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ " \n",
+ "
read_only: True
\n",
+ "
snapshot_id: ZFAQBYGWY6YVZCY4TMC0
\n",
+ "
\n"
+ ],
+ "text/plain": [
+ "\n",
+ "read_only: True\n",
+ "snapshot_id: ZFAQBYGWY6YVZCY4TMC0"
+ ]
+ },
+ "execution_count": 8,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "final_session = final_repo.readonly_session(\"main\")\n",
+ "final_session"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "ZHCJ",
+ "metadata": {
+ "marimo": {
+ "config": {
+ "hide_code": true
+ },
+ "md_prefix": "r"
+ }
+ },
+ "source": [
+ "Now we open the OISST datatree. The daily data has the 4 normal variables, but the monthly data has those variables aggregated ito mean, min, max, and standard deviation. We're going to use the monthly dataset."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "id": "ROlb",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/home/.pixi/envs/default/lib/python3.14/site-packages/argopy/utils/lists.py:38: UserWarning: An error occurred while loading the ERDDAP data fetcher, it will not be available !\n",
+ "\n",
+ "cannot import name '_quote_string_constraints' from 'erddapy.erddapy' (/home/.pixi/envs/default/lib/python3.14/site-packages/erddapy/erddapy.py)\n",
+ " warnings.warn(\n",
+ "/home/.pixi/envs/default/lib/python3.14/site-packages/argopy/utils/lists.py:50: UserWarning: An error occurred while loading the ArgoVis data fetcher, it will not be available !\n",
+ "\n",
+ "cannot import name '_quote_string_constraints' from 'erddapy.erddapy' (/home/.pixi/envs/default/lib/python3.14/site-packages/erddapy/erddapy.py)\n",
+ " warnings.warn(\n",
+ "/home/.pixi/envs/default/lib/python3.14/site-packages/argopy/utils/lists.py:66: UserWarning: An error occurred while loading the GDAC data fetcher, it will not be available !\n",
+ "\n",
+ "cannot import name '_quote_string_constraints' from 'erddapy.erddapy' (/home/.pixi/envs/default/lib/python3.14/site-packages/erddapy/erddapy.py)\n",
+ " warnings.warn(\n",
+ "/home/.pixi/envs/default/lib/python3.14/site-packages/argopy/utils/lists.py:91: UserWarning: An error occurred while loading the ERDDAP index fetcher, it will not be available !\n",
+ "\n",
+ "cannot import name '_quote_string_constraints' from 'erddapy.erddapy' (/home/.pixi/envs/default/lib/python3.14/site-packages/erddapy/erddapy.py)\n",
+ " warnings.warn(\n",
+ "/home/.pixi/envs/default/lib/python3.14/site-packages/argopy/utils/lists.py:107: UserWarning: An error occurred while loading the GDAC index fetcher, it will not be available !\n",
+ "\n",
+ "cannot import name '_quote_string_constraints' from 'erddapy.erddapy' (/home/.pixi/envs/default/lib/python3.14/site-packages/erddapy/erddapy.py)\n",
+ " warnings.warn(\n",
+ "/home/.pixi/envs/default/lib/python3.14/site-packages/xarray/backends/plugins.py:110: RuntimeWarning: Engine 'argo' loading failed:\n",
+ "cannot import name '_quote_string_constraints' from 'erddapy.erddapy' (/home/.pixi/envs/default/lib/python3.14/site-packages/erddapy/erddapy.py)\n",
+ " external_backend_entrypoints = backends_dict_from_pkg(entrypoints_unique)\n",
+ "/tmp/ipykernel_202/1777144980.py:1: FutureWarning: zarr_version is deprecated, use zarr_format\n",
+ " oisst_dt = xr.open_datatree(final_session.store, engine=\"zarr\", zarr_version=3)\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ "
<xarray.DatasetView> Size: 0B\n",
+ "Dimensions: ()\n",
+ "Data variables:\n",
+ " *empty* Groups: (2)
\n",
+ "\n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ "
<xarray.DatasetView> Size: 544GB\n",
+ "Dimensions: (time: 16405, zlev: 1, lat: 720, lon: 1440)\n",
+ "Coordinates:\n",
+ " * lat (lat) float32 3kB -89.88 -89.62 -89.38 -89.12 ... 89.38 89.62 89.88\n",
+ " * zlev (zlev) float32 4B 0.0\n",
+ " * time (time) datetime64[ns] 131kB 1981-12-21T12:00:00 ... 2026-08-02T1...\n",
+ " * lon (lon) float32 6kB 0.125 0.375 0.625 0.875 ... 359.4 359.6 359.9\n",
+ "Data variables:\n",
+ " err (time, zlev, lat, lon) float64 136GB ...\n",
+ " anom (time, zlev, lat, lon) float64 136GB ...\n",
+ " sst (time, zlev, lat, lon) float64 136GB ...\n",
+ " ice (time, zlev, lat, lon) float64 136GB ...\n",
+ "Attributes: (12/38)\n",
+ " title: NOAA/NCEI 1/4 Degree Daily Optimum Interpolat...\n",
+ " source: ICOADS, NCEP_GTS, GSFC_ICE, NCEP_ICE, Pathfin...\n",
+ " id: oisst-avhrr-v02r01.20260802.nc\n",
+ " naming_authority: gov.noaa.ncei\n",
+ " summary: NOAAs 1/4-degree Daily Optimum Interpolation ...\n",
+ " cdm_data_type: Grid\n",
+ " ... ...\n",
+ " ncei_template_version: NCEI_NetCDF_Grid_Template_v2.0\n",
+ " comment: Data was converted from NetCDF-3 to NetCDF-4 ...\n",
+ " sensor: Thermometer, AVHRR\n",
+ " Conventions: CF-1.6, ACDD-1.3\n",
+ " references: Reynolds, et al.(2007) Daily High-Resolution-...\n",
+ " Description: Reynolds, et al.(2007) Daily High-resolution ... Groups: (0)
Dimensions: time : 16405zlev : 1lat : 720lon : 1440
Coordinates: (4)
lat
(lat)
float32
-89.88 -89.62 ... 89.62 89.88
long_name : Latitude units : degrees_north grids : Uniform grid from -89.875 to 89.875 by 0.25 array([-89.875, -89.625, -89.375, ..., 89.375, 89.625, 89.875],\n",
+ " shape=(720,), dtype=float32) zlev
(zlev)
float32
0.0
long_name : Sea surface height units : meters actual_range : 0, 0 positive : down array([0.], dtype=float32) time
(time)
datetime64[ns]
1981-12-21T12:00:00 ... 2026-08-...
long_name : Center time of the day array(['1981-12-21T12:00:00.000000000', '1981-09-15T12:00:00.000000000',\n",
+ " '1981-09-13T12:00:00.000000000', ..., '2026-07-31T12:00:00.000000000',\n",
+ " '2026-08-01T12:00:00.000000000', '2026-08-02T12:00:00.000000000'],\n",
+ " shape=(16405,), dtype='datetime64[ns]') lon
(lon)
float32
0.125 0.375 0.625 ... 359.6 359.9
long_name : Longitude units : degrees_east grids : Uniform grid from 0.125 to 359.875 by 0.25 array([1.25000e-01, 3.75000e-01, 6.25000e-01, ..., 3.59375e+02, 3.59625e+02,\n",
+ " 3.59875e+02], shape=(1440,), dtype=float32) Data variables: (4)
err
(time, zlev, lat, lon)
float64
...
long_name : Estimated error standard deviation of analysed_sst units : Celsius valid_min : 0 valid_max : 1000 [17008704000 values with dtype=float64] anom
(time, zlev, lat, lon)
float64
...
long_name : Daily sea surface temperature anomalies valid_min : -1200 valid_max : 1200 units : Celsius [17008704000 values with dtype=float64] sst
(time, zlev, lat, lon)
float64
...
long_name : Daily sea surface temperature units : Celsius valid_min : -300 valid_max : 4500 [17008704000 values with dtype=float64] ice
(time, zlev, lat, lon)
float64
...
long_name : Sea ice concentration units : % valid_min : 0 valid_max : 100 [17008704000 values with dtype=float64] Attributes: (38)
title : NOAA/NCEI 1/4 Degree Daily Optimum Interpolation Sea Surface Temperature (OISST) Analysis, Version 2.1 - Final source : ICOADS, NCEP_GTS, GSFC_ICE, NCEP_ICE, Pathfinder_AVHRR, Navy_AVHRR, NOAA_ACSP id : oisst-avhrr-v02r01.20260802.nc naming_authority : gov.noaa.ncei summary : NOAAs 1/4-degree Daily Optimum Interpolation Sea Surface Temperature (OISST) (sometimes referred to as Reynolds SST, which however also refers to earlier products at different resolution), currently available as version v02r01, is created by interpolating and extrapolating SST observations from different sources, resulting in a smoothed complete field. The sources of data are satellite (AVHRR) and in situ platforms (i.e., ships and buoys), and the specific datasets employed may change over time. At the marginal ice zone, sea ice concentrations are used to generate proxy SSTs. A preliminary version of this file is produced in near-real time (1-day latency), and then replaced with a final version after 2 weeks. Note that this is the AVHRR-ONLY DOISST, available from Oct 1981, but there is a companion DOISST product that includes microwave satellite data, available from June 2002 cdm_data_type : Grid history : Final file created using preliminary as first guess, and 3 days of AVHRR data. Preliminary uses only 1 day of AVHRR data. date_modified : 2026-08-17T13:43:00Z date_created : 2026-08-17T13:43:00Z product_version : Version v02r01 processing_level : NOAA Level 4 institution : NOAA/National Centers for Environmental Information creator_url : https://www.ncei.noaa.gov/ creator_email : oisst-help@noaa.gov keywords : Earth Science > Oceans > Ocean Temperature > Sea Surface Temperature keywords_vocabulary : Global Change Master Directory (GCMD) Earth Science Keywords platform : Ships, buoys, Argo floats, MetOp-A, MetOp-B platform_vocabulary : Global Change Master Directory (GCMD) Platform Keywords instrument : Earth Remote Sensing Instruments > Passive Remote Sensing > Spectrometers/Radiometers > Imaging Spectrometers/Radiometers > AVHRR > Advanced Very High Resolution Radiometer instrument_vocabulary : Global Change Master Directory (GCMD) Instrument Keywords standard_name_vocabulary : CF Standard Name Table (v40, 25 January 2017) geospatial_lat_min : -90.0 geospatial_lat_max : 90.0 geospatial_lon_min : 0.0 geospatial_lon_max : 360.0 geospatial_lat_units : degrees_north geospatial_lat_resolution : 0.25 geospatial_lon_units : degrees_east geospatial_lon_resolution : 0.25 time_coverage_start : 2026-08-02T00:00:00Z time_coverage_end : 2026-08-02T23:59:59Z metadata_link : https://doi.org/10.25921/RE9P-PT57 ncei_template_version : NCEI_NetCDF_Grid_Template_v2.0 comment : Data was converted from NetCDF-3 to NetCDF-4 format with metadata updates in November 2017. sensor : Thermometer, AVHRR Conventions : CF-1.6, ACDD-1.3 references : Reynolds, et al.(2007) Daily High-Resolution-Blended Analyses for Sea Surface Temperature (available at https://doi.org/10.1175/2007JCLI1824.1). Banzon, et al.(2016) A long-term record of blended satellite and in situ sea-surface temperature for climate monitoring, modeling and environmental studies (available at https://doi.org/10.5194/essd-8-165-2016). Huang et al. (2020) Improvements of the Daily Optimum Interpolation Sea Surface Temperature (DOISST) Version v02r01, submitted.Climatology is based on 1971-2000 OI.v2 SST. Satellite data: Pathfinder AVHRR SST, Navy AVHRR SST, and NOAA ACSPO SST. Ice data: NCEP Ice and GSFC Ice. Description : Reynolds, et al.(2007) Daily High-resolution Blended Analyses. Available at ftp://eclipse.ncdc.noaa.gov/pub/OI-daily/daily-sst.pdf Climatology is based on 1971-2000 OI.v2 SST, Satellite data: Navy NOAA19 METOP AVHRR, Ice data: NCEP ice
\n",
+ "\n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ "
<xarray.DatasetView> Size: 71GB\n",
+ "Dimensions: (time: 537, zlev: 1, lat: 720, lon: 1440)\n",
+ "Coordinates:\n",
+ " * lat (lat) float32 3kB -89.88 -89.62 -89.38 ... 89.38 89.62 89.88\n",
+ " * time (time) datetime64[ns] 4kB 1981-10-01 1985-03-01 ... 2026-05-01\n",
+ " * zlev (zlev) float32 4B 0.0\n",
+ " * lon (lon) float32 6kB 0.125 0.375 0.625 ... 359.4 359.6 359.9\n",
+ "Data variables: (12/17)\n",
+ " anom_max (time, zlev, lat, lon) float64 4GB ...\n",
+ " anom_mean (time, zlev, lat, lon) float64 4GB ...\n",
+ " anom_min (time, zlev, lat, lon) float64 4GB ...\n",
+ " err_mean (time, zlev, lat, lon) float64 4GB ...\n",
+ " err_std (time, zlev, lat, lon) float64 4GB ...\n",
+ " ice_max (time, zlev, lat, lon) float64 4GB ...\n",
+ " ... ...\n",
+ " sst_min (time, zlev, lat, lon) float64 4GB ...\n",
+ " ice_std (time, zlev, lat, lon) float64 4GB ...\n",
+ " err_min (time, zlev, lat, lon) float64 4GB ...\n",
+ " sst_std (time, zlev, lat, lon) float64 4GB ...\n",
+ " days_missing int64 8B ...\n",
+ " ice_mean (time, zlev, lat, lon) float64 4GB ... Groups: (0)
Dimensions: time : 537zlev : 1lat : 720lon : 1440
Coordinates: (4)
lat
(lat)
float32
-89.88 -89.62 ... 89.62 89.88
long_name : Latitude units : degrees_north grids : Uniform grid from -89.875 to 89.875 by 0.25 array([-89.875, -89.625, -89.375, ..., 89.375, 89.625, 89.875],\n",
+ " shape=(720,), dtype=float32) time
(time)
datetime64[ns]
1981-10-01 ... 2026-05-01
array(['1981-10-01T00:00:00.000000000', '1985-03-01T00:00:00.000000000',\n",
+ " '1985-02-01T00:00:00.000000000', ..., '2026-02-01T00:00:00.000000000',\n",
+ " '2026-03-01T00:00:00.000000000', '2026-05-01T00:00:00.000000000'],\n",
+ " shape=(537,), dtype='datetime64[ns]') zlev
(zlev)
float32
0.0
long_name : Sea surface height units : meters actual_range : 0, 0 positive : down array([0.], dtype=float32) lon
(lon)
float32
0.125 0.375 0.625 ... 359.6 359.9
long_name : Longitude units : degrees_east grids : Uniform grid from 0.125 to 359.875 by 0.25 array([1.25000e-01, 3.75000e-01, 6.25000e-01, ..., 3.59375e+02, 3.59625e+02,\n",
+ " 3.59875e+02], shape=(1440,), dtype=float32) Data variables: (17)
anom_max
(time, zlev, lat, lon)
float64
...
long_name : Daily sea surface temperature anomalies valid_min : -1200 valid_max : 1200 units : Celsius [556761600 values with dtype=float64] anom_mean
(time, zlev, lat, lon)
float64
...
long_name : Daily sea surface temperature anomalies valid_min : -1200 valid_max : 1200 units : Celsius [556761600 values with dtype=float64] anom_min
(time, zlev, lat, lon)
float64
...
long_name : Daily sea surface temperature anomalies valid_min : -1200 valid_max : 1200 units : Celsius [556761600 values with dtype=float64] err_mean
(time, zlev, lat, lon)
float64
...
long_name : Estimated error standard deviation of analysed_sst units : Celsius valid_min : 0 valid_max : 1000 [556761600 values with dtype=float64] err_std
(time, zlev, lat, lon)
float64
...
long_name : Estimated error standard deviation of analysed_sst units : Celsius valid_min : 0 valid_max : 1000 [556761600 values with dtype=float64] ice_max
(time, zlev, lat, lon)
float64
...
long_name : Sea ice concentration units : % valid_min : 0 valid_max : 100 [556761600 values with dtype=float64] anom_std
(time, zlev, lat, lon)
float64
...
long_name : Daily sea surface temperature anomalies valid_min : -1200 valid_max : 1200 units : Celsius [556761600 values with dtype=float64] err_max
(time, zlev, lat, lon)
float64
...
long_name : Estimated error standard deviation of analysed_sst units : Celsius valid_min : 0 valid_max : 1000 [556761600 values with dtype=float64] sst_mean
(time, zlev, lat, lon)
float64
...
long_name : Daily sea surface temperature units : Celsius valid_min : -300 valid_max : 4500 [556761600 values with dtype=float64] ice_min
(time, zlev, lat, lon)
float64
...
long_name : Sea ice concentration units : % valid_min : 0 valid_max : 100 [556761600 values with dtype=float64] sst_max
(time, zlev, lat, lon)
float64
...
long_name : Daily sea surface temperature units : Celsius valid_min : -300 valid_max : 4500 [556761600 values with dtype=float64] sst_min
(time, zlev, lat, lon)
float64
...
long_name : Daily sea surface temperature units : Celsius valid_min : -300 valid_max : 4500 [556761600 values with dtype=float64] ice_std
(time, zlev, lat, lon)
float64
...
long_name : Sea ice concentration units : % valid_min : 0 valid_max : 100 [556761600 values with dtype=float64] err_min
(time, zlev, lat, lon)
float64
...
long_name : Estimated error standard deviation of analysed_sst units : Celsius valid_min : 0 valid_max : 1000 [556761600 values with dtype=float64] sst_std
(time, zlev, lat, lon)
float64
...
long_name : Daily sea surface temperature units : Celsius valid_min : -300 valid_max : 4500 [556761600 values with dtype=float64] days_missing
()
int64
...
description : Number of days missing from the monthly aggregation [1 values with dtype=int64] ice_mean
(time, zlev, lat, lon)
float64
...
long_name : Sea ice concentration units : % valid_min : 0 valid_max : 100 [556761600 values with dtype=float64] Attributes: (0)
Dimensions:
Coordinates: (0)
Inherited coordinates: (0)
Data variables: (0)
Attributes: (0)
"
+ ],
+ "text/plain": [
+ "\n",
+ "Group: /\n",
+ "├── Group: /daily\n",
+ "│ Dimensions: (time: 16405, zlev: 1, lat: 720, lon: 1440)\n",
+ "│ Coordinates:\n",
+ "│ * lat (lat) float32 3kB -89.88 -89.62 -89.38 -89.12 ... 89.38 89.62 89.88\n",
+ "│ * zlev (zlev) float32 4B 0.0\n",
+ "│ * time (time) datetime64[ns] 131kB 1981-12-21T12:00:00 ... 2026-08-02T1...\n",
+ "│ * lon (lon) float32 6kB 0.125 0.375 0.625 0.875 ... 359.4 359.6 359.9\n",
+ "│ Data variables:\n",
+ "│ err (time, zlev, lat, lon) float64 136GB ...\n",
+ "│ anom (time, zlev, lat, lon) float64 136GB ...\n",
+ "│ sst (time, zlev, lat, lon) float64 136GB ...\n",
+ "│ ice (time, zlev, lat, lon) float64 136GB ...\n",
+ "│ Attributes: (12/38)\n",
+ "│ title: NOAA/NCEI 1/4 Degree Daily Optimum Interpolat...\n",
+ "│ source: ICOADS, NCEP_GTS, GSFC_ICE, NCEP_ICE, Pathfin...\n",
+ "│ id: oisst-avhrr-v02r01.20260802.nc\n",
+ "│ naming_authority: gov.noaa.ncei\n",
+ "│ summary: NOAAs 1/4-degree Daily Optimum Interpolation ...\n",
+ "│ cdm_data_type: Grid\n",
+ "│ ... ...\n",
+ "│ ncei_template_version: NCEI_NetCDF_Grid_Template_v2.0\n",
+ "│ comment: Data was converted from NetCDF-3 to NetCDF-4 ...\n",
+ "│ sensor: Thermometer, AVHRR\n",
+ "│ Conventions: CF-1.6, ACDD-1.3\n",
+ "│ references: Reynolds, et al.(2007) Daily High-Resolution-...\n",
+ "│ Description: Reynolds, et al.(2007) Daily High-resolution ...\n",
+ "└── Group: /monthly\n",
+ " Dimensions: (time: 537, zlev: 1, lat: 720, lon: 1440)\n",
+ " Coordinates:\n",
+ " * lat (lat) float32 3kB -89.88 -89.62 -89.38 ... 89.38 89.62 89.88\n",
+ " * time (time) datetime64[ns] 4kB 1981-10-01 1985-03-01 ... 2026-05-01\n",
+ " * zlev (zlev) float32 4B 0.0\n",
+ " * lon (lon) float32 6kB 0.125 0.375 0.625 ... 359.4 359.6 359.9\n",
+ " Data variables: (12/17)\n",
+ " anom_max (time, zlev, lat, lon) float64 4GB ...\n",
+ " anom_mean (time, zlev, lat, lon) float64 4GB ...\n",
+ " anom_min (time, zlev, lat, lon) float64 4GB ...\n",
+ " err_mean (time, zlev, lat, lon) float64 4GB ...\n",
+ " err_std (time, zlev, lat, lon) float64 4GB ...\n",
+ " ice_max (time, zlev, lat, lon) float64 4GB ...\n",
+ " ... ...\n",
+ " sst_min (time, zlev, lat, lon) float64 4GB ...\n",
+ " ice_std (time, zlev, lat, lon) float64 4GB ...\n",
+ " err_min (time, zlev, lat, lon) float64 4GB ...\n",
+ " sst_std (time, zlev, lat, lon) float64 4GB ...\n",
+ " days_missing int64 8B ...\n",
+ " ice_mean (time, zlev, lat, lon) float64 4GB ..."
+ ]
+ },
+ "execution_count": 9,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "oisst_dt = xr.open_datatree(final_session.store, engine=\"zarr\", zarr_version=3)\n",
+ "oisst_dt"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "qnkX",
+ "metadata": {
+ "marimo": {
+ "config": {
+ "hide_code": true
+ },
+ "md_prefix": "r"
+ }
+ },
+ "source": [
+ "We're explictly sorting the time as our aggregation process for both daily and monthly data is non linear, and we'd rather not have miss-ordered dates later."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "id": "mwHO",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Full dataset: 615.54 Gigabytes\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(f\"Full dataset: {oisst_dt.nbytes / 1_000_000_000:,.2f} Gigabytes\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "id": "TqIu",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ "
<xarray.Dataset> Size: 71GB\n",
+ "Dimensions: (time: 537, zlev: 1, lat: 720, lon: 1440)\n",
+ "Coordinates:\n",
+ " * lat (lat) float32 3kB -89.88 -89.62 -89.38 ... 89.38 89.62 89.88\n",
+ " * time (time) datetime64[ns] 4kB 1981-10-01 1981-11-01 ... 2026-06-01\n",
+ " * zlev (zlev) float32 4B 0.0\n",
+ " * lon (lon) float32 6kB 0.125 0.375 0.625 ... 359.4 359.6 359.9\n",
+ "Data variables: (12/17)\n",
+ " anom_max (time, zlev, lat, lon) float64 4GB ...\n",
+ " anom_mean (time, zlev, lat, lon) float64 4GB ...\n",
+ " anom_min (time, zlev, lat, lon) float64 4GB ...\n",
+ " err_mean (time, zlev, lat, lon) float64 4GB ...\n",
+ " err_std (time, zlev, lat, lon) float64 4GB ...\n",
+ " ice_max (time, zlev, lat, lon) float64 4GB ...\n",
+ " ... ...\n",
+ " sst_min (time, zlev, lat, lon) float64 4GB ...\n",
+ " ice_std (time, zlev, lat, lon) float64 4GB ...\n",
+ " err_min (time, zlev, lat, lon) float64 4GB ...\n",
+ " sst_std (time, zlev, lat, lon) float64 4GB ...\n",
+ " days_missing int64 8B ...\n",
+ " ice_mean (time, zlev, lat, lon) float64 4GB ... Dimensions: time : 537zlev : 1lat : 720lon : 1440
Coordinates: (4)
lat
(lat)
float32
-89.88 -89.62 ... 89.62 89.88
long_name : Latitude units : degrees_north grids : Uniform grid from -89.875 to 89.875 by 0.25 array([-89.875, -89.625, -89.375, ..., 89.375, 89.625, 89.875],\n",
+ " shape=(720,), dtype=float32) time
(time)
datetime64[ns]
1981-10-01 ... 2026-06-01
array(['1981-10-01T00:00:00.000000000', '1981-11-01T00:00:00.000000000',\n",
+ " '1981-12-01T00:00:00.000000000', ..., '2026-04-01T00:00:00.000000000',\n",
+ " '2026-05-01T00:00:00.000000000', '2026-06-01T00:00:00.000000000'],\n",
+ " shape=(537,), dtype='datetime64[ns]') zlev
(zlev)
float32
0.0
long_name : Sea surface height units : meters actual_range : 0, 0 positive : down array([0.], dtype=float32) lon
(lon)
float32
0.125 0.375 0.625 ... 359.6 359.9
long_name : Longitude units : degrees_east grids : Uniform grid from 0.125 to 359.875 by 0.25 array([1.25000e-01, 3.75000e-01, 6.25000e-01, ..., 3.59375e+02, 3.59625e+02,\n",
+ " 3.59875e+02], shape=(1440,), dtype=float32) Data variables: (17)
anom_max
(time, zlev, lat, lon)
float64
...
long_name : Daily sea surface temperature anomalies valid_min : -1200 valid_max : 1200 units : Celsius [556761600 values with dtype=float64] anom_mean
(time, zlev, lat, lon)
float64
...
long_name : Daily sea surface temperature anomalies valid_min : -1200 valid_max : 1200 units : Celsius [556761600 values with dtype=float64] anom_min
(time, zlev, lat, lon)
float64
...
long_name : Daily sea surface temperature anomalies valid_min : -1200 valid_max : 1200 units : Celsius [556761600 values with dtype=float64] err_mean
(time, zlev, lat, lon)
float64
...
long_name : Estimated error standard deviation of analysed_sst units : Celsius valid_min : 0 valid_max : 1000 [556761600 values with dtype=float64] err_std
(time, zlev, lat, lon)
float64
...
long_name : Estimated error standard deviation of analysed_sst units : Celsius valid_min : 0 valid_max : 1000 [556761600 values with dtype=float64] ice_max
(time, zlev, lat, lon)
float64
...
long_name : Sea ice concentration units : % valid_min : 0 valid_max : 100 [556761600 values with dtype=float64] anom_std
(time, zlev, lat, lon)
float64
...
long_name : Daily sea surface temperature anomalies valid_min : -1200 valid_max : 1200 units : Celsius [556761600 values with dtype=float64] err_max
(time, zlev, lat, lon)
float64
...
long_name : Estimated error standard deviation of analysed_sst units : Celsius valid_min : 0 valid_max : 1000 [556761600 values with dtype=float64] sst_mean
(time, zlev, lat, lon)
float64
...
long_name : Daily sea surface temperature units : Celsius valid_min : -300 valid_max : 4500 [556761600 values with dtype=float64] ice_min
(time, zlev, lat, lon)
float64
...
long_name : Sea ice concentration units : % valid_min : 0 valid_max : 100 [556761600 values with dtype=float64] sst_max
(time, zlev, lat, lon)
float64
...
long_name : Daily sea surface temperature units : Celsius valid_min : -300 valid_max : 4500 [556761600 values with dtype=float64] sst_min
(time, zlev, lat, lon)
float64
...
long_name : Daily sea surface temperature units : Celsius valid_min : -300 valid_max : 4500 [556761600 values with dtype=float64] ice_std
(time, zlev, lat, lon)
float64
...
long_name : Sea ice concentration units : % valid_min : 0 valid_max : 100 [556761600 values with dtype=float64] err_min
(time, zlev, lat, lon)
float64
...
long_name : Estimated error standard deviation of analysed_sst units : Celsius valid_min : 0 valid_max : 1000 [556761600 values with dtype=float64] sst_std
(time, zlev, lat, lon)
float64
...
long_name : Daily sea surface temperature units : Celsius valid_min : -300 valid_max : 4500 [556761600 values with dtype=float64] days_missing
()
int64
...
description : Number of days missing from the monthly aggregation [1 values with dtype=int64] ice_mean
(time, zlev, lat, lon)
float64
...
long_name : Sea ice concentration units : % valid_min : 0 valid_max : 100 [556761600 values with dtype=float64] Indexes: (4)
PandasIndex
PandasIndex(Index([-89.875, -89.625, -89.375, -89.125, -88.875, -88.625, -88.375, -88.125,\n",
+ " -87.875, -87.625,\n",
+ " ...\n",
+ " 87.625, 87.875, 88.125, 88.375, 88.625, 88.875, 89.125, 89.375,\n",
+ " 89.625, 89.875],\n",
+ " dtype='float32', name='lat', length=720)) PandasIndex
PandasIndex(DatetimeIndex(['1981-10-01', '1981-11-01', '1981-12-01', '1982-01-01',\n",
+ " '1982-02-01', '1982-03-01', '1982-04-01', '1982-05-01',\n",
+ " '1982-06-01', '1982-07-01',\n",
+ " ...\n",
+ " '2025-09-01', '2025-10-01', '2025-11-01', '2025-12-01',\n",
+ " '2026-01-01', '2026-02-01', '2026-03-01', '2026-04-01',\n",
+ " '2026-05-01', '2026-06-01'],\n",
+ " dtype='datetime64[ns]', name='time', length=537, freq=None)) PandasIndex
PandasIndex(Index([0.0], dtype='float32', name='zlev')) PandasIndex
PandasIndex(Index([ 0.125, 0.375, 0.625, 0.875, 1.125, 1.375, 1.625, 1.875,\n",
+ " 2.125, 2.375,\n",
+ " ...\n",
+ " 357.625, 357.875, 358.125, 358.375, 358.625, 358.875, 359.125, 359.375,\n",
+ " 359.625, 359.875],\n",
+ " dtype='float32', name='lon', length=1440)) Attributes: (0)
"
+ ],
+ "text/plain": [
+ " Size: 71GB\n",
+ "Dimensions: (time: 537, zlev: 1, lat: 720, lon: 1440)\n",
+ "Coordinates:\n",
+ " * lat (lat) float32 3kB -89.88 -89.62 -89.38 ... 89.38 89.62 89.88\n",
+ " * time (time) datetime64[ns] 4kB 1981-10-01 1981-11-01 ... 2026-06-01\n",
+ " * zlev (zlev) float32 4B 0.0\n",
+ " * lon (lon) float32 6kB 0.125 0.375 0.625 ... 359.4 359.6 359.9\n",
+ "Data variables: (12/17)\n",
+ " anom_max (time, zlev, lat, lon) float64 4GB ...\n",
+ " anom_mean (time, zlev, lat, lon) float64 4GB ...\n",
+ " anom_min (time, zlev, lat, lon) float64 4GB ...\n",
+ " err_mean (time, zlev, lat, lon) float64 4GB ...\n",
+ " err_std (time, zlev, lat, lon) float64 4GB ...\n",
+ " ice_max (time, zlev, lat, lon) float64 4GB ...\n",
+ " ... ...\n",
+ " sst_min (time, zlev, lat, lon) float64 4GB ...\n",
+ " ice_std (time, zlev, lat, lon) float64 4GB ...\n",
+ " err_min (time, zlev, lat, lon) float64 4GB ...\n",
+ " sst_std (time, zlev, lat, lon) float64 4GB ...\n",
+ " days_missing int64 8B ...\n",
+ " ice_mean (time, zlev, lat, lon) float64 4GB ..."
+ ]
+ },
+ "execution_count": 11,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "monthly_ds = oisst_dt[\"monthly\"].to_dataset()\n",
+ "# Or directly with\n",
+ "# monthly_ds = xr.open_zarr(final_session.store, group=\"monthly\", zarr_version=3)\n",
+ "monthly_ds = monthly_ds.sortby(\"time\")\n",
+ "monthly_ds"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "id": "ADRs",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Monthly: 71.27 bytes\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(f\"Monthly: {monthly_ds.nbytes / 1_000_000_000:,.2f} bytes\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "Vxnm",
+ "metadata": {
+ "marimo": {
+ "config": {
+ "hide_code": true
+ },
+ "md_prefix": "r"
+ }
+ },
+ "source": [
+ "Most of these samples were collected on shore, so we need to find the nearest 'wet' OISST cell. This also handles when our point longitudes are in -180 to 180 vs OISST in 0-360."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "id": "DnEU",
+ "metadata": {
+ "marimo": {
+ "config": {
+ "hide_code": true
+ }
+ },
+ "tags": [
+ "remove-input"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "def sel_ocean_points(da, pts, window=1.0):\n",
+ " \"\"\"Pointwise-select `da` at each (lat, lon) in `pts`, snapping to ocean cells.\n",
+ "\n",
+ " Handles two gotchas of point extraction from OISST-style grids:\n",
+ "\n",
+ " - **Longitude convention** - converts the points' -180..180 longitudes to\n",
+ " 0..360 when that's what the grid uses (detected from `da.lon.max()`).\n",
+ " - **Land cells** - a point whose nearest cell is all-null (land) is snapped\n",
+ " to the nearest non-null (ocean) cell within +/-`window` degrees, using\n",
+ " the first timestep's null mask as the land/ocean mask.\n",
+ "\n",
+ " Returns a DataArray with a `points` dimension (labels from `pts`), the\n",
+ " chosen cell's lat/lon as coordinates, and a boolean `snapped` coordinate\n",
+ " marking points that were moved off land.\n",
+ " \"\"\"\n",
+ " lon_max = float(da.lon.max())\n",
+ " lons = pts[\"lon\"] % 360 if lon_max > 180 else pts[\"lon\"]\n",
+ " ocean = da.isel(time=0).notnull().drop_vars(\"time\", errors=\"ignore\")\n",
+ "\n",
+ " series = []\n",
+ " snapped = []\n",
+ " for i in range(pts.sizes[\"points\"]):\n",
+ " t_lat, t_lon = float(pts[\"lat\"][i]), float(lons[i])\n",
+ " cell = da.sel(lat=t_lat, lon=t_lon, method=\"nearest\")\n",
+ " if bool(cell.isnull().all()):\n",
+ " win = da.sel(\n",
+ " lat=slice(t_lat - window, t_lat + window),\n",
+ " lon=slice(t_lon - window, t_lon + window),\n",
+ " )\n",
+ " dist = (win.lat - t_lat) ** 2 + (win.lon - t_lon) ** 2\n",
+ " idx = dist.where(ocean.sel(lat=win.lat, lon=win.lon)).argmin(\n",
+ " dim=(\"lat\", \"lon\")\n",
+ " )\n",
+ " cell = win.isel(idx)\n",
+ " snapped.append(True)\n",
+ " else:\n",
+ " snapped.append(False)\n",
+ " series.append(cell)\n",
+ "\n",
+ " out = xr.concat(series, dim=pts[\"points\"])\n",
+ " return out.assign_coords(snapped=(\"points\", snapped))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "ulZA",
+ "metadata": {
+ "marimo": {
+ "config": {
+ "hide_code": true
+ },
+ "md_prefix": "r"
+ }
+ },
+ "source": [
+ "Now that function helps us [pointwise index](https://docs.xarray.dev/en/latest/user-guide/indexing.html#more-advanced-indexing) into the OISST data. Now points and time become the dimensions of the dataset (instead of time/lat/lon), and there is a point for each one that we entered.\n",
+ "\n",
+ "_Note: This cell is disabled as it takes about 40 minutes to run with a good connection, and about 15 GB of data. Click the 3 dots on the cell and re-enable it in the menu to run it and the following cells._"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "id": "ecfG",
+ "metadata": {
+ "marimo": {
+ "config": {
+ "disabled": true
+ }
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ "
<xarray.DataArray 'sst_mean' (points: 85, time: 537)> Size: 365kB\n",
+ "array([[11.55580619, 8.94466647, 7.06419339, ..., 5.46333321,\n",
+ " 9.10967722, 13.75933303],\n",
+ " [11.55580619, 8.94466647, 7.06419339, ..., 5.46333321,\n",
+ " 9.10967722, 13.75933303],\n",
+ " [11.55580619, 8.94466647, 7.06419339, ..., 5.46333321,\n",
+ " 9.10967722, 13.75933303],\n",
+ " ...,\n",
+ " [11.52354813, 8.9799998 , 7.190645 , ..., 5.31199988,\n",
+ " 8.86225787, 13.3989997 ],\n",
+ " [11.52354813, 8.9799998 , 7.190645 , ..., 5.31199988,\n",
+ " 8.86225787, 13.3989997 ],\n",
+ " [11.52354813, 8.9799998 , 7.190645 , ..., 5.31199988,\n",
+ " 8.86225787, 13.3989997 ]], shape=(85, 537))\n",
+ "Coordinates:\n",
+ " lat (points) float32 340B 43.62 43.62 43.62 43.62 ... 43.62 43.62 43.62\n",
+ " * time (time) datetime64[ns] 4kB 1981-10-01 1981-11-01 ... 2026-06-01\n",
+ " lon (points) float32 340B 289.9 289.9 289.9 289.9 ... 290.1 290.1 290.1\n",
+ " * points (points) object 680B 'Little John-NW' ... 'Stovers Point'\n",
+ " snapped (points) bool 85B True True True False ... True True False True\n",
+ "Attributes:\n",
+ " long_name: Daily sea surface temperature\n",
+ " units: Celsius\n",
+ " valid_min: -300\n",
+ " valid_max: 4500 11.56 8.945 7.064 5.027 3.955 3.787 ... 3.2 2.895 5.312 8.862 13.4
array([[11.55580619, 8.94466647, 7.06419339, ..., 5.46333321,\n",
+ " 9.10967722, 13.75933303],\n",
+ " [11.55580619, 8.94466647, 7.06419339, ..., 5.46333321,\n",
+ " 9.10967722, 13.75933303],\n",
+ " [11.55580619, 8.94466647, 7.06419339, ..., 5.46333321,\n",
+ " 9.10967722, 13.75933303],\n",
+ " ...,\n",
+ " [11.52354813, 8.9799998 , 7.190645 , ..., 5.31199988,\n",
+ " 8.86225787, 13.3989997 ],\n",
+ " [11.52354813, 8.9799998 , 7.190645 , ..., 5.31199988,\n",
+ " 8.86225787, 13.3989997 ],\n",
+ " [11.52354813, 8.9799998 , 7.190645 , ..., 5.31199988,\n",
+ " 8.86225787, 13.3989997 ]], shape=(85, 537)) Coordinates: (5)
lat
(points)
float32
43.62 43.62 43.62 ... 43.62 43.62
long_name : Latitude units : degrees_north grids : Uniform grid from -89.875 to 89.875 by 0.25 array([43.625, 43.625, 43.625, 43.625, 43.625, 43.625, 43.625, 43.625,\n",
+ " 43.625, 43.625, 43.625, 43.875, 43.375, 43.625, 43.625, 43.125,\n",
+ " 43.375, 43.375, 43.375, 43.625, 43.625, 43.625, 43.875, 43.875,\n",
+ " 44.375, 44.375, 44.125, 44.125, 44.375, 44.375, 44.375, 44.375,\n",
+ " 44.625, 44.625, 44.625, 44.625, 44.875, 44.875, 44.875, 43.625,\n",
+ " 43.625, 43.875, 43.625, 43.625, 43.625, 43.625, 43.625, 44.125,\n",
+ " 44.125, 44.125, 44.125, 44.625, 44.625, 44.625, 44.625, 43.875,\n",
+ " 43.875, 44.375, 43.875, 44.375, 89.875, 44.375, 43.625, 43.625,\n",
+ " 43.625, 43.625, 43.625, 43.625, 43.625, 43.625, 43.625, 43.625,\n",
+ " 43.625, 43.625, 43.625, 43.625, 43.625, 43.625, 43.625, 43.625,\n",
+ " 43.625, 43.625, 43.625, 43.625, 43.625], dtype=float32) time
(time)
datetime64[ns]
1981-10-01 ... 2026-06-01
array(['1981-10-01T00:00:00.000000000', '1981-11-01T00:00:00.000000000',\n",
+ " '1981-12-01T00:00:00.000000000', ..., '2026-04-01T00:00:00.000000000',\n",
+ " '2026-05-01T00:00:00.000000000', '2026-06-01T00:00:00.000000000'],\n",
+ " shape=(537,), dtype='datetime64[ns]') lon
(points)
float32
289.9 289.9 289.9 ... 290.1 290.1
long_name : Longitude units : degrees_east grids : Uniform grid from 0.125 to 359.875 by 0.25 array([289.875, 289.875, 289.875, 289.875, 289.875, 289.875, 289.875,\n",
+ " 290.125, 290.125, 290.125, 289.875, 290.375, 289.625, 289.875,\n",
+ " 289.875, 289.375, 289.625, 289.625, 289.625, 290.125, 290.125,\n",
+ " 290.125, 290.625, 290.625, 291.125, 291.125, 291.375, 291.375,\n",
+ " 291.875, 291.875, 292.375, 292.375, 292.625, 292.625, 292.875,\n",
+ " 292.875, 293.125, 293.125, 293.125, 290.125, 290.125, 290.375,\n",
+ " 289.875, 290.125, 290.125, 290.125, 290.125, 291.375, 291.375,\n",
+ " 291.375, 291.375, 292.625, 292.625, 292.625, 292.625, 290.375,\n",
+ " 290.375, 291.875, 290.625, 291.875, 359.875, 291.875, 290.125,\n",
+ " 289.875, 289.875, 289.875, 289.875, 289.875, 289.875, 289.875,\n",
+ " 289.875, 289.875, 289.875, 289.875, 289.875, 289.875, 289.875,\n",
+ " 289.875, 290.125, 290.125, 290.125, 290.125, 290.125, 290.125,\n",
+ " 290.125], dtype=float32) points
(points)
object
'Little John-NW' ... 'Stovers Po...
array(['Little John-NW', 'Little John-NE', 'Little John-SE', 'Mackworth- SE',\n",
+ " 'Mackworth- SW', 'Mackworth- NW', 'Maquoit', 'Middle Bay',\n",
+ " 'Middle Bay ', 'Harpswell Cove', 'Princes Point', 'Thomas Point Beach',\n",
+ " 'Biddeford', 'Fore River- Scour Pool', 'Fore River- Marsh Pool',\n",
+ " 'Dolphin Lane', 'Upper Landing', 'Jones Creek ', 'WinnockÊNeck',\n",
+ " 'Thomas Point ', 'Atkins Flat ', 'Branch Flat', 'SamÕs Cove',\n",
+ " 'Broad Cove', 'Little Broad Cove', 'Ryder Cove', 'Hatch Cove ',\n",
+ " 'Sunshine Bar ', 'Raccoon Cove', 'Hog Bay', 'DobbinsÕ Island',\n",
+ " 'Perio Point ', 'Sanborn Cove', 'Randall Point', 'Burnt Cove ',\n",
+ " 'Hallowell Island ', 'Marion Cove', 'Gleason Cove', 'HalfMoonÊCove',\n",
+ " 'Penneville', 'Simpsons Point', 'Thomas Point', "Prince's Point",\n",
+ " 'Atkins Flat (Fab)', 'Atkins Flat (Mes)', 'Branch Flat (mes)',\n",
+ " 'Branch Flat (fab)', 'Hatch Cove (fab)', 'Hatch Cove (Mesh)',\n",
+ " 'Sunshine Bar (fab)', 'Sunshine Bar (mesh)', 'Sanborn Cove (fab)',\n",
+ " 'Sanborn Cove (mes)', 'Randall Point (fab)', 'Randall Point (mes)',\n",
+ " 'Cushman Cove ', 'Maine Yankee', 'Bunker Harbor', 'Hog Island',\n",
+ " 'Grand Marsh Bay', nan, 'Jones Cove', 'Widgeon Cove',\n",
+ " 'Mackworth Island', 'Cousins Island', 'Little Chebeague Island',\n",
+ " 'Mackworth Island - North', 'Mussel Cove', 'Mackworth Island - Beach',\n",
+ " 'Cushing Island', 'Audubon', 'Back Cove', 'SMCC',\n",
+ " 'The Brothers - North', 'Alewife Cove', 'Skitterygusset',\n",
+ " 'Great Diamond Island', 'Presumpscot Moorings', 'Snow Island',\n",
+ " 'Long Point Cove', 'Lowell Cove', 'Garrison Cove', 'Orrs Cove',\n",
+ " 'Cedar Beach', 'Stovers Point'], dtype=object) snapped
(points)
bool
True True True ... True False True
array([ True, True, True, False, False, False, True, True, True,\n",
+ " True, True, True, False, True, True, True, True, True,\n",
+ " True, True, True, False, False, True, False, False, False,\n",
+ " False, True, True, True, True, False, False, True, True,\n",
+ " True, True, True, True, True, True, True, True, True,\n",
+ " False, False, False, False, False, False, False, False, False,\n",
+ " False, False, False, True, False, False, False, False, True,\n",
+ " False, True, False, False, False, False, False, False, True,\n",
+ " False, False, False, False, False, True, True, True, True,\n",
+ " True, True, False, True]) Indexes: (2)
PandasIndex
PandasIndex(DatetimeIndex(['1981-10-01', '1981-11-01', '1981-12-01', '1982-01-01',\n",
+ " '1982-02-01', '1982-03-01', '1982-04-01', '1982-05-01',\n",
+ " '1982-06-01', '1982-07-01',\n",
+ " ...\n",
+ " '2025-09-01', '2025-10-01', '2025-11-01', '2025-12-01',\n",
+ " '2026-01-01', '2026-02-01', '2026-03-01', '2026-04-01',\n",
+ " '2026-05-01', '2026-06-01'],\n",
+ " dtype='datetime64[ns]', name='time', length=537, freq=None)) PandasIndex
PandasIndex(Index(['Little John-NW', 'Little John-NE', 'Little John-SE', 'Mackworth- SE',\n",
+ " 'Mackworth- SW', 'Mackworth- NW', 'Maquoit', 'Middle Bay',\n",
+ " 'Middle Bay ', 'Harpswell Cove', 'Princes Point', 'Thomas Point Beach',\n",
+ " 'Biddeford', 'Fore River- Scour Pool', 'Fore River- Marsh Pool',\n",
+ " 'Dolphin Lane', 'Upper Landing', 'Jones Creek ', 'WinnockÊNeck',\n",
+ " 'Thomas Point ', 'Atkins Flat ', 'Branch Flat', 'SamÕs Cove',\n",
+ " 'Broad Cove', 'Little Broad Cove', 'Ryder Cove', 'Hatch Cove ',\n",
+ " 'Sunshine Bar ', 'Raccoon Cove', 'Hog Bay', 'DobbinsÕ Island',\n",
+ " 'Perio Point ', 'Sanborn Cove', 'Randall Point', 'Burnt Cove ',\n",
+ " 'Hallowell Island ', 'Marion Cove', 'Gleason Cove', 'HalfMoonÊCove',\n",
+ " 'Penneville', 'Simpsons Point', 'Thomas Point', 'Prince's Point',\n",
+ " 'Atkins Flat (Fab)', 'Atkins Flat (Mes)', 'Branch Flat (mes)',\n",
+ " 'Branch Flat (fab)', 'Hatch Cove (fab)', 'Hatch Cove (Mesh)',\n",
+ " 'Sunshine Bar (fab)', 'Sunshine Bar (mesh)', 'Sanborn Cove (fab)',\n",
+ " 'Sanborn Cove (mes)', 'Randall Point (fab)', 'Randall Point (mes)',\n",
+ " 'Cushman Cove ', 'Maine Yankee', 'Bunker Harbor', 'Hog Island',\n",
+ " 'Grand Marsh Bay', nan, 'Jones Cove', 'Widgeon Cove',\n",
+ " 'Mackworth Island', 'Cousins Island', 'Little Chebeague Island',\n",
+ " 'Mackworth Island - North', 'Mussel Cove', 'Mackworth Island - Beach',\n",
+ " 'Cushing Island', 'Audubon', 'Back Cove', 'SMCC',\n",
+ " 'The Brothers - North', 'Alewife Cove', 'Skitterygusset',\n",
+ " 'Great Diamond Island', 'Presumpscot Moorings', 'Snow Island',\n",
+ " 'Long Point Cove', 'Lowell Cove', 'Garrison Cove', 'Orrs Cove',\n",
+ " 'Cedar Beach', 'Stovers Point'],\n",
+ " dtype='str', name='points')) Attributes: (4)
long_name : Daily sea surface temperature units : Celsius valid_min : -300 valid_max : 4500 "
+ ],
+ "text/plain": [
+ " Size: 365kB\n",
+ "array([[11.55580619, 8.94466647, 7.06419339, ..., 5.46333321,\n",
+ " 9.10967722, 13.75933303],\n",
+ " [11.55580619, 8.94466647, 7.06419339, ..., 5.46333321,\n",
+ " 9.10967722, 13.75933303],\n",
+ " [11.55580619, 8.94466647, 7.06419339, ..., 5.46333321,\n",
+ " 9.10967722, 13.75933303],\n",
+ " ...,\n",
+ " [11.52354813, 8.9799998 , 7.190645 , ..., 5.31199988,\n",
+ " 8.86225787, 13.3989997 ],\n",
+ " [11.52354813, 8.9799998 , 7.190645 , ..., 5.31199988,\n",
+ " 8.86225787, 13.3989997 ],\n",
+ " [11.52354813, 8.9799998 , 7.190645 , ..., 5.31199988,\n",
+ " 8.86225787, 13.3989997 ]], shape=(85, 537))\n",
+ "Coordinates:\n",
+ " lat (points) float32 340B 43.62 43.62 43.62 43.62 ... 43.62 43.62 43.62\n",
+ " * time (time) datetime64[ns] 4kB 1981-10-01 1981-11-01 ... 2026-06-01\n",
+ " lon (points) float32 340B 289.9 289.9 289.9 289.9 ... 290.1 290.1 290.1\n",
+ " * points (points) object 680B 'Little John-NW' ... 'Stovers Point'\n",
+ " snapped (points) bool 85B True True True False ... True True False True\n",
+ "Attributes:\n",
+ " long_name: Daily sea surface temperature\n",
+ " units: Celsius\n",
+ " valid_min: -300\n",
+ " valid_max: 4500"
+ ]
+ },
+ "execution_count": 14,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "points_sst = sel_ocean_points(\n",
+ " monthly_ds[\"sst_mean\"].squeeze(drop=True), points_ds\n",
+ ")\n",
+ "points_sst"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "Pvdt",
+ "metadata": {
+ "marimo": {
+ "config": {
+ "hide_code": true
+ },
+ "md_prefix": "r"
+ }
+ },
+ "source": [
+ "We save it to NetCDF which keeps the dimensions and metadata, and CSV which can be easier to access."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "id": "ZBYS",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "points_sst.to_netcdf(\"oisst_points_sst_mean.nc\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "id": "aLJB",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "points_sst.to_dataframe().to_csv(\"oisst_points_sst_mean.csv\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "nHfw",
+ "metadata": {},
+ "source": [
+ "points_sst_nc = xr.open_dataset(\"~/Downloads/oisst_points_sst_mean.nc\")\n",
+ "points_sst_nc"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python (Pixi)",
+ "language": "python",
+ "name": "pixi-kernel-python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.14.6"
+ },
+ "marimo": {
+ "app_config": {
+ "auto_download": [
+ "ipynb"
+ ],
+ "width": "medium"
+ },
+ "header": "# /// script\n# dependencies = [\n# \"geopandas==1.1.4\",\n# \"icechunk==2.1.1\",\n# \"marimo\",\n# \"matplotlib==3.11.1\",\n# \"netcdf4==1.7.4\",\n# \"numpy==2.5.1\",\n# \"obspec-utils==0.9.0\",\n# \"pandas==3.0.5\",\n# \"pydantic==2.13.4\",\n# \"ruff==0.16.0\",\n# \"xarray==2026.7.0\",\n# ]\n# requires-python = \">=3.14\"\n# ///\n\n",
+ "marimo_version": "0.24.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/01-Tue/xarray/oisst_points_sst_mean.nc b/01-Tue/xarray/oisst_points_sst_mean.nc
new file mode 100644
index 00000000..ad998693
Binary files /dev/null and b/01-Tue/xarray/oisst_points_sst_mean.nc differ
diff --git a/01-Tue/xarray/oisst_read.ipynb b/01-Tue/xarray/oisst_read.ipynb
new file mode 100644
index 00000000..ca334897
--- /dev/null
+++ b/01-Tue/xarray/oisst_read.ipynb
@@ -0,0 +1,1292 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "Hbol",
+ "metadata": {
+ "marimo": {
+ "config": {
+ "hide_code": true
+ },
+ "md_prefix": "r"
+ }
+ },
+ "source": [
+ "# Accessing OISST data with Icechunk"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "MJUe",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import re\n",
+ "from getpass import getpass\n",
+ "\n",
+ "import marimo as mo\n",
+ "import icechunk as ic\n",
+ "import numpy as np\n",
+ "import xarray as xr\n",
+ "from pydantic import BaseModel, Field"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "vblA",
+ "metadata": {
+ "marimo": {
+ "config": {
+ "hide_code": true
+ },
+ "md_prefix": "r"
+ }
+ },
+ "source": [
+ "## S3 credentials\n",
+ "\n",
+ "While the daily data is in a regular public bucket (that is paid for through the open data program), the metadata for fast access, and the monthly data are in a requester-pays bucket. NERACOOS pays for the storage costs, but the users pay for the data transfer."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "bkHC",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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"
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "access_key = mo.ui.text(label=\"Enter S3 access key ID: \", kind=\"password\")\n",
+ "secret_key = mo.ui.text(label=\"Enter S3 secret access key: \", kind=\"password\")\n",
+ "mo.hstack([access_key, secret_key])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "lEQa",
+ "metadata": {
+ "jupyter": {
+ "source_hidden": true
+ },
+ "marimo": {
+ "config": {
+ "hide_code": true
+ }
+ },
+ "tags": [
+ "remove-input"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "class S3Credentials(BaseModel):\n",
+ " \"\"\"S3 credentials\"\"\"\n",
+ "\n",
+ " access_key_id: str\n",
+ " secret_access_key: str\n",
+ "\n",
+ "s3_credentials = S3Credentials(\n",
+ " access_key_id=access_key.value,\n",
+ " secret_access_key=secret_key.value,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "PKri",
+ "metadata": {
+ "marimo": {
+ "config": {
+ "hide_code": true
+ },
+ "md_prefix": "r"
+ }
+ },
+ "source": [
+ "The data is split between two repo, a preliminary one with the last ~35 days of data, and the full store."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "Xref",
+ "metadata": {
+ "jupyter": {
+ "source_hidden": true
+ },
+ "marimo": {
+ "config": {
+ "hide_code": true
+ }
+ },
+ "tags": [
+ "remove-input"
+ ]
+ },
+ "outputs": [
+ {
+ "ename": "ValueError",
+ "evalue": "Conflicting arguments to s3_credentials function",
+ "output_type": "error",
+ "traceback": [
+ "Traceback (most recent call last):",
+ " File \"\", line 96, in ",
+ " prelim_repo = open_repo(",
+ " oisst_storage.preliminary_prefix, oisst_storage, s3_credentials",
+ " )",
+ " File \"\", line 74, in open_repo",
+ " storage_for(prefix, oisst_storage, s3_credentials),",
+ " ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^",
+ " File \"\", line 30, in storage_for",
+ " return ic.s3_storage(",
+ " ~~~~~~~~~~~~~^",
+ " bucket=oisst_storage.bucket,",
+ " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^",
+ " ...<4 lines>...",
+ " requester_pays=True,",
+ " ^^^^^^^^^^^^^^^^^^^^",
+ " )",
+ " ^",
+ " File \"/var/folders/n6/hl07fcc55ys7mbybt01gb8r80000gn/T/marimo-sandbox-ppvvg8ob/venv/lib/python3.14/site-packages/icechunk/storage.py\", line 272, in s3_storage",
+ " credentials = s3_credentials(",
+ " access_key_id=access_key_id,",
+ " ...<6 lines>...",
+ " scatter_initial_credentials=scatter_initial_credentials,",
+ " )",
+ " File \"/var/folders/n6/hl07fcc55ys7mbybt01gb8r80000gn/T/marimo-sandbox-ppvvg8ob/venv/lib/python3.14/site-packages/icechunk/credentials.py\", line 256, in s3_credentials",
+ " raise ValueError(\"Conflicting arguments to s3_credentials function\")",
+ "ValueError: Conflicting arguments to s3_credentials function",
+ ""
+ ]
+ }
+ ],
+ "source": [
+ "BUCKET = \"noaa-cdr-sea-surface-temp-optimum-interpolation-pds\"\n",
+ "URL_PREFIX = f\"s3://{BUCKET}/\"\n",
+ "DATA_PREFIX = \"data/v2.1/avhrr\"\n",
+ "REGION = \"us-east-1\"\n",
+ "\n",
+ "# obstore / icechunk want the prefix without a trailing slash for the registry\n",
+ "# key, while icechunk's VirtualChunkContainer matches against the trailing-slash\n",
+ "# form (mirroring services/xreds dataset_spec.py).\n",
+ "STORE_PREFIX = URL_PREFIX.rstrip(\"/\")\n",
+ "\n",
+ "_FINAL_RE = re.compile(r\"^oisst-avhrr-v02r01\\.(\\d{8})\\.nc$\")\n",
+ "_PRELIM_RE = re.compile(r\"^oisst-avhrr-v02r01\\.(\\d{8})_preliminary\\.nc$\")\n",
+ "\n",
+ "class OisstStorageConfig(BaseModel):\n",
+ " \"\"\"Destination bucket/prefix for the OISST icechunk stores.\"\"\"\n",
+ "\n",
+ " bucket: str = \"neracoos-data-requester-pays\"\n",
+ " final_prefix: str = \"oisst/final\"\n",
+ " preliminary_prefix: str = \"oisst/preliminary\"\n",
+ " region_name: str = \"us-east-1\"\n",
+ "\n",
+ "oisst_storage = OisstStorageConfig()\n",
+ "\n",
+ "def storage_for(\n",
+ " prefix: str,\n",
+ " oisst_storage: OisstStorageConfig,\n",
+ " s3_credentials: S3Credentials,\n",
+ ") -> ic.Storage:\n",
+ " \"\"\"Build writable icechunk S3 storage for a store prefix using real credentials.\"\"\"\n",
+ " return ic.s3_storage(\n",
+ " bucket=oisst_storage.bucket,\n",
+ " prefix=prefix,\n",
+ " region=oisst_storage.region_name,\n",
+ " access_key_id=s3_credentials.access_key_id,\n",
+ " secret_access_key=s3_credentials.secret_access_key,\n",
+ " requester_pays=True,\n",
+ " )\n",
+ "\n",
+ "def build_virtual_chunk_container_config() -> icechunk.RepositoryConfig:\n",
+ " \"\"\"Build a ``RepositoryConfig`` registering the anonymous NOAA S3 bucket as\n",
+ " a virtual chunk container.\n",
+ "\n",
+ " This is the write-side mirror of the read-side config in\n",
+ " ``services/xreds/xreds/dataset_spec.py`` so the stores we write stay\n",
+ " readable by ``VirtualIcechunkDatasetSpec``.\n",
+ " \"\"\"\n",
+ " config = ic.RepositoryConfig.default()\n",
+ " config.set_virtual_chunk_container(\n",
+ " ic.VirtualChunkContainer(\n",
+ " url_prefix=URL_PREFIX,\n",
+ " store=ic.s3_store(region=REGION, anonymous=True),\n",
+ " ),\n",
+ " )\n",
+ " return config\n",
+ "\n",
+ "def virtual_chunk_credentials() -> dict:\n",
+ " \"\"\"Anonymous credentials authorizing access to the NOAA virtual chunks.\n",
+ "\n",
+ " Passed as ``authorize_virtual_chunk_access`` to ``Repository.create`` /\n",
+ " ``Repository.open_or_create``.\n",
+ " \"\"\"\n",
+ " return ic.containers_credentials(\n",
+ " {URL_PREFIX: ic.s3_anonymous_credentials()}\n",
+ " )\n",
+ "\n",
+ "def open_repo(\n",
+ " prefix: str,\n",
+ " oisst_storage: OisstStorageConfig,\n",
+ " s3_credentials: S3Credentials,\n",
+ ") -> ic.Repository:\n",
+ " \"\"\"Open (or create on first run) the icechunk repo for a store prefix, with\n",
+ " the NOAA virtual chunk container registered and authorized.\"\"\"\n",
+ " return ic.Repository.open(\n",
+ " storage_for(prefix, oisst_storage, s3_credentials),\n",
+ " config=build_virtual_chunk_container_config(),\n",
+ " authorize_virtual_chunk_access=virtual_chunk_credentials(),\n",
+ " )\n",
+ "\n",
+ "def build_object_store_registry():\n",
+ " \"\"\"Build a VirtualiZarr ``ObjectStoreRegistry`` for the anonymous NOAA bucket.\n",
+ "\n",
+ " VirtualiZarr 2.x requires an explicit obstore-backed registry passed to\n",
+ " ``open_virtual_dataset`` (this replaced the older\n",
+ " ``open_virtual_dataset(url, parser=...)`` signature the pipeline was first\n",
+ " sketched against).\n",
+ " \"\"\"\n",
+ " from obspec_utils.registry import ObjectStoreRegistry\n",
+ " from obstore.store import S3Store\n",
+ "\n",
+ " store = S3Store.from_url(\n",
+ " STORE_PREFIX, region=REGION, skip_signature=True\n",
+ " )\n",
+ " return ObjectStoreRegistry({STORE_PREFIX: store})\n",
+ "\n",
+ "registry = build_object_store_registry()\n",
+ "prelim_repo = open_repo(\n",
+ " oisst_storage.preliminary_prefix, oisst_storage, s3_credentials\n",
+ ")\n",
+ "prelim_repo"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "SFPL",
+ "metadata": {},
+ "outputs": [
+ {
+ "ename": "Ancestor raised",
+ "evalue": "An ancestor raised an exception (ValueError)",
+ "output_type": "error",
+ "traceback": []
+ }
+ ],
+ "source": [
+ "prelim_session = prelim_repo.readonly_session(\"main\")\n",
+ "prelim_session"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "BYtC",
+ "metadata": {},
+ "outputs": [
+ {
+ "ename": "Ancestor raised",
+ "evalue": "An ancestor raised an exception (ValueError)",
+ "output_type": "error",
+ "traceback": []
+ }
+ ],
+ "source": [
+ "prelim_ds = xr.open_zarr(prelim_session.store, zarr_version=3)\n",
+ "# The times aren't necesarily ordered when appending to the store, so sort them for sanity\n",
+ "prelim_ds = prelim_ds.sortby(\"time\")\n",
+ "prelim_ds"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "RGSE",
+ "metadata": {
+ "marimo": {
+ "config": {
+ "hide_code": true
+ },
+ "md_prefix": "r"
+ }
+ },
+ "source": [
+ "The final data is split into two groups, the `daily/` virutal chunks, and pre-computed aggregated `monthly/` data."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "Kclp",
+ "metadata": {},
+ "outputs": [
+ {
+ "ename": "Ancestor raised",
+ "evalue": "An ancestor raised an exception (ValueError)",
+ "output_type": "error",
+ "traceback": []
+ }
+ ],
+ "source": [
+ "final_repo = open_repo(\n",
+ " oisst_storage.final_prefix, oisst_storage, s3_credentials\n",
+ ")\n",
+ "final_repo"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "emfo",
+ "metadata": {},
+ "outputs": [
+ {
+ "ename": "Ancestor raised",
+ "evalue": "An ancestor raised an exception (ValueError)",
+ "output_type": "error",
+ "traceback": []
+ }
+ ],
+ "source": [
+ "final_session = final_repo.readonly_session(\"main\")\n",
+ "final_session"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "Hstk",
+ "metadata": {},
+ "outputs": [
+ {
+ "ename": "Ancestor raised",
+ "evalue": "An ancestor raised an exception (ValueError)",
+ "output_type": "error",
+ "traceback": []
+ }
+ ],
+ "source": [
+ "final_ds = xr.open_zarr(final_session.store, zarr_version=3, group=\"daily\")\n",
+ "final_ds = final_ds.sortby(\"time\")\n",
+ "final_ds"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "nWHF",
+ "metadata": {},
+ "outputs": [
+ {
+ "ename": "Ancestor raised",
+ "evalue": "An ancestor raised an exception (ValueError)",
+ "output_type": "error",
+ "traceback": []
+ }
+ ],
+ "source": [
+ "monthly_ds = xr.open_zarr(\n",
+ " final_session.store, group=\"monthly\", zarr_version=3\n",
+ ")\n",
+ "monthly_ds = monthly_ds.sortby(\"time\")\n",
+ "monthly_ds"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "iLit",
+ "metadata": {
+ "marimo": {
+ "config": {
+ "hide_code": true
+ },
+ "md_prefix": "r"
+ }
+ },
+ "source": [
+ "Querying monthly data with a single point"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "ZHCJ",
+ "metadata": {
+ "jupyter": {
+ "source_hidden": true
+ },
+ "marimo": {
+ "config": {
+ "hide_code": true
+ }
+ },
+ "tags": [
+ "remove-input"
+ ]
+ },
+ "outputs": [
+ {
+ "ename": "Ancestor raised",
+ "evalue": "An ancestor raised an exception (ValueError)",
+ "output_type": "error",
+ "traceback": []
+ }
+ ],
+ "source": [
+ "TARGET_LAT, TARGET_LON = 43.66631, -69.52353\n",
+ "\n",
+ "# OISST longitudes are 0-360 (this store: 0.125-359.875); convert if needed\n",
+ "target_lon = (\n",
+ " TARGET_LON % 360 if float(monthly_ds.lon.max()) > 180 else TARGET_LON\n",
+ ")\n",
+ "\n",
+ "sst_mean_point = (\n",
+ " monthly_ds[\"sst_mean\"]\n",
+ " .sel(lat=TARGET_LAT, lon=target_lon, method=\"nearest\")\n",
+ " .squeeze(drop=True)\n",
+ ")\n",
+ "\n",
+ "# Guard against landing on a land cell (all-null): fall back to the nearest\n",
+ "# ocean cell within a +/-1 degree window\n",
+ "if sst_mean_point.isnull().all():\n",
+ " _win = (\n",
+ " monthly_ds[\"sst_mean\"]\n",
+ " .sel(\n",
+ " lat=slice(TARGET_LAT - 1, TARGET_LAT + 1),\n",
+ " lon=slice(target_lon - 1, target_lon + 1),\n",
+ " )\n",
+ " .squeeze(drop=True)\n",
+ " )\n",
+ " _ocean = _win.isel(time=0).notnull()\n",
+ " _dist = (_win.lat - TARGET_LAT) ** 2 + (_win.lon - target_lon) ** 2\n",
+ " _nearest = _dist.where(_ocean).argmin(dim=(\"lat\", \"lon\"))\n",
+ " sst_mean_point = _win.isel(_nearest)\n",
+ "\n",
+ "cell_lat, cell_lon = float(sst_mean_point.lat), float(sst_mean_point.lon)\n",
+ "n_null = int(sst_mean_point.isnull().sum())\n",
+ "mo.md(\n",
+ " f\"Using ocean cell **({cell_lat:.3f}, {cell_lon:.3f})** \"\n",
+ " f\"(= {cell_lon - 360:.3f} in -180..180) for target ({TARGET_LAT}, {TARGET_LON}) — \"\n",
+ " f\"{sst_mean_point.sizes['time']} months, {n_null} nulls\"\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "ROlb",
+ "metadata": {
+ "jupyter": {
+ "source_hidden": true
+ },
+ "marimo": {
+ "config": {
+ "hide_code": true
+ }
+ },
+ "tags": [
+ "remove-input"
+ ]
+ },
+ "outputs": [
+ {
+ "ename": "Ancestor raised",
+ "evalue": "An ancestor raised an exception (ValueError)",
+ "output_type": "error",
+ "traceback": []
+ }
+ ],
+ "source": [
+ "import matplotlib.pyplot as plt\n",
+ "\n",
+ "_fig, _ax = plt.subplots(figsize=(9, 4))\n",
+ "sst_mean_point.plot.line(marker=\"o\", ax=_ax)\n",
+ "_ax.set_title(\n",
+ " f\"OISST monthly mean SST near ({TARGET_LAT}, {TARGET_LON}) \"\n",
+ " f\"[cell {cell_lat:.3f}, {cell_lon - 360:.3f}]\"\n",
+ ")\n",
+ "_ax.set_ylabel(\"sst_mean (°C)\")\n",
+ "_ax.grid(alpha=0.3)\n",
+ "_fig.tight_layout()\n",
+ "_fig"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "qnkX",
+ "metadata": {
+ "marimo": {
+ "config": {
+ "hide_code": true
+ },
+ "md_prefix": "r"
+ }
+ },
+ "source": [
+ "Querying monthly data with a set of points which are snapped to the nearest point with data, not just the nearest in case of `NaN` values."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "TqIu",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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",
+ "text/html": [
+ "\n",
+ "\n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ "
<xarray.Dataset> Size: 60B\n",
+ "Dimensions: (points: 3)\n",
+ "Coordinates:\n",
+ " * points (points) <U1 12B 'a' 'b' 'c'\n",
+ "Data variables:\n",
+ " lat (points) float64 24B 43.67 43.65 44.57\n",
+ " lon (points) float64 24B -69.52 -70.26 -68.2 "
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "_point_coords = {\"points\": [\"a\", \"b\", \"c\"]}\n",
+ "_lat = xr.DataArray([43.66631, 43.64694, 44.57038], coords=_point_coords)\n",
+ "_lon = xr.DataArray(\n",
+ " [-69.52353, -70.25663, -68.19852], coords=_point_coords\n",
+ ")\n",
+ "points = xr.Dataset({\"lat\": _lat, \"lon\": _lon})\n",
+ "points"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "Vxnm",
+ "metadata": {
+ "jupyter": {
+ "source_hidden": true
+ },
+ "marimo": {
+ "config": {
+ "hide_code": true
+ }
+ },
+ "tags": [
+ "remove-input"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "def sel_ocean_points(da, pts, window=1.0):\n",
+ " \"\"\"Pointwise-select `da` at each (lat, lon) in `pts`, snapping to ocean cells.\n",
+ "\n",
+ " Handles two gotchas of point extraction from OISST-style grids:\n",
+ "\n",
+ " - **Longitude convention** - converts the points' -180..180 longitudes to\n",
+ " 0..360 when that's what the grid uses (detected from `da.lon.max()`).\n",
+ " - **Land cells** - a point whose nearest cell is all-null (land) is snapped\n",
+ " to the nearest non-null (ocean) cell within +/-`window` degrees, using\n",
+ " the first timestep's null mask as the land/ocean mask.\n",
+ "\n",
+ " Returns a DataArray with a `points` dimension (labels from `pts`), the\n",
+ " chosen cell's lat/lon as coordinates, and a boolean `snapped` coordinate\n",
+ " marking points that were moved off land.\n",
+ " \"\"\"\n",
+ " lon_max = float(da.lon.max())\n",
+ " lons = pts[\"lon\"] % 360 if lon_max > 180 else pts[\"lon\"]\n",
+ " ocean = da.isel(time=0).notnull().drop_vars(\"time\", errors=\"ignore\")\n",
+ "\n",
+ " series = []\n",
+ " snapped = []\n",
+ " for i in range(pts.sizes[\"points\"]):\n",
+ " t_lat, t_lon = float(pts[\"lat\"][i]), float(lons[i])\n",
+ " cell = da.sel(lat=t_lat, lon=t_lon, method=\"nearest\")\n",
+ " if bool(cell.isnull().all()):\n",
+ " win = da.sel(\n",
+ " lat=slice(t_lat - window, t_lat + window),\n",
+ " lon=slice(t_lon - window, t_lon + window),\n",
+ " )\n",
+ " dist = (win.lat - t_lat) ** 2 + (win.lon - t_lon) ** 2\n",
+ " idx = dist.where(ocean.sel(lat=win.lat, lon=win.lon)).argmin(\n",
+ " dim=(\"lat\", \"lon\")\n",
+ " )\n",
+ " cell = win.isel(idx)\n",
+ " snapped.append(True)\n",
+ " else:\n",
+ " snapped.append(False)\n",
+ " series.append(cell)\n",
+ "\n",
+ " out = xr.concat(series, dim=pts[\"points\"])\n",
+ " return out.assign_coords(snapped=(\"points\", snapped))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "DnEU",
+ "metadata": {
+ "jupyter": {
+ "source_hidden": true
+ },
+ "marimo": {
+ "config": {
+ "hide_code": true
+ }
+ },
+ "tags": [
+ "remove-input"
+ ]
+ },
+ "outputs": [
+ {
+ "ename": "Ancestor raised",
+ "evalue": "An ancestor raised an exception (ValueError)",
+ "output_type": "error",
+ "traceback": []
+ }
+ ],
+ "source": [
+ "points_sst = sel_ocean_points(\n",
+ " monthly_ds[\"sst_mean\"].squeeze(drop=True), points\n",
+ ")\n",
+ "\n",
+ "_rows = \"\\n\".join(\n",
+ " f\"| {str(points_sst['points'][i].item())} \"\n",
+ " f\"| {float(points['lat'][i]):.5f}, {float(points['lon'][i]):.5f} \"\n",
+ " f\"| {float(points_sst.lat[i]):.3f}, {float(points_sst.lon[i]) - 360:.3f} \"\n",
+ " f\"| {'yes' if bool(points_sst.snapped[i]) else 'no'} \"\n",
+ " f\"| {int(points_sst.isel(points=i).isnull().sum())} |\"\n",
+ " for i in range(points_sst.sizes[\"points\"])\n",
+ ")\n",
+ "mo.md(\n",
+ " \"| point | requested (lat, lon) | cell used (lat, lon) | snapped off land? | nulls |\\n\"\n",
+ " \"| --- | --- | --- | --- | --- |\\n\" + _rows\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "ulZA",
+ "metadata": {
+ "jupyter": {
+ "source_hidden": true
+ },
+ "marimo": {
+ "config": {
+ "hide_code": true
+ }
+ },
+ "tags": [
+ "remove-input"
+ ]
+ },
+ "outputs": [
+ {
+ "ename": "Ancestor raised",
+ "evalue": "An ancestor raised an exception (ValueError)",
+ "output_type": "error",
+ "traceback": []
+ }
+ ],
+ "source": [
+ "_fig2, _ax2 = plt.subplots(figsize=(9, 4))\n",
+ "for _i in range(points_sst.sizes[\"points\"]):\n",
+ " _r = points_sst.isel(points=_i)\n",
+ " _ax2.plot(\n",
+ " _r.time,\n",
+ " _r,\n",
+ " marker=\"o\",\n",
+ " label=f\"{str(_r['points'].item())} ({float(_r.lat):.2f}, {float(_r.lon) - 360:.2f})\"\n",
+ " + (\" [snapped]\" if bool(_r.snapped) else \"\"),\n",
+ " )\n",
+ "_ax2.set_title(\n",
+ " \"OISST monthly mean SST - pointwise selection with ocean snapping\"\n",
+ ")\n",
+ "_ax2.set_ylabel(\"sst_mean (°C)\")\n",
+ "_ax2.legend()\n",
+ "_ax2.grid(alpha=0.3)\n",
+ "_fig2.tight_layout()\n",
+ "_fig2"
+ ]
+ }
+ ],
+ "metadata": {
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3"
+ },
+ "marimo": {
+ "app_config": {
+ "auto_download": [
+ "ipynb"
+ ],
+ "width": "medium"
+ },
+ "header": "# /// script\n# dependencies = [\n# \"dagster==1.13.14\",\n# \"icechunk==2.1.1\",\n# \"marimo\",\n# \"matplotlib==3.11.1\",\n# \"numpy==2.5.1\",\n# \"pydantic==2.13.4\",\n# \"s3fs==2026.6.0\",\n# \"virtualizarr==2.7.1\",\n# \"xarray==2026.7.0\",\n# ]\n# requires-python = \">=3.14\"\n# ///\n\n",
+ "marimo_version": "0.23.11"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}