This Phoenix application provides a STAC (SpatioTemporal Asset Catalog) API implementation with both REST API endpoints and HTML browser interface for exploring geospatial data catalogs.
- Fork the repository from GitHub.
- Create a feature branch for your changes.
- Submit a Pull Request to the
masterbranch. - Keep pull requests focused on specific updates.
We maintain an internal GitLab (origin) and a public GitHub mirror (outbound). To keep histories identical across both while using rebases, follow the "Mirroring Workflow":
Create a combined all remote that pushes to both GitLab and GitHub:
git remote add all git@gitlab.ut.ee:geog/stac_api_ex.git
git remote set-url --add --push all git@gitlab.ut.ee:geog/stac_api_ex.git
git remote set-url --add --push all git@github.com:LandscapeGeoinformatics/stac_api_ex.gitAlways pull from the internal GitLab and push to the combined remote:
# Pull and rebase from internal source
git pull origin master --rebase
# Push to both remotes simultaneously
git push all master --force-with-leaseWhen merging external contributions from GitHub:
# Fetch the PR branch (replace ID with PR number)
git fetch outbound pull/ID/head:pr-branch
# Rebase and merge locally
git checkout master
git rebase pr-branch
# Update both remotes
git push all master --force-with-lease
git branch -D pr-branchThe application uses a dedicated importer module to load STAC JSON data into PostgreSQL with PostGIS extension.
# Import from default directory (priv/stac_data)
mix stac.import
# Import from custom directory
mix stac.import /path/to/stac/data
The importer (StacApi.Data.Importer) performs the following:
-
Collections Import: Scans for
collection.jsonfiles using pattern**/collection.json -
Items Import: Processes all
*.jsonfiles exceptcollection.jsonandcatalog.json -
Data Validation: Only imports valid STAC Items (Features with geometry)
-
Conflict Resolution: Uses
on_conflict: :replace_allfor upserts
priv/stac_data/
├── collection1/
│ ├── collection.json
│ ├── item1.json
│ ├── item2.json
│ └── assets/
│ ├── item1.tif
│ └── item2.tif
└── collection2/
├── collection.json
└── items/
├── item3.json
└── item4.json
The import can be triggered:
-
CLI Task:
mix stac.import(shown above) -
Programmatically: Call
StacApi.Data.Importer.import_from_directory/1 -
Potential Web Interface: Could be extended to provide admin upload interface
-
Collections: Stores STAC Collection metadata
-
Items: Stores STAC Items with PostGIS geometry fields
-
Relationships: Items reference collections via
collection_id
The browser interface (StacBrowserController) provides:
-
Directory Navigation: Browse STAC data structure
-
Search Interface: Query items with various filters
-
File Viewing: Display JSON content inline
The application uses PostgreSQL with PostGIS extension for spatial data:
# Database configuration includes PostGIS extension
config :stac_api, StacApi.Repo,
extensions: [{Geo.PostGIS.Extension, library: Geo}]Geo.ex provides Elixir structs for geometric data:
# Converting GeoJSON to PostGIS format
{:ok, geo_struct} = Geo.JSON.decode(geojson_geometry)
# Geo structs used in schema
field :geometry, Geo.PostGIS.GeometryThe Search module can be extended for spatial queries:
# Example spatial query (to be implemented)
from i in Item,
where: st_intersects(i.geometry, ^query_polygon)StacApi/
├── Data/ # Data layer
│ ├── Collection.ex # Collection schema
│ ├── Item.ex # Item schema
│ ├── Search.ex # Search logic
│ └── Importer.ex # Data import
├── Web/ # Web layer
│ ├── Controllers/
│ │ ├── RootController.ex
│ │ ├── SearchController.ex
│ │ └── StacBrowserController.ex
│ └── Router.ex
└── Repo.ex # Database interface
For merging into an existing Phoenix application (e.g., geokuup.ee):
config :your_app, YourApp.Repo,
extensions: [{Geo.PostGIS.Extension, library: Geo}]# STAC API — public read (optional auth for private catalogs)
scope "/stac/api/v1", YourAppWeb do
pipe_through [:api, :read_auth]
get "/", RootController, :index
get "/conformance", RootController, :conformance
get "/search", SearchController, :index
post "/search", SearchController, :index
get "/collections", CollectionsController, :index
get "/collections/:id", CollectionsController, :show
get "/collections/:id/items", CollectionsController, :items
get "/collections/:collection_id/items/:item_id", CollectionsController, :show_item
end
# Management API — requires RW key
scope "/stac/manage/v1", YourAppWeb do
pipe_through [:api, :auth]
# catalogs, collections, items CRUD routes
end
scope "/stac/web", YourAppWeb do
pipe_through :browser
get "/browse", StacBrowserController, :index
get "/browse/*path", StacBrowserController, :show
end-
Copy
StacApi.Data.*modules to your app's data layer -
Adapt controllers to your app's naming convention
-
Update references to use your app's Repo module
Add to mix.exs:
{:geo, "~> 3.4"},
{:geo_postgis, "~> 3.4"},
{:jason, "~> 1.2"}-
Database: Run STAC table migrations in target app
-
Modules: Namespace modules under target app
-
Routes: Integrate routes into existing router
-
Configuration: Merge database and geo configurations
-
Assets: Ensure asset serving routes don't conflict
# config/runtime.exs
import Config
config :stac_api, :stac_data_path,
System.get_env("STAC_DATA_PATH") || "priv/stac_data"# config/dev.exs
config :stac_api, :stac_data_path, "priv/stac_data"
# config/prod.exs
config :stac_api, :stac_data_path, "/opt/app/stac_data"# Replace hardcoded paths with configuration
defmodule StacApiWeb.StacBrowserController do
@stac_data_path Application.compile_env(:stac_api, :stac_data_path, "priv/stac_data")
# Or for runtime configuration:
defp get_stac_data_path do
Application.get_env(:stac_api, :stac_data_path, "priv/stac_data")
end
end# .env or deployment configuration
export STAC_DATA_PATH="/mnt/stac-storage"
export DATABASE_URL="postgresql://user:pass@localhost/stac_prod"
# config/config.exs - Base configuration
config :stac_api,
stac_data_path: "priv/stac_data",
max_search_results: 10000,
default_page_size: 10
# config/runtime.exs - Runtime overrides
config :stac_api,
stac_data_path: System.get_env("STAC_DATA_PATH") || "priv/stac_data"GET /stac/api/v1/- STAC root catalogGET /stac/api/v1/conformance- OGC/STAC conformance classesGET /stac/api/v1/search- Search STAC items (GET + POST)GET /stac/api/v1/collections- List all collectionsGET /stac/api/v1/collections/:id- Get specific collectionGET /stac/api/v1/collections/:id/items- Get items in collectionGET /stac/api/v1/collections/:collection_id/items/:item_id- Get specific item
- Full CRUD for catalogs, collections, and items
- Bulk item import (
POST /stac/manage/v1/items/import)
GET /stac/web/browse- HTML directory browserGET /stac/web/browse/*path- Browse specific pathsGET /stac/web/search- HTML search interface
-
Setup: Configure database with PostGIS
-
Import Data: Use
mix stac.importto load JSON files -
Development: Use browser interface for testing
-
API Testing: Query REST endpoints for integration
-
Deployment: Configure production data paths and database