PostgreSQL Function Sources

Function Source is a database function which can be used to query vector tiles. When started, Martin will look for the functions with a suitable signature. A function that takes z integer (or zoom integer), x integer, y integer, and an optional query json and returns bytea, can be used as a Function Source. Alternatively the function could return a record with a single bytea field, or a record with two fields of types bytea and text, where the text field is an etag key (i.e. md5 hash).

ArgumentTypeDescription
z (or zoom)integerTile zoom parameter
xintegerTile x parameter
yintegerTile y parameter
query (optional, any name)jsonQuery string parameters

Simple Function

For example, if you have a table table_source in WGS84 (4326 SRID), then you can use this function as a Function Source:

CREATE OR REPLACE
    FUNCTION function_zxy_query(z integer, x integer, y integer)
    RETURNS bytea AS $$
DECLARE
  mvt bytea;
BEGIN
  SELECT INTO mvt ST_AsMVT(tile, 'function_zxy_query', 4096, 'geom') FROM (
    SELECT
      ST_AsMVTGeom(
          ST_Transform(ST_CurveToLine(geom), 3857),
          ST_TileEnvelope(z, x, y),
          4096, 64, true) AS geom
    FROM table_source
    WHERE geom && ST_Transform(ST_TileEnvelope(z, x, y), 4326)
  ) as tile WHERE geom IS NOT NULL;

  RETURN mvt;
END
$$ LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;

Function with Query Parameters

Users may add a query parameter to pass additional parameters to the function.

TODO: Modify this example to actually use the query parameters.

CREATE OR REPLACE
    FUNCTION function_zxy_query(z integer, x integer, y integer, query_params json)
    RETURNS bytea AS $$
DECLARE
  mvt bytea;
BEGIN
  SELECT INTO mvt ST_AsMVT(tile, 'function_zxy_query', 4096, 'geom') FROM (
    SELECT
      ST_AsMVTGeom(
          ST_Transform(ST_CurveToLine(geom), 3857),
          ST_TileEnvelope(z, x, y),
          4096, 64, true) AS geom
    FROM table_source
    WHERE geom && ST_Transform(ST_TileEnvelope(z, x, y), 4326)
  ) as tile WHERE geom IS NOT NULL;

  RETURN mvt;
END
$$ LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;

The query_params argument is a JSON representation of the tile request query params. Query params could be passed as simple query values, e.g.

curl localhost:3000/function_zxy_query/0/0/0?token=martin

You can also use urlencoded params to encode complex values:

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/function_zxy_query/0/0/0

then query_params will be parsed as:

{
  "arrayParam": [1, 2, 3],
  "numberParam": 42,
  "stringParam": "value",
  "booleanParam": true,
  "objectParam": { "answer": 42 }
}

You can access this params using json operators:

...WHERE answer = (query_params->'objectParam'->>'answer')::int;

Modifying TileJSON

Martin will automatically generate a basic TileJSON manifest for each function source that will contain the name and description of the function, plus optionally minzoom, maxzoom, and bounds (if they were specified via one of the configuration methods). For example, if there is a function public.function_zxy_query_jsonb, the default TileJSON might look like this (note that URL will be automatically adjusted to match the request host):

{
  "tilejson": "3.0.0",
  "tiles": [
    "http://localhost:3111/function_zxy_query_jsonb/{z}/{x}/{y}"
  ],
  "name": "function_zxy_query_jsonb",
  "description": "public.function_zxy_query_jsonb"
}

TileJSON in SQL Comments

To modify automatically generated TileJSON, you can add a valid JSON as an SQL comment on the function. Martin will merge function comment into the generated TileJSON using JSON Merge patch. The following example adds attribution and version fields to the TileJSON.

Note: This example uses EXECUTE to ensure that the comment is a valid JSON (or else PostgreSQL will throw an error). You can use other methods of creating SQL comments.

DO $do$ BEGIN
    EXECUTE 'COMMENT ON FUNCTION my_function_name IS $tj$' || $$
    {
        "description": "my new description",
        "attribution": "my attribution",
        "vector_layers": [
            {
                "id": "my_layer_id",
                "fields": {
                    "field1": "String",
                    "field2": "Number"
                }
            }
        ]
    }
    $$::json || '$tj$';
END $do$;