MCPcopy Index your code
hub / github.com/CrunchyData/pg_tileserv

github.com/CrunchyData/pg_tileserv @v1.0.11

Chat with this repo
repository ↗ · DeepWiki ↗ · release v1.0.11 ↗ · + Follow
911 symbols 1,565 edges 18 files 316 documented · 35%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

pg_tileserv

.github/workflows/ci.yml

A PostGIS-only tile server in Go. Strip away all the other requirements, it just has to take in HTTP tile requests and form and execute SQL. In a sincere act of flattery, the API looks a lot like that of the Martin tile server.

  • https://access.crunchydata.com/documentation/pg_tileserv/latest/

Setup and Installation

Download

Builds of the latest code:

Basic Operation

The executable will read user/connection information from the DATABASE_URL and connect to the database, exposing all functions and tables the database user has read and execute permissions on.

For production deployment, place an HTTP proxy caching layer (eg Varnish) in between the tile server and clients to reduce database load and increase application performance.

Linux/MacOS

export DATABASE_URL=postgresql://username:password@host/dbname
./pg_tileserv

Windows

SET DATABASE_URL=postgresql://username:password@host/dbname
pg_tileserv.exe

PostgreSQL not on 5432

If your PostgreSQL is not running on unix socket or 5432 port, you can specify the port as part of the URL

DATABASE_URL=postgresql://username:password@host:port/dbname

Docker

Use Dockerfile.alpine to build a lightweight (18MB expanded) Docker Image. See also a full example with Docker Compose.

Trouble-shooting

To get more information about what is going on behind the scenes, run with the --debug commandline parameter on, or turn on debugging in the configuration file:

./pg_tileserv --debug

Configuration File

The configuration file will be automatically read from the following locations, if it exists:

  • Relative to the directory from which the program is run, ./config/pg_tileserv.toml
  • In a root volume at /config/pg_tileserv.toml
  • In the system configuration directory, at /etc/pg_tileserv.toml

If you want to pass a path directly to the configuration file, use the --config commandline parameter to pass in a pull path to configuration file. When using the --config option, configuration files in other locations will be ignored.

./pg_tileserv --config /opt/pg_tileserv/pg_tileserv.toml

In general the defaults are fine, and the program autodetects things like the server name. If you are not running PostgreSQL on 5432 port, you may need to add the port to the DbConnection parameter

user=you host=localhost dbname=yourdb port=yourdbport

# Database connection
DbConnection = "user=you host=localhost dbname=yourdb"

# Close pooled connections after this interval
DbPoolMaxConnLifeTime = "1h"

# Hold no more than this number of connections in the database pool
DbPoolMaxConns = 4

# Look to read html templates from this directory
AssetsPath = "/usr/share/pg_tileserv/assets"

# Accept connections on this subnet (default accepts on all)
HttpHost = "0.0.0.0"

# Accept connections on this port
HttpPort = 7800
HttpsPort = 7801

# HTTPS configuration
# TLS server certificate full chain and private key
# If you do not specify both, the TLS server will not be started
TlsServerCertificateFile = "server.crt"
TlsServerPrivateKeyFile = "server.key"

For SSL support, you will need both a server private key and an authority certificate. For testing purposes you can generate a self-signed key/cert pair using openssl:

openssl req  -nodes -new -x509  -keyout server.key -out server.crt
# Cache control configuration. TTL is time in seconds to request
# that responses be cached by any downstream caching services.
# Zero means no cache control header will be set.
CacheTTL = 60

# Advertise URLs relative to this server name
# default is to looke this up from incoming request headers
# UrlBase = "http://yourserver.com/"
# Resolution to quantize vector tiles to
DefaultResolution = 4096
# Padding to add to vector tiles
DefaultBuffer = 256
# Limit number of features requested (-1 = no limit)
MaxFeaturesPerTile = 50000
# Advertise this minimum zoom level
DefaultMinZoom = 0
# Advertise this maximum zoom level
DefaultMaxZoom = 22

# Allow any page to consume these tiles
CORSOrigins = ["*"]

# Output extra logging information?
Debug = false

# Enable Prometheus metrics
# Metrics will be exported at `/metrics`.
EnableMetrics = false

# Default CS is Web Mercator (EPSG:3857)
[CoordinateSystem]
SRID = 3857
Xmin = -20037508.3427892
Ymin = -20037508.3427892
Xmax = 20037508.3427892
Ymax = 20037508.3427892

You can use the CoordinateSystem block to output files in a system other than the default Web Mercator projection. In order to view a map with multiple layers in a non-standard projection, you will have to ensure that all layers share the same projection, otherwise the layers will not line up.

Configuration Using Environment Variables

Any parameter in the configuration file can be over-ridden at run-time in the environment. Prepend the upper-cased parameter name with TS_ to set the value. For example, to change the HTTP port using the environment:

export TS_HTTPPORT=8889

Operation

The purpose of pg_tileserv is to turn a set of spatial records into tiles, on the fly. The tile server reads two different layers of data:

  • Table layers are what they sound like: tables in the database that have a spatial column with a spatial reference system defined on it.
  • Function layers hide the source of data from the server, and allow the HTTP client to send in optional parameters to allow more complex SQL functionality. Any function of the form function(z integer, x integer, y integer, ...) that returns an MVT bytea result can serve as a function layer.

Web Interface

After start-up you can connect to the server and explore the published tables and functions in the database via a web interface at:

  • http://localhost:7800

To disable the web interface, supply the run time flag --no-preview

Health Check Endpoint

In order to run the server in an orchestrated environment, like Docker, it can be useful to have a health check endpoint. This is /health by default, and returns a 200 OK if the server is responding to requests. To use a custom URL for the health check endpoint, supply the run time flag -e and your path. This setting will respect the base path setting, so if you choose a base path of /example and a health check endpoint of /foo, your health check URL becomes /example/foo.

Layers List

A list of layers is available in JSON at:

  • http://localhost:7800/index.json

The index JSON just returns the minimum information about each layer.

{
    "public.ne_50m_admin_0_countries" : {
        "name" : "ne_50m_admin_0_countries",
        "schema" : "public",
        "type" : "table",
        "id" : "public.ne_50m_admin_0_countries",
        "description" : "Natural Earth country data",
        "detailurl" : "http://localhost:7800/public.ne_50m_admin_0_countries.json"
    }
}

The detailurl provides more detailed metadata for table and function layers.

The description field is read from the comment value of the table. To set a comment on a table, use the COMMENT command.

COMMENT ON TABLE ne_50m_admin_0_countries IS 'This is my comment';

Table Layers

By default, pg_tileserv will provide access to only those spatial tables:

  • that your database connection has SELECT privileges for;
  • that include a geometry column;
  • that declare a geometry type; and,
  • that declare an SRID (spatial reference ID)

For example:

CREATE TABLE mytable (
    geom Geometry(Polygon, 4326),
    pid text,
    address text
);
GRANT SELECT ON mytable TO myuser;

To restrict access to a certain set of tables, use database security principles:

  • Create a role with limited privileges
  • Only grant SELECT to that role for tables you want to publish
  • Only grant EXECUTE to that role for functions you want to publish
  • Connect pg_tileserv to the database using that role

If your table contains a geometry column that appears valid, but it is not available within pg_tileserv, you may need to specifically set a geometry type or SRID.

To determine if a table is compatible, make sure that it is returned by the following query:

SELECT
    nspname AS SCHEMA,
    relname AS TABLE,
    attname AS geometry_column,
    postgis_typmod_srid (atttypmod) AS srid,
    postgis_typmod_type (atttypmod) AS geometry_type
FROM
    pg_class c
    JOIN pg_namespace n ON (c.relnamespace = n.oid)
    JOIN pg_attribute a ON (a.attrelid = c.oid)
    JOIN pg_type t ON (a.atttypid = t.oid)
WHERE
    relkind IN('r', 'v', 'm')
    AND typname = 'geometry'
    AND postgis_typmod_srid (atttypmod) != 0
    AND relname = '<mytable>';

If not, make sure that the geometry column has a valid SRID defined in the table metadata. You may need to specifically assign a geometry type, especially if the table was created using a SELECT query from another geometry table.

For example, to set the geometry as a Point type:

ALTER TABLE mytable ALTER COLUMN geom TYPE geometry (Point, 4326);

Table Layer Detail JSON

In the detail JSON, each layer declares information relevant to setting up a map interface for the layer.

{
   "id" : "public.ne_50m_admin_0_countries",
   "geometrytype" : "MultiPolygon",
   "name" : "ne_50m_admin_0_countries",
   "description" : "Natural Earth countries",
   "schema" : "public",
   "bounds" : [
      -180,
      -89.9989318847656,
      180,
      83.599609375
   ],
   "center" : [
      0,
      -3.19966125488281
   ],
   "tileurl" : "http://localhost:7800/public.ne_50m_admin_0_countries/{z}/{x}/{y}.pbf",
   "properties" : [
      {
         "name" : "gid",
         "type" : "int4",
         "description" : ""
      },{
         "name" : "featurecla",
         "description" : "",
         "type" : "varchar"
      },{
         "description" : "",
         "type" : "varchar",
         "name" : "name"
      },{
         "type" : "varchar",
         "description" : "",
         "name" : "name_long"
      }
   ],
   "minzoom" : 0,
   "maxzoom" : 22
}
  • id, name and schema are the fully qualified, table and schema name of the database table.
  • bounds and center give the extent and middle of the data collection, in geographic coordinates. The order of coordinates in bounds is [minlon, minlat, maxlon, maxlat]. The order of coordinates in center is [lon, lat].
  • tileurl is the standard substitution pattern URL consumed by map clients like Mapbox GL JS and OpenLayers.
  • properties is a list of columns in the table, with their data types and descriptions. The column description field can be set using the COMMENT SQL command, for example: sql COMMENT ON COLUMN ne_50m_admin_0_countries.name_long IS 'This is the long name';

Feature id

The vector tile specification allows for an optional id field for each feature. This field should be unique within the parent layer.

By default, pg_tileserv will generate this id if:

  • the PostGIS version is >= 3.0 and;
  • the table has a primary key and;
  • the primary key field is one of 'int2', 'int4', 'int8'

A feature id will not be generated for Views since these do not have a primary key. In cases where an id is not generated, depending on the map renderer, it may be possible to generate a feature id at runtime. See https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/#vector-promoteId for an example in Mapbox GL JS.

Table Tile Request Customization

Most developers will just use the tileurl as is, but it possible to add some parameters to the URL to customize behaviour at run time:

  • limit controls the number of features to write to a tile, the default is 50000.
  • resolution controls the resolution of a tile, the default is 4096 units per side for a tile.
  • buffer controls the size of the extra data buffer for a tile, the default is 256 units.
  • properties is a comma-separated list of properties to include in the tile. For wide tables with large numbers of columns, this allows a slimmer tile to be composed.
  • filter is a CQL logical expression which specifies the features to be included in the tile. See the CQL documentation.

For example:

http://localhost:7800/public.ne_50m_admin_0_countries/{z}/{x}/{y}.pbf?limit=100000&properties=name,long_name

For property names that include commas (why did you do that?) URL encode the comma in the name string before composing the comma-separated string of all names.

Multi-Layer Tile Requests

For more complex applications, multi-layer tiles can be useful to cut down on the amount of HTTP requests to pull in vector tiles. Doing this with pg_tileserv is easy, just add additional tables to your request. You can add as many tables as you like to your request, just separate them with a comma.

For example:

http://localhost:7800/public.ne_50m_admin_0_countries,public.ne_50m_airports/{z}

Extension points exported contracts — how you extend this code

Layer (Interface)
A Layer is a LayerTable or a LayerFunction in either case it should be able to generate a TileRequest containing SQL to [2 …
layer.go
ICqlFilterContext (Interface)
ICqlFilterContext is an interface to support dynamic dispatch. [1 implementers]
cql/cql_parser.go
CQLParserListener (Interface)
CQLParserListener is a complete listener for a parse tree produced by CQLParser. [1 implementers]
cql/cqlparser_listener.go
SqlHolder (Interface)
(no doc) [1 implementers]
cql/cql.go
IBooleanValueExpressionContext (Interface)
IBooleanValueExpressionContext is an interface to support dynamic dispatch. [1 implementers]
cql/cql_parser.go
IBooleanTermContext (Interface)
IBooleanTermContext is an interface to support dynamic dispatch. [1 implementers]
cql/cql_parser.go
IBooleanFactorContext (Interface)
IBooleanFactorContext is an interface to support dynamic dispatch. [1 implementers]
cql/cql_parser.go
IBooleanPrimaryContext (Interface)
IBooleanPrimaryContext is an interface to support dynamic dispatch. [1 implementers]
cql/cql_parser.go

Core symbols most depended-on inside this repo

NewCqlContext
called by 68
cql/cql.go
EnterRule
called by 31
cql/cql_parser.go
ExitRule
called by 31
cql/cql_parser.go
sqlFor
called by 30
cql/cql.go
ServeHTTP
called by 17
main.go
SetSql
called by 17
cql/cql.go
String
called by 13
tile.go
PropertyName
called by 12
cql/cql_parser.go

Shape

Method 672
Function 146
Struct 54
Interface 37
FuncType 1
TypeAlias 1

Languages

Go100%

Modules by API surface

cql/cql_parser.go599 symbols
cql/cqlparser_base_listener.go73 symbols
cql/cqlparser_listener.go69 symbols
cql/cql.go46 symbols
layer_table.go22 symbols
main.go16 symbols
layer_proc.go15 symbols
layer.go15 symbols
cql/cql_test.go14 symbols
util.go11 symbols
main_test.go9 symbols
tile.go6 symbols

Datastores touched

dbnameDatabase · 1 repos
tileservDatabase · 1 repos

For agents

$ claude mcp add pg_tileserv \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact