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", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
SITESLATITUDELONGITUDE
0Little John-NW43.753894-70.136267
1Little John-NE43.753994-70.135828
2Little John-SE43.753647-70.136350
3Mackworth- SE43.6919-70.228983
4Mackworth- SW43.691739-70.228897
............
1263Long Point Cove43.7818491-69.934574
1264Lowell Cove43.7509752-69.983197
1265Skitterygusset43.71458-70.247910
1266Mackworth Island - Beach43.68958-70.235720
1267Mussel Cove43.71143-70.218140
\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", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
LATITUDELONGITUDE
SITES
Little John-NW43.753894-70.136267
Little John-NE43.753994-70.135828
Little John-SE43.753647-70.136350
Mackworth- SE43.6919-70.228983
Mackworth- SW43.691739-70.228897
.........
Lowell Cove43.7509752-69.983197
Garrison Cove43.750853-69.959060
Orrs Cove43.835471-69.913802
Cedar Beach43.7437857-69.985609
Stovers Point43.7578601-69.998103
\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
" + ], + "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", + "
icechunk.Repository (v2)
\n", + "
\n", + " storage\n", + " \n", + "
\n", + "
icechunk.Storage
\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", + "
icechunk.config.RepositoryConfig
\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", + "
icechunk.config.CompressionConfig
\n", + "
algorithm: Zstd (default)
\n", + "
level: 3 (default)
\n", + "
\n", + "\n", + "
\n", + "
\n", + " caching\n", + " \n", + "
\n", + "
icechunk.config.CachingConfig
\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", + "
icechunk.storage.StorageSettings
\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", + "
icechunk.storage.StorageConcurrencySettings
\n", + "
max_concurrent_requests_for_object: 18 (default)
\n", + "
ideal_concurrent_request_size: 12582912 (default)
\n", + "
\n", + "\n", + "
\n", + "
\n", + " retries\n", + " \n", + "
\n", + "
icechunk.storage.StorageRetriesSettings
\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", + "
icechunk.config.ManifestConfig
\n", + "
\n", + " preload\n", + " \n", + "
\n", + "
icechunk.config.ManifestPreloadConfig
\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", + "
icechunk.config.ManifestSplittingConfig
\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", + "
icechunk.config.ManifestVirtualChunkLocationCompressionConfig
\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", + "
icechunk.config.RepoUpdateRetryConfig
\n", + "
\n", + " default\n", + " \n", + "
\n", + "
icechunk.storage.StorageRetriesSettings
\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", + "
icechunk.virtual.VirtualChunkContainer
\n", + "
name: None
\n", + "
url_prefix: \"s3://noaa-cdr-sea-surface-temp-optimum-interpolation-pds/\"
\n", + "
\n", + " store\n", + " \n", + "
\n", + "
icechunk.config.ObjectStoreConfig.S3
\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", + "
icechunk.session.Session
\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*
" + ], + "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 ...
" + ], + "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
" + ], + "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 +}