2018-01-16 15:55:05 +03:00
# Martin
2017-10-09 14:29:03 +03:00
2019-09-30 12:42:49 +03:00
[![CI ](https://github.com/urbica/martin/workflows/CI/badge.svg )](https://github.com/urbica/martin/actions)
2020-05-05 14:13:48 +03:00
![Security audit ](https://github.com/urbica/martin/workflows/Security%20audit/badge.svg )
2019-01-28 11:26:26 +03:00
[![Docker pulls ](https://img.shields.io/docker/pulls/urbica/martin.svg )](https://hub.docker.com/r/urbica/martin)
2017-10-15 14:48:23 +03:00
2018-10-20 12:24:28 +03:00
Martin is a [PostGIS ](https://github.com/postgis/postgis ) [vector tiles ](https://github.com/mapbox/vector-tile-spec ) server suitable for large databases. Martin is written in [Rust ](https://github.com/rust-lang/rust ) using [Actix ](https://github.com/actix/actix-web ) web framework.
2017-10-09 14:29:03 +03:00
2019-02-04 20:22:02 +03:00
![Martin ](https://raw.githubusercontent.com/urbica/martin/master/logo.png )
2018-10-19 19:11:44 +03:00
2019-01-28 11:16:24 +03:00
- [Requirements ](#requirements )
- [Installation ](#installation )
- [Usage ](#usage )
2019-07-03 17:29:38 +03:00
- [API ](#api )
2019-02-04 13:39:57 +03:00
- [Using with Mapbox GL JS ](#using-with-mapbox-gl-js )
2021-02-23 12:01:45 +03:00
- [Using with Leaflet ](#using-with-leaflet )
2021-10-07 17:00:21 +03:00
- [Using with deck.gl ](#using-with-deckgl )
2019-01-28 11:16:24 +03:00
- [Table Sources ](#table-sources )
- [Table Sources List ](#table-sources-list )
- [Table Source TileJSON ](#table-source-tilejson )
2019-02-04 13:39:57 +03:00
- [Table Source Tiles ](#table-source-tiles )
2021-04-24 20:19:37 +03:00
- [Composite Sources ](#composite-sources )
- [Composite Source TileJSON ](#composite-source-tilejson )
- [Composite Source Tiles ](#composite-source-tiles )
2019-01-28 11:16:24 +03:00
- [Function Sources ](#function-sources )
- [Function Sources List ](#function-sources-list )
- [Function Source TileJSON ](#function-source-tilejson )
- [Function Source Tiles ](#function-source-tiles )
- [Command-line Interface ](#command-line-interface )
- [Environment Variables ](#environment-variables )
2019-02-04 13:39:57 +03:00
- [Configuration File ](#configuration-file )
2019-01-28 11:16:24 +03:00
- [Using with Docker ](#using-with-docker )
2019-08-28 14:04:15 +03:00
- [Using with Docker Compose ](#using-with-docker-compose )
2019-02-04 13:14:51 +03:00
- [Using with Nginx ](#using-with-nginx )
2021-10-07 17:00:21 +03:00
- [Rewriting URLs ](#rewriting-urls )
- [Caching tiles ](#caching-tiles )
2019-01-28 11:16:24 +03:00
- [Building from Source ](#building-from-source )
2019-02-04 20:22:02 +03:00
- [Debugging ](#debugging )
2019-01-28 11:16:24 +03:00
- [Development ](#development )
2021-10-18 14:42:11 +03:00
- [Recipes ](#recipes )
- [Using with DigitalOcean PostgreSQL ](#using-with-digitalocean-postgresql )
- [Using with Heroku PostgreSQL ](#using-with-heroku-postgresql )
2019-01-28 11:16:24 +03:00
2019-01-09 10:01:03 +03:00
## Requirements
Martin requires PostGIS >= 2.4.0.
2017-10-09 14:29:03 +03:00
## Installation
2018-10-17 11:49:02 +03:00
You can download martin from [Github releases page ](https://github.com/urbica/martin/releases ).
2018-10-10 16:32:24 +03:00
2019-09-30 18:54:25 +03:00
| Platform | Downloads (latest) |
| -------- | ----------------------- |
| Linux | [64-bit][rl-linux-tar] |
| macOS | [64-bit][rl-macos-tar] |
| Windows | [64-bit][rl-winx64-zip] |
[rl-linux-tar]: https://github.com/urbica/martin/releases/latest/download/martin-Linux-x86_64.tar.gz
[rl-macos-tar]: https://github.com/urbica/martin/releases/latest/download/martin-Darwin-x86_64.tar.gz
[rl-winx64-zip]: https://github.com/urbica/martin/releases/latest/download/martin-Windows-x86_64.zip
2018-10-17 11:49:02 +03:00
If you are using macOS and [Homebrew ](https://brew.sh/ ) you can install martin using Homebrew tap.
2018-10-10 16:32:24 +03:00
```shell
2018-10-10 17:23:41 +03:00
brew tap urbica/tap
2018-10-10 16:32:24 +03:00
brew install martin
```
2017-10-09 14:29:03 +03:00
2019-09-30 18:54:25 +03:00
You can also use [official Docker image ](https://hub.docker.com/r/urbica/martin )
```shell
docker run -p 3000:3000 -e DATABASE_URL=postgres://postgres@localhost/db urbica/martin
```
2017-10-09 14:29:03 +03:00
## Usage
2018-10-10 19:27:21 +03:00
Martin requires a database connection string. It can be passed as a command-line argument or as a `DATABASE_URL` environment variable.
2018-10-10 16:32:24 +03:00
```shell
martin postgres://postgres@localhost/db
```
2017-10-09 14:29:03 +03:00
2021-04-24 20:19:37 +03:00
Martin provides [TileJSON ](https://github.com/mapbox/tilejson-spec ) endpoint for each [geospatial-enabled ](https://postgis.net/docs/postgis_usage.html#geometry_columns ) table in your database.
2019-07-03 17:29:38 +03:00
## API
2021-04-24 20:19:37 +03:00
| Method | URL | Description |
| ------ | -------------------------------------------------------------------------------- | ------------------------------------------------------- |
| `GET` | `/index.json` | [Table Sources List ](#table-sources-list ) |
| `GET` | `/{schema_name}.{table_name}.json` | [Table Source TileJSON ](#table-source-tilejson ) |
| `GET` | `/{schema_name}.{table_name}/{z}/{x}/{y}.pbf` | [Table Source Tiles ](#table-source-tiles ) |
| `GET` | `/{schema_name1}.{table_name1},...,{schema_nameN}.{table_nameN}.json` | [Composite Source TileJSON ](#composite-source-tilejson ) |
| `GET` | `/{schema_name1}.{table_name1},...,{schema_nameN}.{table_nameN}/{z}/{x}/{y}.pbf` | [Composite Source Tiles ](#composite-source-tiles ) |
| `GET` | `/rpc/index.json` | [Function Sources List ](#function-sources-list ) |
| `GET` | `/rpc/{schema_name}.{function_name}.json` | [Function Source TileJSON ](#function-source-tilejson ) |
| `GET` | `/rpc/{schema_name}.{function_name}/{z}/{x}/{y}.pbf` | [Function Source Tiles ](#function-source-tiles ) |
| `GET` | `/healthz` | Martin server health check: returns `200 OK` |
2019-07-03 17:29:38 +03:00
2019-02-04 13:39:57 +03:00
## Using with Mapbox GL JS
[Mapbox GL JS ](https://github.com/mapbox/mapbox-gl-js ) is a JavaScript library for interactive, customizable vector maps on the web. It takes map styles that conform to the
[Mapbox Style Specification ](https://www.mapbox.com/mapbox-gl-js/style-spec ), applies them to vector tiles that
conform to the [Mapbox Vector Tile Specification ](https://github.com/mapbox/vector-tile-spec ), and renders them using
WebGL.
You can add a layer to the map and specify martin TileJSON endpoint as a vector source URL. You should also specify a `source-layer` property. For [Table Sources ](#table-sources ) it is `{schema_name}.{table_name}` by default.
```js
map.addLayer({
2021-10-07 17:00:21 +03:00
id: 'public.points',
type: 'circle',
2019-02-04 13:39:57 +03:00
source: {
2021-10-07 17:00:21 +03:00
type: 'vector',
url: 'http://localhost:3000/public.points.json'
2021-02-23 12:01:45 +03:00
},
2021-10-07 17:00:21 +03:00
'source-layer': 'public.points',
2021-02-23 12:01:45 +03:00
paint: {
2021-10-07 17:00:21 +03:00
'circle-color': 'red'
}
2019-02-04 13:39:57 +03:00
});
```
2021-04-24 20:19:37 +03:00
You can also combine multiple tables into one source with [Composite Sources ](#composite-sources ). Each [Table Source ](#table-sources ) in Composite Source can be accessed with its `{schema_name}.{table_name}` as a `source-layer` property.
```js
2021-10-07 17:00:21 +03:00
map.addSource('points', {
type: 'vector',
url: `http://0.0.0.0:3000/public.points1,public.points2.json`
2021-04-24 20:19:37 +03:00
});
map.addLayer({
2021-10-07 17:00:21 +03:00
id: 'red_points',
type: 'circle',
source: 'points',
'source-layer': 'public.points1',
2021-04-24 20:19:37 +03:00
paint: {
2021-10-07 17:00:21 +03:00
'circle-color': 'red'
}
2021-04-24 20:19:37 +03:00
});
map.addLayer({
2021-10-07 17:00:21 +03:00
id: 'blue_points',
type: 'circle',
source: 'points',
'source-layer': 'public.points2',
2021-04-24 20:19:37 +03:00
paint: {
2021-10-07 17:00:21 +03:00
'circle-color': 'blue'
}
2021-04-24 20:19:37 +03:00
});
```
2021-02-23 12:01:45 +03:00
## Using with Leaflet
[Leaflet ](https://github.com/Leaflet/Leaflet ) is the leading open-source JavaScript library for mobile-friendly interactive maps.
You can add vector tiles using [Leaflet.VectorGrid ](https://github.com/Leaflet/Leaflet.VectorGrid ) plugin. You must initialize a [VectorGrid.Protobuf ](https://leaflet.github.io/Leaflet.VectorGrid/vectorgrid-api-docs.html#vectorgrid-protobuf ) with a URL template, just like in L.TileLayers. The difference is that you should define the styling for all the features.
```js
L.vectorGrid
.protobuf('http://localhost:3000/public.points/{z}/{x}/{y}.pbf', {
vectorTileLayerStyles: {
'public.points': {
color: 'red',
2021-10-07 17:00:21 +03:00
fill: true
}
}
2021-02-23 12:01:45 +03:00
})
.addTo(map);
```
2021-02-25 13:33:44 +03:00
## Using with deck.gl
[deck.gl ](https://deck.gl/ ) is a WebGL-powered framework for visual exploratory data analysis of large datasets.
You can add vector tiles using [MVTLayer ](https://deck.gl/docs/api-reference/geo-layers/mvt-layer ). MVTLayer `data` property defines the remote data for the MVT layer. It can be
2021-10-07 17:00:21 +03:00
- `String` : Either a URL template or a [TileJSON ](https://github.com/mapbox/tilejson-spec ) URL.
- `Array` : an array of URL templates. It allows to balance the requests across different tile endpoints. For example, if you define an array with 4 urls and 16 tiles need to be loaded, each endpoint is responsible to server 16/4 tiles.
- `JSON` : A valid [TileJSON object ](https://github.com/mapbox/tilejson-spec/tree/master/2.2.0 ).
2021-02-25 13:33:44 +03:00
```js
const pointsLayer = new MVTLayer({
data: 'http://localhost:3000/public.points.json', // 'http://localhost:3000/public.table_source/{z}/{x}/{y}.pbf'
pointRadiusUnits: 'pixels',
getRadius: 5,
getFillColor: [230, 0, 0]
2021-02-25 16:07:57 +03:00
});
2021-02-25 13:33:44 +03:00
2021-02-25 16:07:57 +03:00
const deckgl = new DeckGL({
container: 'map',
mapStyle: 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json',
initialViewState: {
latitude: 0,
longitude: 0,
zoom: 1
},
layers: [pointsLayer]
});
2021-02-25 13:33:44 +03:00
```
2018-10-12 18:18:34 +03:00
## Table Sources
2018-10-17 11:49:02 +03:00
Table Source is a database table which can be used to query [vector tiles ](https://github.com/mapbox/vector-tile-spec ). When started, martin will go through all spatial tables in the database and build a list of table sources. A table should have at least one geometry column with non-zero SRID. All other table columns will be represented as properties of a vector tile feature.
2018-10-12 18:18:34 +03:00
2018-10-11 15:01:47 +03:00
### Table Sources List
Table Sources list endpoint is available at `/index.json`
```shell
curl localhost:3000/index.json
```
2019-03-16 18:53:12 +03:00
**Note**: if in `watch` mode, this will rescan database for table sources.
2018-10-11 15:01:47 +03:00
### Table Source TileJSON
2018-10-12 18:18:34 +03:00
Table Source [TileJSON ](https://github.com/mapbox/tilejson-spec ) endpoint is available at `/{schema_name}.{table_name}.json` .
2018-10-11 15:01:47 +03:00
For example, `points` table in `public` schema will be available at `/public.points.json`
```shell
curl localhost:3000/public.points.json
```
2019-02-04 13:39:57 +03:00
### Table Source Tiles
2018-10-11 15:01:47 +03:00
Table Source tiles endpoint is available at `/{schema_name}.{table_name}/{z}/{x}/{y}.pbf`
For example, `points` table in `public` schema will be available at `/public.points/{z}/{x}/{y}.pbf`
```shell
curl localhost:3000/public.points/0/0/0.pbf
```
2021-04-24 20:19:37 +03:00
## Composite Sources
Composite Sources allows combining multiple Table Sources into one. Composite Source consists of multiple Table Sources separated by comma `{schema_name1}.{table_name1},...,{schema_nameN}.{table_nameN}`
Each [Table Source ](#table-sources ) in Composite Source can be accessed with its `{schema_name}.{table_name}` as a `source-layer` property.
### Composite Source TileJSON
Composite Source [TileJSON ](https://github.com/mapbox/tilejson-spec ) endpoint is available at `/{schema_name1}.{table_name1},...,{schema_nameN}.{table_nameN}.json` .
For example, composite source for `points` and `lines` tables in `public` schema will be available at `/public.points,public.lines.json`
```shell
curl localhost:3000/public.points,public.lines.json
```
### Composite Source Tiles
Composite Source tiles endpoint is available at `/{schema_name1}.{table_name1},...,{schema_nameN}.{table_nameN}/{z}/{x}/{y}.pbf`
For example, composite source for `points` and `lines` tables in `public` schema will be available at `/public.points,public.lines/{z}/{x}/{y}.pbf`
```shell
curl localhost:3000/public.points,public.lines/0/0/0.pbf
```
2018-10-12 18:18:34 +03:00
## Function Sources
2018-10-17 11:49:02 +03:00
Function Source is a database function which can be used to query [vector tiles ](https://github.com/mapbox/vector-tile-spec ). When started, martin will look for the functions with a suitable signature. A function that takes `z integer` , `x integer` , `y integer` , and `query_params json` and returns `bytea` , can be used as a Function Source.
2018-10-12 18:18:34 +03:00
| Argument | Type | Description |
2018-10-20 12:24:28 +03:00
| ------------ | ------- | ----------------------- |
2018-10-12 18:18:34 +03:00
| z | integer | Tile zoom parameter |
| x | integer | Tile x parameter |
| y | integer | Tile y parameter |
| query_params | json | Query string parameters |
**Hint**: You may want to use [TileBBox ](https://github.com/mapbox/postgis-vt-util#tilebbox ) function to generate bounding-box geometry of the area covered by a tile.
2019-10-14 16:57:19 +03:00
For example, if you have a table `public.table_source` in WGS84 (`4326` SRID), then you can use this function as a Function Source:
2018-10-12 18:18:34 +03:00
2019-07-03 17:29:38 +03:00
```sql
2019-10-14 16:57:19 +03:00
CREATE OR REPLACE FUNCTION public.function_source(z integer, x integer, y integer, query_params json) RETURNS bytea AS $$
2018-10-12 18:18:34 +03:00
DECLARE
2019-10-14 16:57:19 +03:00
mvt bytea;
2018-10-12 18:18:34 +03:00
BEGIN
SELECT INTO mvt ST_AsMVT(tile, 'public.function_source', 4096, 'geom') FROM (
SELECT
2021-10-12 19:01:25 +03:00
ST_AsMVTGeom(ST_Transform(ST_CurveToLine(geom), 3857), TileBBox(z, x, y, 3857), 4096, 64, true) AS geom
2018-10-12 18:18:34 +03:00
FROM public.table_source
2019-10-14 16:57:19 +03:00
WHERE geom & & TileBBox(z, x, y, 4326)
2018-10-12 18:18:34 +03:00
) as tile WHERE geom IS NOT NULL;
RETURN mvt;
END
$$ LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
```
2019-10-14 16:57:19 +03:00
The `query_params` argument is a JSON representation of the tile request query params. For example, if user requested a tile with [urlencoded ](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent ) params:
```shell
curl \
--data-urlencode 'arrayParam=[1, 2, 3]' \
--data-urlencode 'numberParam=42' \
--data-urlencode 'stringParam=value' \
--data-urlencode 'booleanParam=true' \
--data-urlencode 'objectParam={"answer" : 42}' \
--get localhost:3000/rpc/public.function_source/0/0/0.pbf
```
then `query_params` will be parsed as:
```json
{
"arrayParam": [1, 2, 3],
"numberParam": 42,
"stringParam": "value",
"booleanParam": true,
"objectParam": { "answer": 42 }
}
```
You can access this params using [json operators ](https://www.postgresql.org/docs/current/functions-json.html ):
```sql
...WHERE answer = (query_params->'objectParam'->>'answer')::int;
```
2018-10-11 15:01:47 +03:00
### Function Sources List
Function Sources list endpoint is available at `/rpc/index.json`
```shell
curl localhost:3000/rpc/index.json
```
2019-03-16 18:53:12 +03:00
**Note**: if in `watch` mode, this will rescan database for function sources.
2018-10-11 15:01:47 +03:00
### Function Source TileJSON
2018-10-12 18:18:34 +03:00
Function Source [TileJSON ](https://github.com/mapbox/tilejson-spec ) endpoint is available at `/rpc/{schema_name}.{function_name}.json`
2018-10-11 15:01:47 +03:00
For example, `points` function in `public` schema will be available at `/rpc/public.points.json`
```shell
curl localhost:3000/rpc/public.points.json
```
2018-10-17 11:49:02 +03:00
### Function Source Tiles
2018-10-11 15:01:47 +03:00
Function Source tiles endpoint is available at `/rpc/{schema_name}.{function_name}/{z}/{x}/{y}.pbf`
For example, `points` function in `public` schema will be available at `/rpc/public.points/{z}/{x}/{y}.pbf`
```shell
curl localhost:3000/rpc/public.points/0/0/0.pbf
```
2018-10-17 11:49:02 +03:00
## Command-line Interface
2018-10-11 15:01:47 +03:00
You can configure martin using command-line interface
2018-10-10 19:27:21 +03:00
```shell
Usage:
martin [options] [< connection > ]
martin -h | --help
martin -v | --version
Options:
2020-06-02 09:49:21 +03:00
-h --help Show this screen.
-v --version Show version.
--config=< path > Path to config file.
--keep-alive=< n > Connection keep alive timeout [default: 75].
--listen-addresses=< n > The socket address to bind [default: 0.0.0.0:3000].
--pool-size=< n > Maximum connections pool size [default: 20].
--watch Scan for new sources on sources list requests.
--workers=< n > Number of web server workers.
2021-10-18 14:35:08 +03:00
--ca-root-file=< path > Loads trusted root certificates from a file. The file should contain a sequence of PEM-formatted CA certificates.
2020-06-02 09:49:21 +03:00
--danger-accept-invalid-certs Trust invalid certificates. This introduces significant vulnerabilities, and should only be used as a last resort.
2018-10-10 19:27:21 +03:00
```
2018-10-17 11:49:02 +03:00
## Environment Variables
2018-03-27 14:40:33 +03:00
2018-10-11 15:01:47 +03:00
You can also configure martin using environment variables
2018-10-10 16:32:24 +03:00
2021-10-18 14:35:08 +03:00
| Environment variable | Example | Description |
| ----------------------------- | ---------------------------------- | --------------------------------------------- |
| `DATABASE_URL` | `postgres://postgres@localhost/db` | Postgres database connection |
| `WATCH_MODE` | `true` | Scan for new sources on sources list requests |
| `CA_ROOT_FILE` | `./ca-certificate.crt` | Loads trusted root certificates from a file |
| `DANGER_ACCEPT_INVALID_CERTS` | `false` | Trust invalid certificates |
2018-03-27 14:40:33 +03:00
2019-02-04 13:39:57 +03:00
## Configuration File
If you don't want to expose all of your tables and functions, you can list your sources in a configuration file. To start martin with a configuration file you need to pass a path to a file with a `--config` argument.
```shell
martin --config config.yaml
```
You can find an example of a configuration file [here ](https://github.com/urbica/martin/blob/master/tests/config.yaml ).
```yaml
2021-10-13 14:51:29 +03:00
# The socket address to bind [default: 0.0.0.0:3000]
listen_addresses: '0.0.0.0:3000'
2019-03-16 18:53:12 +03:00
# Database connection string
2021-10-13 14:51:29 +03:00
connection_string: 'postgres://postgres@localhost:5432/db'
2019-03-16 18:53:12 +03:00
2019-02-04 13:39:57 +03:00
# Maximum connections pool size [default: 20]
pool_size: 20
# Connection keep alive timeout [default: 75]
keep_alive: 75
# Number of web server workers
worker_processes: 8
2019-03-16 18:53:12 +03:00
# Enable watch mode
2021-10-13 14:51:29 +03:00
watch: false
2019-03-16 18:53:12 +03:00
2020-06-02 09:49:21 +03:00
# Trust invalid certificates. This introduces significant vulnerabilities, and should only be used as a last resort.
danger_accept_invalid_certs: false
2021-10-13 14:51:29 +03:00
# Associative arrays of table sources
2019-02-04 13:39:57 +03:00
table_sources:
public.table_source:
2021-10-13 14:51:29 +03:00
# Table source id (required)
2019-02-04 13:39:57 +03:00
id: public.table_source
2021-10-13 14:51:29 +03:00
# Table schema (required)
2019-02-04 13:39:57 +03:00
schema: public
2021-10-13 14:51:29 +03:00
# Table name (required)
2019-02-04 13:39:57 +03:00
table: table_source
2021-10-13 14:51:29 +03:00
# Geometry SRID (required)
srid: 4326
# Geometry column name (required)
2019-02-04 13:39:57 +03:00
geometry_column: geom
2021-10-13 14:51:29 +03:00
# Feature id column name
id_column: ~
# 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]
2019-02-04 13:39:57 +03:00
2021-10-13 14:51:29 +03:00
# Tile extent in tile coordinate space
2019-02-04 13:39:57 +03:00
extent: 4096
2021-10-13 14:51:29 +03:00
# Buffer distance in tile coordinate space to optionally clip geometries
2019-02-04 13:39:57 +03:00
buffer: 64
2021-10-13 14:51:29 +03:00
# Boolean to control if geometries should be clipped or encoded as is
2019-02-04 13:39:57 +03:00
clip_geom: true
2021-10-13 14:51:29 +03:00
# Geometry type
2019-02-04 13:39:57 +03:00
geometry_type: GEOMETRY
2021-10-13 14:51:29 +03:00
# List of columns, that should be encoded as tile properties (required)
2019-02-04 13:39:57 +03:00
properties:
gid: int4
2021-10-13 14:51:29 +03:00
# Associative arrays of function sources
2019-02-04 13:39:57 +03:00
function_sources:
public.function_source:
2021-10-13 14:51:29 +03:00
# Function source id (required)
2019-02-04 13:39:57 +03:00
id: public.function_source
2021-10-13 14:51:29 +03:00
# Schema name (required)
2019-02-04 13:39:57 +03:00
schema: public
2021-10-13 14:51:29 +03:00
# Function name (required)
2019-02-04 13:39:57 +03:00
function: function_source
2021-10-13 14:51:29 +03:00
# 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]
2019-02-04 13:39:57 +03:00
```
2017-10-09 14:29:03 +03:00
## Using with Docker
2019-02-04 13:00:37 +03:00
You can use official Docker image [`urbica/martin` ](https://hub.docker.com/r/urbica/martin )
```shell
docker run \
-p 3000:3000 \
-e DATABASE_URL=postgres://postgres@localhost/db \
urbica/martin
```
If you are running PostgreSQL instance on `localhost` , you have to change network settings to allow the Docker container to access the `localhost` network.
For Linux, add the `--net=host` flag to access the `localhost` PostgreSQL service.
2018-10-10 16:32:24 +03:00
```shell
docker run \
2019-02-04 13:00:37 +03:00
--net=host \
2018-10-10 16:32:24 +03:00
-p 3000:3000 \
-e DATABASE_URL=postgres://postgres@localhost/db \
urbica/martin
```
2019-02-04 13:00:37 +03:00
For macOS, use `host.docker.internal` as hostname to access the `localhost` PostgreSQL service.
```shell
docker run \
-p 3000:3000 \
-e DATABASE_URL=postgres://postgres@host.docker.internal/db \
urbica/martin
```
For Windows, use `docker.for.win.localhost` as hostname to access the `localhost` PostgreSQL service.
```shell
docker run \
-p 3000:3000 \
-e DATABASE_URL=postgres://postgres@docker.for.win.localhost/db \
urbica/martin
```
2019-08-28 14:04:15 +03:00
## Using with Docker Compose
You can use example [`docker-compose.yml` ](https://raw.githubusercontent.com/urbica/martin/master/docker-compose.yml ) file as a reference
```yml
2021-10-07 17:00:21 +03:00
version: '3'
2019-08-28 14:04:15 +03:00
services:
martin:
image: urbica/martin
restart: unless-stopped
ports:
- 3000:3000
environment:
2020-12-27 16:41:42 +03:00
- DATABASE_URL=postgres://postgres:password@db/db
2019-08-28 14:04:15 +03:00
depends_on:
- db
db:
2021-10-07 17:00:21 +03:00
image: postgis/postgis:14-3.1-alpine
2019-08-28 14:04:15 +03:00
restart: unless-stopped
environment:
- POSTGRES_DB=db
- POSTGRES_USER=postgres
2020-12-27 16:35:05 +03:00
- POSTGRES_PASSWORD=password
2019-08-28 14:04:15 +03:00
volumes:
- ./pg_data:/var/lib/postgresql/data
```
First, you need to start `db` service
```shell
docker-compose up -d db
```
Then, after `db` service is ready to accept connections, you can start `martin`
```shell
docker-compose up -d martin
```
By default, martin will be available at [localhost:3000 ](http://localhost:3000/index.json )
2019-02-04 13:13:16 +03:00
## Using with Nginx
2021-10-07 17:00:21 +03:00
You can run martin behind Nginx proxy, so you can cache frequently accessed tiles and reduce unnecessary pressure on the database.
```yml
version: '3'
services:
nginx:
image: nginx:alpine
restart: unless-stopped
ports:
- 80:80
volumes:
- ./cache:/var/cache/nginx
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- martin
martin:
image: urbica/martin
restart: unless-stopped
environment:
- DATABASE_URL=postgres://postgres:password@db/db
depends_on:
- db
db:
image: postgis/postgis:14-3.1-alpine
restart: unless-stopped
environment:
- POSTGRES_DB=db
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
volumes:
- ./pg_data:/var/lib/postgresql/data
```
You can find an example Nginx configuration file [here ](https://github.com/urbica/martin/blob/master/nginx.conf ).
### Rewriting URLs
If you are running martin behind Nginx proxy, you may want to rewrite the request URL to properly handle tile URLs in [TileJSON ](#table-source-tilejson ) [endpoints ](#function-source-tilejson ).
2019-02-04 13:13:16 +03:00
```nginx
location ~ /tiles/(?< fwd_path > .*) {
2021-10-15 18:19:36 +03:00
proxy_set_header X-Rewrite-URL $uri;
2020-12-15 13:12:16 +03:00
proxy_set_header X-Forwarded-Host $host:$server_port;
2021-10-07 17:00:21 +03:00
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
2019-02-04 13:13:16 +03:00
proxy_pass http://martin:3000/$fwd_path$is_args$args;
}
```
2021-10-07 17:00:21 +03:00
### Caching tiles
You can also use Nginx to cache tiles. In the example, the maximum cache size is set to 10GB, and caching time is set to 1 hour for responses with codes 200, 204, and 302 and 1 minute for responses with code 404.
```nginx
http {
...
proxy_cache_path /var/cache/nginx/
levels=1:2
max_size=10g
use_temp_path=off
keys_zone=tiles_cache:10m;
server {
...
location ~ /tiles/(?< fwd_path > .*) {
2021-10-15 18:19:36 +03:00
proxy_set_header X-Rewrite-URL $uri;
2021-10-07 17:00:21 +03:00
proxy_set_header X-Forwarded-Host $host:$server_port;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
proxy_cache tiles_cache;
proxy_cache_lock on;
proxy_cache_revalidate on;
# Set caching time for responses
proxy_cache_valid 200 204 302 1h;
proxy_cache_valid 404 1m;
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
add_header X-Cache-Status $upstream_cache_status;
proxy_pass http://martin:3000/$fwd_path$is_args$args;
}
}
}
```
You can find an example Nginx configuration file [here ](https://github.com/urbica/martin/blob/master/nginx.conf ).
2018-10-17 11:49:02 +03:00
## Building from Source
2018-10-10 16:32:24 +03:00
2018-10-17 11:49:02 +03:00
You can clone the repository and build martin using [cargo ](https://doc.rust-lang.org/cargo ) package manager.
2018-10-10 16:32:24 +03:00
```shell
git clone git@github.com:urbica/martin.git
cd martin
cargo build --release
```
2018-10-17 11:49:02 +03:00
The binary will be available at `./target/release/martin` .
2018-10-01 20:10:33 +03:00
2018-10-10 16:32:24 +03:00
```shell
2018-10-10 19:27:21 +03:00
cd ./target/release/
./martin postgres://postgres@localhost/db
2018-10-10 16:32:24 +03:00
```
2017-10-09 14:29:03 +03:00
2019-02-04 20:22:02 +03:00
## Debugging
Log levels are controlled on a per-module basis, and by default all logging is disabled except for errors. Logging is controlled via the `RUST_LOG` environment variable. The value of this environment variable is a comma-separated list of logging directives.
2021-07-16 13:09:18 +03:00
This will enable debug logging for all modules:
2019-02-04 20:22:02 +03:00
```shell
2021-07-16 13:09:18 +03:00
export RUST_LOG=debug
martin postgres://postgres@localhost/db
```
While this will only enable verbose logging for the `actix_web` module and enable debug logging for the `martin` and `tokio_postgres` modules:
```shell
export RUST_LOG=actix_web=info,martin=debug,tokio_postgres=debug
2019-02-04 20:22:02 +03:00
martin postgres://postgres@localhost/db
```
2017-10-09 14:29:03 +03:00
## Development
2020-12-20 15:45:33 +03:00
Clone project
2017-10-09 14:29:03 +03:00
2018-10-10 16:32:24 +03:00
```shell
2020-12-20 15:45:33 +03:00
git clone git@github.com:urbica/martin.git
cd martin
```
Start `db` service using [docker-compose ](https://docs.docker.com/compose/ )
```shell
docker-compose up -d db
```
Then, after `db` service is ready to accept connections, you can start `martin` with
```shell
DATABASE_URL=postgres://postgres@localhost/db cargo run
```
2021-10-08 17:24:16 +03:00
Open `tests/debug.html` for debugging. By default, martin will be available at [localhost:3000 ](http://localhost:3000/index.json )
2020-12-20 15:45:33 +03:00
Make your changes, and check if all the tests are running
```shell
DATABASE_URL=postgres://postgres@localhost/db cargo test
2018-10-17 11:49:02 +03:00
```
2021-07-23 11:04:34 +03:00
You can also run benchmarks with
```shell
DATABASE_URL=postgres://postgres@localhost/db cargo bench
```
An HTML report displaying the results of the benchmark will be generated under `target/criterion/report/index.html`
2021-10-17 17:30:35 +03:00
## Recipes
2021-10-18 14:42:11 +03:00
### Using with DigitalOcean PostgreSQL
You can use Martin with [Managed PostgreSQL from DigitalOcean ](https://www.digitalocean.com/products/managed-databases-postgresql/ ) with PostGIS extension
First, you need to download the CA certificate and get your cluster connection string from the [dashboard ](https://cloud.digitalocean.com/databases ). After that, you can use the connection string and the CA certificate to connect to the database
```
martin --ca-root-file ./ca-certificate.crt postgres://user:password@host:port/db?sslmode=require
```
### Using with Heroku PostgreSQL
2021-10-17 17:30:35 +03:00
You can use Martin with [Managed PostgreSQL from Heroku ](https://www.heroku.com/postgres ) with PostGIS extension
```
heroku pg:psql -a APP_NAME -c 'create extension postgis'
```
In order to trust the Heroku certificate, you can disable certificate validation with either `DANGER_ACCEPT_INVALID_CERTS` environment variable
```
DATABASE_URL=$(heroku config:get DATABASE_URL -a APP_NAME) DANGER_ACCEPT_INVALID_CERTS=true martin
```
or `--danger-accept-invalid-certs` command-line argument
```
martin --danger-accept-invalid-certs $(heroku config:get DATABASE_URL -a APP_NAME)
```