{{ response.title }}
-- {{ response.description }} -
- -Links
--
-{% for link in response.links %}
-
- {{ link.title }} -{% endfor %} -
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index b102280..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,184 +0,0 @@ -name: CI - -# On every pull request, but only on push to main -on: - push: - branches: - - main - tags: - - '*' - paths: - # Only rebuild website when docs have changed - - 'runtime/**' - - 'dockerfiles/**' - - 'docker-compose.*' - - '.github/workflows/ci.yml' - - '.isort.cfg' - - '.mypy.ini' - - 'ruff.toml' - - '.pre-commit-config.yaml' - pull_request: - -jobs: - tests: - runs-on: ubuntu-20.04 - strategy: - fail-fast: false - steps: - - uses: actions/checkout@v3 - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.10' - - - name: Run pre-commit - run: | - python -m pip install --upgrade pip - python -m pip install pre-commit - pre-commit run --all-files - - - name: Launch services - run: docker compose -f docker-compose.custom.yml --profile gunicorn up -d - - - name: install lib postgres - run: | - sudo apt update - wget -q https://www.postgresql.org/media/keys/ACCC4CF8.asc -O- | sudo apt-key add - - echo "deb [arch=amd64] http://apt.postgresql.org/pub/repos/apt/ focal-pgdg main" | sudo tee /etc/apt/sources.list.d/postgresql.list - sudo apt update - sudo apt-get install --yes libpq-dev postgis postgresql-14-postgis-3 - - - name: Install python dependencies - run: | - python -m pip install pytest pytest-asyncio httpx pypgstac==0.8.1 psycopg[pool] brotlipy boto3 pytest-pgsql psycopg2 - - - name: Test CDK DB Bootstrap - working-directory: ./infrastructure/aws - run: | - python -m pytest tests/test_bootstrap.py -v -ss - - - name: Ingest Stac Items/Collection - run: | - pypgstac pgready --dsn postgresql://username:password@0.0.0.0:5439/postgis - pypgstac load collections .github/workflows/data/noaa-emergency-response.json --dsn postgresql://username:password@0.0.0.0:5439/postgis --method insert_ignore - pypgstac load items .github/workflows/data/noaa-eri-nashville2020.json --dsn postgresql://username:password@0.0.0.0:5439/postgis --method insert_ignore - psql postgresql://username:password@0.0.0.0:5439/postgis -f .github/workflows/data/my_data.sql - - # see https://github.com/developmentseed/tipg/issues/37 - - name: Restart the Vector service - run: | - docker compose -f docker-compose.custom.yml --profile gunicorn stop vector - docker compose -f docker-compose.custom.yml --profile gunicorn up -d vector - - - name: Sleep for 10 seconds - run: sleep 10s - shell: bash - - - name: Integrations tests - run: python -m pytest .github/workflows/tests/ - - - name: Stop services - run: docker compose -f docker-compose.custom.yml stop - - - publish-docker: - needs: [tests] - if: github.ref == 'refs/heads/main' || startsWith(github.event.ref, 'refs/tags') || github.event_name == 'release' - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - - - name: Login to DockerHub - uses: docker/login-action@v1 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Login to Github - uses: docker/login-action@v1 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Set tag version - id: tag - run: | - echo "version=${GITHUB_REF#refs/*/}" - echo "version=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT - - ############################################################################# - # RASTER - - name: RASTER - Build and push latest - if: github.ref == 'refs/heads/main' - uses: docker/build-push-action@v2 - with: - platforms: linux/amd64 - context: . - file: dockerfiles/Dockerfile.raster-uvicorn - push: true - tags: | - ghcr.io/developmentseed/eoapi-raster:latest - - - name: RASTER - Build and push tags - if: startsWith(github.event.ref, 'refs/tags') || github.event_name == 'release' - uses: docker/build-push-action@v2 - with: - platforms: linux/amd64 - context: . - file: dockerfiles/Dockerfile.raster-uvicorn - push: true - tags: | - ghcr.io/developmentseed/eoapi-raster:${{ steps.tag.outputs.tag }} - - ############################################################################# - # STAC - - name: STAC - Build and push latest - if: github.ref == 'refs/heads/main' - uses: docker/build-push-action@v2 - with: - context: . - file: dockerfiles/Dockerfile.stac-uvicorn - push: true - tags: | - ghcr.io/developmentseed/eoapi-stac:latest - - - name: STAC - Build and push tags - if: startsWith(github.event.ref, 'refs/tags') || github.event_name == 'release' - uses: docker/build-push-action@v2 - with: - context: . - file: dockerfiles/Dockerfile.stac-uvicorn - push: true - tags: | - ghcr.io/developmentseed/eoapi-stac:${{ steps.tag.outputs.tag }} - - ############################################################################# - # VECTOR - - name: VECTOR - Build and push latest - if: github.ref == 'refs/heads/main' - uses: docker/build-push-action@v2 - with: - context: . - file: dockerfiles/Dockerfile.vector-uvicorn - push: true - tags: | - ghcr.io/developmentseed/eoapi-vector:latest - - - name: VECTOR - Build and push tags - if: startsWith(github.event.ref, 'refs/tags') || github.event_name == 'release' - uses: docker/build-push-action@v2 - with: - context: . - file: dockerfiles/Dockerfile.vector-uvicorn - push: true - tags: | - ghcr.io/developmentseed/eoapi-vector:${{ steps.tag.outputs.tag }} - diff --git a/.github/workflows/data/my_data.sql b/.github/workflows/data/my_data.sql deleted file mode 100644 index 21b1e6d..0000000 --- a/.github/workflows/data/my_data.sql +++ /dev/null @@ -1,16 +0,0 @@ -SET standard_conforming_strings = OFF; -DROP TABLE IF EXISTS "public"."my_data" CASCADE; -DELETE FROM geometry_columns WHERE f_table_name = 'my_data' AND f_table_schema = 'public'; -BEGIN; -CREATE TABLE "public"."my_data" ( "ogc_fid" SERIAL, CONSTRAINT "my_data_pk" PRIMARY KEY ("ogc_fid") ); -SELECT AddGeometryColumn('public','my_data','geom',4326,'GEOMETRY',2); -CREATE INDEX "my_data_geom_geom_idx" ON "public"."my_data" USING GIST ("geom"); -ALTER TABLE "public"."my_data" ADD COLUMN "id" VARCHAR; -ALTER TABLE "public"."my_data" ADD COLUMN "datetime" TIMESTAMP; -INSERT INTO "public"."my_data" ("geom" , "id", "datetime") VALUES ('0103000020E6100000010000001B0000003670CC05599B25C03A92CB7F483F54408907944DB9F221C0D9CEF753E315544069D68681BE5B22C0355D864BD1145440984C2580F45C27C062327530C20754409CB396CA942C30C08E6EC42E50F05340F32225E11DCB30C07C98C2D614ED5340075F984C15FC30C0075F984C15EC53400AA1BD9D6AD732C03439A530F50B5440D8BFC6C0170533C00414E74C050F54407650100F7C0E33C0B199D586A60F5440A01BF45DE29634C0B61719B9F6295440838D3D254B5D35C0DC611EC044375440B8A6A26802F135C06705618A2C4154407CBD21E2CF3136C09B1B77FC844554402CD49AE61D3736C076711B0DE045544039117CFD650136C001AEC11005475440DC27DD0AB9C935C0F45E61C1344854406182187FE9BA35C03AF2E08A854854400736A0D273F130C050CF32FAA1625440ED137AA9497230C0441F419D576554401D9FC06CB06E2BC0B1930B183C745440017C2AECC5F92AC01E2006F67A7554401895D40968822AC0986E1283C07654405D44620EE0782AC0E00B92AC54765440FAACE2F3F95C27C0CDCE93B2275354400D2FBCF61DD226C0385BB99C044D54403670CC05599B25C03A92CB7F483F5440', '0', '2004-10-19 10:23:54'); -INSERT INTO "public"."my_data" ("geom" , "id", "datetime") VALUES ('0103000020E61000000100000019000000984067B8143E3DC043C2B8B8F40B5440ACEF9DFAC14B3DC0E950B3BEBB0C544070CE88D2DE503DC01B2FDD24060D544034C8A112A4243DC064CC7707650E54409232AD9551103DC079704A40060F5440A630DBCBFBF43CC0E22ABE1BDF0F5440AC95A5A7DFA638C09E34007606325440FE987A2A9D7238C05D165F0DA5335440D1BF9E64C80A38C0FF6D3AC6DC3654409ACC3E07335D36C0578150C82C4454407CBD21E2CF3136C09B1B77FC8445544039117CFD650136C001AEC110054754401EA7E8482ECF35C07F6ABC7493485440DC27DD0AB9C935C0F45E61C134485440A2F3387764C135C09C775737A44754405526CE34BBDB34C047F7C465133854408DF37646C5EA33C0F10FDC85BE2754406D6485236BA431C08C72AF36460054403EE8D9ACFA9C30C07CF2B0506BEE5340F32225E11DCB30C07C98C2D614ED5340FE41CA2BA27737C016B27D9C8ABB5340C442AD69DEA137C05F07CE1951BA5340F9CBEEC9C30A38C07E078C8947C05340898D7238194D38C059C5B4D10CC45340984067B8143E3DC043C2B8B8F40B5440', '1', '2004-10-20 10:23:54'); -INSERT INTO "public"."my_data" ("geom" , "id", "datetime") VALUES ('0103000020E61000000100000013000000C0155236C40A38C052F1FFE1D8C75340B244B5A16EC837C014EBB5CD0CC4534073D712F2414F37C0D3BCE3141DBD5340FE41CA2BA27737C016B27D9C8ABB5340A2728C64C30A38C03BFB4402D0B553400C6AB4D7723A3DC0BDA377861D82534058CA32C4B15E3DC062105839B48053402A2097D1F19641C0EAE96F4E58CC5340F0A7C64B379941C07F6ABC7493CC5340E11AE2531A8741C01F2670501DCE5340CED31A45F57241C03EC92059D3CF534009E08D47F1E83FC0EAC3384350F05340DFE755925F713EC036A2858243005440ACEF9DFAC14B3DC0E950B3BEBB0C544034C8A112A4243DC064CC7707650E5440F602E719D4063DC0AE877727A90F54400A68226C78FA3CC0234A7B832F105440A630DBCBFBF43CC0E22ABE1BDF0F5440C0155236C40A38C052F1FFE1D8C75340', '2', '2004-10-21 10:23:54'); -INSERT INTO "public"."my_data" ("geom" , "id", "datetime") VALUES ('0103000020E610000001000000110000001B2CBE53855542C051F99E0E805D534049A5CD2EAE0644C03857A7D846865340462575029A0844C0A60A46257586534063B4EEABC4F943C08D992E511D8853409C72BC6BC5E843C0920AAB5C038A5340721D3749863342C03D0220C7DABA53402A2097D1F19641C0EAE96F4E58CC5340E11AE2531A8741C01F2670501DCE534068226C787A7541C0075F984C15D05340CED31A45F57241C03EC92059D3CF534048E17A14AE173DC06B2BF697DD8353400C6AB4D7723A3DC0BDA377861D825340A03E0335AD283FC0314A54553C6953409C6F1F2DEA1541C00EA6095E6A425340BEC11726532541C0BE9F1A2FDD405340EB51B81E853342C0302C67AA4C5A53401B2CBE53855542C051F99E0E805D5340', '3', '2004-10-22 10:23:54'); -INSERT INTO "public"."my_data" ("geom" , "id", "datetime") VALUES ('0103000020E610000001000000110000000A4C8422590E46C0B656FB86F03B5340D5E76A2BF60F46C0075F984C153C5340FA28B2217F0346C0CE0A257ADB3D5340BEE6287052F545C01AA33BF2DF3F5340F25A937BB7D244C009CB92853C69534049A5CD2EAE0644C03857A7D84686534063B4EEABC4F943C08D992E511D88534034A2B437F8EA43C0F54A5986388A53409C72BC6BC5E843C0920AAB5C038A534050AF9465883342C0363B85F6B5605340D43E0032881142C02A5884BF7F5D5340F4FDD478E90641C007F01648504453409C6F1F2DEA1541C00EA6095E6A4253404E4E9C88873342C06DC6E4C7471E53403EDF52396E3443C0DC9EAF2DC7FD524044696FF0854143C032772D211FFC52400A4C8422590E46C0B656FB86F03B5340', '4', '2004-10-23 10:23:54'); -INSERT INTO "public"."my_data" ("geom" , "id", "datetime") VALUES ('0103000020E6100000010000000D000000BBE9944235C347C0EBF06E7961EE52406ADE718A8EC447C0D122DBF97EEE5240942D6301ECB947C05B59871F60F0524086CAEEF61AAE47C0BDEF3BBB76F252400A4C8422590E46C0B656FB86F03B5340FA28B2217F0346C0CE0A257ADB3D534057EC2FBB27F745C02B1895D409405340BEE6287052F545C01AA33BF2DF3F53401D386744692743C07958A835CDFF52403EDF52396E3443C0DC9EAF2DC7FD5240B9E39237FD0645C0574B4E2543B552400AD7A3703D1245C03A234A7B83B35240BBE9944235C347C0EBF06E7961EE5240', '5', '2004-10-24 10:23:54'); -COMMIT; diff --git a/.github/workflows/data/noaa-emergency-response.json b/.github/workflows/data/noaa-emergency-response.json deleted file mode 100644 index 41ed177..0000000 --- a/.github/workflows/data/noaa-emergency-response.json +++ /dev/null @@ -1 +0,0 @@ -{"id":"noaa-emergency-response", "title": "NOAA Emergency Response Imagery", "description":"NOAA Emergency Response Imagery hosted on AWS Public Dataset.","stac_version":"1.0.0","license":"public-domain","links":[],"extent":{"spatial":{"bbox":[[-180,-90,180,90]]},"temporal":{"interval":[["2005-01-01T00:00:00Z",null]]}}} diff --git a/.github/workflows/data/noaa-eri-nashville2020.json b/.github/workflows/data/noaa-eri-nashville2020.json deleted file mode 100644 index 6f42c56..0000000 --- a/.github/workflows/data/noaa-eri-nashville2020.json +++ /dev/null @@ -1,163 +0,0 @@ -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0852700w360900","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.4501,36.1501],[-85.4501,36.1249],[-85.4249,36.1249],[-85.4249,36.1501],[-85.4501,36.1501]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0852700w360900n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.4501,36.1249,-85.4249,36.1501],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0852700w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.4501,36.1751],[-85.4501,36.1499],[-85.4249,36.1499],[-85.4249,36.1751],[-85.4501,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0852700w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.4501,36.1499,-85.4249,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0852700w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.4501,36.2001],[-85.4501,36.1749],[-85.4249,36.1749],[-85.4249,36.2001],[-85.4501,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0852700w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.4501,36.1749,-85.4249,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0852830w360730","properties":{"event":"Nashville Tornado","datetime":"2020-03-10T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.4751,36.1251],[-85.4751,36.0999],[-85.4499,36.0999],[-85.4499,36.1251],[-85.4751,36.1251]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0852830w360730n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.4751,36.0999,-85.4499,36.1251],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0852830w360900","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.4751,36.1501],[-85.4751,36.1249],[-85.4499,36.1249],[-85.4499,36.1501],[-85.4751,36.1501]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0852830w360900n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.4751,36.1249,-85.4499,36.1501],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0852830w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.4751,36.1751],[-85.4751,36.1499],[-85.4499,36.1499],[-85.4499,36.1751],[-85.4751,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0852830w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.4751,36.1499,-85.4499,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0852830w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.4751,36.2001],[-85.4751,36.1749],[-85.4499,36.1749],[-85.4499,36.2001],[-85.4751,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0852830w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.4751,36.1749,-85.4499,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0853000w360730","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.5001,36.1251],[-85.5001,36.0999],[-85.4749,36.0999],[-85.4749,36.1251],[-85.5001,36.1251]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0853000w360730n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.5001,36.0999,-85.4749,36.1251],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0853000w360900","properties":{"event":"Nashville Tornado","datetime":"2020-03-10T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.5001,36.1501],[-85.5001,36.1249],[-85.4749,36.1249],[-85.4749,36.1501],[-85.5001,36.1501]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0853000w360900n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.5001,36.1249,-85.4749,36.1501],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0853000w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.5001,36.1751],[-85.5001,36.1499],[-85.4749,36.1499],[-85.4749,36.1751],[-85.5001,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0853000w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.5001,36.1499,-85.4749,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0853000w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.5001,36.2001],[-85.5001,36.1749],[-85.4749,36.1749],[-85.4749,36.2001],[-85.5001,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0853000w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.5001,36.1749,-85.4749,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0853130w360900","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.5251,36.1501],[-85.5251,36.1249],[-85.4999,36.1249],[-85.4999,36.1501],[-85.5251,36.1501]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0853130w360900n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.5251,36.1249,-85.4999,36.1501],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0853130w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.5251,36.1751],[-85.5251,36.1499],[-85.4999,36.1499],[-85.4999,36.1751],[-85.5251,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0853130w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.5251,36.1499,-85.4999,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0853130w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-10T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.5251,36.2001],[-85.5251,36.1749],[-85.4999,36.1749],[-85.4999,36.2001],[-85.5251,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0853130w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.5251,36.1749,-85.4999,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0853300w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.5501,36.1751],[-85.5501,36.1499],[-85.5249,36.1499],[-85.5249,36.1751],[-85.5501,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0853300w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.5501,36.1499,-85.5249,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0853300w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.5501,36.2001],[-85.5501,36.1749],[-85.5249,36.1749],[-85.5249,36.2001],[-85.5501,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0853300w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.5501,36.1749,-85.5249,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0853430w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.5751,36.1751],[-85.5751,36.1499],[-85.5499,36.1499],[-85.5499,36.1751],[-85.5751,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0853430w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.5751,36.1499,-85.5499,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0853430w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.5751,36.2001],[-85.5751,36.1749],[-85.5499,36.1749],[-85.5499,36.2001],[-85.5751,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0853430w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.5751,36.1749,-85.5499,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0853600w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-10T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.6001,36.1751],[-85.6001,36.1499],[-85.5749,36.1499],[-85.5749,36.1751],[-85.6001,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0853600w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.6001,36.1499,-85.5749,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0853600w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.6001,36.2001],[-85.6001,36.1749],[-85.5749,36.1749],[-85.5749,36.2001],[-85.6001,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0853600w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.6001,36.1749,-85.5749,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0853730w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.6251,36.1751],[-85.6251,36.1499],[-85.5999,36.1499],[-85.5999,36.1751],[-85.6251,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0853730w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.6251,36.1499,-85.5999,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0853730w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.6251,36.2001],[-85.6251,36.1749],[-85.5999,36.1749],[-85.5999,36.2001],[-85.6251,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0853730w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.6251,36.1749,-85.5999,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0853900w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.6501,36.1751],[-85.6501,36.1499],[-85.6249,36.1499],[-85.6249,36.1751],[-85.6501,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0853900w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.6501,36.1499,-85.6249,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0853900w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.6501,36.2001],[-85.6501,36.1749],[-85.6249,36.1749],[-85.6249,36.2001],[-85.6501,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0853900w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.6501,36.1749,-85.6249,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0854030w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-10T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.6751,36.1751],[-85.6751,36.1499],[-85.6499,36.1499],[-85.6499,36.1751],[-85.6751,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0854030w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.6751,36.1499,-85.6499,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0854030w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.6751,36.2001],[-85.6751,36.1749],[-85.6499,36.1749],[-85.6499,36.2001],[-85.6751,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0854030w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.6751,36.1749,-85.6499,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0854200w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.7001,36.1751],[-85.7001,36.1499],[-85.6749,36.1499],[-85.6749,36.1751],[-85.7001,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0854200w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.7001,36.1499,-85.6749,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0854200w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.7001,36.2001],[-85.7001,36.1749],[-85.6749,36.1749],[-85.6749,36.2001],[-85.7001,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0854200w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.7001,36.1749,-85.6749,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0854330w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.7251,36.1751],[-85.7251,36.1499],[-85.6999,36.1499],[-85.6999,36.1751],[-85.7251,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0854330w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.7251,36.1499,-85.6999,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0854330w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-10T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.7251,36.2001],[-85.7251,36.1749],[-85.6999,36.1749],[-85.6999,36.2001],[-85.7251,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0854330w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.7251,36.1749,-85.6999,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0854500w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.7501,36.1751],[-85.7501,36.1499],[-85.7249,36.1499],[-85.7249,36.1751],[-85.7501,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0854500w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.7501,36.1499,-85.7249,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0854500w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.7501,36.2001],[-85.7501,36.1749],[-85.7249,36.1749],[-85.7249,36.2001],[-85.7501,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0854500w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.7501,36.1749,-85.7249,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0854630w360900","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.7751,36.1501],[-85.7751,36.1249],[-85.7499,36.1249],[-85.7499,36.1501],[-85.7751,36.1501]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0854630w360900n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.7751,36.1249,-85.7499,36.1501],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0854630w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.7751,36.1751],[-85.7751,36.1499],[-85.7499,36.1499],[-85.7499,36.1751],[-85.7751,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0854630w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.7751,36.1499,-85.7499,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0854630w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-10T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.7751,36.2001],[-85.7751,36.1749],[-85.7499,36.1749],[-85.7499,36.2001],[-85.7751,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0854630w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.7751,36.1749,-85.7499,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0854800w360900","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.8001,36.1501],[-85.8001,36.1249],[-85.7749,36.1249],[-85.7749,36.1501],[-85.8001,36.1501]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0854800w360900n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.8001,36.1249,-85.7749,36.1501],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0854800w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.8001,36.1751],[-85.8001,36.1499],[-85.7749,36.1499],[-85.7749,36.1751],[-85.8001,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0854800w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.8001,36.1499,-85.7749,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0854800w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.8001,36.2001],[-85.8001,36.1749],[-85.7749,36.1749],[-85.7749,36.2001],[-85.8001,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0854800w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.8001,36.1749,-85.7749,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0854930w360900","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.8251,36.1501],[-85.8251,36.1249],[-85.7999,36.1249],[-85.7999,36.1501],[-85.8251,36.1501]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0854930w360900n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.8251,36.1249,-85.7999,36.1501],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0854930w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-10T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.8251,36.1751],[-85.8251,36.1499],[-85.7999,36.1499],[-85.7999,36.1751],[-85.8251,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0854930w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.8251,36.1499,-85.7999,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0854930w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.8251,36.2001],[-85.8251,36.1749],[-85.7999,36.1749],[-85.7999,36.2001],[-85.8251,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0854930w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.8251,36.1749,-85.7999,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0855100w360900","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.8501,36.1501],[-85.8501,36.1249],[-85.8249,36.1249],[-85.8249,36.1501],[-85.8501,36.1501]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0855100w360900n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.8501,36.1249,-85.8249,36.1501],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0855100w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.8501,36.1751],[-85.8501,36.1499],[-85.8249,36.1499],[-85.8249,36.1751],[-85.8501,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0855100w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.8501,36.1499,-85.8249,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0855230w360900","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.8751,36.1501],[-85.8751,36.1249],[-85.8499,36.1249],[-85.8499,36.1501],[-85.8751,36.1501]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0855230w360900n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.8751,36.1249,-85.8499,36.1501],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0855230w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.8751,36.1751],[-85.8751,36.1499],[-85.8499,36.1499],[-85.8499,36.1751],[-85.8751,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0855230w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.8751,36.1499,-85.8499,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0855400w360900","properties":{"event":"Nashville Tornado","datetime":"2020-03-10T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.9001,36.1501],[-85.9001,36.1249],[-85.8749,36.1249],[-85.8749,36.1501],[-85.9001,36.1501]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0855400w360900n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.9001,36.1249,-85.8749,36.1501],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0855400w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.9001,36.1751],[-85.9001,36.1499],[-85.8749,36.1499],[-85.8749,36.1751],[-85.9001,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0855400w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.9001,36.1499,-85.8749,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0855530w360900","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.9251,36.1501],[-85.9251,36.1249],[-85.8999,36.1249],[-85.8999,36.1501],[-85.9251,36.1501]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0855530w360900n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.9251,36.1249,-85.8999,36.1501],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0855530w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.9251,36.1751],[-85.9251,36.1499],[-85.8999,36.1499],[-85.8999,36.1751],[-85.9251,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0855530w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.9251,36.1499,-85.8999,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0855700w360900","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.9501,36.1501],[-85.9501,36.1249],[-85.9249,36.1249],[-85.9249,36.1501],[-85.9501,36.1501]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0855700w360900n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.9501,36.1249,-85.9249,36.1501],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0855700w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-10T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.9501,36.1751],[-85.9501,36.1499],[-85.9249,36.1499],[-85.9249,36.1751],[-85.9501,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0855700w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.9501,36.1499,-85.9249,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0855700w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.9501,36.2001],[-85.9501,36.1749],[-85.9249,36.1749],[-85.9249,36.2001],[-85.9501,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0855700w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.9501,36.1749,-85.9249,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0855830w360900","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.9751,36.1501],[-85.9751,36.1249],[-85.9499,36.1249],[-85.9499,36.1501],[-85.9751,36.1501]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0855830w360900n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.9751,36.1249,-85.9499,36.1501],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0855830w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.9751,36.1751],[-85.9751,36.1499],[-85.9499,36.1499],[-85.9499,36.1751],[-85.9751,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0855830w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.9751,36.1499,-85.9499,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0855830w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-85.9751,36.2001],[-85.9751,36.1749],[-85.9499,36.1749],[-85.9499,36.2001],[-85.9751,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0855830w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-85.9751,36.1749,-85.9499,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0860000w360900","properties":{"event":"Nashville Tornado","datetime":"2020-03-10T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.0001,36.1501],[-86.0001,36.1249],[-85.9749,36.1249],[-85.9749,36.1501],[-86.0001,36.1501]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0860000w360900n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.0001,36.1249,-85.9749,36.1501],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0860000w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.0001,36.1751],[-86.0001,36.1499],[-85.9749,36.1499],[-85.9749,36.1751],[-86.0001,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0860000w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.0001,36.1499,-85.9749,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0860000w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.0001,36.2001],[-86.0001,36.1749],[-85.9749,36.1749],[-85.9749,36.2001],[-86.0001,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0860000w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.0001,36.1749,-85.9749,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0860130w360900","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.0251,36.1501],[-86.0251,36.1249],[-85.9999,36.1249],[-85.9999,36.1501],[-86.0251,36.1501]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0860130w360900n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.0251,36.1249,-85.9999,36.1501],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0860130w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.0251,36.1751],[-86.0251,36.1499],[-85.9999,36.1499],[-85.9999,36.1751],[-86.0251,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0860130w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.0251,36.1499,-85.9999,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0860130w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-10T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.0251,36.2001],[-86.0251,36.1749],[-85.9999,36.1749],[-85.9999,36.2001],[-86.0251,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0860130w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.0251,36.1749,-85.9999,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0860300w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.0501,36.1751],[-86.0501,36.1499],[-86.0249,36.1499],[-86.0249,36.1751],[-86.0501,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0860300w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.0501,36.1499,-86.0249,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0860300w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.0501,36.2001],[-86.0501,36.1749],[-86.0249,36.1749],[-86.0249,36.2001],[-86.0501,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0860300w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.0501,36.1749,-86.0249,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0860430w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.0751,36.1751],[-86.0751,36.1499],[-86.0499,36.1499],[-86.0499,36.1751],[-86.0751,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0860430w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.0751,36.1499,-86.0499,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0860430w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.0751,36.2001],[-86.0751,36.1749],[-86.0499,36.1749],[-86.0499,36.2001],[-86.0751,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0860430w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.0751,36.1749,-86.0499,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0860600w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.1001,36.1751],[-86.1001,36.1499],[-86.0749,36.1499],[-86.0749,36.1751],[-86.1001,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0860600w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.1001,36.1499,-86.0749,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0860600w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-10T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.1001,36.2001],[-86.1001,36.1749],[-86.0749,36.1749],[-86.0749,36.2001],[-86.1001,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0860600w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.1001,36.1749,-86.0749,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0860730w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.1251,36.1751],[-86.1251,36.1499],[-86.0999,36.1499],[-86.0999,36.1751],[-86.1251,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0860730w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.1251,36.1499,-86.0999,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0860730w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.1251,36.2001],[-86.1251,36.1749],[-86.0999,36.1749],[-86.0999,36.2001],[-86.1251,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0860730w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.1251,36.1749,-86.0999,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0860900w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.1501,36.1751],[-86.1501,36.1499],[-86.1249,36.1499],[-86.1249,36.1751],[-86.1501,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0860900w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.1501,36.1499,-86.1249,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0860900w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.1501,36.2001],[-86.1501,36.1749],[-86.1249,36.1749],[-86.1249,36.2001],[-86.1501,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0860900w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.1501,36.1749,-86.1249,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0861030w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-10T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.1751,36.1751],[-86.1751,36.1499],[-86.1499,36.1499],[-86.1499,36.1751],[-86.1751,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0861030w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.1751,36.1499,-86.1499,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0861030w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.1751,36.2001],[-86.1751,36.1749],[-86.1499,36.1749],[-86.1499,36.2001],[-86.1751,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0861030w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.1751,36.1749,-86.1499,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0861200w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.2001,36.1751],[-86.2001,36.1499],[-86.1749,36.1499],[-86.1749,36.1751],[-86.2001,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0861200w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.2001,36.1499,-86.1749,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0861200w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.2001,36.2001],[-86.2001,36.1749],[-86.1749,36.1749],[-86.1749,36.2001],[-86.2001,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0861200w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.2001,36.1749,-86.1749,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0861200w361330","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.2001,36.2251],[-86.2001,36.1999],[-86.1749,36.1999],[-86.1749,36.2251],[-86.2001,36.2251]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0861200w361330n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.2001,36.1999,-86.1749,36.2251],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0861330w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-10T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.2251,36.1751],[-86.2251,36.1499],[-86.1999,36.1499],[-86.1999,36.1751],[-86.2251,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0861330w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.2251,36.1499,-86.1999,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0861330w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.2251,36.2001],[-86.2251,36.1749],[-86.1999,36.1749],[-86.1999,36.2001],[-86.2251,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0861330w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.2251,36.1749,-86.1999,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0861330w361330","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.2251,36.2251],[-86.2251,36.1999],[-86.1999,36.1999],[-86.1999,36.2251],[-86.2251,36.2251]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0861330w361330n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.2251,36.1999,-86.1999,36.2251],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0861500w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.2501,36.1751],[-86.2501,36.1499],[-86.2249,36.1499],[-86.2249,36.1751],[-86.2501,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0861500w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.2501,36.1499,-86.2249,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0861500w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.2501,36.2001],[-86.2501,36.1749],[-86.2249,36.1749],[-86.2249,36.2001],[-86.2501,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0861500w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.2501,36.1749,-86.2249,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0861500w361330","properties":{"event":"Nashville Tornado","datetime":"2020-03-10T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.2501,36.2251],[-86.2501,36.1999],[-86.2249,36.1999],[-86.2249,36.2251],[-86.2501,36.2251]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0861500w361330n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.2501,36.1999,-86.2249,36.2251],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0861630w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.2751,36.1751],[-86.2751,36.1499],[-86.2499,36.1499],[-86.2499,36.1751],[-86.2751,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0861630w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.2751,36.1499,-86.2499,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0861630w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.2751,36.2001],[-86.2751,36.1749],[-86.2499,36.1749],[-86.2499,36.2001],[-86.2751,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0861630w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.2751,36.1749,-86.2499,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0861630w361330","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.2751,36.2251],[-86.2751,36.1999],[-86.2499,36.1999],[-86.2499,36.2251],[-86.2751,36.2251]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0861630w361330n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.2751,36.1999,-86.2499,36.2251],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0861800w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.3001,36.1751],[-86.3001,36.1499],[-86.2749,36.1499],[-86.2749,36.1751],[-86.3001,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0861800w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.3001,36.1499,-86.2749,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0861800w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.3001,36.2001],[-86.3001,36.1749],[-86.2749,36.1749],[-86.2749,36.2001],[-86.3001,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0861800w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.3001,36.1749,-86.2749,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0861800w361330","properties":{"event":"Nashville Tornado","datetime":"2020-03-10T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.3001,36.2251],[-86.3001,36.1999],[-86.2749,36.1999],[-86.2749,36.2251],[-86.3001,36.2251]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0861800w361330n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.3001,36.1999,-86.2749,36.2251],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0861930w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.3251,36.1751],[-86.3251,36.1499],[-86.2999,36.1499],[-86.2999,36.1751],[-86.3251,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0861930w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.3251,36.1499,-86.2999,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0861930w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.3251,36.2001],[-86.3251,36.1749],[-86.2999,36.1749],[-86.2999,36.2001],[-86.3251,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0861930w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.3251,36.1749,-86.2999,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0861930w361330","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.3251,36.2251],[-86.3251,36.1999],[-86.2999,36.1999],[-86.2999,36.2251],[-86.3251,36.2251]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0861930w361330n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.3251,36.1999,-86.2999,36.2251],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0862100w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.3501,36.1751],[-86.3501,36.1499],[-86.3249,36.1499],[-86.3249,36.1751],[-86.3501,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0862100w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.3501,36.1499,-86.3249,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0862100w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-10T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.3501,36.2001],[-86.3501,36.1749],[-86.3249,36.1749],[-86.3249,36.2001],[-86.3501,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0862100w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.3501,36.1749,-86.3249,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0862100w361330","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.3501,36.2251],[-86.3501,36.1999],[-86.3249,36.1999],[-86.3249,36.2251],[-86.3501,36.2251]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0862100w361330n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.3501,36.1999,-86.3249,36.2251],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0862230w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.3751,36.1751],[-86.3751,36.1499],[-86.3499,36.1499],[-86.3499,36.1751],[-86.3751,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0862230w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.3751,36.1499,-86.3499,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0862230w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.3751,36.2001],[-86.3751,36.1749],[-86.3499,36.1749],[-86.3499,36.2001],[-86.3751,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0862230w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.3751,36.1749,-86.3499,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0862230w361330","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.3751,36.2251],[-86.3751,36.1999],[-86.3499,36.1999],[-86.3499,36.2251],[-86.3751,36.2251]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0862230w361330n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.3751,36.1999,-86.3499,36.2251],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0862400w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-10T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.4001,36.1751],[-86.4001,36.1499],[-86.3749,36.1499],[-86.3749,36.1751],[-86.4001,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0862400w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.4001,36.1499,-86.3749,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0862400w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.4001,36.2001],[-86.4001,36.1749],[-86.3749,36.1749],[-86.3749,36.2001],[-86.4001,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0862400w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.4001,36.1749,-86.3749,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0862400w361330","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:01Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.4001,36.2251],[-86.4001,36.1999],[-86.3749,36.1999],[-86.3749,36.2251],[-86.4001,36.2251]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0862400w361330n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.4001,36.1999,-86.3749,36.2251],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0862530w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.4251,36.1751],[-86.4251,36.1499],[-86.3999,36.1499],[-86.3999,36.1751],[-86.4251,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0862530w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.4251,36.1499,-86.3999,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0862530w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.4251,36.2001],[-86.4251,36.1749],[-86.3999,36.1749],[-86.3999,36.2001],[-86.4251,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0862530w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.4251,36.1749,-86.3999,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0862530w361330","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.4251,36.2251],[-86.4251,36.1999],[-86.3999,36.1999],[-86.3999,36.2251],[-86.4251,36.2251]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0862530w361330n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.4251,36.1999,-86.3999,36.2251],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0862700w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-10T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.4501,36.1751],[-86.4501,36.1499],[-86.4249,36.1499],[-86.4249,36.1751],[-86.4501,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0862700w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.4501,36.1499,-86.4249,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0862700w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.4501,36.2001],[-86.4501,36.1749],[-86.4249,36.1749],[-86.4249,36.2001],[-86.4501,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0862700w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.4501,36.1749,-86.4249,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0862700w361330","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.4501,36.2251],[-86.4501,36.1999],[-86.4249,36.1999],[-86.4249,36.2251],[-86.4501,36.2251]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0862700w361330n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.4501,36.1999,-86.4249,36.2251],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0862830w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.4751,36.1751],[-86.4751,36.1499],[-86.4499,36.1499],[-86.4499,36.1751],[-86.4751,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0862830w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.4751,36.1499,-86.4499,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0862830w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.4751,36.2001],[-86.4751,36.1749],[-86.4499,36.1749],[-86.4499,36.2001],[-86.4751,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0862830w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.4751,36.1749,-86.4499,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0862830w361330","properties":{"event":"Nashville Tornado","datetime":"2020-03-10T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.4751,36.2251],[-86.4751,36.1999],[-86.4499,36.1999],[-86.4499,36.2251],[-86.4751,36.2251]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0862830w361330n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.4751,36.1999,-86.4499,36.2251],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0863000w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.5001,36.1751],[-86.5001,36.1499],[-86.4749,36.1499],[-86.4749,36.1751],[-86.5001,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0863000w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.5001,36.1499,-86.4749,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0863000w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.5001,36.2001],[-86.5001,36.1749],[-86.4749,36.1749],[-86.4749,36.2001],[-86.5001,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0863000w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.5001,36.1749,-86.4749,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0863000w361330","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.5001,36.2251],[-86.5001,36.1999],[-86.4749,36.1999],[-86.4749,36.2251],[-86.5001,36.2251]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0863000w361330n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.5001,36.1999,-86.4749,36.2251],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0863130w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.5251,36.1751],[-86.5251,36.1499],[-86.4999,36.1499],[-86.4999,36.1751],[-86.5251,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0863130w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.5251,36.1499,-86.4999,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0863130w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-10T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.5251,36.2001],[-86.5251,36.1749],[-86.4999,36.1749],[-86.4999,36.2001],[-86.5251,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0863130w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.5251,36.1749,-86.4999,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0863130w361330","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.5251,36.2251],[-86.5251,36.1999],[-86.4999,36.1999],[-86.4999,36.2251],[-86.5251,36.2251]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0863130w361330n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.5251,36.1999,-86.4999,36.2251],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0863300w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.5501,36.1751],[-86.5501,36.1499],[-86.5249,36.1499],[-86.5249,36.1751],[-86.5501,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0863300w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.5501,36.1499,-86.5249,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0863300w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.5501,36.2001],[-86.5501,36.1749],[-86.5249,36.1749],[-86.5249,36.2001],[-86.5501,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0863300w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.5501,36.1749,-86.5249,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0863300w361330","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.5501,36.2251],[-86.5501,36.1999],[-86.5249,36.1999],[-86.5249,36.2251],[-86.5501,36.2251]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0863300w361330n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.5501,36.1999,-86.5249,36.2251],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0863430w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-10T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.5751,36.1751],[-86.5751,36.1499],[-86.5499,36.1499],[-86.5499,36.1751],[-86.5751,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0863430w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.5751,36.1499,-86.5499,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0863430w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.5751,36.2001],[-86.5751,36.1749],[-86.5499,36.1749],[-86.5499,36.2001],[-86.5751,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0863430w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.5751,36.1749,-86.5499,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0863430w361330","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.5751,36.2251],[-86.5751,36.1999],[-86.5499,36.1999],[-86.5499,36.2251],[-86.5751,36.2251]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0863430w361330n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.5751,36.1999,-86.5499,36.2251],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0863600w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.6001,36.1751],[-86.6001,36.1499],[-86.5749,36.1499],[-86.5749,36.1751],[-86.6001,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0863600w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.6001,36.1499,-86.5749,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0863600w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.6001,36.2001],[-86.6001,36.1749],[-86.5749,36.1749],[-86.5749,36.2001],[-86.6001,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0863600w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.6001,36.1749,-86.5749,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0863600w361330","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.6001,36.2251],[-86.6001,36.1999],[-86.5749,36.1999],[-86.5749,36.2251],[-86.6001,36.2251]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0863600w361330n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.6001,36.1999,-86.5749,36.2251],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0863730w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-10T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.6251,36.1751],[-86.6251,36.1499],[-86.5999,36.1499],[-86.5999,36.1751],[-86.6251,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0863730w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.6251,36.1499,-86.5999,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0863730w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.6251,36.2001],[-86.6251,36.1749],[-86.5999,36.1749],[-86.5999,36.2001],[-86.6251,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0863730w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.6251,36.1749,-86.5999,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0863730w361330","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.6251,36.2251],[-86.6251,36.1999],[-86.5999,36.1999],[-86.5999,36.2251],[-86.6251,36.2251]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0863730w361330n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.6251,36.1999,-86.5999,36.2251],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0863900w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.6501,36.1751],[-86.6501,36.1499],[-86.6249,36.1499],[-86.6249,36.1751],[-86.6501,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0863900w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.6501,36.1499,-86.6249,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0863900w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.6501,36.2001],[-86.6501,36.1749],[-86.6249,36.1749],[-86.6249,36.2001],[-86.6501,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0863900w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.6501,36.1749,-86.6249,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0863900w361330","properties":{"event":"Nashville Tornado","datetime":"2020-03-10T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.6501,36.2251],[-86.6501,36.1999],[-86.6249,36.1999],[-86.6249,36.2251],[-86.6501,36.2251]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0863900w361330n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.6501,36.1999,-86.6249,36.2251],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0864030w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.6751,36.1751],[-86.6751,36.1499],[-86.6499,36.1499],[-86.6499,36.1751],[-86.6751,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0864030w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.6751,36.1499,-86.6499,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0864030w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.6751,36.2001],[-86.6751,36.1749],[-86.6499,36.1749],[-86.6499,36.2001],[-86.6751,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0864030w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.6751,36.1749,-86.6499,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0864030w361330","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.6751,36.2251],[-86.6751,36.1999],[-86.6499,36.1999],[-86.6499,36.2251],[-86.6751,36.2251]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0864030w361330n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.6751,36.1999,-86.6499,36.2251],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0864200w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.7001,36.1751],[-86.7001,36.1499],[-86.6749,36.1499],[-86.6749,36.1751],[-86.7001,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0864200w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.7001,36.1499,-86.6749,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0864200w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-10T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.7001,36.2001],[-86.7001,36.1749],[-86.6749,36.1749],[-86.6749,36.2001],[-86.7001,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0864200w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.7001,36.1749,-86.6749,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0864330w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.7251,36.1751],[-86.7251,36.1499],[-86.6999,36.1499],[-86.6999,36.1751],[-86.7251,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0864330w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.7251,36.1499,-86.6999,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0864330w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.7251,36.2001],[-86.7251,36.1749],[-86.6999,36.1749],[-86.6999,36.2001],[-86.7251,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0864330w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.7251,36.1749,-86.6999,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0864500w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.7501,36.1751],[-86.7501,36.1499],[-86.7249,36.1499],[-86.7249,36.1751],[-86.7501,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0864500w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.7501,36.1499,-86.7249,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0864500w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.7501,36.2001],[-86.7501,36.1749],[-86.7249,36.1749],[-86.7249,36.2001],[-86.7501,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0864500w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.7501,36.1749,-86.7249,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0864630w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-10T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.7751,36.1751],[-86.7751,36.1499],[-86.7499,36.1499],[-86.7499,36.1751],[-86.7751,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0864630w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.7751,36.1499,-86.7499,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0864630w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.7751,36.2001],[-86.7751,36.1749],[-86.7499,36.1749],[-86.7499,36.2001],[-86.7751,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0864630w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.7751,36.1749,-86.7499,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0864800w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.8001,36.1751],[-86.8001,36.1499],[-86.7749,36.1499],[-86.7749,36.1751],[-86.8001,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0864800w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.8001,36.1499,-86.7749,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0864800w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.8001,36.2001],[-86.8001,36.1749],[-86.7749,36.1749],[-86.7749,36.2001],[-86.8001,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0864800w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.8001,36.1749,-86.7749,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0864930w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.8251,36.1751],[-86.8251,36.1499],[-86.7999,36.1499],[-86.7999,36.1751],[-86.8251,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0864930w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.8251,36.1499,-86.7999,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0864930w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.8251,36.2001],[-86.8251,36.1749],[-86.7999,36.1749],[-86.7999,36.2001],[-86.8251,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0864930w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.8251,36.1749,-86.7999,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0865100w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-10T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.8501,36.1751],[-86.8501,36.1499],[-86.8249,36.1499],[-86.8249,36.1751],[-86.8501,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0865100w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.8501,36.1499,-86.8249,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0865100w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.8501,36.2001],[-86.8501,36.1749],[-86.8249,36.1749],[-86.8249,36.2001],[-86.8501,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0865100w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.8501,36.1749,-86.8249,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0865230w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.8751,36.1751],[-86.8751,36.1499],[-86.8499,36.1499],[-86.8499,36.1751],[-86.8751,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0865230w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.8751,36.1499,-86.8499,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0865230w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.8751,36.2001],[-86.8751,36.1749],[-86.8499,36.1749],[-86.8499,36.2001],[-86.8751,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0865230w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.8751,36.1749,-86.8499,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0865400w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.9001,36.1751],[-86.9001,36.1499],[-86.8749,36.1499],[-86.8749,36.1751],[-86.9001,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0865400w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.9001,36.1499,-86.8749,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0865400w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-10T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.9001,36.2001],[-86.9001,36.1749],[-86.8749,36.1749],[-86.8749,36.2001],[-86.9001,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0865400w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.9001,36.1749,-86.8749,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0865530w360900","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.9251,36.1501],[-86.9251,36.1249],[-86.8999,36.1249],[-86.8999,36.1501],[-86.9251,36.1501]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0865530w360900n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.9251,36.1249,-86.8999,36.1501],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0865530w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.9251,36.1751],[-86.9251,36.1499],[-86.8999,36.1499],[-86.8999,36.1751],[-86.9251,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0865530w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.9251,36.1499,-86.8999,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0865530w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.9251,36.2001],[-86.9251,36.1749],[-86.8999,36.1749],[-86.8999,36.2001],[-86.9251,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0865530w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.9251,36.1749,-86.8999,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0865700w360900","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.9501,36.1501],[-86.9501,36.1249],[-86.9249,36.1249],[-86.9249,36.1501],[-86.9501,36.1501]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0865700w360900n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.9501,36.1249,-86.9249,36.1501],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0865700w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-10T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.9501,36.1751],[-86.9501,36.1499],[-86.9249,36.1499],[-86.9249,36.1751],[-86.9501,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0865700w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.9501,36.1499,-86.9249,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0865700w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-11T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.9501,36.2001],[-86.9501,36.1749],[-86.9249,36.1749],[-86.9249,36.2001],[-86.9501,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0865700w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.9501,36.1749,-86.9249,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0865830w360900","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.9751,36.1501],[-86.9751,36.1249],[-86.9499,36.1249],[-86.9499,36.1501],[-86.9751,36.1501]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0865830w360900n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.9751,36.1249,-86.9499,36.1501],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0865830w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.9751,36.1751],[-86.9751,36.1499],[-86.9499,36.1499],[-86.9499,36.1751],[-86.9751,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0865830w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.9751,36.1499,-86.9499,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0865830w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-86.9751,36.2001],[-86.9751,36.1749],[-86.9499,36.1749],[-86.9499,36.2001],[-86.9751,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0865830w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-86.9751,36.1749,-86.9499,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0870000w361030","properties":{"event":"Nashville Tornado","datetime":"2020-03-07T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-87.0001,36.1751],[-87.0001,36.1499],[-86.9749,36.1499],[-86.9749,36.1751],[-87.0001,36.1751]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0870000w361030n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-87.0001,36.1499,-86.9749,36.1751],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0870000w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-08T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-87.0001,36.2001],[-87.0001,36.1749],[-86.9749,36.1749],[-86.9749,36.2001],[-87.0001,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0870000w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-87.0001,36.1749,-86.9749,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} -{"type":"Feature","stac_version":"1.0.0","id":"20200307aC0870130w361200","properties":{"event":"Nashville Tornado","datetime":"2020-03-09T00:00:00Z"},"geometry":{"type":"Polygon","coordinates":[[[-87.0251,36.2001],[-87.0251,36.1749],[-86.9999,36.1749],[-86.9999,36.2001],[-87.0251,36.2001]]]},"links":[{"rel":"collection","href":"noaa-emergency-response","type":"application/json"}],"assets":{"cog":{"href":"https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0870130w361200n.tif","type":"image/tiff; application=geotiff; profile=cloud-optimized"}},"bbox":[-87.0251,36.1749,-86.9999,36.2001],"stac_extensions":[],"collection":"noaa-emergency-response"} diff --git a/.github/workflows/tests/test_raster.py b/.github/workflows/tests/test_raster.py deleted file mode 100644 index 0bd21cd..0000000 --- a/.github/workflows/tests/test_raster.py +++ /dev/null @@ -1,249 +0,0 @@ -"""test EOapi.""" - -import httpx - -raster_endpoint = "http://0.0.0.0:8082" - - -def test_raster_api(): - """test api.""" - # health - resp = httpx.get(f"{raster_endpoint}/healthz") - assert resp.status_code == 200 - assert resp.headers["content-type"] == "application/json" - - -def test_mosaic_api(): - """test mosaic.""" - query = {"collections": ["noaa-emergency-response"], "filter-lang": "cql-json"} - resp = httpx.post(f"{raster_endpoint}/searches/register", json=query) - assert resp.headers["content-type"] == "application/json" - assert resp.status_code == 200 - assert resp.json()["id"] - assert resp.json()["links"] - - searchid = resp.json()["id"] - - resp = httpx.get(f"{raster_endpoint}/searches/{searchid}/-85.6358,36.1624/assets") - assert resp.status_code == 200 - assert len(resp.json()) == 1 - assert list(resp.json()[0]) == ["id", "bbox", "assets", "collection"] - assert resp.json()[0]["id"] == "20200307aC0853900w361030" - - resp = httpx.get( - f"{raster_endpoint}/searches/{searchid}/tiles/15/8589/12849/assets" - ) - assert resp.status_code == 200 - assert len(resp.json()) == 1 - assert list(resp.json()[0]) == ["id", "bbox", "assets", "collection"] - assert resp.json()[0]["id"] == "20200307aC0853900w361030" - - z, x, y = 15, 8589, 12849 - resp = httpx.get( - f"{raster_endpoint}/searches/{searchid}/tiles/{z}/{x}/{y}", - params={"assets": "cog"}, - headers={"Accept-Encoding": "br, gzip"}, - timeout=10.0, - ) - assert resp.status_code == 200 - assert resp.headers["content-type"] == "image/jpeg" - assert "content-encoding" not in resp.headers - - -def test_mosaic_collection_api(): - """test mosaic collection.""" - resp = httpx.get( - f"{raster_endpoint}/collections/noaa-emergency-response/-85.6358,36.1624/assets" - ) - assert resp.status_code == 200 - assert len(resp.json()) == 1 - assert list(resp.json()[0]) == ["id", "bbox", "assets", "collection"] - assert resp.json()[0]["id"] == "20200307aC0853900w361030" - - resp = httpx.get( - f"{raster_endpoint}/collections/noaa-emergency-response/tiles/15/8589/12849/assets" - ) - assert resp.status_code == 200 - assert len(resp.json()) == 1 - assert list(resp.json()[0]) == ["id", "bbox", "assets", "collection"] - assert resp.json()[0]["id"] == "20200307aC0853900w361030" - - z, x, y = 15, 8589, 12849 - resp = httpx.get( - f"{raster_endpoint}/collections/noaa-emergency-response/tiles/{z}/{x}/{y}", - params={"assets": "cog"}, - headers={"Accept-Encoding": "br, gzip"}, - timeout=10.0, - ) - assert resp.status_code == 200 - assert resp.headers["content-type"] == "image/jpeg" - assert "content-encoding" not in resp.headers - - -def test_mosaic_search(): - """test mosaic.""" - # register some fake mosaic - searches = [ - { - "filter": {"op": "=", "args": [{"property": "collection"}, "collection1"]}, - "metadata": {"owner": "vincent"}, - }, - { - "filter": {"op": "=", "args": [{"property": "collection"}, "collection2"]}, - "metadata": {"owner": "vincent"}, - }, - { - "filter": {"op": "=", "args": [{"property": "collection"}, "collection3"]}, - "metadata": {"owner": "vincent"}, - }, - { - "filter": {"op": "=", "args": [{"property": "collection"}, "collection4"]}, - "metadata": {"owner": "vincent"}, - }, - { - "filter": {"op": "=", "args": [{"property": "collection"}, "collection5"]}, - "metadata": {"owner": "vincent"}, - }, - { - "filter": {"op": "=", "args": [{"property": "collection"}, "collection6"]}, - "metadata": {"owner": "vincent"}, - }, - { - "filter": {"op": "=", "args": [{"property": "collection"}, "collection7"]}, - "metadata": {"owner": "vincent"}, - }, - { - "filter": {"op": "=", "args": [{"property": "collection"}, "collection8"]}, - "metadata": {"owner": "sean"}, - }, - { - "filter": {"op": "=", "args": [{"property": "collection"}, "collection9"]}, - "metadata": {"owner": "sean"}, - }, - { - "filter": {"op": "=", "args": [{"property": "collection"}, "collection10"]}, - "metadata": {"owner": "drew"}, - }, - { - "filter": {"op": "=", "args": [{"property": "collection"}, "collection11"]}, - "metadata": {"owner": "drew"}, - }, - { - "filter": {"op": "=", "args": [{"property": "collection"}, "collection12"]}, - "metadata": {"owner": "drew"}, - }, - ] - for search in searches: - resp = httpx.post(f"{raster_endpoint}/searches/register", json=search) - assert resp.status_code == 200 - assert resp.json()["id"] - - resp = httpx.get(f"{raster_endpoint}/searches/list") - assert resp.headers["content-type"] == "application/json" - assert resp.status_code == 200 - assert ( - resp.json()["context"]["matched"] > 10 - ) # there should be at least 12 mosaic registered - assert resp.json()["context"]["returned"] == 10 # default limit is 10 - - # Make sure all mosaics returned have - for mosaic in resp.json()["searches"]: - assert mosaic["search"]["metadata"]["type"] == "mosaic" - - links = resp.json()["links"] - assert len(links) == 2 - assert links[0]["rel"] == "self" - assert links[1]["rel"] == "next" - assert links[1]["href"] == f"{raster_endpoint}/searches/list?limit=10&offset=10" - - resp = httpx.get( - f"{raster_endpoint}/searches/list", params={"limit": 1, "offset": 1} - ) - assert resp.status_code == 200 - assert resp.json()["context"]["matched"] > 10 - assert resp.json()["context"]["limit"] == 1 - assert resp.json()["context"]["returned"] == 1 - - links = resp.json()["links"] - assert len(links) == 3 - assert links[0]["rel"] == "self" - assert links[0]["href"] == f"{raster_endpoint}/searches/list?limit=1&offset=1" - assert links[1]["rel"] == "next" - assert links[1]["href"] == f"{raster_endpoint}/searches/list?limit=1&offset=2" - assert links[2]["rel"] == "prev" - assert links[2]["href"] == f"{raster_endpoint}/searches/list?limit=1&offset=0" - - # Filter on mosaic metadata - resp = httpx.get(f"{raster_endpoint}/searches/list", params={"owner": "vincent"}) - assert resp.status_code == 200 - assert resp.json()["context"]["matched"] == 7 - assert resp.json()["context"]["limit"] == 10 - assert resp.json()["context"]["returned"] == 7 - - # sortBy - resp = httpx.get(f"{raster_endpoint}/searches/list", params={"sortby": "lastused"}) - assert resp.status_code == 200 - - resp = httpx.get(f"{raster_endpoint}/searches/list", params={"sortby": "usecount"}) - assert resp.status_code == 200 - - resp = httpx.get(f"{raster_endpoint}/searches/list", params={"sortby": "-owner"}) - assert resp.status_code == 200 - assert ( - "owner" not in resp.json()["searches"][0]["search"]["metadata"] - ) # some mosaic don't have owners - - resp = httpx.get(f"{raster_endpoint}/searches/list", params={"sortby": "owner"}) - assert resp.status_code == 200 - assert "owner" in resp.json()["searches"][0]["search"]["metadata"] - - -def test_item(): - """test stac endpoints.""" - resp = httpx.get( - f"{raster_endpoint}/collections/noaa-emergency-response/items/20200307aC0853300w361200/assets", - ) - assert resp.status_code == 200 - assert resp.headers["content-type"] == "application/json" - assert resp.json() == ["cog"] - - resp = httpx.get( - f"{raster_endpoint}/collections/noaa-emergency-response/items/20200307aC0853300w361200/tilejson.json", - params={ - "assets": "cog", - }, - ) - assert resp.status_code == 200 - assert resp.headers["content-type"] == "application/json" - assert resp.json()["tilejson"] - assert "assets=cog" in resp.json()["tiles"][0] - assert ( - "/collections/noaa-emergency-response/items/20200307aC0853300w361200" - in resp.json()["tiles"][0] - ) - assert resp.json()["bounds"] == [-85.5501, 36.1749, -85.5249, 36.2001] - - -def test_collections(): - """test collection endpoints.""" - resp = httpx.get( - f"{raster_endpoint}/collections", - ) - assert resp.status_code == 200 - assert resp.headers["content-type"] == "application/json" - - collections = resp.json() - assert len(collections) == 1 - assert collections[0]["id"] == "noaa-emergency-response" - - -def test_cog_endpoints(): - """test /cog endpoints""" - resp = httpx.get( - f"{raster_endpoint}/cog/info", - params={ - "url": "https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0854500w361030n.tif", - }, - ) - assert resp.status_code == 200 - assert resp.headers["content-type"] == "application/json" diff --git a/.github/workflows/tests/test_stac.py b/.github/workflows/tests/test_stac.py deleted file mode 100644 index 3b4e15d..0000000 --- a/.github/workflows/tests/test_stac.py +++ /dev/null @@ -1,53 +0,0 @@ -"""test EOapi.""" - -import httpx - -stac_endpoint = "http://0.0.0.0:8081" - - -def test_stac_api(): - """test stac.""" - # Ping - assert httpx.get(f"{stac_endpoint}/_mgmt/ping").status_code == 200 - - # viewer - assert httpx.get(f"{stac_endpoint}/index.html").status_code == 200 - - # Collections - resp = httpx.get(f"{stac_endpoint}/collections") - assert resp.status_code == 200 - collections = resp.json()["collections"] - assert len(collections) > 0 - ids = [c["id"] for c in collections] - assert "noaa-emergency-response" in ids - - # items - resp = httpx.get(f"{stac_endpoint}/collections/noaa-emergency-response/items") - assert resp.status_code == 200 - items = resp.json()["features"] - assert len(items) == 10 - - # item - resp = httpx.get( - f"{stac_endpoint}/collections/noaa-emergency-response/items/20200307aC0853300w361200" - ) - assert resp.status_code == 200 - item = resp.json() - assert item["id"] == "20200307aC0853300w361200" - - -def test_stac_to_raster(): - """test link to raster api.""" - # tilejson - resp = httpx.get( - f"{stac_endpoint}/collections/noaa-emergency-response/items/20200307aC0853300w361200/tilejson.json", - params={"assets": "cog"}, - ) - assert resp.status_code == 307 - - # viewer - resp = httpx.get( - f"{stac_endpoint}/collections/noaa-emergency-response/items/20200307aC0853300w361200/viewer", - params={"assets": "cog"}, - ) - assert resp.status_code == 307 diff --git a/.github/workflows/tests/test_vector.py b/.github/workflows/tests/test_vector.py deleted file mode 100644 index 3d15efa..0000000 --- a/.github/workflows/tests/test_vector.py +++ /dev/null @@ -1,100 +0,0 @@ -"""test EOapi.vector""" - -import httpx - -vector_endpoint = "http://0.0.0.0:8083" - - -def test_vector_api(): - """test vector.""" - # landing - resp = httpx.get(f"{vector_endpoint}/") - assert resp.status_code == 200 - assert resp.headers["content-type"] == "application/json" - assert resp.json()["links"] - - # conformance - resp = httpx.get(f"{vector_endpoint}/conformance") - assert resp.status_code == 200 - assert resp.headers["content-type"] == "application/json" - assert resp.json()["conformsTo"] - - # collections - resp = httpx.get(f"{vector_endpoint}/collections") - assert resp.status_code == 200 - assert resp.headers["content-type"] == "application/json" - - assert list(resp.json()) == [ - "links", - "numberMatched", - "numberReturned", - "collections", - ] - assert resp.json()["numberMatched"] == 4 # one public table + 3 functions - assert resp.json()["numberReturned"] == 4 - - collections = resp.json()["collections"] - ids = [c["id"] for c in collections] - # 3 Functions - assert "pg_temp.pgstac_collections_view" in ids - assert "pg_temp.pgstac_hash" in ids - assert "pg_temp.pgstac_hash_count" in ids - # 1 public table - assert "public.my_data" in ids - - # collection - resp = httpx.get(f"{vector_endpoint}/collections/pg_temp.pgstac_collections_view") - assert resp.status_code == 200 - assert resp.headers["content-type"] == "application/json" - assert resp.json()["links"] - assert resp.json()["itemType"] == "feature" - - # items - resp = httpx.get( - f"{vector_endpoint}/collections/pg_temp.pgstac_collections_view/items" - ) - assert resp.status_code == 200 - assert resp.headers["content-type"] == "application/geo+json" - items = resp.json()["features"] - assert len(items) == 1 - - # limit - resp = httpx.get( - f"{vector_endpoint}/collections/pg_temp.pgstac_collections_view/items", - params={"limit": 1}, - ) - assert resp.status_code == 200 - items = resp.json()["features"] - assert len(items) == 1 - - # intersects - resp = httpx.get( - f"{vector_endpoint}/collections/pg_temp.pgstac_collections_view/items", - params={"bbox": "-180,0,0,90"}, - ) - assert resp.status_code == 200 - items = resp.json()["features"] - assert len(items) == 1 - - # item - resp = httpx.get( - f"{vector_endpoint}/collections/pg_temp.pgstac_collections_view/items/noaa-emergency-response" - ) - assert resp.status_code == 200 - item = resp.json() - assert item["id"] == "noaa-emergency-response" - - # OGC Tiles - resp = httpx.get(f"{vector_endpoint}/collections/public.my_data/tiles/0/0/0") - assert resp.status_code == 200 - - resp = httpx.get( - f"{vector_endpoint}/collections/pg_temp.pgstac_collections_view/tilejson.json" - ) - assert resp.status_code == 200 - - resp = httpx.get(f"{vector_endpoint}/tileMatrixSets") - assert resp.status_code == 200 - - resp = httpx.get(f"{vector_endpoint}/tileMatrixSets/WebMercatorQuad") - assert resp.status_code == 200 diff --git a/.isort.cfg b/.isort.cfg deleted file mode 100644 index 86365d2..0000000 --- a/.isort.cfg +++ /dev/null @@ -1,3 +0,0 @@ -[settings] -profile = black -known_first_party = ["eoapi"] diff --git a/.mypy.ini b/.mypy.ini deleted file mode 100644 index 1122180..0000000 --- a/.mypy.ini +++ /dev/null @@ -1,5 +0,0 @@ -[mypy] -no_implicit_optional = true -strict_optional = true -namespace_packages = true -explicit_package_bases = true diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml deleted file mode 100644 index 1a765fd..0000000 --- a/.pre-commit-config.yaml +++ /dev/null @@ -1,27 +0,0 @@ -repos: - - repo: https://github.com/psf/black - rev: 22.12.0 - hooks: - - id: black - language_version: python - - - repo: https://github.com/PyCQA/isort - rev: 5.12.0 - hooks: - - id: isort - language_version: python - - - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.238 - hooks: - - id: ruff - args: ["--fix"] - - - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.991 - hooks: - - id: mypy - language_version: python - additional_dependencies: - - types-requests - - types-attrs diff --git a/CHANGES.md b/CHANGES.md deleted file mode 100644 index b9e0080..0000000 --- a/CHANGES.md +++ /dev/null @@ -1,5 +0,0 @@ -# Release Notes - -## 0.1.0 (TBD) - -Initial release diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 96d5774..2789f6e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,35 +2,6 @@ Issues and pull requests are more than welcome: https://github.com/developmentse You can also start **Discussions** in https://github.com/developmentseed/eoAPI/discussions -**dev install** - -```bash -# Download the code -git clone https://github.com/developmentseed/eoAPI.git -cd eoAPI - -# Create a virtual environment -python -m pip install --upgrade virtualenv -virtualenv .venv -source .venv/bin/activate - -# Install eoapi module -python -m pip install "psycopg[binary,pool]" -python -m pip install -e runtime/eoapi/raster["test"] # or -e runtime/eoapi/stac["test"] | -e runtime/eoapi/vector["test"] -``` - -!!! danger - - Python applications might have incompatible dependencies which you can resolve by using virtual environment *per application* - -**pre-commit** - -This repo is set to use `pre-commit` to run *isort*, *ruff*, *pydocstring*, *black* ("uncompromising Python code formatter") and mypy when committing new code. - -```bash -$ pre-commit install -``` - ### Open Source You can also contribute indirectly to eoAPI by helping on the sub-modules: diff --git a/README.md b/README.md index 4cf8d9e..d0e1580 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,31 @@ +
Create a full Earth Observation API with Metadata, Raster, and Vector services.
--- -**Documentation**: https://eoapi.dev - -**Source Code**: https://github.com/developmentseed/eoAPI +- **Documentation**: https://eoapi.dev +- **Source Code**: https://github.com/developmentseed/eoAPI --- -## **E**arth **O**bservation **API** - -`eoAPI` combines several *state-of-the-art* projects to create a full Earth Observation API. Each service can be used and deployed independently, but `eoAPI` creates the interconnections between each service: - -- **pgSTAC** database [https://github.com/stac-utils/pgstac](https://github.com/stac-utils/pgstac) +## Earth Observation API -- **STAC API** built on top of [https://github.com/stac-utils/stac-fastapi](https://github.com/stac-utils/stac-fastapi) +`eoAPI` combines several state-of-the-art projects to create a full Earth Observation API. Each service can be used and deployed independently, but `eoAPI` creates the interconnections between each service: -- **STAC Items And Mosaic Raster Tiles** API built on top of [https://github.com/stac-utils/titiler-pgstac](https://github.com/stac-utils/titiler-pgstac) - -- **OGC Features and Vector Tiles** API built on top of [https://github.com/developmentseed/tipg](https://github.com/developmentseed/tipg) - -- **A STAC Catalog browsing UI** based on the radiant earth browser : [https://github.com/radiantearth/stac-browser](https://github.com/radiantearth/stac-browser) +- **pgSTAC** database: https://github.com/stac-utils/pgstac +- **STAC API** built on top of: https://github.com/stac-utils/stac-fastapi +- **STAC Items And Mosaic Raster Tiles** API built on top of: https://github.com/stac-utils/titiler-pgstac +- **OGC Features and Vector Tiles** API built on top of: https://github.com/developmentseed/tipg +- **A STAC Catalog browsing UI** based on the radiant earth browser: https://github.com/radiantearth/stac-browser --- @@ -40,38 +33,33 @@ `eoAPI` is proudly open-source and driven by a dedicated community of contributors. We believe in the power of open collaboration and welcome anyone to contribute, discuss, and grow this tool. Join the conversations on [GitHub Discussions](https://github.com/developmentseed/eoAPI/discussions) and make a difference in the Earth Observation realm. - --- ## Getting started -The easiest way to start exploring the different eoAPI services is with *Docker*. Clone this repository and start the multi-container *Docker* applications using `Compose`: +The easiest way to explore the different eoAPI services is with **Docker**. Clone this repository and start the multi-container **Docker** applications using `Compose`: -``` +```bash git clone https://github.com/developmentseed/eoAPI.git cd eoAPI docker compose up ``` -Once the applications are *up*, you'll need to add STAC **Collections** and **Items** to the PgSTAC database. If you don't have these available, you can follow the [MAXAR open data demo](https://github.com/vincentsarago/MAXAR_opendata_to_pgstac) (or get inspired by the other [demos](https://github.com/developmentseed/eoAPI/tree/main/demo)). +Once the applications are **up**, you'll need to add STAC **Collections** and **Items** to the PgSTAC database. If you don't have these available, you can follow the [MAXAR open data demo](https://github.com/vincentsarago/MAXAR_opendata_to_pgstac) (or get inspired by the other [demos](https://github.com/developmentseed/eoAPI/tree/main/demo)). Then you can start exploring your dataset with: - - the STAC Metadata service [http://localhost:8081](http://localhost:8081) - - the Raster service [http://localhost:8082](http://localhost:8082) - - the browser UI [http://localhost:8085](http://localhost:8085) - -If you've added a vector dataset to the `public` schema in the Postgres database, they will be available through the **Vector** service at [http://localhost:8083](http://localhost:8083). - -## Deployment with standard runtimes +- the STAC Metadata service: http://localhost:8081 +- the Raster service: http://localhost:8082 +- the browser UI: http://localhost:8085 -This repository has current runtimes that are consistently updated with new functionality. +If you've added a vector dataset to the `public` schema in the Postgres database, they will be available through the **Vector** service at http://localhost:8083. ### Local deployment The services can be deployed altogether locally with `docker compose up`. -Alternatively, you may install the libraries and launch the applications manually: +Alternatively, you may install the libraries and launch the applications manually:- - + +
-Code: [/runtime/eoapi/stac](https://github.com/developmentseed/eoAPI/tree/main/runtime/eoapi/stac) +Code: [/runtimes/eoapi/stac](https://github.com/developmentseed/eoapi-devseed/tree/main/runtimes/eoapi/stac) --- + ## eoapi.raster -The dynamic tiler deployed within `eoAPI` is built on top of [titiler-pgstac](https://github.com/stac-utils/titiler-pgstac) and [pgstac](https://github.com/stac-utils/pgstac). It enables large-scale mosaic based on the results of STAC search queries. +The dynamic tiler deployed within `eoapi-devseed` is built on top of [titiler-pgstac](https://github.com/stac-utils/titiler-pgstac) and [pgstac](https://github.com/stac-utils/pgstac). It enables large-scale mosaic based on the results of STAC search queries. The service includes all the default endpoints from **titiler-pgstac** application and: - `/`: a custom landing page with links to the different endpoints - -- `/mosaic/builder`: a virtual mosaic builder UI, which helps create and register STAC Search queries - -- `/collections`: a *secret* (not in OpenAPI documentation) endpoint used in the mosaic-builder page - +- `/mosaic/builder`: a virtual mosaic builder UI that helps create and register STAC Search queries +- `/collections`: a secret (not in OpenAPI documentation) endpoint used in the mosaic-builder page - `/collections/{collection_id}/items/{item_id}/viewer`: a simple STAC Item viewer- - - + + +
- -Code: [/runtime/eoapi/raster](https://github.com/developmentseed/eoAPI/tree/main/runtime/eoapi/raster) +Code: [/runtimes/eoapi/raster](https://github.com/developmentseed/eoapi-devseed/tree/main/runtimes/eoapi/raster) --- + ## eoapi.vector OGC Features and Tiles API built on top of [tipg](https://github.com/developmentseed/tipg). -By default, the API will look for tables in the `public` schema of the database. We've also added three functions that connect to the pgSTAC schema: +The API will look for tables in the database's `public` schema by default. We've also added three functions that connect to the pgSTAC schema: - **pg_temp.pgstac_collections_view**: Simple function which returns PgSTAC Collections - **pg_temp.pgstac_hash**: Return features for a specific `searchId` (hash) - **pg_temp.pgstac_hash_count**: Return the number of items per geometry for a specific `searchId` (hash)- - + +
-Code: [/runtime/eoapi/vector](https://github.com/developmentseed/eoAPI/tree/main/runtime/eoapi/vector) +Code: [/runtimes/eoapi/vector](https://github.com/developmentseed/eoapi-devseed/tree/main/runtimes/eoapi/vector) + +--- --- # STAC browser -The custom browser configuration can be modified using the config located in [/dockerfiles/browser_config.js](https://github.com/developmentseed/eoAPI/tree/main/dockerfiles/browser_config.js). For more information about available configurations, see the [Radiant Earth repository](https://github.com/radiantearth/stac-browser). +The custom browser configuration can be modified using the config located in [/dockerfiles/browser_config.js](https://github.com/developmentseed/eoapi-devseed/blob/main/dockerfiles/browser_config.js). For more information about available configurations, see the [Radiant Earth repository](https://github.com/radiantearth/stac-browser). diff --git a/docs/src/deployment.md b/docs/src/deployment.md index cdeb8a0..4860277 100644 --- a/docs/src/deployment.md +++ b/docs/src/deployment.md @@ -1,13 +1,11 @@ ---- + hide: - - navigation +- navigation --- - ## Via [eoapi-cdk](https://github.com/developmentseed/eoapi-cdk) - -[eoapi-cdk](https://github.com/developmentseed/eoapi-cdk) is a set of AWS CDK constructs that can be used to easily deploy eoAPI services on AWS with the CDK. +[eoapi-cdk](https://github.com/developmentseed/eoapi-cdk) is a set of AWS CDK constructs that can readily deploy eoAPI services on AWS with the CDK. [eoapi-template](https://github.com/developmentseed/eoapi-template) is an AWS CDK app that shows how to configure the [eoapi-cdk](https://github.com/developmentseed/eoapi-cdk) constructs. @@ -18,146 +16,140 @@ The stack is deployed by the [AWS CDK](https://aws.amazon.com/cdk/) utility. Und The example commands here will deploy a CloudFormation stack called `eoAPI-staging`. 1. Clone the `eoapi` repo and install dependencies - ```bash - # Download eoapi repo - git clone https://github.com/developmentseed/eoapi.git - - # Create a virtual environment - python -m pip install --upgrade virtualenv - virtualenv infrastructure/aws/.venv - source infrastructure/aws/.venv/bin/activate - # install cdk dependencies - python -m pip install -r infrastructure/aws/requirements-cdk.txt - ``` +```bash +# Download eoapi repo +git clone https://github.com/developmentseed/eoapi-template.git +cd eoapi-template -2. Install node dependency - requires node version 14+ - ```bash - npm --prefix infrastructure/aws install - ``` +# Create a virtual environment +python -m venv .venv +source .venv/bin/activate -3. Update settings +# install cdk dependencies +python -m pip install -r requirements.txt +``` - Set environment variable or complex code in the `infrastructure/aws/.env` file (e.g., `CDK_EOAPI_DB_PGSTAC_VERSION=0.7.1`). +2. Install node dependency - requires node version 14+ - To modify the size of the burstable database instance, modify `CDK_EOAPI_DB_INSTANCE_SIZE` to one of the values of [`aws_cdk.aws_ec2.InstanceSize`](https://docs.aws.amazon.com/cdk/api/v2/python/aws_cdk.aws_ec2/InstanceSize.html#instancesize). - The default size is `SMALL`. +```bash +npm install +``` - **Important**: +3. Update settings - - `CDK_EOAPI_DB_PGSTAC_VERSION` is a required env (see https://github.com/stac-utils/pgstac/tags for the latest version) +Set environment variable or complex code in the `.env` or `config.yaml` file (e.g., https://github.com/developmentseed/eoapi-template/blob/main/config.yaml.example). - - You can choose which functions to deploy by setting `CDK_EOAPI_FUNCTIONS` env (e.g., `CDK_EOAPI_FUNCTIONS='["stac","raster","vector"]'`) +See https://github.com/developmentseed/eoapi-template/blob/main/infrastructure/config.py for more info on the configuration options. +4. Install CDK and connect to your AWS account. This step is only necessary once per AWS account. The environment variables `PROJECT_ID` and `STAGE` determine the name of the stack (e.g., eoAPI-staging or eoAPI-production) -4. Install CDK and connect to your AWS account. This step is only necessary once per AWS account. The environment variable `CDK_EOAPI_STAGE` determines the name of the stack -(e.g., eoAPI-staging or eoAPI-production) - ```bash - # Deploy the CDK toolkit stack into an AWS environment. - CDK_EOAPI_STAGE=staging \ - CDK_EOAPI_DB_PGSTAC_VERSION=0.7.1 \ - npm --prefix infrastructure/aws run cdk -- bootstrap +```bash +# Deploy the CDK toolkit stack into an AWS environment. +PROJECT_ID=eoAPI \ +STAGE=staging \ +npx cdk bootstrap - # or to a specific region - AWS_DEFAULT_REGION=us-west-2 \ - AWS_REGION=us-west-2 \ - CDK_EOAPI_STAGE=staging \ - CDK_EOAPI_DB_PGSTAC_VERSION=0.7.1 \ - npm --prefix infrastructure/aws run cdk -- bootstrap - ``` +# or to a specific region +AWS_DEFAULT_REGION=us-west-2 \ +AWS_REGION=us-west-2 \ +PROJECT_ID=eoAPI \ +STAGE=staging \ +npx cdk bootstrap +``` 5. Pre-Generate CFN template - ```bash - CDK_EOAPI_STAGE=staging \ - CDK_EOAPI_DB_PGSTAC_VERSION=0.7.1 \ - npm --prefix infrastructure/aws run cdk -- synth # Synthesizes and prints the CloudFormation template for this stack - ``` +```bash +PROJECT_ID=eoAPI \ +STAGE=staging \ +npx cdk synth --all # Synthesizes and prints the CloudFormation template for this stack +``` 6. Deploy - ```bash - CDK_EOAPI_STAGE=staging \ - CDK_EOAPI_DB_PGSTAC_VERSION=0.7.1 \ - npm --prefix infrastructure/aws run cdk -- deploy eoAPI-staging +```bash +# Note: a VPC stack is needed for the database +PROJECT_ID=eoAPI \ +STAGE=staging \ +npx cdk deploy vpceoAPI-staging eoAPI-staging - # Deploy in a specific region - AWS_DEFAULT_REGION=eu-central-1 \ - AWS_REGION=eu-central-1 \ - CDK_EOAPI_STAGE=staging \ - CDK_EOAPI_DB_PGSTAC_VERSION=0.7.1 \ - npm --prefix infrastructure/aws run cdk -- deploy eoapi-staging --profile {my-aws-profile} - ``` +# Deploy in a specific region +AWS_DEFAULT_REGION=eu-central-1 \ +AWS_REGION=eu-central-1 \ +PROJECT_ID=eoAPI \ +STAGE=staging \ +npx cdk deploy vpceoAPI-staging eoAPI-staging --profile {my-aws-profile} +``` -If you get an error saying that the max VPCs have been reached, you have hit the limit for the number of VPCs per unique AWS account and region combination. You can change the AWS region to a region with fewer VPCs and deploy again to fix this. +If you get an error saying that the max VPCs have been reached, you have hit the limit for the number of VPCs per unique AWS account and region combination. To fix this, you can change the AWS region to a region with fewer VPCs and deploy again. ## Via [eoapi-k8s](https://github.com/developmentseed/eoapi-k8s) [eoapi-k8s](https://github.com/developmentseed/eoapi-k8s) has the IaC and Helm charts for deploying eoAPI services on AWS and GCP. -**Getting started** +### Getting started -If you still need to set up a k8s cluster on AWS or GCP, then follow an IaC guide below that is relevant to you. +If you still need to set up a K8s cluster on AWS or GCP, follow the relevant IaC guide below. > ⓘ The helm chart in this repo assumes your cluster has a few third-party add-ons and controllers installed. So > It's in your best interest to read through the IaC guides to understand what those defaults are -* :octicons-link-external-16: [AWS EKS Cluster Setup](https://github.com/developmentseed/eoapi-k8s/blob/main/docs/aws-eks.md) - -* :octicons-link-external-16: [TBD: GCP GKE Cluster Setup](https://github.com/developmentseed/eoapi-k8s/blob/main/docs/gcp-gke.md) +- :octicons-link-external-16: [AWS EKS Cluster Setup](https://github.com/developmentseed/eoapi-k8s/blob/main/docs/aws-eks.md) +- :octicons-link-external-16: [TBD: GCP GKE Cluster Setup](https://github.com/developmentseed/eoapi-k8s/blob/main/docs/gcp-gke.md) -**Helm Installation** +### Helm Installation -Once you have a k8s cluster set up, you can `helm install` eoAPI as follows +Once you have a k8s cluster, you can `helm install` eoAPI as follows. 1. `helm install` from this repo's `helm-chart/` folder: - ```python - ###################################################### - # create os environment variables for required secrets - ###################################################### - $ export GITSHA=$(git rev-parse HEAD | cut -c1-10) - $ export PGUSER=s00pers3cr3t - $ export POSTGRES_USER=s00pers3cr3t - $ export POSTGRES_PASSWORD=superuserfoobar - $ export PGPASSWORD=foobar - - $ cd ./helm-chart - - $ helm install \ - --namespace eoapi \ - --create-namespace \ - --set gitSha=$GITSHA \ - --set db.settings.secrets.PGUSER=$PGUSER \ - --set db.settings.secrets.POSTGRES_USER=$POSTGRES_USER \ - --set db.settings.secrets.PGPASSWORD=$PGPASSWORD \ - --set db.settings.secrets.POSTGRES_PASSWORD=$POSTGRES_PASSWORD \ - eoapi \ - ./eoapi - ``` +```bash +###################################################### +# create os environment variables for required secrets +###################################################### +export GITSHA=$(git rev-parse HEAD | cut -c1-10) +export PGUSER=s00pers3cr3t +export POSTGRES_USER=s00pers3cr3t +export POSTGRES_PASSWORD=superuserfoobar +export PGPASSWORD=foobar + +cd ./helm-chart +helm install \ +--namespace eoapi \ +--create-namespace \ +--set gitSha=$GITSHA \ +--set db.settings.secrets.PGUSER=$PGUSER \ +--set db.settings.secrets.POSTGRES_USER=$POSTGRES_USER \ +--set db.settings.secrets.PGPASSWORD=$PGPASSWORD \ +--set db.settings.secrets.POSTGRES_PASSWORD=$POSTGRES_PASSWORD \ +eoapi \ +./eoapi +``` 2. or `helm install` from https://devseed.com/eoapi-k8s/: - ```python - # add the eoapi helm repo locally - $ helm repo add eoapi https://devseed.com/eoapi-k8s/ - - # List out the eoapi chart versions - $ helm search repo eoapi - NAME CHART VERSION APP VERSION DESCRIPTION - eoapi/eoapi 0.1.1 0.1.0 Create a full Earth Observation API with Metada... - eoapi/eoapi 0.1.2 0.1.0 Create a full Earth Observation API with Metada... - - # add the required secret overrides to an arbitrarily named `.yaml` file (`config.yaml` below) - $ cat config.yaml - db: - settings: - secrets: - PGUSER: "username" - POSTGRES_USER: "username" - PGPASSWORD: "password" - POSTGRES_PASSWORD: "password" - - # then run `helm install` with those overrides - helm install eoapi eoapi/eoapi --version 0.1.1 -f config.yaml - ``` \ No newline at end of file +```bash +# add the eoapi helm repo locally +helm repo add eoapi https://devseed.com/eoapi-k8s/ + +# List out the eoapi chart versions +helm search repo eoapi + +NAME CHART VERSION APP VERSION DESCRIPTION +eoapi/eoapi 0.1.1 0.1.0 Create a full Earth Observation API with Metada... +eoapi/eoapi 0.1.2 0.1.0 Create a full Earth Observation API with Metada... + +# add the required secret overrides to an arbitrarily named `.yaml` file (`config.yaml` below) +cat config.yaml +db: + settings: + secrets: + PGUSER: "username" + POSTGRES_USER: "username" + PGPASSWORD: "password" + POSTGRES_PASSWORD: "password" + +# then run `helm install` with those overrides +helm install eoapi eoapi/eoapi --version 0.1.1 -f config.yaml +``` diff --git a/docs/src/intro.md b/docs/src/intro.md index ffe4789..2c187c3 100644 --- a/docs/src/intro.md +++ b/docs/src/intro.md @@ -1,24 +1,20 @@ +
Create a full Earth Observation API with Metadata, Raster, and Vector services.
- --- -## **E**arth **O**bservation **API** - -`eoAPI` combines several *state-of-the-art* projects to create a full Earth Observation API. Each service can be used and deployed independently, but `eoAPI` creates the interconnections between each service: - -- **pgSTAC** database [https://github.com/stac-utils/pgstac](https://github.com/stac-utils/pgstac) - -- **STAC API** built on top of [https://github.com/stac-utils/stac-fastapi](https://github.com/stac-utils/stac-fastapi) - -- **STAC browser** a UI that exposes, in a user friendly interface, the metadata served by the STAC API. Built on top of [https://github.com/radiantearth/stac-browser](https://github.com/radiantearth/stac-browser) +## Earth Observation API -- **STAC Items And Mosaic Raster Tiles** API built on top of [https://github.com/stac-utils/titiler-pgstac](https://github.com/stac-utils/titiler-pgstac) +`eoAPI` combines several **state-of-the-art** projects to create a full Earth Observation API. Each service can be used and deployed independently, but `eoAPI` creates the interconnections between each service: -- **OGC Features and Vector Tiles** API built on top of [https://github.com/developmentseed/tipg](https://github.com/developmentseed/tipg) +- **pgSTAC** database https://github.com/stac-utils/pgstac +- **STAC API** built on top of https://github.com/stac-utils/stac-fastapi +- **STAC browser** a UI that exposes, in a user-friendly interface, the metadata served by the STAC API. Built on top of https://github.com/radiantearth/stac-browser +- **STAC Items And Mosaic Raster Tiles** API built on top of https://github.com/stac-utils/titiler-pgstac +- **OGC Features and Vector Tiles** API built on top of https://github.com/developmentseed/tipg --- @@ -30,58 +26,47 @@ ## Why should you use `eoAPI` -- **Focus on your use case:** `eoAPI` is used for large-scale data processing, building geographic information systems (GIS), creating real-time data applications, climate research and environmental monitoring, machine learning model training, and more. - -- **Unified Repository:** `eoAPI` provides a single, unified repository for several state-of-the-art Earth Observation (EO) data services, including Metadata search (STAC), Raster, and Vector services. This can simplify the process of accessing and working with these services. - -- **Interoperability:** `eoAPI` is designed to enable interoperability among its included services. This can make building complex applications that leverage different types of EO data easier. - -- **Open Source and Community Support:** As an open-source project, `eoAPI` allows developers to inspect its code, contribute to its development, and use it as a base for custom solutions. It also benefits from the support and innovation of a community of developers and EO data users. - -- **Scalability and Flexibility:** Each service in `eoAPI` can be used or deployed independently, which provides a lot of flexibility. If a developer's application only requires one or two of eoAPI's services, they don't need to deploy the entire suite. - -- **Facilitate Earth Observation Tasks:** `eoAPI` includes specialized tools for working with EO data, such as dynamic tiling, metadata searching, and features/vector tiles API. These can significantly facilitate EO data processing, analysis, and visualization. - -- **Ease of Deployment:** `eoAPI` supports containerized deployment using Docker, making it easier to set up, scale, and maintain applications built on it. Spin up the demo locally and start experimenting in minutes. +- **Focus on your use case**: `eoAPI` is used for large-scale data processing, building geographic information systems (GIS), creating real-time data applications, climate research and environmental monitoring, machine learning model training, and more. +- **Unified Repository**: `eoAPI` provides a single, unified repository for several state-of-the-art Earth Observation (EO) data services, including Metadata search (STAC), Raster, and Vector services. This can simplify accessing and working with these services. +- **Interoperability**: `eoAPI` is designed to enable interoperability among its included services. This can make building complex applications that leverage different types of EO data easier. +- **Open Source and Community Support**: As an open-source project, `eoAPI` allows developers to inspect its code, contribute to its development, and use it as a base for custom solutions. It also benefits from the support and innovation of a community of developers and EO data users. +- **Scalability and Flexibility**: Each service in `eoAPI` can be used or deployed independently, which provides a lot of flexibility. If a developer's application only requires one or two of eoAPI's services, they don't need to deploy the entire suite. +- **Facilitate Earth Observation Tasks**: `eoAPI` includes specialized tools for working with EO data, such as dynamic tiling, metadata searching, and features/vector tiles API. These can significantly facilitate EO data processing, analysis, and visualization. +- **Ease of Deployment**: `eoAPI` supports containerized deployment using Docker, making setting up, scaling, and maintaining applications built on it more accessible. Spin up the demo locally and start experimenting in minutes. --- ## Services Overview - **STAC Metadata**: Built with [stac-fastapi.pgstac](https://github.com/stac-utils/stac-fastapi) to enable data discovery. See the specifications [core](https://github.com/radiantearth/stac-api-spec/blob/v1.0.0/core/README.md), [search](https://github.com/radiantearth/stac-api-spec/blob/v1.0.0/item-search/README.md) and [features](https://github.com/radiantearth/stac-api-spec/blob/v1.0.0/ogcapi-features/README.md) for API details. - -- **STAC browser** : Built with the [Radiant Earth STAC browser](https://github.com/radiantearth/stac-browser) to provide a simple user-friendly interface for searching the STAC metadata. - -- **Raster Tiles**: Built with [titiler-pgstac](https://github.com/stac-utils/titiler-pgstac) and [pgstac](https://github.com/stac-utils/pgstac) to enable large scale mosaic based on results of STAC searches queries. See [docs](https://stac-utils.github.io/titiler-pgstac/0.8.0/mosaic_endpoints/) for API details. - +- **STAC browser**: Built with the [Radiant Earth STAC browser](https://github.com/radiantearth/stac-browser) to provide a simple, user-friendly interface for searching the STAC metadata. +- **Raster Tiles**: Built with [titiler-pgstac](https://github.com/stac-utils/titiler-pgstac) and [pgstac](https://github.com/stac-utils/pgstac) to enable large-scale mosaic based on results of STAC searches queries. See [docs](https://stac-utils.github.io/titiler-pgstac/0.8.0/mosaic_endpoints/) for API details. - **OGC Features & Vector Tiles**: Built with [tipg](https://github.com/developmentseed/tipg) to create a lightweight OGC Features and Tiles API with a PostGIS database. See [docs](https://developmentseed.org/tipg/user_guide/endpoints/) for API details. - See [service details](./services.md) for more information. --- ## Getting started -The easiest way to start exploring the different eoAPI services is with *Docker*. Clone this repository and start the multi-container *Docker* applications using `Compose`: +The easiest way to explore the different eoAPI services is with **Docker**. Clone this repository and start the multi-container **Docker** applications using `Compose`: -``` +```bash git clone https://github.com/developmentseed/eoAPI.git cd eoAPI docker compose up ``` -Once the applications are *up*, you'll need to add STAC **Collections** and **Items** to the PgSTAC database. If you don't have, you can use the follow the [MAXAR open data demo](https://github.com/vincentsarago/MAXAR_opendata_to_pgstac) (or get inspired by the other [demos](https://github.com/developmentseed/eoAPI/tree/main/demo)). - +Once the applications are **up**, you'll need to add STAC **Collections** and **Items** to the PgSTAC database. If you don't have a STAC collection, you can use the [MAXAR open data demo](https://github.com/vincentsarago/MAXAR_opendata_to_pgstac) (or get inspired by the other [demos](https://github.com/developmentseed/eoAPI/tree/main/demo)). Then you can start exploring your dataset with: - - the STAC Metadata service [http://localhost:8081](http://localhost:8081) - - the Raster service [http://localhost:8082](http://localhost:8082) +- the STAC Metadata service http://localhost:8081 +- the Raster service http://localhost:8082 !!! info - If you've added vector datasets to the `public` schema in the Postgres database, they will be available through the **Vector** service at [http://localhost:8083](http://localhost:8083). +If you've added vector datasets to the `public` schema in the Postgres database, they will be available through the **Vector** service at http://localhost:8083. Alternatively, you may install and launch applications locally: @@ -91,29 +76,28 @@ virtualenv .venv source .venv/bin/activate export DATABASE_URL=postgresql://username:password@0.0.0.0:5439/postgis # Connect to the database of your choice - python -m pip install uvicorn ############################################################################### # Install and launch the application # Select one of the following - ############################################################################### + # STAC python -m pip install "psycopg[binary,pool]" stac-fastapi-pgstac -.venv/bin/uvicorn stac_fastapi.pgstac.app:app --port 8081 --reload +python -m uvicorn stac_fastapi.pgstac.app:app --port 8081 --reload ############################################################################### # RASTER python -m pip install "psycopg[binary,pool]" titiler-pgstac -.venv/bin/uvicorn titiler.pgstac.main:app --port 8082 --reload +python -m uvicorn titiler.pgstac.main:app --port 8082 --reload ############################################################################### # VECTOR python -m pip install tipg -.venv/bin/uvicorn tipg.main:app --port 8083 --reload +python -m uvicorn tipg.main:app --port 8083 --reload ``` !!! danger - Python applications might have incompatible dependencies, which you can resolve by using a virtual environment *per application* +Python applications might have incompatible dependencies, which you can resolve by using a virtual environment *per application* diff --git a/infrastructure/aws/.env.example b/infrastructure/aws/.env.example deleted file mode 100644 index 5c27dbb..0000000 --- a/infrastructure/aws/.env.example +++ /dev/null @@ -1,19 +0,0 @@ -# STACK -CDK_EOAPI_NAME=eoAPI -CDK_EOAPI_FUNCTIONS='["stac","raster","vector"]' - -# DB -CDK_EOAPI_DB_PGSTAC_VERSION="0.8.1" -CDK_EOAPI_DB_PGSTAC_MOSAIC_INDEX=TRUE - -# STAC API -CDK_EOAPI_STAC_TIMEOUT=20 -CDK_EOAPI_STAC_MEMORY=1024 - -# Raster API -CDK_EOAPI_RASTER_TIMEOUT=20 -CDK_EOAPI_RASTER_MEMORY=3008 - -# Vector -CDK_EOAPI_VECTOR_TIMEOUT=20 -CDK_EOAPI_VECTOR_MEMORY=1024 diff --git a/infrastructure/aws/browser_config.example.js b/infrastructure/aws/browser_config.example.js deleted file mode 100644 index 6c8f9de..0000000 --- a/infrastructure/aws/browser_config.example.js +++ /dev/null @@ -1,39 +0,0 @@ -module.exports = { - catalogUrl: null, - catalogTitle: "eoAPI STAC Browser", - allowExternalAccess: true, // Must be true if catalogUrl is not given - allowedDomains: [], - detectLocaleFromBrowser: true, - storeLocale: true, - locale: "en", - fallbackLocale: "en", - supportedLocales: [ - "de", - "es", - "en", - "fr", - "it", - "ro" - ], - apiCatalogPriority: null, - useTileLayerAsFallback: true, - displayGeoTiffByDefault: false, - buildTileUrlTemplate: ({href, asset}) => "https://raster.dev/cog/tiles/{z}/{x}/{y}@2x?url=" + encodeURIComponent(asset.href.startsWith("/vsi") ? asset.href : href), - stacProxyUrl: null, - pathPrefix: "/", - historyMode: "history", - cardViewMode: "cards", - cardViewSort: "asc", - showThumbnailsAsAssets: false, - stacLint: true, - geoTiffResolution: 128, - redirectLegacyUrls: false, - itemsPerPage: 12, - defaultThumbnailSize: null, - maxPreviewsOnMap: 50, - crossOriginMedia: null, - requestHeaders: {}, - requestQueryParameters: {}, - preprocessSTAC: null, - authConfig: null -}; \ No newline at end of file diff --git a/infrastructure/aws/cdk.json b/infrastructure/aws/cdk.json deleted file mode 100644 index 57cc60c..0000000 --- a/infrastructure/aws/cdk.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "app": "python3 cdk/app.py" -} diff --git a/infrastructure/aws/cdk/__init__.py b/infrastructure/aws/cdk/__init__.py deleted file mode 100644 index 4955682..0000000 --- a/infrastructure/aws/cdk/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""AWS App.""" diff --git a/infrastructure/aws/cdk/app.py b/infrastructure/aws/cdk/app.py deleted file mode 100644 index cf14874..0000000 --- a/infrastructure/aws/cdk/app.py +++ /dev/null @@ -1,423 +0,0 @@ -""" -CDK Stack definition code for EOAPI -""" -import os -from typing import Any - -import boto3 -from aws_cdk import App, CfnOutput, Duration, RemovalPolicy, Stack, Tags -from aws_cdk import aws_ec2 as ec2 -from aws_cdk import aws_iam as iam -from aws_cdk import aws_lambda -from aws_cdk import aws_logs as logs -from aws_cdk import aws_rds as rds -from aws_cdk import aws_s3 as s3 -from config import ( - eoAPISettings, - eoDBSettings, - eoRasterSettings, - eoStacBrowserSettings, - eoSTACSettings, - eoVectorSettings, -) -from constructs import Construct -from eoapi_cdk import ( - PgStacApiLambda, - PgStacDatabase, - StacBrowser, - StacIngestor, - TiPgApiLambda, - TitilerPgstacApiLambda, -) - -eoapi_settings = eoAPISettings() - - -class eoAPIconstruct(Stack): - """Earth Observation API CDK application""" - - def __init__( # noqa: C901 - self, - scope: Construct, - id: str, - stage: str, - name: str, - context_dir: str = "../../", - **kwargs: Any, - ) -> None: - """Define stack.""" - super().__init__(scope, id, **kwargs) - - vpc = ec2.Vpc( - self, - f"{id}-vpc", - subnet_configuration=[ - ec2.SubnetConfiguration( - name="ingress", - cidr_mask=24, - subnet_type=ec2.SubnetType.PUBLIC, - ) - ], - ) - - interface_endpoints = [ - ( - "SecretsManager Endpoint", - ec2.InterfaceVpcEndpointAwsService.SECRETS_MANAGER, - ), - ( - "CloudWatch Logs Endpoint", - ec2.InterfaceVpcEndpointAwsService.CLOUDWATCH_LOGS, - ), - ] - for (key, service) in interface_endpoints: - vpc.add_interface_endpoint(key, service=service) - - gateway_endpoints = [("S3", ec2.GatewayVpcEndpointAwsService.S3)] - for (key, service) in gateway_endpoints: - vpc.add_gateway_endpoint(key, service=service) - - eodb_settings = eoDBSettings() - - pgstac_db = PgStacDatabase( - self, - "pgstac-db", - vpc=vpc, - engine=rds.DatabaseInstanceEngine.postgres( - version=rds.PostgresEngineVersion.VER_14 - ), - vpc_subnets=ec2.SubnetSelection(subnet_type=ec2.SubnetType.PUBLIC), - allocated_storage=eodb_settings.allocated_storage, - instance_type=ec2.InstanceType.of( - ec2.InstanceClass.BURSTABLE3, - ec2.InstanceSize(eodb_settings.instance_size), - ), - database_name="postgres", - backup_retention=Duration.days(7), - deletion_protection=eoapi_settings.stage.lower() == "production", - removal_policy=RemovalPolicy.SNAPSHOT - if eoapi_settings.stage.lower() == "production" - else RemovalPolicy.DESTROY, - custom_resource_properties={ - "pgstac_version": eodb_settings.pgstac_version, - "context": eodb_settings.context, - "mosaic_index": eodb_settings.mosaic_index, - }, - bootstrapper_lambda_function_options={ - "handler": "handler.handler", - "runtime": aws_lambda.Runtime.PYTHON_3_10, - "code": aws_lambda.Code.from_docker_build( - path=os.path.abspath(context_dir), - file="infrastructure/aws/dockerfiles/Dockerfile.db", - build_args={ - "PYTHON_VERSION": "3.10", - "PGSTAC_VERSION": eodb_settings.pgstac_version, - }, - platform="linux/amd64", - ), - "timeout": Duration.minutes(5), - "allow_public_subnet": True, - "log_retention": logs.RetentionDays.ONE_WEEK, - }, - pgstac_db_name=eodb_settings.dbname, - pgstac_username=eodb_settings.user, - secrets_prefix=os.path.join(stage, name), - ) - - # allow connections from anywhere to the DB - pgstac_db.db.connections.allow_default_port_from_any_ipv4() - - CfnOutput( - self, - f"{id}-database-secret-arn", - value=pgstac_db.pgstac_secret.secret_arn, - description="Arn of the SecretsManager instance holding the connection info for Postgres DB", - ) - - # eoapi.raster - if "raster" in eoapi_settings.functions: - - db_secrets = { - "POSTGRES_HOST": pgstac_db.pgstac_secret.secret_value_from_json( - "host" - ).to_string(), - "POSTGRES_DBNAME": pgstac_db.pgstac_secret.secret_value_from_json( - "dbname" - ).to_string(), - "POSTGRES_USER": pgstac_db.pgstac_secret.secret_value_from_json( - "username" - ).to_string(), - "POSTGRES_PASS": pgstac_db.pgstac_secret.secret_value_from_json( - "password" - ).to_string(), - "POSTGRES_PORT": pgstac_db.pgstac_secret.secret_value_from_json( - "port" - ).to_string(), - } - - eoraster_settings = eoRasterSettings() - env = eoraster_settings.env or {} - if "DB_MAX_CONN_SIZE" not in env: - env["DB_MAX_CONN_SIZE"] = "1" - env.update(db_secrets) - - eoraster = TitilerPgstacApiLambda( - self, - f"{id}-raster-lambda", - db=pgstac_db.db, - db_secret=pgstac_db.pgstac_secret, - api_env=env, - lambda_function_options={ - "code": aws_lambda.Code.from_docker_build( - path=os.path.abspath(context_dir), - file="infrastructure/aws/dockerfiles/Dockerfile.raster", - build_args={ - "PYTHON_VERSION": "3.11", - }, - platform="linux/amd64", - ), - "allow_public_subnet": True, - "handler": "handler.handler", - "runtime": aws_lambda.Runtime.PYTHON_3_11, - "memory_size": eoraster_settings.memory, - "timeout": Duration.seconds(eoraster_settings.timeout), - "log_retention": logs.RetentionDays.ONE_WEEK, - }, - buckets=eoraster_settings.buckets, - ) - - # eoapi.stac - if "stac" in eoapi_settings.functions: - db_secrets = { - "POSTGRES_HOST_READER": pgstac_db.pgstac_secret.secret_value_from_json( - "host" - ).to_string(), - "POSTGRES_HOST_WRITER": pgstac_db.pgstac_secret.secret_value_from_json( - "host" - ).to_string(), - "POSTGRES_DBNAME": pgstac_db.pgstac_secret.secret_value_from_json( - "dbname" - ).to_string(), - "POSTGRES_USER": pgstac_db.pgstac_secret.secret_value_from_json( - "username" - ).to_string(), - "POSTGRES_PASS": pgstac_db.pgstac_secret.secret_value_from_json( - "password" - ).to_string(), - "POSTGRES_PORT": pgstac_db.pgstac_secret.secret_value_from_json( - "port" - ).to_string(), - } - - eostac_settings = eoSTACSettings() - env = eostac_settings.env or {} - if "DB_MAX_CONN_SIZE" not in env: - env["DB_MAX_CONN_SIZE"] = "1" - if "DB_MIN_CONN_SIZE" not in env: - env["DB_MIN_CONN_SIZE"] = "1" - env.update(db_secrets) - # If raster is deployed we had the TITILER_ENDPOINT env to add the Proxy extension - if "raster" in eoapi_settings.functions: - env["TITILER_ENDPOINT"] = eoraster.url.strip("/") - - eostac = PgStacApiLambda( - self, - id=f"{id}-stac-lambda", - db=pgstac_db.db, - db_secret=pgstac_db.pgstac_secret, - api_env=env, - lambda_function_options={ - "runtime": aws_lambda.Runtime.PYTHON_3_11, - "code": aws_lambda.Code.from_docker_build( - path=os.path.abspath(context_dir), - file="infrastructure/aws/dockerfiles/Dockerfile.stac", - build_args={ - "PYTHON_VERSION": "3.11", - }, - platform="linux/amd64", - ), - "handler": "handler.handler", - "memory_size": eostac_settings.memory, - "timeout": Duration.seconds(eostac_settings.timeout), - "log_retention": logs.RetentionDays.ONE_WEEK, - }, - ) - - if "browser" in eoapi_settings.functions: - eobrowser_settings = eoStacBrowserSettings() - - stac_browser_bucket = s3.Bucket( - self, - "stac-browser-bucket", - bucket_name=f"{id.lower()}-stac-browser", - removal_policy=RemovalPolicy.DESTROY, - auto_delete_objects=True, - website_index_document="index.html", - public_read_access=True, - block_public_access=s3.BlockPublicAccess( - block_public_acls=False, - block_public_policy=False, - ignore_public_acls=False, - restrict_public_buckets=False, - ), - object_ownership=s3.ObjectOwnership.OBJECT_WRITER, - ) - - # need to build this manually, the attribute eostac.url is not resolved yet. - - StacBrowser( - self, - "stac-browser", - github_repo_tag=eobrowser_settings.stac_browser_github_tag, - stac_catalog_url=eobrowser_settings.stac_catalog_url, - website_index_document="index.html", - bucket_arn=stac_browser_bucket.bucket_arn, - config_file_path=eobrowser_settings.config_file_path, - ) - - # eoapi.vector - if "vector" in eoapi_settings.functions: - db_secrets = { - "POSTGRES_HOST": pgstac_db.pgstac_secret.secret_value_from_json( - "host" - ).to_string(), - "POSTGRES_DBNAME": pgstac_db.pgstac_secret.secret_value_from_json( - "dbname" - ).to_string(), - "POSTGRES_USER": pgstac_db.pgstac_secret.secret_value_from_json( - "username" - ).to_string(), - "POSTGRES_PASS": pgstac_db.pgstac_secret.secret_value_from_json( - "password" - ).to_string(), - "POSTGRES_PORT": pgstac_db.pgstac_secret.secret_value_from_json( - "port" - ).to_string(), - } - - eovector_settings = eoVectorSettings() - env = eovector_settings.env or {} - - if "DB_MAX_CONN_SIZE" not in env: - env["DB_MAX_CONN_SIZE"] = "1" - if "DB_MIN_CONN_SIZE" not in env: - env["DB_MIN_CONN_SIZE"] = "1" - - env.update(db_secrets) - - TiPgApiLambda( - self, - f"{id}-vector-lambda", - db=pgstac_db.db, - db_secret=pgstac_db.pgstac_secret, - api_env=env, - lambda_function_options={ - "runtime": aws_lambda.Runtime.PYTHON_3_11, - "code": aws_lambda.Code.from_docker_build( - path=os.path.abspath(context_dir), - file="infrastructure/aws/dockerfiles/Dockerfile.vector", - build_args={ - "PYTHON_VERSION": "3.11", - }, - platform="linux/amd64", - ), - "handler": "handler.handler", - "memory_size": eovector_settings.memory, - "timeout": Duration.seconds(eovector_settings.timeout), - "log_retention": logs.RetentionDays.ONE_WEEK, - }, - ) - - if "ingestor" in eoapi_settings.functions: - - data_access_role = self._create_data_access_role() - - stac_ingestor = StacIngestor( - self, - "stac-ingestor", - stac_url=eostac.url, - stage=eoapi_settings.stage, - data_access_role=data_access_role, - stac_db_secret=pgstac_db.pgstac_secret, - stac_db_security_group=pgstac_db.db.connections.security_groups[0], - api_env={"REQUESTER_PAYS": "True"}, - ) - - data_access_role = self._grant_assume_role_with_principal_pattern( - data_access_role, stac_ingestor.handler_role.role_name - ) - - def _create_data_access_role(self) -> iam.Role: - - """ - Creates an IAM role with full S3 read access. - """ - - data_access_role = iam.Role( - self, - "data-access-role", - assumed_by=iam.ServicePrincipal("lambda.amazonaws.com"), - ) - - data_access_role.add_managed_policy( - iam.ManagedPolicy.from_aws_managed_policy_name("AmazonS3FullAccess") - ) - - return data_access_role - - def _grant_assume_role_with_principal_pattern( - self, - role_to_assume: iam.Role, - principal_pattern: str, - account_id: str = boto3.client("sts").get_caller_identity().get("Account"), - ) -> iam.Role: - """ - Grants assume role permissions to the role of the given - account with the given name pattern. Default account - is the current account. - """ - - role_to_assume.assume_role_policy.add_statements( - iam.PolicyStatement( - effect=iam.Effect.ALLOW, - principals=[iam.AnyPrincipal()], - actions=["sts:AssumeRole"], - conditions={ - "StringLike": { - "aws:PrincipalArn": [ - f"arn:aws:iam::{account_id}:role/{principal_pattern}" - ] - } - }, - ) - ) - - return role_to_assume - - -app = App() - - -eoapi_stack = eoAPIconstruct( - app, - f"{eoapi_settings.name}-{eoapi_settings.stage}", - eoapi_settings.name, - eoapi_settings.stage, - env={ - "account": os.environ["CDK_DEFAULT_ACCOUNT"], - "region": os.environ["CDK_DEFAULT_REGION"], - }, -) - -# Tag infrastructure -for key, value in { - "Project": eoapi_settings.name, - "Stack": eoapi_settings.stage, - "Owner": eoapi_settings.owner, - "Client": eoapi_settings.client, -}.items(): - if value: - Tags.of(eoapi_stack).add(key, value) - - -app.synth() diff --git a/infrastructure/aws/cdk/config.py b/infrastructure/aws/cdk/config.py deleted file mode 100644 index 463a689..0000000 --- a/infrastructure/aws/cdk/config.py +++ /dev/null @@ -1,127 +0,0 @@ -"""eoAPI Configs.""" - -from enum import Enum -from typing import Dict, List, Optional - -from pydantic_settings import BaseSettings - - -class functionName(str, Enum): - """Function names.""" - - stac = "stac" - raster = "raster" - vector = "vector" - ingestor = "ingestor" - browser = "browser" # not actually a function, but this keeps things clean. - - -class eoAPISettings(BaseSettings): - """Application settings""" - - name: str = "eoapi" - stage: str = "production" - owner: Optional[str] = None - client: Optional[str] = None - functions: List[functionName] = [functionName.stac, functionName.raster] - - model_config = { - "env_prefix": "CDK_EOAPI_", - "env_file": ".env", - "extra": "ignore", - "use_enum_values": True, - } - - -class eoDBSettings(BaseSettings): - """Application settings""" - - dbname: str = "eoapi" - user: str = "eouser" - - # Define PGSTAC VERSION - pgstac_version: str - instance_size: str = "SMALL" - context: bool = True - mosaic_index: bool = True - allocated_storage: int = 20 - model_config = { - "env_prefix": "CDK_EOAPI_DB_", - "env_file": ".env", - } - - -class eoSTACSettings(BaseSettings): - """Application settings""" - - env: Dict = {} - - timeout: int = 10 - memory: int = 256 - model_config = { - "env_prefix": "CDK_EOAPI_STAC_", - "env_file": ".env", - } - - -class eoRasterSettings(BaseSettings): - """Application settings""" - - # Default options are optimized for CloudOptimized GeoTIFF - # For more information on GDAL env see: https://gdal.org/user/configoptions.html - # or https://developmentseed.org/titiler/advanced/performance_tuning/ - env: Dict = { - "CPL_VSIL_CURL_ALLOWED_EXTENSIONS": ".tif,.TIF,.tiff", - "GDAL_CACHEMAX": "200", # 200 mb - "GDAL_DISABLE_READDIR_ON_OPEN": "EMPTY_DIR", - "GDAL_INGESTED_BYTES_AT_OPEN": "32768", - "GDAL_HTTP_MERGE_CONSECUTIVE_RANGES": "YES", - "GDAL_HTTP_MULTIPLEX": "YES", - "GDAL_HTTP_VERSION": "2", - "PYTHONWARNINGS": "ignore", - "VSI_CACHE": "TRUE", - "VSI_CACHE_SIZE": "5000000", # 5 MB (per file-handle) - "DB_MIN_CONN_SIZE": "1", - "DB_MAX_CONN_SIZE": "1", - } - - # S3 bucket names where TiTiler could do HEAD and GET Requests - # specific private and public buckets MUST be added if you want to use s3:// urls - # You can whitelist all bucket by setting `*`. - # ref: https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-arn-format.html - buckets: List = ["*"] - - timeout: int = 10 - memory: int = 3008 - - model_config = { - "env_prefix": "CDK_EOAPI_RASTER_", - "env_file": ".env", - } - - -class eoVectorSettings(BaseSettings): - """Application settings""" - - env: Dict = {} - - timeout: int = 10 - memory: int = 512 - - model_config = { - "env_prefix": "CDK_EOAPI_VECTOR_", - "env_file": ".env", - } - - -class eoStacBrowserSettings(BaseSettings): - """STAC browser settings""" - - stac_browser_github_tag: str = "v3.1.0" - stac_catalog_url: str - config_file_path: str = "browser_config.js" - - model_config = { - "env_prefix": "CDK_EOAPI_BROWSER_", - "env_file": ".env", - } diff --git a/infrastructure/aws/dockerfiles/Dockerfile.db b/infrastructure/aws/dockerfiles/Dockerfile.db deleted file mode 100644 index 333fd3c..0000000 --- a/infrastructure/aws/dockerfiles/Dockerfile.db +++ /dev/null @@ -1,21 +0,0 @@ -ARG PYTHON_VERSION=3.10 - -FROM public.ecr.aws/lambda/python:${PYTHON_VERSION} - -WORKDIR /tmp - -ARG PGSTAC_VERSION - -WORKDIR /tmp -RUN python -m pip install pip -U - -RUN echo "Using PGSTAC Version ${PGSTAC_VERSION}" -RUN python -m pip install httpx psycopg["binary,pool"] pypgstac==${PGSTAC_VERSION} -t /asset - -COPY infrastructure/aws/handlers/db_handler.py /asset/handler.py - -# https://stackoverflow.com/a/61746719 -# Turns out, asyncio is part of python -RUN rm -rf /asset/asyncio* - -CMD ["echo", "hello world"] diff --git a/infrastructure/aws/dockerfiles/Dockerfile.raster b/infrastructure/aws/dockerfiles/Dockerfile.raster deleted file mode 100644 index 3259f54..0000000 --- a/infrastructure/aws/dockerfiles/Dockerfile.raster +++ /dev/null @@ -1,21 +0,0 @@ -ARG PYTHON_VERSION=3.10 - -FROM public.ecr.aws/lambda/python:${PYTHON_VERSION} - -WORKDIR /tmp -RUN python -m pip install pip -U - -COPY runtime/eoapi/raster /tmp/raster -RUN python -m pip install "mangum>=0.14,<0.15" /tmp/raster["psycopg-binary"] -t /asset --no-binary pydantic -RUN rm -rf /tmp/raster - -# Reduce package size and remove useless files -RUN cd /asset && find . -type f -name '*.pyc' | while read f; do n=$(echo $f | sed 's/__pycache__\///' | sed 's/.cpython-[0-9]*//'); cp $f $n; done; -RUN cd /asset && find . -type d -a -name '__pycache__' -print0 | xargs -0 rm -rf -RUN cd /asset && find . -type f -a -name '*.py' -print0 | xargs -0 rm -f -RUN find /asset -type d -a -name 'tests' -print0 | xargs -0 rm -rf -RUN rm -rdf /asset/numpy/doc/ /asset/boto3* /asset/botocore* /asset/bin /asset/geos_license /asset/Misc - -COPY infrastructure/aws/handlers/raster_handler.py /asset/handler.py - -CMD ["echo", "hello world"] diff --git a/infrastructure/aws/dockerfiles/Dockerfile.stac b/infrastructure/aws/dockerfiles/Dockerfile.stac deleted file mode 100644 index a9675e7..0000000 --- a/infrastructure/aws/dockerfiles/Dockerfile.stac +++ /dev/null @@ -1,20 +0,0 @@ -ARG PYTHON_VERSION=3.10 - -FROM public.ecr.aws/lambda/python:${PYTHON_VERSION} - -WORKDIR /tmp -RUN python -m pip install pip -U - -COPY runtime/eoapi/stac /tmp/stac -RUN python -m pip install "mangum>=0.14,<0.15" /tmp/stac -t /asset --no-binary pydantic -RUN rm -rf /tmp/stac - -# Reduce package size and remove useless files -RUN cd /asset && find . -type f -name '*.pyc' | while read f; do n=$(echo $f | sed 's/__pycache__\///' | sed 's/.cpython-[0-9]*//'); cp $f $n; done; -RUN cd /asset && find . -type d -a -name '__pycache__' -print0 | xargs -0 rm -rf -RUN cd /asset && find . -type f -a -name '*.py' -print0 | xargs -0 rm -f -RUN find /asset -type d -a -name 'tests' -print0 | xargs -0 rm -rf - -COPY infrastructure/aws/handlers/stac_handler.py /asset/handler.py - -CMD ["echo", "hello world"] diff --git a/infrastructure/aws/dockerfiles/Dockerfile.vector b/infrastructure/aws/dockerfiles/Dockerfile.vector deleted file mode 100644 index eb211f9..0000000 --- a/infrastructure/aws/dockerfiles/Dockerfile.vector +++ /dev/null @@ -1,22 +0,0 @@ -ARG PYTHON_VERSION=3.10 - -FROM public.ecr.aws/lambda/python:${PYTHON_VERSION} - -RUN yum install -y git - -WORKDIR /tmp -RUN python -m pip install pip -U - -COPY runtime/eoapi/vector /tmp/vector -RUN python -m pip install "mangum>=0.14,<0.15" /tmp/vector -t /asset --no-binary pydantic -RUN rm -rf /tmp/vector - -# Reduce package size and remove useless files -RUN cd /asset && find . -type f -name '*.pyc' | while read f; do n=$(echo $f | sed 's/__pycache__\///' | sed 's/.cpython-[0-9]*//'); cp $f $n; done; -RUN cd /asset && find . -type d -a -name '__pycache__' -print0 | xargs -0 rm -rf -RUN cd /asset && find . -type f -a -name '*.py' -print0 | xargs -0 rm -f -RUN find /asset -type d -a -name 'tests' -print0 | xargs -0 rm -rf - -COPY infrastructure/aws/handlers/vector_handler.py /asset/handler.py - -CMD ["echo", "hello world"] diff --git a/infrastructure/aws/handlers/db_handler.py b/infrastructure/aws/handlers/db_handler.py deleted file mode 100644 index 4ec0847..0000000 --- a/infrastructure/aws/handlers/db_handler.py +++ /dev/null @@ -1,275 +0,0 @@ -"""Bootstrap Postgres db.""" - -import json -import logging - -import boto3 -import httpx -import psycopg -from psycopg import sql -from psycopg.conninfo import make_conninfo -from pypgstac.db import PgstacDB -from pypgstac.migrate import Migrate - -logger = logging.getLogger("eoapi-bootstrap") - - -def send( - event, - context, - responseStatus, - responseData, - physicalResourceId=None, - noEcho=False, -): - """ - Copyright 2016 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. - This file is licensed to you under the AWS Customer Agreement (the "License"). - You may not use this file except in compliance with the License. - A copy of the License is located at http://aws.amazon.com/agreement/ . - This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. - See the License for the specific language governing permissions and limitations under the License. - - Send response from AWS Lambda. - - Note: The cfnresponse module is available only when you use the ZipFile property to write your source code. - It isn't available for source code that's stored in Amazon S3 buckets. - For code in buckets, you must write your own functions to send responses. - """ - responseUrl = event["ResponseURL"] - - print(responseUrl) - - responseBody = {} - responseBody["Status"] = responseStatus - responseBody["Reason"] = ( - "See the details in CloudWatch Log Stream: " + context.log_stream_name - ) - responseBody["PhysicalResourceId"] = physicalResourceId or context.log_stream_name - responseBody["StackId"] = event["StackId"] - responseBody["RequestId"] = event["RequestId"] - responseBody["LogicalResourceId"] = event["LogicalResourceId"] - responseBody["NoEcho"] = noEcho - responseBody["Data"] = responseData - - json_responseBody = json.dumps(responseBody) - - print("Response body:\n" + json_responseBody) - - headers = {"content-type": "", "content-length": str(len(json_responseBody))} - - try: - response = httpx.put(responseUrl, data=json_responseBody, headers=headers) - print(f"Status code: {response.status_code}") - logger.debug(f"OK - Status code: {response.status_code}") - except Exception as e: - print("send(..) failed executing httpx.put(..): " + str(e)) - logger.debug(f"NOK - failed executing PUT requests: {e}") - - -def get_secret(secret_name): - """Get Secrets from secret manager.""" - print(f"Fetching {secret_name}") - client = boto3.client(service_name="secretsmanager") - response = client.get_secret_value(SecretId=secret_name) - return json.loads(response["SecretString"]) - - -def create_db(cursor, db_name: str) -> None: - """Create DB.""" - cursor.execute( - sql.SQL("SELECT 1 FROM pg_catalog.pg_database " "WHERE datname = %s"), [db_name] - ) - if cursor.fetchone(): - print(f"database {db_name} exists, not creating DB") - else: - print(f"database {db_name} not found, creating...") - cursor.execute( - sql.SQL("CREATE DATABASE {db_name}").format(db_name=sql.Identifier(db_name)) - ) - - -def create_user(cursor, username: str, password: str) -> None: - """Create User.""" - cursor.execute( - sql.SQL( - "DO $$ " - "BEGIN " - " IF NOT EXISTS ( " - " SELECT 1 FROM pg_roles " - " WHERE rolname = {user}) " - " THEN " - " CREATE USER {username} " - " WITH PASSWORD {password}; " - " ELSE " - " ALTER USER {username} " - " WITH PASSWORD {password}; " - " END IF; " - "END " - "$$; " - ).format(username=sql.Identifier(username), password=password, user=username) - ) - - -def create_permissions(cursor, db_name: str, username: str) -> None: - """Add permissions.""" - cursor.execute( - sql.SQL( - "GRANT CONNECT ON DATABASE {db_name} TO {username};" - "GRANT CREATE ON DATABASE {db_name} TO {username};" # Allow schema creation - "GRANT USAGE ON SCHEMA public TO {username};" - "ALTER DEFAULT PRIVILEGES IN SCHEMA public " - "GRANT ALL PRIVILEGES ON TABLES TO {username};" - "ALTER DEFAULT PRIVILEGES IN SCHEMA public " - "GRANT ALL PRIVILEGES ON SEQUENCES TO {username};" - "GRANT pgstac_read TO {username};" - "GRANT pgstac_ingest TO {username};" - "GRANT pgstac_admin TO {username};" - ).format( - db_name=sql.Identifier(db_name), - username=sql.Identifier(username), - ) - ) - - -def register_extensions(cursor) -> None: - """Add PostGIS extension.""" - cursor.execute(sql.SQL("CREATE EXTENSION IF NOT EXISTS postgis;")) - - -############################################################################### -# PgSTAC Customization -############################################################################### -def customization(cursor, params) -> None: - """ - CUSTOMIZED YOUR PGSTAC DATABASE - - ref: https://github.com/stac-utils/pgstac/blob/main/docs/src/pgstac.md - - """ - if params.get("context", False): - # Add CONTEXT=ON - pgstac_settings = """ - INSERT INTO pgstac_settings (name, value) - VALUES ('context', 'on') - ON CONFLICT ON CONSTRAINT pgstac_settings_pkey DO UPDATE SET value = excluded.value;""" - cursor.execute(sql.SQL(pgstac_settings)) - - if params.get("mosaic_index", False): - # Create index of searches with `mosaic`` type - cursor.execute( - sql.SQL( - "CREATE INDEX IF NOT EXISTS searches_mosaic ON searches ((true)) WHERE metadata->>'type'='mosaic';" - ) - ) - - -def handler(event, context): - """Lambda Handler.""" - print(f"Handling {event}") - - if event["RequestType"] not in ["Create", "Update"]: - return send(event, context, "SUCCESS", {"msg": "No action to be taken"}) - - try: - params = event["ResourceProperties"] - connection_params = get_secret(params["conn_secret_arn"]) - user_params = get_secret(params["new_user_secret_arn"]) - - print("Connecting to admin DB...") - admin_db_conninfo = make_conninfo( - dbname=connection_params.get("dbname", "postgres"), - user=connection_params["username"], - password=connection_params["password"], - host=connection_params["host"], - port=connection_params["port"], - ) - with psycopg.connect(admin_db_conninfo, autocommit=True) as conn: - with conn.cursor() as cur: - print("Creating database...") - create_db( - cursor=cur, - db_name=user_params["dbname"], - ) - - print("Creating user...") - create_user( - cursor=cur, - username=user_params["username"], - password=user_params["password"], - ) - - # Install extensions on the user DB with - # superuser permissions, since they will - # otherwise fail to install when run as - # the non-superuser within the pgstac - # migrations. - print("Connecting to STAC DB...") - stac_db_conninfo = make_conninfo( - dbname=user_params["dbname"], - user=connection_params["username"], - password=connection_params["password"], - host=connection_params["host"], - port=connection_params["port"], - ) - with psycopg.connect(stac_db_conninfo, autocommit=True) as conn: - with conn.cursor() as cur: - print("Registering PostGIS ...") - register_extensions(cursor=cur) - - stac_db_admin_dsn = ( - "postgresql://{user}:{password}@{host}:{port}/{dbname}".format( - dbname=user_params.get("dbname", "postgres"), - user=connection_params["username"], - password=connection_params["password"], - host=connection_params["host"], - port=connection_params["port"], - ) - ) - - with PgstacDB(dsn=stac_db_admin_dsn, debug=True) as pgdb: - print(f"Current {pgdb.version}") - - # As admin, run migrations - print("Running migrations...") - Migrate(pgdb).run_migration(params["pgstac_version"]) - - # Assign appropriate permissions to user (requires pgSTAC migrations to have run) - with psycopg.connect(admin_db_conninfo, autocommit=True) as conn: - with conn.cursor() as cur: - print("Setting permissions...") - create_permissions( - cursor=cur, - db_name=user_params["dbname"], - username=user_params["username"], - ) - - print("Customize PgSTAC database...") - with psycopg.connect( - stac_db_admin_dsn, - autocommit=True, - options="-c search_path=pgstac,public -c application_name=pgstac", - ) as conn: - with conn.cursor() as cur: - customization(cursor=cur, params=params) - - # Make sure the user can access the database - stac_db_user_dsn = ( - "postgresql://{user}:{password}@{host}:{port}/{dbname}".format( - dbname=user_params.get("dbname", "postgres"), - user=user_params["username"], - password=user_params["password"], - host=connection_params["host"], - port=connection_params["port"], - ) - ) - with PgstacDB(dsn=stac_db_user_dsn, debug=True) as pgdb: - print(f"User can access pgstac: {pgdb.version}") - - except Exception as e: - print(f"Unable to bootstrap database with exception={e}") - send(event, context, "FAILED", {"message": str(e)}) - raise e - - print("Complete.") - return send(event, context, "SUCCESS", {}) diff --git a/infrastructure/aws/handlers/raster_handler.py b/infrastructure/aws/handlers/raster_handler.py deleted file mode 100644 index 36da4f7..0000000 --- a/infrastructure/aws/handlers/raster_handler.py +++ /dev/null @@ -1,25 +0,0 @@ -"""AWS Lambda handler.""" - -import asyncio -import logging -import os - -from eoapi.raster.app import app -from mangum import Mangum -from titiler.pgstac.db import connect_to_db - -logging.getLogger("mangum.lifespan").setLevel(logging.ERROR) -logging.getLogger("mangum.http").setLevel(logging.ERROR) - - -@app.on_event("startup") -async def startup_event() -> None: - """Connect to database on startup.""" - await connect_to_db(app) - - -handler = Mangum(app, lifespan="off") - -if "AWS_EXECUTION_ENV" in os.environ: - loop = asyncio.get_event_loop() - loop.run_until_complete(app.router.startup()) diff --git a/infrastructure/aws/handlers/stac_handler.py b/infrastructure/aws/handlers/stac_handler.py deleted file mode 100644 index 1bc4a66..0000000 --- a/infrastructure/aws/handlers/stac_handler.py +++ /dev/null @@ -1,25 +0,0 @@ -"""AWS Lambda handler.""" - -import asyncio -import logging -import os - -from eoapi.stac.app import app -from mangum import Mangum -from stac_fastapi.pgstac.db import connect_to_db - -logging.getLogger("mangum.lifespan").setLevel(logging.ERROR) -logging.getLogger("mangum.http").setLevel(logging.ERROR) - - -@app.on_event("startup") -async def startup_event() -> None: - """Connect to database on startup.""" - await connect_to_db(app) - - -handler = Mangum(app, lifespan="off") - -if "AWS_EXECUTION_ENV" in os.environ: - loop = asyncio.get_event_loop() - loop.run_until_complete(app.router.startup()) diff --git a/infrastructure/aws/handlers/vector_handler.py b/infrastructure/aws/handlers/vector_handler.py deleted file mode 100644 index c7ea2ea..0000000 --- a/infrastructure/aws/handlers/vector_handler.py +++ /dev/null @@ -1,54 +0,0 @@ -"""AWS Lambda handler.""" - -import asyncio -import logging -import os - -from eoapi.vector.app import app -from mangum import Mangum -from tipg.collections import register_collection_catalog -from tipg.database import connect_to_db -from tipg.settings import PostgresSettings - -logging.getLogger("mangum.lifespan").setLevel(logging.ERROR) -logging.getLogger("mangum.http").setLevel(logging.ERROR) - -postgres_settings = PostgresSettings() - -try: - from importlib.resources import files as resources_files # type: ignore -except ImportError: - # Try backported to PY<39 `importlib_resources`. - from importlib_resources import files as resources_files # type: ignore - - -CUSTOM_SQL_DIRECTORY = resources_files("eoapi.vector") / "sql" -sql_files = list(CUSTOM_SQL_DIRECTORY.glob("*.sql")) # type: ignore - - -@app.on_event("startup") -async def startup_event() -> None: - """Connect to database on startup.""" - await connect_to_db( - app, - settings=postgres_settings, - # We enable both pgstac and public schemas (pgstac will be used by custom functions) - schemas=["pgstac", "public"], - user_sql_files=sql_files, - ) - await register_collection_catalog( - app, - # For the Tables' Catalog we only use the `public` schema - schemas=["public"], - # We exclude public functions - exclude_function_schemas=["public"], - # We allow non-spatial tables - spatial=False, - ) - - -handler = Mangum(app, lifespan="off") - -if "AWS_EXECUTION_ENV" in os.environ: - loop = asyncio.get_event_loop() - loop.run_until_complete(app.router.startup()) diff --git a/infrastructure/aws/package-lock.json b/infrastructure/aws/package-lock.json deleted file mode 100644 index f1aa3a8..0000000 --- a/infrastructure/aws/package-lock.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "name": "cdk-deploy", - "version": "0.1.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "cdk-deploy", - "version": "0.1.0", - "license": "MIT", - "dependencies": { - "cdk": "2.94.0" - } - }, - "node_modules/aws-cdk": { - "version": "2.94.0", - "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.94.0.tgz", - "integrity": "sha512-9bJkzxFDYZDwPDfZi/DSUODn4HFRzuXWPhpFgIIgRykfT18P+iAIJ1AEhaaCmlqrrog5yQgN+2iYd9BwDsiBeg==", - "bin": { - "cdk": "bin/cdk" - }, - "engines": { - "node": ">= 14.15.0" - }, - "optionalDependencies": { - "fsevents": "2.3.2" - } - }, - "node_modules/cdk": { - "version": "2.94.0", - "resolved": "https://registry.npmjs.org/cdk/-/cdk-2.94.0.tgz", - "integrity": "sha512-dMgSTaMtfpPxY2biMSHlNrwrJcq0iJkihvkVuSSQyhlHyLmH0s9fSBzO8zrGFAoEp/cDofg6iDfGzmwrHQ55LA==", - "dependencies": { - "aws-cdk": "2.94.0" - }, - "bin": { - "cdk": "bin/cdk" - }, - "engines": { - "node": ">= 14.15.0" - } - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - } - }, - "dependencies": { - "aws-cdk": { - "version": "2.94.0", - "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.94.0.tgz", - "integrity": "sha512-9bJkzxFDYZDwPDfZi/DSUODn4HFRzuXWPhpFgIIgRykfT18P+iAIJ1AEhaaCmlqrrog5yQgN+2iYd9BwDsiBeg==", - "requires": { - "fsevents": "2.3.2" - } - }, - "cdk": { - "version": "2.94.0", - "resolved": "https://registry.npmjs.org/cdk/-/cdk-2.94.0.tgz", - "integrity": "sha512-dMgSTaMtfpPxY2biMSHlNrwrJcq0iJkihvkVuSSQyhlHyLmH0s9fSBzO8zrGFAoEp/cDofg6iDfGzmwrHQ55LA==", - "requires": { - "aws-cdk": "2.94.0" - } - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "optional": true - } - } -} diff --git a/infrastructure/aws/package.json b/infrastructure/aws/package.json deleted file mode 100644 index f551ad4..0000000 --- a/infrastructure/aws/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "cdk-deploy", - "version": "0.1.0", - "description": "Dependencies for CDK deployment", - "license": "MIT", - "private": true, - "dependencies": { - "cdk": "2.94.0" - }, - "scripts": { - "cdk": "cdk" - } -} diff --git a/infrastructure/aws/requirements-cdk.txt b/infrastructure/aws/requirements-cdk.txt deleted file mode 100644 index 867a9f7..0000000 --- a/infrastructure/aws/requirements-cdk.txt +++ /dev/null @@ -1,10 +0,0 @@ -# aws cdk -aws-cdk-lib==2.99.1 -aws_cdk-aws_apigatewayv2_alpha==2.99.1a0 -aws_cdk-aws_apigatewayv2_integrations_alpha==2.99.1a0 -constructs>=10.0.0 -boto3==1.28.71 -# pydantic settings -pydantic~=2.0 -pydantic-settings~=2.0 -eoapi-cdk==6.1.0 \ No newline at end of file diff --git a/infrastructure/aws/tests/conftest.py b/infrastructure/aws/tests/conftest.py deleted file mode 100644 index e2190ed..0000000 --- a/infrastructure/aws/tests/conftest.py +++ /dev/null @@ -1,19 +0,0 @@ -"""``pytest`` configuration.""" - - -import pytest -import pytest_pgsql - -test_db = pytest_pgsql.TransactedPostgreSQLTestDB.create_fixture( - "test_db", scope="session", use_restore_state=False -) - - -@pytest.fixture(scope="session") -def database_url(test_db): - """ - Session scoped fixture to launch a postgresql database in a separate process. We use psycopg2 to ingest test data - because pytest-asyncio event loop is a function scoped fixture and cannot be called within the current scope. Yields - a database url which we pass to our application through a monkeypatched environment variable. - """ - return test_db.connection.engine.url diff --git a/infrastructure/aws/tests/test_bootstrap.py b/infrastructure/aws/tests/test_bootstrap.py deleted file mode 100644 index bc72460..0000000 --- a/infrastructure/aws/tests/test_bootstrap.py +++ /dev/null @@ -1,74 +0,0 @@ -"""Test bootstrap handler.""" - -import json -import logging -from unittest.mock import patch - -from handlers import db_handler - - -@patch("handlers.db_handler.httpx") -@patch("handlers.db_handler.boto3") -def test_bootstrap(boto3, httpx, database_url, caplog): - """Test Bootstrap.""" - boto3.client.return_value.get_secret_value.side_effect = [ - # connection_params - { - "SecretString": json.dumps( - { - "dbname": "postgres", - "username": database_url.username, - "password": database_url.password, - "host": database_url.host, - "port": database_url.port, - } - ) - }, - # user_params - { - "SecretString": json.dumps( - { - "dbname": "eoapi", - "username": "eouser", - "password": "mypassword", - "host": database_url.host, - "port": database_url.port, - } - ) - }, - ] - - class put: - """Fake httpx.put response.""" - - status_code: int = 200 - reason: str = "All Good" - - httpx.put.return_value = put() - - event = { - "StackId": "eoapi-test", - "RequestId": "request-00001", - "LogicalResourceId": "eoapi-test", - "RequestType": "Create", - "ResourceProperties": { - "pgstac_version": "0.6.11", - "context": True, - "mosaic_index": True, - "conn_secret_arn": "secret1", - "new_user_secret_arn": "secret2", - }, - "ResponseURL": "http://0.0.0.0", - } - - class ctx: - """Fake context object.""" - - log_stream_name: str = "something" - - context = ctx() - - with caplog.at_level(logging.DEBUG, logger="eoapi-bootstrap"): - db_handler.handler(event, context) - for record in caplog.records: - assert record.message == "OK - Status code: 200" diff --git a/ruff.toml b/ruff.toml deleted file mode 100644 index d92bf79..0000000 --- a/ruff.toml +++ /dev/null @@ -1,13 +0,0 @@ -select = [ - "D1", # pydocstyle errors - "E", # pycodestyle errors - "W", # pycodestyle warnings - "F", # flake8 - "C", # flake8-comprehensions - "B", # flake8-bugbear -] -ignore = [ - "E501", # line too long, handled by black - "B008", # do not perform function calls in argument defaults - "B905", # ignore zip() without an explicit strict= parameter, only support with python >3.10 -] diff --git a/runtime/eoapi/raster/README.md b/runtime/eoapi/raster/README.md deleted file mode 100644 index aeefc7e..0000000 --- a/runtime/eoapi/raster/README.md +++ /dev/null @@ -1,3 +0,0 @@ -## eoapi.raster - -![](https://user-images.githubusercontent.com/10407788/151455911-c455a043-3313-4c26-b980-042cb80787a3.png) diff --git a/runtime/eoapi/raster/eoapi/raster/__init__.py b/runtime/eoapi/raster/eoapi/raster/__init__.py deleted file mode 100644 index 9b25578..0000000 --- a/runtime/eoapi/raster/eoapi/raster/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""eoapi.raster.""" - -__version__ = "0.1.0" diff --git a/runtime/eoapi/raster/eoapi/raster/app.py b/runtime/eoapi/raster/eoapi/raster/app.py deleted file mode 100644 index 4012d16..0000000 --- a/runtime/eoapi/raster/eoapi/raster/app.py +++ /dev/null @@ -1,370 +0,0 @@ -"""TiTiler+PgSTAC FastAPI application.""" - -import logging -from contextlib import asynccontextmanager -from typing import Dict - -import jinja2 -import pystac -from eoapi.raster import __version__ as eoapi_raster_version -from eoapi.raster.config import ApiSettings -from fastapi import Depends, FastAPI, Query -from psycopg import OperationalError -from psycopg.rows import dict_row -from psycopg_pool import PoolTimeout -from starlette.middleware.cors import CORSMiddleware -from starlette.requests import Request -from starlette.responses import HTMLResponse -from starlette.templating import Jinja2Templates -from starlette_cramjam.middleware import CompressionMiddleware -from titiler.core.errors import DEFAULT_STATUS_CODES, add_exception_handlers -from titiler.core.factory import ( - AlgorithmFactory, - MultiBaseTilerFactory, - TilerFactory, - TMSFactory, -) -from titiler.core.middleware import CacheControlMiddleware -from titiler.extensions import cogViewerExtension -from titiler.mosaic.errors import MOSAIC_STATUS_CODES -from titiler.pgstac.db import close_db_connection, connect_to_db -from titiler.pgstac.dependencies import CollectionIdParams, ItemIdParams, SearchIdParams -from titiler.pgstac.extensions import searchInfoExtension -from titiler.pgstac.factory import ( - MosaicTilerFactory, - add_search_list_route, - add_search_register_route, -) -from titiler.pgstac.reader import PgSTACReader - -logging.getLogger("botocore.credentials").disabled = True -logging.getLogger("botocore.utils").disabled = True -logging.getLogger("rio-tiler").setLevel(logging.ERROR) - -settings = ApiSettings() - -jinja2_env = jinja2.Environment( - loader=jinja2.ChoiceLoader( - [ - jinja2.PackageLoader(__package__, "templates"), - ] - ) -) -templates = Jinja2Templates(env=jinja2_env) - - -@asynccontextmanager -async def lifespan(app: FastAPI): - """FastAPI Lifespan.""" - # Create Connection Pool - await connect_to_db(app) - yield - # Close the Connection Pool - await close_db_connection(app) - - -app = FastAPI( - title=settings.name, - version=eoapi_raster_version, - root_path=settings.root_path, - lifespan=lifespan, -) -add_exception_handlers(app, DEFAULT_STATUS_CODES) -add_exception_handlers(app, MOSAIC_STATUS_CODES) - -if settings.cors_origins: - app.add_middleware( - CORSMiddleware, - allow_origins=settings.cors_origins, - allow_credentials=True, - allow_methods=["GET", "POST", "OPTIONS"], - allow_headers=["*"], - ) - -app.add_middleware( - CacheControlMiddleware, - cachecontrol=settings.cachecontrol, - exclude_path={r"/healthz"}, -) -app.add_middleware( - CompressionMiddleware, - exclude_mediatype={ - "image/jpeg", - "image/jpg", - "image/png", - "image/jp2", - "image/webp", - }, -) - -############################################################################### -# `Secret` endpoint for mosaic builder. Do not need to be public (in the OpenAPI docs) -@app.get("/collections", include_in_schema=False) -async def list_collection(request: Request): - """list collections.""" - with request.app.state.dbpool.connection() as conn: - with conn.cursor(row_factory=dict_row) as cursor: - cursor.execute("SELECT * FROM pgstac.all_collections();") - r = cursor.fetchone() - return r.get("all_collections", []) - - -############################################################################### -# STAC Search Endpoints -searches = MosaicTilerFactory( - path_dependency=SearchIdParams, - router_prefix="/searches/{search_id}", - add_statistics=True, - add_viewer=True, - add_part=True, - extensions=[ - searchInfoExtension(), - ], -) -app.include_router( - searches.router, tags=["STAC Search"], prefix="/searches/{search_id}" -) - -add_search_register_route( - app, - prefix="/searches", - tile_dependencies=[ - searches.layer_dependency, - searches.dataset_dependency, - searches.pixel_selection_dependency, - searches.tile_dependency, - searches.process_dependency, - searches.rescale_dependency, - searches.colormap_dependency, - searches.render_dependency, - searches.pgstac_dependency, - searches.reader_dependency, - searches.backend_dependency, - ], - tags=["STAC Search"], -) -add_search_list_route(app, prefix="/searches", tags=["STAC Search"]) - - -@app.get("/searches/builder", response_class=HTMLResponse, tags=["STAC Search"]) -async def virtual_mosaic_builder(request: Request): - """Mosaic Builder Viewer.""" - base_url = str(request.base_url) - return templates.TemplateResponse( - name="mosaic-builder.html", - context={ - "request": request, - "register_endpoint": str( - app.url_path_for("register_search").make_absolute_url(base_url=base_url) - ), - "collections_endpoint": str( - app.url_path_for("list_collection").make_absolute_url(base_url=base_url) - ), - }, - media_type="text/html", - ) - - -############################################################################### -# STAC COLLECTION Endpoints -collection = MosaicTilerFactory( - path_dependency=CollectionIdParams, - router_prefix="/collections/{collection_id}", - add_statistics=True, - add_viewer=True, - add_part=True, -) -app.include_router( - collection.router, tags=["STAC Collection"], prefix="/collections/{collection_id}" -) - - -############################################################################### -# STAC Item Endpoints -stac = MultiBaseTilerFactory( - reader=PgSTACReader, - path_dependency=ItemIdParams, - router_prefix="/collections/{collection_id}/items/{item_id}", - add_viewer=True, -) -app.include_router( - stac.router, - tags=["STAC Item"], - prefix="/collections/{collection_id}/items/{item_id}", -) - - -@stac.router.get("/viewer", response_class=HTMLResponse) -def viewer(request: Request, item: pystac.Item = Depends(stac.path_dependency)): - """STAC Viewer - - Simplified version of https://github.com/developmentseed/titiler/blob/main/src/titiler/extensions/titiler/extensions/templates/stac_viewer.html - """ - return templates.TemplateResponse( - name="stac-viewer.html", - context={ - "request": request, - "endpoint": request.url.path.replace("/viewer", ""), - }, - media_type="text/html", - ) - - -app.include_router( - stac.router, - tags=["STAC Item"], - prefix="/collections/{collection_id}/items/{item_id}", -) - - -############################################################################### -# COG Endpoints -cog = TilerFactory( - router_prefix="/cog", - extensions=[ - cogViewerExtension(), - ], -) - -app.include_router(cog.router, prefix="/cog", tags=["Cloud Optimized GeoTIFF"]) - -############################################################################### -# Tiling Schemes Endpoints -tms = TMSFactory() -app.include_router(tms.router, tags=["Tiling Schemes"]) - -############################################################################### -# Algorithms Endpoints -algorithms = AlgorithmFactory() -app.include_router(algorithms.router, tags=["Algorithms"]) - - -############################################################################### -# Health Check Endpoint -@app.get("/healthz", description="Health Check", tags=["Health Check"]) -def ping( - timeout: int = Query(1, description="Timeout getting SQL connection from the pool.") -) -> Dict: - """Health check.""" - try: - with app.state.dbpool.connection(timeout) as conn: - with conn.cursor() as cursor: - cursor.execute("SELECT version from pgstac.migrations;") - version = cursor.fetchone() - return {"database_online": True, "pgstac_version": version} - except (OperationalError, PoolTimeout): - return {"database_online": False} - - -############################################################################### -# Landing page Endpoint -@app.get( - "/", - response_class=HTMLResponse, -) -def landing(request: Request): - """Get landing page.""" - data = { - "title": settings.name or "eoAPI-raster", - "links": [ - { - "title": "Landing page", - "href": str(request.url_for("landing")), - "type": "text/html", - "rel": "self", - }, - { - "title": "the API definition (JSON)", - "href": str(request.url_for("openapi")), - "type": "application/vnd.oai.openapi+json;version=3.0", - "rel": "service-desc", - }, - { - "title": "the API documentation", - "href": str(request.url_for("swagger_ui_html")), - "type": "text/html", - "rel": "service-doc", - }, - { - "title": "PgSTAC Virtual Mosaic list (JSON)", - "href": str(app.url_path_for("list_searches")), - "type": "application/json", - "rel": "data", - }, - { - "title": "PgSTAC Virtual Mosaic builder", - "href": str(app.url_path_for("virtual_mosaic_builder")), - "type": "text/html", - "rel": "data", - }, - { - "title": "PgSTAC Virtual Mosaic viewer (template URL)", - "href": str(app.url_path_for("map_viewer", search_id="{search_id}")), - "type": "text/html", - "rel": "data", - }, - { - "title": "PgSTAC Collection viewer (template URL)", - "href": str( - app.url_path_for("map_viewer", collection_id="{collection_id}") - ), - "type": "text/html", - "rel": "data", - }, - { - "title": "PgSTAC Item viewer (template URL)", - "href": str( - app.url_path_for( - "map_viewer", - collection_id="{collection_id}", - item_id="{item_id}", - ) - ), - "type": "text/html", - "rel": "data", - }, - { - "title": "TiTiler-PgSTAC Documentation (external link)", - "href": "https://stac-utils.github.io/titiler-pgstac/", - "type": "text/html", - "rel": "doc", - }, - { - "title": "TiTiler-PgSTAC source code (external link)", - "href": "https://github.com/stac-utils/titiler-pgstac", - "type": "text/html", - "rel": "doc", - }, - ], - } - - urlpath = request.url.path - crumbs = [] - baseurl = str(request.base_url).rstrip("/") - - crumbpath = str(baseurl) - for crumb in urlpath.split("/"): - crumbpath = crumbpath.rstrip("/") - part = crumb - if part is None or part == "": - part = "Home" - crumbpath += f"/{crumb}" - crumbs.append({"url": crumbpath.rstrip("/"), "part": part.capitalize()}) - - return templates.TemplateResponse( - "landing.html", - { - "request": request, - "response": data, - "template": { - "api_root": baseurl, - "params": request.query_params, - "title": "", - }, - "crumbs": crumbs, - "url": str(request.url), - "baseurl": baseurl, - "urlpath": str(request.url.path), - "urlparams": str(request.url.query), - }, - ) diff --git a/runtime/eoapi/raster/eoapi/raster/config.py b/runtime/eoapi/raster/eoapi/raster/config.py deleted file mode 100644 index f6f898b..0000000 --- a/runtime/eoapi/raster/eoapi/raster/config.py +++ /dev/null @@ -1,21 +0,0 @@ -"""API settings.""" - -from pydantic import field_validator -from pydantic_settings import BaseSettings - - -class ApiSettings(BaseSettings): - """API settings""" - - name: str = "eoAPI-raster" - cors_origins: str = "*" - cachecontrol: str = "public, max-age=3600" - debug: bool = False - root_path: str = "" - - model_config = {"env_prefix": "EOAPI_RASTER_", "env_file": ".env"} - - @field_validator("cors_origins") - def parse_cors_origin(cls, v): - """Parse CORS origins.""" - return [origin.strip() for origin in v.split(",")] diff --git a/runtime/eoapi/raster/eoapi/raster/templates/landing.html b/runtime/eoapi/raster/eoapi/raster/templates/landing.html deleted file mode 100644 index a71d3fa..0000000 --- a/runtime/eoapi/raster/eoapi/raster/templates/landing.html +++ /dev/null @@ -1,72 +0,0 @@ - - - -- {{ response.description }} -
- -