Tilegarden is a serverless tile-rendering tool using Mapnik, built for AWS Lambda. Serve custom-generated map tiles without having to worry about server maintenance, scaling, or paying for resources that aren't being accessed.
A deployed instance of Tilegarden consists of several AWS resources:
A CloudFront distribution that acts as a proxy in order to circumvent some issues that API Gateway has serving image content.
src/tiler/src/config/
(where you can find pre-existing examples). A full specification for Carto's .mml format can be found here..env
. The following variables are required:AWS_PROFILE
: the name of the AWS user profile you want to deploy your project as. You may have this set already in your environment, otherwise you'll want to set it to one of the names of the sets of credentials specified in ~/.aws/credentials
. Defaults to “default”.PROJECT_NAME
: the name of your project. This must be unique among the functions you currently have deployed to AWS Lambda.PROD_TILEGARDEN_*
: the credentials necessary to connect to your production database.LAMBDA_REGION
: the AWS region to which you want your lambda functions deployed.LAMBDA_ROLE
: the name/ARN of the IAM role to give your lambda. Leave this blank to have one created for you automatically.LAMBDA_SUBNETS
and LAMBDA_SECURITY_GROUPS
: only required if you need to connect to other AWS resources (such as an RDS instance), in which case these should match the values that those resources have../scripts/deploy --new
to create new instances of the necessary AWS resources../scripts/deploy
(no “--new”). Note that only certain function settings (LAMBDA_TIMEOUT
and LAMBDA_MEMORY
) are updated by this command, changes to IAM Role/subnets/security groups/etc. must be made manually on the AWS dashboard../scripts/destroy
.USER
: Left unspecified so that the code knows who the local user is from inside docker containers. Gets tacked on to the end of the project name at deployment.LAMBDA_TIMEOUT
: Time (in seconds) before your lambda function is automatically cancelled (maximum 300). You'll probably need to modify this value as tile rendering can take longer than AWS's default timeout, depending on the complexity of the data you're rendering.LAMBDA_MEMORY
: Amount of memory (in MB) to allocate per function. Must be a multiple of 64, minimum 128, maximum 3008. As with LAMBDA_TIMEOUT
, more complex data requires more allocated memory, but 128MB should be enough in most scenarios.The AWS profile used for deployment must have at least the following policies and permissions:
CloudFrontFullAccess
AmazonAPIGatewayAdministrator
AWSLambdaFullAccess
iam:DeleteRolePolicy
iam:DeleteRole
iam:CreateRole
iam:AttachRolePolicy
iam:PutRolePolicy
Multiple map configuration .xml
files can be included in your project's src/tiler/src/config
to be dynamically loaded at run-time. Use the config
path parameter with the name of your configuration file (without the file extension) to select which file gets loaded when rendering tiles.
Example: if you have a configuration file named my-good-map.xml
, you can tell Tilegarden to use it with the endpoint /tile/my-good-map/{z}/{x}/{y}.png
Configuration files can also be optionally loaded from an AWS S3 bucket, which allows you to add/remove/replace available maps without having to redeploy your entire project. To do so, add &s3bucket=<bucket-name>
to your query string, and treat the config
parameter as the desired config file's key (omitting the .xml
extension). You can use the build-xml
script to generate config files for this purpose without having to spin up the entire development environment, run ./scripts/build-xml --help
for more info.
AmazonS3ReadOnlyAccess
permission to the IAM role that gets created for your lambda, or otherwise use a custom role that has that permission, since claudia
doesn't add it during creation.build-xml
has a -b/--s3_bucket <name>
flag which automatically uploads built files to S3 instead of saving them to disk. This requires you to have the AWS CLI installed on your machine, and uses the AWS credentials you've specified in .env
.Tilegarden supports the use of any geospatial data source that Mapnik/Carto does (shapefile, postgis, pgraster, raster). However, bear in mind that only PostGIS data sources support custom queries, and are thus the only ones that allow you to perform additional filtering on your data (see Filters). Also, attempting to bundle large local data files into your Tilegarden deployment could lead to rejection by AWS Lambda due to size restrictions.
Map styles are specified in CartoCSS, the specification for which can be found here. The default stylesheet is located at src/tiler/src/config/style.mss
. Multiple stylesheets can be used by adding them to the parameter “Stylesheet” in src/tiler/src/config/map-config.mml
. Each .mss “class” refers to a map layer (its “id” property), specified in map-config.mml
.
Filtering your geospatial data can be done in two ways:
Filters can be specified on the back-end by altering the query of one of your map's layers in src/tiler/src/config/map-config.mml
, and can then be fetched through the API. To create a filtered layer, modify the “table” property of the layer to have the format (QUERY) as VARIABLE
.
(SELECT geom,homes FROM table_name WHERE homes='big') as q
would create a filtered layer for all points that have a “homes” value of “big” in your database.Layers can be accessed from the API by adding ?layers=["layer1","layer2",etc.]
(a valid JSON array)as a query string to a tile or UTF grid URL. If no layers are specified in the query string, all layers specified in map-config.mml
are displayed simultaneously.
Layers can also be filtered dynamically by passing a JSON object as a value of a layer in the ?layers
array. The schema for this sort of request looks like the following:
[
{
"name":"layer1",
"mode": "AND" // optional, operator to use to combine filters, can either be AND or OR, defaults to AND
"filters": [ // optional, applies no filters if missing
{
"col":"column1", // table column you wish to filter by
"val":"value1", // value to filter on
"op": ">" // optional, boolean operator to use when comparing col to val, = by default
}
]
}
]
The resulting url would have the query string ?layers=[{"name":"layer1","filters":[{"col":"column1","val":"value1","like": true}]}]
. Filtering layers in this way appends WHERE col='val'
to the existing query defined for the layer. Defining multiple filters in the "filters" array of the layer object AND
s the filters together in the query.
%
s in your JSON! Browsers/Tilegarden automatically handle most percent-encodable characters, but %
s especially can cause your JSON to become unparsable. Call encodeURIComponent()
on your filter objects for safety.UTF grids can be generated at the url /grid/{z}/{x}/{y}?utfFields=field1,field2,etc.
, where “utfFields” is a comma-separated list of one or more table columns from the database. Each grid url MUST have a “utfFields” query string. If you have already modified the table query of a layer to specify a filter, be sure the include the utf field column in the query. Ex. (SELECT geom,homes,dog... )
, if you want to base a UTF grid off the value of “dog”.
Vector tiles of your data can be generated at the endpoint /vector/{z}/{x}/{y}
. Filtering by layer works via query string but can also be handled client side, as can styling. See here for more info about the Mapbox Vector Tile specification.
Dependencies: docker, docker-compose
data/
directory. .zipped shapefiles and gzipped SQL dumps will be loaded in to the development database automatically.src/tiler/src/config/map-config.mml
. This is where you specify your map settings. A full specification for Carto's .mml format can be found here.layers
query string. See Filters for more info.env-template
named .env
. This contains production-specific configuration options and doesn't need to be filled out right now (but must exist in order to run the development server)../scripts/update
to create your Docker containers and populate the development database. The optional flag --download
will download the data sets used for our demos../scripts/server
to start the development server. It will be available at localhost:3000, your tiles can be found at /tile/{z}/{x}/{y}.png
../scripts/update --download
, opening index.html
(or any of the pages in demo/
) should show you working demos../scripts/clean
, which removes all development artifacts including Docker containers and volumes.The local development server exposes a Node.js process WebSocket that can be attached to your IDE of choice to step through and debug your code. Here are instructions on how to do so using Google Chrome's Dev Tools:
./scripts/server
.about:inspect
.file:///home/tiler/bin
. Click "inspect", underneath.ctrl+p
to open a search bar that lets you navigate to your source code. The transpiled code in src/tiler/bin/
is what is actually being tracked by the debugger, not the source code in src/tiler/src/
.