├── .github ├── ISSUE_TEMPLATE │ └── release.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml ├── release-drafter.yml └── workflows │ ├── ci.yml │ └── release-drafter.yml ├── .gitignore ├── .sbtopts ├── .scalafmt.conf ├── CHANGELOG.md ├── LICENSE ├── README.md ├── azure ├── README.md └── src │ └── main │ ├── resources │ ├── META-INF │ │ └── services │ │ │ └── geotrellis.util.RangeReaderProvider │ └── reference.conf │ └── scala │ └── geotrellis │ └── store │ └── azure │ ├── AzureBlobServiceClientProducer.scala │ ├── conf │ └── AzureConfig.scala │ ├── package.scala │ └── util │ ├── AzureRangeReader.scala │ ├── AzureRangeReaderProvider.scala │ └── AzureURI.scala ├── bench └── src │ └── main │ ├── resources │ ├── application.conf │ └── logback.xml │ └── scala │ └── geotrellis │ └── server │ └── TmsReificationBench.scala ├── build.sbt ├── core └── src │ ├── main │ └── scala │ │ └── geotrellis │ │ ├── server │ │ ├── ExtentReification.scala │ │ ├── HasRasterExtents.scala │ │ ├── LayerExtent.scala │ │ ├── LayerHistogram.scala │ │ ├── LayerTms.scala │ │ ├── TmsReification.scala │ │ ├── extent │ │ │ └── SampleUtils.scala │ │ ├── gtlayer │ │ │ └── GTLayerNode.scala │ │ ├── utils │ │ │ ├── Implicits.scala │ │ │ └── package.scala │ │ └── vlm │ │ │ ├── RasterSourceUtils.scala │ │ │ ├── geotiff │ │ │ ├── GeoTiffNode.scala │ │ │ └── util │ │ │ │ ├── CacheRangeReader.scala │ │ │ │ ├── CogUtils.scala │ │ │ │ └── RangeReaderUtils.scala │ │ │ └── package.scala │ │ └── store │ │ └── query │ │ ├── QueryF.scala │ │ ├── RasterSourceRepository.scala │ │ ├── RepositoryM.scala │ │ ├── package.scala │ │ └── vector │ │ └── ProjectedGeometry.scala │ └── test │ ├── resources │ ├── 8x8.tif │ └── resources.md │ └── scala │ └── geotrellis │ ├── server │ ├── ExtentMergeStrategyTest.scala │ ├── HistogramHeuristicsTest.scala │ ├── LayerExtentTest.scala │ ├── LayerHistogramTest.scala │ ├── NoDataHandlingTest.scala │ ├── ResourceTile.scala │ ├── TileAsSourceImplicits.scala │ └── package.scala │ └── store │ └── query │ ├── EmptyMetadata.scala │ ├── EmptyRasterSource.scala │ └── QueryFSpec.scala ├── docker-compose.yml ├── effects └── src │ └── main │ └── scala │ └── geotrellis │ └── raster │ └── effects │ ├── MosaicRasterSourceF.scala │ ├── MosaicRasterSourceIO.scala │ ├── RasterMetadataF.scala │ ├── RasterSourceF.scala │ ├── UnsafeLift.scala │ └── geotiff │ ├── GeoTiffRasterSource.scala │ ├── GeoTiffReprojectRasterSource.scala │ └── GeoTiffResampleRasterSource.scala ├── example └── src │ └── main │ ├── resources │ ├── application.conf │ ├── logback.xml │ └── overlay-demo │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── overlay.js │ │ └── style.css │ └── scala │ └── geotrellis │ └── server │ └── example │ ├── ExampleConf.scala │ ├── ndvi │ ├── NdviServer.scala │ └── NdviService.scala │ └── persistence │ ├── MamlStore.scala │ ├── PersistenceServer.scala │ ├── PersistenceService.scala │ └── package.scala ├── ogc-example ├── docs │ ├── README.md │ ├── conf.md │ ├── img │ │ └── ogcserver-in-console.png │ ├── maml-operations.md │ ├── maml.md │ ├── temporal-layers.md │ └── usage.md └── src │ ├── main │ ├── resources │ │ ├── application-spacetimekey.conf │ │ ├── application.conf │ │ └── logback.xml │ └── scala │ │ └── geotrellis │ │ └── server │ │ └── ogc │ │ ├── ExtendedParameters.scala │ │ ├── FocalParameters.scala │ │ ├── Main.scala │ │ ├── OgcService.scala │ │ ├── RGBParameters.scala │ │ ├── ToMediaType.scala │ │ ├── conf │ │ ├── Conf.scala │ │ ├── OgcServiceConf.scala │ │ ├── OgcSourceConf.scala │ │ ├── StyleConf.scala │ │ └── package.scala │ │ ├── wcs │ │ └── WcsView.scala │ │ ├── wms │ │ └── WmsView.scala │ │ └── wmts │ │ └── WmtsView.scala │ └── test │ └── scala │ └── geotrellis │ └── server │ ├── HillshadeSpec.scala │ └── ogc │ └── conf │ └── ColorMapConfigurationSpec.scala ├── ogc ├── README.md └── src │ ├── main │ └── scala │ │ └── geotrellis │ │ └── server │ │ └── ogc │ │ ├── FeatureCollection.scala │ │ ├── InfoFormat.scala │ │ ├── OgcLayer.scala │ │ ├── OgcSource.scala │ │ ├── OgcSourceRepository.scala │ │ ├── OgcTime.scala │ │ ├── OgcTimeDefault.scala │ │ ├── OgcTimeFormat.scala │ │ ├── OutputFormat.scala │ │ ├── Render.scala │ │ ├── TiledOgcLayer.scala │ │ ├── URN.scala │ │ ├── gml │ │ └── GmlDataRecord.scala │ │ ├── ows │ │ ├── OwsDataRecord.scala │ │ └── ServiceMetadata.scala │ │ ├── package.scala │ │ ├── params │ │ ├── CRSUtils.scala │ │ ├── ParamError.scala │ │ └── ParamMap.scala │ │ ├── style │ │ ├── ClipDefinition.scala │ │ ├── ColorMapStyle.scala │ │ ├── ColorRampStyle.scala │ │ ├── InterpolatedColorMap.scala │ │ ├── InterpolatedColorMapStyle.scala │ │ ├── LegendModel.scala │ │ ├── OgcStyle.scala │ │ ├── OnlineResourceModel.scala │ │ └── RGBStyle.scala │ │ ├── utils │ │ ├── ExpressionUtils.scala │ │ ├── Implicits.scala │ │ ├── ScalaxbUtils.scala │ │ └── package.scala │ │ ├── wcs │ │ ├── CapabilitiesView.scala │ │ ├── CoverageView.scala │ │ ├── GetCoverage.scala │ │ ├── WcsModel.scala │ │ ├── WcsParams.scala │ │ └── package.scala │ │ ├── wfs │ │ ├── WfsFeatureCollection.scala │ │ └── package.scala │ │ ├── wms │ │ ├── CapabilitiesView.scala │ │ ├── GetFeatureInfo.scala │ │ ├── GetFeatureInfoException.scala │ │ ├── GetFeatureInfoExtended.scala │ │ ├── GetMap.scala │ │ ├── GetMapException.scala │ │ ├── WmsModel.scala │ │ ├── WmsParams.scala │ │ ├── WmsParentLayerMeta.scala │ │ └── package.scala │ │ └── wmts │ │ ├── CapabilitiesView.scala │ │ ├── GeotrellisTileMatrix.scala │ │ ├── GeotrellisTileMatrixSet.scala │ │ ├── WmtsModel.scala │ │ ├── WmtsParams.scala │ │ └── package.scala │ └── test │ └── scala │ └── geotrellis │ └── server │ └── ogc │ ├── InterpolatedColorMapSpec.scala │ └── OgcStyleSpec.scala ├── opengis └── src │ └── main │ └── xsd │ ├── ReadMe.txt │ ├── filter │ └── 1.1.0 │ │ ├── expr.xsd │ │ ├── filter.xsd │ │ ├── filterAll.xsd │ │ ├── filterCapabilities.xsd │ │ └── sort.xsd │ ├── gml │ ├── 3.1.1 │ │ ├── base │ │ │ ├── basicTypes.xsd │ │ │ ├── coordinateOperations.xsd │ │ │ ├── coordinateReferenceSystems.xsd │ │ │ ├── coordinateSystems.xsd │ │ │ ├── coverage.xsd │ │ │ ├── dataQuality.xsd │ │ │ ├── datums.xsd │ │ │ ├── defaultStyle.xsd │ │ │ ├── dictionary.xsd │ │ │ ├── direction.xsd │ │ │ ├── dynamicFeature.xsd │ │ │ ├── feature.xsd │ │ │ ├── geometryAggregates.xsd │ │ │ ├── geometryBasic0d1d.xsd │ │ │ ├── geometryBasic2d.xsd │ │ │ ├── geometryComplexes.xsd │ │ │ ├── geometryPrimitives.xsd │ │ │ ├── gml.xsd │ │ │ ├── gmlBase.xsd │ │ │ ├── grids.xsd │ │ │ ├── measures.xsd │ │ │ ├── observation.xsd │ │ │ ├── referenceSystems.xsd │ │ │ ├── temporal.xsd │ │ │ ├── temporalReferenceSystems.xsd │ │ │ ├── temporalTopology.xsd │ │ │ ├── topology.xsd │ │ │ ├── units.xsd │ │ │ └── valueObjects.xsd │ │ ├── gml_3_1_1-ReadMe.txt │ │ └── smil │ │ │ ├── smil20-language.xsd │ │ │ └── smil20.xsd │ └── ReadMe.txt │ ├── ows │ ├── 1.0.0 │ │ ├── ReadMe.txt │ │ ├── ows19115subset.xsd │ │ ├── owsAll.xsd │ │ ├── owsCommon.xsd │ │ ├── owsDataIdentification.xsd │ │ ├── owsExceptionReport.xsd │ │ ├── owsGetCapabilities.xsd │ │ ├── owsOperationsMetadata.xsd │ │ ├── owsServiceIdentification.xsd │ │ └── owsServiceProvider.xsd │ ├── 1.1.0 │ │ ├── ows19115subset.xsd │ │ ├── owsAll.xsd │ │ ├── owsCommon.xsd │ │ ├── owsContents.xsd │ │ ├── owsDataIdentification.xsd │ │ ├── owsDomainType.xsd │ │ ├── owsExceptionReport.xsd │ │ ├── owsGetCapabilities.xsd │ │ ├── owsGetResourceByID.xsd │ │ ├── owsInputOutputData.xsd │ │ ├── owsManifest.xsd │ │ ├── owsOperationsMetadata.xsd │ │ ├── owsServiceIdentification.xsd │ │ └── owsServiceProvider.xsd │ └── ReadMe.txt │ ├── se │ └── 1.1.0 │ │ ├── FeatureStyle.xsd │ │ ├── Symbolizer.xsd │ │ └── common.xsd │ ├── sld │ └── 1.1.0 │ │ ├── DescribeLayer.xsd │ │ ├── GetMap.xsd │ │ ├── StyledLayerDescriptor.xsd │ │ ├── sldAll.xsd │ │ └── sld_capabilities.xsd │ ├── wcs │ └── 1.1.1 │ │ ├── Examples │ │ ├── example2dGridCRSdefault1.xml │ │ ├── example2dGridCRSdefault2.xml │ │ ├── example2dGridCRSdefault3.xml │ │ ├── exampleCapabilities1.xml │ │ ├── exampleCapabilities2.xml │ │ ├── exampleCoverageDescription1.xml │ │ ├── exampleCoverageDescription2.xml │ │ ├── exampleCoverages1.xml │ │ ├── exampleDescribeCoverage1.xml │ │ ├── exampleDescribeCoverage2.xml │ │ ├── exampleGetCapabilitiesMax.xml │ │ ├── exampleGetCapabilitiesMin.xml │ │ ├── exampleGetCoverage1.xml │ │ ├── exampleGetCoverage2.xml │ │ ├── exampleGetCoverage3.xml │ │ ├── exampleGetCoverage4.xml │ │ └── exampleInterpolationMethods1.xml │ │ ├── GridMethods │ │ ├── 2dGridIn2dCrsMethod.xml │ │ ├── 2dGridIn3dCRsMethod.xml │ │ └── 2dSimpleGridMethod.xml │ │ ├── gml4wcs.xsd │ │ ├── interpolationMethods.xml │ │ ├── wcsAll.xsd │ │ ├── wcsCommon.xsd │ │ ├── wcsContents.xsd │ │ ├── wcsCoverages.xsd │ │ ├── wcsDescribeCoverage.xsd │ │ ├── wcsGetCapabilities.xsd │ │ ├── wcsGetCoverage.xsd │ │ ├── wcsGridCRS.xsd │ │ └── wcsInterpolationMethod.xsd │ ├── wfs │ └── 1.1.0 │ │ └── wfs.xsd │ ├── wms │ └── 1.3.0 │ │ ├── ReadMe.txt │ │ ├── capabilities_1_3_0.xml │ │ ├── capabilities_1_3_0.xsd │ │ ├── exceptions_1_3_0.xml │ │ └── exceptions_1_3_0.xsd │ ├── wmts │ ├── 1.0 │ │ ├── wmts.xsd │ │ ├── wmtsAbstract.wsdl │ │ ├── wmtsGetCapabilities_request.xsd │ │ ├── wmtsGetCapabilities_response.xsd │ │ ├── wmtsGetFeatureInfo_request.xsd │ │ ├── wmtsGetFeatureInfo_response.xsd │ │ ├── wmtsGetTile_request.xsd │ │ ├── wmtsKVP.xsd │ │ └── wmtsPayload_response.xsd │ └── ReadMe.txt │ └── xlink │ ├── 1.0.0 │ └── xlink.xsd │ └── ReadMe.txt ├── project ├── Dependencies.scala ├── build.properties └── plugins.sbt ├── sbt ├── scripts ├── cibuild ├── cipublish ├── server ├── test └── update ├── stac-example ├── Dockerfile ├── Makefile ├── README.md ├── catalog │ ├── landsat-stac-collection │ │ ├── catalog.json │ │ └── landsat-8-l1 │ │ │ ├── 2018-05 │ │ │ └── LC80150322018141LGN00.json │ │ │ ├── 2018-06 │ │ │ ├── LC80140332018166LGN00.json │ │ │ └── LC80300332018166LGN00.json │ │ │ ├── 2018-07 │ │ │ └── LC80150332018189LGN00.json │ │ │ └── catalog.json │ ├── landsat-stac-layers │ │ ├── catalog.json │ │ ├── landsat-8-l1 │ │ │ ├── 2018-05 │ │ │ │ └── LC80150322018141LGN00.json │ │ │ ├── 2018-06 │ │ │ │ ├── LC80140332018166LGN00.json │ │ │ │ └── LC80300332018166LGN00.json │ │ │ ├── 2018-07 │ │ │ │ └── LC80150332018189LGN00.json │ │ │ └── catalog.json │ │ └── layers │ │ │ ├── catalog.json │ │ │ ├── pa.json │ │ │ └── us.json │ └── landsat-stac-periodic-collection │ │ ├── catalog.json │ │ └── landsat-8-l1 │ │ ├── 2018-05 │ │ └── LC80150322018141LGN00.json │ │ ├── 2018-06 │ │ └── LC80140332018166LGN00.json │ │ ├── 2018-07 │ │ └── LC80150332018189LGN00.json │ │ └── catalog.json ├── docker-compose.ci.yml ├── docker-compose.yml └── src │ ├── main │ ├── resources │ │ ├── application.conf │ │ └── logback.xml │ └── scala │ │ └── geotrellis │ │ └── server │ │ └── ogc │ │ ├── ExtendedParameters.scala │ │ ├── FocalParameters.scala │ │ ├── Main.scala │ │ ├── OgcService.scala │ │ ├── RGBParameters.scala │ │ ├── StacOgcSource.scala │ │ ├── ToMediaType.scala │ │ ├── conf │ │ ├── Conf.scala │ │ ├── OgcServiceConf.scala │ │ ├── OgcSourceConf.scala │ │ ├── StyleConf.scala │ │ └── package.scala │ │ ├── stac │ │ ├── MapAlgebraStacOgcRepositories.scala │ │ ├── SearchFiltersQuery.scala │ │ ├── StacOgcRepositories.scala │ │ ├── StacSearchCriteria.scala │ │ ├── StacSummary.scala │ │ └── package.scala │ │ ├── wcs │ │ └── WcsView.scala │ │ ├── wms │ │ └── WmsView.scala │ │ └── wmts │ │ └── WmtsView.scala │ └── test │ └── scala │ └── geotrellis │ ├── IOSpec.scala │ └── server │ └── ogc │ └── stac │ ├── Http4sStacClientSpec.scala │ └── StacRepository.scala └── stac └── src └── main └── scala └── geotrellis └── stac ├── extensions └── proj │ ├── ProjItemExtension.scala │ ├── ProjShape.scala │ └── ProjTransform.scala ├── package.scala ├── raster ├── StacAssetOps.scala ├── StacAssetRasterSource.scala ├── StacAssetReprojectRasterSource.scala ├── StacAssetResampleRasterSource.scala ├── StacCollectionSource.scala ├── StacItemAsset.scala ├── StacItemAssetMetadata.scala ├── StacItemOps.scala └── StacSource.scala └── util └── logging ├── StacClientLoggingMid.scala ├── StreamingStacClientLoggingMid.scala └── syntax.scala /.github/ISSUE_TEMPLATE/release.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Release 3 | about: When ready to cut a release 4 | title: Release X.Y.Z 5 | labels: release 6 | assignees: '' 7 | 8 | --- 9 | 10 | - [ ] Make sure `main` is up-to-date: `git checkout -f main && git pull origin main` 11 | - [ ] Rotate `CHANGELOG.md` (following [Keep a Changelog](https://keepachangelog.com/) principles) 12 | - [ ] Ensure outstanding changes are committed: 13 | ```bash 14 | $ git status # Is the git staging area clean? 15 | $ git add CHANGELOG.md 16 | $ git commit -m "X.Y.Z" 17 | ``` 18 | - [ ] Make and push an annotated tag for your release and push `main`: 19 | ```bash 20 | $ git tag -s -a vX.Y.Z -m "Release version " 21 | $ git push origin --tags 22 | $ git push origin main 23 | ``` 24 | - [ ] Ensure that CI checks pass 25 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Overview 2 | 3 | Brief description of what this PR does, and why it is needed. 4 | 5 | ### Checklist 6 | 7 | - [ ] Description of PR is in an appropriate section of the CHANGELOG and grouped with similar changes if possible 8 | 9 | ### Demo 10 | 11 | Optional. Screenshots, `http` examples, etc. 12 | 13 | ### Notes 14 | 15 | Optional. Ancillary topics, caveats, alternative strategies that didn't work out, anything else. 16 | 17 | ## Testing Instructions 18 | 19 | - How to test this PR 20 | - Prefer bulleted description 21 | - Start after checking out this branch 22 | - Include any setup required, such as bundling scripts, restarting services, etc. 23 | - Include test case, and expected output 24 | 25 | Closes #XXX 26 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name-template: '$NEXT_MINOR_VERSION' 2 | tag-template: 'v$NEXT_MINOR_VERSION' 3 | categories: 4 | - title: 'Added' 5 | labels: 6 | - 'feature' 7 | - title: 'Changed' 8 | labels: 9 | - 'enhancement' 10 | - 'dependency-update' 11 | - title: 'Fixed' 12 | labels: 13 | - 'fix' 14 | - 'bugfix' 15 | - 'bug' 16 | exclude-labels: 17 | - 'skip-changelog' 18 | - 'docs' 19 | - 'build' 20 | change-template: '- $TITLE [#$NUMBER](https://github.com/geotrellis/geotrelis-server/pull/$NUMBER) (@$AUTHOR)' 21 | template: | 22 | $CHANGES 23 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | pull_request: 4 | branches: ['**'] 5 | push: 6 | branches: ['**'] 7 | tags: [v*] 8 | jobs: 9 | build: 10 | name: Build and Test 11 | if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != 'geotrellis/geotrellis-server' 12 | strategy: 13 | matrix: 14 | os: [ubuntu-latest] 15 | java: [11, 21] 16 | distribution: [temurin] 17 | runs-on: ${{ matrix.os }} 18 | steps: 19 | - uses: actions/checkout@v4 20 | with: 21 | fetch-depth: 0 22 | - uses: coursier/cache-action@v6 23 | - uses: actions/setup-java@v4 24 | with: 25 | distribution: ${{ matrix.distribution }} 26 | java-version: ${{ matrix.java }} 27 | 28 | - name: Check formatting 29 | run: sbt scalafmtCheckAll 30 | 31 | - name: Build project 32 | run: sbt +test 33 | 34 | publish: 35 | name: Publish Artifacts 36 | needs: [build] 37 | if: github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) 38 | strategy: 39 | matrix: 40 | os: [ubuntu-latest] 41 | java: [11] 42 | distribution: [temurin] 43 | runs-on: ${{ matrix.os }} 44 | steps: 45 | - uses: actions/checkout@v4 46 | with: 47 | fetch-depth: 0 48 | - uses: coursier/cache-action@v6 49 | - uses: actions/setup-java@v4 50 | with: 51 | distribution: ${{ matrix.distribution }} 52 | java-version: ${{ matrix.java }} 53 | 54 | - name: Release 55 | run: sbt ci-release 56 | env: 57 | PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} 58 | PGP_SECRET: ${{ secrets.PGP_SECRET }} 59 | SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} 60 | SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} 61 | if: ${{ env.SONATYPE_PASSWORD != '' && env.SONATYPE_USERNAME != '' }} 62 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | types: [opened, reopened, synchronize] 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | update_release_draft: 15 | permissions: 16 | contents: write 17 | pull-requests: write 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: release-drafter/release-drafter@v6 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Operating System Files 2 | 3 | *.DS_Store 4 | Thumbs.db 5 | 6 | # Build Files 7 | 8 | bin 9 | target 10 | build/ 11 | .gradle 12 | cmake-build-debug 13 | 14 | # Eclipse Project Files 15 | 16 | .classpath 17 | .project 18 | .settings 19 | 20 | # IntelliJ IDEA Files 21 | 22 | *.iml 23 | *.ipr 24 | *.iws 25 | *.idea 26 | 27 | # Sublime files 28 | 29 | *.sublime-workspace 30 | 31 | # VSCode files 32 | 33 | .vscode 34 | .history 35 | 36 | # Metals 37 | 38 | .metals 39 | .bloop 40 | metals.sbt 41 | 42 | # SBT 43 | 44 | .bsp 45 | 46 | # Other 47 | 48 | .#* 49 | .lib 50 | *.aux.xml 51 | *.jar 52 | *.crc 53 | 54 | _SUCCESS 55 | 56 | *.pyc 57 | .cache 58 | .settings 59 | *.swp 60 | *.swo 61 | 62 | nohup.out 63 | derby.log 64 | metastore_db/ 65 | *.log 66 | -------------------------------------------------------------------------------- /.sbtopts: -------------------------------------------------------------------------------- 1 | -J-Xmx4G 2 | -J-Xms2G 3 | -J-Xss5M 4 | -J-XX:+UseG1GC 5 | -Djava.awt.headless=true 6 | -Dsbt.color=always 7 | -Dsbt.supershell=false 8 | -------------------------------------------------------------------------------- /.scalafmt.conf: -------------------------------------------------------------------------------- 1 | version=3.8.1 2 | runner.dialect = scala3 3 | align.openParenCallSite = true 4 | align.openParenDefnSite = true 5 | maxColumn = 150 6 | continuationIndent.defnSite = 2 7 | assumeStandardLibraryStripMargin = true 8 | danglingParentheses.preset = true 9 | rewrite.rules = [AvoidInfix, SortImports, RedundantParens, SortModifiers] 10 | docstrings = JavaDoc 11 | newlines.afterCurlyLambda = preserve 12 | docstrings.style = Asterisk 13 | docstrings.oneline = unfold 14 | -------------------------------------------------------------------------------- /azure/README.md: -------------------------------------------------------------------------------- 1 | ## GeoTrellis Azure 2 | 3 | ### Configuration (Java native RasterSource) 4 | 5 | * Set the env variable `AZURE_STORAGE_CONNECTION_STRING` (can be of any form) 6 | 7 | ### Configuration (GDAL RasterSource) 8 | 9 | * Set the env variable `AZURE_STORAGE_CONNECTION_STRING` 10 | * Should contain `AccountName` and `AccountKey` 11 | * Instead of setting `AZURE_STORAGE_CONNECTION_STRING` it is possible to set 12 | * `AZURE_STORAGE_SAS_TOKEN` (`AZURE_SAS`, GDAL < 3.5) to sign GDAL requests 13 | * `AZURE_STORAGE_ACCOUNT` 14 | 15 | For more details, see [/vsiaz/ GDAL documentation](https://gdal.org/user/virtual_file_systems.html#vsiaz-microsoft-azure-blob-files). -------------------------------------------------------------------------------- /azure/src/main/resources/META-INF/services/geotrellis.util.RangeReaderProvider: -------------------------------------------------------------------------------- 1 | geotrellis.store.azure.util.AzureRangeReaderProvider -------------------------------------------------------------------------------- /azure/src/main/resources/reference.conf: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Azavea 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | geotrellis.azure { 16 | storage-connection-string = ${?AZURE_STORAGE_CONNECTION_STRING} 17 | } -------------------------------------------------------------------------------- /azure/src/main/scala/geotrellis/store/azure/AzureBlobServiceClientProducer.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.store.azure 18 | 19 | import com.azure.storage.blob.models.DownloadRetryOptions 20 | import com.azure.storage.blob.{BlobServiceClient, BlobServiceClientBuilder} 21 | import geotrellis.store.azure.conf.AzureConfig 22 | 23 | object AzureBlobServiceClientProducer { 24 | @transient lazy val DEFAULT_RETRY_POLICY: DownloadRetryOptions = new DownloadRetryOptions().setMaxRetryRequests(5) 25 | 26 | @transient private lazy val client: BlobServiceClient = 27 | new BlobServiceClientBuilder().connectionString(AzureConfig.storageConnectionString).buildClient() 28 | 29 | private var summonClient: () => BlobServiceClient = () => client 30 | 31 | /** 32 | * Set an alternative default function for summoning BlobServiceClient 33 | */ 34 | def set(getClient: () => BlobServiceClient): Unit = summonClient = getClient 35 | 36 | /** 37 | * Get the current function registered as default for summoning BlobServiceClients 38 | */ 39 | def get: () => BlobServiceClient = summonClient 40 | } 41 | -------------------------------------------------------------------------------- /azure/src/main/scala/geotrellis/store/azure/conf/AzureConfig.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.store.azure.conf 18 | 19 | import pureconfig.ConfigSource 20 | import pureconfig.generic.auto._ 21 | 22 | case class AzureConfig(storageConnectionString: String) 23 | 24 | object AzureConfig { 25 | lazy val conf: AzureConfig = ConfigSource.default.at("geotrellis.azure").loadOrThrow[AzureConfig] 26 | implicit def cassandraConfigToClass(obj: AzureConfig.type): AzureConfig = conf 27 | } 28 | -------------------------------------------------------------------------------- /azure/src/main/scala/geotrellis/store/azure/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.store 18 | 19 | package object azure extends Serializable { 20 | final val SCHEMES: Array[String] = Array("wasb", "wasbs") 21 | } 22 | -------------------------------------------------------------------------------- /azure/src/main/scala/geotrellis/store/azure/util/AzureRangeReader.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.store.azure.util 18 | 19 | import com.azure.core.util.Context 20 | import com.azure.storage.blob.models.BlobRange 21 | import com.azure.storage.blob.{BlobClient, BlobContainerClient, BlobServiceClient} 22 | import geotrellis.store.azure.AzureBlobServiceClientProducer 23 | import geotrellis.util.RangeReader 24 | 25 | import java.io.ByteArrayOutputStream 26 | import java.net.URI 27 | 28 | class AzureRangeReader(uri: AzureURI, blobServiceClient: BlobServiceClient) extends RangeReader { 29 | @transient lazy val blobContainerClient: BlobContainerClient = blobServiceClient.getBlobContainerClient(uri.getContainer) 30 | @transient lazy val blobClient: BlobClient = blobContainerClient.getBlobClient(uri.getPath) 31 | 32 | val totalLength: Long = blobClient.getProperties.getBlobSize 33 | 34 | def readClippedRange(start: Long, length: Int): Array[Byte] = { 35 | val os = new ByteArrayOutputStream() 36 | try { 37 | blobClient.downloadStreamWithResponse( 38 | os, 39 | new BlobRange(start, length), 40 | AzureBlobServiceClientProducer.DEFAULT_RETRY_POLICY, 41 | null, 42 | false, 43 | null, 44 | Context.NONE 45 | ) 46 | os.toByteArray 47 | } finally os.close() 48 | } 49 | } 50 | 51 | object AzureRangeReader { 52 | def apply(uri: AzureURI, blobServiceClient: BlobServiceClient): AzureRangeReader = 53 | new AzureRangeReader(uri, blobServiceClient) 54 | 55 | def apply(path: String, blobServiceClient: BlobServiceClient): AzureRangeReader = 56 | apply(new URI(path), blobServiceClient) 57 | } 58 | -------------------------------------------------------------------------------- /azure/src/main/scala/geotrellis/store/azure/util/AzureRangeReaderProvider.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.store.azure.util 18 | 19 | import geotrellis.store.azure.{AzureBlobServiceClientProducer, SCHEMES} 20 | import geotrellis.util.RangeReaderProvider 21 | 22 | import java.net.URI 23 | 24 | class AzureRangeReaderProvider extends RangeReaderProvider { 25 | def canProcess(uri: URI): Boolean = AzureRangeReaderProvider.canProcess(uri) 26 | 27 | def rangeReader(uri: URI): AzureRangeReader = AzureRangeReader(AzureURI.fromURI(uri), AzureBlobServiceClientProducer.get()) 28 | } 29 | 30 | object AzureRangeReaderProvider { 31 | def canProcess(uri: String): Boolean = canProcess(new URI(uri)) 32 | def canProcess(uri: URI): Boolean = (uri.getScheme match { 33 | case str: String => SCHEMES contains str.toLowerCase 34 | case null => false 35 | }) || uri.getAuthority.endsWith(".blob.core.windows.net") 36 | } 37 | -------------------------------------------------------------------------------- /azure/src/main/scala/geotrellis/store/azure/util/AzureURI.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.store.azure.util 18 | 19 | import java.net.URI 20 | 21 | /** 22 | * Works with the following URIs: wasb://${container}@${account}.blob.core.windows.net/testDir/testFile 23 | * wasbs://${mycontainer}@${myaccount}.blob.core.windows.net/testDir/testFile 24 | */ 25 | 26 | case class AzureURI(uri: URI) { 27 | def getContainer: String = uri.getUserInfo 28 | def getAccount: String = uri.getAuthority.split("@").last.split("\\.").head 29 | def getPath: String = { 30 | val path = uri.getPath 31 | if (path.startsWith("/")) path.drop(1) else path 32 | } 33 | 34 | override def toString: String = uri.toString 35 | } 36 | 37 | object AzureURI { 38 | 39 | /** 40 | * May transform the input http / https URI of the following shape: https://${account}.blob.core.windows.net/${container}/path into the format 41 | * compatible with the AzureURI 42 | */ 43 | def fromURI(uri: URI): AzureURI = 44 | if (List("wasbs", "wasb") contains uri.getScheme) AzureURI(uri) 45 | else { 46 | // get URI path, remove the first slash 47 | val path = uri.getPath 48 | val npath = (if (path.startsWith("/")) path.tail else path).split("/") 49 | // the first item in the path is the container 50 | val container = npath.head 51 | // no container path 52 | val ncpath = npath.tail.mkString("/") 53 | AzureURI(new URI(s"wasbs://$container@${uri.getAuthority}/$ncpath")) 54 | } 55 | def fromString(uri: String): AzureURI = fromURI(new URI(uri)) 56 | 57 | implicit def uriToAzureUri(uri: URI): AzureURI = fromURI(uri) 58 | implicit def stringToAzureUri(uri: String): AzureURI = fromString(uri) 59 | implicit def azureUriToUri(uri: AzureURI): URI = uri.uri 60 | } 61 | -------------------------------------------------------------------------------- /bench/src/main/resources/application.conf: -------------------------------------------------------------------------------- 1 | geotrellis.raster.gdal.options { 2 | GDAL_DISABLE_READDIR_ON_OPEN = "YES" 3 | CPL_VSIL_CURL_ALLOWED_EXTENSIONS = ".tif" 4 | GDAL_CACHEMAX = "0" 5 | } 6 | -------------------------------------------------------------------------------- /bench/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | [%thread] %-5level - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /bench/src/main/scala/geotrellis/server/TmsReificationBench.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server 18 | 19 | import geotrellis.server.vlm.geotiff._ 20 | import geotrellis.raster.MultibandTile 21 | import com.azavea.maml.ast._ 22 | import com.azavea.maml.error._ 23 | import com.azavea.maml.eval.ConcurrentInterpreter 24 | import cats.effect._ 25 | import org.typelevel.log4cats.slf4j.Slf4jLogger 26 | 27 | import org.openjdk.jmh.annotations._ 28 | 29 | import java.net.URI 30 | 31 | @BenchmarkMode(Array(Mode.AverageTime)) 32 | @State(Scope.Thread) 33 | class TmsReificationBench { 34 | 35 | implicit val logger = Slf4jLogger.getLogger[IO] 36 | import cats.effect.unsafe.implicits.global 37 | 38 | // NDVI 39 | val ast: Expression = 40 | Division(List(Subtraction(List(RasterVar("red"), RasterVar("nir"))), Addition(List(RasterVar("red"), RasterVar("nir"))))) 41 | 42 | // red, green, NIR bands which should have data for z/x/y 9/454/200 43 | val geotiffVars = Map( 44 | "red" -> GeoTiffNode(new URI("https://s3.amazonaws.com/geotrellis-test/daunnc/r-g-nir-with-ovrs.tif"), 0, None), 45 | "nir" -> GeoTiffNode(new URI("https://s3.amazonaws.com/geotrellis-test/daunnc/r-g-nir-with-ovrs.tif"), 2, None) 46 | ) 47 | 48 | val gdalVars = Map( 49 | "red" -> GeoTiffNode(new URI("gdal+https://s3.amazonaws.com/geotrellis-test/daunnc/r-g-nir-with-ovrs.tif"), 0, None), 50 | "nir" -> GeoTiffNode(new URI("gdal+https://s3.amazonaws.com/geotrellis-test/daunnc/r-g-nir-with-ovrs.tif"), 2, None) 51 | ) 52 | 53 | @Setup(Level.Trial) 54 | def setup(): Unit = {} 55 | 56 | @Benchmark 57 | def geotiffLayerTms: Interpreted[MultibandTile] = { 58 | val eval = LayerTms(IO(ast), IO(geotiffVars), ConcurrentInterpreter.DEFAULT[IO], None) 59 | eval(9, 454, 200).unsafeRunSync 60 | } 61 | 62 | @Benchmark 63 | def gdalLayerTms: Interpreted[MultibandTile] = { 64 | val eval = LayerTms(IO(ast), IO(gdalVars), ConcurrentInterpreter.DEFAULT[IO], None) 65 | eval(9, 454, 200).unsafeRunSync 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /core/src/main/scala/geotrellis/server/ExtentReification.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server 18 | 19 | import geotrellis.raster.{CellSize, MultibandTile, ProjectedRaster} 20 | import geotrellis.vector.Extent 21 | import cats.Contravariant 22 | 23 | trait ExtentReification[F[_], A] { 24 | def extentReification(self: A): (Extent, Option[CellSize]) => F[ProjectedRaster[MultibandTile]] 25 | } 26 | 27 | object ExtentReification { 28 | implicit def contravariantExtentReification[F[_]]: Contravariant[ExtentReification[F, *]] = 29 | new Contravariant[ExtentReification[F, *]] { 30 | def contramap[A, B](fa: ExtentReification[F, A])(f: B => A): ExtentReification[F, B] = { self => 31 | fa.extentReification(f(self)) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /core/src/main/scala/geotrellis/server/HasRasterExtents.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server 18 | 19 | import geotrellis.raster.RasterExtent 20 | 21 | import cats.Contravariant 22 | import cats.data.{NonEmptyList => NEL} 23 | 24 | trait HasRasterExtents[F[_], A] { 25 | def rasterExtents(self: A): F[NEL[RasterExtent]] 26 | } 27 | 28 | object HasRasterExtents { 29 | def apply[F[_], A](implicit ev: HasRasterExtents[F, A]): HasRasterExtents[F, A] = ev 30 | 31 | implicit def contravariantHasRasterExtents[F[_]]: Contravariant[HasRasterExtents[F, *]] = 32 | new Contravariant[HasRasterExtents[F, *]] { 33 | 34 | def contramap[A, B](fa: HasRasterExtents[F, A])(f: B => A): HasRasterExtents[F, B] = { self => 35 | fa.rasterExtents(f(self)) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/scala/geotrellis/server/TmsReification.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server 18 | 19 | import geotrellis.raster.{MultibandTile, ProjectedRaster} 20 | import cats.Contravariant 21 | 22 | trait TmsReification[F[_], A] { 23 | def tmsReification(self: A, buffer: Int): (Int, Int, Int) => F[ProjectedRaster[MultibandTile]] 24 | } 25 | 26 | object TmsReificiation { 27 | implicit def contravariantTmsReification[F[_]]: Contravariant[TmsReification[F, *]] = 28 | new Contravariant[TmsReification[F, *]] { 29 | def contramap[A, B](fa: TmsReification[F, A])(f: B => A): TmsReification[F, B] = { (self, buffer) => 30 | fa.tmsReification(f(self), buffer) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /core/src/main/scala/geotrellis/server/utils/Implicits.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.utils 18 | 19 | import java.io.{PrintWriter, StringWriter} 20 | 21 | trait Implicits { 22 | implicit class throwableExtensions[T <: Throwable](th: T) { 23 | def stackTraceString: String = { 24 | val writer = new StringWriter() 25 | th.printStackTrace(new PrintWriter(writer)) 26 | writer.toString 27 | } 28 | } 29 | } 30 | 31 | object Implicits extends Implicits 32 | -------------------------------------------------------------------------------- /core/src/main/scala/geotrellis/server/utils/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server 18 | 19 | package object utils extends utils.Implicits 20 | -------------------------------------------------------------------------------- /core/src/main/scala/geotrellis/server/vlm/geotiff/util/CacheRangeReader.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.vlm.geotiff.util 18 | 19 | import geotrellis.util._ 20 | 21 | // TODO: GeoTiffRasterSources should be able to accept custom range readers? 22 | case class CacheRangeReader(rr: RangeReader, cachedBytes: Array[Byte]) extends RangeReader { 23 | def totalLength: Long = rr.totalLength 24 | 25 | override def readRange(start: Long, length: Int): Array[Byte] = { 26 | val end = length + start 27 | if (end <= cachedBytes.length) java.util.Arrays.copyOfRange(cachedBytes, start.toInt, end.toInt) 28 | else rr.readRange(start, length) 29 | } 30 | 31 | protected def readClippedRange(start: Long, length: Int): Array[Byte] = rr.readRange(start, length) 32 | 33 | override def readAll(): Array[Byte] = rr.readAll() 34 | } 35 | -------------------------------------------------------------------------------- /core/src/main/scala/geotrellis/server/vlm/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server 18 | 19 | import cats.effect.IO 20 | 21 | package object vlm { 22 | implicit class OptionMethods[T](opt: Option[T]) { 23 | def toIO[E <: Throwable](e: => E): IO[T] = opt.fold(IO.raiseError[T](e))(IO.pure) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /core/src/main/scala/geotrellis/store/query/RepositoryM.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.store.query 18 | 19 | import cats.{~>, Applicative, Id, Monoid, MonoidK} 20 | import cats.syntax.apply._ 21 | import cats.syntax.applicative._ 22 | import cats.syntax.semigroupk._ 23 | 24 | trait RepositoryM[M[_], G[_], T] { self => 25 | def store: M[G[T]] 26 | def find(query: Query): M[G[T]] 27 | 28 | def mapK[F[_]](f: M ~> F): RepositoryM[F, G, T] = 29 | new RepositoryM[F, G, T] { 30 | def store: F[G[T]] = f(self.store) 31 | def find(query: Query): F[G[T]] = f(self.find(query)) 32 | } 33 | } 34 | 35 | object RepositoryM { 36 | def empty[M[_]: Applicative, G[_]: MonoidK, T]: RepositoryM[M, G, T] = 37 | new RepositoryM[M, G, T] { 38 | def store: M[G[T]] = MonoidK[G].empty[T].pure[M] 39 | def find(query: Query): M[G[T]] = store 40 | } 41 | 42 | implicit def repositoryMMonoid[M[_]: Applicative, G[_]: MonoidK, T]: Monoid[RepositoryM[M, G, T]] = 43 | new Monoid[RepositoryM[M, G, T]] { 44 | def empty: RepositoryM[M, G, T] = RepositoryM.empty[M, G, T] 45 | def combine(l: RepositoryM[M, G, T], r: RepositoryM[M, G, T]): RepositoryM[M, G, T] = 46 | new RepositoryM[M, G, T] { 47 | def store: M[G[T]] = l.store.map2(r.store)(_ <+> _) 48 | def find(query: Query): M[G[T]] = l.find(query).map2(r.find(query))(_ <+> _) 49 | } 50 | } 51 | 52 | implicit def repositoryMIdToApplicative[M[_]: Applicative, G[_], T](repository: RepositoryM[Id, G, T]): RepositoryM[M, G, T] = 53 | repository.mapK(λ[Id ~> M](_.pure[M])) 54 | } 55 | -------------------------------------------------------------------------------- /core/src/main/scala/geotrellis/store/query/vector/ProjectedGeometry.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.store.query.vector 18 | 19 | import geotrellis.proj4.CRS 20 | import geotrellis.vector.{io => _, _} 21 | import io.circe.generic.JsonCodec 22 | 23 | @JsonCodec 24 | case class ProjectedGeometry(geometry: Geometry, crs: CRS) { 25 | def reproject(dest: CRS): ProjectedGeometry = ProjectedGeometry(geometry.reproject(crs, dest), dest) 26 | 27 | def intersects(that: ProjectedGeometry): Boolean = geometry.intersects(that.reproject(crs).geometry) 28 | def covers(that: ProjectedGeometry): Boolean = geometry.covers(that.reproject(crs).geometry) 29 | def contains(that: ProjectedGeometry): Boolean = geometry.contains(that.reproject(crs).geometry) 30 | 31 | def toProjectedExtent: ProjectedExtent = ProjectedExtent(geometry.extent, crs) 32 | } 33 | 34 | object ProjectedGeometry { 35 | def apply(projectedExtent: ProjectedExtent): ProjectedGeometry = 36 | ProjectedGeometry(projectedExtent.extent.toPolygon(), projectedExtent.crs) 37 | 38 | def apply(extent: Extent, crs: CRS): ProjectedGeometry = apply(ProjectedExtent(extent, crs)) 39 | } 40 | -------------------------------------------------------------------------------- /core/src/test/resources/8x8.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geotrellis/geotrellis-server/dfc62f5e0335fbb6330a6bbe0ff7967226cfe0ad/core/src/test/resources/8x8.tif -------------------------------------------------------------------------------- /core/src/test/resources/resources.md: -------------------------------------------------------------------------------- 1 | | | cols | rows | extent | cell width | cell height | values | 2 | |---------|------|------|------------|------------|-------------|--------------| 3 | | 8x8.tif | 8 | 8 | 0, 0, 8, 8 | 1 | 1 | range(1, 64) | 4 | 5 | 6 | -------------------------------------------------------------------------------- /core/src/test/scala/geotrellis/server/LayerExtentTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server 18 | 19 | import cats.effect.IO 20 | import geotrellis.raster._ 21 | import geotrellis.vector._ 22 | import cats.syntax.option._ 23 | 24 | import org.scalatest.funsuite.AnyFunSuite 25 | import org.scalatest.matchers.should.Matchers 26 | 27 | class LayerExtentTest extends AnyFunSuite with Matchers { 28 | 29 | test("ability to read a selected extent") { 30 | val rt = ResourceTile("8x8.tif") 31 | val eval = LayerExtent.identity[IO, ResourceTile](rt) 32 | // We'll sample such that the bottom row (from 56 to 64) are excised from the result 33 | val sampled = eval(Extent(0, 1, 8, 8), CellSize(1, 1).some).unsafeRunSync 34 | val sample = sampled.toOption.get.band(0).toArray() 35 | val sampleSum = sample.sum 36 | assert(sampleSum == 1596, s"Expected sum of 1596, got $sampleSum") 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/src/test/scala/geotrellis/server/LayerHistogramTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server 18 | 19 | import cats.effect.IO 20 | 21 | import org.scalatest.funsuite.AnyFunSuite 22 | import org.scalatest.matchers.should.Matchers 23 | 24 | class LayerHistogramTest extends AnyFunSuite with Matchers { 25 | 26 | // This test works when the chosen sampling strategy is to work from the corners 27 | ignore("extents sampled from within overall extent") { 28 | val rt = ResourceTile("8x8.tif") 29 | val samples = 30 | LayerHistogram.concurrent[IO, ResourceTile](rt, 4).unsafeRunSync 31 | val sampleCount = samples.toOption.get.head.statistics.get.dataCells 32 | assert(sampleCount == 4, s"Expected 4 cells in histogram, got $sampleCount") 33 | } 34 | 35 | test( 36 | "histogram samples the total extent when budget is equal to the cell count" 37 | ) { 38 | val rt = ResourceTile("8x8.tif") 39 | val samples = 40 | LayerHistogram.concurrent[IO, ResourceTile](rt, 64).unsafeRunSync 41 | val sampleCount = samples.toOption.get.head.statistics.get.dataCells 42 | assert( 43 | sampleCount == 64, 44 | s"Expected 64 cells in histogram, got $sampleCount" 45 | ) 46 | } 47 | 48 | test("histogram samples the total extent when budget too big") { 49 | val rt = ResourceTile("8x8.tif") 50 | val samples = 51 | LayerHistogram.concurrent[IO, ResourceTile](rt, 128).unsafeRunSync 52 | val sampleCount = samples.toOption.get.head.statistics.get.dataCells 53 | assert( 54 | sampleCount == 64, 55 | s"Expected 64 cells in histogram, got $sampleCount" 56 | ) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /core/src/test/scala/geotrellis/server/NoDataHandlingTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server 18 | 19 | import geotrellis.raster._ 20 | 21 | import cats.effect.IO 22 | import com.azavea.maml.ast._ 23 | import com.azavea.maml.eval._ 24 | 25 | import org.scalatest.funsuite.AnyFunSuite 26 | import org.scalatest.matchers.should.Matchers 27 | 28 | class NoDataHandlingTest extends AnyFunSuite with Matchers with TileAsSourceImplicits { 29 | val expr = Addition(List(RasterVar("t1"), RasterVar("t2"))) 30 | val eval = LayerTms.curried(expr, ConcurrentInterpreter.DEFAULT[IO], None) 31 | 32 | test( 33 | "NODATA should be respected - user-defined, integer-based source celltype" 34 | ) { 35 | val t1 = IntUserDefinedNoDataArrayTile((1 to 100).toArray, 10, 10, IntUserDefinedNoDataCellType(1)) 36 | val t2 = IntUserDefinedNoDataArrayTile((1 to 100).toArray, 10, 10, IntUserDefinedNoDataCellType(1)) 37 | val paramMap = Map("t1" -> t1, "t2" -> t2) 38 | // We'll sample such that the bottom row (from 56 to 64) are excised from the result 39 | val res = eval(paramMap, 0, 0, 0).unsafeRunSync 40 | val tileRes = res.toOption.get.band(0) 41 | assert( 42 | tileRes.toArrayDouble.head.isNaN, 43 | s"Expected Double.NaN, got ${tileRes.toArrayDouble.head}" 44 | ) 45 | } 46 | 47 | test("NODATA should be respected - different source celltypes") { 48 | val t1 = IntUserDefinedNoDataArrayTile((1 to 100).toArray, 10, 10, IntUserDefinedNoDataCellType(1)) 49 | val t2 = DoubleUserDefinedNoDataArrayTile((1 to 100).map(_.toDouble).toArray, 10, 10, DoubleUserDefinedNoDataCellType(2.0)) 50 | val paramMap = Map("t1" -> t1, "t2" -> t2) 51 | // We'll sample such that the bottom row (from 56 to 64) are excised from the result 52 | val res = eval(paramMap, 0, 0, 0).unsafeRunSync 53 | val tileRes = res.toOption.get.band(0) 54 | assert( 55 | tileRes.toArrayDouble.apply(0).isNaN, 56 | s"Expected Double.NaN, got ${tileRes.toArrayDouble.head}" 57 | ) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /core/src/test/scala/geotrellis/server/TileAsSourceImplicits.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server 18 | 19 | import geotrellis.raster._ 20 | import geotrellis.proj4._ 21 | import geotrellis.layer._ 22 | import geotrellis.vector.Extent 23 | 24 | import cats.effect._ 25 | import cats.data.{NonEmptyList => NEL} 26 | 27 | // The entire point of this is to provide a *very unsafe* way to quickly test MAML evaluation 28 | // ZXY/Extent/CellSize/Etc are just ignored and the tile you pass in is what will be used 29 | trait TileAsSourceImplicits { 30 | val tmsLevels: Array[LayoutDefinition] = { 31 | val scheme = ZoomedLayoutScheme(WebMercator, 256) 32 | for (zoom <- 0 to 64) yield scheme.levelForZoom(zoom).layout 33 | }.toArray 34 | 35 | implicit val extentReification: ExtentReification[IO, Tile] = { self => (extent: Extent, _: Option[CellSize]) => 36 | IO.pure(ProjectedRaster(MultibandTile(self), extent, WebMercator)) 37 | } 38 | 39 | implicit val nodeRasterExtents: HasRasterExtents[IO, Tile] = { self => 40 | IO.pure(NEL.of(RasterExtent(Extent(0, 0, 100, 100), 1.0, 1.0, self.cols, self.rows))) 41 | } 42 | 43 | implicit val tmsReification: TmsReification[IO, Tile] = { (self, buffer) => (z: Int, x: Int, y: Int) => 44 | { 45 | val extent = tmsLevels(z).mapTransform.keyToExtent(x, y) 46 | IO.pure(ProjectedRaster(MultibandTile(self), extent, WebMercator)) 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /core/src/test/scala/geotrellis/server/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis 18 | 19 | import cats.effect.IO 20 | import cats.effect.unsafe.IORuntime 21 | import org.typelevel.log4cats.Logger 22 | import org.typelevel.log4cats.slf4j.Slf4jLogger 23 | 24 | package object server { 25 | implicit val logger: Logger[IO] = Slf4jLogger.getLogger 26 | implicit val runtime: IORuntime = cats.effect.unsafe.implicits.global 27 | } 28 | -------------------------------------------------------------------------------- /core/src/test/scala/geotrellis/store/query/EmptyMetadata.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.store.query 18 | 19 | import geotrellis.proj4.CRS 20 | import geotrellis.raster.{CellSize, CellType, GridExtent, RasterMetadata, SourceName} 21 | 22 | case class EmptyMetadata( 23 | name: SourceName, 24 | crs: CRS, 25 | bandCount: Int, 26 | cellType: CellType, 27 | gridExtent: GridExtent[Long], 28 | resolutions: List[CellSize], 29 | attributes: Map[String, String] 30 | ) extends RasterMetadata { 31 | def attributesForBand(band: Int): Map[String, String] = Map.empty 32 | } 33 | -------------------------------------------------------------------------------- /core/src/test/scala/geotrellis/store/query/EmptyRasterSource.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.store.query 18 | 19 | import geotrellis.proj4.CRS 20 | import geotrellis.vector.{Extent, ProjectedExtent} 21 | import geotrellis.raster.io.geotiff.OverviewStrategy 22 | import geotrellis.raster._ 23 | 24 | import java.time.ZonedDateTime 25 | 26 | case class EmptyRasterSource(identifier: String, projectedExtent: ProjectedExtent, time: Option[ZonedDateTime]) extends RasterSource { 27 | 28 | def targetCellType: Option[TargetCellType] = None 29 | 30 | def metadata: RasterMetadata = EmptyMetadata(name, crs, bandCount, cellType, gridExtent, resolutions, attributes) 31 | 32 | def reprojection( 33 | targetCRS: CRS, 34 | resampleTarget: ResampleTarget, 35 | method: ResampleMethod, 36 | strategy: OverviewStrategy 37 | ): RasterSource = throw new UnsupportedOperationException 38 | 39 | def resample(resampleTarget: ResampleTarget, method: ResampleMethod, strategy: OverviewStrategy): RasterSource = 40 | throw new UnsupportedOperationException 41 | 42 | def read(extent: Extent, bands: Seq[Int]): Option[Raster[MultibandTile]] = throw new UnsupportedOperationException 43 | 44 | def read(bounds: GridBounds[Long], bands: Seq[Int]): Option[Raster[MultibandTile]] = 45 | throw new UnsupportedOperationException 46 | 47 | def convert(targetCellType: TargetCellType): RasterSource = throw new UnsupportedOperationException 48 | 49 | def name: SourceName = StringName(identifier) 50 | 51 | def crs: CRS = projectedExtent.crs 52 | 53 | def bandCount: Int = 0 54 | 55 | def cellType: CellType = IntCellType 56 | 57 | def gridExtent: GridExtent[Long] = GridExtent(projectedExtent.extent, CellSize(1, 1)) 58 | 59 | def resolutions: List[CellSize] = Nil 60 | 61 | def attributes: Map[String, String] = time.fold(Map.empty[String, String])(t => Map("time" -> t.toString)) 62 | 63 | def attributesForBand(band: Int): Map[String, String] = Map.empty 64 | } 65 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | overlay-example: 4 | image: daunnc/openjdk-gdal:3.1-jdk8-slim 5 | command: /bin/sh -c "java -cp /opt/geotrellis-server-example.jar geotrellis.server.example.overlay.WeightedOverlayServer" 6 | ports: 7 | - "9000:9000" 8 | volumes: 9 | - $HOME/.aws:/root/.aws 10 | - ./example/target/scala-2.12/geotrellis-server-example.jar:/opt/geotrellis-server-example.jar 11 | persistence-example: 12 | image: daunnc/openjdk-gdal:3.1-jdk8-slim 13 | command: /bin/sh -c "java -cp /opt/geotrellis-server-example.jar geotrellis.server.example.persistence.PersistenceServer" 14 | ports: 15 | - "9000:9000" 16 | volumes: 17 | - $HOME/.aws:/root/.aws 18 | - ./example/target/scala-2.12/geotrellis-server-example.jar:/opt/geotrellis-server-example.jar 19 | ndvi-example: 20 | image: daunnc/openjdk-gdal:3.1-jdk8-slim 21 | command: /bin/sh -c "java -cp /opt/geotrellis-server-example.jar geotrellis.server.example.ndvi.NdviServer" 22 | ports: 23 | - "9000:9000" 24 | volumes: 25 | - $HOME/.aws:/root/.aws 26 | - ./example/target/scala-2.12/geotrellis-server-example.jar:/opt/geotrellis-server-example.jar 27 | gdal-ndvi-example: 28 | image: daunnc/openjdk-gdal:3.1-jdk8-slim 29 | command: /bin/sh -c "java -Djava.library.path=/usr/local/lib -cp /opt/geotrellis-server-example.jar geotrellis.server.example.ndvi.GdalNdviServer" 30 | ports: 31 | - "9000:9000" 32 | volumes: 33 | - $HOME/.aws:/root/.aws 34 | - ./example/target/scala-2.12/geotrellis-server-example.jar:/opt/geotrellis-server-example.jar 35 | -------------------------------------------------------------------------------- /effects/src/main/scala/geotrellis/raster/effects/RasterMetadataF.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.raster.effects 18 | 19 | import geotrellis.raster.SourceName 20 | import geotrellis.proj4.CRS 21 | import geotrellis.raster.{CellSize, CellType, GridBounds, GridExtent} 22 | import geotrellis.vector.Extent 23 | 24 | import cats._ 25 | import cats.syntax.functor._ 26 | import cats.syntax.apply._ 27 | 28 | abstract class RasterMetadataF[F[_]: Monad] { 29 | def name: SourceName 30 | def crs: F[CRS] 31 | def bandCount: F[Int] 32 | def cellType: F[CellType] 33 | def size: F[Long] = (cols, rows).mapN(_ * _) 34 | def dimensions: F[(Long, Long)] = (cols, rows).mapN((c, r) => (c, r)) 35 | def gridBounds: F[GridBounds[Long]] = (cols, rows).mapN { case (c, r) => GridBounds(0, 0, c - 1, r - 1) } 36 | def cellSize: F[CellSize] = gridExtent.map(_.cellSize) 37 | def gridExtent: F[GridExtent[Long]] 38 | def resolutions: F[List[CellSize]] 39 | def extent: F[Extent] = gridExtent.map(_.extent) 40 | def cols: F[Long] = gridExtent.map(_.cols) 41 | def rows: F[Long] = gridExtent.map(_.rows) 42 | 43 | /** 44 | * Return the "base" metadata, usually it is a zero band metadata, a metadata that is valid for the entire source and for the zero band 45 | */ 46 | def attributes: F[Map[String, String]] 47 | 48 | /** 49 | * Return a per band metadata 50 | */ 51 | def attributesForBand(band: Int): F[Map[String, String]] 52 | } 53 | -------------------------------------------------------------------------------- /effects/src/main/scala/geotrellis/raster/effects/UnsafeLift.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.raster.effects 18 | 19 | import cats.Id 20 | import cats.effect.IO 21 | 22 | import scala.concurrent.{ExecutionContext, Future} 23 | import scala.util.{Failure, Success, Try} 24 | 25 | /** 26 | * Type class that allows to handle unsafe calls 27 | */ 28 | trait UnsafeLift[F[_]] { 29 | def apply[A](value: => A): F[A] 30 | } 31 | 32 | object UnsafeLift { 33 | def apply[F[_]: UnsafeLift]: UnsafeLift[F] = implicitly[UnsafeLift[F]] 34 | 35 | implicit val liftId: UnsafeLift[Id] = new UnsafeLift[Id] { 36 | def apply[A](value: => A): Id[A] = value 37 | } 38 | 39 | implicit val liftIO: UnsafeLift[IO] = new UnsafeLift[IO] { 40 | def apply[A](value: => A): IO[A] = IO(value) 41 | } 42 | 43 | implicit def liftFuture(implicit ec: ExecutionContext): UnsafeLift[Future] = new UnsafeLift[Future] { 44 | def apply[A](value: => A): Future[A] = Future(value) 45 | } 46 | 47 | implicit val liftOption: UnsafeLift[Option] = new UnsafeLift[Option] { 48 | def apply[A](value: => A): Option[A] = Try(value).toOption 49 | } 50 | 51 | implicit val liftEither: UnsafeLift[Either[Throwable, *]] = new UnsafeLift[Either[Throwable, *]] { 52 | def apply[A](value: => A): Either[Throwable, A] = Try(value) match { 53 | case Success(value) => Right(value) 54 | case Failure(e) => Left(e) 55 | } 56 | } 57 | 58 | implicit val liftTry: UnsafeLift[Try] = new UnsafeLift[Try] { 59 | def apply[A](value: => A): Try[A] = Try(value) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /example/src/main/resources/application.conf: -------------------------------------------------------------------------------- 1 | http { 2 | "interface": "0.0.0.0" 3 | "interface": ${?HTTP_INTERFACE} 4 | "port": 9000 5 | "port": ${?HTTP_PORT} 6 | } 7 | 8 | auth { 9 | "signing-key": "REPLACEME" 10 | "signing-key": ${?SIGNING_KEY} 11 | } 12 | 13 | gdal.cache { 14 | maximumSize: 1000 15 | enableDefaultRemovalListener: true 16 | valuesType: Weak 17 | enabled: true 18 | withShutdownHook: true 19 | } 20 | 21 | -------------------------------------------------------------------------------- /example/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | [%thread] %-5level - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /example/src/main/resources/overlay-demo/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geotrellis/geotrellis-server/dfc62f5e0335fbb6330a6bbe0ff7967226cfe0ad/example/src/main/resources/overlay-demo/favicon.ico -------------------------------------------------------------------------------- /example/src/main/resources/overlay-demo/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 0; 3 | overflow: hidden; 4 | font-family: helvetica, arial, sans-serif; 5 | } 6 | 7 | #map { 8 | position: absolute; 9 | top: 0px; 10 | height: 100%; 11 | left: 300px; 12 | width: 100%; 13 | } 14 | 15 | #sidebar { 16 | position: absolute; 17 | top: 0px; 18 | height: 100%; 19 | left: 0px; 20 | width: 300px; 21 | overflow-y: auto; 22 | padding: 5px; 23 | } 24 | 25 | .cogform { 26 | border: 1px solid black; 27 | background-color: #ffffff; 28 | padding: 5px; 29 | margin-bottom: 5px 30 | } 31 | 32 | .custom-range { 33 | padding: 10px; 34 | width: 85%; 35 | } 36 | 37 | .divider { 38 | height: 1px; 39 | width: 100%; 40 | display: block; 41 | margin: 9px 0; 42 | overflow: hidden; 43 | background-color: #e5e5e5; 44 | } 45 | 46 | .btn-bottom { 47 | margin: 5px; 48 | width: 45%; 49 | } 50 | 51 | #sidebody { 52 | overflow: scroll; 53 | height: 80%; 54 | } 55 | 56 | #bottom { 57 | background: #ffffff; 58 | z-index: 10; 59 | } 60 | -------------------------------------------------------------------------------- /example/src/main/scala/geotrellis/server/example/ExampleConf.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.example 18 | 19 | import cats.effect.{Resource, Sync} 20 | import com.typesafe.config.ConfigFactory 21 | import pureconfig._ 22 | import pureconfig.generic.auto._ 23 | import pureconfig.module.catseffect.syntax._ 24 | 25 | case class ExampleConf(http: ExampleConf.Http, auth: ExampleConf.Auth) 26 | 27 | object ExampleConf { 28 | case class Http(interface: String, port: Int) 29 | case class Auth(signingKey: String) 30 | 31 | lazy val load: ExampleConf = ConfigSource.default.loadOrThrow[ExampleConf] 32 | def loadF[F[_]: Sync](configPath: Option[String]): F[ExampleConf] = 33 | ConfigSource 34 | .fromConfig(ConfigFactory.load(configPath.getOrElse("application.conf"))) 35 | .loadF[F, ExampleConf] 36 | 37 | def loadResourceF[F[_]: Sync](configPath: Option[String]): Resource[F, ExampleConf] = Resource.eval(loadF[F](configPath)) 38 | } 39 | -------------------------------------------------------------------------------- /example/src/main/scala/geotrellis/server/example/persistence/MamlStore.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.example.persistence 18 | 19 | import com.azavea.maml.ast.Expression 20 | 21 | import java.util.UUID 22 | 23 | trait MamlStore[F[_], A] { 24 | def getMaml(self: A, key: UUID): F[Option[Expression]] 25 | def putMaml(self: A, key: UUID, maml: Expression): F[Unit] 26 | } 27 | 28 | object MamlStore { 29 | def apply[F[_], A](implicit ev: MamlStore[F, A]) = ev 30 | 31 | /** 32 | * This exception should be thrown when a MAML expression can't be found in a putative MamlStore implementer 33 | */ 34 | case class ExpressionNotFound(key: UUID) extends Exception(s"No expression found at $key") 35 | } 36 | -------------------------------------------------------------------------------- /example/src/main/scala/geotrellis/server/example/persistence/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.example 18 | 19 | import com.azavea.maml.ast.Expression 20 | 21 | import cats.effect.IO 22 | import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap 23 | 24 | import java.util.UUID 25 | 26 | package object persistence { 27 | type HashMapMamlStore = ConcurrentLinkedHashMap[UUID, Expression] 28 | implicit val inMemMamlStore: MamlStore[IO, ConcurrentLinkedHashMap[UUID, Expression]] = 29 | new MamlStore[IO, ConcurrentLinkedHashMap[UUID, Expression]] { 30 | def getMaml(self: ConcurrentLinkedHashMap[UUID, Expression], key: UUID): IO[Option[Expression]] = 31 | IO(Option(self.get(key))) 32 | 33 | def putMaml(self: ConcurrentLinkedHashMap[UUID, Expression], key: UUID, maml: Expression): IO[Unit] = IO(self.put(key, maml)) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ogc-example/docs/README.md: -------------------------------------------------------------------------------- 1 | # GeoTrellis OGC Services 2 | 3 | This is a scala project that is able to serve contents of Geotrellis 4 | indexed layers and transformations of said layers which can be described 5 | with the help of the 6 | [Map Algebra Modeling Language (MAML)](https://github.com/geotrellis/maml) 7 | in accordance with certain OGC standards. The currently supported standards 8 | are [Web Coverage Service](http://www.opengeospatial.org/standards/wcs), 9 | [Web Map Service](https://www.opengeospatial.org/standards/wms), 10 | and [Web Map Tile Service](https://www.opengeospatial.org/standards/wmts). 11 | 12 | Each layer presented by a given service requires that appropriate 13 | configuration be provided *at the start of the application*. To get 14 | started with a demonstration configuration and demonstration layers, 15 | check out [usage.md](./usage.md). Once comfortable with starting the 16 | server and interacting with the resources it provides, modification 17 | of the configuration to serve your own layers is a logical next step 18 | and is documented in [conf.md](./conf.md). If the evaluation 19 | of complex layers with MAML requires special tuning, look to 20 | [maml.md](./maml.md) for an explanation of how MAML ASTs (which are 21 | used directly in the configuration file to support complex, map algebra 22 | layers) are interpreted. 23 | 24 | ## Contents 25 | 26 | - [Starting the server](usage.md) 27 | - [Modifying configuration](conf.md) 28 | - [Custom MAML](maml.md) 29 | - [Available MAML operations](maml-operations.md) 30 | -------------------------------------------------------------------------------- /ogc-example/docs/img/ogcserver-in-console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geotrellis/geotrellis-server/dfc62f5e0335fbb6330a6bbe0ff7967226cfe0ad/ogc-example/docs/img/ogcserver-in-console.png -------------------------------------------------------------------------------- /ogc-example/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /ogc-example/src/main/scala/geotrellis/server/ogc/ExtendedParameters.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc 18 | 19 | import com.azavea.maml.ast.Expression 20 | import geotrellis.server.ogc.params.ParamMap 21 | 22 | import cats.syntax.apply._ 23 | import cats.instances.option._ 24 | 25 | object ExtendedParameters { 26 | val extendedParametersBinding: Option[ParamMap => Option[Expression => Expression]] = Option { p => 27 | (FocalParameters.extendedParametersBinding, RGBParameters.extendedParametersBinding).tupled.flatMap { case (l, r) => 28 | (l(p), r(p)).mapN(_ andThen _) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ogc-example/src/main/scala/geotrellis/server/ogc/ToMediaType.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc 18 | 19 | import org.http4s.MediaType 20 | 21 | object ToMediaType { 22 | def apply(format: OutputFormat): MediaType = format match { 23 | case OutputFormat.Png(_) => MediaType.image.png 24 | case OutputFormat.Jpg => MediaType.image.jpeg 25 | case OutputFormat.GeoTiff => MediaType.image.tiff 26 | } 27 | 28 | def apply(format: InfoFormat): MediaType = format match { 29 | case InfoFormat.Json => MediaType.application.json 30 | case InfoFormat.XML => MediaType.text.xml 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ogc-example/src/main/scala/geotrellis/server/ogc/conf/Conf.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc.conf 18 | 19 | import pureconfig._ 20 | import pureconfig.generic.auto._ 21 | import pureconfig.module.catseffect.syntax._ 22 | import cats.effect.{Resource, Sync} 23 | import com.typesafe.config.ConfigFactory 24 | import scalaxb.DataRecord 25 | 26 | import java.io.File 27 | 28 | /** 29 | * The top level configuration object for all layers and styles. This object should be supplied by the various sections in the provided configuration. 30 | * If the application won't start because of a bad configuration, start here and recursively descend through properties verifying that the 31 | * configuration file provides sufficient information. 32 | * 33 | * Complex types can be read with the help of [[ConfigReader]] instances. See package.scala and 34 | * https://pureconfig.github.io/docs/supporting-new-types.html for more examples and explanation of ConfigReader instances. 35 | */ 36 | case class Conf( 37 | layers: Map[String, OgcSourceConf], 38 | wms: Option[WmsConf], 39 | wmts: Option[WmtsConf], 40 | wcs: Option[WcsConf] 41 | ) 42 | 43 | object Conf { 44 | lazy val load: Conf = ConfigSource.default.loadOrThrow[Conf] 45 | def loadF[F[_]: Sync](configPath: Option[String]): F[Conf] = 46 | configPath match { 47 | case Some(path) => ConfigSource.fromConfig(ConfigFactory.parseFile(new File(path))).loadF[F, Conf] 48 | case _ => ConfigSource.default.loadF[F, Conf] 49 | } 50 | 51 | def loadResourceF[F[_]: Sync](configPath: Option[String]): Resource[F, Conf] = 52 | Resource.eval(loadF[F](configPath)) 53 | 54 | // This is a work-around to use pureconfig to read scalaxb generated case classes 55 | // DataRecord should never be specified from configuration, this satisfied the resolution 56 | // ConfigReader should be the containing class if DataRecord values need to be set 57 | implicit def dataRecordReader: ConfigReader[DataRecord[Any]] = null 58 | } 59 | -------------------------------------------------------------------------------- /ogc-example/src/main/scala/geotrellis/server/ogc/conf/OgcServiceConf.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc.conf 18 | 19 | import geotrellis.server.ogc 20 | import geotrellis.server.ogc.{ows, MapAlgebraSource, OgcSource, RasterOgcSource} 21 | import geotrellis.server.ogc.wms.WmsParentLayerMeta 22 | import geotrellis.server.ogc.wmts.GeotrellisTileMatrixSet 23 | import geotrellis.store.query.Repository 24 | import geotrellis.proj4.CRS 25 | 26 | /** 27 | * Each service has its own unique configuration requirements (see the below instances) but share certain basic behaviors related to layer management. 28 | * This trait encodes those expectations 29 | */ 30 | sealed trait OgcServiceConf { 31 | def layerDefinitions: List[OgcSourceConf] 32 | def layerSources(rasterOgcSources: List[RasterOgcSource]): Repository[OgcSource] = { 33 | val rasterLayers: List[RasterOgcSource] = layerDefinitions.collect { case rsc: RasterSourceConf => rsc.toLayer } 34 | val mapAlgebraLayers: List[MapAlgebraSource] = layerDefinitions.collect { case masc: MapAlgebraSourceConf => masc.model(rasterOgcSources) } 35 | ogc.OgcSourceRepository(rasterLayers ++ mapAlgebraLayers) 36 | } 37 | } 38 | 39 | /** 40 | * WMS Service configuration 41 | */ 42 | case class WmsConf( 43 | parentLayerMeta: WmsParentLayerMeta, 44 | serviceMetadata: opengis.wms.Service, 45 | layerDefinitions: List[OgcSourceConf] 46 | ) extends OgcServiceConf 47 | 48 | /** 49 | * WMTS Service configuration 50 | */ 51 | case class WmtsConf( 52 | serviceMetadata: ows.ServiceMetadata, 53 | layerDefinitions: List[OgcSourceConf], 54 | tileMatrixSets: List[GeotrellisTileMatrixSet] 55 | ) extends OgcServiceConf 56 | 57 | /** 58 | * WCS Service configuration 59 | */ 60 | case class WcsConf( 61 | serviceMetadata: ows.ServiceMetadata, 62 | layerDefinitions: List[OgcSourceConf], 63 | supportedProjections: List[CRS] 64 | ) extends OgcServiceConf 65 | -------------------------------------------------------------------------------- /ogc-example/src/main/scala/geotrellis/server/ogc/conf/StyleConf.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc.conf 18 | 19 | import geotrellis.server.ogc.style._ 20 | 21 | import geotrellis.raster.render.{ColorMap, ColorRamp} 22 | 23 | /** 24 | * The trait implemented by different style configuration options 25 | */ 26 | sealed trait StyleConf { 27 | def name: String 28 | def title: String 29 | def toStyle: OgcStyle 30 | } 31 | 32 | /** 33 | * Styling in which a color scheme is known but not the values that these colors should map to 34 | */ 35 | final case class ColorRampConf( 36 | name: String, 37 | title: String, 38 | colors: ColorRamp, 39 | stops: Option[Int], 40 | minRender: Option[Double], 41 | maxRender: Option[Double], 42 | clampWithColor: Boolean = false, 43 | legends: List[LegendModel] = Nil 44 | ) extends StyleConf { 45 | def toStyle: OgcStyle = 46 | ColorRampStyle(name, title, colors, stops, minRender, maxRender, clampWithColor, legends) 47 | } 48 | 49 | /** 50 | * Styling in which both a color scheme and the data-to-color mapping is known 51 | */ 52 | final case class ColorMapConf( 53 | name: String, 54 | title: String, 55 | colorMap: ColorMap, 56 | legends: List[LegendModel] = Nil 57 | ) extends StyleConf { 58 | def toStyle: OgcStyle = 59 | ColorMapStyle(name, title, colorMap, legends) 60 | } 61 | 62 | final case class InterpolatedColorMapConf( 63 | name: String, 64 | title: String, 65 | colorMap: InterpolatedColorMap, 66 | legends: List[LegendModel] = Nil 67 | ) extends StyleConf { 68 | def toStyle: OgcStyle = 69 | InterpolatedColorMapStyle(name, title, colorMap, legends) 70 | } 71 | 72 | final case class RGBStyleConf(name: String = "RGB", title: String = "Default RGB rendering", legends: List[LegendModel] = Nil) extends StyleConf { 73 | def toStyle: OgcStyle = RGBStyle(name, title, legends) 74 | } 75 | -------------------------------------------------------------------------------- /ogc-example/src/test/scala/geotrellis/server/ogc/conf/ColorMapConfigurationSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc 18 | 19 | import geotrellis.server.ogc.conf._ 20 | import geotrellis.raster.render.ColorMap 21 | 22 | import pureconfig.ConfigSource 23 | 24 | import org.scalatest.funspec.AnyFunSpec 25 | import org.scalatest.matchers.should.Matchers 26 | 27 | class ColorMapConfigurationSpec extends AnyFunSpec with Matchers { 28 | 29 | describe("ColorMap Configuration") { 30 | it("should produce the same colormap regardless of key type") { 31 | val quoted = 32 | """{"-1.0": 0x1947B0FF,"-0.7": 0x3961B7FF,"-0.5": 0x5A7BBFFF,"-0.4": 0x7B95C6FF,"-0.3": 0x9CB0CEFF,"-0.2": 0xBDCAD5FF,"-0.1": 0xDEE4DDFF,"0": 0xFFFFE5FF,"0.1": 0xDAE4CAFF,"0.2": 0xB6C9AFFF,"0.3": 0x91AF94FF,"0.4": 0x6D9479FF,"0.5": 0x487A5EFF,"0.7": 0x245F43FF,"1.0": 0x004529FF}""" 33 | val unquoted = 34 | """{-1.0: 0x1947B0FF,-0.7: 0x3961B7FF,-0.5: 0x5A7BBFFF,-0.4: 0x7B95C6FF,-0.3: 0x9CB0CEFF,-0.2: 0xBDCAD5FF,-0.1: 0xDEE4DDFF, 0.0: 0xFFFFE5FF,0.1: 0xDAE4CAFF,0.2: 0xB6C9AFFF,0.3: 0x91AF94FF,0.4: 0x6D9479FF,0.5: 0x487A5EFF,0.7: 0x245F43FF,1.0: 0x004529FF}""" 35 | 36 | val quotedSource = ConfigSource.string(quoted) 37 | val unquotedSource = ConfigSource.string(unquoted) 38 | 39 | quotedSource.load[ColorMap].right.get.colors shouldBe (unquotedSource.load[ColorMap].right.get.colors) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ogc/src/main/scala/geotrellis/server/ogc/FeatureCollection.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc 18 | 19 | import geotrellis.vector.io.json.GeometryFormats 20 | import geotrellis.vector.{Feature, Geometry} 21 | import io.circe.{Encoder, Json} 22 | import io.circe.syntax._ 23 | 24 | /** 25 | * TODO: consider moving it into GeoTrellis 26 | */ 27 | case class FeatureCollection[G <: Geometry, D](list: List[Feature[G, D]]) 28 | 29 | object FeatureCollection { 30 | private def writeFeatureCollectionJson[G <: Geometry, D: Encoder](obj: Feature[G, D]): Json = 31 | Json.obj( 32 | "type" -> "Feature".asJson, 33 | "geometry" -> GeometryFormats.geometryEncoder(obj.geom), 34 | "properties" -> obj.data.asJson 35 | ) 36 | 37 | implicit def featureCollectionEncoder[G <: Geometry, D: Encoder]: Encoder[FeatureCollection[G, D]] = 38 | Encoder.encodeJson.contramap { fc => 39 | Json.obj( 40 | "type" -> "FeatureCollection".asJson, 41 | "features" -> fc.list.map(writeFeatureCollectionJson[G, D]).asJson 42 | ) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ogc/src/main/scala/geotrellis/server/ogc/InfoFormat.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc 18 | 19 | import scala.util.Try 20 | 21 | sealed trait InfoFormat { 22 | def name: String 23 | override def toString: String = name 24 | } 25 | 26 | object InfoFormat { 27 | case object XML extends InfoFormat { val name: String = "text/xml" } 28 | case object Json extends InfoFormat { val name: String = "application/json" } 29 | 30 | def fromStringUnsafe(str: String): InfoFormat = 31 | str match { 32 | case XML.name => XML 33 | case Json.name => Json 34 | } 35 | 36 | def fromString(str: String): Option[InfoFormat] = Try(fromStringUnsafe(str)).toOption 37 | 38 | val all: List[String] = List(XML, Json).map(_.name) 39 | } 40 | -------------------------------------------------------------------------------- /ogc/src/main/scala/geotrellis/server/ogc/OgcTimeDefault.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc 18 | 19 | import cats.data.NonEmptyList 20 | import io.circe.Decoder 21 | import cats.syntax.either._ 22 | 23 | import java.time.ZonedDateTime 24 | import scala.util.Try 25 | 26 | sealed trait OgcTimeDefault { 27 | lazy val name: String = getClass.getName.split("\\$").last.toLowerCase 28 | } 29 | 30 | object OgcTimeDefault { 31 | case object Oldest extends OgcTimeDefault 32 | case object Newest extends OgcTimeDefault 33 | case class Time(time: ZonedDateTime) extends OgcTimeDefault 34 | 35 | def fromString(str: String): OgcTimeDefault = str match { 36 | case Oldest.name => Oldest 37 | case Newest.name => Newest 38 | case _ => Time(ZonedDateTime.parse(str)) 39 | } 40 | 41 | implicit val ogcTimeDefaultDecoder: Decoder[OgcTimeDefault] = Decoder[String].emap { s => 42 | Try(fromString(s)).toEither.leftMap(_.getMessage) 43 | } 44 | 45 | implicit class OgcTimeDefaultOps(val self: OgcTimeDefault) extends AnyVal { 46 | def selectTime(list: NonEmptyList[ZonedDateTime]): ZonedDateTime = { 47 | lazy val sorted = list.sorted 48 | self match { 49 | case OgcTimeDefault.Oldest => sorted.head 50 | case OgcTimeDefault.Newest => sorted.last 51 | case OgcTimeDefault.Time(t) => t 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /ogc/src/main/scala/geotrellis/server/ogc/OgcTimeFormat.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc 18 | 19 | import io.circe.Codec 20 | import io.circe.generic.extras.Configuration 21 | import io.circe.generic.extras.semiauto.deriveEnumerationCodec 22 | 23 | /** 24 | * ADT to change [[OgcTime]] internal representation 25 | */ 26 | sealed trait OgcTimeFormat 27 | 28 | object OgcTimeFormat { 29 | 30 | /** 31 | * Represent [[OgcTime]] as [[OgcTimePositions]]. 32 | */ 33 | case object Positions extends OgcTimeFormat 34 | 35 | /** 36 | * Represent [[OgcTime]] as [[OgcTimeInterval]]. 37 | */ 38 | case object Interval extends OgcTimeFormat 39 | 40 | /** 41 | * Don't change the internal [[OgcTime]] representation. 42 | */ 43 | case object Default extends OgcTimeFormat 44 | 45 | implicit private val config: Configuration = Configuration.default.copy(transformConstructorNames = _.toLowerCase) 46 | implicit val ogcTimeFormatCodec: Codec[OgcTimeFormat] = deriveEnumerationCodec 47 | } 48 | -------------------------------------------------------------------------------- /ogc/src/main/scala/geotrellis/server/ogc/URN.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc 18 | 19 | import geotrellis.proj4.CRS 20 | 21 | object URN { 22 | def fromCrs(crs: CRS): Option[String] = crs.epsgCode.map(code => s"urn:ogc:def:crs:EPSG::$code") 23 | def unsafeFromCrs(crs: CRS): String = 24 | fromCrs(crs).getOrElse(throw new Exception(s"Unrecognized CRS: $crs. Unable to construct URN")) 25 | } 26 | -------------------------------------------------------------------------------- /ogc/src/main/scala/geotrellis/server/ogc/gml/GmlDataRecord.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc.gml 18 | 19 | import cats.syntax.option._ 20 | import scalaxb.{CanWriteXML, DataRecord} 21 | 22 | import scala.reflect.{classTag, ClassTag} 23 | 24 | object GmlDataRecord { 25 | def apply[T: CanWriteXML: ClassTag](value: T): DataRecord[T] = 26 | apply[T](classTag[T].toString.split("\\.").lastOption.flatMap(_.split("Type").headOption), value) 27 | 28 | def apply[T: CanWriteXML](key: Option[String], value: T): DataRecord[T] = 29 | DataRecord("gml".some, key.map(k => s"gml:$k"), value) 30 | 31 | def apply[T: CanWriteXML](key: String, value: T): DataRecord[T] = 32 | DataRecord("gml".some, s"gml:$key".some, value) 33 | } 34 | -------------------------------------------------------------------------------- /ogc/src/main/scala/geotrellis/server/ogc/ows/OwsDataRecord.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc.ows 18 | 19 | import cats.syntax.option._ 20 | import scalaxb.{CanWriteXML, DataRecord} 21 | 22 | import scala.reflect.{classTag, ClassTag} 23 | 24 | /** 25 | * A function that reduces boilerplate by generating a namespace and a key for a common ows [[DataRecord]] 26 | */ 27 | object OwsDataRecord { 28 | def apply[T: CanWriteXML: ClassTag](value: T): DataRecord[T] = 29 | apply[T](classTag[T].toString.split("\\.").lastOption.flatMap(_.split("Type").headOption), value) 30 | 31 | def apply[T: CanWriteXML](key: Option[String], value: T): DataRecord[T] = 32 | DataRecord("ows".some, key.map(k => s"ows:$k"), value) 33 | 34 | def apply[T: CanWriteXML](key: String, value: T): DataRecord[T] = 35 | DataRecord("ows".some, s"ows:$key".some, value) 36 | } 37 | -------------------------------------------------------------------------------- /ogc/src/main/scala/geotrellis/server/ogc/ows/ServiceMetadata.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc.ows 18 | 19 | import java.net.URI 20 | 21 | /** 22 | * Service-level metadata; roughly corresponds to ows service identification and service providers 23 | */ 24 | case class ServiceMetadata( 25 | identification: Identification, 26 | provider: Provider 27 | ) 28 | 29 | case class Identification( 30 | title: String, 31 | description: String, 32 | keywords: List[String], 33 | profile: List[URI], 34 | fees: Option[String], 35 | accessConstraints: List[String] 36 | ) 37 | 38 | case class Provider( 39 | name: String, 40 | site: Option[String], 41 | contact: Option[ResponsiblePartySubset] 42 | ) 43 | 44 | /** 45 | * corresponds roughly to opengis.ows.ResponsiblePartySubsetType 46 | */ 47 | case class ResponsiblePartySubset( 48 | name: Option[String], 49 | position: Option[String], 50 | role: Option[String] 51 | ) 52 | -------------------------------------------------------------------------------- /ogc/src/main/scala/geotrellis/server/ogc/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server 18 | 19 | import geotrellis.proj4.CRS 20 | import geotrellis.raster.{CellSize, Histogram, MultibandTile, Raster} 21 | import geotrellis.server.ogc.style.OgcStyle 22 | import geotrellis.vector.Extent 23 | import cats.Order 24 | import org.threeten.extra.PeriodDuration 25 | import jp.ne.opt.chronoscala.Imports._ 26 | 27 | import java.time.ZonedDateTime 28 | 29 | package object ogc { 30 | implicit val ZonedDateTimeOrder: Order[ZonedDateTime] = Order.fromOrdering[ZonedDateTime] 31 | 32 | implicit class ExtentOps(val self: Extent) extends AnyVal { 33 | def swapXY: Extent = Extent(xmin = self.ymin, ymin = self.xmin, xmax = self.ymax, ymax = self.xmax) 34 | def buffer(cellSize: CellSize): Extent = self.buffer(cellSize.width / 2, cellSize.height / 2) 35 | def buffer(cellSize: Option[CellSize]): Extent = cellSize.fold(self)(buffer) 36 | } 37 | 38 | implicit class RasterOps(val self: Raster[MultibandTile]) extends AnyVal { 39 | def render(crs: CRS, maybeStyle: Option[OgcStyle], format: OutputFormat, hists: List[Histogram[Double]]): Array[Byte] = 40 | if (self.tile.bandCount == 1) Render.singleband(self, crs, maybeStyle, format, hists) 41 | else Render.multiband(self, crs, maybeStyle, format, hists) 42 | } 43 | 44 | implicit class PeriodDurationOps(val self: PeriodDuration) extends AnyVal { 45 | def toMillis: Long = { 46 | val p = self.getPeriod 47 | p.getYears.toLong * 365 * 24 * 3600 * 1000 + 48 | p.getMonths.toLong * 30 * 24 * 3600 * 1000 + 49 | p.getDays * 24 * 3600 * 100 + self.getDuration.toMillis 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ogc/src/main/scala/geotrellis/server/ogc/params/CRSUtils.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc.params 18 | 19 | import geotrellis.proj4._ 20 | import cats.data.{Validated, ValidatedNel} 21 | import Validated._ 22 | 23 | import scala.util.{Success, Try} 24 | 25 | object CRSUtils { 26 | 27 | /** 28 | * Converts an OGC URN string representing a CRS into a [[geotrellis.proj4.CRS]] instance. TODO: Move this into geotrellis.proj4 (or future 29 | * geotrellis.crs) 30 | */ 31 | def ogcToCRS(crsDesc: String): ValidatedNel[ParamError, CRS] = { 32 | val code = crsDesc.trim.toLowerCase 33 | if (code == "wgs84(dd)") Valid(LatLng).toValidatedNel 34 | else if (code.startsWith("urn:ogc:def:crs:epsg::")) 35 | Try(CRS.fromEpsgCode(code.split("::")(1).toInt)) match { 36 | case Success(crs) => Valid(crs).toValidatedNel 37 | case _ => Invalid(ParamError.CrsParseError(crsDesc)).toValidatedNel 38 | } 39 | else Valid(LatLng).toValidatedNel // TODO: Complete implementation WCS codes (urn:ogc:*) -> CRS 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ogc/src/main/scala/geotrellis/server/ogc/style/ClipDefinition.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc.style 18 | 19 | import org.log4s._ 20 | import cats.syntax.option._ 21 | 22 | abstract class ClipDefinition(repr: String) extends Product with Serializable 23 | case object ClipNone extends ClipDefinition("clip-none") 24 | case object ClipLeft extends ClipDefinition("clip-left") 25 | case object ClipRight extends ClipDefinition("clip-right") 26 | case object ClipBoth extends ClipDefinition("clip-both") 27 | 28 | object ClipDefinition { 29 | private val logger = getLogger 30 | 31 | def fromString(str: String): Option[ClipDefinition] = 32 | str match { 33 | case "clip-none" => ClipNone.some 34 | case "clip-left" => ClipLeft.some 35 | case "clip-right" => ClipRight.some 36 | case "clip-both" => ClipBoth.some 37 | case _ => 38 | logger.warn(s"Unable to deserialize string as ClipDefinition $str") 39 | None 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /ogc/src/main/scala/geotrellis/server/ogc/style/ColorMapStyle.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc.style 18 | 19 | import geotrellis.server.ogc._ 20 | 21 | import geotrellis.proj4.CRS 22 | import geotrellis.raster._ 23 | import geotrellis.raster.histogram.Histogram 24 | import geotrellis.raster.io.geotiff.GeoTiff 25 | import geotrellis.raster.render.ColorMap 26 | 27 | case class ColorMapStyle(name: String, title: String, colorMap: ColorMap, legends: List[LegendModel] = Nil) extends OgcStyle { 28 | def renderRaster( 29 | raster: Raster[MultibandTile], 30 | crs: CRS, 31 | format: OutputFormat, 32 | hists: List[Histogram[Double]] 33 | ): Array[Byte] = 34 | format match { 35 | case format: OutputFormat.Png => format.render(raster.tile.band(bandIndex = 0), colorMap) 36 | case OutputFormat.Jpg => raster.tile.band(bandIndex = 0).renderJpg(colorMap).bytes 37 | case OutputFormat.GeoTiff => GeoTiff(raster.mapTile(_.band(bandIndex = 0).color(colorMap)), crs).toCloudOptimizedByteArray 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ogc/src/main/scala/geotrellis/server/ogc/style/InterpolatedColorMapStyle.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc.style 18 | 19 | import geotrellis.server.ogc._ 20 | 21 | import geotrellis.proj4.CRS 22 | import geotrellis.raster._ 23 | import geotrellis.raster.histogram.Histogram 24 | import geotrellis.raster.io.geotiff.GeoTiff 25 | 26 | case class InterpolatedColorMapStyle( 27 | name: String, 28 | title: String, 29 | colorMap: InterpolatedColorMap, 30 | legends: List[LegendModel] = Nil 31 | ) extends OgcStyle { 32 | def renderRaster( 33 | raster: Raster[MultibandTile], 34 | crs: CRS, 35 | format: OutputFormat, 36 | hists: List[Histogram[Double]] 37 | ): Array[Byte] = 38 | format match { 39 | case format: OutputFormat.Png => format.render(raster.tile.band(bandIndex = 0), colorMap) 40 | case OutputFormat.Jpg => colorMap.render(raster.tile.band(bandIndex = 0)).renderJpg().bytes 41 | case OutputFormat.GeoTiff => GeoTiff(raster.mapTile(tile => colorMap.render(tile.band(bandIndex = 0))), crs).toCloudOptimizedByteArray 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ogc/src/main/scala/geotrellis/server/ogc/style/LegendModel.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc.style 18 | 19 | case class LegendModel( 20 | format: String, 21 | width: Int, 22 | height: Int, 23 | onlineResource: OnlineResourceModel 24 | ) 25 | -------------------------------------------------------------------------------- /ogc/src/main/scala/geotrellis/server/ogc/style/OgcStyle.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc.style 18 | 19 | import geotrellis.proj4.CRS 20 | import geotrellis.server.ogc.OutputFormat 21 | import geotrellis.raster._ 22 | import geotrellis.raster.histogram.Histogram 23 | 24 | trait OgcStyle { 25 | def name: String 26 | def title: String 27 | def legends: List[LegendModel] 28 | def renderRaster(raster: Raster[MultibandTile], crs: CRS, format: OutputFormat, hists: List[Histogram[Double]]): Array[Byte] 29 | } 30 | -------------------------------------------------------------------------------- /ogc/src/main/scala/geotrellis/server/ogc/style/OnlineResourceModel.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc.style 18 | 19 | case class OnlineResourceModel( 20 | `type`: String, 21 | href: String, 22 | role: Option[String] = None, 23 | title: Option[String] = None, 24 | show: Option[String] = None, 25 | actuate: Option[String] = None 26 | ) 27 | -------------------------------------------------------------------------------- /ogc/src/main/scala/geotrellis/server/ogc/style/RGBStyle.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc.style 18 | 19 | import geotrellis.proj4.CRS 20 | import geotrellis.raster.{MultibandTile, Raster} 21 | import geotrellis.raster.histogram.Histogram 22 | import geotrellis.raster.io.geotiff.tags.codes.ColorSpace 23 | import geotrellis.raster.io.geotiff.{GeoTiffOptions, MultibandGeoTiff} 24 | import geotrellis.server.ogc.OutputFormat 25 | 26 | case class RGBStyle(name: String, title: String, legends: List[LegendModel] = Nil) extends OgcStyle { 27 | def renderRaster(raster: Raster[MultibandTile], crs: CRS, format: OutputFormat, hists: List[Histogram[Double]]): Array[Byte] = 28 | format match { 29 | case _: OutputFormat.Png => raster.tile.renderPng() 30 | case OutputFormat.Jpg => raster.tile.renderJpg() 31 | case OutputFormat.GeoTiff => MultibandGeoTiff(raster, crs, GeoTiffOptions(colorSpace = ColorSpace.RGB)).toCloudOptimizedByteArray 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /ogc/src/main/scala/geotrellis/server/ogc/utils/ExpressionUtils.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc.utils 18 | 19 | import geotrellis.raster.TargetCell 20 | import com.azavea.maml.ast.Expression 21 | import cats.syntax.option._ 22 | 23 | object ExpressionUtils { 24 | def bindExpression(expr: Expression, fun: Expression => Expression): Expression = { 25 | def deepMap(expression: Expression, f: Expression => Expression): Expression = 26 | f(expression).withChildren(expression.children.map(deepMap(_, f))) 27 | 28 | deepMap(expr, fun) 29 | } 30 | 31 | def targetCell(str: String): Option[TargetCell] = 32 | str match { 33 | case "nodata" => TargetCell.NoData.some 34 | case "data" => TargetCell.Data.some 35 | case "all" => TargetCell.All.some 36 | case _ => None 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ogc/src/main/scala/geotrellis/server/ogc/utils/ScalaxbUtils.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc.utils 18 | 19 | import scalaxb.DataRecord 20 | 21 | import scala.xml.XML 22 | import scala.xml.Elem 23 | 24 | object ScalaxbUtils { 25 | def toXML[A](record: DataRecord[A], dropValue: Boolean = false): Elem = 26 | if (!dropValue) (record.namespace, record.key, record.value) match { 27 | case (None, Some(k), _) => XML.loadString(s"<$k>${record.value}") 28 | case (Some(n), Some(k), _) => XML.loadString(s"<$n:$k>${record.value}") 29 | case _ => XML.loadString(s"<${record.value}/>") 30 | } 31 | else 32 | (record.namespace, record.key, record.value) match { 33 | case (None, Some(k), _) => XML.loadString(s"<$k/>") 34 | case (Some(n), Some(k), _) => XML.loadString(s"<$n:$k/>") 35 | case _ => throw new IllegalArgumentException(s"The passed record $record should contain namespace or key.") 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ogc/src/main/scala/geotrellis/server/ogc/utils/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc 18 | 19 | package object utils extends utils.Implicits 20 | -------------------------------------------------------------------------------- /ogc/src/main/scala/geotrellis/server/ogc/wcs/WcsModel.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc.wcs 18 | 19 | import geotrellis.server.ogc._ 20 | import geotrellis.server.ogc.params.ParamMap 21 | import geotrellis.server.ogc.utils._ 22 | import com.azavea.maml.ast.Expression 23 | import cats.Functor 24 | import cats.syntax.functor._ 25 | import geotrellis.store.query.RepositoryM 26 | import geotrellis.proj4.CRS 27 | 28 | /** 29 | * This class holds all the information necessary to construct a response to a WCS request 30 | */ 31 | case class WcsModel[F[_]: Functor]( 32 | serviceMetadata: ows.ServiceMetadata, 33 | sources: RepositoryM[F, List, OgcSource], 34 | supportedProjections: List[CRS], 35 | extendedParametersBinding: Option[ParamMap => Option[Expression => Expression]] = None 36 | ) { 37 | def getLayers(p: GetCoverageWcsParams): F[List[OgcLayer]] = { 38 | val filteredSources = sources.find(p.toQuery) 39 | filteredSources.map { 40 | _.map { 41 | case rs: RasterOgcSource => rs.toLayer(p.crs, None, p.temporalSequence) 42 | case mas: MapAlgebraSource => 43 | val (name, title, algebra, resampleMethod, overviewStrategy) = (mas.name, mas.title, mas.algebra, mas.resampleMethod, mas.overviewStrategy) 44 | val simpleLayers = mas.sources.map { case (key, rs) => 45 | key -> SimpleOgcLayer(name, title, p.crs, rs, None, resampleMethod, overviewStrategy) 46 | } 47 | val extendedParameters = extendedParametersBinding.flatMap(_.apply(p.params)) 48 | MapAlgebraOgcLayer( 49 | name, 50 | title, 51 | p.crs, 52 | simpleLayers, 53 | algebra.bindExtendedParameters(extendedParameters), 54 | None, 55 | resampleMethod, 56 | overviewStrategy, 57 | mas.targetCellType 58 | ) 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /ogc/src/main/scala/geotrellis/server/ogc/wcs/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc 18 | 19 | import scala.xml.NamespaceBinding 20 | 21 | package object wcs { 22 | val wcsScope: NamespaceBinding = scalaxb.toScope( 23 | None -> "http://www.opengis.net/wcs/1.1.1", 24 | Some("gml") -> "http://www.opengis.net/gml", 25 | Some("ows") -> "http://www.opengis.net/ows/1.1", 26 | Some("ogc") -> "http://www.opengis.net/ogc", 27 | Some("xlink") -> "http://www.w3.org/1999/xlink", 28 | Some("xsi") -> "http://www.w3.org/2001/XMLSchema-instance" 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /ogc/src/main/scala/geotrellis/server/ogc/wfs/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc 18 | 19 | import scalaxb.DataRecord 20 | 21 | import scala.xml.{Elem, NamespaceBinding, NodeSeq, XML} 22 | 23 | package object wfs { 24 | val wfsScope: NamespaceBinding = scalaxb.toScope( 25 | None -> "http://www.opengis.net/wfs", 26 | Some("gml") -> "http://www.opengis.net/gml", 27 | Some("ows") -> "http://www.opengis.net/ows/1.1", 28 | Some("ogc") -> "http://www.opengis.net/ogc", 29 | Some("xlink") -> "http://www.w3.org/1999/xlink", 30 | Some("xsi") -> "http://www.w3.org/2001/XMLSchema-instance" 31 | ) 32 | 33 | implicit class ElemOps(val elem: Elem) extends AnyVal { 34 | def nestedXML(key: String): Elem = XML.loadString(s"<$key>$elem") 35 | } 36 | 37 | implicit class NodeSeqOps(val elem: NodeSeq) extends AnyVal { 38 | def nestedXML(key: String): Elem = XML.loadString(s"<$key>$elem") 39 | } 40 | 41 | implicit class DataRecordOps(val record: DataRecord[Any]) extends AnyVal { 42 | def nested: Elem = nestedSeq.asInstanceOf[Elem] 43 | 44 | def nestedSeq: NodeSeq = 45 | scalaxb 46 | .toXML[DataRecord[Any]]( 47 | obj = record, 48 | namespace = record.namespace, 49 | elementLabel = record.key, 50 | scope = wfsScope, 51 | typeAttribute = false 52 | ) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ogc/src/main/scala/geotrellis/server/ogc/wms/GetMapException.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc.wms 18 | 19 | sealed trait GetMapException extends Exception 20 | case class GetMapBadRequest(msg: String) extends GetMapException 21 | case class GetMapInternalServerError(msg: String) extends GetMapException 22 | -------------------------------------------------------------------------------- /ogc/src/main/scala/geotrellis/server/ogc/wms/WmsParentLayerMeta.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc.wms 18 | 19 | import geotrellis.proj4.CRS 20 | 21 | /** 22 | * Parent layer metadata class (used in configuration and reporting capabilities) 23 | */ 24 | case class WmsParentLayerMeta( 25 | name: Option[String], 26 | title: String, 27 | description: Option[String], 28 | supportedProjections: List[CRS] 29 | ) 30 | -------------------------------------------------------------------------------- /ogc/src/main/scala/geotrellis/server/ogc/wmts/GeotrellisTileMatrix.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc.wmts 18 | 19 | import geotrellis.proj4.{CRS, LatLng, WebMercator} 20 | import geotrellis.raster.TileLayout 21 | import geotrellis.layer._ 22 | import geotrellis.vector.Extent 23 | 24 | import opengis.ows._ 25 | import opengis.wmts.TileMatrix 26 | 27 | /** 28 | * Relates Geotrellis Extent and TileLayout to a corresponding OGC Tile Matrix 29 | */ 30 | case class GeotrellisTileMatrix( 31 | identifier: String, 32 | extent: Extent, 33 | tileLayout: TileLayout, 34 | title: Option[String] = None, 35 | `abstract`: Option[String] = None 36 | ) { 37 | val layout: LayoutDefinition = LayoutDefinition(extent, tileLayout) 38 | require(layout.cellSize.width == layout.cellSize.height, s"Layout definition cell size width must be same as height: ${layout.cellSize}") 39 | 40 | val projectionMetersPerUnit: Map[CRS, Double] = Map( 41 | // meters per unit on equator 42 | LatLng -> 6378137.0 * 2.0 * math.Pi / 360.0, 43 | WebMercator -> 1 44 | ) 45 | 46 | def toXml(crs: CRS): TileMatrix = 47 | projectionMetersPerUnit.get(crs) match { 48 | case Some(metersPerUnit) => 49 | val scaleDenominator = layout.cellSize.width / 0.00028 * metersPerUnit 50 | TileMatrix( 51 | Title = title.map(LanguageStringType(_)).toList, 52 | AbstractValue = `abstract`.map(LanguageStringType(_)).toList, 53 | Keywords = Nil, 54 | Identifier = CodeType(identifier), 55 | ScaleDenominator = scaleDenominator, 56 | TopLeftCorner = layout.extent.xmin :: layout.extent.ymax :: Nil, 57 | TileWidth = layout.tileLayout.tileCols, 58 | TileHeight = layout.tileLayout.tileRows, 59 | MatrixWidth = layout.tileLayout.layoutCols, 60 | MatrixHeight = layout.tileLayout.layoutRows 61 | ) 62 | 63 | case None => 64 | throw new Exception(s"Invalid CRS: ${crs}") 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /ogc/src/main/scala/geotrellis/server/ogc/wmts/GeotrellisTileMatrixSet.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc.wmts 18 | 19 | import geotrellis.server.ogc.URN 20 | import geotrellis.proj4.CRS 21 | import geotrellis.vector.Extent 22 | 23 | import opengis.ows._ 24 | import opengis.wmts.TileMatrixSet 25 | 26 | import java.net.URI 27 | 28 | /** 29 | * A collection of tile matrices; most commonly forming a pyramid of different resolutions 30 | */ 31 | case class GeotrellisTileMatrixSet( 32 | identifier: String, 33 | supportedCrs: CRS, 34 | title: Option[String] = None, 35 | `abstract`: Option[String] = None, 36 | boundingBox: Option[Extent] = None, 37 | wellKnownScaleSet: Option[String] = None, 38 | tileMatrix: List[GeotrellisTileMatrix] 39 | ) { 40 | def toXml: TileMatrixSet = { 41 | val ret = TileMatrixSet( 42 | Title = title.map(LanguageStringType(_)).toList, 43 | AbstractValue = `abstract`.map(LanguageStringType(_)).toList, 44 | Keywords = Nil, 45 | Identifier = CodeType(identifier), 46 | TileMatrix = tileMatrix.map(_.toXml(supportedCrs)), 47 | SupportedCRS = new URI(URN.unsafeFromCrs(supportedCrs)) 48 | ) 49 | 50 | if (wellKnownScaleSet.isDefined) ret.copy(WellKnownScaleSet = wellKnownScaleSet.map(new URI(_))) 51 | else ret.copy(BoundingBox = ???) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ogc/src/main/scala/geotrellis/server/ogc/wmts/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc 18 | 19 | import scala.xml.NamespaceBinding 20 | 21 | package object wmts { 22 | val wmtsScope: NamespaceBinding = scalaxb.toScope( 23 | None -> "http://www.opengis.net/wmts/1.0", 24 | Some("gml") -> "http://www.opengis.net/gml", 25 | Some("ows") -> "http://www.opengis.net/ows/1.1", 26 | Some("xlink") -> "http://www.w3.org/1999/xlink", 27 | Some("xsi") -> "http://www.w3.org/2001/XMLSchema-instance" 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /ogc/src/test/scala/geotrellis/server/ogc/OgcStyleSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc 18 | 19 | import geotrellis.server.ogc.style._ 20 | 21 | import geotrellis.raster.render.ColorRamp 22 | import geotrellis.raster.histogram.DoubleHistogram 23 | 24 | import org.scalatest.funspec.AnyFunSpec 25 | import org.scalatest.matchers.should.Matchers 26 | 27 | class OgcStyleSpec extends AnyFunSpec with Matchers { 28 | 29 | describe("ColorRampStyle") { 30 | it("should interpolate breaks") { 31 | val minimum = -10 32 | val maximum = 90 33 | val desiredBreaks = 50 34 | val ramp = ColorRamp(Array(0xff0000, 0x0000ff)) 35 | val style = ColorRampStyle("test", "title", ramp, Some(20), Some(minimum), Some(maximum)) 36 | val breaks = style.breaks(List(DoubleHistogram()), desiredBreaks) 37 | breaks.length shouldBe desiredBreaks 38 | breaks.head shouldBe -10 39 | breaks.reverse.head shouldBe 90 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/ReadMe.txt: -------------------------------------------------------------------------------- 1 | To avoid problems, use http://schemas.opengis.net/ to download XSD / WSDL. For instance: http://schemas.opengis.net/ows/. 2 | Usage of the GeoTools schemas version can lead to ScalaXB protocol generation issues. -------------------------------------------------------------------------------- /opengis/src/main/xsd/filter/1.1.0/filterAll.xsd: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | This XML Schema document includes and imports, directly or indirectly, 14 | all the XML Schema defined by the Filter Encoding Standard. 15 | 16 | Filter Encoding is an OGC Standard. 17 | Copyright (c) 2010 Open Geospatial Consortium. 18 | To obtain additional rights of use, visit http://www.opengeospatial.org/legal/ . 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/filter/1.1.0/sort.xsd: -------------------------------------------------------------------------------- 1 | 2 | 8 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 32 | 33 | 34 | 35 | 36 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/gml/3.1.1/base/gml.xsd: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | gml.xsd 6 | Top level GML schema 7 | 8 | GML is an OGC Standard. 9 | Copyright (c) 2001,2005,2010 Open Geospatial Consortium. 10 | To obtain additional rights of use, visit http://www.opengeospatial.org/legal/ . 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/gml/3.1.1/gml_3_1_1-ReadMe.txt: -------------------------------------------------------------------------------- 1 | GML 3.1.1 Schema Package Readme 2 | 3 | The schema package contained in this directory is the normative GML 4 | 3.1.1 schema set as approved by the OGC membership in May 2005. This 5 | version corrects a number of errors in the GML 3.1.0 schemas that 6 | prevented validation. 7 | 8 | Reason for Changes: The XML Schema for GML 3.1 fails to validate using 9 | several of the most popular XML processing software applications. 10 | 11 | Four sets of errors have been found in the GML 3.1 Schema, with 12 | corrections applied as described: 13 | 14 | 1. the content model for "metaDataProperty" is non-determinstic: you 15 | can't have a choice group containing with anything else 16 | - delete _MetaData from the choice group 17 | 18 | 2. membership of the _timeLength, _Value, _ScalarValue, 19 | _ScalarValueList substitution groups relies on an interpretation of the 20 | XML Schema specification which is not uniformly accepted 21 | - replace with an explicit groups 22 | 23 | 3. axisName (type="string") in grids.xsd clashes with axisName 24 | (different type) in coordinateSystems.xsd 25 | - Remove the latter, use the gml:name property instead 26 | 27 | 4. a "derivation-by-restriction" pattern used widely throughout the 28 | schema has a cardinality constraint (minOccurs="0", and sometimes 29 | maxOccurs="unbounded") applied to a "property" element in a base type, 30 | where the property element is the head of a substitution group; in a 31 | type derived by restriction in which one or more members of the 32 | substitution group is selected, the validation procedure described in 33 | the W3C XML Schema specification requires that the validation 34 | constraint appear on a container element, rather than on the 35 | substitution group member element 36 | - move the cardinality constraint up to the 37 | container element 38 | - remove unnecessary derivation-by-restriction chains, 39 | particularly concerning derivation of property elements 40 | 41 | As this pattern is used pervasively in GML, there are numerous changes 42 | in almost all of the schema documents. 43 | 44 | The revised schema has been tested with Xerces-J, XSV, .NET, MSXML and 45 | no validation errors are found. 46 | 47 | http://schemas.opengis.net/gml/3.1.1/ 48 | 49 | 2005-08-11 50 | 51 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/ows/1.0.0/owsAll.xsd: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | owsAll.xsd 8 | This XML Schema Document includes and imports, directly and indirectly, all the XML Schemas defined by the OWS Common Implemetation Specification. 9 | 10 | OWS is an OGC Standard. 11 | Copyright (c) 2005,2010 Open Geospatial Consortium. 12 | To obtain additional rights of use, visit http://www.opengeospatial.org/legal/ . 13 | 14 | 15 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/ows/1.0.0/owsServiceProvider.xsd: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | owsServiceProvider.xsd 8 | This XML Schema Document encodes the common "ServiceProvider" section of the GetCapabilities operation response, known as the Capabilities XML document. This section encodes the SV_ServiceProvider class of ISO 19119 (OGC Abstract Specification Topic 12). 9 | 10 | OWS is an OGC Standard. 11 | Copyright (c) 2005,2010 Open Geospatial Consortium. 12 | To obtain additional rights of use, visit http://www.opengeospatial.org/legal/ . 13 | 14 | 15 | 18 | 19 | 20 | 23 | 24 | 25 | Metadata about the organization that provides this specific service instance or server. 26 | 27 | 28 | 29 | 30 | 31 | A unique identifier for the service provider organization. 32 | 33 | 34 | 35 | 36 | Reference to the most relevant web site of the service provider. 37 | 38 | 39 | 40 | 41 | Information for contacting the service provider. The OnlineResource element within this ServiceContact element should not be used to reference a web site of the service provider. 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/ows/1.1.0/owsAll.xsd: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | owsAll.xsd 8 | This XML Schema Document includes and imports, directly and indirectly, all the XML Schemas defined by the OWS Common Implemetation Specification. 9 | 10 | OWS is an OGC Standard. 11 | Copyright (c) 2006,2010 Open Geospatial Consortium. 12 | To obtain additional rights of use, visit http://www.opengeospatial.org/legal/ . 13 | 14 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/ows/1.1.0/owsServiceProvider.xsd: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | owsServiceProvider.xsd 9 | This XML Schema Document encodes the common "ServiceProvider" section of the GetCapabilities operation response, known as the Capabilities XML document. This section encodes the SV_ServiceProvider class of ISO 19119 (OGC Abstract Specification Topic 12). 10 | 11 | OWS is an OGC Standard. 12 | Copyright (c) 2006,2010 Open Geospatial Consortium. 13 | To obtain additional rights of use, visit http://www.opengeospatial.org/legal/ . 14 | 15 | 16 | 19 | 20 | 21 | 24 | 25 | 26 | Metadata about the organization that provides this specific service instance or server. 27 | 28 | 29 | 30 | 31 | 32 | A unique identifier for the service provider organization. 33 | 34 | 35 | 36 | 37 | Reference to the most relevant web site of the service provider. 38 | 39 | 40 | 41 | 42 | Information for contacting the service provider. The OnlineResource element within this ServiceContact element should not be used to reference a web site of the service provider. 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/sld/1.1.0/DescribeLayer.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Styled Layer Descriptor version 1.1.0 (2010-02-01) 13 | 14 | SLD is an OGC Standard. 15 | Copyright (c) 2007,2010 Open Geospatial Consortium. 16 | To obtain additional rights of use, visit http://www.opengeospatial.org/legal/ . 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/sld/1.1.0/sldAll.xsd: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | Styled Layer Descriptor version 1.1 11 | This 'all-components' schema document includes and imports, directly or indirectly, 12 | all the XML Schema defined by SLD. 13 | 14 | 15 | SLD is an OGC Standard. 16 | Copyright (c) 2007,2010 Open Geospatial Consortium. 17 | To obtain additional rights of use, visit http://www.opengeospatial.org/legal/ . 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/sld/1.1.0/sld_capabilities.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Styled Layer Descriptor version 1.1.0 (2010-02-01) 8 | 9 | SLD is an OGC Standard. 10 | Copyright (c) 2007,2010 Open Geospatial Consortium. 11 | To obtain additional rights of use, visit http://www.opengeospatial.org/legal/ . 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/wcs/1.1.1/Examples/example2dGridCRSdefault1.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | urn:ogc:def:crs:EPSG:6.6:4326 7 | 0.1 0.2 8 | 9 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/wcs/1.1.1/Examples/example2dGridCRSdefault2.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 2D Grid CRS Example 1 8 | urn:ogc:def:crs:EPSG:6.6:4326 9 | urn:ogc:def:method:WCS:1.1:2dGridIn2dCrs 10 | 1.0 2.0 11 | 0.1 0 0 0.2 12 | urn:ogc:def:cs:OGC:0.0:Grid2dSquareCS 13 | 14 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/wcs/1.1.1/Examples/example2dGridCRSdefault3.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 2D Grid CRS Example 3 8 | urn:ogc:def:crs:EPSG:6.6:4326 9 | urn:ogc:def:method:WCS:1.2:grid2dIn3dMethod 10 | 0 0 0 11 | 0.0707 0.0707 0 -0.1414 0.1414 0 12 | urn:ogc:def:cs:OGC:0.0:Grid2dSquareCS 13 | 14 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/wcs/1.1.1/Examples/exampleCoverageDescription1.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | TBD 10 | TBD 11 | TBD 12 | 13 | 14 | 15 | -30.00 -30.00 16 | 30.00 30.00 17 | 18 | 19 | 20 | 21 | 22 | TBD 23 | TBD 24 | TBD 25 | 26 | 27 | 28 | 29 | linear 30 | cubic 31 | 32 | 33 | 34 | urn:ogc:def:crs:EPSG::XXXX 35 | urn:ogc:def:crs:EPSG::YYYY 36 | text/xml 37 | 38 | 39 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/wcs/1.1.1/Examples/exampleCoverages1.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | TBD 9 | Coverage created from GetCoverage operation request to a WCS 10 | TBD 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/wcs/1.1.1/Examples/exampleDescribeCoverage1.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | SPVIEW_530_274 5 | 6 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/wcs/1.1.1/Examples/exampleDescribeCoverage2.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | Landsat_TM_Mosaic 8 | WMO_Daily_Temps 9 | Census_population_tables 10 | 11 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/wcs/1.1.1/Examples/exampleGetCapabilitiesMax.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 1.0.0 10 | 1.1.1 11 | 12 | 13 | Contents 14 | 15 | 16 | text/xml 17 | 18 | 19 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/wcs/1.1.1/Examples/exampleGetCapabilitiesMin.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/wcs/1.1.1/Examples/exampleGetCoverage1.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | Cov123 8 | 9 | 10 | -71 47 11 | -66 51 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/wcs/1.1.1/Examples/exampleGetCoverage2.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | Cov123 8 | 9 | 10 | -71 47 11 | -66 51 12 | 13 | 14 | 15 | 2006-08-01 16 | 2006-09-01 17 | P1D 18 | 19 | 20 | 21 | 22 | 23 | urn:ogc:def:crs:EPSG:6.6:32618 24 | urn:ogc:def:method:WCS:1.1:2dGridin2dCrs 25 | 3000 4000 26 | 6.0 8.0 -8.0 6.0 27 | urn:ogc:def:cs:OGC:0.0:Grid2dSquareCS 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/wcs/1.1.1/Examples/exampleGetCoverage3.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | TBD 9 | 10 | 11 | 9999 9999 12 | 9999 9999 13 | 14 | 15 | 16 | 17 | TBD 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/wcs/1.1.1/Examples/exampleGetCoverage4.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | SPVIEW_530_274_0_020809_5_1_J_3 10 | 11 | 12 | 551182.3990 4173461.7219 13 | 569339.8990 4192512.2137 14 | 15 | 16 | 2002-08-09T19:08:46Z 17 | 18 | 19 | 20 | 21 | Wavelength 22 | nearest 23 | 24 | Band 25 | band1 26 | band2 27 | band3 28 | 29 | 30 | 31 | 32 | 33 | SPVIEW_530_274_0_020809_5_1_J_3_SUBSET_CRS 34 | urn:ogc:def:crs:EPSG::4326 35 | urn:ogc:def:method:WCS:1.1:grid2dIn2dMethod 36 | -122.4193 37.7058 37 | 0.0006926667 0 0 -0.000548889 38 | urn:ogc:def:cs:OGC:0.0:Grid2dSquareCS 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/wcs/1.1.1/Examples/exampleInterpolationMethods1.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | linear 7 | nearest 8 | 9 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/wcs/1.1.1/gml4wcs.xsd: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | gml.xsd 8 | GML profile for WCS 1.2. Primary editor: Arliss Whiteside. Last updated 2007-06-06 9 | Copyright © 2007 Open Geospatial Consortium, Inc. All Rights Reserved. 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/wcs/1.1.1/wcsAll.xsd: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | wcsAll.xsd 2007-06-06 8 | This XML Schema Document includes, directly and indirectly, all the XML Schema Documents defined by the OGC Web Coverage Service (WCS). 9 | Copyright (c) 2007 Open Geospatial Consortium, Inc, All Rights Reserved. 10 | 11 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/wcs/1.1.1/wcsCoverages.xsd: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | wcsCoverages.xsd 2007-07-01 9 | This XML Schema Document specifies types and elements for groups of coverages, allowing each coverage to include or reference multiple files. 10 | Copyright (c) 2007 Open Geospatial Consortium, Inc. All Rights Reserved. 11 | 12 | 15 | 16 | 19 | 20 | 21 | 22 | 23 | Group of coverages that can be used as the response from the WCS GetCoverage operation, allowing each coverage to include or reference multiple files. This Coverages element may also be used for outputs from, or inputs to, other OWS operations. 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Complete data for one coverage, referencing each coverage file either remotely or locally in the same message. 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/wms/1.3.0/ReadMe.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geotrellis/geotrellis-server/dfc62f5e0335fbb6330a6bbe0ff7967226cfe0ad/opengis/src/main/xsd/wms/1.3.0/ReadMe.txt -------------------------------------------------------------------------------- /opengis/src/main/xsd/wms/1.3.0/exceptions_1_3_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | Plain text message about an error. 8 | 9 | 10 | Another error message, this one with a service exception code supplied. 11 | 12 | 13 | , line 42 15 | 16 | A message that includes angle brackets in text 17 | must be enclosed in a Character Data Section 18 | as in this example. All XML-like markup is 19 | ignored except for this sequence of three 20 | closing characters: 21 | ]]> 22 | 23 | 24 | foo.c 26 | An error occurred 27 | Similarly, actual XML 28 | can be enclosed in a CDATA section. 29 | A generic parser will ignore that XML, 30 | but application-specific software may choose 31 | to process it. 32 | ]]> 33 | 34 | 35 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/wms/1.3.0/exceptions_1_3_0.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/wmts/1.0/wmts.xsd: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | wmts 9 | 10 | This XML Schema Document includes all WMTS schemas and 11 | is useful for SOAP messages. 12 | 13 | WMTS is an OGC Standard. 14 | Copyright (c) 2009,2010 Open Geospatial Consortium. 15 | To obtain additional rights of use, visit http://www.opengeospatial.org/legal/. 16 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/wmts/1.0/wmtsGetCapabilities_request.xsd: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | wmtsGetCapabilities_request 10 | 11 | This XML Schema Document defines the XML WMTS 12 | GetCapabilites request that can be used in SOAP encodings. 13 | 14 | WMTS is an OGC Standard. 15 | Copyright (c) 2009,2010 Open Geospatial Consortium. 16 | To obtain additional rights of use, visit http://www.opengeospatial.org/legal/. 17 | 18 | 19 | 22 | 23 | 24 | 27 | 28 | 29 | WMTS GetCapabilities operation request. 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/wmts/1.0/wmtsGetFeatureInfo_request.xsd: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | wmtsGetFeatureInfo_request 10 | 11 | This XML Schema Document defines XML WMTS 12 | GetFeatureInfo request that can be used in SOAP encodings. 13 | 14 | WMTS is an OGC Standard. 15 | Copyright (c) 2009,2010 Open Geospatial Consortium. 16 | To obtain additional rights of use, visit http://www.opengeospatial.org/legal/. 17 | 18 | 19 | 22 | 24 | 25 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | The corresponding GetTile request parameters 35 | 36 | 37 | 38 | 39 | Row index of a pixel in the tile 40 | 41 | 42 | 43 | 44 | Column index of a pixel in the tile 45 | 46 | 47 | 48 | 49 | Output MIME type format of the 50 | retrieved information 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/wmts/ReadMe.txt: -------------------------------------------------------------------------------- 1 | OGC(r) WMTS 1.0.0 - ReadMe.txt 2 | ====================================== 3 | 4 | OGC Web Map Tile Service (WMTS) Interface Standard 5 | ----------------------------------------------------------------------- 6 | 7 | More information on the OGC WMTS standard may be found at 8 | http://www.opengeospatial.org/standards/wmts 9 | 10 | The most current schema are available at http://schemas.opengis.net/ . 11 | 12 | ----------------------------------------------------------------------- 13 | 14 | 2014-11-06 Joan Maso 15 | * wmts/1.0/profiles/wmts-simple: added WMTS Simple Profile 1.0.0 (OGC 13-082r2) 16 | 17 | 2012-07-21 Kevin Stegemoller 18 | * v1.0: WARNING XLink change is NOT BACKWARD COMPATIBLE. 19 | * Changed OGC XLink (xlink:simpleLink) to W3C XLink (xlink:simpleAttrs) 20 | per an approved TC and PC motion during the Dec. 2011 Brussels meeting. 21 | See http://www.opengeospatial.org/blog/1597 22 | * v1.0: Per 11-025, all leaf documents of a namespace shall retroactively 23 | and explicitly require/add an of the all-components schema. 24 | * v1.0: Updated xsd:schema/@version to 1.0.1 (06-135r11 s#13.4) 25 | * v1.0: Included wmts.xsd as the all-components document (06-135r11 #14) 26 | * v1.0: Removed date from xs:schema/xs:annotation/xs:appinfo 27 | * v1.0: Update copyright 28 | * v1.0: xsd:annontation removed from wsdl files 29 | 30 | 2010-05-04 Kevin Stegemoller 31 | * v1.0: Published wmts/1.0.0 as wmts/1.0 from OGC 07-057r7 32 | * v1.0: These documents were validated with: 33 | + XSV Validator version 3.1.1 34 | + Xerces-c validator version 2.8.0 35 | + libxml2 validator version 2.7.3 36 | + AltovaXML 2009 37 | + MSXML parser 4.0 sp2. 38 | -- Joan Maso 39 | 40 | ----------------------------------------------------------------------- 41 | 42 | Policies, Procedures, Terms, and Conditions of OGC(r) are available 43 | http://www.opengeospatial.org/ogc/legal/ . 44 | 45 | Copyright (c) 2012 Open Geospatial Consortium. 46 | 47 | ----------------------------------------------------------------------- 48 | -------------------------------------------------------------------------------- /opengis/src/main/xsd/xlink/ReadMe.txt: -------------------------------------------------------------------------------- 1 | 2 | OGC(r) XLink schema - ReadMe.txt 3 | ================================ 4 | 5 | OGC(r) XLink implementation of W3C XLink 1.0 6 | ------------------------------------------------------------------- 7 | 8 | More information may be found at 9 | http://www.opengeospatial.org/standards/ 10 | 11 | The most current schema are available at http://schemas.opengis.net/ . 12 | 13 | ----------------------------------------------------------------------- 14 | 15 | 2012-07-21 Kevin Stegemoller 16 | * WARNING XLink change is NOT BACKWARD COMPATIBLE. 17 | * Changed OGC XLink (xlink:simpleLink) to W3C XLink (xlink:simpleAttrs) 18 | per an approved TC and PC motion during the Dec. 2011 Brussels meeting. 19 | See http://www.opengeospatial.org/blog/1597 20 | See http://www.ogcnetwork.net/node/1829 21 | * v1.0: xlink/1.0.0/xlinks.xsd schema was removed and archived 22 | 23 | 2005-11-22 Arliss Whiteside 24 | * This XML Schema Document named xlinks.xsd has been stored here based 25 | on the change request: 26 | OGC 05-068r1 "Store xlinks.xsd file at a fixed location" 27 | 28 | Note: check each OGC numbered document for detailed changes. 29 | 30 | ----------------------------------------------------------------------- 31 | 32 | Policies, Procedures, Terms, and Conditions of OGC(r) are available 33 | http://www.opengeospatial.org/ogc/legal/ . 34 | 35 | Copyright (c) 2012 Open Geospatial Consortium. 36 | 37 | ----------------------------------------------------------------------- 38 | 39 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.10.0 2 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addDependencyTreePlugin 2 | 3 | addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.12") 4 | addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.2.0") 5 | addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.6.4") 6 | addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.2") 7 | addSbtPlugin("com.eed3si9n" % "sbt-unidoc" % "0.4.3") 8 | addSbtPlugin("org.scalaxb" % "sbt-scalaxb" % "1.12.0") 9 | addSbtPlugin("se.marcuslonnberg" % "sbt-docker" % "1.11.0") 10 | addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.10.0") 11 | addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2") 12 | addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.12.1") 13 | -------------------------------------------------------------------------------- /scripts/cibuild: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | if [[ -n "${GEOTRELLIS_SERVER_DEBUG}" ]]; then 6 | set -x 7 | fi 8 | 9 | if [[ -n "${GIT_COMMIT}" ]]; then 10 | GIT_COMMIT="${GIT_COMMIT:0:7}" 11 | else 12 | GIT_COMMIT="$(git rev-parse --short HEAD)" 13 | fi 14 | 15 | function usage() { 16 | echo -n \ 17 | "Usage: $(basename "$0") 18 | Execute tests. 19 | " 20 | } 21 | 22 | if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then 23 | if [[ "${1:-}" == "--help" ]]; then 24 | usage 25 | else 26 | ./scripts/test 27 | 28 | echo "Building Scala container images" 29 | pushd ./stac-example 30 | GIT_COMMIT="${GIT_COMMIT}" docker-compose -f docker-compose.ci.yml build server 31 | popd 32 | fi 33 | fi 34 | -------------------------------------------------------------------------------- /scripts/cipublish: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | if [[ -n "${GEOTRELLIS_SERVER_DEBUG}" ]]; then 6 | set -x 7 | fi 8 | 9 | if [[ -n "${GIT_COMMIT}" ]]; then 10 | GIT_COMMIT="${GIT_COMMIT:0:7}" 11 | else 12 | GIT_COMMIT="$(git rev-parse --short HEAD)" 13 | fi 14 | 15 | function usage() { 16 | echo -n \ 17 | "Usage: $(basename "$0") 18 | Publish artifacts. 19 | " 20 | } 21 | 22 | if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then 23 | if [[ "${1:-}" == "--help" ]]; then 24 | usage 25 | else 26 | echo "Publishing artifacts to Sonatype" 27 | if [[ -n "${CIRCLE_TAG}" ]]; then 28 | ./sbt ";++${SCALA_VERSION};compile;sonatypeOpen ${CIRCLE_BUILD_NUM};publish;sonatypeRelease" 29 | else 30 | ./sbt ";++${SCALA_VERSION};compile;publish" 31 | fi 32 | 33 | echo "Publishing artifacts to quay.io" 34 | docker login -u "${QUAY_USER}" -p "${QUAY_PASSWORD}" quay.io 35 | docker tag "geotrellis-server-stac:${GIT_COMMIT}" "quay.io/geotrellis/geotrellis-server-stac:${GIT_COMMIT}" 36 | docker push "quay.io/geotrellis/geotrellis-server-stac:${GIT_COMMIT}" 37 | if [[ "${CIRCLE_BRANCH}" =~ ^main$ ]]; then 38 | docker tag "geotrellis-server-stac:${GIT_COMMIT}" "quay.io/geotrellis/geotrellis-server-stac:latest" 39 | docker push "quay.io/geotrellis/geotrellis-server-stac:latest" 40 | fi 41 | fi 42 | fi 43 | -------------------------------------------------------------------------------- /scripts/server: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | if [[ -n "${GEOTRELLIS_SERVER_DEBUG}" ]]; then 6 | set -x 7 | fi 8 | 9 | function usage() { 10 | echo -n \ 11 | "Usage: $(basename "$0") 12 | Starts an example server on port 9000. 13 | 14 | --overlay serve overlay example 15 | --persistence serve persistence example 16 | --ndvi serve ndvi example 17 | --gdal-ndvi serve gdal ndvi example 18 | " 19 | } 20 | 21 | if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then 22 | if [[ "${1:-}" == "--overlay" ]]; then 23 | docker-compose up \ 24 | overlay-example 25 | elif [[ "${1:-}" == "--persistence" ]]; then 26 | docker-compose up \ 27 | persistence-example 28 | elif [[ "${1:-}" == "--ndvi" ]]; then 29 | docker-compose up \ 30 | ndvi-example 31 | elif [[ "${1:-}" == "--gdal-ndvi" ]]; then 32 | docker-compose up \ 33 | gdal-ndvi-example 34 | else 35 | usage 36 | fi 37 | fi 38 | -------------------------------------------------------------------------------- /scripts/test: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | if [[ -n "${GEOTRELLIS_SERVER_DEBUG}" ]]; then 6 | set -x 7 | fi 8 | 9 | function usage() { 10 | echo -n \ 11 | "Usage: $(basename "$0") 12 | Update Scala dependencies and execute tests. 13 | " 14 | } 15 | 16 | if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then 17 | if [[ "${1:-}" == "--help" ]]; then 18 | usage 19 | else 20 | echo "Executing Scala test suite" 21 | ./sbt "++${SCALA_VERSION}" scalafmtCheck scalafmtSbtCheck test 22 | 23 | echo "Build GeoTrellis Server STAC Example assembly" 24 | ./sbt "++${SCALA_VERSION}" stac-example/assembly 25 | fi 26 | fi 27 | -------------------------------------------------------------------------------- /scripts/update: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | if [[ -n "${GEOTRELLIS_SERVER_DEBUG}" ]]; then 6 | set -x 7 | fi 8 | 9 | function usage() { 10 | echo -n \ 11 | "Usage: $(basename "$0") 12 | Update project dependencies and build assembly JARs. 13 | " 14 | } 15 | 16 | if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then 17 | if [[ "${1:-}" == "--help" ]]; then 18 | usage 19 | else 20 | ./sbt "++${SCALA_VERSION:-2.12.12}" \ 21 | ";update;assembly" 22 | fi 23 | fi 24 | -------------------------------------------------------------------------------- /stac-example/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM quay.io/azavea/openjdk-gdal:3.1-jdk11-slim 2 | 3 | COPY ./target/scala-2.12/geotrellis-server-stac-example.jar /var/lib/geotrellis-server/ 4 | 5 | WORKDIR /var/lib/geotrellis-server 6 | 7 | ENTRYPOINT ["java", "-XX:+UseG1GC", "-jar", "geotrellis-server-stac-example.jar"] 8 | -------------------------------------------------------------------------------- /stac-example/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | cd ../; ./sbt "project stac-example" run --public-url http://localhost:9000 3 | 4 | assembly: 5 | cd ../; ./sbt "project stac-example" assembly 6 | 7 | postgres: 8 | docker-compose up -d 9 | 10 | migrations: 11 | docker run --rm \ 12 | --network stac-example_default \ 13 | quay.io/azavea/franklin:be7cf48 \ 14 | migrate \ 15 | --db-user franklin \ 16 | --db-name franklin \ 17 | --db-password franklin \ 18 | --db-host database 19 | 20 | # -e FRANKLIN_LOG_LEVEL=DEBUG 21 | import-landsat-stac-collection: 22 | docker run --rm \ 23 | --network stac-example_default \ 24 | -e AWS_REGION=us-east-1 \ 25 | -v ${PWD}/catalog:/opt/data/ \ 26 | quay.io/azavea/franklin:be7cf48 \ 27 | import-catalog \ 28 | --db-user franklin \ 29 | --db-name franklin \ 30 | --db-password franklin \ 31 | --db-host database \ 32 | --catalog-root /opt/data/landsat-stac-collection/catalog.json 33 | 34 | import-landsat-stac-periodic-collection: 35 | docker run --rm \ 36 | --network stac-example_default \ 37 | -e AWS_REGION=us-east-1 \ 38 | -v ${PWD}/catalog:/opt/data/ \ 39 | quay.io/azavea/franklin:be7cf48 \ 40 | import-catalog \ 41 | --db-user franklin \ 42 | --db-name franklin \ 43 | --db-password franklin \ 44 | --db-host database \ 45 | --catalog-root /opt/data/landsat-stac-periodic-collection/catalog.json 46 | 47 | import-landsat-stac-layers: 48 | docker run --rm \ 49 | --network stac-example_default \ 50 | -e AWS_REGION=us-east-1 \ 51 | -v ${PWD}/catalog:/opt/data/ \ 52 | quay.io/azavea/franklin:be7cf48 \ 53 | import-catalog \ 54 | --db-user franklin \ 55 | --db-name franklin \ 56 | --db-password franklin \ 57 | --db-host database \ 58 | --catalog-root /opt/data/landsat-stac-layers/catalog.json 59 | 60 | run-franklin: 61 | docker run --rm \ 62 | --network stac-example_default \ 63 | -p 9090:9090 \ 64 | quay.io/azavea/franklin:be7cf48 \ 65 | serve \ 66 | --db-user franklin \ 67 | --db-name franklin \ 68 | --db-password franklin \ 69 | --db-host database 70 | 71 | run-geotrellis-server: assembly 72 | java -jar ${PWD}/target/scala-2.12/geotrellis-stac-example.jar --public-url http://localhost:9000 73 | 74 | run: postgres migrations import-landsat-stac-collection run-franklin run-geotrellis-server 75 | -------------------------------------------------------------------------------- /stac-example/catalog/landsat-stac-collection/catalog.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Catalog", 3 | "stac_version": "1.0.0", 4 | "stac_extensions": [], 5 | "id": "landsat-stac-collection-catalog", 6 | "title": "STAC for Landsat data", 7 | "description": "STAC for Landsat data", 8 | "links": [ 9 | { 10 | "href": "./catalog.json", 11 | "rel": "self" 12 | }, 13 | { 14 | "href": "./catalog.json", 15 | "rel": "root" 16 | }, 17 | { 18 | "href": "./landsat-8-l1/catalog.json", 19 | "rel": "child" 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /stac-example/catalog/landsat-stac-layers/catalog.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Catalog", 3 | "stac_version": "1.0.0", 4 | "stac_extensions": [], 5 | "id": "landsat-stac-layers-catalog", 6 | "title": "STAC for Landsat data", 7 | "description": "STAC for Landsat data", 8 | "links": [ 9 | { 10 | "href": "./catalog.json", 11 | "rel": "self" 12 | }, 13 | { 14 | "href": "./catalog.json", 15 | "rel": "root" 16 | }, 17 | { 18 | "href": "./landsat-8-l1/catalog.json", 19 | "rel": "child" 20 | }, 21 | { 22 | "href": "./layers/catalog.json", 23 | "rel": "child" 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /stac-example/catalog/landsat-stac-layers/layers/catalog.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Catalog", 3 | "stac_version": "1.0.0", 4 | "stac_extensions": [], 5 | "id": "layer-us-global", 6 | "title": "Landsat 8 L1", 7 | "description": "US STAC Layers, a STAC Catalog that represents a list of STAC Layers", 8 | "links": [ 9 | { 10 | "href": "../catalog.json", 11 | "rel": "root" 12 | }, 13 | { 14 | "href": "../catalog.json", 15 | "rel": "parent" 16 | }, 17 | { 18 | "href": "./catalog.json", 19 | "rel": "self" 20 | }, 21 | { 22 | "href": "./pa.json", 23 | "rel": "item" 24 | }, 25 | { 26 | "href": "./us.json", 27 | "rel": "item" 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /stac-example/catalog/landsat-stac-layers/layers/pa.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Feature", 3 | "id": "layer-pa", 4 | "stac_version": "1.0.0", 5 | "stac_extensions": [ 6 | "layer" 7 | ], 8 | "bbox": [ 9 | -78.32015745740821, 10 | 37.79447027981874, 11 | -73.92530589507936, 12 | 41.41037947529183 13 | ], 14 | "geometry": { 15 | "type": "Polygon", 16 | "coordinates": [ 17 | [ 18 | [ 19 | -78.32015745740821, 20 | 37.79447027981874 21 | ], 22 | [ 23 | -78.32015745740821, 24 | 41.41037947529183 25 | ], 26 | [ 27 | -73.92530589507936, 28 | 41.41037947529183 29 | ], 30 | [ 31 | -73.92530589507936, 32 | 37.79447027981874 33 | ], 34 | [ 35 | -78.32015745740821, 36 | 37.79447027981874 37 | ] 38 | ] 39 | ] 40 | }, 41 | "properties": { 42 | "datetime": null, 43 | "start_datetime": "2018-05-01T00:00:00Z", 44 | "end_datetime": "2018-08-01T00:00:00Z" 45 | }, 46 | "links": [ 47 | { 48 | "rel": "self", 49 | "href": "./pa.json" 50 | }, 51 | { 52 | "rel": "parent", 53 | "href": "../catalog.json" 54 | }, 55 | { 56 | "rel": "root", 57 | "href": "../catalog.json" 58 | }, 59 | { 60 | "href": "../landsat-8-l1/2018-06/LC80140332018166LGN00.json", 61 | "rel": "item" 62 | }, 63 | { 64 | "href": "../landsat-8-l1/2018-05/LC80150322018141LGN00.json", 65 | "rel": "item" 66 | }, 67 | { 68 | "href": "../landsat-8-l1/2018-07/LC80150332018189LGN00.json", 69 | "rel": "item" 70 | } 71 | ] 72 | } 73 | -------------------------------------------------------------------------------- /stac-example/catalog/landsat-stac-layers/layers/us.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Feature", 3 | "id": "layer-us", 4 | "stac_version": "1.0.0", 5 | "stac_extensions": [ 6 | "layer" 7 | ], 8 | "bbox": [ 9 | -101.40824987104652, 10 | 37.79718802132125, 11 | -73.94863288954222, 12 | 41.41061537114088 13 | ], 14 | "geometry": { 15 | "type": "Polygon", 16 | "coordinates": [ 17 | [ 18 | [ 19 | -101.40824987104652, 20 | 37.79718802132125 21 | ], 22 | [ 23 | -101.40824987104652, 24 | 41.41061537114088 25 | ], 26 | [ 27 | -73.94863288954222, 28 | 41.41061537114088 29 | ], 30 | [ 31 | -73.94863288954222, 32 | 37.79718802132125 33 | ], 34 | [ 35 | -101.40824987104652, 36 | 37.79718802132125 37 | ] 38 | ] 39 | ] 40 | }, 41 | "properties": { 42 | "datetime": null, 43 | "start_datetime": "2018-05-01T00:00:00Z", 44 | "end_datetime": "2018-08-01T00:00:00Z" 45 | }, 46 | "links": [ 47 | { 48 | "rel": "self", 49 | "href": "./us.json" 50 | }, 51 | { 52 | "rel": "parent", 53 | "href": "../catalog.json" 54 | }, 55 | { 56 | "rel": "root", 57 | "href": "../catalog.json" 58 | }, 59 | { 60 | "href": "../landsat-8-l1/2018-06/LC80140332018166LGN00.json", 61 | "rel": "item" 62 | }, 63 | { 64 | "href": "../landsat-8-l1/2018-05/LC80150322018141LGN00.json", 65 | "rel": "item" 66 | }, 67 | { 68 | "href": "../landsat-8-l1/2018-07/LC80150332018189LGN00.json", 69 | "rel": "item" 70 | }, 71 | { 72 | "href": "../landsat-8-l1/2018-06/LC80300332018166LGN00.json", 73 | "rel": "item" 74 | } 75 | ] 76 | } 77 | -------------------------------------------------------------------------------- /stac-example/catalog/landsat-stac-periodic-collection/catalog.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Catalog", 3 | "stac_version": "1.0.0", 4 | "stac_extensions": [], 5 | "id": "landsat-stac-periodic-catalog", 6 | "title": "STAC for Landsat data", 7 | "description": "STAC for Landsat data", 8 | "links": [ 9 | { 10 | "href": "./catalog.json", 11 | "rel": "self" 12 | }, 13 | { 14 | "href": "./catalog.json", 15 | "rel": "root" 16 | }, 17 | { 18 | "href": "./landsat-8-l1/catalog.json", 19 | "rel": "child" 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /stac-example/docker-compose.ci.yml: -------------------------------------------------------------------------------- 1 | version: "3.8" 2 | services: 3 | server: 4 | image: "geotrellis-server-stac:${GIT_COMMIT}" 5 | build: 6 | context: . 7 | dockerfile: Dockerfile 8 | -------------------------------------------------------------------------------- /stac-example/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2.3' 2 | services: 3 | database: 4 | image: quay.io/azavea/postgis:3-postgres12.2-slim 5 | environment: 6 | - POSTGRES_USER=franklin 7 | - POSTGRES_PASSWORD=franklin 8 | - POSTGRES_DB=franklin 9 | ports: 10 | - "5432:5432" 11 | healthcheck: 12 | test: ["CMD", "pg_isready", "-U", "franklin"] 13 | interval: 3s 14 | timeout: 3s 15 | retries: 3 16 | start_period: 5s 17 | command: postgres -c log_statement=all -------------------------------------------------------------------------------- /stac-example/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /stac-example/src/main/scala/geotrellis/server/ogc/ExtendedParameters.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc 18 | 19 | import com.azavea.maml.ast.Expression 20 | import geotrellis.server.ogc.params.ParamMap 21 | 22 | import cats.syntax.apply._ 23 | import cats.instances.option._ 24 | 25 | object ExtendedParameters { 26 | val extendedParametersBinding: Option[ParamMap => Option[Expression => Expression]] = Option { p => 27 | (FocalParameters.extendedParametersBinding, RGBParameters.extendedParametersBinding).tupled.flatMap { case (l, r) => 28 | (l(p), r(p)).mapN(_ andThen _) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /stac-example/src/main/scala/geotrellis/server/ogc/StacOgcSource.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc 18 | 19 | import geotrellis.server.ogc.stac._ 20 | 21 | import com.azavea.stac4s.StacCollection 22 | import geotrellis.proj4.CRS 23 | import geotrellis.raster.{RasterSource, ResampleMethod} 24 | import geotrellis.raster.io.geotiff.OverviewStrategy 25 | import geotrellis.server.ogc.style.OgcStyle 26 | import geotrellis.server.ogc.utils._ 27 | import geotrellis.stac.raster.StacSource 28 | import geotrellis.vector.{Extent, ProjectedExtent} 29 | 30 | /** 31 | * An imagery source with a [[RasterSource]] that defines its capacities 32 | */ 33 | case class StacOgcSource( 34 | name: String, 35 | title: String, 36 | stacSource: StacSource[StacCollection], 37 | defaultStyle: Option[String], 38 | styles: List[OgcStyle], 39 | resampleMethod: ResampleMethod, 40 | overviewStrategy: OverviewStrategy, 41 | timeMetadataKey: Option[String], 42 | fetchTimePositions: Boolean, 43 | timeFormat: OgcTimeFormat, 44 | timeDefault: OgcTimeDefault 45 | ) extends RasterOgcSource { 46 | 47 | lazy val time: OgcTime = { 48 | val summaryTime = if (!fetchTimePositions) stacSource.stacExtent.ogcTime else None 49 | summaryTime 50 | .orElse( 51 | attributes 52 | .time(timeMetadataKey) 53 | .orElse(source.attributes.time(timeMetadataKey)) 54 | ) 55 | .getOrElse(source.time(timeMetadataKey)) 56 | .format(timeFormat) 57 | .sorted 58 | } 59 | 60 | def source: RasterSource = stacSource.source 61 | 62 | override def extentIn(crs: CRS): Extent = projectedExtent.reproject(crs).extent 63 | 64 | override def projectedExtent: ProjectedExtent = stacSource.projectedExtent 65 | override lazy val attributes: Map[String, String] = stacSource.attributes 66 | 67 | def toLayer(crs: CRS, style: Option[OgcStyle], temporalSequence: List[OgcTime]): SimpleOgcLayer = 68 | SimpleOgcLayer(name, title, crs, source, style, resampleMethod, overviewStrategy) 69 | } 70 | -------------------------------------------------------------------------------- /stac-example/src/main/scala/geotrellis/server/ogc/ToMediaType.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc 18 | 19 | import org.http4s.MediaType 20 | 21 | object ToMediaType { 22 | def apply(format: OutputFormat): MediaType = format match { 23 | case OutputFormat.Png(_) => MediaType.image.png 24 | case OutputFormat.Jpg => MediaType.image.jpeg 25 | case OutputFormat.GeoTiff => MediaType.image.tiff 26 | } 27 | 28 | def apply(format: InfoFormat): MediaType = format match { 29 | case InfoFormat.Json => MediaType.application.json 30 | case InfoFormat.XML => MediaType.text.xml 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /stac-example/src/main/scala/geotrellis/server/ogc/conf/StyleConf.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc.conf 18 | 19 | import geotrellis.server.ogc.style._ 20 | 21 | import geotrellis.raster.render.{ColorMap, ColorRamp} 22 | 23 | /** 24 | * The trait implemented by different style configuration options 25 | */ 26 | sealed trait StyleConf { 27 | def name: String 28 | def title: String 29 | def toStyle: OgcStyle 30 | } 31 | 32 | /** 33 | * Styling in which a color scheme is known but not the values that these colors should map to 34 | */ 35 | final case class ColorRampConf( 36 | name: String, 37 | title: String, 38 | colors: ColorRamp, 39 | stops: Option[Int], 40 | minRender: Option[Double], 41 | maxRender: Option[Double], 42 | clampWithColor: Boolean = false, 43 | legends: List[LegendModel] = Nil 44 | ) extends StyleConf { 45 | def toStyle: OgcStyle = 46 | ColorRampStyle(name, title, colors, stops, minRender, maxRender, clampWithColor, legends) 47 | } 48 | 49 | /** 50 | * Styling in which both a color scheme and the data-to-color mapping is known 51 | */ 52 | final case class ColorMapConf( 53 | name: String, 54 | title: String, 55 | colorMap: ColorMap, 56 | legends: List[LegendModel] = Nil 57 | ) extends StyleConf { 58 | def toStyle: OgcStyle = 59 | ColorMapStyle(name, title, colorMap, legends) 60 | } 61 | 62 | final case class InterpolatedColorMapConf( 63 | name: String, 64 | title: String, 65 | colorMap: InterpolatedColorMap, 66 | legends: List[LegendModel] = Nil 67 | ) extends StyleConf { 68 | def toStyle: OgcStyle = 69 | InterpolatedColorMapStyle(name, title, colorMap, legends) 70 | } 71 | 72 | final case class RGBStyleConf(name: String = "RGB", title: String = "Default RGB rendering", legends: List[LegendModel] = Nil) extends StyleConf { 73 | def toStyle: OgcStyle = RGBStyle(name, title, legends) 74 | } 75 | -------------------------------------------------------------------------------- /stac-example/src/main/scala/geotrellis/server/ogc/stac/StacSearchCriteria.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc.stac 18 | 19 | sealed trait StacSearchCriteria 20 | case object ByLayer extends StacSearchCriteria 21 | case object ByCollection extends StacSearchCriteria 22 | -------------------------------------------------------------------------------- /stac-example/src/main/scala/geotrellis/server/ogc/stac/StacSummary.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc.stac 18 | 19 | import com.azavea.stac4s.StacCollection 20 | 21 | sealed trait StacSummary 22 | case object EmptySummary extends StacSummary 23 | case class CollectionSummary(asset: StacCollection) extends StacSummary 24 | -------------------------------------------------------------------------------- /stac-example/src/test/scala/geotrellis/IOSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis 18 | 19 | import cats.effect.IO 20 | import cats.effect.unsafe.IORuntime 21 | import org.typelevel.log4cats.Logger 22 | import org.typelevel.log4cats.slf4j.Slf4jLogger 23 | import org.scalatest.funspec.AsyncFunSpec 24 | import org.scalatest.matchers.should.Matchers 25 | import org.scalatest.{Assertion, Assertions} 26 | 27 | trait IOSpec extends AsyncFunSpec with Assertions with Matchers { 28 | implicit val runtime: IORuntime = IORuntime.global 29 | implicit val logger: Logger[IO] = Slf4jLogger.getLogger[IO] 30 | 31 | private val itWord = new ItWord 32 | 33 | def it(name: String)(test: => IO[Assertion]): Unit = itWord.apply(name)(test.unsafeToFuture()) 34 | 35 | def ignore(name: String)(test: => IO[Assertion]): Unit = super.ignore(name)(test.unsafeToFuture()) 36 | 37 | def describe(description: String)(fun: => Unit): Unit = super.describe(description)(fun) 38 | } 39 | -------------------------------------------------------------------------------- /stac-example/src/test/scala/geotrellis/server/ogc/stac/StacRepository.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.server.ogc.stac 18 | 19 | import geotrellis.raster.RasterSource 20 | import geotrellis.store.query 21 | import geotrellis.store.query.{Query, RepositoryM} 22 | import com.azavea.stac4s.api.client.{SearchFilters, SttpStacClient} 23 | 24 | import cats.effect.Sync 25 | import cats.syntax.functor._ 26 | import cats.syntax.traverse._ 27 | 28 | case class StacRepository[F[_]: Sync](client: SttpStacClient[F]) extends RepositoryM[F, List, RasterSource] { 29 | def store: F[List[RasterSource]] = find(query.all) 30 | 31 | def find(query: Query): F[List[RasterSource]] = 32 | SearchFilters 33 | .eval(ByLayer)(query) 34 | .map { filter => 35 | client 36 | .search(filter) 37 | .compile 38 | .toList 39 | .map(_.flatMap(_.assets.values.map(a => RasterSource(a.href)))) 40 | } 41 | .toList 42 | .sequence 43 | .map(_.flatten) 44 | } 45 | -------------------------------------------------------------------------------- /stac/src/main/scala/geotrellis/stac/extensions/proj/ProjItemExtension.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.stac.extensions.proj 18 | 19 | import geotrellis.stac._ 20 | 21 | import geotrellis.vector.{io => _, _} 22 | import com.azavea.stac4s.extensions.{ItemExtension, StacAssetExtension} 23 | import cats.kernel.Eq 24 | import io.circe.generic.extras.{ConfiguredJsonCodec, JsonKey} 25 | 26 | @ConfiguredJsonCodec 27 | case class ProjItemExtension( 28 | @JsonKey("proj:epsg") epsgCode: Option[Int], 29 | @JsonKey("proj:wkt2") wktString: Option[String], 30 | @JsonKey("proj:geometry") geometry: Option[Geometry], 31 | @JsonKey("proj:transform") transform: Option[ProjTransform], 32 | @JsonKey("proj:shape") shape: Option[ProjShape] 33 | ) 34 | 35 | object ProjItemExtension { 36 | implicit val eq: Eq[ProjItemExtension] = Eq.fromUniversalEquals 37 | 38 | implicit lazy val itemExtension: ItemExtension[ProjItemExtension] = ItemExtension.instance 39 | implicit lazy val stacAssetExtension: StacAssetExtension[ProjItemExtension] = StacAssetExtension.instance 40 | } 41 | -------------------------------------------------------------------------------- /stac/src/main/scala/geotrellis/stac/extensions/proj/ProjShape.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.stac.extensions.proj 18 | 19 | import geotrellis.raster.Dimensions 20 | import io.circe.{Decoder, Encoder} 21 | import cats.syntax.either._ 22 | 23 | import scala.util.Try 24 | 25 | case class ProjShape(cols: Long, rows: Long) { 26 | def toDimensions: Dimensions[Long] = Dimensions(cols, rows) 27 | def toList: List[Long] = List(cols, rows) 28 | } 29 | 30 | object ProjShape { 31 | def apply(list: List[Long]): Either[String, ProjShape] = 32 | Try { 33 | val List(cols, rows) = list.take(2) 34 | ProjShape(cols, rows) 35 | }.toEither.leftMap(_.getMessage) 36 | 37 | implicit val enProjShape: Encoder[ProjShape] = Encoder.encodeList[Long].contramap(_.toList) 38 | implicit val decProjShape: Decoder[ProjShape] = Decoder.decodeList[Long].emap(ProjShape.apply) 39 | } 40 | -------------------------------------------------------------------------------- /stac/src/main/scala/geotrellis/stac/extensions/proj/ProjTransform.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.stac.extensions.proj 18 | 19 | import io.circe.{Decoder, Encoder} 20 | import cats.syntax.either._ 21 | import geotrellis.raster.CellSize 22 | 23 | import scala.util.Try 24 | 25 | case class ProjTransform(upx: Double, xres: Double, xskew: Double, upy: Double, yskew: Double, yres: Double) { 26 | def toArray: Array[Double] = Array(upx, xres, xskew, upy, yskew, yres) 27 | def toList: List[Double] = toArray.toList 28 | def cellSize: CellSize = CellSize(math.abs(xres), math.abs(yres)) 29 | } 30 | 31 | object ProjTransform { 32 | def fromArray(transform: Array[Double]): ProjTransform = { 33 | val Array(upx, xres, xskew, upy, yskew, yres) = transform 34 | ProjTransform(upx, xres, xskew, upy, yskew, yres) 35 | } 36 | 37 | def fromList(transform: List[Double]): ProjTransform = fromArray(transform.toArray) 38 | 39 | def apply(transform: Array[Double]): Either[String, ProjTransform] = 40 | Try(fromArray(transform)).toEither.leftMap(_.getMessage) 41 | 42 | def apply(transform: List[Double]): Either[String, ProjTransform] = apply(transform.toArray) 43 | 44 | implicit val enProjGDALTransform: Encoder[ProjTransform] = Encoder.encodeList[Double].contramap(_.toList) 45 | implicit val decProjGDALTransform: Decoder[ProjTransform] = Decoder.decodeList[Double].emap { list => 46 | val transform = if (list.length > 6) { 47 | // handle rasterio affine transform 48 | val List(a, b, c, d, e, f) = list.take(6) 49 | List(c, a, b, f, d, e) 50 | } else list 51 | apply(transform) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /stac/src/main/scala/geotrellis/stac/raster/StacAssetOps.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.stac.raster 18 | 19 | import cats.syntax.apply._ 20 | import com.azavea.stac4s.StacAsset 21 | import com.azavea.stac4s.syntax._ 22 | import geotrellis.proj4.CRS 23 | import geotrellis.raster.{CellSize, Dimensions, GridExtent, RasterExtent} 24 | import geotrellis.stac.extensions.proj.{ProjItemExtension, ProjTransform} 25 | import geotrellis.vector._ 26 | 27 | case class StacAssetOps(self: StacAsset) { 28 | def projExtension: Option[ProjItemExtension] = self.getExtensionFields[ProjItemExtension].toOption 29 | 30 | def crs: Option[CRS] = 31 | projExtension 32 | .flatMap(_.epsgCode) 33 | .map(CRS.fromEpsgCode) 34 | .orElse { 35 | projExtension 36 | .flatMap(_.wktString) 37 | .flatMap(CRS.fromWKT) 38 | } 39 | 40 | def getGeometry: Option[Geometry] = projExtension.flatMap(_.geometry) 41 | def getExtent: Option[Extent] = getGeometry.map(geom => Extent(geom.getEnvelopeInternal)) 42 | 43 | def transform: Option[ProjTransform] = projExtension.flatMap(_.transform) 44 | 45 | // https://github.com/radiantearth/stac-spec/blob/v1.0.0-rc.1/item-spec/common-metadata.md#gsd 46 | def gsd: Option[Double] = self.extensionFields("gsd").flatMap(_.as[Double].toOption) 47 | 48 | // the cellSize can be extracted from the transform object or derived from the given extent and shape 49 | def cellSize: Option[CellSize] = 50 | transform 51 | .map(_.cellSize) 52 | .orElse((getExtent, dimensions).mapN { case (e, Dimensions(c, r)) => 53 | GridExtent(e, c, r).cellSize 54 | }) 55 | .orElse(gsd.map(d => CellSize(d, d))) 56 | 57 | def gridExtent: Option[GridExtent[Long]] = (getExtent, cellSize).mapN(GridExtent.apply[Long]) 58 | def rasterExtent: Option[RasterExtent] = gridExtent.map(_.toRasterExtent) 59 | def dimensions: Option[Dimensions[Long]] = projExtension.flatMap(_.shape).map(_.toDimensions) 60 | } 61 | -------------------------------------------------------------------------------- /stac/src/main/scala/geotrellis/stac/raster/StacCollectionSource.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.stac.raster 18 | 19 | import io.circe.syntax._ 20 | import geotrellis.raster._ 21 | import com.azavea.stac4s.{StacCollection, StacExtent} 22 | 23 | case class StacCollectionSource(asset: StacCollection, source: RasterSource) extends StacSource[StacCollection] { 24 | val stacExtent: StacExtent = asset.extent 25 | val name: SourceName = StringName(asset.id) 26 | 27 | lazy val attributes: Map[String, String] = 28 | asset.asJson.asObject 29 | .map(_.toMap) 30 | .getOrElse(Map.empty) 31 | .map { case (key, value) => key -> value.toString } 32 | 33 | override def toString: String = s"StacCollectionSource($name, $asset, $source)" 34 | } 35 | -------------------------------------------------------------------------------- /stac/src/main/scala/geotrellis/stac/raster/StacItemAsset.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.stac.raster 18 | 19 | import geotrellis.stac._ 20 | import com.azavea.stac4s.{StacAsset, StacItem} 21 | import geotrellis.proj4.CRS 22 | import geotrellis.raster.{CellSize, Dimensions, GridExtent, RasterExtent} 23 | import geotrellis.stac.extensions.proj.ProjTransform 24 | import geotrellis.vector.{Extent, Geometry} 25 | 26 | case class StacItemAsset(itemAsset: StacAsset, item: StacItem) { 27 | def href: String = itemAsset.href 28 | def bandCount: Option[Int] = item.bandCount 29 | def crs: Option[CRS] = itemAsset.crs.orElse(item.crs) 30 | 31 | // geometry can be taken from the proj extension or projected from the LatLng geometry 32 | def getGeometry: Option[Geometry] = itemAsset.getGeometry.orElse(item.getGeometry) 33 | def getExtent: Option[Extent] = itemAsset.getExtent.orElse(item.getExtent) 34 | 35 | def transform: Option[ProjTransform] = itemAsset.transform.orElse(item.transform) 36 | 37 | // https://github.com/radiantearth/stac-spec/blob/v1.0.0-rc.1/item-spec/common-metadata.md#gsd 38 | def gsd: Option[Double] = itemAsset.gsd.orElse(item.gsd) 39 | 40 | // the cellSize can be extracted from the transform object or derived from the given extent and shape 41 | def cellSize: Option[CellSize] = itemAsset.cellSize.orElse(item.cellSize) 42 | 43 | def gridExtent: Option[GridExtent[Long]] = itemAsset.gridExtent.orElse(item.gridExtent) 44 | def rasterExtent: Option[RasterExtent] = itemAsset.rasterExtent.orElse(item.rasterExtent) 45 | def dimensions: Option[Dimensions[Long]] = itemAsset.dimensions.orElse(item.dimensions) 46 | } 47 | -------------------------------------------------------------------------------- /stac/src/main/scala/geotrellis/stac/raster/StacItemAssetMetadata.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.stac.raster 18 | 19 | import geotrellis.stac._ 20 | import geotrellis.proj4.CRS 21 | import geotrellis.raster.{CellSize, CellType, GridExtent, RasterMetadata, SourceName} 22 | 23 | case class StacItemAssetMetadata( 24 | name: SourceName, 25 | crs: CRS, 26 | bandCount: Int, 27 | cellType: CellType, 28 | gridExtent: GridExtent[Long], 29 | resolutions: List[CellSize], 30 | stacItemAsset: StacItemAsset 31 | ) extends RasterMetadata { 32 | def attributes: Map[String, String] = stacItemAsset.item.properties.toMap 33 | def attributesForBand(band: Int): Map[String, String] = Map.empty 34 | } 35 | -------------------------------------------------------------------------------- /stac/src/main/scala/geotrellis/stac/raster/StacSource.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.stac.raster 18 | 19 | import geotrellis.stac._ 20 | 21 | import com.azavea.stac4s.StacExtent 22 | import geotrellis.proj4.{CRS, LatLng} 23 | import geotrellis.raster.{RasterSource, SourceName} 24 | import geotrellis.vector.{Extent, ProjectedExtent} 25 | 26 | trait StacSource[T] { 27 | val crs: CRS = LatLng 28 | def projectedExtent: ProjectedExtent = ProjectedExtent(extent, crs) 29 | def extent: Extent = stacExtent.spatial.toExtent 30 | 31 | def asset: T 32 | def name: SourceName 33 | def stacExtent: StacExtent 34 | // native projection can be not LatLng 35 | def source: RasterSource 36 | 37 | def attributes: Map[String, String] 38 | } 39 | -------------------------------------------------------------------------------- /stac/src/main/scala/geotrellis/stac/util/logging/syntax.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Azavea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package geotrellis.stac.util.logging 18 | 19 | import com.azavea.stac4s.api.client.{StreamingStacClient, StreamingStacClientFS2} 20 | import cats.effect.Sync 21 | import cats.tagless.{ApplyK, Derive} 22 | import fs2.Stream 23 | import tofu.higherKind.Mid 24 | 25 | object syntax { 26 | 27 | /** 28 | * Syntax to attach two [[Mid]] instances (each for a separate param) to a service with two type params. 29 | */ 30 | implicit class MidOps3Tuple[U[_[_], _[_], _], F[_], G[_], A](val mfg: (U[Mid[F, *], G, A], U[F, Mid[G, *], A])) extends AnyVal { 31 | def attach(u: U[F, G, A])(implicit af: ApplyK[U[F, *[_], A]], ag: ApplyK[U[*[_], G, A]]): U[F, G, A] = 32 | Mid.attach[U[*[_], G, A], F](mfg._1)(Mid.attach[U[F, *[_], A], G](mfg._2)(u)) 33 | } 34 | 35 | implicit class MidOps3TupleReverse[U[_[_], _[_], _], F[_], G[_], A](val mfg: (U[F, Mid[G, *], A], U[Mid[F, *], G, A])) extends AnyVal { 36 | def attach(u: U[F, G, A])(implicit af: ApplyK[U[F, *[_], A]], ag: ApplyK[U[*[_], G, A]]): U[F, G, A] = 37 | mfg.swap.attach(u) 38 | } 39 | 40 | implicit def stacClientApplyKF[F[_]]: ApplyK[StreamingStacClient[*[_], Stream[F, *]]] = Derive.applyK[StreamingStacClient[*[_], Stream[F, *]]] 41 | implicit def stacClientApplyKG[F[_]]: ApplyK[StreamingStacClient[F, *[_]]] = Derive.applyK[StreamingStacClient[F, *[_]]] 42 | 43 | implicit class StreamingStacClientOps[F[_]](val self: StreamingStacClientFS2[F]) extends AnyVal { 44 | def withLogging(implicit sync: Sync[F]): StreamingStacClientFS2[F] = (StacClientLoggingMid[F], StreamingStacClientLoggingMid[F]).attach(self) 45 | } 46 | } 47 | --------------------------------------------------------------------------------