Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- added ensure_adminuser command to add subtly more sophisticated behaviour to Django's createsuperuser - i.e. don't raise non-zero exit code if superuser already exists, add more useful output.
- Use uv in the docker image.
- Empty arrays to empty responses for a consistent response structure.
- Experimental feature: mesh tile serving using [Martin](https://github.com/maplibre/martin).

### Changed
- Inappropriate use of 204 code: RecentRoutesView changed from 204 to 200 OK with an empty array and the original message ("No recent routes found for today.").
Expand Down
6 changes: 4 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV DJANGO_SETTINGS_MODULE=polarrouteserver.settings.development

# Install GDAL - used by Fiona
RUN apt-get update && apt-get install -y \
gdal-bin \
libgdal-dev \
binutils\
libproj-dev \
postgresql-client \
&& rm -rf /var/lib/apt/lists/*

ENV GDAL_CONFIG=/usr/bin/gdal-config
Expand All @@ -22,4 +24,4 @@ COPY pyproject.toml manage.py /usr/src/app/
COPY polarrouteserver /usr/src/app/polarrouteserver

RUN uv pip install --system -e .
RUN uv pip install --system django-debug-toolbar
RUN uv pip install --system django-debug-toolbar
19 changes: 18 additions & 1 deletion compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ services:
POLARROUTE_DB_PORT: 5432
POLARROUTE_SUPERUSER_USERNAME: admin
POLARROUTE_SUPERUSER_PASSWORD: password
POLARROUTE_LOG_LEVEL: INFO
entrypoint: /usr/src/app/docker-entrypoint.sh
command: ["python", "manage.py", "runserver", "0.0.0.0:8000"]
volumes:
Expand All @@ -22,7 +23,7 @@ services:
- celery

db:
image: postgres:16
image: postgis/postgis
restart: always
environment:
POSTGRES_USER: polarroute
Expand All @@ -31,6 +32,7 @@ services:
PGDATA: /var/lib/postgresql/data/pgdata
volumes:
- db-data:/var/lib/postgresql/data
- ./postgis-init-scripts/:/docker-entrypoint-initdb.d
ports:
- 5432:5432

Expand All @@ -55,6 +57,21 @@ services:
- 5672:5672
- 15672:15672

martin:
image: ghcr.io/maplibre/martin
restart: unless-stopped
command: ["--config", "/config/config.yaml"]
environment:
# DATABASE_URL: postgres://polarroute@db:5432/polarroute
PGPASSWORD: polarroute
RUST_LOG: debug
ports:
- "3000:3000"
volumes:
- ./martin:/config
depends_on:
- db

swagger:
profiles: ['swagger']
image: swaggerapi/swagger-ui
Expand Down
263 changes: 263 additions & 0 deletions martin/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
web_ui: enable-for-all
base_path: /tiles
postgres:
# Database connection string.
#
# You can use environment variables too, for example:
# connection_string: $DATABASE_URL
# connection_string: ${DATABASE_URL:-postgres://postgres@localhost/db}
connection_string: 'postgres://polarroute:polarroute@db:5432/polarroute?sslmode=disable'

# Same as PGSSLCERT for psql
# ssl_cert: './postgresql.crt'
# Same as PGSSLKEY for psql
# ssl_key: './postgresql.key'
# Same as PGSSLROOTCERT for psql
# ssl_root_cert: './root.crt'

# If a spatial table has SRID 0, then this SRID will be used as a fallback
default_srid: 4326

# Maximum Postgres connections pool size [default: 20]
pool_size: 20

# Limit the number of geo features per tile.
#
# If the source table has more features than set here, they will not be
# included in the tile and the result will look "cut off"/incomplete.
# This feature allows you to put a maximum latency bound on tiles with an
# extreme amount of detail at the cost of not returning all data.
# It is sensible to set this limit if you have user generated/untrusted
# geodata, e.g. a lot of data points at [Null Island]
# (https://en.wikipedia.org/wiki/Null_Island).
max_feature_count: null # either a positive integer, or null=unlimited (default)

# Specify how bounds should be computed for the spatial PG tables [default: quick]
#
# Options:
# - `calc` compute table geometry bounds on startup.
# - `quick` same as 'calc', but the calculation will be aborted after 5 seconds.
# - `skip` does not compute table geometry bounds on startup.
auto_bounds: quick

# Enable automatic discovery of tables and functions.
# You may set this to `false` to disable.
auto_publish: false
# # Optionally limit to just these schemas
# from_schemas:
# - public
# - my_schema
# Here we enable both tables and functions auto discovery.
# You can also enable just one of them by not mentioning the other, or
# setting it to false. Setting one to true disables the other one as well.
# E.g. `tables: false` enables just the functions auto-discovery.
# tables:
# Optionally set how source ID should be generated based on the table's name,
# schema, and geometry column
# source_id_format: 'table.{schema}.{table}.{column}'
# Add more schemas to the ones listed above
# from_schemas: my_other_schema
# A table column to use as the feature ID
# If a table has no column with this name, `id_column` will not be set for
# that table.
# If a list of strings is given, the first found column will be treated as a
# feature ID.
# id_columns: feature_id
# Controls if geometries should be clipped or encoded as is [default: true]
# clip_geom: true
# Buffer distance in tile coordinate space to optionally clip geometries,
# optional, default to 64
# buffer: 64
# Tile extent in tile coordinate space, optional, default to 4096
# extent: 4096
# functions:
# # Optionally limit to just these schemas
# from_schemas:
# - public
# - my_schema
# # Optionally set how source ID should be generated based on the function's
# # name and schema
# source_id_format: '{schema}.{function}'

# Associative arrays of table sources
# tables:
# mesh:
# # ID of the MVT layer (optional, defaults to table name)
# layer_id: mesh

# # Table schema (required)
# schema: public

# # Table name (required)
# table: route_api_mesh

# # Geometry SRID (required)
# srid: 4326

# # Geometry column name (required)
# geometry_column: geometry

# # Feature id column name
# id_column: id

# # An integer specifying the minimum zoom level
# minzoom: 0

# # An integer specifying the maximum zoom level. MUST be >= minzoom
# maxzoom: 30

# # The maximum extent of available map tiles. Bounds MUST define an area
# # covered by all zoom levels. The bounds are represented in WGS:84 latitude
# # and longitude values, in the order left, bottom, right, top. Values may
# # be integers or floating point numbers.
# bounds: [ -180.0, -90.0, 180.0, 90.0 ]

# # Tile extent in tile coordinate space
# extent: 4096

# # Buffer distance in tile coordinate space to optionally clip geometries
# buffer: 64

# # Boolean to control if geometries should be clipped or encoded as is
# clip_geom: true

# # Geometry type
# geometry_type: MULTIPOLYGON

# # List of columns, that should be encoded as tile properties (required)
# #
# # Keys and values are the names and descriptions of attributes available in this layer.
# # Each value (description) must be a string that describes the underlying data.
# # If no fields (=just the geometry) should be encoded, an empty object is allowed.
# properties:
# id: int4

# Associative arrays of function sources
functions:
mesh:
# Schema name (required)
schema: public

# Function name (required)
function: get_mesh_zxy

# An integer specifying the minimum zoom level
minzoom: 0

# An integer specifying the maximum zoom level. MUST be >= minzoom
maxzoom: 30

# The maximum extent of available map tiles. Bounds MUST define an area
# covered by all zoom levels. The bounds are represented in WGS:84
# latitude and longitude values, in the order left, bottom, right, top.
# Values may be integers or floating point numbers.
bounds: [ -180.0, -90.0, 180.0, 90.0 ]

# Publish PMTiles files from local disk or proxy to a web server
# pmtiles:
# # Size of the directory cache (in MB).
# # Defaults to cache_size_mb / 4
# #
# # Note:
# # Tile and directory caching are complementary.
# # For good performance, you want
# # - directory caching (to not resolve the directory on each request) and
# # - tile caching (for high access tiles)
# directory_cache_size_mb: 128

# # You can pass options for pmtiles files located on remote storages here.
# #
# # The avaliable options are documented here:
# # - local file sources don't have options
# # - Http(s) Source: https://docs.rs/object_store/latest/object_store/http/struct.HttpBuilder.html
# # - Amazon S3: https://docs.rs/object_store/latest/object_store/aws/struct.AmazonS3Builder.html
# # - Google Cloud Storage: https://docs.rs/object_store/latest/object_store/gcp/struct.GoogleCloudStorageBuilder.html
# # - Microsoft Azure: https://docs.rs/object_store/latest/object_store/azure/struct.MicrosoftAzureBuilder.html
# #
# # Example for configuring a source to allow http
# allow_http: true

# paths:
# # scan this whole dir, matching all *.pmtiles files
# - /dir-path
# # specific pmtiles file will be published as a pmt source (filename without extension)
# - /path/to/pmt.pmtiles
# # A web server with a PMTiles file that supports range requests
# - https://example.org/path/tiles.pmtiles
# sources:
# # named source matching source name to a single file
# pm-src1: /path/to/pmt.pmtiles
# # A named source to a web server with a PMTiles file that supports range requests
# pm-web2: https://example.org/path/tiles.pmtiles

# Publish MBTiles files
# mbtiles:
# paths:
# # scan this whole dir, matching all *.mbtiles files
# - /dir-path
# # specific mbtiles file will be published as mbtiles2 source
# - /path/to/mbtiles.mbtiles
# sources:
# # named source matching source name to a single file
# mb-src1: /path/to/mbtiles1.mbtiles

# # Sprite configuration
# sprites:
# # Size of the sprite cache (in MB).
# # Defaults to cache_size_mb / 8
# cache_size_mb: 64

# paths:
# # all SVG files in this dir will be published as a "my_images" sprite source
# - /path/to/my_images
# sources:
# # SVG images in this directory will be published as a "my_sprites" sprite source
# my_sprites: /path/to/some_dir

# Font configuration
# fonts:
# # Size of the sprite cache (in MB).
# # Defaults to cache_size_mb / 4
# cache_size_mb: 64

# # A list of *.otf, *.ttf, and *.ttc font files and dirs to search recursively.
# paths:
# - /path/to/font/file.ttf
# - /path/to/font_dir

# Publish MapLibre style files
# In the future, the style files will be used for the server-side rendering as well
styles:
paths:
# publish all *.json files in this directory
# The name of the file will be used as the style name
- /config/style/
# sources:
# # publish a JSON file found at this path as `some_style_name`
# #
# # Contrairy to paths, if directories are specified, Martin will print a
# # warning and ignore them.
# # To serve a style-directory, use the `paths` section above or name each
# # style individually. This prevents footguns with names being unclear.
# some_style_name: /path/to/this/style.json
# # Publish specific file as `other_style_name`
# other_style_name: /path/to/other_style.json

# If set, the version of the tileset (as specified in the MBTiles or PMTiles metadata)
# will be embedded in the TileJSON `tiles` URL, with the set identifier.
# This is useful to give clients a better way to cache-bust a CDN:
# 1. maplibre requests tilejson, tilejson contains the tiles URL. This is always up-to-date.
# 2. maplibre requests each tile it requires, with the tiles URL in the tilejson.
# 3. Add `Control: public, max-age=..., immutable` on the tile responses
# optimize browser/CDN cache hit rates, while also making sure that
# old tiles aren't served when a new tileset is deployed.
#
# The CDN must handle query parameters for caching to work correctly.
# Many CDNs ignore them by default.
#
# For example, if
# - the setting here is `version`, and
# - the PMTiles tileset version is `1.0.0`, the
# TileJSON will be:
# { ..., "tiles": [".../{z}/{x}/{y}?version=1.0.0"], ... }
tilejson_url_version_param: null # a string, such as `version` or `v`
Loading