├── .gitignore ├── LICENSE ├── README.md ├── RELEASE.sh ├── data ├── antarctic.geojson ├── dicksonfjord.geojson ├── grandmesa.dbf ├── grandmesa.geojson ├── grandmesa.prj ├── grandmesa.shp ├── grandmesa.shx └── polygon.geojson ├── demo ├── .gitignore ├── Makefile ├── docker │ ├── demo-nginx │ │ ├── Dockerfile │ │ └── nginx.conf │ └── demo │ │ ├── Dockerfile │ │ └── docker-entrypoint.sh ├── terraform │ ├── .terraform.lock.hcl │ ├── alb.tf │ ├── autoscaling.tf │ ├── backend.tf │ ├── ecs.tf │ ├── iam.tf │ ├── logs.tf │ ├── outputs.tf │ ├── policies │ │ ├── appautoscaling-role-policy.json │ │ └── appautoscaling-role.json │ ├── provider.tf │ ├── security-groups.tf │ ├── templates │ │ └── demo.json.tpl │ ├── variables.tf │ └── vpc.tf └── voila_demo.ipynb ├── environment.yml ├── examples ├── 3dep_gedi_sample.ipynb ├── GLIMS_ATL06_Subsetter.ipynb ├── api_widgets_demo.ipynb ├── arcticdem_mosaic.ipynb ├── arcticdem_strip_boundaries.ipynb ├── atl03_subsetter.py ├── atl03_widgets_demo.ipynb ├── atl06_ancillary.ipynb ├── atl06_subsetting.ipynb ├── atl13_subsetting.ipynb ├── boulder_watershed_demo.ipynb ├── boulder_watershed_viewer_demo.ipynb ├── cmr_debug_regions.ipynb ├── gedi_l1b.ipynb ├── gedi_l2a.ipynb ├── gedi_l2a_access.ipynb ├── gedi_l3_sample.ipynb ├── gedi_l4a.ipynb ├── gedi_l4b_sample.ipynb ├── grand_mesa_atl03_classification.ipynb ├── grand_mesa_demo.ipynb ├── grandmesa.geojson ├── multi_mission_grand_mesa.ipynb ├── phoreal.ipynb ├── sierra.geojson ├── single_track_demo.ipynb ├── swot_cmr_sim.ipynb └── swot_l2_sim.ipynb └── version.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # User 132 | .vscode/ 133 | .DS_Store 134 | .DS_Store? 135 | pytrace.* 136 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021, University of Washington. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | 3. Neither the name of the University of Washington nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY OF WASHINGTON AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OF WASHINGTON OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sliderule-python 2 | 3 | [![DOI](https://zenodo.org/badge/311384982.svg)](https://zenodo.org/badge/latestdoi/311384982) 4 | 5 | Example notebooks that use SlideRule's Python client for processing Earth science data. 6 | 7 | ## Overview 8 | Detailed [documentation](https://slideruleearth.io/rtd/) on installing and using the SlideRule Python client can be found at [slideruleearth.io](https://slideruleearth.io/). 9 | 10 | > NOTE: As of 3/10/2023 the source code for SlideRule's Python client has moved to the [sliderule](https://github.com/SlideRuleEarth/sliderule) repository. This [sliderule-python](https://github.com/SlideRuleEarth/sliderule-python) repository continues to function as a collection of example notebooks that use the SlideRule Python client and demonstrate common workflows. 11 | 12 | ## Getting Started 13 | 14 | The easiest way to install the Sliderule Python client and run the example notebooks in this repository is to create a conda environment from the provided `environment.yml` file: 15 | ```bash 16 | conda env create -f environment.yml 17 | conda activate sliderule_env 18 | ``` 19 | 20 | If you have your own conda environment that you want to install the SlideRule Python client into, then: 21 | ```bash 22 | conda activate my_env 23 | conda install -c conda-forge sliderule 24 | ``` 25 | 26 | If you already have the SlideRule Python client installed in a conda environment and want to update to the latest version, then: 27 | ```bash 28 | conda activate sliderule_env 29 | conda update -c conda-forge sliderule 30 | ``` 31 | 32 | For alternate methods to install SlideRule, including options for developers, please see the [installation instructions](https://slideruleearth.io/rtd/getting_started/Install.html). 33 | 34 | ## Documentation 35 | 36 | Please see our [documentation](https://slideruleearth.io/rtd/) page for reference and user's guide material. 37 | -------------------------------------------------------------------------------- /RELEASE.sh: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # Acceptable version identifier is x.y.z, e.g. 1.0.4 4 | # the version number is then prepended with 'v' for 5 | # the tags annotation in git. 6 | # 7 | VERSION=$1 8 | if [[ "$VERSION" != "v"*"."*"."* ]]; then 9 | echo "Invalid version number" 10 | exit 1 11 | fi 12 | if git tag -l | grep -w $VERSION; then 13 | echo "Git tag already exists" 14 | exit 1 15 | fi 16 | 17 | # 18 | # Clean up any previously attempted archives 19 | # 20 | rm sliderule-python-$VERSION.tar.gz 2> /dev/null 21 | 22 | # 23 | # Update version in local repository 24 | # 25 | echo $VERSION > version.txt 26 | git add version.txt 27 | git commit -m "Version $VERSION" 28 | 29 | # 30 | # Create tag and acrhive 31 | # 32 | git tag -a $VERSION -m "version $VERSION" 33 | git archive --format=tar.gz --prefix=sliderule-python/ $VERSION > sliderule-python-$VERSION.tar.gz 34 | 35 | -------------------------------------------------------------------------------- /data/antarctic.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "properties": {}, 7 | "geometry": { 8 | "coordinates": [ 9 | [ 10 | [ 11 | -65.38441388357364, 12 | -58.814502694853296 13 | ], 14 | [ 15 | -107.06260689878462, 16 | -71.64363061934944 17 | ], 18 | [ 19 | -39.87842054012137, 20 | -77.48100239823627 21 | ], 22 | [ 23 | -45.11430516711954, 24 | -61.21401193422109 25 | ], 26 | [ 27 | -65.38441388357364, 28 | -58.814502694853296 29 | ] 30 | ] 31 | ], 32 | "type": "Polygon" 33 | } 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /data/dicksonfjord.geojson: -------------------------------------------------------------------------------- 1 | {"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"coordinates":[[[-27.324285913258706,72.91303911622592],[-27.678594995289217,72.80131917669064],[-27.313299585132967,72.60615538614448],[-26.434393335133166,72.69461098330294],[-26.173468042164387,72.83782721550836],[-26.448126245289757,72.942067469947],[-27.324285913258706,72.91303911622592]]],"type":"Polygon"}}]} -------------------------------------------------------------------------------- /data/grandmesa.dbf: -------------------------------------------------------------------------------- 1 | zA FIDN 0 -------------------------------------------------------------------------------- /data/grandmesa.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "name": "grand_mesa_poly", 4 | "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, 5 | "features": [ 6 | { "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -108.311682565537666, 39.137576462129438 ], [ -108.341156683252237, 39.037589876133246 ], [ -108.287868638779599, 38.89051431295789 ], [ -108.207729687800509, 38.823205529198098 ], [ -108.074601643110313, 38.847513782586297 ], [ -107.985605104949812, 38.943991201101703 ], [ -107.728398587557521, 39.015109302306328 ], [ -107.787241424909936, 39.195630349659986 ], [ -108.049394800987542, 39.139504663354245 ], [ -108.172870009708575, 39.159200663961158 ], [ -108.311682565537666, 39.137576462129438 ] ] ] } } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /data/grandmesa.prj: -------------------------------------------------------------------------------- 1 | GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]] -------------------------------------------------------------------------------- /data/grandmesa.shp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SlideRuleEarth/sliderule-python/69f0f9d332b525de7fb449747b25907b106dc3fe/data/grandmesa.shp -------------------------------------------------------------------------------- /data/grandmesa.shx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SlideRuleEarth/sliderule-python/69f0f9d332b525de7fb449747b25907b106dc3fe/data/grandmesa.shx -------------------------------------------------------------------------------- /data/polygon.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "properties": {}, 7 | "geometry": { 8 | "type": "Polygon", 9 | "coordinates": [ 10 | [ 11 | [ 12 | -108.3087158203125, 13 | 38.81670618152057 14 | ], 15 | [ 16 | -107.87406921386717, 17 | 38.81670618152057 18 | ], 19 | [ 20 | -107.87406921386717, 21 | 39.122602866278996 22 | ], 23 | [ 24 | -108.3087158203125, 25 | 39.122602866278996 26 | ], 27 | [ 28 | -108.3087158203125, 29 | 38.81670618152057 30 | ] 31 | ] 32 | ] 33 | } 34 | } 35 | ] 36 | } 37 | 38 | 39 | -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | .terraform 2 | stage/ -------------------------------------------------------------------------------- /demo/Makefile: -------------------------------------------------------------------------------- 1 | ROOT = $(shell pwd) 2 | STAGE = $(ROOT)/stage 3 | DEMO_STAGE_DIR = $(STAGE)/demo 4 | DEMO_NGINX_STAGE_DIR = $(STAGE)/demo-nginx 5 | VERSION ?= latest 6 | REPO ?= 742127912612.dkr.ecr.us-west-2.amazonaws.com 7 | DOCKEROPTS ?= 8 | DOMAIN ?= testsliderule.org 9 | DOMAIN_ROOT = $(firstword $(subst ., ,$(DOMAIN))) 10 | 11 | all: demo-docker 12 | 13 | demo-docker: # make the python client demo docker image; needs VERSION 14 | -rm -Rf $(DEMO_STAGE_DIR) 15 | mkdir -p $(DEMO_STAGE_DIR) 16 | cp ../environment.yml $(DEMO_STAGE_DIR) 17 | cp voila_demo.ipynb $(DEMO_STAGE_DIR) 18 | cp docker/demo/* $(DEMO_STAGE_DIR) 19 | # used to install local copy of client (only if necessary during development, see dockerfile for additional steps) 20 | cp -R ../../sliderule $(DEMO_STAGE_DIR) 21 | chmod +x $(DEMO_STAGE_DIR)/docker-entrypoint.sh 22 | cd $(DEMO_STAGE_DIR) && docker build $(DOCKEROPTS) -t $(REPO)/demo-client:latest . 23 | docker tag $(REPO)/demo-client:latest $(REPO)/demo-client:$(VERSION) 24 | mkdir -p $(DEMO_NGINX_STAGE_DIR) 25 | cp docker/demo-nginx/* $(DEMO_NGINX_STAGE_DIR) 26 | cd $(DEMO_NGINX_STAGE_DIR) && docker build $(DOCKEROPTS) -t $(REPO)/demo-nginx:latest . 27 | docker tag $(REPO)/demo-nginx:latest $(REPO)/demo-nginx:$(VERSION) 28 | 29 | demo-run: ## run the python client demo docker container locally; needs VERSION 30 | docker run -it --rm --name=python-app -p 8866:8866 --entrypoint /usr/local/etc/docker-entrypoint.sh $(REPO)/demo-client:$(VERSION) 31 | 32 | demo-push: 33 | docker push $(REPO)/demo-client:$(VERSION) 34 | docker push $(REPO)/demo-nginx:$(VERSION) 35 | 36 | demo-deploy: ## deploy demo using terraform; needs VERSION, DOMAIN 37 | cd terraform && terraform init 38 | cd terraform && terraform workspace select $(DOMAIN)-demo || terraform workspace new $(DOMAIN)-demo 39 | cd terraform && terraform apply -var docker_image_url_demo-client=$(REPO)/demo-client:$(VERSION) -var docker_image_url_demo-nginx=$(REPO)/demo-nginx:$(VERSION) -var domain=$(DOMAIN) -var domain_root=$(DOMAIN_ROOT) 40 | 41 | demo-destroy: ## destroy demo using terraform; needs DOMAIN 42 | cd terraform && terraform init 43 | cd terraform && terraform workspace select $(DOMAIN)-demo || terraform workspace new $(DOMAIN)-demo 44 | cd terraform && terraform destroy -var domain=$(DOMAIN) -var domain_root=$(DOMAIN_ROOT) 45 | 46 | distclean: ## fully remove all non-version controlled files and directories 47 | - rm -Rf $(STAGE) 48 | -------------------------------------------------------------------------------- /demo/docker/demo-nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | FROM nginx:stable 3 | RUN rm /etc/nginx/conf.d/default.conf 4 | COPY nginx.conf /etc/nginx/conf.d 5 | EXPOSE 80 -------------------------------------------------------------------------------- /demo/docker/demo-nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | error_log /var/log/nginx/error.log debug; 4 | access_log /var/log/nginx/access_log; 5 | server_name voila.*; 6 | proxy_buffering off; 7 | location / { 8 | proxy_pass http://localhost:8866/; 9 | proxy_set_header Host $host; 10 | proxy_set_header X-Real-IP $remote_addr; 11 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 12 | 13 | proxy_http_version 1.1; 14 | proxy_set_header Upgrade $http_upgrade; 15 | proxy_set_header Connection "upgrade"; 16 | proxy_read_timeout 86400; 17 | } 18 | 19 | location /ping/ { 20 | access_log off; 21 | return 200; 22 | } 23 | 24 | client_max_body_size 100M; 25 | } 26 | -------------------------------------------------------------------------------- /demo/docker/demo/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM continuumio/miniconda3 2 | MAINTAINER JP Swinski (jp.swinski@nasa.gov) 3 | 4 | # Environment 5 | ENV PYTHONPATH=/usr/local/lib 6 | 7 | # Install SlideRule client 8 | COPY environment.yml /environment.yml 9 | RUN conda env create -f environment.yml 10 | 11 | # Make RUN commands use the new environment: 12 | SHELL ["conda", "run", "-n", "sliderule_env", "/bin/bash", "-c"] 13 | RUN conda install -c conda-forge voila 14 | 15 | # Install Voila Demo 16 | COPY voila_demo.ipynb /voila_demo.ipynb 17 | 18 | # Local install of client (only if necessary) 19 | #COPY sliderule /sliderule 20 | #RUN cd /sliderule/clients/python && pip install . 21 | 22 | # Entry point 23 | COPY docker-entrypoint.sh /usr/local/etc/ 24 | ENTRYPOINT ["/bin/bash"] -------------------------------------------------------------------------------- /demo/docker/demo/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | conda run --no-capture-output -n sliderule_env voila --theme=dark --no-browser --Voila.ip=0.0.0.0 --MappingKernelManager.cull_interval=60 --MappingKernelManager.cull_idle_timeout=120 /voila_demo.ipynb 3 | -------------------------------------------------------------------------------- /demo/terraform/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/hashicorp/aws" { 5 | version = "4.37.0" 6 | hashes = [ 7 | "h1:qf29Ohi9uNHvLlhi1Q/exYG+8VvmOwfyCwKg23Mn6WU=", 8 | "zh:12c2eb60cb1eb0a41d1afbca6fc6f0eed6ca31a12c51858f951a9e71651afbe0", 9 | "zh:1e17482217c39a12e930e71fd2c9af8af577bec6736b184674476ebcaad28477", 10 | "zh:1e8163c3d871bbd54c189bf2fe5e60e556d67fa399e4c88c8e6ee0834525dc33", 11 | "zh:399c41a3e096fd75d487b98b1791f7cea5bd38567ac4e621c930cb67ec45977c", 12 | "zh:40d4329eef2cc130e4cbed7a6345cb053dd258bf6f5f8eb0f8ce777ae42d5a01", 13 | "zh:625db5fa75638d543b418be7d8046c4b76dc753d9d2184daa0faaaaebc02d207", 14 | "zh:7785c8259f12b45d19fa5abdac6268f3b749fe5a35c8be762c27b7a634a4952b", 15 | "zh:8a7611f33cc6422799c217ec2eeb79c779035ef05331d12505a6002bc48582f0", 16 | "zh:9188178235a73c829872d2e82d88ac6d334d8bb01433e9be31615f1c1633e921", 17 | "zh:994895b57bf225232a5fa7422e6ab87d8163a2f0605f54ff6a18cdd71f0aeadf", 18 | "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", 19 | "zh:b57de6903ef30c9f22d38d595d64b4f92a89ea717b65782e1f44f57020ce8b1f", 20 | ] 21 | } 22 | 23 | provider "registry.terraform.io/hashicorp/template" { 24 | version = "2.2.0" 25 | hashes = [ 26 | "h1:12Bac8B6Aq2+18xe8iqp5iYytav2Bw+jG43z/VaK5zI=", 27 | "zh:01702196f0a0492ec07917db7aaa595843d8f171dc195f4c988d2ffca2a06386", 28 | "zh:09aae3da826ba3d7df69efeb25d146a1de0d03e951d35019a0f80e4f58c89b53", 29 | "zh:09ba83c0625b6fe0a954da6fbd0c355ac0b7f07f86c91a2a97849140fea49603", 30 | "zh:0e3a6c8e16f17f19010accd0844187d524580d9fdb0731f675ffcf4afba03d16", 31 | "zh:45f2c594b6f2f34ea663704cc72048b212fe7d16fb4cfd959365fa997228a776", 32 | "zh:77ea3e5a0446784d77114b5e851c970a3dde1e08fa6de38210b8385d7605d451", 33 | "zh:8a154388f3708e3df5a69122a23bdfaf760a523788a5081976b3d5616f7d30ae", 34 | "zh:992843002f2db5a11e626b3fc23dc0c87ad3729b3b3cff08e32ffb3df97edbde", 35 | "zh:ad906f4cebd3ec5e43d5cd6dc8f4c5c9cc3b33d2243c89c5fc18f97f7277b51d", 36 | "zh:c979425ddb256511137ecd093e23283234da0154b7fa8b21c2687182d9aea8b2", 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /demo/terraform/alb.tf: -------------------------------------------------------------------------------- 1 | # demo Load Balancer 2 | # demo Load Balancer 3 | resource "aws_lb" "demo" { 4 | name = "${var.domain_root}-${var.Name_tag}-alb" 5 | load_balancer_type = "application" 6 | internal = false 7 | security_groups = [aws_security_group.alb-sg.id] 8 | subnets = aws_subnet.public.*.id 9 | # access_logs { 10 | # bucket = "sliderule" 11 | # prefix = "access_logs/${domain}/demo" 12 | # enabled = true 13 | # } 14 | tags = { 15 | Name = "${var.domain_root}-${var.Name_tag}-alb" 16 | } 17 | } 18 | 19 | # Target group 20 | resource "aws_alb_target_group" "demo-target-group" { 21 | name = "${var.domain_root}-${var.Name_tag}-alb-tg" 22 | port = var.nginx_container_port 23 | protocol = "HTTP" 24 | vpc_id = aws_vpc.demo.id 25 | target_type = "ip" 26 | 27 | health_check { 28 | path = var.demo_health_check_path 29 | port = "traffic-port" 30 | healthy_threshold = 5 31 | unhealthy_threshold = 5 32 | timeout = 2 33 | interval = 5 34 | matcher = "200" 35 | } 36 | stickiness { 37 | enabled = true 38 | type = "lb_cookie" 39 | } 40 | tags = { 41 | Name = "${var.domain_root}-${var.Name_tag}-alb-tg" 42 | } 43 | } 44 | data "aws_acm_certificate" "sliderule_cluster_cert" { 45 | domain = "*.${var.domain}" 46 | types = ["AMAZON_ISSUED"] 47 | most_recent = true 48 | } 49 | # Listener (redirects traffic from the load balancer to the target group) 50 | resource "aws_alb_listener" "demo-https-listener" { 51 | load_balancer_arn = aws_lb.demo.id 52 | port = 443 53 | protocol = "HTTPS" 54 | ssl_policy = "ELBSecurityPolicy-2016-08" 55 | certificate_arn = data.aws_acm_certificate.sliderule_cluster_cert.arn 56 | depends_on = [aws_alb_target_group.demo-target-group] 57 | 58 | default_action { 59 | type = "forward" 60 | target_group_arn = aws_alb_target_group.demo-target-group.arn 61 | } 62 | tags = { 63 | Name = "${var.domain_root}-${var.Name_tag}-https-lsnr" 64 | } 65 | } 66 | 67 | resource "aws_alb_listener" "demo-http-listener" { 68 | load_balancer_arn = aws_lb.demo.id 69 | port = 80 70 | protocol = "HTTP" 71 | default_action { 72 | type = "redirect" 73 | redirect { 74 | port = "443" 75 | protocol = "HTTPS" 76 | status_code = "HTTP_301" 77 | } 78 | } 79 | tags = { 80 | Name = "${var.domain_root}-${var.Name_tag}-http-lsnr" 81 | } 82 | } 83 | 84 | # Route 53 85 | 86 | data "aws_route53_zone" "selected" { 87 | name = "${var.domain}" 88 | } 89 | 90 | resource "aws_route53_record" "demo-site" { 91 | zone_id = data.aws_route53_zone.selected.zone_id 92 | name = "demo.${data.aws_route53_zone.selected.name}" 93 | type = "A" 94 | allow_overwrite = true 95 | alias { 96 | name = aws_lb.demo.dns_name 97 | zone_id = aws_lb.demo.zone_id 98 | evaluate_target_health = false 99 | } 100 | } -------------------------------------------------------------------------------- /demo/terraform/autoscaling.tf: -------------------------------------------------------------------------------- 1 | resource "aws_iam_role" "autoscaling" { 2 | name = "${var.domain_root}-${var.Name_tag}-appautoscaling-role" 3 | assume_role_policy = file("policies/appautoscaling-role.json") 4 | } 5 | 6 | resource "aws_iam_role_policy" "autoscaling" { 7 | name = "${var.domain_root}-${var.Name_tag}-appautoscaling-policy" 8 | policy = file("policies/appautoscaling-role-policy.json") 9 | role = aws_iam_role.autoscaling.id 10 | } 11 | 12 | resource "aws_appautoscaling_target" "ecs_target" { 13 | max_capacity = "${var.auto_scale_max}" 14 | min_capacity = "${var.auto_scale_min}" 15 | resource_id = "service/${aws_ecs_cluster.demo.name}/${aws_ecs_service.demo-ecs-service.name}" 16 | role_arn = aws_iam_role.autoscaling.arn 17 | scalable_dimension = "ecs:service:DesiredCount" 18 | service_namespace = "ecs" 19 | depends_on = [aws_ecs_service.demo-ecs-service] 20 | } 21 | 22 | resource "aws_appautoscaling_policy" "ecs_mem_tgt_policy" { 23 | name = "${var.domain_root}-${var.Name_tag}-mem-asp" 24 | policy_type = "TargetTrackingScaling" 25 | resource_id = aws_appautoscaling_target.ecs_target.resource_id 26 | scalable_dimension = aws_appautoscaling_target.ecs_target.scalable_dimension 27 | service_namespace = aws_appautoscaling_target.ecs_target.service_namespace 28 | 29 | target_tracking_scaling_policy_configuration { 30 | target_value = "${var.mem_scale_target_value}" 31 | scale_in_cooldown = "${var.mem_scale_in_cooldown}" 32 | scale_out_cooldown = "${var.mem_scale_out_cooldown}" 33 | predefined_metric_specification { 34 | predefined_metric_type = "ECSServiceAverageMemoryUtilization" 35 | } 36 | } 37 | depends_on = [aws_appautoscaling_target.ecs_target] 38 | } 39 | 40 | resource "aws_appautoscaling_policy" "ecs_cpu_tgt_policy" { 41 | name = "${var.domain_root}-${var.Name_tag}-cpu-asp" 42 | policy_type = "TargetTrackingScaling" 43 | resource_id = aws_appautoscaling_target.ecs_target.resource_id 44 | scalable_dimension = aws_appautoscaling_target.ecs_target.scalable_dimension 45 | service_namespace = aws_appautoscaling_target.ecs_target.service_namespace 46 | 47 | target_tracking_scaling_policy_configuration { 48 | target_value = "${var.cpu_scale_target_value}" 49 | scale_in_cooldown = "${var.cpu_scale_in_cooldown}" 50 | scale_out_cooldown = "${var.cpu_scale_out_cooldown}" 51 | predefined_metric_specification { 52 | predefined_metric_type = "ECSServiceAverageCPUUtilization" 53 | } 54 | } 55 | #create the policies one at at time 56 | depends_on = [aws_appautoscaling_target.ecs_target,aws_appautoscaling_policy.ecs_mem_tgt_policy] 57 | } 58 | 59 | # resource "aws_appautoscaling_policy" "ecs_req_cnt_tgt_policy" { 60 | # name = "${var.domain_root}-${var.Name_tag}-cpu-asp" 61 | # policy_type = "TargetTrackingScaling" 62 | # resource_id = aws_appautoscaling_target.ecs_target.resource_id 63 | # scalable_dimension = aws_appautoscaling_target.ecs_target.scalable_dimension 64 | # service_namespace = aws_appautoscaling_target.ecs_target.service_namespace 65 | 66 | # target_tracking_scaling_policy_configuration { 67 | # target_value = "${var.req_cnt_scale_target_value}" 68 | # scale_in_cooldown = "${var.req_cnt_scale_in_cooldown}" 69 | # scale_out_cooldown = "${var.req_cnt_scale_out_cooldown}" 70 | # predefined_metric_specification { 71 | # predefined_metric_type = "ALBRequestCountPerTarget" 72 | # // app///targetgroup// 73 | # resource_label = "${aws_lb.demo.arn_suffix}/${aws_alb_target_group.demo-target-group.arn_suffix}" 74 | # } 75 | # } 76 | # depends_on = [aws_appautoscaling_target.ecs_target] 77 | # } 78 | -------------------------------------------------------------------------------- /demo/terraform/backend.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "s3" { 3 | bucket = "sliderule" 4 | key = "tf-states/demo.tfstate" 5 | workspace_key_prefix = "tf-workspaces" 6 | encrypt = true 7 | profile = "default" 8 | region = "us-west-2" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /demo/terraform/ecs.tf: -------------------------------------------------------------------------------- 1 | resource "aws_ecs_cluster" "demo" { 2 | name = "${var.domain_root}-${var.Name_tag}-ecs-clstr" 3 | setting { 4 | name = "containerInsights" 5 | value = "enabled" 6 | } 7 | tags = { 8 | Name = "${var.domain_root}-${var.Name_tag}-ecs-clstr" 9 | } 10 | } 11 | 12 | resource "aws_ecs_cluster_capacity_providers" "demo" { 13 | cluster_name = aws_ecs_cluster.demo.name 14 | 15 | capacity_providers = ["FARGATE"] 16 | 17 | default_capacity_provider_strategy { 18 | base = 1 19 | weight = 100 20 | capacity_provider = "FARGATE" 21 | } 22 | } 23 | 24 | data "template_file" "demo" { 25 | template = file("templates/demo.json.tpl") 26 | 27 | vars = { 28 | docker_image_url_demo-client = var.docker_image_url_demo-client 29 | docker_image_url_demo-nginx = var.docker_image_url_demo-nginx 30 | region = var.region 31 | demo_container_port = var.demo_container_port 32 | nginx_container_port = var.nginx_container_port 33 | Name_tag = var.Name_tag 34 | domain_root = var.domain_root 35 | } 36 | } 37 | 38 | resource "aws_ecs_task_definition" "demo" { 39 | family = "${var.domain_root}-${var.Name_tag}" 40 | requires_compatibilities = ["FARGATE"] 41 | network_mode = "awsvpc" 42 | cpu = var.demo_task_cpu 43 | memory = var.demo_task_memory 44 | execution_role_arn = aws_iam_role.tasks-service-role.arn 45 | task_role_arn = aws_iam_role.ecs_task_role.arn 46 | container_definitions = data.template_file.demo.rendered 47 | runtime_platform { 48 | operating_system_family = "LINUX" 49 | cpu_architecture = var.runtime_cpu_arch 50 | } 51 | 52 | tags = { 53 | Name = "${var.domain_root}-${var.Name_tag}-ecs-task" 54 | } 55 | } 56 | 57 | resource "aws_ecs_service" "demo-ecs-service" { 58 | name = "${var.domain_root}-${var.Name_tag}-ecs-srvc" 59 | cluster = aws_ecs_cluster.demo.id 60 | task_definition = aws_ecs_task_definition.demo.arn 61 | desired_count = var.task_count 62 | launch_type = "FARGATE" 63 | depends_on = [aws_alb_listener.demo-http-listener] 64 | 65 | network_configuration { 66 | security_groups = [aws_security_group.task-sg.id] 67 | subnets = aws_subnet.private.*.id 68 | } 69 | 70 | load_balancer { 71 | target_group_arn = aws_alb_target_group.demo-target-group.arn 72 | container_name = "${var.domain_root}-${var.Name_tag}-nginx" 73 | container_port = var.nginx_container_port 74 | } 75 | tags = { 76 | Name = "${var.domain_root}-${var.Name_tag}-ecs-srvc" 77 | } 78 | 79 | lifecycle { 80 | ignore_changes = [desired_count] 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /demo/terraform/iam.tf: -------------------------------------------------------------------------------- 1 | 2 | # --------------------------------------------------------------------------------------------------------------------- 3 | # ECS execution ROLE 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | resource "aws_iam_role" "tasks-service-role" { 6 | name = "${var.domain_root}-${var.Name_tag}-EcsTskSrvc" 7 | path = "/" 8 | assume_role_policy = data.aws_iam_policy_document.tasks-service-assume-policy.json 9 | } 10 | 11 | data "aws_iam_policy_document" "tasks-service-assume-policy" { 12 | statement { 13 | actions = ["sts:AssumeRole"] 14 | 15 | principals { 16 | type = "Service" 17 | identifiers = ["ecs-tasks.amazonaws.com"] 18 | } 19 | } 20 | } 21 | 22 | resource "aws_iam_role_policy_attachment" "tasks-service-role-attachment" { 23 | role = aws_iam_role.tasks-service-role.name 24 | policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" 25 | } 26 | # --------------------------------------------------------------------------------------------------------------------- 27 | # ECS task ROLE 28 | # --------------------------------------------------------------------------------------------------------------------- 29 | resource "aws_iam_role" "ecs_task_role" { 30 | name = "${var.domain_root}-${var.Name_tag}-EcsTsk" 31 | 32 | assume_role_policy = < 0:\n", 122 | " print(\"Beams: {}\".format(gedi04a[\"beam\"].unique()))" 123 | ] 124 | }, 125 | { 126 | "cell_type": "code", 127 | "execution_count": null, 128 | "id": "983fc7fe-0426-4f1f-9785-5d3a15dcc7b3", 129 | "metadata": { 130 | "tags": [] 131 | }, 132 | "outputs": [], 133 | "source": [ 134 | "gedi04a" 135 | ] 136 | }, 137 | { 138 | "cell_type": "markdown", 139 | "id": "87e57e7e-cafe-451c-8fee-cc2311884a4a", 140 | "metadata": {}, 141 | "source": [ 142 | "### Massage DataFrame: trim NaN and no-data rows and flatten samples" 143 | ] 144 | }, 145 | { 146 | "cell_type": "code", 147 | "execution_count": null, 148 | "id": "28df7f4d-085c-4296-8651-974075d20a0a", 149 | "metadata": { 150 | "tags": [] 151 | }, 152 | "outputs": [], 153 | "source": [ 154 | "gdf = gedi04a[gedi04a[\"3dep.value\"].notna()]" 155 | ] 156 | }, 157 | { 158 | "cell_type": "code", 159 | "execution_count": null, 160 | "id": "c63bea65-f811-4d74-8101-f1209cdfcb54", 161 | "metadata": { 162 | "tags": [] 163 | }, 164 | "outputs": [], 165 | "source": [ 166 | "def getFirstValue(x):\n", 167 | " if type(x[\"3dep.value\"]) == float:\n", 168 | " return x['3dep.value']\n", 169 | " else:\n", 170 | " return x['3dep.value'][0]\n", 171 | "gdf[\"3dep\"] = gdf.apply(lambda x: getFirstValue(x), axis=1)" 172 | ] 173 | }, 174 | { 175 | "cell_type": "code", 176 | "execution_count": null, 177 | "id": "b9f50ffd-cf9d-4d5f-8064-769156876861", 178 | "metadata": { 179 | "tags": [] 180 | }, 181 | "outputs": [], 182 | "source": [ 183 | "gdf = gdf[gdf[\"3dep\"] > -9999.0]" 184 | ] 185 | }, 186 | { 187 | "cell_type": "markdown", 188 | "id": "6ce24753-f025-4093-9193-b096549e28fc", 189 | "metadata": {}, 190 | "source": [ 191 | "### Plot elevations using coordinates" 192 | ] 193 | }, 194 | { 195 | "cell_type": "code", 196 | "execution_count": null, 197 | "id": "30801862-83b9-42ee-9f4c-e63585a851d9", 198 | "metadata": {}, 199 | "outputs": [], 200 | "source": [ 201 | "f, ax = plt.subplots(1, 2, figsize=[12,8])\n", 202 | "ax[0].set_title(\"GEDI\")\n", 203 | "ax[0].set_aspect('equal')\n", 204 | "gdf.plot(ax=ax[0], column='elevation', cmap='inferno', s=0.1)\n", 205 | "ax[1].set_title(\"3DEP\")\n", 206 | "ax[1].set_aspect('equal')\n", 207 | "gdf.plot(ax=ax[1], column='3dep', cmap='inferno', s=0.1)" 208 | ] 209 | }, 210 | { 211 | "cell_type": "markdown", 212 | "id": "f7abd8b2-e431-48c1-8271-f6e1f9a7c4c5", 213 | "metadata": {}, 214 | "source": [ 215 | "### Plot comparison of elevations" 216 | ] 217 | }, 218 | { 219 | "cell_type": "code", 220 | "execution_count": null, 221 | "id": "208e540d-2f2c-4d90-a8d1-58f376a42408", 222 | "metadata": { 223 | "tags": [] 224 | }, 225 | "outputs": [], 226 | "source": [ 227 | "fig,ax = plt.subplots(num=None, figsize=(10, 8))\n", 228 | "fig.set_facecolor('white')\n", 229 | "fig.canvas.header_visible = False\n", 230 | "ax.set_title(\"Elevations between GEDI and 3DEP\")\n", 231 | "ax.set_xlabel('UTC')\n", 232 | "ax.set_ylabel('height (m)')\n", 233 | "ax.yaxis.grid(True)\n", 234 | "sc1 = ax.scatter(gdf.index.values, gdf[\"elevation\"].values, c='blue', s=2.5)\n", 235 | "sc2 = ax.scatter(gdf.index.values, gdf[\"3dep\"].values, c='green', s=2.5)\n", 236 | "plt.show()" 237 | ] 238 | }, 239 | { 240 | "cell_type": "markdown", 241 | "id": "ae8a9ab1-48bf-44a1-9ba1-6446c0173c9a", 242 | "metadata": {}, 243 | "source": [ 244 | "### Plot Histogram of Differences" 245 | ] 246 | }, 247 | { 248 | "cell_type": "code", 249 | "execution_count": null, 250 | "id": "47a2e378-4bd1-460e-8e10-ee9c07d6de47", 251 | "metadata": { 252 | "tags": [] 253 | }, 254 | "outputs": [], 255 | "source": [ 256 | "gdf['elev_diff'] = gdf['elevation'] - gdf['3dep']" 257 | ] 258 | }, 259 | { 260 | "cell_type": "code", 261 | "execution_count": null, 262 | "id": "4d16ef5c-0fda-4e4c-ae3c-e7609fe4593f", 263 | "metadata": { 264 | "tags": [] 265 | }, 266 | "outputs": [], 267 | "source": [ 268 | "plt.hist(gdf['elev_diff'], bins=128)" 269 | ] 270 | }, 271 | { 272 | "cell_type": "code", 273 | "execution_count": null, 274 | "id": "480aad8a-3a54-4354-a15b-b429143f699b", 275 | "metadata": {}, 276 | "outputs": [], 277 | "source": [] 278 | } 279 | ], 280 | "metadata": { 281 | "kernelspec": { 282 | "display_name": "Python 3 (ipykernel)", 283 | "language": "python", 284 | "name": "python3" 285 | }, 286 | "language_info": { 287 | "codemirror_mode": { 288 | "name": "ipython", 289 | "version": 3 290 | }, 291 | "file_extension": ".py", 292 | "mimetype": "text/x-python", 293 | "name": "python", 294 | "nbconvert_exporter": "python", 295 | "pygments_lexer": "ipython3", 296 | "version": "3.12.0" 297 | } 298 | }, 299 | "nbformat": 4, 300 | "nbformat_minor": 5 301 | } 302 | -------------------------------------------------------------------------------- /examples/GLIMS_ATL06_Subsetter.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "ca561032-e4a4-4b7a-b01f-46048a33f6e7", 6 | "metadata": {}, 7 | "source": [ 8 | "## Subset ATL06 to GLIMS Shapefile\n", 9 | "\n", 10 | "This notebook uses SlideRule to retrieve ATL06 segments that intersect a provided shapefile.\n", 11 | "1. Generate the convex hull of the region of interest characterized by the shapefile so that we have a polygon to submit to CMR to get all ATL06 granules that could possibly intersect the region of interest.\n", 12 | "2. Convert the shapefile to a geojson, and in the process, buffer out the polygons so that no points are missed by SlideRule\n", 13 | "3. Make the processing request to SlideRule to retrieve all ATL06 segments within the region of interest\n", 14 | "4. Trim the returned values to the original shapefile to get rid of any segments that were only included in the bufferred region\n", 15 | "\n", 16 | "### Notes\n", 17 | "\n", 18 | "* SlideRule v4.6.2 and earlier versions have a bug in the ATL06 subsetter that does not handle geojson subsetting correctly. When running against a SlideRule cluster with this bug, a large amount of data is returned (which takes a long time), and is then substantially trimmed in the final steps. When running against a later version of SlideRule, the data returned from the server is substantially less and takes significantly less time.\n" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": null, 24 | "id": "4c841bde-7ece-492b-b749-bfc5c9397dbc", 25 | "metadata": {}, 26 | "outputs": [], 27 | "source": [ 28 | "from sliderule import sliderule, icesat2, earthdata\n", 29 | "from shapely.geometry import Polygon, MultiPolygon, mapping\n", 30 | "import geopandas as gpd\n", 31 | "import geojson" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": null, 37 | "id": "2a6d95a3-de3b-418a-b2db-62e77887e202", 38 | "metadata": { 39 | "scrolled": true 40 | }, 41 | "outputs": [], 42 | "source": [ 43 | "#\n", 44 | "# If *.shx file is missing, run this cell to generate\n", 45 | "#\n", 46 | "#import fiona\n", 47 | "#with fiona.Env(SHAPE_RESTORE_SHX='YES'):\n", 48 | "# region = sliderule.toregion(\"glims_polygons.shp\")" 49 | ] 50 | }, 51 | { 52 | "cell_type": "markdown", 53 | "id": "bdbb24ce-841c-44aa-a2c7-0c20c2069245", 54 | "metadata": {}, 55 | "source": [ 56 | "### Read in shapefile" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": null, 62 | "id": "f5ac416c-452e-4a04-a578-eae34fda5959", 63 | "metadata": {}, 64 | "outputs": [], 65 | "source": [ 66 | "# read shapefile\n", 67 | "gdf = gpd.read_file(\"glims_polygons.shp\")" 68 | ] 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "id": "114393da-6e0e-4604-9f8e-01c8aa83332d", 73 | "metadata": {}, 74 | "source": [ 75 | "### Get granules that intersect larger area of interest" 76 | ] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "execution_count": null, 81 | "id": "d937c909-3cb3-4e63-b159-2683b3069acd", 82 | "metadata": {}, 83 | "outputs": [], 84 | "source": [ 85 | "# create a multipolygon with simplified internal polygons (needed to get convex hull)\n", 86 | "polygons = list(gdf.geometry)\n", 87 | "cleaned_polygons = [polygon.convex_hull for polygon in polygons]\n", 88 | "cleaned_multipoly = MultiPolygon(cleaned_polygons)" 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": null, 94 | "id": "fe62d6e4-66bd-4ab0-8bd6-d24fcd74deb9", 95 | "metadata": {}, 96 | "outputs": [], 97 | "source": [ 98 | "# build geojson of multipolygon\n", 99 | "cleaned_glims_geojson = \"cleaned_glims.geojson\"\n", 100 | "geojson_obj = geojson.Feature(geometry=mapping(cleaned_multipoly))\n", 101 | "with open(cleaned_glims_geojson, \"w\") as geojson_file:\n", 102 | " geojson.dump(geojson_obj, geojson_file)" 103 | ] 104 | }, 105 | { 106 | "cell_type": "code", 107 | "execution_count": null, 108 | "id": "924fcb89-2c2e-4389-8573-ba528eb6e64d", 109 | "metadata": { 110 | "scrolled": true 111 | }, 112 | "outputs": [], 113 | "source": [ 114 | "# get sliderule region of geojson\n", 115 | "region = sliderule.toregion(cleaned_glims_geojson)" 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": null, 121 | "id": "87f24364-9e76-4459-a906-9310a5ef0712", 122 | "metadata": { 123 | "scrolled": true 124 | }, 125 | "outputs": [], 126 | "source": [ 127 | "# query CMR for granules that intersect larger area of interest\n", 128 | "cmr_parms = {\n", 129 | " \"asset\": \"icesat2-atl06\",\n", 130 | " \"poly\": region[\"poly\"]\n", 131 | "}\n", 132 | "earthdata.set_max_resources(350)\n", 133 | "granules = earthdata.search(cmr_parms)" 134 | ] 135 | }, 136 | { 137 | "cell_type": "code", 138 | "execution_count": null, 139 | "id": "9d75d509-7e1b-4730-821a-db855e7df300", 140 | "metadata": { 141 | "scrolled": true 142 | }, 143 | "outputs": [], 144 | "source": [ 145 | "granules" 146 | ] 147 | }, 148 | { 149 | "cell_type": "markdown", 150 | "id": "235303b9-ebac-4bf5-90df-c3a2a0011885", 151 | "metadata": {}, 152 | "source": [ 153 | "### Get detailed geojson of area of interest" 154 | ] 155 | }, 156 | { 157 | "cell_type": "code", 158 | "execution_count": null, 159 | "id": "030ef8a6-8dc8-43cd-8272-66aae250404a", 160 | "metadata": {}, 161 | "outputs": [], 162 | "source": [ 163 | "# create a multipolygon of internal polygons\n", 164 | "multipoly = MultiPolygon(list(gdf.geometry))" 165 | ] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "execution_count": null, 170 | "id": "c1b2745c-601a-455d-95e8-e4c2df9690ba", 171 | "metadata": {}, 172 | "outputs": [], 173 | "source": [ 174 | "# buffer out multiplygon\n", 175 | "buffered_multipoly = multipoly.buffer(0.01)" 176 | ] 177 | }, 178 | { 179 | "cell_type": "code", 180 | "execution_count": null, 181 | "id": "13e68d5f-7cc9-44da-82e1-19317776b7b5", 182 | "metadata": {}, 183 | "outputs": [], 184 | "source": [ 185 | "# build geojson of multipolygon\n", 186 | "glims_geojson = \"glims.geojson\"\n", 187 | "geojson_obj = geojson.Feature(geometry=mapping(buffered_multipoly))\n", 188 | "with open(glims_geojson, \"w\") as geojson_file:\n", 189 | " geojson.dump(geojson_obj, geojson_file)" 190 | ] 191 | }, 192 | { 193 | "cell_type": "code", 194 | "execution_count": null, 195 | "id": "231320d1-2266-4300-a43a-749b4f8a2a89", 196 | "metadata": {}, 197 | "outputs": [], 198 | "source": [ 199 | "g = gpd.read_file(\"glims.geojson\")\n", 200 | "g.plot(markersize=1)" 201 | ] 202 | }, 203 | { 204 | "cell_type": "code", 205 | "execution_count": null, 206 | "id": "684aa4f8-badd-47cc-b7b4-f743c23646d0", 207 | "metadata": {}, 208 | "outputs": [], 209 | "source": [ 210 | "# open the geojson and read in as raw bytes\n", 211 | "with open(glims_geojson, mode='rt') as file:\n", 212 | " datafile = file.read()" 213 | ] 214 | }, 215 | { 216 | "cell_type": "code", 217 | "execution_count": null, 218 | "id": "f1e64edc-57cb-4f15-b812-cca4667b727c", 219 | "metadata": {}, 220 | "outputs": [], 221 | "source": [ 222 | "# build the raster parameters for sliderule\n", 223 | "cellsize = 0.001\n", 224 | "raster = {\n", 225 | " \"data\": datafile, # geojson file\n", 226 | " \"length\": len(datafile), # geojson file length\n", 227 | " \"cellsize\": cellsize # units are in crs/projection\n", 228 | "}" 229 | ] 230 | }, 231 | { 232 | "cell_type": "markdown", 233 | "id": "0d088011-8858-47d0-89ba-747719397365", 234 | "metadata": {}, 235 | "source": [ 236 | "### Use sliderule to generate subsetted ATL06 over area of interest" 237 | ] 238 | }, 239 | { 240 | "cell_type": "code", 241 | "execution_count": null, 242 | "id": "1518295c-f429-4656-93b8-00f52c52d076", 243 | "metadata": {}, 244 | "outputs": [], 245 | "source": [ 246 | "# initialize the client\n", 247 | "sliderule.init(\"slideruleearth.io\", verbose=True)" 248 | ] 249 | }, 250 | { 251 | "cell_type": "code", 252 | "execution_count": null, 253 | "id": "659797d8-7991-4684-a64f-d8fe4a12b457", 254 | "metadata": { 255 | "scrolled": true 256 | }, 257 | "outputs": [], 258 | "source": [ 259 | "# atl06 subsetting parameters\n", 260 | "atl06_parms = {\n", 261 | " \"poly\": region[\"poly\"],\n", 262 | " \"raster\": raster,\n", 263 | "}" 264 | ] 265 | }, 266 | { 267 | "cell_type": "code", 268 | "execution_count": null, 269 | "id": "07bcd282-ff63-448e-921d-e924e41343ee", 270 | "metadata": { 271 | "scrolled": true 272 | }, 273 | "outputs": [], 274 | "source": [ 275 | "# make processing request\n", 276 | "atl06 = icesat2.atl06sp(atl06_parms, resources=granules)" 277 | ] 278 | }, 279 | { 280 | "cell_type": "code", 281 | "execution_count": null, 282 | "id": "6d133f19-5006-4bc8-90a4-fae1f348b54d", 283 | "metadata": {}, 284 | "outputs": [], 285 | "source": [ 286 | "# display results\n", 287 | "atl06" 288 | ] 289 | }, 290 | { 291 | "cell_type": "code", 292 | "execution_count": null, 293 | "id": "c9c38697-d5a0-4761-b969-fac87d1a996e", 294 | "metadata": {}, 295 | "outputs": [], 296 | "source": [ 297 | "# plot results\n", 298 | "atl06.plot(markersize=1)" 299 | ] 300 | }, 301 | { 302 | "cell_type": "code", 303 | "execution_count": null, 304 | "id": "992d200b-9951-44d0-8eea-313274fbbb2e", 305 | "metadata": {}, 306 | "outputs": [], 307 | "source": [ 308 | "# save results to a geoparquet file\n", 309 | "atl06.to_parquet(\"glims_atl06.geoparquet\")" 310 | ] 311 | }, 312 | { 313 | "cell_type": "markdown", 314 | "id": "d889e500-1309-45ae-b267-de8e4acc78af", 315 | "metadata": {}, 316 | "source": [ 317 | "### Trim the output to GLIMS polygons\n", 318 | "The subsetting on SlideRule used a buffered multipolygon so that it wouldn't miss any data. The steps below further trim the data to the exact region of interest." 319 | ] 320 | }, 321 | { 322 | "cell_type": "code", 323 | "execution_count": null, 324 | "id": "7c093b5b-9b03-45f5-b6a1-7d7c815d1c26", 325 | "metadata": {}, 326 | "outputs": [], 327 | "source": [ 328 | "# read data from geoparquet file, set ICESat-2 crs\n", 329 | "atl06rb = gpd.read_parquet(\"glims_atl06.geoparquet\")\n", 330 | "gdf = gdf.set_crs(\"EPSG:7912\", allow_override=True)" 331 | ] 332 | }, 333 | { 334 | "cell_type": "code", 335 | "execution_count": null, 336 | "id": "4943490e-69f3-4506-93bc-d31e5357ec91", 337 | "metadata": {}, 338 | "outputs": [], 339 | "source": [ 340 | "# trim geodataframe to initial shapefile\n", 341 | "trimmed_gdf = gpd.sjoin(atl06rb, gdf, how='inner', predicate='within')" 342 | ] 343 | }, 344 | { 345 | "cell_type": "code", 346 | "execution_count": null, 347 | "id": "ea8fcf0e-9f46-422d-b6ce-482b187b2f2b", 348 | "metadata": {}, 349 | "outputs": [], 350 | "source": [ 351 | "# plot trimmed results\n", 352 | "trimmed_gdf.plot(markersize=1)" 353 | ] 354 | }, 355 | { 356 | "cell_type": "code", 357 | "execution_count": null, 358 | "id": "849915e4-927a-41f7-a8b3-9a5de8297bda", 359 | "metadata": {}, 360 | "outputs": [], 361 | "source": [ 362 | "# save trimmed results\n", 363 | "trimmed_gdf.to_parquet(\"glims_subsetted_atl06.geoparquet\")" 364 | ] 365 | }, 366 | { 367 | "cell_type": "code", 368 | "execution_count": null, 369 | "id": "77ca35d4-d510-41ed-8c5b-018b77d451ff", 370 | "metadata": {}, 371 | "outputs": [], 372 | "source": [ 373 | "# display trimmed results\n", 374 | "trimmed_gdf" 375 | ] 376 | }, 377 | { 378 | "cell_type": "code", 379 | "execution_count": null, 380 | "id": "a0029f4c-ac1e-4985-b73b-01903132c73c", 381 | "metadata": {}, 382 | "outputs": [], 383 | "source": [] 384 | } 385 | ], 386 | "metadata": { 387 | "kernelspec": { 388 | "display_name": "Python 3 (ipykernel)", 389 | "language": "python", 390 | "name": "python3" 391 | }, 392 | "language_info": { 393 | "codemirror_mode": { 394 | "name": "ipython", 395 | "version": 3 396 | }, 397 | "file_extension": ".py", 398 | "mimetype": "text/x-python", 399 | "name": "python", 400 | "nbconvert_exporter": "python", 401 | "pygments_lexer": "ipython3", 402 | "version": "3.12.0" 403 | } 404 | }, 405 | "nbformat": 4, 406 | "nbformat_minor": 5 407 | } 408 | -------------------------------------------------------------------------------- /examples/arcticdem_mosaic.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "29ec1570-d65b-4e52-9d4d-d93604882190", 6 | "metadata": {}, 7 | "source": [ 8 | "## ArcticDEM Mosaic Example\n", 9 | "\n", 10 | "### Purpose\n", 11 | "Demonstrate how to sample the ArcticDEM at generated ATL06-SR points" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "id": "e29fa51f-77bf-4c55-a99e-a4f166833755", 17 | "metadata": {}, 18 | "source": [ 19 | "#### Import Packages" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": null, 25 | "id": "d58f8efa-e074-4baf-9bdf-51d819082844", 26 | "metadata": { 27 | "tags": [] 28 | }, 29 | "outputs": [], 30 | "source": [ 31 | "import warnings\n", 32 | "warnings.filterwarnings(\"ignore\") # suppress warnings" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": null, 38 | "id": "fc573692-19b2-41d2-9575-9b42d1ea1031", 39 | "metadata": { 40 | "tags": [] 41 | }, 42 | "outputs": [], 43 | "source": [ 44 | "import matplotlib.pyplot as plt\n", 45 | "import matplotlib\n", 46 | "import sliderule\n", 47 | "from sliderule import icesat2" 48 | ] 49 | }, 50 | { 51 | "cell_type": "markdown", 52 | "id": "53e68348-2d49-4e22-b665-1acd8b367dcf", 53 | "metadata": {}, 54 | "source": [ 55 | "#### Initialize SlideRule Python Client" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": null, 61 | "id": "93edfc47-1cd5-4927-962c-fd447c9e807a", 62 | "metadata": { 63 | "tags": [] 64 | }, 65 | "outputs": [], 66 | "source": [ 67 | "icesat2.init(\"slideruleearth.io\", verbose=True)" 68 | ] 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "id": "c588e3ea-8ab8-452b-8f5a-9fd8d6364ca9", 73 | "metadata": { 74 | "tags": [] 75 | }, 76 | "source": [ 77 | "#### Make Processing Request to SlideRule\n", 78 | "ATL06-SR request includes the `samples` parameter to specify that ArcticDEM Mosiac dataset should be sampled at each generated ATL06 elevation." 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": null, 84 | "id": "4ebef6dc-c05d-4b97-973c-05da9565e841", 85 | "metadata": { 86 | "tags": [] 87 | }, 88 | "outputs": [], 89 | "source": [ 90 | "resource = \"ATL03_20190314093716_11600203_005_01.h5\"\n", 91 | "region = sliderule.toregion(\"../data/dicksonfjord.geojson\")\n", 92 | "parms = { \"poly\": region['poly'],\n", 93 | " \"cnf\": \"atl03_high\",\n", 94 | " \"ats\": 5.0,\n", 95 | " \"cnt\": 5,\n", 96 | " \"len\": 20.0,\n", 97 | " \"res\": 10.0,\n", 98 | " \"samples\": {\"mosaic\": {\"asset\": \"arcticdem-mosaic\", \"radius\": 10.0, \"zonal_stats\": True}} }\n", 99 | "gdf = icesat2.atl06p(parms, resources=[resource])" 100 | ] 101 | }, 102 | { 103 | "cell_type": "markdown", 104 | "id": "b779ddf2-f9ea-41c2-bb9a-1db92e277fe7", 105 | "metadata": {}, 106 | "source": [ 107 | "#### Display GeoDataFrame\n", 108 | "Notice the columns that start with \"mosaic\"" 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": null, 114 | "id": "e19bae20-140e-4d55-bb73-64a9630096d1", 115 | "metadata": { 116 | "tags": [] 117 | }, 118 | "outputs": [], 119 | "source": [ 120 | "gdf" 121 | ] 122 | }, 123 | { 124 | "cell_type": "markdown", 125 | "id": "6178683e-2d08-4ccb-a80e-4bb997876330", 126 | "metadata": {}, 127 | "source": [ 128 | "#### Print Out File Directory\n", 129 | "When a GeoDataFrame includes samples from rasters, each sample value has a file id that is used to look up the file name of the source raster for that value." 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": null, 135 | "id": "b4c99349-c44e-4e59-bd31-ad6121df2f80", 136 | "metadata": {}, 137 | "outputs": [], 138 | "source": [ 139 | "gdf.attrs['file_directory']" 140 | ] 141 | }, 142 | { 143 | "cell_type": "markdown", 144 | "id": "13185f3c-23f8-4334-a300-68c39284711c", 145 | "metadata": {}, 146 | "source": [ 147 | "#### Demonstrate How To Access Source Raster Filename for Entry in GeoDataFrame" 148 | ] 149 | }, 150 | { 151 | "cell_type": "code", 152 | "execution_count": null, 153 | "id": "02ddb59c-b63b-4fef-b8c4-ec4b3d7580c6", 154 | "metadata": {}, 155 | "outputs": [], 156 | "source": [ 157 | "filedir = gdf.attrs['file_directory']\n", 158 | "filedir[gdf['mosaic.file_id'][0]]" 159 | ] 160 | }, 161 | { 162 | "cell_type": "markdown", 163 | "id": "88c529c1-9d72-4628-8b34-d850ae9e262d", 164 | "metadata": {}, 165 | "source": [ 166 | "#### Difference the Sampled Value from ArcticDEM with SlideRule ATL06-SR" 167 | ] 168 | }, 169 | { 170 | "cell_type": "code", 171 | "execution_count": null, 172 | "id": "f3476592-b5b2-470e-bd35-d63e23e42ca0", 173 | "metadata": {}, 174 | "outputs": [], 175 | "source": [ 176 | "gdf[\"value_delta\"] = gdf[\"h_mean\"] - gdf[\"mosaic.value\"]\n", 177 | "gdf[\"value_delta\"].describe()" 178 | ] 179 | }, 180 | { 181 | "cell_type": "markdown", 182 | "id": "5447dd00-69fa-4ab7-a2f3-674bf72126e9", 183 | "metadata": {}, 184 | "source": [ 185 | "#### Difference the Zonal Statistic Mean from ArcticDEM with SlideRule ATL06-SR" 186 | ] 187 | }, 188 | { 189 | "cell_type": "code", 190 | "execution_count": null, 191 | "id": "54621607-cdbc-4849-8e65-530957c2adc9", 192 | "metadata": {}, 193 | "outputs": [], 194 | "source": [ 195 | "gdf[\"mean_delta\"] = gdf[\"h_mean\"] - gdf[\"mosaic.mean\"]\n", 196 | "gdf[\"mean_delta\"].describe()" 197 | ] 198 | }, 199 | { 200 | "cell_type": "markdown", 201 | "id": "279dded2-5165-4b88-b28d-971fa303966d", 202 | "metadata": {}, 203 | "source": [ 204 | "#### Difference the Zonal Statistic Mdeian from ArcticDEM with SlideRule ATL06-SR" 205 | ] 206 | }, 207 | { 208 | "cell_type": "code", 209 | "execution_count": null, 210 | "id": "fafc2593-f6b4-44c1-8fb9-a9d345c6561e", 211 | "metadata": {}, 212 | "outputs": [], 213 | "source": [ 214 | "gdf[\"median_delta\"] = gdf[\"h_mean\"] - gdf[\"mosaic.median\"]\n", 215 | "gdf[\"median_delta\"].describe()" 216 | ] 217 | }, 218 | { 219 | "cell_type": "markdown", 220 | "id": "32beb064-f10f-46e1-8756-a03756e069fd", 221 | "metadata": {}, 222 | "source": [ 223 | "#### Plot the Different ArcticDEM Values against the SlideRule ATL06-SR Values" 224 | ] 225 | }, 226 | { 227 | "cell_type": "code", 228 | "execution_count": null, 229 | "id": "12645d05-fda6-44bd-878b-37b0aa217065", 230 | "metadata": {}, 231 | "outputs": [], 232 | "source": [ 233 | "# Setup Plot\n", 234 | "fig,ax = plt.subplots(num=None, figsize=(10, 8))\n", 235 | "fig.set_facecolor('white')\n", 236 | "fig.canvas.header_visible = False\n", 237 | "ax.set_title(\"SlideRule vs. ArcticDEM Elevations\")\n", 238 | "ax.set_xlabel('UTC')\n", 239 | "ax.set_ylabel('height (m)')\n", 240 | "legend_elements = []\n", 241 | "\n", 242 | "# Plot SlideRule ATL06 Elevations\n", 243 | "df = gdf[(gdf['rgt'] == 1160) & (gdf['gt'] == 10) & (gdf['cycle'] == 2)]\n", 244 | "sc1 = ax.scatter(df.index.values, df[\"h_mean\"].values, c='red', s=2.5)\n", 245 | "legend_elements.append(matplotlib.lines.Line2D([0], [0], color='red', lw=6, label='ATL06-SR'))\n", 246 | "\n", 247 | "# Plot ArcticDEM Elevations\n", 248 | "sc2 = ax.scatter(df.index.values, df[\"mosaic.value\"].values, c='blue', s=2.5)\n", 249 | "legend_elements.append(matplotlib.lines.Line2D([0], [0], color='blue', lw=6, label='ArcticDEM'))\n", 250 | "\n", 251 | "# Display Legend\n", 252 | "lgd = ax.legend(handles=legend_elements, loc=3, frameon=True)\n", 253 | "lgd.get_frame().set_alpha(1.0)\n", 254 | "lgd.get_frame().set_edgecolor('white')\n", 255 | "\n", 256 | "# Show Plot\n", 257 | "plt.show()" 258 | ] 259 | }, 260 | { 261 | "cell_type": "markdown", 262 | "id": "343ad4b0-e94b-48bb-ae23-ca57867597fb", 263 | "metadata": {}, 264 | "source": [ 265 | "#### Plot the Sampled Value and Zonal Statistic Mean Deltas to SlideRule ATL06-SR Values" 266 | ] 267 | }, 268 | { 269 | "cell_type": "code", 270 | "execution_count": null, 271 | "id": "7154e9db-ff4d-4b17-ac8c-62c3d12d7d54", 272 | "metadata": {}, 273 | "outputs": [], 274 | "source": [ 275 | "# Setup Plot\n", 276 | "fig,ax = plt.subplots(num=None, figsize=(10, 8))\n", 277 | "fig.set_facecolor('white')\n", 278 | "fig.canvas.header_visible = False\n", 279 | "ax.set_title(\"Delta Elevations between SlideRule and ArcticDEM\")\n", 280 | "ax.set_xlabel('UTC')\n", 281 | "ax.set_ylabel('height (m)')\n", 282 | "ax.yaxis.grid(True)\n", 283 | "\n", 284 | "# Plot Deltas\n", 285 | "df1 = gdf[(gdf['rgt'] == 1160) & (gdf['gt'] == 10) & (gdf['cycle'] == 2)]\n", 286 | "sc1 = ax.scatter(df1.index.values, df1[\"value_delta\"].values, c='blue', s=2.5)\n", 287 | "\n", 288 | "# Plot Deltas\n", 289 | "df2 = gdf[(gdf['rgt'] == 1160) & (gdf['gt'] == 10) & (gdf['cycle'] == 2)]\n", 290 | "sc2 = ax.scatter(df2.index.values, df2[\"mean_delta\"].values, c='green', s=2.5)\n", 291 | "\n", 292 | "# Show Plot\n", 293 | "plt.show()" 294 | ] 295 | }, 296 | { 297 | "cell_type": "code", 298 | "execution_count": null, 299 | "id": "eed8f243-dd0c-4473-a952-fcb2bb863e3c", 300 | "metadata": {}, 301 | "outputs": [], 302 | "source": [] 303 | } 304 | ], 305 | "metadata": { 306 | "kernelspec": { 307 | "display_name": "Python 3 (ipykernel)", 308 | "language": "python", 309 | "name": "python3" 310 | }, 311 | "language_info": { 312 | "codemirror_mode": { 313 | "name": "ipython", 314 | "version": 3 315 | }, 316 | "file_extension": ".py", 317 | "mimetype": "text/x-python", 318 | "name": "python", 319 | "nbconvert_exporter": "python", 320 | "pygments_lexer": "ipython3", 321 | "version": "3.12.0" 322 | } 323 | }, 324 | "nbformat": 4, 325 | "nbformat_minor": 5 326 | } 327 | -------------------------------------------------------------------------------- /examples/arcticdem_strip_boundaries.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "bd51a330-73c7-494a-b3d9-3f6f07935604", 6 | "metadata": {}, 7 | "source": [ 8 | "## ArcticDEM Strips Example\n", 9 | "\n", 10 | "### Purpose\n", 11 | "Demonstrate how to work with individual strips when sampling ArcticDEM at ATL06-SR points\n", 12 | "\n", 13 | "### Prerequisites\n", 14 | "1. Access to the PGC S3 bucket `pgc-opendata-dems`\n", 15 | "2. `gdalinfo` tool installed local to jupyter lab" 16 | ] 17 | }, 18 | { 19 | "cell_type": "markdown", 20 | "id": "a12e5062-ed39-4694-ab2a-53ba804f34ec", 21 | "metadata": {}, 22 | "source": [ 23 | "#### Import Packages" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": null, 29 | "id": "9af774d2-b404-4c52-b932-83992803675f", 30 | "metadata": { 31 | "tags": [] 32 | }, 33 | "outputs": [], 34 | "source": [ 35 | "import warnings\n", 36 | "warnings.filterwarnings(\"ignore\") # suppress warnings" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": null, 42 | "id": "d7be24a2-d09a-4ed3-8252-e010bd5be5b9", 43 | "metadata": { 44 | "tags": [] 45 | }, 46 | "outputs": [], 47 | "source": [ 48 | "import sliderule\n", 49 | "from sliderule import icesat2\n", 50 | "import matplotlib\n", 51 | "import matplotlib.pyplot as plt\n", 52 | "import pandas as pd\n", 53 | "import numpy as np\n", 54 | "import pyproj\n", 55 | "import re\n", 56 | "import os" 57 | ] 58 | }, 59 | { 60 | "cell_type": "markdown", 61 | "id": "1ef5f6de-1454-4c50-82f9-d01c252aede9", 62 | "metadata": {}, 63 | "source": [ 64 | "#### Initialize Python Client" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": null, 70 | "id": "48c7b8ca-74bf-4998-965d-d8b871a10c51", 71 | "metadata": { 72 | "tags": [] 73 | }, 74 | "outputs": [], 75 | "source": [ 76 | "icesat2.init(\"slideruleearth.io\", verbose=True)" 77 | ] 78 | }, 79 | { 80 | "cell_type": "markdown", 81 | "id": "751c7ba4-3985-4d84-a98b-89eae6346dd7", 82 | "metadata": {}, 83 | "source": [ 84 | "#### Build Region of Interest" 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": null, 90 | "id": "04333ea1-3564-4657-a392-bee8d0c5de62", 91 | "metadata": { 92 | "tags": [] 93 | }, 94 | "outputs": [], 95 | "source": [ 96 | "xy0=np.array([ -73000., -2683000.])\n", 97 | "transformer = pyproj.Transformer.from_crs(3413, 4326)\n", 98 | "xyB=[xy0[0]+np.array([-1, 1, 1, -1, -1])*1.e4, xy0[1]+np.array([-1, -1, 1, 1, -1])*1.e4]\n", 99 | "llB=transformer.transform(*xyB)\n", 100 | "poly=[{'lat':lat,'lon':lon} for lat, lon in zip(*llB)]\n", 101 | "plist = []\n", 102 | "for p in poly:\n", 103 | " plist += p[\"lat\"], \n", 104 | " plist += p[\"lon\"],\n", 105 | "region_of_interest = sliderule.toregion(plist)\n", 106 | "region_of_interest[\"gdf\"].plot()" 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "id": "ea351ded-8069-4f0c-a4bc-c41854e5f583", 112 | "metadata": {}, 113 | "source": [ 114 | "#### Make Processing Request\n", 115 | "ATL06-SR request includes the `samples` parameter to specify that ArcticDEM Strips dataset should be sampled at each generated ATL06 elevation." 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": null, 121 | "id": "79ad73d6-ece4-4ebd-ac90-77c4d7cf006f", 122 | "metadata": { 123 | "tags": [] 124 | }, 125 | "outputs": [], 126 | "source": [ 127 | "parms = { \"poly\": region_of_interest[\"poly\"],\n", 128 | " \"cnf\": \"atl03_high\",\n", 129 | " \"ats\": 10.0,\n", 130 | " \"cnt\": 5,\n", 131 | " \"len\": 40.0,\n", 132 | " \"res\": 120.0,\n", 133 | " \"rgt\": 658,\n", 134 | " \"time_start\":'2020-01-01',\n", 135 | " \"time_end\":'2021-01-01',\n", 136 | " \"samples\": {\"strips\": {\"asset\": \"arcticdem-strips\", \"with_flags\": True}} }\n", 137 | "gdf = icesat2.atl06p(parms)" 138 | ] 139 | }, 140 | { 141 | "cell_type": "markdown", 142 | "id": "cf78cf33-9ed1-402f-b2c9-be8604ed823e", 143 | "metadata": { 144 | "tags": [] 145 | }, 146 | "source": [ 147 | "#### Print Out File Directory\n", 148 | "When a GeoDataFrame includes samples from rasters, each sample value has a file id that is used to look up the file name of the source raster for that value." 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": null, 154 | "id": "e330379c-3890-4314-bb74-9780e549d724", 155 | "metadata": { 156 | "tags": [] 157 | }, 158 | "outputs": [], 159 | "source": [ 160 | "gdf" 161 | ] 162 | }, 163 | { 164 | "cell_type": "code", 165 | "execution_count": null, 166 | "id": "19c9060d-c502-425c-99c4-7d55a9780ef5", 167 | "metadata": { 168 | "tags": [] 169 | }, 170 | "outputs": [], 171 | "source": [ 172 | "gdf.attrs['file_directory']" 173 | ] 174 | }, 175 | { 176 | "cell_type": "markdown", 177 | "id": "6dca9849-713f-4c7a-a68e-e6b782fac3b0", 178 | "metadata": { 179 | "tags": [] 180 | }, 181 | "source": [ 182 | "#### Pull Out Bounding Box of Raster\n", 183 | "This step requires AWS credentials to be able to access S3 and `gdalinfo` be installed on the host machine to read the bounding box for each raster sampled by SlideRule." 184 | ] 185 | }, 186 | { 187 | "cell_type": "code", 188 | "execution_count": null, 189 | "id": "6a97e1d1-ff1c-4068-a305-04959faf5d49", 190 | "metadata": { 191 | "tags": [] 192 | }, 193 | "outputs": [], 194 | "source": [ 195 | "# helper functions\n", 196 | "def getXY(line):\n", 197 | " line = line.replace(\"(\",\"$\")\n", 198 | " line = line.replace(\")\",\"$\")\n", 199 | " point = line.split(\"$\")[1]\n", 200 | " coord = point.split(\",\")\n", 201 | " x = float(coord[0].strip())\n", 202 | " y = float(coord[1].strip())\n", 203 | " return x, y\n", 204 | "\n", 205 | "def getLonLat(line):\n", 206 | " line = line.replace(\"(\",\"$\")\n", 207 | " line = line.replace(\")\",\"$\")\n", 208 | " point = line.split(\"$\")[3]\n", 209 | " coord = point.split(\",\")\n", 210 | " deg, minutes, seconds, direction = re.split('[d\\'\"]', coord[1].strip())\n", 211 | " lon = (float(deg) + float(minutes)/60 + float(seconds)/(60*60)) * (-1 if direction in ['W', 'S'] else 1)\n", 212 | " deg, minutes, seconds, direction = re.split('[d\\'\"]', coord[0].strip())\n", 213 | " lat = (float(deg) + float(minutes)/60 + float(seconds)/(60*60)) * (-1 if direction in ['W', 'S'] else 1)\n", 214 | " return [lon, lat]\n", 215 | "\n", 216 | "def getBB(dem):\n", 217 | " os.system(\"gdalinfo {} > /tmp/r.txt\".format(dem))\n", 218 | " with open(\"/tmp/r.txt\", \"r\") as file:\n", 219 | " lines = file.readlines()\n", 220 | " for line in lines:\n", 221 | " if \"Upper Left\" in line:\n", 222 | " ul = getLonLat(line)\n", 223 | " elif \"Lower Left\" in line:\n", 224 | " ll = getLonLat(line)\n", 225 | " elif \"Upper Right\" in line:\n", 226 | " ur = getLonLat(line)\n", 227 | " elif \"Lower Right\" in line:\n", 228 | " lr = getLonLat(line)\n", 229 | " return ul + ll + lr + ur + ul" 230 | ] 231 | }, 232 | { 233 | "cell_type": "code", 234 | "execution_count": null, 235 | "id": "ce2fff54-c837-48d1-96c1-a396f01b54ff", 236 | "metadata": { 237 | "tags": [] 238 | }, 239 | "outputs": [], 240 | "source": [ 241 | "file_dict = {}\n", 242 | "for file_id, file_name in gdf.attrs['file_directory'].items():\n", 243 | " if \"bitmask\" in file_name:\n", 244 | " continue\n", 245 | " if file_name not in file_dict:\n", 246 | " file_dict[file_name] = {\"ids\": []}\n", 247 | " file_dict[file_name][\"ids\"].append(file_id)" 248 | ] 249 | }, 250 | { 251 | "cell_type": "code", 252 | "execution_count": null, 253 | "id": "8fd83146-6f4c-4ee1-91b6-6187ea2f5646", 254 | "metadata": { 255 | "tags": [] 256 | }, 257 | "outputs": [], 258 | "source": [ 259 | "# get boundaries for each raster\n", 260 | "for file_name in file_dict:\n", 261 | " print(\"Retrieving raster info for:\", file_name)\n", 262 | " rlist = getBB(file_name)\n", 263 | " file_dict[file_name][\"region\"] = sliderule.toregion(rlist)" 264 | ] 265 | }, 266 | { 267 | "cell_type": "markdown", 268 | "id": "b65bb3d5-de72-4d03-aada-ffd394741f6a", 269 | "metadata": { 270 | "tags": [] 271 | }, 272 | "source": [ 273 | "#### Pull Out Individual DEM Values and Put in Separate Columns" 274 | ] 275 | }, 276 | { 277 | "cell_type": "code", 278 | "execution_count": null, 279 | "id": "478f0a19-2aa7-44ea-8c81-3b4b0c8657da", 280 | "metadata": { 281 | "tags": [] 282 | }, 283 | "outputs": [], 284 | "source": [ 285 | "def getValue(x, file_ids):\n", 286 | " for file_id in file_ids:\n", 287 | " l = np.where(x['strips.file_id'] == file_id)[0]\n", 288 | " if len(l) == 1:\n", 289 | " return x['strips.value'][l[0]]\n", 290 | " return None\n", 291 | "sampled_data = gdf[gdf['strips.time'].notnull()]\n", 292 | "for file_name in file_dict:\n", 293 | " sampled_data[file_name] = sampled_data.apply(lambda x: getValue(x, file_dict[file_name][\"ids\"]), axis=1)" 294 | ] 295 | }, 296 | { 297 | "cell_type": "markdown", 298 | "id": "574fb9af-6db6-44e2-9a7a-1fab84a15f51", 299 | "metadata": {}, 300 | "source": [ 301 | "#### Plot Overlays of Boundaries and Returns" 302 | ] 303 | }, 304 | { 305 | "cell_type": "code", 306 | "execution_count": null, 307 | "id": "ec498d91-77dd-415b-b0fe-8125d0bd6655", 308 | "metadata": { 309 | "tags": [] 310 | }, 311 | "outputs": [], 312 | "source": [ 313 | "import warnings\n", 314 | "with warnings.catch_warnings():\n", 315 | " warnings.simplefilter(\"ignore\")\n", 316 | " fig = plt.figure(num=None, figsize=(24, 24))\n", 317 | " region_lons = [p[\"lon\"] for p in region_of_interest[\"poly\"]]\n", 318 | " region_lats = [p[\"lat\"] for p in region_of_interest[\"poly\"]]\n", 319 | " ax = {}\n", 320 | " k = 0\n", 321 | " for file_name in file_dict:\n", 322 | " raster = file_dict[file_name]\n", 323 | " raster_lons = [p[\"lon\"] for p in raster[\"region\"][\"poly\"]]\n", 324 | " raster_lats = [p[\"lat\"] for p in raster[\"region\"][\"poly\"]]\n", 325 | " plot_data = sampled_data[sampled_data[file_name].notnull()]\n", 326 | " ax[k] = plt.subplot(5,4,k+1)\n", 327 | " gdf.plot(ax=ax[k], column='h_mean', color='y', markersize=0.5)\n", 328 | " plot_data.plot(ax=ax[k], column='h_mean', color='b', markersize=0.5)\n", 329 | " ax[k].plot(region_lons, region_lats, linewidth=1.5, color='r', zorder=2)\n", 330 | " ax[k].plot(raster_lons, raster_lats, linewidth=1.5, color='g', zorder=2)\n", 331 | " k += 1\n", 332 | " plt.tight_layout()" 333 | ] 334 | }, 335 | { 336 | "cell_type": "markdown", 337 | "id": "d1addca2-fdbb-4e37-ae3e-417652e1f119", 338 | "metadata": {}, 339 | "source": [ 340 | "#### Plot the Different ArcticDEM Values against the SlideRule ATL06-SR Values" 341 | ] 342 | }, 343 | { 344 | "cell_type": "code", 345 | "execution_count": null, 346 | "id": "f98551c9-c76b-4469-a6df-acf2c2c375ef", 347 | "metadata": { 348 | "tags": [] 349 | }, 350 | "outputs": [], 351 | "source": [ 352 | "# Select DEM File ID\n", 353 | "file_name = list(file_dict.keys())[0]\n", 354 | "\n", 355 | "# Setup Plot\n", 356 | "fig,ax = plt.subplots(num=None, figsize=(10, 8))\n", 357 | "ax.set_title(\"SlideRule vs. ArcticDEM Elevations\")\n", 358 | "ax.set_xlabel('distance (m)')\n", 359 | "ax.set_ylabel('height (m)')\n", 360 | "legend_elements = []\n", 361 | "\n", 362 | "# Filter Data to Plot\n", 363 | "plot_data = sampled_data[sampled_data[file_name].notnull()]\n", 364 | "\n", 365 | "# Set X Axis\n", 366 | "x_axis = plot_data[\"x_atc\"]\n", 367 | "\n", 368 | "# Plot SlideRule ATL06 Elevations\n", 369 | "sc1 = ax.scatter(x_axis, plot_data[\"h_mean\"].values, c='red', s=2.5)\n", 370 | "legend_elements.append(matplotlib.lines.Line2D([0], [0], color='red', lw=6, label='ATL06-SR'))\n", 371 | "\n", 372 | "# Plot ArcticDEM Elevations\n", 373 | "sc2 = ax.scatter(x_axis, plot_data[file_name].values, c='blue', s=2.5)\n", 374 | "legend_elements.append(matplotlib.lines.Line2D([0], [0], color='blue', lw=6, label='ArcticDEM'))\n", 375 | "\n", 376 | "# Display Legend\n", 377 | "lgd = ax.legend(handles=legend_elements, loc=3, frameon=True)\n", 378 | "lgd.get_frame().set_alpha(1.0)\n", 379 | "lgd.get_frame().set_edgecolor('white')\n", 380 | "\n", 381 | "# Show Plot\n", 382 | "plt.show()" 383 | ] 384 | }, 385 | { 386 | "cell_type": "code", 387 | "execution_count": null, 388 | "id": "5270acb7-a1f8-4fce-bd16-91c2c055816a", 389 | "metadata": {}, 390 | "outputs": [], 391 | "source": [] 392 | } 393 | ], 394 | "metadata": { 395 | "kernelspec": { 396 | "display_name": "Python 3 (ipykernel)", 397 | "language": "python", 398 | "name": "python3" 399 | }, 400 | "language_info": { 401 | "codemirror_mode": { 402 | "name": "ipython", 403 | "version": 3 404 | }, 405 | "file_extension": ".py", 406 | "mimetype": "text/x-python", 407 | "name": "python", 408 | "nbconvert_exporter": "python", 409 | "pygments_lexer": "ipython3", 410 | "version": "3.12.0" 411 | } 412 | }, 413 | "nbformat": 4, 414 | "nbformat_minor": 5 415 | } 416 | -------------------------------------------------------------------------------- /examples/atl03_subsetter.py: -------------------------------------------------------------------------------- 1 | # Imports 2 | import os 3 | import sys 4 | import math 5 | import time 6 | import argparse 7 | import configparser 8 | import logging 9 | import traceback 10 | import multiprocessing 11 | import sliderule 12 | from datetime import datetime, timedelta 13 | from sliderule import icesat2, earthdata 14 | 15 | # Command Line Arguments 16 | parser = argparse.ArgumentParser(description="""Subset ATL03 granules""") 17 | parser.add_argument('--domain', '-d', type=str, default="slideruleearth.io") 18 | parser.add_argument('--organization', '-o', type=str, default="developers") 19 | parser.add_argument('--desired_nodes', '-n', type=int, default=1) 20 | parser.add_argument('--time_to_live', '-l', type=int, default=120) # minutes 21 | parser.add_argument('--aoi', '-a', type=str, default="examples/grandmesa.geojson") 22 | parser.add_argument('--rgt', type=int, default=None) 23 | parser.add_argument('--cycle', type=int, default=None) 24 | parser.add_argument('--region' , type=int, default=None) 25 | parser.add_argument('--track', type=int, default=None) 26 | parser.add_argument('--beam', type=str, nargs='+', default=['gt1l', 'gt1r', 'gt2l', 'gt2r', 'gt3l', 'gt3r']) 27 | parser.add_argument('--pass_invalid', action='store_true', default=False) 28 | parser.add_argument('--cnf', type=int, default=icesat2.CNF_NOT_CONSIDERED) 29 | parser.add_argument('--ats', type=int, default=None) 30 | parser.add_argument('--cnt', type=int, default=None) 31 | parser.add_argument('--len', type=int, default=None) 32 | parser.add_argument('--res', type=int, default=None) 33 | parser.add_argument('--subset_pixel_size', type=float, default=None) 34 | parser.add_argument('--proj', type=str, default=None) 35 | parser.add_argument('--ignore_poly_for_cmr', type=bool, default=None) 36 | parser.add_argument('--name', type=str, default='output') 37 | parser.add_argument('--no_geo', action='store_true', default=False) 38 | parser.add_argument('--output_path', '-p', type=str, default="hosted") # "hosted" tells sliderule to host results in a bucket it owns 39 | parser.add_argument('--timeout', '-t', type=int, default=600) # seconds 40 | parser.add_argument('--generate', action='store_true', default=False) 41 | parser.add_argument('--simulate_delay', type=float, default=1) 42 | parser.add_argument('--startup_wait', type=int, default=120) # seconds 43 | parser.add_argument('--granules_per_request', type=int, default=None) # None == all granules 44 | parser.add_argument('--concurrent_requests', type=int, default=1) 45 | parser.add_argument('--slice', type=int, nargs=2, default=None) 46 | parser.add_argument('--log_file', '-f', type=str, default="sliderule.log") 47 | parser.add_argument('--verbose', '-v', action='store_true', default=False) 48 | args,_ = parser.parse_known_args() 49 | 50 | # Setup Log File 51 | LOG_FORMAT = '%(created)f %(levelname)-5s [%(filename)s:%(lineno)5d] %(message)s' 52 | log = logging.getLogger(__name__) 53 | format = logging.Formatter(LOG_FORMAT) 54 | logfile = logging.FileHandler(args.log_file) 55 | logfile.setFormatter(format) 56 | log.addHandler(logfile) 57 | log.setLevel(logging.INFO) 58 | logfile.setLevel(logging.INFO) 59 | 60 | # Setup Config Parser for Credentials 61 | home_directory = os.path.expanduser('~') 62 | aws_credential_file = os.path.join(home_directory, '.aws', 'credentials') 63 | config = configparser.RawConfigParser() 64 | 65 | # Check for Hosted Option 66 | if args.output_path == "hosted": 67 | credentials = None 68 | else: 69 | credentials = {} 70 | 71 | # Check Organization 72 | organization = args.organization 73 | desired_nodes = args.desired_nodes 74 | if args.organization == "None": 75 | organization = None 76 | desired_nodes = None 77 | 78 | # Get Area of Interest 79 | if args.subset_pixel_size: 80 | region = sliderule.toregion(args.aoi, cellsize=args.subset_pixel_size) 81 | raster = region["raster"] 82 | else: 83 | region = sliderule.toregion(args.aoi) 84 | raster = None 85 | 86 | # Populate Request Parameters 87 | parms = { 88 | "asset": "icesat2", 89 | "poly": region["poly"], 90 | "raster": raster, 91 | "proj": args.proj, 92 | "ignore_poly_for_cmr": args.ignore_poly_for_cmr, 93 | "rgt": args.rgt, 94 | "cycle": args.cycle, 95 | "region": args.region, 96 | "pass_invalid": args.pass_invalid, 97 | "cnf": args.cnf, 98 | "ats": args.ats, 99 | "cnt": args.cnt, 100 | "len": args.len, 101 | "res": args.res, 102 | "timeout": args.timeout, 103 | "output": { 104 | "path": "", 105 | "format": "parquet", 106 | "as_geo": not args.no_geo, 107 | "open_on_complete": False, 108 | "region": "us-west-2", 109 | "credentials": credentials 110 | } 111 | } 112 | 113 | # Clear Out None Keys 114 | keys_to_delete = [] 115 | for key in parms: 116 | if parms[key] == None: 117 | keys_to_delete.append(key) 118 | for key in keys_to_delete: 119 | del parms[key] 120 | 121 | # Get Resources 122 | resources = earthdata.search(parms) 123 | if args.slice != None: 124 | resources = resources[args.slice[0]:args.slice[1]] 125 | 126 | # Calculate Requests 127 | requests = [] 128 | granules_per_request = len(resources) 129 | if granules_per_request == 0: 130 | log.critical(f'no resources to process, exiting') 131 | sys.exit(0) 132 | if args.granules_per_request != None: 133 | granules_per_request = args.granules_per_request 134 | for i in range(0, len(resources), granules_per_request): 135 | requests.append(resources[i:i+granules_per_request]) 136 | 137 | # Display Parameters 138 | log.info(f'organization = {organization}') 139 | log.info(f'desired_nodes = {desired_nodes}') 140 | log.critical(f'logfile = {args.log_file}') 141 | log.info(f'concurrent_requests = {args.concurrent_requests}') 142 | log.info(f'granules_per_request = {granules_per_request}') 143 | log.info(f'num_granules = {len(resources)}') 144 | log.info(f'parms = \n{parms}') 145 | 146 | # Create Request Queue 147 | rqst_q = multiprocessing.Queue() 148 | 149 | # 150 | # Update Credentials 151 | # 152 | def update_credentials(worker_id): 153 | 154 | # Log Maintanence Action 155 | now = datetime.now() 156 | expiration = now + timedelta(minutes=args.time_to_live) 157 | log.info(f'<{worker_id}> updating capacity and credentials until {expiration.strftime("%I:%M:%S")}') 158 | 159 | # Update Pending Capacity of Cluster 160 | if args.generate: 161 | sliderule.update_available_servers(desired_nodes=desired_nodes, time_to_live=args.time_to_live) 162 | elif args.simulate_delay > 0: 163 | time.sleep(args.simulate_delay) 164 | 165 | # Read AWS Credentials 166 | if credentials != None: 167 | config.read(aws_credential_file) 168 | parms["output"]["credentials"] = { 169 | "aws_access_key_id": config.get('default', 'aws_access_key_id'), 170 | "aws_secret_access_key": config.get('default', 'aws_secret_access_key'), 171 | "aws_session_token": config.get('default', 'aws_session_token') 172 | } 173 | 174 | # Finish Request 175 | log.info(f'<{worker_id}> finished update') 176 | 177 | # 178 | # Process Request 179 | # 180 | def process_request(worker_id, count, resources): 181 | 182 | # Start Processing 183 | log.info(f'<{worker_id}> processing {len(resources)} resources: {resources[0]} ...') 184 | 185 | # Set Output Path 186 | if credentials != None: 187 | parms["output"]["path"] = f'{args.output_path}/{args.name}_{count}.{"parquet" if args.no_geo else "geoparquet"}' 188 | else: 189 | parms["output"]["asset"] = "sliderule-stage" 190 | 191 | # Make Request 192 | if args.generate: 193 | outfile = icesat2.atl03sp(parms, resources=resources) 194 | else: 195 | outfile = parms["output"]["path"] 196 | if args.simulate_delay > 0: 197 | time.sleep(args.simulate_delay) 198 | 199 | # Finish Request 200 | log.info(f'<{worker_id}> finished {len(resources)} resources: {resources[0]} ...') 201 | log.info(f'<{worker_id}> writing {outfile}') 202 | 203 | # 204 | # Worker 205 | # 206 | def worker(worker_id): 207 | 208 | # Initialize Python Client 209 | if args.generate: 210 | icesat2.init(args.domain, verbose=args.verbose, loglevel=logging.INFO, organization=organization, desired_nodes=desired_nodes, time_to_live=args.time_to_live, rethrow=True) 211 | log.info(f'<{worker_id}> waiting {args.startup_wait} seconds for the newly created cluster to obtain credentials') 212 | time.sleep(args.startup_wait) 213 | elif args.simulate_delay > 0: 214 | time.sleep(args.simulate_delay) 215 | 216 | # While Queue Not Empty 217 | complete = False 218 | while not complete: 219 | 220 | # Get Request 221 | try: 222 | count, resources = rqst_q.get(block=False) 223 | except Exception as e: 224 | # Handle No More Requests 225 | if rqst_q.empty(): 226 | log.info(f'<{worker_id}> no more requests {e}') 227 | complete = True 228 | else: 229 | log.info(f'<{worker_id}> exception: {e}') 230 | time.sleep(5) # prevents a spin 231 | continue 232 | 233 | # Process Request 234 | attempts = 3 235 | success = False 236 | while attempts > 0 and not success: 237 | attempts -= 1 238 | try: 239 | update_credentials(worker_id) 240 | process_request(worker_id, count, resources) 241 | success = True 242 | except Exception as e: 243 | log.critical(f'attempt {3 - attempts} of 3 failed to process: {e}') 244 | print(traceback.format_exc()) 245 | 246 | # Queue Processing Requests 247 | count = 0 248 | for rqst in requests: 249 | log.debug(f'queueing processing request of {len(rqst)} resources: {rqst[0]} ...') 250 | rqst_q.put((count, rqst)) 251 | count += 1 252 | 253 | # Create Workers 254 | processes = [multiprocessing.Process(target=worker, args=(worker_id,), daemon=True) for worker_id in range(args.concurrent_requests)] 255 | 256 | # Start Workers 257 | for process in processes: 258 | process.start() 259 | 260 | # Wait for Workers to Complete 261 | for process in processes: 262 | process.join() 263 | log.info('all processing requests completed') 264 | -------------------------------------------------------------------------------- /examples/atl06_ancillary.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "3d6b3418-01b5-4546-ba53-175e3ad50d55", 7 | "metadata": { 8 | "tags": [] 9 | }, 10 | "outputs": [], 11 | "source": [ 12 | "# Imports\n", 13 | "import matplotlib.pyplot as plt\n", 14 | "from sliderule import sliderule, icesat2" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": null, 20 | "id": "2e7c4e03-2993-4ff3-9beb-0a815328ae02", 21 | "metadata": { 22 | "tags": [] 23 | }, 24 | "outputs": [], 25 | "source": [ 26 | "# Configure ICESat-2 API\n", 27 | "icesat2.init(\"slideruleearth.io\", verbose=False)" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": null, 33 | "id": "23d6327c-1b96-4a80-8843-adc760fba0e0", 34 | "metadata": { 35 | "tags": [] 36 | }, 37 | "outputs": [], 38 | "source": [ 39 | "# Area of Interest\n", 40 | "region = sliderule.toregion('grandmesa.geojson')" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": null, 46 | "id": "c7dc7f2e-c8a3-4745-a8f5-7d28af1e0449", 47 | "metadata": { 48 | "tags": [] 49 | }, 50 | "outputs": [], 51 | "source": [ 52 | "# Build ATL06 Request\n", 53 | "parms = {\n", 54 | " \"poly\": region[\"poly\"],\n", 55 | " \"srt\": icesat2.SRT_LAND,\n", 56 | " \"cnf\": icesat2.CNF_SURFACE_HIGH,\n", 57 | " \"ats\": 10.0,\n", 58 | " \"cnt\": 10,\n", 59 | " \"len\": 40.0,\n", 60 | " \"res\": 20.0,\n", 61 | " \"atl03_geo_fields\": [\"dem_h\"]\n", 62 | "}" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": null, 68 | "id": "fcf33b7a-026e-425e-8e6c-9887b9896138", 69 | "metadata": { 70 | "tags": [] 71 | }, 72 | "outputs": [], 73 | "source": [ 74 | "# Request ATL06 Data\n", 75 | "atl06 = icesat2.atl06p(parms)\n", 76 | "atl06.head()" 77 | ] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "execution_count": null, 82 | "id": "6af9de57-1bc5-4163-abe0-f5bddd2207e2", 83 | "metadata": { 84 | "tags": [] 85 | }, 86 | "outputs": [], 87 | "source": [ 88 | "# Display Statistics\n", 89 | "print(\"Reference Ground Tracks: {}\".format(atl06[\"rgt\"].unique()))\n", 90 | "print(\"Cycles: {}\".format(atl06[\"cycle\"].unique()))\n", 91 | "print(\"Received {} elevations\".format(atl06.shape[0]))\n", 92 | "print(\"Timing Profiles\")\n", 93 | "for key in icesat2.profiles:\n", 94 | " print(\"{:20} {:.6f} secs\".format(key + \":\", icesat2.profiles[key]))" 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": null, 100 | "id": "47b48f46-8c12-4796-bd0f-8f26b6d5fbe3", 101 | "metadata": { 102 | "tags": [] 103 | }, 104 | "outputs": [], 105 | "source": [ 106 | "# Build Delta Column\n", 107 | "atl06[\"h_delta\"] = atl06[\"h_mean\"] - atl06[\"dem_h\"]" 108 | ] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "execution_count": null, 113 | "id": "96aacc09-79a4-48bb-8287-8703f217aae6", 114 | "metadata": { 115 | "tags": [] 116 | }, 117 | "outputs": [], 118 | "source": [ 119 | "# Plot Heights\n", 120 | "f, ax = plt.subplots(1, 2)\n", 121 | "ax[0].set_title(\"h_mean\")\n", 122 | "atl06.plot(ax=ax[0], column='h_mean', cmap='inferno', s=0.1)\n", 123 | "ax[1].set_title(\"h_delta\")\n", 124 | "atl06.plot(ax=ax[1], column='h_delta', cmap='inferno', s=0.1)" 125 | ] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": null, 130 | "id": "1d0ecee5-9d52-4370-a258-411fbb0b7626", 131 | "metadata": {}, 132 | "outputs": [], 133 | "source": [] 134 | } 135 | ], 136 | "metadata": { 137 | "kernelspec": { 138 | "display_name": "Python 3 (ipykernel)", 139 | "language": "python", 140 | "name": "python3" 141 | }, 142 | "language_info": { 143 | "codemirror_mode": { 144 | "name": "ipython", 145 | "version": 3 146 | }, 147 | "file_extension": ".py", 148 | "mimetype": "text/x-python", 149 | "name": "python", 150 | "nbconvert_exporter": "python", 151 | "pygments_lexer": "ipython3", 152 | "version": "3.11.3" 153 | } 154 | }, 155 | "nbformat": 4, 156 | "nbformat_minor": 5 157 | } 158 | -------------------------------------------------------------------------------- /examples/atl06_subsetting.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "e243172d-a731-4f1a-ae97-7c7a1fd63757", 6 | "metadata": { 7 | "tags": [], 8 | "user_expressions": [] 9 | }, 10 | "source": [ 11 | "# ATL06 Subsetting and On-Demand Product Generation" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "id": "a824b4cc-e7a1-4d4e-be55-13f3a6c8e96e", 17 | "metadata": { 18 | "user_expressions": [] 19 | }, 20 | "source": [ 21 | "### Purpose\n", 22 | "Subset ATL06 granule and compare against on-demand generated ATL06 elevations using SlideRule" 23 | ] 24 | }, 25 | { 26 | "cell_type": "markdown", 27 | "id": "e5e2efc2-078a-4e37-8083-75994ebf62e8", 28 | "metadata": { 29 | "tags": [], 30 | "user_expressions": [] 31 | }, 32 | "source": [ 33 | "#### Import Packages" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": null, 39 | "id": "338d80d9-e8f7-40ec-a294-683562437f69", 40 | "metadata": { 41 | "tags": [] 42 | }, 43 | "outputs": [], 44 | "source": [ 45 | "from sliderule import sliderule, icesat2, earthdata" 46 | ] 47 | }, 48 | { 49 | "cell_type": "markdown", 50 | "id": "7dc950a2-4b0c-4c8f-b7cc-ea6092f8c96e", 51 | "metadata": { 52 | "tags": [], 53 | "user_expressions": [] 54 | }, 55 | "source": [ 56 | "#### Configure Logging" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": null, 62 | "id": "73e23172-83d2-4bd7-a2e9-84b9ac296037", 63 | "metadata": { 64 | "tags": [] 65 | }, 66 | "outputs": [], 67 | "source": [ 68 | "import logging\n", 69 | "loglevel = logging.CRITICAL\n", 70 | "logging.basicConfig(level=loglevel)" 71 | ] 72 | }, 73 | { 74 | "cell_type": "markdown", 75 | "id": "613b066a-fbda-4583-a29c-dbe35c182252", 76 | "metadata": { 77 | "tags": [], 78 | "user_expressions": [] 79 | }, 80 | "source": [ 81 | "#### Initialize SlideRule Python Client" 82 | ] 83 | }, 84 | { 85 | "cell_type": "code", 86 | "execution_count": null, 87 | "id": "0ca128df-4ff0-4b95-ae40-d0a040c9a9db", 88 | "metadata": { 89 | "tags": [] 90 | }, 91 | "outputs": [], 92 | "source": [ 93 | "domain = \"slideruleearth.io\"\n", 94 | "sliderule.init(domain, verbose=True, loglevel=loglevel)" 95 | ] 96 | }, 97 | { 98 | "cell_type": "markdown", 99 | "id": "95e29e39-bef3-4312-8498-f037267da964", 100 | "metadata": { 101 | "tags": [], 102 | "user_expressions": [] 103 | }, 104 | "source": [ 105 | "#### Build Request Parameters" 106 | ] 107 | }, 108 | { 109 | "cell_type": "code", 110 | "execution_count": null, 111 | "id": "3f58197d-5265-4c83-bc62-6049ace71538", 112 | "metadata": { 113 | "tags": [] 114 | }, 115 | "outputs": [], 116 | "source": [ 117 | "granule = '_20181016104402_02720106_006_02.h5'" 118 | ] 119 | }, 120 | { 121 | "cell_type": "code", 122 | "execution_count": null, 123 | "id": "07da0425-2ebf-41aa-b84d-8f27a508cdbe", 124 | "metadata": { 125 | "tags": [] 126 | }, 127 | "outputs": [], 128 | "source": [ 129 | "region = sliderule.toregion(\"../data/grandmesa.geojson\")" 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": null, 135 | "id": "3f01b208-4665-47c0-90e3-95834cc61338", 136 | "metadata": { 137 | "tags": [] 138 | }, 139 | "outputs": [], 140 | "source": [ 141 | "parms = {\n", 142 | " \"poly\": region[\"poly\"],\n", 143 | " \"srt\": icesat2.SRT_LAND,\n", 144 | " \"cnf\": icesat2.CNF_SURFACE_HIGH,\n", 145 | " \"ats\": 10.0,\n", 146 | " \"cnt\": 10,\n", 147 | " \"len\": 40.0,\n", 148 | " \"res\": 20.0\n", 149 | "}" 150 | ] 151 | }, 152 | { 153 | "cell_type": "markdown", 154 | "id": "90bb8070-d857-424a-a100-d1ea32631e82", 155 | "metadata": { 156 | "tags": [], 157 | "user_expressions": [] 158 | }, 159 | "source": [ 160 | "#### Make ATL06 Subsetting Request" 161 | ] 162 | }, 163 | { 164 | "cell_type": "code", 165 | "execution_count": null, 166 | "id": "ecc3f250-0785-4630-a261-13d12ab59819", 167 | "metadata": { 168 | "tags": [] 169 | }, 170 | "outputs": [], 171 | "source": [ 172 | "sdp = icesat2.atl06sp(parms, resources=['ATL06'+granule])\n", 173 | "sdp" 174 | ] 175 | }, 176 | { 177 | "cell_type": "markdown", 178 | "id": "a0f5c12e-c439-4549-ae7d-61af9954787b", 179 | "metadata": { 180 | "user_expressions": [] 181 | }, 182 | "source": [ 183 | "#### Make ATL06 On-Demand Request" 184 | ] 185 | }, 186 | { 187 | "cell_type": "code", 188 | "execution_count": null, 189 | "id": "d5128cac-5df0-4102-91e5-bb65cce7e0f2", 190 | "metadata": { 191 | "tags": [] 192 | }, 193 | "outputs": [], 194 | "source": [ 195 | "sr = icesat2.atl06p(parms, resources=['ATL03_20181016104402_02720106_006_02.h5'])\n", 196 | "sr" 197 | ] 198 | }, 199 | { 200 | "cell_type": "markdown", 201 | "id": "0b5a2b18-f3fb-48bc-90d2-53350638d1dc", 202 | "metadata": { 203 | "tags": [], 204 | "user_expressions": [] 205 | }, 206 | "source": [ 207 | "#### Plot Results" 208 | ] 209 | }, 210 | { 211 | "cell_type": "code", 212 | "execution_count": null, 213 | "id": "61c365cf-6bab-408c-8b2f-49a3d896692b", 214 | "metadata": { 215 | "tags": [] 216 | }, 217 | "outputs": [], 218 | "source": [ 219 | "# Import Plotting Library\n", 220 | "import matplotlib.pyplot as plt\n", 221 | "import matplotlib" 222 | ] 223 | }, 224 | { 225 | "cell_type": "code", 226 | "execution_count": null, 227 | "id": "64bc423d-2348-4217-804f-63119ff71d16", 228 | "metadata": { 229 | "tags": [] 230 | }, 231 | "outputs": [], 232 | "source": [ 233 | "# Setup Plot\n", 234 | "fig,ax = plt.subplots(num=None, figsize=(10, 8))\n", 235 | "fig.set_facecolor('white')\n", 236 | "fig.canvas.header_visible = False\n", 237 | "ax.set_title(\"SlideRule vs. Standard Data Product Elevations\")\n", 238 | "ax.set_xlabel('UTC')\n", 239 | "ax.set_ylabel('height (m)')\n", 240 | "legend_elements = []\n", 241 | "\n", 242 | "# Plot SlideRule ATL06 Elevations\n", 243 | "sc1 = ax.scatter(sr.index.values, sr[\"h_mean\"].values, c='red', s=2.5)\n", 244 | "legend_elements.append(matplotlib.lines.Line2D([0], [0], color='red', lw=6, label='SR'))\n", 245 | "\n", 246 | "# Plot SDP ATL06 Elevations\n", 247 | "sc2 = ax.scatter(sdp.index.values, sdp[\"h_li\"].values, c='blue', s=2.5)\n", 248 | "legend_elements.append(matplotlib.lines.Line2D([0], [0], color='blue', lw=6, label='SDP'))\n", 249 | "\n", 250 | "# Display Legend\n", 251 | "lgd = ax.legend(handles=legend_elements, loc=3, frameon=True)\n", 252 | "lgd.get_frame().set_alpha(1.0)\n", 253 | "lgd.get_frame().set_edgecolor('white')\n", 254 | "\n", 255 | "# Show Plot\n", 256 | "plt.show()" 257 | ] 258 | }, 259 | { 260 | "cell_type": "code", 261 | "execution_count": null, 262 | "id": "04137254-5356-4f1d-9438-2126063f2258", 263 | "metadata": {}, 264 | "outputs": [], 265 | "source": [] 266 | } 267 | ], 268 | "metadata": { 269 | "kernelspec": { 270 | "display_name": "Python 3 (ipykernel)", 271 | "language": "python", 272 | "name": "python3" 273 | }, 274 | "language_info": { 275 | "codemirror_mode": { 276 | "name": "ipython", 277 | "version": 3 278 | }, 279 | "file_extension": ".py", 280 | "mimetype": "text/x-python", 281 | "name": "python", 282 | "nbconvert_exporter": "python", 283 | "pygments_lexer": "ipython3", 284 | "version": "3.12.0" 285 | } 286 | }, 287 | "nbformat": 4, 288 | "nbformat_minor": 5 289 | } 290 | -------------------------------------------------------------------------------- /examples/atl13_subsetting.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "e243172d-a731-4f1a-ae97-7c7a1fd63757", 6 | "metadata": { 7 | "tags": [], 8 | "user_expressions": [] 9 | }, 10 | "source": [ 11 | "# ATL13 Subsetting" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "id": "a824b4cc-e7a1-4d4e-be55-13f3a6c8e96e", 17 | "metadata": { 18 | "user_expressions": [] 19 | }, 20 | "source": [ 21 | "### Purpose\n", 22 | "Subset ATL13 granule using SlideRule" 23 | ] 24 | }, 25 | { 26 | "cell_type": "markdown", 27 | "id": "e5e2efc2-078a-4e37-8083-75994ebf62e8", 28 | "metadata": { 29 | "tags": [], 30 | "user_expressions": [] 31 | }, 32 | "source": [ 33 | "#### Import Packages" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": null, 39 | "id": "338d80d9-e8f7-40ec-a294-683562437f69", 40 | "metadata": { 41 | "tags": [] 42 | }, 43 | "outputs": [], 44 | "source": [ 45 | "from sliderule import sliderule, icesat2, earthdata\n", 46 | "import matplotlib.pyplot as plt" 47 | ] 48 | }, 49 | { 50 | "cell_type": "markdown", 51 | "id": "7dc950a2-4b0c-4c8f-b7cc-ea6092f8c96e", 52 | "metadata": { 53 | "tags": [], 54 | "user_expressions": [] 55 | }, 56 | "source": [ 57 | "#### Configure Logging" 58 | ] 59 | }, 60 | { 61 | "cell_type": "markdown", 62 | "id": "613b066a-fbda-4583-a29c-dbe35c182252", 63 | "metadata": { 64 | "tags": [], 65 | "user_expressions": [] 66 | }, 67 | "source": [ 68 | "#### Initialize SlideRule Python Client" 69 | ] 70 | }, 71 | { 72 | "cell_type": "code", 73 | "execution_count": null, 74 | "id": "0ca128df-4ff0-4b95-ae40-d0a040c9a9db", 75 | "metadata": { 76 | "tags": [] 77 | }, 78 | "outputs": [], 79 | "source": [ 80 | "#sliderule.init(\"localhost\", organization=None, verbose=True, loglevel=\"INFO\")\n", 81 | "sliderule.init()" 82 | ] 83 | }, 84 | { 85 | "cell_type": "markdown", 86 | "id": "95e29e39-bef3-4312-8498-f037267da964", 87 | "metadata": { 88 | "tags": [], 89 | "user_expressions": [] 90 | }, 91 | "source": [ 92 | "#### Build Request Parameters" 93 | ] 94 | }, 95 | { 96 | "cell_type": "code", 97 | "execution_count": null, 98 | "id": "07da0425-2ebf-41aa-b84d-8f27a508cdbe", 99 | "metadata": { 100 | "tags": [] 101 | }, 102 | "outputs": [], 103 | "source": [ 104 | "region = sliderule.toregion(\"../../sliderule/clients/python/tests/data/tarawa.geojson\")" 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": null, 110 | "id": "3f01b208-4665-47c0-90e3-95834cc61338", 111 | "metadata": { 112 | "tags": [] 113 | }, 114 | "outputs": [], 115 | "source": [ 116 | "parms = {\n", 117 | " \"poly\": region[\"poly\"],\n", 118 | "}" 119 | ] 120 | }, 121 | { 122 | "cell_type": "markdown", 123 | "id": "90bb8070-d857-424a-a100-d1ea32631e82", 124 | "metadata": { 125 | "tags": [], 126 | "user_expressions": [] 127 | }, 128 | "source": [ 129 | "#### Make ATL13 Subsetting Request" 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": null, 135 | "id": "ecc3f250-0785-4630-a261-13d12ab59819", 136 | "metadata": { 137 | "scrolled": true, 138 | "tags": [] 139 | }, 140 | "outputs": [], 141 | "source": [ 142 | "sdp = icesat2.atl13sp(parms)\n", 143 | "sdp" 144 | ] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "execution_count": null, 149 | "id": "0aa1301a-f2cf-4549-927b-c8db796d6dbc", 150 | "metadata": { 151 | "tags": [], 152 | "user_expressions": [] 153 | }, 154 | "outputs": [], 155 | "source": [ 156 | "f, ax = plt.subplots()\n", 157 | "ax.set_title(\"ATL13 Points\")\n", 158 | "sdp.plot(ax=ax, column='ht_water_surf', cmap='inferno', s=0.1)" 159 | ] 160 | }, 161 | { 162 | "cell_type": "code", 163 | "execution_count": null, 164 | "id": "04137254-5356-4f1d-9438-2126063f2258", 165 | "metadata": {}, 166 | "outputs": [], 167 | "source": [] 168 | } 169 | ], 170 | "metadata": { 171 | "kernelspec": { 172 | "display_name": "Python 3 (ipykernel)", 173 | "language": "python", 174 | "name": "python3" 175 | }, 176 | "language_info": { 177 | "codemirror_mode": { 178 | "name": "ipython", 179 | "version": 3 180 | }, 181 | "file_extension": ".py", 182 | "mimetype": "text/x-python", 183 | "name": "python", 184 | "nbconvert_exporter": "python", 185 | "pygments_lexer": "ipython3", 186 | "version": "3.12.0" 187 | } 188 | }, 189 | "nbformat": 4, 190 | "nbformat_minor": 5 191 | } 192 | -------------------------------------------------------------------------------- /examples/boulder_watershed_demo.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Boulder Watershed Demo\n", 8 | "\n", 9 | "Process ATL03 data from the Boulder Watershed region and produce a customized ATL06 elevation dataset.\n", 10 | "\n", 11 | "### What is demonstrated\n", 12 | "\n", 13 | "* The `icesat2.atl06p` API is used to perform a SlideRule parallel processing request of the Boulder Watershed region\n", 14 | "* The `matplotlib` and `geopandas` packages are used to plot the data returned by SlideRule\n", 15 | "\n", 16 | "### Points of interest\n", 17 | "\n", 18 | "This is a simple notebook showing how a region of interest can be processed by SlideRule and the results analyzed using pandas DataFrames and Matplotlib." 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": null, 24 | "metadata": { 25 | "tags": [] 26 | }, 27 | "outputs": [], 28 | "source": [ 29 | "import logging\n", 30 | "import geopandas as gpd\n", 31 | "import matplotlib.pyplot as plt\n", 32 | "from sliderule import icesat2" 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "metadata": {}, 38 | "source": [ 39 | "## SlideRule Configuration" 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": null, 45 | "metadata": { 46 | "tags": [] 47 | }, 48 | "outputs": [], 49 | "source": [ 50 | "# Configure ICESat-2 API\n", 51 | "icesat2.init(\"slideruleearth.io\")\n", 52 | "# Configure Region of Interest\n", 53 | "region = [ {\"lon\":-105.82971551223244, \"lat\": 39.81983728534918},\n", 54 | " {\"lon\":-105.30742121965137, \"lat\": 39.81983728534918},\n", 55 | " {\"lon\":-105.30742121965137, \"lat\": 40.164048017973755},\n", 56 | " {\"lon\":-105.82971551223244, \"lat\": 40.164048017973755},\n", 57 | " {\"lon\":-105.82971551223244, \"lat\": 39.81983728534918} ]" 58 | ] 59 | }, 60 | { 61 | "cell_type": "markdown", 62 | "metadata": {}, 63 | "source": [ 64 | "## Execute ATL06 Algorithm using SlideRule" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": null, 70 | "metadata": { 71 | "tags": [] 72 | }, 73 | "outputs": [], 74 | "source": [ 75 | "%%time\n", 76 | "\n", 77 | "# Build ATL06 Request\n", 78 | "parms = {\n", 79 | " \"poly\": region,\n", 80 | " \"srt\": icesat2.SRT_LAND,\n", 81 | " \"cnf\": icesat2.CNF_SURFACE_HIGH,\n", 82 | " \"ats\": 10.0,\n", 83 | " \"cnt\": 10,\n", 84 | " \"len\": 40.0,\n", 85 | " \"res\": 20.0\n", 86 | "}\n", 87 | "\n", 88 | "# Request ATL06 Data\n", 89 | "gdf = icesat2.atl06p(parms)\n", 90 | "\n", 91 | "# Display Statistics\n", 92 | "print(\"Reference Ground Tracks: {}\".format(gdf[\"rgt\"].unique()))\n", 93 | "print(\"Cycles: {}\".format(gdf[\"cycle\"].unique()))\n", 94 | "print(\"Received {} elevations\".format(len(gdf)))" 95 | ] 96 | }, 97 | { 98 | "cell_type": "markdown", 99 | "metadata": {}, 100 | "source": [ 101 | "## Plot Region" 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": null, 107 | "metadata": { 108 | "tags": [] 109 | }, 110 | "outputs": [], 111 | "source": [ 112 | "# Calculate Extent\n", 113 | "lons = [p[\"lon\"] for p in region]\n", 114 | "lats = [p[\"lat\"] for p in region]\n", 115 | "lon_margin = (max(lons) - min(lons)) * 0.1\n", 116 | "lat_margin = (max(lats) - min(lats)) * 0.1\n", 117 | "\n", 118 | "# Create Plot\n", 119 | "fig,(ax1,ax2) = plt.subplots(num=None, ncols=2, figsize=(12, 6))\n", 120 | "box_lon = [e[\"lon\"] for e in region]\n", 121 | "box_lat = [e[\"lat\"] for e in region]\n", 122 | "\n", 123 | "# Plot SlideRule Ground Tracks\n", 124 | "ax1.set_title(\"SlideRule Zoomed Ground Tracks\")\n", 125 | "gdf.plot(ax=ax1, column=gdf[\"h_mean\"], cmap='winter_r', s=1.0, zorder=3)\n", 126 | "ax1.plot(box_lon, box_lat, linewidth=1.5, color='r', zorder=2)\n", 127 | "ax1.set_xlim(min(lons) - lon_margin, max(lons) + lon_margin)\n", 128 | "ax1.set_ylim(min(lats) - lat_margin, max(lats) + lat_margin)\n", 129 | "ax1.set_aspect('equal', adjustable='box')\n", 130 | "\n", 131 | "# Plot SlideRule Global View\n", 132 | "ax2.set_title(\"SlideRule Global Reference\")\n", 133 | "world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))\n", 134 | "world.plot(ax=ax2, color='0.8', edgecolor='black')\n", 135 | "gdf.plot(ax=ax2, marker='o', color='red', markersize=2.5, zorder=3)\n", 136 | "ax2.set_xlim(-180,180)\n", 137 | "ax2.set_ylim(-90,90)\n", 138 | "ax2.set_aspect('equal', adjustable='box')\n", 139 | "\n", 140 | "# Show Plot\n", 141 | "plt.tight_layout()" 142 | ] 143 | }, 144 | { 145 | "cell_type": "code", 146 | "execution_count": null, 147 | "metadata": {}, 148 | "outputs": [], 149 | "source": [] 150 | } 151 | ], 152 | "metadata": { 153 | "interpreter": { 154 | "hash": "d7f94b8b1e41b02170d45ac71ce2d6b011e7cd56207b4c480f5292088bcfab93" 155 | }, 156 | "kernelspec": { 157 | "display_name": "Python 3 (ipykernel)", 158 | "language": "python", 159 | "name": "python3" 160 | }, 161 | "language_info": { 162 | "codemirror_mode": { 163 | "name": "ipython", 164 | "version": 3 165 | }, 166 | "file_extension": ".py", 167 | "mimetype": "text/x-python", 168 | "name": "python", 169 | "nbconvert_exporter": "python", 170 | "pygments_lexer": "ipython3", 171 | "version": "3.12.0" 172 | } 173 | }, 174 | "nbformat": 4, 175 | "nbformat_minor": 4 176 | } 177 | -------------------------------------------------------------------------------- /examples/boulder_watershed_viewer_demo.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Boulder Watershed Demo\n", 8 | "\n", 9 | "Process ATL03 data from the Boulder Watershed region and produce a photon count dataset.\n", 10 | "\n", 11 | "### What is demonstrated\n", 12 | "\n", 13 | "* The `icesat2.atl03vp` API is used to perform a SlideRule parallel processing request of the Boulder Watershed region\n", 14 | "* The `matplotlib` and `geopandas` packages are used to plot the data returned by SlideRule\n", 15 | "\n", 16 | "### Points of interest\n", 17 | "\n", 18 | "This is a simple notebook showing how a region of interest can be processed by SlideRule and the results analyzed using pandas DataFrames and Matplotlib." 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": null, 24 | "metadata": { 25 | "tags": [] 26 | }, 27 | "outputs": [], 28 | "source": [ 29 | "import logging\n", 30 | "import geopandas as gpd\n", 31 | "import matplotlib.pyplot as plt\n", 32 | "from sliderule import icesat2" 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "metadata": {}, 38 | "source": [ 39 | "## SlideRule Configuration" 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": null, 45 | "metadata": { 46 | "tags": [] 47 | }, 48 | "outputs": [], 49 | "source": [ 50 | "# Configure ICESat-2 API\n", 51 | "icesat2.init(\"slideruleearth.io\")\n", 52 | "# Configure Region of Interest\n", 53 | "region = [ {\"lon\":-105.82971551223244, \"lat\": 39.81983728534918},\n", 54 | " {\"lon\":-105.30742121965137, \"lat\": 39.81983728534918},\n", 55 | " {\"lon\":-105.30742121965137, \"lat\": 40.164048017973755},\n", 56 | " {\"lon\":-105.82971551223244, \"lat\": 40.164048017973755},\n", 57 | " {\"lon\":-105.82971551223244, \"lat\": 39.81983728534918} ]" 58 | ] 59 | }, 60 | { 61 | "cell_type": "markdown", 62 | "metadata": {}, 63 | "source": [ 64 | "## Execute ATL03 Viewer Algorithm using SlideRule" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": null, 70 | "metadata": { 71 | "tags": [] 72 | }, 73 | "outputs": [], 74 | "source": [ 75 | "%%time\n", 76 | "\n", 77 | "# Build ATL03 Viewer Request\n", 78 | "parms = {\n", 79 | " \"poly\": region,\n", 80 | " \"track\": 1\n", 81 | "}\n", 82 | "\n", 83 | "# Request ATL03 Viewer Data\n", 84 | "gdf = icesat2.atl03vp(parms)\n", 85 | "\n", 86 | "# Display Statistics\n", 87 | "print(\"Reference Ground Tracks: {}\".format(gdf[\"rgt\"].unique()))\n", 88 | "print(\"Cycles: {}\".format(gdf[\"cycle\"].unique()))\n", 89 | "print(\"Received {} segments\".format(len(gdf)))" 90 | ] 91 | }, 92 | { 93 | "cell_type": "markdown", 94 | "metadata": {}, 95 | "source": [ 96 | "## Plot Region" 97 | ] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": null, 102 | "metadata": { 103 | "tags": [] 104 | }, 105 | "outputs": [], 106 | "source": [ 107 | "# Calculate Extent\n", 108 | "lons = [p[\"lon\"] for p in region]\n", 109 | "lats = [p[\"lat\"] for p in region]\n", 110 | "lon_margin = (max(lons) - min(lons)) * 0.1\n", 111 | "lat_margin = (max(lats) - min(lats)) * 0.1\n", 112 | "\n", 113 | "# Create Plot\n", 114 | "fig,(ax1) = plt.subplots(num=None, ncols=1, figsize=(12, 6))\n", 115 | "box_lon = [e[\"lon\"] for e in region]\n", 116 | "box_lat = [e[\"lat\"] for e in region]\n", 117 | "\n", 118 | "# Plot SlideRule Ground Tracks\n", 119 | "ax1.set_title(\"SlideRule Zoomed Ground Tracks\")\n", 120 | "gdf.plot(ax=ax1, column=gdf[\"segment_ph_cnt\"], cmap='winter_r', s=1.0, zorder=3)\n", 121 | "ax1.plot(box_lon, box_lat, linewidth=1.5, color='r', zorder=2)\n", 122 | "ax1.set_xlim(min(lons) - lon_margin, max(lons) + lon_margin)\n", 123 | "ax1.set_ylim(min(lats) - lat_margin, max(lats) + lat_margin)\n", 124 | "ax1.set_aspect('equal', adjustable='box')\n", 125 | "\n", 126 | "# Show Plot\n", 127 | "plt.tight_layout()" 128 | ] 129 | } 130 | ], 131 | "metadata": { 132 | "interpreter": { 133 | "hash": "d7f94b8b1e41b02170d45ac71ce2d6b011e7cd56207b4c480f5292088bcfab93" 134 | }, 135 | "kernelspec": { 136 | "display_name": "Python 3 (ipykernel)", 137 | "language": "python", 138 | "name": "python3" 139 | }, 140 | "language_info": { 141 | "codemirror_mode": { 142 | "name": "ipython", 143 | "version": 3 144 | }, 145 | "file_extension": ".py", 146 | "mimetype": "text/x-python", 147 | "name": "python", 148 | "nbconvert_exporter": "python", 149 | "pygments_lexer": "ipython3", 150 | "version": "3.8.13" 151 | } 152 | }, 153 | "nbformat": 4, 154 | "nbformat_minor": 4 155 | } 156 | -------------------------------------------------------------------------------- /examples/gedi_l1b.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "0e7a3594-2353-476d-a42a-b6bf1b279c61", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "import time\n", 11 | "import matplotlib.pyplot as plt\n", 12 | "from sliderule import gedi\n", 13 | "from sliderule import earthdata\n", 14 | "import sliderule" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": null, 20 | "id": "54acbd2a-23d8-4cb9-ac06-b3893a3b45db", 21 | "metadata": {}, 22 | "outputs": [], 23 | "source": [ 24 | "# initialize client (notebook only processes one granule, so one node is sufficient)\n", 25 | "gedi.init(\"slideruleearth.io\", verbose=True)" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": null, 31 | "id": "e1421300-b0d1-4139-928e-f8bc612a8ac9", 32 | "metadata": {}, 33 | "outputs": [], 34 | "source": [ 35 | "# Specify region of interest from geojson\n", 36 | "poly_fn = 'grandmesa.geojson'\n", 37 | "region = sliderule.toregion(poly_fn)" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": null, 43 | "id": "720dc13b-c6af-4580-ac55-ab4c5263b96e", 44 | "metadata": { 45 | "tags": [] 46 | }, 47 | "outputs": [], 48 | "source": [ 49 | "granules = earthdata.cmr(short_name=\"GEDI01_B\", polygon=region[\"poly\"])" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": null, 55 | "id": "a46a84ed-05f5-47b8-b4b7-d063dacfddde", 56 | "metadata": {}, 57 | "outputs": [], 58 | "source": [ 59 | "# Build GEDI Request Parameters\n", 60 | "parms = {\n", 61 | " \"poly\": region[\"poly\"],\n", 62 | " \"beam\": 0\n", 63 | "}" 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": null, 69 | "id": "02682045-e626-4864-b4eb-1ed04d77d778", 70 | "metadata": {}, 71 | "outputs": [], 72 | "source": [ 73 | "# Latch Start Time\n", 74 | "perf_start = time.perf_counter()\n", 75 | "\n", 76 | "# Request GEDI Data\n", 77 | "gedi01b = gedi.gedi01bp(parms, resources=['GEDI01_B_2019109210809_O01988_03_T02056_02_005_01_V002.h5'])\n", 78 | " \n", 79 | "# Latch Stop Time\n", 80 | "perf_stop = time.perf_counter()\n", 81 | "\n", 82 | "# Display Statistics\n", 83 | "perf_duration = perf_stop - perf_start\n", 84 | "print(\"Completed in {:.3f} seconds of wall-clock time\".format(perf_duration))\n", 85 | "print(\"Received {} footprints\".format(gedi01b.shape[0]))\n", 86 | "if len(gedi01b) > 0:\n", 87 | " print(\"Beams: {}\".format(gedi01b[\"beam\"].unique()))" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": null, 93 | "id": "92d482e1-08e2-49a4-9ff7-155b7952e178", 94 | "metadata": {}, 95 | "outputs": [], 96 | "source": [ 97 | "gedi01b" 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": null, 103 | "id": "eeade03e-0dc7-42fe-a879-8e59cce8e6af", 104 | "metadata": {}, 105 | "outputs": [], 106 | "source": [ 107 | "# plot elevations\n", 108 | "f, ax = plt.subplots(1, 2, figsize=[12,8])\n", 109 | "ax[0].set_title(\"Elevation of First Bin\")\n", 110 | "ax[0].set_aspect('equal')\n", 111 | "gedi01b.plot(ax=ax[0], column='elevation_start', cmap='inferno', s=0.1)\n", 112 | "ax[1].set_title(\"Elevation of Last Bin\")\n", 113 | "ax[1].set_aspect('equal')\n", 114 | "gedi01b.plot(ax=ax[1], column='elevation_stop', cmap='inferno', s=0.1)" 115 | ] 116 | }, 117 | { 118 | "cell_type": "code", 119 | "execution_count": null, 120 | "id": "fb72876c-0512-4bcd-9a7c-1876bf23870d", 121 | "metadata": {}, 122 | "outputs": [], 123 | "source": [] 124 | } 125 | ], 126 | "metadata": { 127 | "kernelspec": { 128 | "display_name": "Python 3 (ipykernel)", 129 | "language": "python", 130 | "name": "python3" 131 | }, 132 | "language_info": { 133 | "codemirror_mode": { 134 | "name": "ipython", 135 | "version": 3 136 | }, 137 | "file_extension": ".py", 138 | "mimetype": "text/x-python", 139 | "name": "python", 140 | "nbconvert_exporter": "python", 141 | "pygments_lexer": "ipython3", 142 | "version": "3.12.0" 143 | } 144 | }, 145 | "nbformat": 4, 146 | "nbformat_minor": 5 147 | } 148 | -------------------------------------------------------------------------------- /examples/gedi_l2a.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "0e7a3594-2353-476d-a42a-b6bf1b279c61", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "import time\n", 11 | "import matplotlib.pyplot as plt\n", 12 | "from sliderule import gedi\n", 13 | "from sliderule import earthdata\n", 14 | "import sliderule" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": null, 20 | "id": "54acbd2a-23d8-4cb9-ac06-b3893a3b45db", 21 | "metadata": {}, 22 | "outputs": [], 23 | "source": [ 24 | "# initialize client\n", 25 | "gedi.init(\"slideruleearth.io\", verbose=False)" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": null, 31 | "id": "e1421300-b0d1-4139-928e-f8bc612a8ac9", 32 | "metadata": {}, 33 | "outputs": [], 34 | "source": [ 35 | "# Specify region of interest from geojson\n", 36 | "poly_fn = 'grandmesa.geojson'\n", 37 | "region = sliderule.toregion(poly_fn)" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": null, 43 | "id": "720dc13b-c6af-4580-ac55-ab4c5263b96e", 44 | "metadata": { 45 | "tags": [] 46 | }, 47 | "outputs": [], 48 | "source": [ 49 | "granules = earthdata.cmr(short_name=\"GEDI02_A\", polygon=region[\"poly\"])" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": null, 55 | "id": "a46a84ed-05f5-47b8-b4b7-d063dacfddde", 56 | "metadata": {}, 57 | "outputs": [], 58 | "source": [ 59 | "# Build GEDI L4A Request Parameters\n", 60 | "parms = {\n", 61 | " \"poly\": region[\"poly\"],\n", 62 | " \"degrade_flag\": 0,\n", 63 | " \"l2_quality_flag\": 1,\n", 64 | " \"beam\": 0\n", 65 | "}" 66 | ] 67 | }, 68 | { 69 | "cell_type": "code", 70 | "execution_count": null, 71 | "id": "02682045-e626-4864-b4eb-1ed04d77d778", 72 | "metadata": {}, 73 | "outputs": [], 74 | "source": [ 75 | "# Latch Start Time\n", 76 | "perf_start = time.perf_counter()\n", 77 | "\n", 78 | "# Request GEDI Data\n", 79 | "gedi02a = gedi.gedi02ap(parms)\n", 80 | " \n", 81 | "# Latch Stop Time\n", 82 | "perf_stop = time.perf_counter()\n", 83 | "\n", 84 | "# Display Statistics\n", 85 | "perf_duration = perf_stop - perf_start\n", 86 | "print(\"Completed in {:.3f} seconds of wall-clock time\".format(perf_duration))\n", 87 | "print(\"Received {} footprints\".format(gedi02a.shape[0]))\n", 88 | "if len(gedi02a) > 0:\n", 89 | " print(\"Beams: {}\".format(gedi02a[\"beam\"].unique()))" 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": null, 95 | "id": "92d482e1-08e2-49a4-9ff7-155b7952e178", 96 | "metadata": {}, 97 | "outputs": [], 98 | "source": [ 99 | "gedi02a" 100 | ] 101 | }, 102 | { 103 | "cell_type": "code", 104 | "execution_count": null, 105 | "id": "eeade03e-0dc7-42fe-a879-8e59cce8e6af", 106 | "metadata": {}, 107 | "outputs": [], 108 | "source": [ 109 | "# plot elevations\n", 110 | "f, ax = plt.subplots(1, 2, figsize=[12,8])\n", 111 | "ax[0].set_title(\"Elevations Highest Return\")\n", 112 | "ax[0].set_aspect('equal')\n", 113 | "vmin_hr, vmax_hr = gedi02a['elevation_hr'].quantile((0.2, 0.8))\n", 114 | "gedi02a.plot(ax=ax[0], column='elevation_hr', cmap='inferno', s=0.1, vmin=vmin_hr, vmax=vmax_hr)\n", 115 | "ax[1].set_title(\"Elevations Lowest Mode\")\n", 116 | "ax[1].set_aspect('equal')\n", 117 | "vmin_lm, vmax_lm = gedi02a['elevation_lm'].quantile((0.2, 0.8))\n", 118 | "gedi02a.plot(ax=ax[1], column='elevation_lm', cmap='inferno', s=0.1, vmin=vmin_lm, vmax=vmax_lm)" 119 | ] 120 | } 121 | ], 122 | "metadata": { 123 | "kernelspec": { 124 | "display_name": "Python 3 (ipykernel)", 125 | "language": "python", 126 | "name": "python3" 127 | }, 128 | "language_info": { 129 | "codemirror_mode": { 130 | "name": "ipython", 131 | "version": 3 132 | }, 133 | "file_extension": ".py", 134 | "mimetype": "text/x-python", 135 | "name": "python", 136 | "nbconvert_exporter": "python", 137 | "pygments_lexer": "ipython3", 138 | "version": "3.12.0" 139 | } 140 | }, 141 | "nbformat": 4, 142 | "nbformat_minor": 5 143 | } 144 | -------------------------------------------------------------------------------- /examples/gedi_l2a_access.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "3449588e-085e-41c9-9e1d-76d737d36be4", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "# Imports\n", 11 | "import logging\n", 12 | "import matplotlib.pyplot as plt\n", 13 | "from sliderule import sliderule, gedi, earthdata" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": null, 19 | "id": "929ac73a-f0d5-421d-a56f-c59d121e3939", 20 | "metadata": {}, 21 | "outputs": [], 22 | "source": [ 23 | "# Configuration\n", 24 | "verbose = True\n", 25 | "loglevel = logging.INFO" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "id": "32eecf8e-0028-49a8-9ce4-2a506bb48689", 31 | "metadata": {}, 32 | "source": [ 33 | "## How to access GEDI02_A data for an area of interest\n", 34 | "\n", 35 | "The code below takes about 30 seconds to execute and processes the 138 GEDI L2A granules that intersect the area of interest defined by the grandmesa.geojson file. It is also filtering all measurements that don't have the L2 quality flag set or have the degrade flag set." 36 | ] 37 | }, 38 | { 39 | "cell_type": "code", 40 | "execution_count": null, 41 | "id": "18619c6a-0d3f-4637-92ab-6697d1e20743", 42 | "metadata": {}, 43 | "outputs": [], 44 | "source": [ 45 | "# call sliderule\n", 46 | "gedi.init(verbose=verbose, loglevel=loglevel)\n", 47 | "parms = {\n", 48 | " \"poly\": sliderule.toregion(\"grandmesa.geojson\")[\"poly\"],\n", 49 | " \"degrade_flag\": 0,\n", 50 | " \"l2_quality_flag\": 1,\n", 51 | " \"beam\": gedi.ALL_BEAMS\n", 52 | "}\n", 53 | "gedi02a = gedi.gedi02ap(parms)\n", 54 | "gedi02a" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": null, 60 | "id": "64902c02-f182-4942-9e87-13f3869aaa5b", 61 | "metadata": {}, 62 | "outputs": [], 63 | "source": [ 64 | "# plot elevations\n", 65 | "f, ax = plt.subplots(1, 1, figsize=[12,8])\n", 66 | "ax.set_title(\"Elevations Lowest Mode\")\n", 67 | "ax.set_aspect('equal')\n", 68 | "vmin_lm, vmax_lm = gedi02a['elevation_lm'].quantile((0.2, 0.8))\n", 69 | "gedi02a.plot(ax=ax, column='elevation_lm', cmap='inferno', s=0.1, vmin=vmin_lm, vmax=vmax_lm)" 70 | ] 71 | }, 72 | { 73 | "cell_type": "markdown", 74 | "id": "65812a1c-76b4-4e18-a021-3f4d2e2ac396", 75 | "metadata": {}, 76 | "source": [ 77 | "## How to list GEDI02_A granules that intersect an area of interest\n", 78 | "\n", 79 | "If you are just interested in knowing what granules intersect an area of interest, you can use the `earthdata` module in the SlideRule client." 80 | ] 81 | }, 82 | { 83 | "cell_type": "code", 84 | "execution_count": null, 85 | "id": "e686b75a-a05d-4c81-9937-f63ef7c63815", 86 | "metadata": { 87 | "scrolled": true 88 | }, 89 | "outputs": [], 90 | "source": [ 91 | "region = sliderule.toregion(\"grandmesa.geojson\")\n", 92 | "granules = earthdata.cmr(short_name=\"GEDI02_A\", polygon=region[\"poly\"])\n", 93 | "granules" 94 | ] 95 | }, 96 | { 97 | "cell_type": "markdown", 98 | "id": "468c87e8-a5f3-4a5a-a1eb-ea8307d0ac09", 99 | "metadata": {}, 100 | "source": [ 101 | "## How to sample 3DEP at each GEDI02_A point for a granule in the area of interest\n", 102 | "\n", 103 | "The code below reads a GEDI L2A granule and for each elevation it samples the 3DEP 1m DEM raster whose measurements are closest in time to the GEDI measurement. The resulting data frame includes the data from both GEDI and 3DEP." 104 | ] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "execution_count": null, 109 | "id": "19aa29d4-3736-4600-ab25-61a6bff7bdbd", 110 | "metadata": {}, 111 | "outputs": [], 112 | "source": [ 113 | "# call sliderule\n", 114 | "gedi.init(verbose=verbose, loglevel=loglevel)\n", 115 | "parms = {\n", 116 | " \"poly\": sliderule.toregion(\"grandmesa.geojson\")[\"poly\"],\n", 117 | " \"degrade_flag\": 0,\n", 118 | " \"quality_flag\": 1,\n", 119 | " \"beam\": 11,\n", 120 | " \"samples\": {\"3dep\": {\"asset\": \"usgs3dep-1meter-dem\", \"use_poi_time\": True}} \n", 121 | "}\n", 122 | "gedi02a = gedi.gedi02ap(parms, resources=['GEDI02_A_2019109210809_O01988_03_T02056_02_003_01_V002.h5'])\n", 123 | "gedi02a" 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": null, 129 | "id": "f9e94cdc-08f8-473d-84a8-1366750117eb", 130 | "metadata": {}, 131 | "outputs": [], 132 | "source": [ 133 | "# plot elevations\n", 134 | "gdf = gedi02a[gedi02a[\"3dep.value\"].notna()]\n", 135 | "fig,ax = plt.subplots(num=None, figsize=(10, 8))\n", 136 | "fig.set_facecolor('white')\n", 137 | "fig.canvas.header_visible = False\n", 138 | "ax.set_title(\"Elevations between GEDI and 3DEP\")\n", 139 | "ax.set_xlabel('UTC')\n", 140 | "ax.set_ylabel('height (m)')\n", 141 | "ax.yaxis.grid(True)\n", 142 | "sc1 = ax.scatter(gdf.index.values, gdf[\"elevation_lm\"].values, c='blue', s=2.5)\n", 143 | "sc2 = ax.scatter(gdf.index.values, gdf[\"3dep.value\"].values, c='red', s=2.5)\n", 144 | "plt.show()" 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": null, 150 | "id": "616cf11c-72da-4949-b5d0-6c358fff5e8d", 151 | "metadata": {}, 152 | "outputs": [], 153 | "source": [] 154 | } 155 | ], 156 | "metadata": { 157 | "kernelspec": { 158 | "display_name": "Python 3 (ipykernel)", 159 | "language": "python", 160 | "name": "python3" 161 | }, 162 | "language_info": { 163 | "codemirror_mode": { 164 | "name": "ipython", 165 | "version": 3 166 | }, 167 | "file_extension": ".py", 168 | "mimetype": "text/x-python", 169 | "name": "python", 170 | "nbconvert_exporter": "python", 171 | "pygments_lexer": "ipython3", 172 | "version": "3.12.0" 173 | } 174 | }, 175 | "nbformat": 4, 176 | "nbformat_minor": 5 177 | } 178 | -------------------------------------------------------------------------------- /examples/gedi_l3_sample.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "29ec1570-d65b-4e52-9d4d-d93604882190", 6 | "metadata": {}, 7 | "source": [ 8 | "## GEDI L3 Example\n", 9 | "\n", 10 | "### Purpose\n", 11 | "Demonstrate how to sample the GEDI L3 rasters at generated ATL06-SR points" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "id": "e29fa51f-77bf-4c55-a99e-a4f166833755", 17 | "metadata": {}, 18 | "source": [ 19 | "#### Import Packages" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": null, 25 | "id": "9dada6f9-e621-4a3a-825b-065ef6846645", 26 | "metadata": { 27 | "tags": [] 28 | }, 29 | "outputs": [], 30 | "source": [ 31 | "import matplotlib.pyplot as plt\n", 32 | "import matplotlib\n", 33 | "import sliderule\n", 34 | "from sliderule import icesat2, raster" 35 | ] 36 | }, 37 | { 38 | "cell_type": "markdown", 39 | "id": "53e68348-2d49-4e22-b665-1acd8b367dcf", 40 | "metadata": {}, 41 | "source": [ 42 | "#### Initialize SlideRule Python Client" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": null, 48 | "id": "93edfc47-1cd5-4927-962c-fd447c9e807a", 49 | "metadata": { 50 | "tags": [] 51 | }, 52 | "outputs": [], 53 | "source": [ 54 | "icesat2.init(\"slideruleearth.io\", verbose=True)" 55 | ] 56 | }, 57 | { 58 | "cell_type": "markdown", 59 | "id": "c588e3ea-8ab8-452b-8f5a-9fd8d6364ca9", 60 | "metadata": { 61 | "tags": [] 62 | }, 63 | "source": [ 64 | "#### Make Processing Request to SlideRule\n", 65 | "ATL06-SR request includes the `samples` parameter to specify that GEDI L3 Mean Elevation dataset should be sampled at each generated ATL06 elevation." 66 | ] 67 | }, 68 | { 69 | "cell_type": "code", 70 | "execution_count": null, 71 | "id": "4ebef6dc-c05d-4b97-973c-05da9565e841", 72 | "metadata": { 73 | "tags": [] 74 | }, 75 | "outputs": [], 76 | "source": [ 77 | "resource = \"ATL03_20220105023009_02111406_005_01.h5\"\n", 78 | "region = sliderule.toregion('grandmesa.geojson')\n", 79 | "bbox = raster.poly2bbox(region[\"poly\"])\n", 80 | "parms = { \"poly\": region['poly'],\n", 81 | " \"cnf\": \"atl03_high\",\n", 82 | " \"ats\": 5.0,\n", 83 | " \"cnt\": 5,\n", 84 | " \"len\": 20.0,\n", 85 | " \"res\": 10.0,\n", 86 | " \"samples\": {\"gedi\": {\"asset\": \"gedil3-elevation\", \"aoi_bbox\": bbox}} \n", 87 | "}\n", 88 | "gdf = icesat2.atl06p(parms, resources=[resource])" 89 | ] 90 | }, 91 | { 92 | "cell_type": "markdown", 93 | "id": "b779ddf2-f9ea-41c2-bb9a-1db92e277fe7", 94 | "metadata": {}, 95 | "source": [ 96 | "#### Display GeoDataFrame\n", 97 | "Notice the columns that start with \"gedi\"" 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": null, 103 | "id": "e19bae20-140e-4d55-bb73-64a9630096d1", 104 | "metadata": { 105 | "tags": [] 106 | }, 107 | "outputs": [], 108 | "source": [ 109 | "gdf" 110 | ] 111 | }, 112 | { 113 | "cell_type": "markdown", 114 | "id": "6178683e-2d08-4ccb-a80e-4bb997876330", 115 | "metadata": {}, 116 | "source": [ 117 | "#### Print Out File Directory\n", 118 | "When a GeoDataFrame includes samples from rasters, each sample value has a file id that is used to look up the file name of the source raster for that value." 119 | ] 120 | }, 121 | { 122 | "cell_type": "code", 123 | "execution_count": null, 124 | "id": "b4c99349-c44e-4e59-bd31-ad6121df2f80", 125 | "metadata": {}, 126 | "outputs": [], 127 | "source": [ 128 | "gdf.attrs['file_directory']" 129 | ] 130 | }, 131 | { 132 | "cell_type": "code", 133 | "execution_count": null, 134 | "id": "06979327-fe4d-4a45-b8da-21356296a341", 135 | "metadata": { 136 | "tags": [] 137 | }, 138 | "outputs": [], 139 | "source": [ 140 | "gdf[\"gedi.value\"]" 141 | ] 142 | }, 143 | { 144 | "cell_type": "markdown", 145 | "id": "32beb064-f10f-46e1-8756-a03756e069fd", 146 | "metadata": {}, 147 | "source": [ 148 | "#### Plot the Different GEDI Values against the SlideRule ATL06-SR Values" 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": null, 154 | "id": "12645d05-fda6-44bd-878b-37b0aa217065", 155 | "metadata": {}, 156 | "outputs": [], 157 | "source": [ 158 | "# Setup Plot\n", 159 | "fig,ax = plt.subplots(num=None, figsize=(10, 8))\n", 160 | "fig.set_facecolor('white')\n", 161 | "fig.canvas.header_visible = False\n", 162 | "ax.set_title(\"SlideRule vs. GEDI Elevations\")\n", 163 | "ax.set_xlabel('UTC')\n", 164 | "ax.set_ylabel('height (m)')\n", 165 | "legend_elements = []\n", 166 | "\n", 167 | "# Plot SlideRule ATL06 Elevations\n", 168 | "df = gdf[(gdf['rgt'] == 211) & (gdf['gt'] == 30) & (gdf['cycle'] == 14)]\n", 169 | "sc1 = ax.scatter(df.index.values, df[\"h_mean\"].values, c='red', s=2.5)\n", 170 | "legend_elements.append(matplotlib.lines.Line2D([0], [0], color='red', lw=6, label='ATL06-SR'))\n", 171 | "\n", 172 | "# Plot GEDI Elevations\n", 173 | "sc2 = ax.scatter(df.index.values, df[\"gedi.value\"].values, c='blue', s=2.5)\n", 174 | "legend_elements.append(matplotlib.lines.Line2D([0], [0], color='blue', lw=6, label='GEDI'))\n", 175 | "\n", 176 | "# Display Legend\n", 177 | "lgd = ax.legend(handles=legend_elements, loc=3, frameon=True)\n", 178 | "lgd.get_frame().set_alpha(1.0)\n", 179 | "lgd.get_frame().set_edgecolor('white')\n", 180 | "\n", 181 | "# Show Plot\n", 182 | "plt.show()" 183 | ] 184 | }, 185 | { 186 | "cell_type": "code", 187 | "execution_count": null, 188 | "id": "5a5455a6-f20b-4ddc-8ebf-a1904c2987dc", 189 | "metadata": {}, 190 | "outputs": [], 191 | "source": [] 192 | } 193 | ], 194 | "metadata": { 195 | "kernelspec": { 196 | "display_name": "Python 3 (ipykernel)", 197 | "language": "python", 198 | "name": "python3" 199 | }, 200 | "language_info": { 201 | "codemirror_mode": { 202 | "name": "ipython", 203 | "version": 3 204 | }, 205 | "file_extension": ".py", 206 | "mimetype": "text/x-python", 207 | "name": "python", 208 | "nbconvert_exporter": "python", 209 | "pygments_lexer": "ipython3", 210 | "version": "3.12.0" 211 | } 212 | }, 213 | "nbformat": 4, 214 | "nbformat_minor": 5 215 | } 216 | -------------------------------------------------------------------------------- /examples/gedi_l4a.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "0e7a3594-2353-476d-a42a-b6bf1b279c61", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "import time\n", 11 | "import matplotlib.pyplot as plt\n", 12 | "from sliderule import gedi\n", 13 | "import sliderule" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": null, 19 | "id": "54acbd2a-23d8-4cb9-ac06-b3893a3b45db", 20 | "metadata": {}, 21 | "outputs": [], 22 | "source": [ 23 | "# initialize client (notebook only processes one granule, so one node is sufficient)\n", 24 | "gedi.init(\"slideruleearth.io\", verbose=True)" 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": null, 30 | "id": "e1421300-b0d1-4139-928e-f8bc612a8ac9", 31 | "metadata": {}, 32 | "outputs": [], 33 | "source": [ 34 | "# Specify region of interest from geojson\n", 35 | "poly_fn = 'grandmesa.geojson'\n", 36 | "region = sliderule.toregion(poly_fn)" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": null, 42 | "id": "b2e2f316-35e2-4345-bd05-8327e031ba0e", 43 | "metadata": {}, 44 | "outputs": [], 45 | "source": [ 46 | "# Build GEDI L4A Request Parameters\n", 47 | "parms = {\n", 48 | " \"poly\": region[\"poly\"],\n", 49 | " \"degrade_flag\": 0,\n", 50 | " \"l2_quality_flag\": 1,\n", 51 | " \"beam\": 0\n", 52 | "}" 53 | ] 54 | }, 55 | { 56 | "cell_type": "code", 57 | "execution_count": null, 58 | "id": "02682045-e626-4864-b4eb-1ed04d77d778", 59 | "metadata": {}, 60 | "outputs": [], 61 | "source": [ 62 | "# Latch Start Time\n", 63 | "perf_start = time.perf_counter()\n", 64 | "\n", 65 | "# Request GEDI Data\n", 66 | "gedi04a = gedi.gedi04ap(parms, resources=['GEDI04_A_2019123154305_O02202_03_T00174_02_002_02_V002.h5'])\n", 67 | " \n", 68 | "# Latch Stop Time\n", 69 | "perf_stop = time.perf_counter()\n", 70 | "\n", 71 | "# Display Statistics\n", 72 | "perf_duration = perf_stop - perf_start\n", 73 | "print(\"Completed in {:.3f} seconds of wall-clock time\".format(perf_duration))\n", 74 | "print(\"Received {} footprints\".format(gedi04a.shape[0]))\n", 75 | "if len(gedi04a) > 0:\n", 76 | " print(\"Beams: {}\".format(gedi04a[\"beam\"].unique()))" 77 | ] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "execution_count": null, 82 | "id": "92d482e1-08e2-49a4-9ff7-155b7952e178", 83 | "metadata": {}, 84 | "outputs": [], 85 | "source": [ 86 | "gedi04a" 87 | ] 88 | }, 89 | { 90 | "cell_type": "code", 91 | "execution_count": null, 92 | "id": "eeade03e-0dc7-42fe-a879-8e59cce8e6af", 93 | "metadata": {}, 94 | "outputs": [], 95 | "source": [ 96 | "# plot elevations and vegetation density\n", 97 | "f, ax = plt.subplots(1, 2, figsize=[12,8])\n", 98 | "ax[0].set_title(\"Elevations\")\n", 99 | "ax[0].set_aspect('equal')\n", 100 | "gedi04a.plot(ax=ax[0], column='elevation', cmap='inferno', s=0.1)\n", 101 | "ax[1].set_title(\"Vegetation Density\")\n", 102 | "ax[1].set_aspect('equal')\n", 103 | "gedi04a.plot(ax=ax[1], column='agbd', cmap='inferno', s=0.1)" 104 | ] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "execution_count": null, 109 | "id": "b7243ae5-f3bb-4219-8486-bde773f4effa", 110 | "metadata": {}, 111 | "outputs": [], 112 | "source": [] 113 | } 114 | ], 115 | "metadata": { 116 | "kernelspec": { 117 | "display_name": "Python 3 (ipykernel)", 118 | "language": "python", 119 | "name": "python3" 120 | }, 121 | "language_info": { 122 | "codemirror_mode": { 123 | "name": "ipython", 124 | "version": 3 125 | }, 126 | "file_extension": ".py", 127 | "mimetype": "text/x-python", 128 | "name": "python", 129 | "nbconvert_exporter": "python", 130 | "pygments_lexer": "ipython3", 131 | "version": "3.11.3" 132 | } 133 | }, 134 | "nbformat": 4, 135 | "nbformat_minor": 5 136 | } 137 | -------------------------------------------------------------------------------- /examples/gedi_l4b_sample.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "29ec1570-d65b-4e52-9d4d-d93604882190", 6 | "metadata": {}, 7 | "source": [ 8 | "## GEDI L4B Example\n", 9 | "\n", 10 | "### Purpose\n", 11 | "Demonstrate how to sample the GEDI L4B raster for BioDensity at generated PhoREAL points " 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "id": "e29fa51f-77bf-4c55-a99e-a4f166833755", 17 | "metadata": {}, 18 | "source": [ 19 | "#### Import Packages" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": null, 25 | "id": "9dada6f9-e621-4a3a-825b-065ef6846645", 26 | "metadata": { 27 | "tags": [] 28 | }, 29 | "outputs": [], 30 | "source": [ 31 | "import matplotlib.pyplot as plt\n", 32 | "import matplotlib\n", 33 | "import sliderule\n", 34 | "from sliderule import icesat2" 35 | ] 36 | }, 37 | { 38 | "cell_type": "markdown", 39 | "id": "53e68348-2d49-4e22-b665-1acd8b367dcf", 40 | "metadata": {}, 41 | "source": [ 42 | "#### Initialize SlideRule Python Client" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": null, 48 | "id": "93edfc47-1cd5-4927-962c-fd447c9e807a", 49 | "metadata": { 50 | "tags": [] 51 | }, 52 | "outputs": [], 53 | "source": [ 54 | "icesat2.init(\"slideruleearth.io\", verbose=True)" 55 | ] 56 | }, 57 | { 58 | "cell_type": "markdown", 59 | "id": "c588e3ea-8ab8-452b-8f5a-9fd8d6364ca9", 60 | "metadata": { 61 | "tags": [] 62 | }, 63 | "source": [ 64 | "#### Make Processing Request to SlideRule\n", 65 | "ATL06-SR request includes the `samples` parameter to specify that GEDI L3 Mean Elevation dataset should be sampled at each generated ATL06 elevation." 66 | ] 67 | }, 68 | { 69 | "cell_type": "code", 70 | "execution_count": null, 71 | "id": "4ebef6dc-c05d-4b97-973c-05da9565e841", 72 | "metadata": { 73 | "tags": [] 74 | }, 75 | "outputs": [], 76 | "source": [ 77 | "resource = \"ATL03_20220105023009_02111406_005_01.h5\"\n", 78 | "region = sliderule.toregion('grandmesa.geojson')\n", 79 | "parms = { \n", 80 | " \"poly\": region['poly'],\n", 81 | " \"srt\": icesat2.SRT_LAND,\n", 82 | " \"len\": 100,\n", 83 | " \"res\": 100,\n", 84 | " \"pass_invalid\": True, \n", 85 | " \"atl08_class\": [\"atl08_ground\", \"atl08_canopy\", \"atl08_top_of_canopy\"],\n", 86 | " \"phoreal\": {\"binsize\": 1.0, \"geoloc\": \"center\", \"use_abs_h\": False, \"send_waveform\": False},\n", 87 | " \"samples\": {\"gedi\": {\"asset\": \"gedil4b\"}} \n", 88 | "}\n", 89 | "gdf = icesat2.atl08p(parms, resources=[resource], keep_id=True)" 90 | ] 91 | }, 92 | { 93 | "cell_type": "markdown", 94 | "id": "b779ddf2-f9ea-41c2-bb9a-1db92e277fe7", 95 | "metadata": {}, 96 | "source": [ 97 | "#### Display GeoDataFrame Columns\n", 98 | "Notice the columns that start with \"gedi\", they are the sampled raster data" 99 | ] 100 | }, 101 | { 102 | "cell_type": "code", 103 | "execution_count": null, 104 | "id": "e19bae20-140e-4d55-bb73-64a9630096d1", 105 | "metadata": { 106 | "tags": [] 107 | }, 108 | "outputs": [], 109 | "source": [ 110 | "gdf.keys()" 111 | ] 112 | }, 113 | { 114 | "cell_type": "markdown", 115 | "id": "6178683e-2d08-4ccb-a80e-4bb997876330", 116 | "metadata": {}, 117 | "source": [ 118 | "#### Print Out File Directory\n", 119 | "When a GeoDataFrame includes samples from rasters, each sample value has a file id that is used to look up the file name of the source raster for that value." 120 | ] 121 | }, 122 | { 123 | "cell_type": "code", 124 | "execution_count": null, 125 | "id": "b4c99349-c44e-4e59-bd31-ad6121df2f80", 126 | "metadata": {}, 127 | "outputs": [], 128 | "source": [ 129 | "gdf.attrs['file_directory']" 130 | ] 131 | }, 132 | { 133 | "cell_type": "markdown", 134 | "id": "6cb9005b-0a80-41fa-a31c-60c5b334dd43", 135 | "metadata": { 136 | "tags": [] 137 | }, 138 | "source": [ 139 | "#### Filter GeoDataFrame Based on Valid Values" 140 | ] 141 | }, 142 | { 143 | "cell_type": "code", 144 | "execution_count": null, 145 | "id": "477dfe8b-28a7-497a-b67a-139f544b2f14", 146 | "metadata": { 147 | "tags": [] 148 | }, 149 | "outputs": [], 150 | "source": [ 151 | "df = gdf[gdf[\"gedi.value\"] > -9999.0]\n", 152 | "df" 153 | ] 154 | }, 155 | { 156 | "cell_type": "markdown", 157 | "id": "32beb064-f10f-46e1-8756-a03756e069fd", 158 | "metadata": {}, 159 | "source": [ 160 | "#### Plot the Different GEDI Values against the SlideRule PhoREAL Values" 161 | ] 162 | }, 163 | { 164 | "cell_type": "code", 165 | "execution_count": null, 166 | "id": "12645d05-fda6-44bd-878b-37b0aa217065", 167 | "metadata": {}, 168 | "outputs": [], 169 | "source": [ 170 | "# Setup Plot\n", 171 | "fig,ax = plt.subplots(num=None, figsize=(10, 8))\n", 172 | "fig.set_facecolor('white')\n", 173 | "fig.canvas.header_visible = False\n", 174 | "ax.set_title(\"SlideRule vs. GEDI Elevations\")\n", 175 | "ax.set_xlabel('UTC')\n", 176 | "ax.set_ylabel('height (m)')\n", 177 | "legend_elements = []\n", 178 | "\n", 179 | "# Plot SlideRule ATL06 Elevations\n", 180 | "sc1 = ax.scatter(df.index.values, df[\"veg_ph_count\"].values, c='red', s=2.5)\n", 181 | "legend_elements.append(matplotlib.lines.Line2D([0], [0], color='red', lw=6, label='ATL06-SR'))\n", 182 | "\n", 183 | "# Plot GEDI Elevations\n", 184 | "sc2 = ax.scatter(df.index.values, df[\"gedi.value\"].values, c='blue', s=2.5)\n", 185 | "legend_elements.append(matplotlib.lines.Line2D([0], [0], color='blue', lw=6, label='GEDI'))\n", 186 | "\n", 187 | "# Display Legend\n", 188 | "lgd = ax.legend(handles=legend_elements, loc=3, frameon=True)\n", 189 | "lgd.get_frame().set_alpha(1.0)\n", 190 | "lgd.get_frame().set_edgecolor('white')\n", 191 | "\n", 192 | "# Show Plot\n", 193 | "plt.show()" 194 | ] 195 | }, 196 | { 197 | "cell_type": "code", 198 | "execution_count": null, 199 | "id": "b5881734-c907-4944-8ce0-819551d632b9", 200 | "metadata": {}, 201 | "outputs": [], 202 | "source": [] 203 | } 204 | ], 205 | "metadata": { 206 | "kernelspec": { 207 | "display_name": "Python 3 (ipykernel)", 208 | "language": "python", 209 | "name": "python3" 210 | }, 211 | "language_info": { 212 | "codemirror_mode": { 213 | "name": "ipython", 214 | "version": 3 215 | }, 216 | "file_extension": ".py", 217 | "mimetype": "text/x-python", 218 | "name": "python", 219 | "nbconvert_exporter": "python", 220 | "pygments_lexer": "ipython3", 221 | "version": "3.11.3" 222 | } 223 | }, 224 | "nbformat": 4, 225 | "nbformat_minor": 5 226 | } 227 | -------------------------------------------------------------------------------- /examples/grand_mesa_atl03_classification.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "ddce2704-937f-46be-a0c8-fd402cc50037", 6 | "metadata": {}, 7 | "source": [ 8 | "## ICESat-2 ATL03 SlideRule Demo\n", 9 | "\n", 10 | "Plot ATL03 data with different classifications for a region over the Grand Mesa, CO region \n", 11 | "\n", 12 | "- [ATL08 Land and Vegetation Height product](https://nsidc.org/data/atl08) photon classification\n", 13 | "- Experimental YAPC (Yet Another Photon Classification) photon-density-based classification\n", 14 | "\n", 15 | "### What is demonstrated\n", 16 | "\n", 17 | "* The `icesat2.atl03sp` API is used to perform a SlideRule parallel subsetting request of the Grand Mesa region\n", 18 | "* The `earthdata.cmr` API's is used to find specific ATL03 granules corresponding to the Grand Mesa region\n", 19 | "* The `matplotlib` package is used to plot the ATL03 data subset by SlideRule" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": null, 25 | "id": "fb9beaf5-794d-457e-8911-f65158139634", 26 | "metadata": { 27 | "tags": [] 28 | }, 29 | "outputs": [], 30 | "source": [ 31 | "import warnings\n", 32 | "warnings.filterwarnings(\"ignore\") # suppress warnings" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": null, 38 | "id": "54813d4f-f6bd-4fe0-b252-320a1e6804c0", 39 | "metadata": { 40 | "tags": [] 41 | }, 42 | "outputs": [], 43 | "source": [ 44 | "import numpy as np\n", 45 | "import matplotlib.pyplot as plt\n", 46 | "from sliderule import sliderule, icesat2, earthdata" 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": null, 52 | "id": "13d96296", 53 | "metadata": { 54 | "tags": [] 55 | }, 56 | "outputs": [], 57 | "source": [ 58 | "icesat2.init(\"slideruleearth.io\", verbose=False)" 59 | ] 60 | }, 61 | { 62 | "cell_type": "markdown", 63 | "id": "76b29139-5a28-4744-9436-902d203bc792", 64 | "metadata": {}, 65 | "source": [ 66 | "## Intro\n", 67 | "This notebook demonstrates how to use the SlideRule Icesat-2 API to retrieve ATL03 data with two different classifications, one based on the external ATL08-product classifications, designed to distinguish between vegetation and ground returns, and the other based on the experimental YAPC (Yet Another Photon Class) algorithm." 68 | ] 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "id": "0c29474c-b649-4739-a3b7-308c1218f76b", 73 | "metadata": {}, 74 | "source": [ 75 | "### Retrieve ATL03 elevations with ATL08 classifications\n", 76 | "\n", 77 | "define a polygon to encompass Grand Mesa, and pick an ATL03 granule that has good coverage over the top of the mesa. Note that this granule was captured at night, under clear-sky conditions. Other granules are unlikely to have results as clear s these." 78 | ] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "execution_count": null, 83 | "id": "3605d245", 84 | "metadata": { 85 | "tags": [] 86 | }, 87 | "outputs": [], 88 | "source": [ 89 | "%%time\n", 90 | "\n", 91 | "# build sliderule parameters for ATL03 subsetting request\n", 92 | "# SRT_LAND = 0\n", 93 | "# SRT_OCEAN = 1\n", 94 | "# SRT_SEA_ICE = 2\n", 95 | "# SRT_LAND_ICE = 3\n", 96 | "# SRT_INLAND_WATER = 4\n", 97 | "parms = {\n", 98 | " # processing parameters\n", 99 | " \"srt\": icesat2.SRT_LAND,\n", 100 | " \"len\": 20,\n", 101 | " \"res\": 20,\n", 102 | " # classification and checks\n", 103 | " # still return photon segments that fail checks\n", 104 | " \"pass_invalid\": True, \n", 105 | " # all photons\n", 106 | " \"cnf\": -2, \n", 107 | " # all land classification flags\n", 108 | " \"atl08_class\": [\"atl08_noise\", \"atl08_ground\", \"atl08_canopy\", \"atl08_top_of_canopy\", \"atl08_unclassified\"],\n", 109 | " # all photons\n", 110 | " \"yapc\": dict(knn=0, win_h=6, win_x=11, min_ph=4, score=0), \n", 111 | "}\n", 112 | "\n", 113 | "# ICESat-2 data release\n", 114 | "release = '006'\n", 115 | "# region of interest\n", 116 | "poly = [{'lat': 39.34603060272382, 'lon': -108.40601489205419},\n", 117 | " {'lat': 39.32770853617356, 'lon': -107.68485163209928},\n", 118 | " {'lat': 38.770676045922684, 'lon': -107.71081820956682},\n", 119 | " {'lat': 38.788639821001155, 'lon': -108.42635020791396},\n", 120 | " {'lat': 39.34603060272382, 'lon': -108.40601489205419}]\n", 121 | "# time bounds for CMR query\n", 122 | "time_start = '2019-11-14'\n", 123 | "time_end = '2019-11-15'\n", 124 | "\n", 125 | "# find granule for each region of interest\n", 126 | "granules_list = earthdata.cmr(short_name='ATL03', polygon=poly, time_start=time_start, time_end=time_end, version=release)\n", 127 | "\n", 128 | "# create an empty geodataframe\n", 129 | "parms[\"poly\"] = poly\n", 130 | "gdf = icesat2.atl03sp(parms, resources=granules_list)" 131 | ] 132 | }, 133 | { 134 | "cell_type": "code", 135 | "execution_count": null, 136 | "id": "6e7c90b1-579a-4f4e-a6ab-0c41c1195963", 137 | "metadata": { 138 | "tags": [] 139 | }, 140 | "outputs": [], 141 | "source": [ 142 | "gdf.head()" 143 | ] 144 | }, 145 | { 146 | "cell_type": "markdown", 147 | "id": "9ac6a168-a8df-46ed-bdcb-64633dedf0da", 148 | "metadata": {}, 149 | "source": [ 150 | "### Reduce GeoDataFrame to plot a single beam\n", 151 | "- Convert coordinate reference system to compound projection" 152 | ] 153 | }, 154 | { 155 | "cell_type": "code", 156 | "execution_count": null, 157 | "id": "38a6f5b5-08f0-45ff-8d8d-ab392d653a4d", 158 | "metadata": { 159 | "tags": [] 160 | }, 161 | "outputs": [], 162 | "source": [ 163 | "def reduce_dataframe(gdf, RGT=None, GT=None, track=None, pair=None, cycle=None, beam='', crs=4326):\n", 164 | " # convert coordinate reference system\n", 165 | " D3 = gdf.to_crs(crs)\n", 166 | " # reduce to reference ground track\n", 167 | " if RGT is not None:\n", 168 | " D3 = D3[D3[\"rgt\"] == RGT]\n", 169 | " # reduce to ground track (gt[123][lr]), track ([123]), or pair (l=0, r=1) \n", 170 | " gtlookup = {icesat2.GT1L: 1, icesat2.GT1R: 1, icesat2.GT2L: 2, icesat2.GT2R: 2, icesat2.GT3L: 3, icesat2.GT3R: 3}\n", 171 | " pairlookup = {icesat2.GT1L: 0, icesat2.GT1R: 1, icesat2.GT2L: 0, icesat2.GT2R: 1, icesat2.GT3L: 0, icesat2.GT3R: 1}\n", 172 | " if GT is not None:\n", 173 | " D3 = D3[(D3[\"track\"] == gtlookup[GT]) & (D3[\"pair\"] == pairlookup[GT])]\n", 174 | " if track is not None:\n", 175 | " D3 = D3[D3[\"track\"] == track]\n", 176 | " if pair is not None:\n", 177 | " D3 = D3[D3[\"pair\"] == pair]\n", 178 | " # reduce to weak or strong beams\n", 179 | " # tested on cycle 11, where the strong beam in the pair matches the spacecraft orientation.\n", 180 | " # Need to check on other cycles\n", 181 | " if (beam == 'strong'):\n", 182 | " D3 = D3[D3['sc_orient'] == D3['pair']]\n", 183 | " elif (beam == 'weak'):\n", 184 | " D3 = D3[D3['sc_orient'] != D3['pair']]\n", 185 | " # reduce to cycle\n", 186 | " if cycle is not None:\n", 187 | " D3 = D3[D3[\"cycle\"] == cycle]\n", 188 | " # otherwise, return both beams\n", 189 | " return D3" 190 | ] 191 | }, 192 | { 193 | "cell_type": "code", 194 | "execution_count": null, 195 | "id": "5afec4e1-44c0-44b6-b9dd-7eb28375dfce", 196 | "metadata": { 197 | "tags": [] 198 | }, 199 | "outputs": [], 200 | "source": [ 201 | "beam_type = 'strong'\n", 202 | "project_srs = \"EPSG:26912+EPSG:5703\"\n", 203 | "D3 = reduce_dataframe(gdf, RGT=737, track=1, beam='strong', crs=project_srs)" 204 | ] 205 | }, 206 | { 207 | "cell_type": "markdown", 208 | "id": "92ec1a67-4581-42d1-9f50-8a16586cea40", 209 | "metadata": {}, 210 | "source": [ 211 | "### Inspect Coordinate Reference System" 212 | ] 213 | }, 214 | { 215 | "cell_type": "code", 216 | "execution_count": null, 217 | "id": "9dce7e2a-a9bf-4028-bfcb-628881cddcc5", 218 | "metadata": { 219 | "tags": [] 220 | }, 221 | "outputs": [], 222 | "source": [ 223 | "D3.crs" 224 | ] 225 | }, 226 | { 227 | "cell_type": "markdown", 228 | "id": "15dd2545-818a-4cd3-aa1b-85e90be1e065", 229 | "metadata": {}, 230 | "source": [ 231 | "### Plot the ATL08 classifications" 232 | ] 233 | }, 234 | { 235 | "cell_type": "code", 236 | "execution_count": null, 237 | "id": "80e69cb2", 238 | "metadata": { 239 | "tags": [] 240 | }, 241 | "outputs": [], 242 | "source": [ 243 | "plt.figure(figsize=[8,6])\n", 244 | "\n", 245 | "colors={0:['gray', 'noise'], \n", 246 | " 4:['gray','unclassified'], \n", 247 | " 2:['green','canopy'], \n", 248 | " 3:['lime', 'canopy_top'], \n", 249 | " 1:['brown', 'ground']}\n", 250 | "d0=np.min(D3['segment_dist'])\n", 251 | "for class_val, color_name in colors.items():\n", 252 | " ii=D3['atl08_class']==class_val\n", 253 | " plt.plot(D3['segment_dist'][ii]+D3['x_atc'][ii]-d0, D3['height'][ii],'o', \n", 254 | " markersize=1, color=color_name[0], label=color_name[1])\n", 255 | "hl=plt.legend(loc=3, frameon=False, markerscale=5)\n", 256 | "plt.gca().set_xlim([26000, 30000])\n", 257 | "plt.gca().set_ylim([2950, 3150])\n", 258 | "\n", 259 | "plt.ylabel('height, m')\n", 260 | "plt.xlabel('$x_{ATC}$, m');" 261 | ] 262 | }, 263 | { 264 | "cell_type": "markdown", 265 | "id": "a7b578b0-393b-4812-bee7-08749311d4b4", 266 | "metadata": {}, 267 | "source": [ 268 | "### Plot the YAPC classifications" 269 | ] 270 | }, 271 | { 272 | "cell_type": "code", 273 | "execution_count": null, 274 | "id": "2ec925a6-161c-4eb8-bfd8-4aa0a785f124", 275 | "metadata": { 276 | "tags": [] 277 | }, 278 | "outputs": [], 279 | "source": [ 280 | "plt.figure(figsize=[10,6])\n", 281 | "\n", 282 | "d0=np.min(D3['segment_dist'])\n", 283 | "ii=np.argsort(D3['yapc_score'])\n", 284 | "plt.scatter(D3['segment_dist'][ii]+D3['x_atc'][ii]-d0,\n", 285 | " D3['height'][ii],2, c=D3['yapc_score'][ii],\n", 286 | " vmin=100, vmax=255, cmap='plasma_r')\n", 287 | "plt.colorbar(label='YAPC score')\n", 288 | "plt.gca().set_xlim([26000, 30000])\n", 289 | "plt.gca().set_ylim([2950, 3150])\n", 290 | "\n", 291 | "plt.ylabel('height, m')\n", 292 | "plt.xlabel('$x_{ATC}$, m');" 293 | ] 294 | }, 295 | { 296 | "cell_type": "code", 297 | "execution_count": null, 298 | "id": "0ba4dfff-e07a-4008-820a-5a6a24f33e62", 299 | "metadata": {}, 300 | "outputs": [], 301 | "source": [] 302 | } 303 | ], 304 | "metadata": { 305 | "kernelspec": { 306 | "display_name": "Python 3 (ipykernel)", 307 | "language": "python", 308 | "name": "python3" 309 | }, 310 | "language_info": { 311 | "codemirror_mode": { 312 | "name": "ipython", 313 | "version": 3 314 | }, 315 | "file_extension": ".py", 316 | "mimetype": "text/x-python", 317 | "name": "python", 318 | "nbconvert_exporter": "python", 319 | "pygments_lexer": "ipython3", 320 | "version": "3.12.0" 321 | } 322 | }, 323 | "nbformat": 4, 324 | "nbformat_minor": 5 325 | } 326 | -------------------------------------------------------------------------------- /examples/grandmesa.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "name": "grand_mesa_poly", 4 | "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, 5 | "features": [ 6 | { "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -108.311682565537666, 39.137576462129438 ], [ -108.341156683252237, 39.037589876133246 ], [ -108.287868638779599, 38.89051431295789 ], [ -108.207729687800509, 38.823205529198098 ], [ -108.074601643110313, 38.847513782586297 ], [ -107.985605104949812, 38.943991201101703 ], [ -107.728398587557521, 39.015109302306328 ], [ -107.787241424909936, 39.195630349659986 ], [ -108.049394800987542, 39.139504663354245 ], [ -108.172870009708575, 39.159200663961158 ], [ -108.311682565537666, 39.137576462129438 ] ] ] } } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /examples/multi_mission_grand_mesa.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "29ec1570-d65b-4e52-9d4d-d93604882190", 6 | "metadata": {}, 7 | "source": [ 8 | "## Multi-Mission Grand Mesa Example\n", 9 | "\n", 10 | "### Purpose\n", 11 | "Demonstrate how to process and sample the various datasets SlideRule supports over the Grand Mesa Colorado region." 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "id": "e29fa51f-77bf-4c55-a99e-a4f166833755", 17 | "metadata": {}, 18 | "source": [ 19 | "#### Import Packages" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": null, 25 | "id": "9dada6f9-e621-4a3a-825b-065ef6846645", 26 | "metadata": { 27 | "tags": [] 28 | }, 29 | "outputs": [], 30 | "source": [ 31 | "import matplotlib.pyplot as plt\n", 32 | "import matplotlib\n", 33 | "import geopandas\n", 34 | "import sliderule\n", 35 | "from sliderule import icesat2, gedi, earthdata" 36 | ] 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "id": "53e68348-2d49-4e22-b665-1acd8b367dcf", 41 | "metadata": {}, 42 | "source": [ 43 | "#### Initialize SlideRule Python Client" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": null, 49 | "id": "93edfc47-1cd5-4927-962c-fd447c9e807a", 50 | "metadata": { 51 | "tags": [] 52 | }, 53 | "outputs": [], 54 | "source": [ 55 | "sliderule.init(\"slideruleearth.io\", verbose=True)" 56 | ] 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "id": "c588e3ea-8ab8-452b-8f5a-9fd8d6364ca9", 61 | "metadata": { 62 | "tags": [] 63 | }, 64 | "source": [ 65 | "#### Setup Processing Parameters\n", 66 | "* Single granule over the Grand Mesa region of interest\n", 67 | "* Run PhoREAL algorithm at 1m vertical bin resolution\n", 68 | "* Sampling LandSat HLS data\n", 69 | "* Sampling GEDI L4B data" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": null, 75 | "id": "d923a9e7-d634-4cb2-99ae-42f6d1f166a5", 76 | "metadata": { 77 | "tags": [] 78 | }, 79 | "outputs": [], 80 | "source": [ 81 | "resource = \"ATL03_20220105023009_02111406_005_01.h5\"" 82 | ] 83 | }, 84 | { 85 | "cell_type": "code", 86 | "execution_count": null, 87 | "id": "636f5e23-76c0-492b-9301-c47c8d39c81b", 88 | "metadata": { 89 | "tags": [] 90 | }, 91 | "outputs": [], 92 | "source": [ 93 | "region = sliderule.toregion('grandmesa.geojson')\n", 94 | "catalog = earthdata.stac(short_name=\"HLS\", polygon=region[\"poly\"], time_start=\"2022-01-01T00:00:00Z\", time_end=\"2022-03-01T00:00:00Z\", as_str=True)" 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": null, 100 | "id": "d8c1e30a-fd05-4652-8d5b-f8bc6bb30d78", 101 | "metadata": { 102 | "tags": [] 103 | }, 104 | "outputs": [], 105 | "source": [ 106 | "samples = {\n", 107 | " \"landsat\": {\n", 108 | " \"asset\": \"landsat-hls\",\n", 109 | " \"catalog\": catalog,\n", 110 | " \"closest_time\": \"2022-01-05T00:00:00Z\", \n", 111 | " \"bands\": [\"NDVI\"]\n", 112 | " },\n", 113 | " \"gedi\": {\n", 114 | " \"asset\": \"gedil4b\"\n", 115 | " } \n", 116 | "}" 117 | ] 118 | }, 119 | { 120 | "cell_type": "code", 121 | "execution_count": null, 122 | "id": "00b01ed7-c5dc-4e72-ac43-cb195b1641ab", 123 | "metadata": { 124 | "tags": [] 125 | }, 126 | "outputs": [], 127 | "source": [ 128 | "parms = { \n", 129 | " \"poly\": region['poly'],\n", 130 | " \"ats\": 5.0,\n", 131 | " \"cnt\": 5,\n", 132 | " \"len\": 20.0,\n", 133 | " \"res\": 10.0,\n", 134 | " \"atl08_class\": [\n", 135 | " \"atl08_ground\", \n", 136 | " \"atl08_canopy\", \n", 137 | " \"atl08_top_of_canopy\"\n", 138 | " ],\n", 139 | " \"phoreal\": {\n", 140 | " \"binsize\": 1.0, \n", 141 | " \"geoloc\": \"center\", \n", 142 | " \"use_abs_h\": False, \n", 143 | " \"send_waveform\": False\n", 144 | " },\n", 145 | " \"samples\": samples\n", 146 | "}" 147 | ] 148 | }, 149 | { 150 | "cell_type": "markdown", 151 | "id": "8e6e45f5-7f30-4b1f-a2d3-5dc4d03b9df8", 152 | "metadata": { 153 | "tags": [] 154 | }, 155 | "source": [ 156 | "#### Make ICESat-2 Processing Request to SlideRule (with LandSat and GEDI sampling)" 157 | ] 158 | }, 159 | { 160 | "cell_type": "code", 161 | "execution_count": null, 162 | "id": "07ad8922-8484-46ee-b259-871b8a9ef22b", 163 | "metadata": { 164 | "tags": [] 165 | }, 166 | "outputs": [], 167 | "source": [ 168 | "atl08 = icesat2.atl08p(parms, resources=[resource], keep_id=True)" 169 | ] 170 | }, 171 | { 172 | "cell_type": "markdown", 173 | "id": "b779ddf2-f9ea-41c2-bb9a-1db92e277fe7", 174 | "metadata": {}, 175 | "source": [ 176 | "#### Display ATL08 GeoDataFrame\n", 177 | "Notice the columns that start with \"landsat\" and \"gedi\"" 178 | ] 179 | }, 180 | { 181 | "cell_type": "code", 182 | "execution_count": null, 183 | "id": "e19bae20-140e-4d55-bb73-64a9630096d1", 184 | "metadata": { 185 | "tags": [] 186 | }, 187 | "outputs": [], 188 | "source": [ 189 | "atl08" 190 | ] 191 | }, 192 | { 193 | "cell_type": "code", 194 | "execution_count": null, 195 | "id": "93996a79-8238-49e5-b57f-4e687ab48f9f", 196 | "metadata": { 197 | "tags": [] 198 | }, 199 | "outputs": [], 200 | "source": [ 201 | "atl08.keys()" 202 | ] 203 | }, 204 | { 205 | "cell_type": "markdown", 206 | "id": "4bcd03a3-17ef-411b-8575-b11b36130c73", 207 | "metadata": {}, 208 | "source": [ 209 | "#### Print Out File Directory\n", 210 | "When a GeoDataFrame includes samples from rasters, each sample value has a file id that is used to look up the file name of the source raster for that value." 211 | ] 212 | }, 213 | { 214 | "cell_type": "code", 215 | "execution_count": null, 216 | "id": "b4c99349-c44e-4e59-bd31-ad6121df2f80", 217 | "metadata": {}, 218 | "outputs": [], 219 | "source": [ 220 | "atl08.attrs['file_directory']" 221 | ] 222 | }, 223 | { 224 | "cell_type": "markdown", 225 | "id": "ca445c0a-1cd4-4a6a-94fa-6796b7bf56f5", 226 | "metadata": {}, 227 | "source": [ 228 | "#### Make GEDI Process Request to SlideRule" 229 | ] 230 | }, 231 | { 232 | "cell_type": "code", 233 | "execution_count": null, 234 | "id": "d8a301c9-44a9-44b6-9870-9d7021b6ad04", 235 | "metadata": { 236 | "tags": [] 237 | }, 238 | "outputs": [], 239 | "source": [ 240 | "# Build GEDI L4A Request Parameters\n", 241 | "parms = {\n", 242 | " \"poly\": region[\"poly\"],\n", 243 | " \"degrade_flag\": 0,\n", 244 | " \"l2_quality_flag\": 1,\n", 245 | " \"beam\": 0,\n", 246 | " \"samples\": samples\n", 247 | "}\n", 248 | "\n", 249 | "# Turn verbose off\n", 250 | "#sliderule.set_verbose(False)\n", 251 | "\n", 252 | "# Request GEDI L4A Data\n", 253 | "gedi04a = gedi.gedi04ap(parms) " 254 | ] 255 | }, 256 | { 257 | "cell_type": "markdown", 258 | "id": "3c54c72b-23ee-4fad-b230-2b848c3b9739", 259 | "metadata": { 260 | "tags": [] 261 | }, 262 | "source": [ 263 | "#### Display GEDI 04A GeoDataFrame" 264 | ] 265 | }, 266 | { 267 | "cell_type": "code", 268 | "execution_count": null, 269 | "id": "ebbe90d5-d695-4818-a20a-9670dccfbff2", 270 | "metadata": { 271 | "tags": [] 272 | }, 273 | "outputs": [], 274 | "source": [ 275 | "gedi04a" 276 | ] 277 | }, 278 | { 279 | "cell_type": "markdown", 280 | "id": "fea880d6-97e9-4cf3-9640-c1f0061eef62", 281 | "metadata": { 282 | "tags": [] 283 | }, 284 | "source": [ 285 | "#### Plot GEDI L4A Data" 286 | ] 287 | }, 288 | { 289 | "cell_type": "code", 290 | "execution_count": null, 291 | "id": "652c40f7-7ed0-45fe-9fff-ed177b92e5d0", 292 | "metadata": { 293 | "tags": [] 294 | }, 295 | "outputs": [], 296 | "source": [ 297 | "f, ax = plt.subplots(1, 1, figsize=[12,8])\n", 298 | "ax.set_title(\"Above Ground BioDensity\")\n", 299 | "ax.set_aspect('equal')\n", 300 | "vmin_hr, vmax_hr = gedi04a['agbd'].quantile((0.2, 0.8))\n", 301 | "gedi04a.plot(ax=ax, column='agbd', cmap='inferno', s=1.0, vmin=vmin_hr, vmax=vmax_hr)" 302 | ] 303 | }, 304 | { 305 | "cell_type": "markdown", 306 | "id": "04d53946-7830-4cab-b9e0-5afb7cc97b9f", 307 | "metadata": { 308 | "tags": [] 309 | }, 310 | "source": [ 311 | "#### Perform Spatial Join (nearest) on GEDI L4A and ATL08/GEDI L4B/LandSat HLS Data" 312 | ] 313 | }, 314 | { 315 | "cell_type": "code", 316 | "execution_count": null, 317 | "id": "805ac5e8-5577-48e4-bd72-e331959845e1", 318 | "metadata": { 319 | "tags": [] 320 | }, 321 | "outputs": [], 322 | "source": [ 323 | "import warnings\n", 324 | "with warnings.catch_warnings():\n", 325 | " warnings.simplefilter(\"ignore\")\n", 326 | " mmds = geopandas.sjoin_nearest(atl08, gedi04a, how='left', lsuffix=\"atl08\", rsuffix=\"gedi04a\")" 327 | ] 328 | }, 329 | { 330 | "cell_type": "code", 331 | "execution_count": null, 332 | "id": "760d5317-6b5b-4e42-9588-bae4b5bdc4f5", 333 | "metadata": { 334 | "tags": [] 335 | }, 336 | "outputs": [], 337 | "source": [ 338 | "mmds" 339 | ] 340 | }, 341 | { 342 | "cell_type": "code", 343 | "execution_count": null, 344 | "id": "b793b0d7-2f1a-4c3b-9e81-e670b78e3438", 345 | "metadata": { 346 | "tags": [] 347 | }, 348 | "outputs": [], 349 | "source": [ 350 | "mmds.keys()" 351 | ] 352 | }, 353 | { 354 | "cell_type": "markdown", 355 | "id": "ef6a8bae-c64f-4c15-bd7e-bc4b8cccfa4f", 356 | "metadata": {}, 357 | "source": [ 358 | "#### Plot the Different GEDI/ATL08-PhoREAL/LandSat Values" 359 | ] 360 | }, 361 | { 362 | "cell_type": "code", 363 | "execution_count": null, 364 | "id": "12645d05-fda6-44bd-878b-37b0aa217065", 365 | "metadata": {}, 366 | "outputs": [], 367 | "source": [ 368 | "# Setup Plot\n", 369 | "fig,ax = plt.subplots(num=None, figsize=(10, 8))\n", 370 | "fig.set_facecolor('white')\n", 371 | "fig.canvas.header_visible = False\n", 372 | "ax.set_title(\"SlideRule vs. Landsat NDVI\")\n", 373 | "ax.set_xlabel('UTC')\n", 374 | "ax.set_ylabel('height (m)')\n", 375 | "legend_elements = []\n", 376 | "\n", 377 | "# Filter DataFrame\n", 378 | "df = mmds[(mmds['rgt'] == 211) & (mmds['gt'] == 30) & (mmds['cycle'] == 14)]\n", 379 | "df = df[df[\"landsat.value_gedi04a\"] < 100]\n", 380 | "df = df[df[\"gedi.value_gedi04a\"] > -100]\n", 381 | "\n", 382 | "# Plot SlideRule ATL08 Vegetation Photon Counts\n", 383 | "sc1 = ax.scatter(df.index.values, df[\"veg_ph_count\"].values, c='red', s=2.5)\n", 384 | "legend_elements.append(matplotlib.lines.Line2D([0], [0], color='red', lw=6, label='ATL08'))\n", 385 | "\n", 386 | "# Plot GEDI L4B AGBD\n", 387 | "sc2 = ax.scatter(df.index.values, df[\"gedi.value_gedi04a\"].values, c='blue', s=2.5)\n", 388 | "legend_elements.append(matplotlib.lines.Line2D([0], [0], color='blue', lw=6, label='L4B AGBD'))\n", 389 | "\n", 390 | "# Plot GEDI L4A AGBD\n", 391 | "sc3 = ax.scatter(df.index.values, df[\"agbd\"].values, c='green', s=2.5)\n", 392 | "legend_elements.append(matplotlib.lines.Line2D([0], [0], color='green', lw=6, label='L4A AGBD'))\n", 393 | "\n", 394 | "# Plot LandSat NVDI\n", 395 | "sc3 = ax.scatter(df.index.values, df[\"landsat.value_gedi04a\"].values, c='orange', s=2.5)\n", 396 | "legend_elements.append(matplotlib.lines.Line2D([0], [0], color='orange', lw=6, label='HLS NVDI'))\n", 397 | "\n", 398 | "\n", 399 | "# Display Legend\n", 400 | "lgd = ax.legend(handles=legend_elements, loc=2, frameon=True)\n", 401 | "lgd.get_frame().set_alpha(1.0)\n", 402 | "lgd.get_frame().set_edgecolor('white')\n", 403 | "\n", 404 | "# Show Plot\n", 405 | "plt.show()" 406 | ] 407 | }, 408 | { 409 | "cell_type": "code", 410 | "execution_count": null, 411 | "id": "5a5455a6-f20b-4ddc-8ebf-a1904c2987dc", 412 | "metadata": {}, 413 | "outputs": [], 414 | "source": [] 415 | } 416 | ], 417 | "metadata": { 418 | "kernelspec": { 419 | "display_name": "Python 3 (ipykernel)", 420 | "language": "python", 421 | "name": "python3" 422 | }, 423 | "language_info": { 424 | "codemirror_mode": { 425 | "name": "ipython", 426 | "version": 3 427 | }, 428 | "file_extension": ".py", 429 | "mimetype": "text/x-python", 430 | "name": "python", 431 | "nbconvert_exporter": "python", 432 | "pygments_lexer": "ipython3", 433 | "version": "3.11.3" 434 | } 435 | }, 436 | "nbformat": 4, 437 | "nbformat_minor": 5 438 | } 439 | -------------------------------------------------------------------------------- /examples/phoreal.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "b0831525-6547-40bd-9d7f-11f9082168ab", 6 | "metadata": {}, 7 | "source": [ 8 | "## PhoREAL / SlideRule Example\n", 9 | "\n", 10 | "Demonstrate running the PhoREAL algorithm in SlideRule to produce canopy metrics over the Grand Mesa, Colorado region." 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "id": "6b0d9805-5b1a-4bce-b828-6f2fee432725", 16 | "metadata": {}, 17 | "source": [ 18 | "#### Imports" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": null, 24 | "id": "2bda0412-61aa-4957-b118-ec6e20f7b453", 25 | "metadata": { 26 | "tags": [] 27 | }, 28 | "outputs": [], 29 | "source": [ 30 | "import warnings\n", 31 | "warnings.filterwarnings(\"ignore\") # suppress warnings" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": null, 37 | "id": "bcd3a1dc-2d8d-4f88-b715-059497f4d52d", 38 | "metadata": { 39 | "tags": [] 40 | }, 41 | "outputs": [], 42 | "source": [ 43 | "import matplotlib.pyplot as plt\n", 44 | "import matplotlib\n", 45 | "import geopandas\n", 46 | "import logging\n", 47 | "import sliderule\n", 48 | "from sliderule import icesat2" 49 | ] 50 | }, 51 | { 52 | "cell_type": "markdown", 53 | "id": "a9cdeed4-810c-4540-869b-78e09591b68c", 54 | "metadata": { 55 | "user_expressions": [] 56 | }, 57 | "source": [ 58 | "#### Initialize Client\n", 59 | "* Organization currently set to \"utexas\"; if you want to be a member of the utexas SlideRule organization, make a request through the SlideRule provisioning system (https://ps.slideruleearth.io); otherwise, remove the organization parameter to default to the public SlideRule cluster.\n", 60 | "* Notebook only processes one granule, so one desired_node is sufficient" 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": null, 66 | "id": "d8b5b11b-eea7-4922-abbc-997a670e03e6", 67 | "metadata": { 68 | "tags": [] 69 | }, 70 | "outputs": [], 71 | "source": [ 72 | "icesat2.init(\"slideruleearth.io\", verbose=True, loglevel=logging.INFO)" 73 | ] 74 | }, 75 | { 76 | "cell_type": "markdown", 77 | "id": "f4c96101-53cf-4614-80a2-e50afcf65a03", 78 | "metadata": { 79 | "user_expressions": [] 80 | }, 81 | "source": [ 82 | "#### Processing parameters\n", 83 | "* 100m segments stepped every 100m\n", 84 | "* Subsetted to the Grand Mesa region\n", 85 | "* Time range is one day, Nov 14, 2019\n", 86 | "* Only processing ground, canopy, and top of canopy photons\n", 87 | "* Request the \"h_dif_ref\" variable as an ancillary field to be included in the results\n", 88 | "* Running PhoREAL algorithm using a binsize of 1m, and geolocating each segment at the center of the segment\n", 89 | "* Sending reconstructed waveforms along with metrics (for diagnostics and demonstration purposes only)" 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": null, 95 | "id": "504049cb-de86-4f6b-98a4-3a3237b17ca6", 96 | "metadata": { 97 | "tags": [] 98 | }, 99 | "outputs": [], 100 | "source": [ 101 | "parms = {\n", 102 | " \"poly\": sliderule.toregion('grandmesa.geojson')['poly'],\n", 103 | " \"t0\": '2019-11-14T00:00:00Z',\n", 104 | " \"t1\": '2019-11-15T00:00:00Z',\n", 105 | " \"srt\": icesat2.SRT_LAND,\n", 106 | " \"len\": 100,\n", 107 | " \"res\": 100,\n", 108 | " \"pass_invalid\": True, \n", 109 | " \"atl08_class\": [\"atl08_ground\", \"atl08_canopy\", \"atl08_top_of_canopy\"],\n", 110 | " \"atl08_fields\": [\"h_dif_ref\"],\n", 111 | " \"phoreal\": {\"binsize\": 1.0, \"geoloc\": \"center\", \"use_abs_h\": False, \"send_waveform\": True}\n", 112 | "}" 113 | ] 114 | }, 115 | { 116 | "cell_type": "markdown", 117 | "id": "e9bd5e72-f5d5-4686-94b2-c65f6715a877", 118 | "metadata": { 119 | "user_expressions": [] 120 | }, 121 | "source": [ 122 | "#### Make Atl08 Request" 123 | ] 124 | }, 125 | { 126 | "cell_type": "code", 127 | "execution_count": null, 128 | "id": "26d37002-0505-4175-9657-2f8bc6aa956c", 129 | "metadata": { 130 | "tags": [] 131 | }, 132 | "outputs": [], 133 | "source": [ 134 | "atl08 = icesat2.atl08p(parms, keep_id=True)" 135 | ] 136 | }, 137 | { 138 | "cell_type": "markdown", 139 | "id": "60702b08-c333-4502-b948-26015c1520d5", 140 | "metadata": { 141 | "user_expressions": [] 142 | }, 143 | "source": [ 144 | "#### Print Resulting GeoDataFrame" 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": null, 150 | "id": "2237bae5-78e9-4edf-8b24-4162039cc2be", 151 | "metadata": { 152 | "tags": [] 153 | }, 154 | "outputs": [], 155 | "source": [ 156 | "atl08" 157 | ] 158 | }, 159 | { 160 | "cell_type": "markdown", 161 | "id": "23a8280a-844e-4405-a8a2-53e2dd51f5e0", 162 | "metadata": { 163 | "user_expressions": [] 164 | }, 165 | "source": [ 166 | "#### Plot Canopy Height" 167 | ] 168 | }, 169 | { 170 | "cell_type": "code", 171 | "execution_count": null, 172 | "id": "92188f98-1d76-4807-9f36-eba16354afea", 173 | "metadata": { 174 | "tags": [] 175 | }, 176 | "outputs": [], 177 | "source": [ 178 | "canopy_gt1l = atl08[atl08['gt'] == icesat2.GT1L]\n", 179 | "canopy_gt1l.plot.scatter(x='x_atc', y='h_canopy')" 180 | ] 181 | }, 182 | { 183 | "cell_type": "markdown", 184 | "id": "d3665d73-2745-4ba6-b209-5cf7a6abe693", 185 | "metadata": { 186 | "user_expressions": [] 187 | }, 188 | "source": [ 189 | "#### Plot Landcover" 190 | ] 191 | }, 192 | { 193 | "cell_type": "code", 194 | "execution_count": null, 195 | "id": "d7f8fe31-ad77-4ea1-9165-6e295434f125", 196 | "metadata": { 197 | "tags": [] 198 | }, 199 | "outputs": [], 200 | "source": [ 201 | "atl08.plot('landcover')" 202 | ] 203 | }, 204 | { 205 | "cell_type": "markdown", 206 | "id": "6fc1e14d-8936-4f26-a6ca-a2361f54ef4d", 207 | "metadata": { 208 | "user_expressions": [] 209 | }, 210 | "source": [ 211 | "#### Create and Plot 75th percentile Across All Ground Tracks" 212 | ] 213 | }, 214 | { 215 | "cell_type": "code", 216 | "execution_count": null, 217 | "id": "5127ea05-56f9-4c4d-88d4-2a74f9ef5eee", 218 | "metadata": { 219 | "tags": [] 220 | }, 221 | "outputs": [], 222 | "source": [ 223 | "atl08['75'] = atl08.apply(lambda row : row[\"canopy_h_metrics\"][icesat2.P['75']], axis = 1)\n", 224 | "atl08.plot.scatter(x='x_atc', y='75')" 225 | ] 226 | }, 227 | { 228 | "cell_type": "markdown", 229 | "id": "22804320-47c7-4965-a4d2-afc855cfd1b9", 230 | "metadata": { 231 | "user_expressions": [] 232 | }, 233 | "source": [ 234 | "#### Create Sample Waveform Plots" 235 | ] 236 | }, 237 | { 238 | "cell_type": "code", 239 | "execution_count": null, 240 | "id": "9c1ebb51-5d93-409f-ac77-b3bcdf954d94", 241 | "metadata": { 242 | "tags": [] 243 | }, 244 | "outputs": [], 245 | "source": [ 246 | "num_plots = 5\n", 247 | "waveform_index = [96, 97, 98, 100, 101]\n", 248 | "fig,ax = plt.subplots(num=1, ncols=num_plots, sharey=True, figsize=(12, 6))\n", 249 | "for x in range(num_plots):\n", 250 | " ax[x].plot([x for x in range(len(canopy_gt1l['waveform'][waveform_index[x]]))], canopy_gt1l['waveform'][waveform_index[x]], zorder=1, linewidth=1.0, color='mediumseagreen')\n", 251 | "plt.show()" 252 | ] 253 | }, 254 | { 255 | "cell_type": "markdown", 256 | "id": "5da80824-8569-4d87-9544-6bc409956893", 257 | "metadata": { 258 | "user_expressions": [] 259 | }, 260 | "source": [ 261 | "#### Make Atl06 Request\n", 262 | "* Below we run an ATL06-SR processing request on the same source data using the same parameters. Because the `keep_id` argument is set to true here and above when we made the ATL08 request, we can merge the resulting dataframes and have a single table of both elevation data using the customized ATL06-SR algorithm, and vegatation data using the PhoREAL algorithm." 263 | ] 264 | }, 265 | { 266 | "cell_type": "code", 267 | "execution_count": null, 268 | "id": "ce21732e-df05-4f20-8797-202f4c0f4b74", 269 | "metadata": { 270 | "tags": [] 271 | }, 272 | "outputs": [], 273 | "source": [ 274 | "atl06 = icesat2.atl06p(parms, keep_id=True)" 275 | ] 276 | }, 277 | { 278 | "cell_type": "markdown", 279 | "id": "fd03f4ab-163a-49e3-b810-2f7d3bb37ffc", 280 | "metadata": { 281 | "user_expressions": [] 282 | }, 283 | "source": [ 284 | "#### Merge Atl06 and Atl08 GeoDataFrames" 285 | ] 286 | }, 287 | { 288 | "cell_type": "code", 289 | "execution_count": null, 290 | "id": "9ba76368-4a10-4be9-8237-f26d7b1b996e", 291 | "metadata": { 292 | "tags": [] 293 | }, 294 | "outputs": [], 295 | "source": [ 296 | "gdf = geopandas.pd.merge(atl08, atl06, on='extent_id', how='left', suffixes=('.atl08','.atl06')).set_axis(atl08.index)\n", 297 | "gdf" 298 | ] 299 | }, 300 | { 301 | "cell_type": "code", 302 | "execution_count": null, 303 | "id": "c71f9ecd-e361-4105-97f6-cefca087cfa8", 304 | "metadata": {}, 305 | "outputs": [], 306 | "source": [] 307 | } 308 | ], 309 | "metadata": { 310 | "kernelspec": { 311 | "display_name": "Python 3 (ipykernel)", 312 | "language": "python", 313 | "name": "python3" 314 | }, 315 | "language_info": { 316 | "codemirror_mode": { 317 | "name": "ipython", 318 | "version": 3 319 | }, 320 | "file_extension": ".py", 321 | "mimetype": "text/x-python", 322 | "name": "python", 323 | "nbconvert_exporter": "python", 324 | "pygments_lexer": "ipython3", 325 | "version": "3.12.0" 326 | } 327 | }, 328 | "nbformat": 4, 329 | "nbformat_minor": 5 330 | } 331 | -------------------------------------------------------------------------------- /examples/sierra.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "name": "ASO_Sierra_watersheds", 4 | "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, 5 | "features": [ 6 | { "type": "Feature", "properties": { "id": 1 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -119.925788289527532, 37.951536840384236 ], [ -119.952161517167184, 38.023342824874803 ], [ -119.916198024931276, 38.155432951752715 ], [ -119.885029664993482, 38.289166118360008 ], [ -119.647670616236425, 38.356880525522676 ], [ -119.530189874932404, 38.334316084931395 ], [ -119.398323736734056, 38.240221939121817 ], [ -119.187337915616652, 38.117717273038188 ], [ -119.168157386424198, 37.99122778233734 ], [ -119.050676645120205, 37.824760442966053 ], [ -118.921208073070872, 37.692074465643856 ], [ -118.813317596363134, 37.547746113062878 ], [ -118.624688606479069, 37.305075690515835 ], [ -118.337167696939261, 37.094503683819937 ], [ -118.23818650341731, 36.795026832112768 ], [ -118.209906162411045, 36.607966118171042 ], [ -118.499779657725298, 36.568228074728857 ], [ -118.789653153039552, 36.573906190637935 ], [ -119.022965966341275, 36.642011011083895 ], [ -119.305769376404029, 36.812009768928057 ], [ -119.659273638982398, 37.173414078225093 ], [ -119.843095855523146, 37.600346666662269 ], [ -119.94207704904511, 37.935678512509604 ], [ -119.94207704904511, 37.935678512509604 ], [ -119.932927545916598, 37.945218982909658 ], [ -119.932927545916598, 37.945218982909658 ], [ -119.925788289527532, 37.951536840384236 ] ] ] ] } } 7 | ] 8 | } -------------------------------------------------------------------------------- /examples/single_track_demo.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Single Track Demo\n", 8 | "\n", 9 | "Process a single ATL03 granule using SlideRule's ATL06-SR algorithm and compare the results to the existing ATL06 data product.\n", 10 | "\n", 11 | "### What is demonstrated\n", 12 | "\n", 13 | "* The `icesat2.atl06` API is used to perform a SlideRule processing request of a single ATL03 granule\n", 14 | "* The `h5.h5p` API is used to read existing ATL06 datasets\n", 15 | "* The `matplotlib` package is used to plot the elevation profile of all three tracks in the granule (with the first track overlaid with the expected profile)\n", 16 | "* The `geopandas` package is used to produce a plot representing the geolocation of the elevations produced by SlideRule.\n", 17 | "\n", 18 | "### Points of interest\n", 19 | "\n", 20 | "Most use cases for SlideRule use the higher level `icesat2.atl06p` API which works on a region of interest; but this notebook shows some of the capabilities of SlideRule for working on individual granules." 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": null, 26 | "metadata": { 27 | "tags": [] 28 | }, 29 | "outputs": [], 30 | "source": [ 31 | "# Suppress Warnings\n", 32 | "import warnings\n", 33 | "warnings.filterwarnings(\"ignore\")" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": null, 39 | "metadata": { 40 | "tags": [] 41 | }, 42 | "outputs": [], 43 | "source": [ 44 | "import re\n", 45 | "import posixpath\n", 46 | "import shapely.geometry\n", 47 | "import geopandas as gpd\n", 48 | "import numpy as np\n", 49 | "import matplotlib.pyplot as plt\n", 50 | "import matplotlib.dates as mdates\n", 51 | "from sliderule import icesat2, io, sliderule, earthdata, h5" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": null, 57 | "metadata": { 58 | "tags": [] 59 | }, 60 | "outputs": [], 61 | "source": [ 62 | "# Configure Session\n", 63 | "icesat2.init(\"slideruleearth.io\")" 64 | ] 65 | }, 66 | { 67 | "cell_type": "markdown", 68 | "metadata": {}, 69 | "source": [ 70 | "### Find ATL03 Granules" 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": null, 76 | "metadata": { 77 | "tags": [] 78 | }, 79 | "outputs": [], 80 | "source": [ 81 | "# find granules for a spatial and temporal query\n", 82 | "box_lon = [-105, -105, -100, -100, -105]\n", 83 | "box_lat = [-75, -77.5, -77.5, -75, -75]\n", 84 | "poly = io.to_region(box_lon, box_lat)\n", 85 | "resources = earthdata.cmr(short_name='ATL03', polygon=poly, time_start='2018-10-19', time_end='2018-10-20') \n", 86 | "granule = resources[0]" 87 | ] 88 | }, 89 | { 90 | "cell_type": "markdown", 91 | "metadata": {}, 92 | "source": [ 93 | "### Execute SlideRule Algorithm" 94 | ] 95 | }, 96 | { 97 | "cell_type": "code", 98 | "execution_count": null, 99 | "metadata": { 100 | "tags": [] 101 | }, 102 | "outputs": [], 103 | "source": [ 104 | "%%time\n", 105 | "# regular expression operator for extracting information from files\n", 106 | "rx = re.compile(r'(ATL\\d{2})(-\\d{2})?_(\\d{4})(\\d{2})(\\d{2})(\\d{2})'\n", 107 | " r'(\\d{2})(\\d{2})_(\\d{4})(\\d{2})(\\d{2})_(\\d{3})_(\\d{2})(.*?).h5$')\n", 108 | "# extract parameters from ICESat-2 granule\n", 109 | "PRD,HEM,YY,MM,DD,HH,MN,SS,TRK,CYCL,GRN,RL,VRS,AUX=rx.findall(granule).pop()\n", 110 | "\n", 111 | "# Build ATL06 Request\n", 112 | "parms = {\n", 113 | " \"poly\":poly,\n", 114 | " \"cnf\": 4,\n", 115 | " \"ats\": 20.0,\n", 116 | " \"cnt\": 10,\n", 117 | " \"len\": 40.0,\n", 118 | " \"res\": 20.0\n", 119 | "}\n", 120 | "\n", 121 | "# Request ATL06 Data\n", 122 | "gdf = icesat2.atl06(parms, granule)\n", 123 | "\n", 124 | "# Return DataFrame\n", 125 | "print(\"Reference Ground Tracks: {} to {}\".format(min(gdf[\"rgt\"]), max(gdf[\"rgt\"])))\n", 126 | "print(\"Cycle: {} to {}\".format(min(gdf[\"cycle\"]), max(gdf[\"cycle\"])))\n", 127 | "print(\"Retrieved {} points from SlideRule\".format(len(gdf[\"h_mean\"])))" 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": null, 133 | "metadata": { 134 | "tags": [] 135 | }, 136 | "outputs": [], 137 | "source": [ 138 | "def s3_retrieve(granule, **kwargs):\n", 139 | " # set default keyword arguments\n", 140 | " kwargs.setdefault('lon_key','longitude')\n", 141 | " kwargs.setdefault('lat_key','latitude')\n", 142 | " kwargs.setdefault('index_key','time')\n", 143 | " kwargs.setdefault('polygon',None)\n", 144 | " # regular expression operator for extracting information from files\n", 145 | " rx = re.compile(r'(ATL\\d{2})(-\\d{2})?_(\\d{4})(\\d{2})(\\d{2})(\\d{2})'\n", 146 | " r'(\\d{2})(\\d{2})_(\\d{4})(\\d{2})(\\d{2})_(\\d{3})_(\\d{2})(.*?).h5$')\n", 147 | " # extract parameters from ICESat-2 granule\n", 148 | " PRD,HEM,YY,MM,DD,HH,MN,SS,TRK,CYCL,GRN,RL,VRS,AUX=rx.findall(granule).pop()\n", 149 | " # variables of interest\n", 150 | " if (PRD == 'ATL06'):\n", 151 | " segment_group = \"land_ice_segments\"\n", 152 | " segment_key = 'segment_id'\n", 153 | " vnames = ['segment_id','delta_time','latitude','longitude',\n", 154 | " 'h_li','h_li_sigma','atl06_quality_summary',\n", 155 | " 'fit_statistics/dh_fit_dx','fit_statistics/dh_fit_dy',\n", 156 | " 'fit_statistics/dh_fit_dx_sigma','fit_statistics/n_fit_photons',\n", 157 | " 'fit_statistics/h_expected_rms','fit_statistics/h_robust_sprd',\n", 158 | " 'fit_statistics/w_surface_window_final','fit_statistics/h_mean']\n", 159 | " elif (PRD == 'ATL08'):\n", 160 | " segment_group = \"land_segments\"\n", 161 | " segment_key = 'segment_id_beg'\n", 162 | " vnames = ['segment_id_beg','segment_id_end','delta_time',\n", 163 | " 'latitude','longitude','brightness_flag','layer_flag',\n", 164 | " 'msw_flag','night_flag','terrain_flg','urban_flag',\n", 165 | " 'segment_landcover','segment_snowcover','segment_watermask',\n", 166 | " 'terrain/h_te_best_fit','terrain/h_te_uncertainty',\n", 167 | " 'terrain/terrain_slope','terrain/n_te_photons',\n", 168 | " 'canopy/h_canopy','canopy/h_canopy_uncertainty',\n", 169 | " 'canopy/canopy_flag','canopy/n_ca_photons']\n", 170 | " # for each valid beam within the HDF5 file\n", 171 | " frames = []\n", 172 | " gt = dict(gt1l=10,gt1r=20,gt2l=30,gt2r=40,gt3l=50,gt3r=60)\n", 173 | " atlas_sdp_epoch = np.datetime64('2018-01-01T00:00:00')\n", 174 | " kwds = dict(startrow=0,numrows=-1)\n", 175 | " for gtx in ['gt1l','gt1r','gt2l','gt2r','gt3l','gt3r']:\n", 176 | " geodatasets = [dict(dataset=f'{gtx}/{segment_group}/{v}',**kwds) for v in vnames]\n", 177 | " try:\n", 178 | " # get datasets from s3\n", 179 | " hidatasets = h5.h5p(geodatasets, granule, \"icesat2\")\n", 180 | " # copy to new \"flattened\" dictionary\n", 181 | " data = {posixpath.basename(key):var for key,var in hidatasets.items()}\n", 182 | " # Generate Time Column\n", 183 | " delta_time = (data['delta_time']*1e9).astype('timedelta64[ns]')\n", 184 | " data['time'] = gpd.pd.to_datetime(atlas_sdp_epoch + delta_time)\n", 185 | " except:\n", 186 | " pass\n", 187 | " else:\n", 188 | " # copy filename parameters\n", 189 | " data['rgt'] = [int(TRK)]*len(data['delta_time'])\n", 190 | " data['cycle'] = [int(CYCL)]*len(data['delta_time'])\n", 191 | " data['gt'] = [gt[gtx]]*len(data['delta_time'])\n", 192 | " # pandas dataframe from compiled dictionary\n", 193 | " frames.append(gpd.pd.DataFrame.from_dict(data))\n", 194 | " # concatenate pandas dataframe\n", 195 | " try:\n", 196 | " df = gpd.pd.concat(frames)\n", 197 | " except:\n", 198 | " return sliderule.emptyframe()\n", 199 | " # convert to a GeoDataFrame\n", 200 | " lon_key,lat_key = (kwargs['lon_key'],kwargs['lat_key'])\n", 201 | " geometry = gpd.points_from_xy(df[lon_key], df[lat_key])\n", 202 | " gdf = gpd.GeoDataFrame(df.drop(columns=[lon_key,lat_key]),\n", 203 | " geometry=geometry,crs='EPSG:4326')\n", 204 | " # intersect with geometry in projected reference system\n", 205 | " if kwargs['polygon'] is not None:\n", 206 | " gdf = gpd.overlay(gdf.to_crs(kwargs['polygon'].crs),\n", 207 | " kwargs['polygon'], how='intersection')\n", 208 | " # sort values for reproducible output despite async processing\n", 209 | " gdf.set_index(kwargs['index_key'], inplace=True)\n", 210 | " gdf.sort_index(inplace=True)\n", 211 | " # remove duplicate points\n", 212 | " gdf = gdf[~gdf.index.duplicated()]\n", 213 | " # convert back to original coordinate reference system\n", 214 | " return gdf.to_crs('EPSG:4326')" 215 | ] 216 | }, 217 | { 218 | "cell_type": "code", 219 | "execution_count": null, 220 | "metadata": { 221 | "tags": [] 222 | }, 223 | "outputs": [], 224 | "source": [ 225 | "# get standard ATL06 products\n", 226 | "atl06_granule = f'ATL06_{YY}{MM}{DD}{HH}{MN}{SS}_{TRK}{CYCL}{GRN}_{RL}_{VRS}{AUX}.h5'\n", 227 | "region_gs = gpd.GeoSeries(shapely.geometry.Polygon(np.c_[box_lon,box_lat]), crs='EPSG:4326')\n", 228 | "region_gdf = gpd.GeoDataFrame(geometry=region_gs).to_crs('EPSG:3857')\n", 229 | "atl06 = s3_retrieve(atl06_granule, polygon=region_gdf)" 230 | ] 231 | }, 232 | { 233 | "cell_type": "markdown", 234 | "metadata": {}, 235 | "source": [ 236 | "### Compare Sliderule and ASAS Results" 237 | ] 238 | }, 239 | { 240 | "cell_type": "code", 241 | "execution_count": null, 242 | "metadata": { 243 | "tags": [] 244 | }, 245 | "outputs": [], 246 | "source": [ 247 | "# Create Elevation Plot\n", 248 | "fig,ax = plt.subplots(num=1, ncols=6, sharey=True, figsize=(12, 6))\n", 249 | "locator = mdates.AutoDateLocator(minticks=3, maxticks=7)\n", 250 | "formatter = mdates.ConciseDateFormatter(locator)\n", 251 | "# Plot Elevations for each track\n", 252 | "tracks = dict(gt1l=10,gt1r=20,gt2l=30,gt2r=40,gt3l=50,gt3r=60)\n", 253 | "for s,gt in enumerate(tracks.keys()):\n", 254 | " sr = gdf[gdf[\"gt\"] == tracks[gt]]\n", 255 | " asas = atl06[(atl06[\"gt\"] == tracks[gt]) &\n", 256 | " (atl06[\"h_mean\"] < 1e38) &\n", 257 | " (atl06[\"segment_id\"] >= sr[\"segment_id\"][0]) &\n", 258 | " (atl06[\"segment_id\"] <= sr[\"segment_id\"][-1])]\n", 259 | " ax[s].set_title(gt)\n", 260 | " ax[s].plot(sr.index.values, sr[\"h_mean\"].values, zorder=1,\n", 261 | " linewidth=1.0, color='mediumseagreen', label='SlideRule')\n", 262 | " ax[s].plot(asas.index.values, asas[\"h_mean\"].values, zorder=0,\n", 263 | " linewidth=1.0, color='darkorchid', label='ASAS')\n", 264 | " ax[s].xaxis.set_major_locator(locator)\n", 265 | " ax[s].xaxis.set_major_formatter(formatter)\n", 266 | "# add labels and legends\n", 267 | "ax[0].set_ylabel('Height Above WGS84 Ellipsoid')\n", 268 | "lgd = ax[0].legend(loc=3,frameon=False)\n", 269 | "lgd.get_frame().set_alpha(1.0)\n", 270 | "for line in lgd.get_lines():\n", 271 | " line.set_linewidth(6)\n", 272 | "# Show Plot\n", 273 | "plt.show()" 274 | ] 275 | }, 276 | { 277 | "cell_type": "markdown", 278 | "metadata": {}, 279 | "source": [ 280 | "## Map Plot" 281 | ] 282 | }, 283 | { 284 | "cell_type": "code", 285 | "execution_count": null, 286 | "metadata": { 287 | "tags": [] 288 | }, 289 | "outputs": [], 290 | "source": [ 291 | "# Create PlateCarree Plot\n", 292 | "fig,ax1 = plt.subplots(num=None, figsize=(12, 6))\n", 293 | "################################\n", 294 | "# add global plot\n", 295 | "################################\n", 296 | "world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))\n", 297 | "world.plot(ax=ax1, color='0.8', edgecolor='black')\n", 298 | "gdf.plot(ax=ax1, marker='o', color='red', markersize=2.5, zorder=3)\n", 299 | "ax1.set_title(\"SlideRule Global Reference\")\n", 300 | "\n", 301 | "# Plot locations of each track\n", 302 | "tracks = dict(gt1l=10,gt1r=20,gt2l=30,gt2r=40,gt3l=50,gt3r=60)\n", 303 | "for s,gt in enumerate(tracks.keys()):\n", 304 | " sr = gdf[gdf[\"gt\"] == tracks[gt]]\n", 305 | " sr.plot(ax=ax1, marker='o', color='red', markersize=2.5, zorder=3)\n", 306 | "\n", 307 | "# Plot Bounding Box\n", 308 | "ax1.plot(box_lon, box_lat, linewidth=1.5, color='blue', zorder=2)\n", 309 | "\n", 310 | "# x and y limits, axis = equal\n", 311 | "ax1.set_xlim(-180,180)\n", 312 | "ax1.set_ylim(-90,90)\n", 313 | "ax1.set_aspect('equal', adjustable='box')\n", 314 | "# show plot\n", 315 | "plt.show()" 316 | ] 317 | }, 318 | { 319 | "cell_type": "code", 320 | "execution_count": null, 321 | "metadata": {}, 322 | "outputs": [], 323 | "source": [] 324 | } 325 | ], 326 | "metadata": { 327 | "interpreter": { 328 | "hash": "d7f94b8b1e41b02170d45ac71ce2d6b011e7cd56207b4c480f5292088bcfab93" 329 | }, 330 | "kernelspec": { 331 | "display_name": "Python 3 (ipykernel)", 332 | "language": "python", 333 | "name": "python3" 334 | }, 335 | "language_info": { 336 | "codemirror_mode": { 337 | "name": "ipython", 338 | "version": 3 339 | }, 340 | "file_extension": ".py", 341 | "mimetype": "text/x-python", 342 | "name": "python", 343 | "nbconvert_exporter": "python", 344 | "pygments_lexer": "ipython3", 345 | "version": "3.12.0" 346 | } 347 | }, 348 | "nbformat": 4, 349 | "nbformat_minor": 4 350 | } 351 | -------------------------------------------------------------------------------- /examples/swot_cmr_sim.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "0b7e6c08-fba9-405d-a278-767ec692efb4", 7 | "metadata": { 8 | "tags": [] 9 | }, 10 | "outputs": [], 11 | "source": [ 12 | "# Imports\n", 13 | "from sliderule import sliderule, earthdata, swot\n", 14 | "import matplotlib.pyplot as plt\n", 15 | "import geopandas as gpd\n", 16 | "import numpy" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": null, 22 | "id": "f4155c77-d1bb-42c2-b47a-1089823a8c89", 23 | "metadata": { 24 | "tags": [] 25 | }, 26 | "outputs": [], 27 | "source": [ 28 | "# Query EarthData for SWOT data at Area of Interest\n", 29 | "region = sliderule.toregion(\"../data/grandmesa.geojson\")\n", 30 | "granules = earthdata.cmr(short_name=\"SWOT_SIMULATED_L2_KARIN_SSH_ECCO_LLC4320_CALVAL_V1\", polygon=region[\"poly\"], time_start=None)\n", 31 | "granules" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": null, 37 | "id": "6f27772e-630a-4e50-bcd3-aa7e1d6acff4", 38 | "metadata": { 39 | "tags": [] 40 | }, 41 | "outputs": [], 42 | "source": [ 43 | "# Boundary information from EarthData for SWOT_L2_LR_SSH_Expert_238_002_20120705T114746_20120705T123852_DG10_01.nc \n", 44 | "track = {\n", 45 | " \"points\": [\n", 46 | " {\n", 47 | " \"longitude\": -180,\n", 48 | " \"latitude\": 77.32443\n", 49 | " },\n", 50 | " {\n", 51 | " \"longitude\": -180,\n", 52 | " \"latitude\": 75.96161\n", 53 | " },\n", 54 | " {\n", 55 | " \"longitude\": -162.09458,\n", 56 | " \"latitude\": 72.95769\n", 57 | " },\n", 58 | " {\n", 59 | " \"longitude\": -148.71771,\n", 60 | " \"latitude\": 68.02475\n", 61 | " },\n", 62 | " {\n", 63 | " \"longitude\": -139.57473,\n", 64 | " \"latitude\": 61.46376\n", 65 | " },\n", 66 | " {\n", 67 | " \"longitude\": -132.76541,\n", 68 | " \"latitude\": 52.52883\n", 69 | " },\n", 70 | " {\n", 71 | " \"longitude\": -128.23535,\n", 72 | " \"latitude\": 42.33934\n", 73 | " },\n", 74 | " {\n", 75 | " \"longitude\": -124.53764,\n", 76 | " \"latitude\": 28.97745\n", 77 | " },\n", 78 | " {\n", 79 | " \"longitude\": -116.14332,\n", 80 | " \"latitude\": -22.21489\n", 81 | " },\n", 82 | " {\n", 83 | " \"longitude\": -112.54563,\n", 84 | " \"latitude\": -38.63235\n", 85 | " },\n", 86 | " {\n", 87 | " \"longitude\": -107.59535,\n", 88 | " \"latitude\": -52.02395\n", 89 | " },\n", 90 | " {\n", 91 | " \"longitude\": -101.02934,\n", 92 | " \"latitude\": -61.71245\n", 93 | " },\n", 94 | " {\n", 95 | " \"longitude\": -96.51192,\n", 96 | " \"latitude\": -65.80947\n", 97 | " },\n", 98 | " {\n", 99 | " \"longitude\": -91.33566,\n", 100 | " \"latitude\": -69.12003\n", 101 | " },\n", 102 | " {\n", 103 | " \"longitude\": -77.82439,\n", 104 | " \"latitude\": -74.1454\n", 105 | " },\n", 106 | " {\n", 107 | " \"longitude\": -59.19556,\n", 108 | " \"latitude\": -77.20514\n", 109 | " },\n", 110 | " {\n", 111 | " \"longitude\": -35.45207,\n", 112 | " \"latitude\": -78.29215\n", 113 | " },\n", 114 | " {\n", 115 | " \"longitude\": -35.45501,\n", 116 | " \"latitude\": -77.0331\n", 117 | " },\n", 118 | " {\n", 119 | " \"longitude\": -59.11783,\n", 120 | " \"latitude\": -75.83566\n", 121 | " },\n", 122 | " {\n", 123 | " \"longitude\": -77.9601,\n", 124 | " \"latitude\": -72.38965\n", 125 | " },\n", 126 | " {\n", 127 | " \"longitude\": -90.38687,\n", 128 | " \"latitude\": -67.38657\n", 129 | " },\n", 130 | " {\n", 131 | " \"longitude\": -99.59434,\n", 132 | " \"latitude\": -60.19465\n", 133 | " },\n", 134 | " {\n", 135 | " \"longitude\": -106.37693,\n", 136 | " \"latitude\": -50.25634\n", 137 | " },\n", 138 | " {\n", 139 | " \"longitude\": -111.51882,\n", 140 | " \"latitude\": -36.48096\n", 141 | " },\n", 142 | " {\n", 143 | " \"longitude\": -115.3008,\n", 144 | " \"latitude\": -19.27607\n", 145 | " },\n", 146 | " {\n", 147 | " \"longitude\": -123.63896,\n", 148 | " \"latitude\": 31.6175\n", 149 | " },\n", 150 | " {\n", 151 | " \"longitude\": -127.38371,\n", 152 | " \"latitude\": 45.02024\n", 153 | " },\n", 154 | " {\n", 155 | " \"longitude\": -131.90094,\n", 156 | " \"latitude\": 54.92655\n", 157 | " },\n", 158 | " {\n", 159 | " \"longitude\": -139.12559,\n", 160 | " \"latitude\": 63.9081\n", 161 | " },\n", 162 | " {\n", 163 | " \"longitude\": -148.94375,\n", 164 | " \"latitude\": 70.29256\n", 165 | " },\n", 166 | " {\n", 167 | " \"longitude\": -162.17736,\n", 168 | " \"latitude\": 74.64083\n", 169 | " },\n", 170 | " {\n", 171 | " \"longitude\": -180,\n", 172 | " \"latitude\": 77.32443\n", 173 | " }\n", 174 | " ]\n", 175 | "}" 176 | ] 177 | }, 178 | { 179 | "cell_type": "code", 180 | "execution_count": null, 181 | "id": "6ff89d99-3045-48db-b084-8757521e2462", 182 | "metadata": {}, 183 | "outputs": [], 184 | "source": [ 185 | "# Build GeoDataFrame of Boundary\n", 186 | "lat = [p['latitude'] for p in track['points']]\n", 187 | "lon = [p['longitude'] for p in track['points']]\n", 188 | "data = [x for x in range(len(lon))]\n", 189 | "a = {\"latitude\": lat, \"longitude\": lon, \"time\": numpy.array(data)}\n", 190 | "gdf = sliderule.todataframe(a)" 191 | ] 192 | }, 193 | { 194 | "cell_type": "code", 195 | "execution_count": null, 196 | "id": "8abcc3a0-2a86-4c3f-9438-7e9befa320c4", 197 | "metadata": { 198 | "tags": [] 199 | }, 200 | "outputs": [], 201 | "source": [ 202 | "# Plot Boundary and Area of Interest on World Map\n", 203 | "worldmap = gpd.read_file(gpd.datasets.get_path(\"naturalearth_lowres\"))\n", 204 | "fig, ax = plt.subplots(figsize=(12, 6))\n", 205 | "worldmap.plot(color=\"lightgrey\", ax=ax)\n", 206 | "gdf.plot(ax=ax)\n", 207 | "region['gdf'].buffer(1.0).plot(ax=ax, color='green')" 208 | ] 209 | }, 210 | { 211 | "cell_type": "code", 212 | "execution_count": null, 213 | "id": "3ba51afd-98fe-4ac3-a885-d9a7975bc009", 214 | "metadata": {}, 215 | "outputs": [], 216 | "source": [] 217 | } 218 | ], 219 | "metadata": { 220 | "kernelspec": { 221 | "display_name": "Python 3 (ipykernel)", 222 | "language": "python", 223 | "name": "python3" 224 | }, 225 | "language_info": { 226 | "codemirror_mode": { 227 | "name": "ipython", 228 | "version": 3 229 | }, 230 | "file_extension": ".py", 231 | "mimetype": "text/x-python", 232 | "name": "python", 233 | "nbconvert_exporter": "python", 234 | "pygments_lexer": "ipython3", 235 | "version": "3.11.3" 236 | } 237 | }, 238 | "nbformat": 4, 239 | "nbformat_minor": 5 240 | } 241 | -------------------------------------------------------------------------------- /examples/swot_l2_sim.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "0b7e6c08-fba9-405d-a278-767ec692efb4", 7 | "metadata": { 8 | "tags": [] 9 | }, 10 | "outputs": [], 11 | "source": [ 12 | "from sliderule import sliderule, earthdata, swot\n", 13 | "import matplotlib.pyplot as plt\n", 14 | "import numpy" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": null, 20 | "id": "e4058afa-16f0-4401-8d7f-52d0521f3651", 21 | "metadata": { 22 | "tags": [] 23 | }, 24 | "outputs": [], 25 | "source": [ 26 | "swot.init(\"slideruleearth.io\", verbose=True)\n", 27 | "region = sliderule.toregion(\"../data/antarctic.geojson\")\n", 28 | "rsps = swot.swotl2p({\"poly\":region[\"poly\"], \"variables\":[\"dynamic_ice_flag\"]}, resources=['SWOT_L2_LR_SSH_Expert_238_002_20120705T114746_20120705T123852_DG10_01.nc'])" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": null, 34 | "id": "ea532080-6b13-46eb-bc59-0a80dc6c52e0", 35 | "metadata": { 36 | "tags": [] 37 | }, 38 | "outputs": [], 39 | "source": [ 40 | "rsps['SWOT_L2_LR_SSH_Expert_238_002_20120705T114746_20120705T123852_DG10_01.nc']" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": null, 46 | "id": "98e39f55-6671-4f01-a447-0ea652203066", 47 | "metadata": {}, 48 | "outputs": [], 49 | "source": [] 50 | } 51 | ], 52 | "metadata": { 53 | "kernelspec": { 54 | "display_name": "Python 3 (ipykernel)", 55 | "language": "python", 56 | "name": "python3" 57 | }, 58 | "language_info": { 59 | "codemirror_mode": { 60 | "name": "ipython", 61 | "version": 3 62 | }, 63 | "file_extension": ".py", 64 | "mimetype": "text/x-python", 65 | "name": "python", 66 | "nbconvert_exporter": "python", 67 | "pygments_lexer": "ipython3", 68 | "version": "3.11.3" 69 | } 70 | }, 71 | "nbformat": 4, 72 | "nbformat_minor": 5 73 | } 74 | -------------------------------------------------------------------------------- /version.txt: -------------------------------------------------------------------------------- 1 | v2.1.1 2 | --------------------------------------------------------------------------------