{"id":13412994,"url":"https://github.com/consbio/mbtileserver","last_synced_at":"2025-03-14T19:30:53.830Z","repository":{"id":22697532,"uuid":"26041451","full_name":"consbio/mbtileserver","owner":"consbio","description":"Basic Go server for mbtiles","archived":false,"fork":false,"pushed_at":"2024-04-19T06:20:01.000Z","size":60659,"stargazers_count":629,"open_issues_count":15,"forks_count":98,"subscribers_count":17,"default_branch":"main","last_synced_at":"2024-07-31T20:51:47.625Z","etag":null,"topics":["go","mbtiles","tilejson","tilesets","vector-tiles"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/consbio.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2014-11-01T04:12:14.000Z","updated_at":"2024-07-31T12:25:31.000Z","dependencies_parsed_at":"2024-01-17T19:34:23.102Z","dependency_job_id":"27c162ea-c1e7-4b80-bd5b-69d12e20b8a6","html_url":"https://github.com/consbio/mbtileserver","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/consbio%2Fmbtileserver","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/consbio%2Fmbtileserver/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/consbio%2Fmbtileserver/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/consbio%2Fmbtileserver/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/consbio","download_url":"https://codeload.github.com/consbio/mbtileserver/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221495322,"owners_count":16832459,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","mbtiles","tilejson","tilesets","vector-tiles"],"created_at":"2024-07-30T20:01:32.076Z","updated_at":"2025-03-14T19:30:53.824Z","avatar_url":"https://github.com/consbio.png","language":"Go","readme":"# mbtileserver\n\nA simple Go-based server for map tiles stored in [mbtiles](https://github.com/mapbox/mbtiles-spec)\nformat.\n\n![Build Status](https://github.com/consbio/mbtileserver/actions/workflows/test.yml/badge.svg)\n[![Coverage Status](https://coveralls.io/repos/github/consbio/mbtileserver/badge.svg?branch=master)](https://coveralls.io/github/consbio/mbtileserver?branch=master)\n[![GoDoc](https://godoc.org/github.com/consbio/mbtileserver?status.svg)](http://godoc.org/github.com/consbio/mbtileserver)\n[![Go Report Card](https://goreportcard.com/badge/github.com/consbio/mbtileserver)](https://goreportcard.com/report/github.com/consbio/mbtileserver)\n\nIt currently provides support for `png`, `jpg`, `webp`, and `pbf` (vector tile)\ntilesets according to version 1.0 of the mbtiles specification. Tiles\nare served following the XYZ tile scheme, based on the Web Mercator\ncoordinate reference system. UTF8 Grids are no longer supported.\n\nIn addition to tile-level access, it provides:\n\n-   TileJSON 2.1.0 endpoint for each tileset, with full metadata\n    from the mbtiles file.\n-   a preview map for exploring each tileset.\n-   a minimal ArcGIS tile map service API\n\nWe have been able to host a bunch of tilesets on an\n[AWS t2.nano](https://aws.amazon.com/about-aws/whats-new/2015/12/introducing-t2-nano-the-smallest-lowest-cost-amazon-ec2-instance/)\nvirtual machine without any issues.\n\n## Goals\n\n-   Provide a web tile API for map tiles stored in mbtiles format\n-   Be fast\n-   Run on small resource cloud hosted machines (limited memory \u0026 CPU)\n-   Be easy to install and operate\n\n## Supported Go versions\n\n_Requires Go \u003e= 1.21+._\n\n`mbtileserver` uses go modules and follows standard practices as of Go 1.21.\n\n## Installation\n\nYou can install this project with\n\n```sh\ngo install github.com/consbio/mbtileserver@latest\n```\n\nThis will create and install an executable called `mbtileserver`.\n\n## Usage\n\nFrom within the repository root ($GOPATH/bin needs to be in your $PATH):\n\n```\n$  mbtileserver --help\nServe tiles from mbtiles files\n\nUsage:\n  mbtileserver [flags]\n\nFlags:\n      --basemap-style-url string   Basemap style URL for preview endpoint (can include authorization token parameter if required by host)\n      --basemap-tiles-url string   Basemap raster tiles URL pattern for preview endpoint (can include authorization token parameter if required by host): https://some.host/{z}/{x}/{y}.png\n  -c, --cert string                X.509 TLS certificate filename.  If present, will be used to enable SSL on the server.\n  -d, --dir string                 Directory containing mbtiles files.  Can be a comma-delimited list of directories. (default \"./tilesets\")\n      --disable-preview            Disable map preview for each tileset (enabled by default)\n      --disable-svc-list           Disable services list endpoint (enabled by default)\n      --disable-tilejson           Disable TileJSON endpoint for each tileset (enabled by default)\n      --domain string              Domain name of this server.  NOTE: only used for Auto TLS.\n      --dsn string                 Sentry DSN\n      --enable-arcgis              Enable ArcGIS Mapserver endpoints\n      --enable-fs-watch            Enable reloading of tilesets by watching filesystem\n      --enable-reload-signal       Enable graceful reload using HUP signal to the server process\n      --generate-ids               Automatically generate tileset IDs instead of using relative path\n  -h, --help                       help for mbtileserver\n      --host string                IP address to listen on. Default is all interfaces. (default \"0.0.0.0\")\n  -k, --key string                 TLS private key\n      --missing-image-tile-404     Return HTTP 404 error code when image tile is misssing instead of default behavior to return blank PNG\n  -p, --port int                   Server port.  Default is 443 if --cert or --tls options are used, otherwise 8000. (default -1)\n  -r, --redirect                   Redirect HTTP to HTTPS\n      --root-url string            Root URL of services endpoint (default \"/services\")\n  -s, --secret-key string          Shared secret key used for HMAC request authentication\n      --tiles-only                 Only enable tile endpoints (shortcut for --disable-svc-list --disable-tilejson --disable-preview)\n  -t, --tls                        Auto TLS via Let's Encrypt.  Requires domain to be set\n  -v, --verbose                    Verbose logging\n```\n\nSo hosting tiles is as easy as putting your mbtiles files in the `tilesets`\ndirectory and starting the server. Woo hoo!\n\nYou can have multiple directories in your `tilesets` directory; these will be converted into appropriate URLs:\n\n`\u003ctile_dir\u003e/foo/bar/baz.mbtiles` will be available at `/services/foo/bar/baz`.\n\nIf `--generate-ids` is provided, tileset IDs are automatically generated using a SHA1 hash of the path to each tileset.\nBy default, tileset IDs are based on the relative path of each tileset to the base directory provided using `--dir`.\n\nWhen you want to remove, modify, or add new tilesets, simply restart the server process or use one of the reloading processes below.\n\nIf a valid Sentry DSN is provided, warnings, errors, fatal errors, and panics will be reported to Sentry.\n\nIf `redirect` option is provided, the server also listens on port 80 and redirects to port 443.\n\nIf the `--tls` option is provided, the Let's Encrypt Terms of Service are accepted automatically on your behalf. Please review them [here](https://letsencrypt.org/repository/). Certificates are cached in a `.certs` folder created where you are executing `mbtileserver`. Please make sure this folder can be written by the `mbtileserver` process or you will get errors. Certificates are not requested until the first request is made to the server. We recommend that you initialize these after startup by making a request against `https://\u003chostname\u003e/services` and watching the logs from the server to make sure that certificates were processed correctly. Common errors include Let's Encrypt not being able to access your server at the domain you provided. `localhost` or internal-only domains will not work.\n\nIf either `--cert` or `--tls` are provided, the default port is 443.\n\nYou can also use environment variables instead of flags, which may be more helpful when deploying in a docker image. Use the associated flag to determine usage. The following variables are available:\n\n-   `PORT` (`--port`)\n-   `TILE_DIR` (`--dir`)\n-   `GENERATE_IDS` (`--generate-ids`)\n-   `ROOT_URL` (`--root-url`)\n-   `DOMAIN` (`--domain`)\n-   `TLS_CERT` (`--cert`)\n-   `TLS_PRIVATE_KEY` (`--key`)\n-   `HMAC_SECRET_KEY` (`--secret-key`)\n-   `AUTO_TLS` (`--tls`)\n-   `REDIRECT` (`--redirect`)\n-   `DSN` (`--dsn`)\n-   `VERBOSE` (`--verbose`)\n\nExample:\n\n```\nPORT=7777 TILE_DIR=./path/to/your/tiles VERBOSE=true mbtileserver\n```\n\nIn a docker-compose.yml file it will look like:\n\n```\nmbtileserver:\n  ...\n\n  environment:\n    PORT: 7777\n    TILE_DIR: \"./path/to/your/tiles\"\n    VERBOSE: true\n  entrypoint: mbtileserver\n\n  ...\n```\n\n### Reloading\n\n#### Reload using a signal\n\nmbtileserver optionally supports graceful reload (without interrupting any in-progress requests). This functionality\nmust be enabled with the `--enable-reload-signal` flag. When enabled, the server can be reloaded by sending it a `HUP` signal:\n\n```\nkill -HUP \u003cpid\u003e\n```\n\nReloading the server will cause it to pick up changes to the tiles directory, adding new tilesets and removing any that\nare no longer present.\n\n#### Reload using a filesystem watcher\n\nmbtileserver optionally supports reload of individual tilesets by watching for filesystem changes. This functionality\nmust be enabled with the `--enable-fs-watch` flag.\n\nAll directories specified by `-d` / `--dir` and any subdirectories that exist at the time the server is started\nwill be watched for changes to the tilesets.\n\nAn existing tileset that is being updated will be locked while the file on disk\nis being updated. This will cause incoming requests to that tileset to stall\nfor up to 30 seconds and will return as soon as the tileset is completely updated\nand unlocked. If it takes longer than 30 seconds for the tileset to be updated,\nHTTP 503 errors will be returned for that tileset until the tileset is completely\nupdated and unlocked.\n\nUnder very high request volumes, requests that come in between when the file is\nfirst modified and when that modification is first detected (and tileset locked)\nmay encounter errors.\n\nWARNING: Do not remove the top-level watched directories while the server is running.\n\nWARNING: Do not create or delete subdirectories within the watched directories while the server is running.\n\nWARNING: do not generate tiles directly in the watched directories. Instead, create them in separate directories and\ncopy them into the watched directories when complete.\n\n### Using with a reverse proxy\n\nYou can use a reverse proxy in front of `mbtileserver` to intercept incoming requests, provide TLS, etc.\n\nWe have used both [`Caddy`](https://caddyserver.com/) and [`NGINX`](https://www.nginx.com/) for our production setups in various projects,\nusually when we need to proxy to additional backend services.\n\nTo make sure that the correct request URL is passed to `mbtileserver` so that TileJSON and map preview endpoints work correctly,\nmake sure to have your reverse proxy send the following headers:\n\nScheme (HTTP vs HTTPS):\none of `X-Forwarded-Proto`, `X-Forwarded-Protocol`, `X-Url-Scheme` to set the scheme of the request.\nOR\n`X-Forwarded-Ssl` to automatically set the scheme to HTTPS.\n\nHost:\nSet `X-Forwarded-Host` to the correct host for the request.\n\n#### Caddy v2 Example:\n\nFor `mbtileserver` running on port 8000 locally, add the following to the block for your domain name:\n\n```\n\u003cdomain_name\u003e {\n  route /services* {\n    reverse_proxy localhost:8000\n  }\n}\n```\n\nYou may want to consider adding cache control headers within the `route` block\ndepending on how often the contents of your tilesets change. For instance,\nto prevent clients from caching tiles longer than 1 hour:\n\n```\n  route /services* {\n    header Cache-Control \"public, max-age=3600, must-revalidate\"\n    localhost mbtileserver:8000\n  }\n```\n\n#### NGINX Example:\n\nFor `mbtileserver` running on port 8000 locally, add the following to your `server` block:\n\n```\nserver {\n   \u003cother config options\u003e\n\n    location /services {\n        proxy_set_header Host $http_host;\n\tproxy_set_header X-Forwarded-Proto $scheme;\n\tproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-Ssl on;\n        proxy_pass http://localhost:8000;\n    }\n}\n```\n\n## Docker\n\nPull the latest image from\n[Github Container Registry](https://github.com/consbio/mbtileserver/pkgs/container/mbtileserver):\n\n```\ndocker pull ghcr.io/consbio/mbtileserver:latest\n```\n\nTo build the Docker image locally (named `mbtileserver`):\n\n```\ndocker build -t mbtileserver -f Dockerfile .\n```\n\nTo run the Docker container on port 8080 with your tilesets in `\u003chost tile dir\u003e`.\nNote that by default, `mbtileserver` runs on port 8000 in the container.\n\n```\ndocker run --rm -p 8080:8000 -v \u003chost tile dir\u003e:/tilesets  consbio/mbtileserver\n```\n\nYou can pass in additional command-line arguments to `mbtileserver`, for example, to use\ncertificates and files in `\u003chost cert dir\u003e` so that you can access the server via HTTPS. The example below uses self-signed certificates generated using\n[`mkcert`](https://github.com/FiloSottile/mkcert). This example uses automatic redirects, which causes `mbtileserver` to also listen on port 80 and automatically redirect to 443.\n\n```\ndocker run  --rm -p 80:80 -p 443:443 -v \u003chost tile dir\u003e:/tilesets -v \u003chost cert dir\u003e:/certs/ consbio/mbtileserver -c /certs/localhost.pem -k /certs/localhost-key.pem -p 443 --redirect\n```\n\nAlternately, use `docker-compose` to run:\n\n```\ndocker-compose up -d\n```\n\nThe default `docker-compose.yml` configures `mbtileserver` to connect to port 8080 on the host, and uses the `./mbtiles/testdata` folder for tilesets. You can use your own `docker-compose.override.yml` or [environment specific files](https://docs.docker.com/compose/extends/) to set these how you like.\n\nTo reload the server:\n\n```\ndocker exec -it mbtileserver sh -c \"kill -HUP 1\"\n```\n\n## Specifications\n\n-   expects mbtiles files to follow version 1.0 of the [mbtiles specification](https://github.com/mapbox/mbtiles-spec). Version 1.1 is preferred.\n-   implements [TileJSON 2.1.0](https://github.com/mapbox/tilejson-spec)\n\n## Creating Tiles\n\nYou can create mbtiles files using a variety of tools. We have created\ntiles for use with mbtileserver using:\n\n-   [TileMill](https://www.mapbox.com/tilemill/) (image tiles)\n-   [tippecanoe](https://github.com/mapbox/tippecanoe) (vector tiles)\n-   [pymbtiles](https://github.com/consbio/pymbtiles) (tiles created using Python)\n-   [tpkutils](https://github.com/consbio/tpkutils) (image tiles from ArcGIS tile packages)\n\nThe root name of each mbtiles file becomes the \"tileset_id\" as used below.\n\n## XYZ Tile API\n\nThe primary use of `mbtileserver` is as a host for XYZ tiles.\n\nThese are provided at:\n`/services/\u003ctileset_id\u003e/tiles/{z}/{x}/{y}.\u003cformat\u003e`\n\nwhere `\u003cformat\u003e` is one of `png`, `jpg`, `webp`, `pbf` depending on the type of data in the tileset.\n\n\n### Missing tiles\n\nMissing vector tiles are always returned as HTTP 204.\n\nMissing image tiles are returned as blank PNGs with the same dimensions as the tileset to give seamless display of\nthese tiles in interactive maps.\n\nWhen serving image tiles that encode data (e.g., terrain) instead of purely for display, this can cause issues.  In\nthis case, you can use the `--missing-image-tile-404` option.  This behavior will be applied to all image tilesets.\n\n\n## TileJSON API\n\n`mbtileserver` automatically creates a TileJSON endpoint for each service at `/services/\u003ctileset_id\u003e`.\nThe TileJSON uses the same scheme and domain name as is used for the incoming request; the `--domain` setting does not\naffect auto-generated URLs.\n\nThis API provides most elements of the `metadata` table in the mbtiles file as well as others that are\nautomatically inferred from tile data.\n\nFor example,\n`http://localhost/services/states_outline`\n\nreturns something like this:\n\n```\n{\n  \"bounds\": [\n    -179.23108,\n    -14.601813,\n    179.85968,\n    71.441055\n  ],\n  \"center\": [\n    0.314297,\n    28.419622,\n    1\n  ],\n  \"credits\": \"US Census Bureau\",\n  \"description\": \"States\",\n  \"format\": \"png\",\n  \"id\": \"states_outline\",\n  \"legend\": \"[{\\\"elements\\\": [{\\\"label\\\": \\\"\\\", \\\"imageData\\\": \\\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IB2cksfwAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAGFJREFUOI3tlDEOgEAIBClI5kF+w0fxwXvQdjZywcZEtDI31YaQgWrdPsYzAPFGJCmmEAhJGzCash0wSVE/HHnlKcDMfrPXYgmXcAl/JswK6lCrz89BdGVm1+qrH0bbWDgA3WwmgzD8ueEAAAAASUVORK5CYII=\\\"}], \\\"name\\\": \\\"tl_2015_us_state\\\"}]\",\n  \"map\": \"http://localhost/services/states_outline/map\",\n  \"maxzoom\": 4,\n  \"minzoom\": 0,\n  \"name\": \"states_outline\",\n  \"scheme\": \"xyz\",\n  \"tags\": \"states\",\n  \"tilejson\": \"2.1.0\",\n  \"tiles\": [\n    \"http://localhost/services/states_outline/tiles/{z}/{x}/{y}.png\"\n  ],\n  \"type\": \"overlay\",\n  \"version\": \"1.0.0\"\n}\n```\n\n## Map preview\n\n`mbtileserver` automatically creates a map preview page for each tileset at `/services/\u003ctileset_id\u003e/map`.\n\nIt uses `MapLibre GL` to render vector and image tiles.\n\nNo built-in basemap is included by default in the map preview.  You can use\none of the following options to include a basemap.\n\n### Basemap style URL\n\nTo include a [MapLibre GL style URL](https://maplibre.org/maplibre-style-spec/)\nuse the `--basemap-style-url` option to provide a URL to that style:\n\n```\n--basemap-style-url \"https://tiles.stadiamaps.com/styles/stamen_toner_lite.json?api_key=\u003cyour key\u003e\"\n```\n\nThe URL can include query parameters as required by the host, such as\n`?access_token=\u003csomething\u003e`.\n\n\n### Basemap tiles URL\n\nTo include a basemap based on image tile URLs, use the `--basemap-tiles-url`\noption to provide a raster tile URL pattern:\n\n```\n--basemap https://some.host/{z}/{x}/{y}.png\n```\n\nThe template parameters `{z}` (zoom), `{x}`, `{y}` are required.\n\nThe extension can be omitted or be any image format supported by MapLibre GL.\n\nThe URL can include query parameters as required by the host, such as\n`?access_token=\u003csomething\u003e`.\n\nIMPORTANT: this does not support vector tiles.\n\n\n## ArcGIS API\n\nThis project currently provides a minimal ArcGIS tiled map service API for tiles stored in an mbtiles file.\n\nThis is enabled with the `--enable-arcgis` flag.\n\nThis should be sufficient for use with online platforms such as [Data Basin](https://databasin.org). Because the ArcGIS API relies on a number of properties that are not commonly available within an mbtiles file, so certain aspects are stubbed out with minimal information.\n\nThis API is not intended for use with more full-featured ArcGIS applications such as ArcGIS Desktop.\n\nAvailable endpoints:\n\n-   Service info: `http://localhost:8000/arcgis/rest/services/\u003ctileset_id\u003e/MapServer`\n-   Layer info: `http://localhost:8000/arcgis/rest/services/\u003ctileset_id\u003e/MapServer/layers`\n-   Tiles: `http://localhost:8000/arcgis/rest/services/\u003ctileset_id\u003e/MapServer/tile/0/0/0`\n\n## Request authorization\n\nProviding a secret key with `-s/--secret-key` or by setting the `HMAC_SECRET_KEY` environment variable will\nrestrict access to all server endpoints and tile requests. Requests will only be served if they provide a cryptographic\nsignature created using the same secret key. This allows, for example, an application server to provide authorized\nclients a short-lived token with which the clients can access tiles for a specific service.\n\nSignatures expire 15 minutes from their creation date to prevent exposed or leaked signatures from being useful past a\nsmall time window.\n\n### Creating signatures\n\nA signature is a URL-safe, base64 encoded HMAC hash using the `SHA1` algorithm. The hash key is an `SHA1` key created\nfrom a randomly generated salt, and the **secret key** string. The hash payload is a combination of the ISO-formatted\ndate when the hash was created, and the authorized service id.\n\nThe following is an example signature, created in Go for the service id `test`, the date\n`2019-03-08T19:31:12.213831+00:00`, the salt `0EvkK316T-sBLA`, and the secret key\n`YMIVXikJWAiiR3q-JMz1v2Mfmx3gTXJVNqme5kyaqrY`\n\nCreate the SHA1 key:\n\n```go\nserviceId := \"test\"\ndate := \"2019-03-08T19:31:12.213831+00:00\"\nsalt := \"0EvkK316T-sBLA\"\nsecretKey := \"YMIVXikJWAiiR3q-JMz1v2Mfmx3gTXJVNqme5kyaqrY\"\n\nkey := sha1.New()\nkey.Write([]byte(salt + secretKey))\n```\n\nCreate the signature hash:\n\n```go\nhash := hmac.New(sha1.New, key.Sum(nil))\nmessage := fmt.Sprintf(\"%s:%s\", date, serviceId)\nhash.Write([]byte(message))\n```\n\nFinally, base64-encode the hash:\n\n```go\nb64hash := base64.RawURLEncoding.EncodeToString(hash.Sum(nil))\nfmt.Println(b64hash) // Should output: 2y8vHb9xK6RSxN8EXMeAEUiYtZk\n```\n\n### Making request\n\nAuthenticated requests must include the ISO-formatted date, and a salt-signature combination in the form of:\n`\u003csalt\u003e:\u003csignature\u003e`. These can be provided as query parameters:\n\n```text\n?date=2019-03-08T19:31:12.213831%2B00:00\u0026signature=0EvkK316T-sBLA:YMIVXikJWAiiR3q-JMz1v2Mfmx3gTXJVNqme5kyaqrY\n```\n\nOr they can be provided as request headers:\n\n```text\nX-Signature-Date: 2019-03-08T19:31:12.213831+00:00\nX-Signature: 0EvkK316T-sBLA:YMIVXikJWAiiR3q-JMz1v2Mfmx3gTXJVNqme5kyaqrY\n```\n\n## Development\n\nDependencies are managed using go modules. Vendored dependencies are stored in `vendor` folder by using `go mod vendor`.\n\nOn Windows, it is necessary to install `gcc` in order to compile `mattn/go-sqlite3`.\nMinGW or [TDM-GCC](https://sourceforge.net/projects/tdm-gcc/) should work fine.\n\nIf you experience very slow builds each time, it may be that you need to first run\n\n```\ngo build -a .\n```\n\nto make subsequent builds much faster.\n\nDevelopment of the templates and static assets requires using\nNodeJS 20 and `npm`. Install these tools in the normal way.\n\nFrom the `handlers/templates/static` folder, run\n\n```bash\nnpm install\n```\n\nto pull in the static dependencies. These are referenced in the\n`package.json` file.\n\nThen to build the minified version, run:\n\n```bash\nnpm run build\n```\n\nBuilt static assets are saved to `handlers/templates/static/dist` and included\nvia `go:embed` into the final executable.\n\nModifying the `.go` files or anything under `handlers/templates` always requires\nre-running `go build .`.\n\n## Changes\n\nSee [CHANGELOG](CHANGELOG.md).\n\n## Contributors ✨\n\nThanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --\u003e\n\u003c!-- prettier-ignore-start --\u003e\n\u003c!-- markdownlint-disable --\u003e\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://astutespruce.com\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/3375604?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eBrendan Ward\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/consbio/mbtileserver/commits?author=brendan-ward\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"https://github.com/consbio/mbtileserver/commits?author=brendan-ward\" title=\"Documentation\"\u003e📖\u003c/a\u003e \u003ca href=\"https://github.com/consbio/mbtileserver/issues?q=author%3Abrendan-ward\" title=\"Bug reports\"\u003e🐛\u003c/a\u003e \u003ca href=\"#blog-brendan-ward\" title=\"Blogposts\"\u003e📝\u003c/a\u003e \u003ca href=\"https://github.com/consbio/mbtileserver/pulls?q=is%3Apr+reviewed-by%3Abrendan-ward\" title=\"Reviewed Pull Requests\"\u003e👀\u003c/a\u003e \u003ca href=\"#ideas-brendan-ward\" title=\"Ideas, Planning, \u0026 Feedback\"\u003e🤔\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/fawick\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/1886500?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eFabian Wickborn\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/consbio/mbtileserver/commits?author=fawick\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"https://github.com/consbio/mbtileserver/commits?author=fawick\" title=\"Documentation\"\u003e📖\u003c/a\u003e \u003ca href=\"https://github.com/consbio/mbtileserver/issues?q=author%3Afawick\" title=\"Bug reports\"\u003e🐛\u003c/a\u003e \u003ca href=\"#ideas-fawick\" title=\"Ideas, Planning, \u0026 Feedback\"\u003e🤔\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/nikmolnar\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/2422416?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eNik Molnar\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/consbio/mbtileserver/commits?author=nikmolnar\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"#ideas-nikmolnar\" title=\"Ideas, Planning, \u0026 Feedback\"\u003e🤔\u003c/a\u003e \u003ca href=\"https://github.com/consbio/mbtileserver/issues?q=author%3Anikmolnar\" title=\"Bug reports\"\u003e🐛\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://sikmir.ru\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/688044?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eNikolay Korotkiy\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/consbio/mbtileserver/commits?author=sikmir\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"https://github.com/consbio/mbtileserver/issues?q=author%3Asikmir\" title=\"Bug reports\"\u003e🐛\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/retbrown\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/3111954?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eRobert Brown\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/consbio/mbtileserver/commits?author=retbrown\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"mika-go-13\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/26978815?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eMihail\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/consbio/mbtileserver/commits?author=mika-go-13\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/buma\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/1055967?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eMarko Burjek\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/consbio/mbtileserver/commits?author=buma\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/Krizz\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/689050?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eKristjan\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/consbio/mbtileserver/commits?author=Krizz\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/evbarnett\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/4960874?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eevbarnett\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/consbio/mbtileserver/issues?q=author%3Aevbarnett\" title=\"Bug reports\"\u003e🐛\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://www.walkaholic.me\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/19690868?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003ewalkaholic.me\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/consbio/mbtileserver/issues?q=author%3Acarlos-mg89\" title=\"Bug reports\"\u003e🐛\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"http://www.webiswhatido.com\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/1580910?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eBrian Voelker\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/consbio/mbtileserver/issues?q=author%3Abrianvoe\" title=\"Bug reports\"\u003e🐛\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"http://salesking.eu\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/13575?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eGeorg Leciejewski\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/consbio/mbtileserver/issues?q=author%3Aschorsch\" title=\"Bug reports\"\u003e🐛\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/cbenz\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/12686?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eChristophe Benz\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/consbio/mbtileserver/issues?q=author%3Acbenz\" title=\"Bug reports\"\u003e🐛\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/reyemtm\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/6398929?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eMalcolm Meyer\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/consbio/mbtileserver/issues?q=author%3Areyemtm\" title=\"Bug reports\"\u003e🐛\u003c/a\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/jleedev\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/23022?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eJosh Lee\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/consbio/mbtileserver/commits?author=jleedev\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/Martin8412\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/2369612?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eMartin Karlsen Jensen\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/consbio/mbtileserver/commits?author=Martin8412\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\u003c!-- markdownlint-restore --\u003e\n\u003c!-- prettier-ignore-end --\u003e\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:END --\u003e\n\nThis project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!\n","funding_links":[],"categories":["Go","Geographic","Relational Databases","位置信息与地理GEO处理库","地理","Servers"],"sub_categories":["Search and Analytic Databases","SQL 查询语句构建库","检索及分析资料库","Advanced Console UIs"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fconsbio%2Fmbtileserver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fconsbio%2Fmbtileserver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fconsbio%2Fmbtileserver/lists"}