{"id":31544530,"url":"https://github.com/jonathanlurie/basemapkit","last_synced_at":"2025-10-04T13:18:06.582Z","repository":{"id":300545427,"uuid":"1004554740","full_name":"jonathanlurie/basemapkit","owner":"jonathanlurie","description":"Basemaps for Protomaps / pmtiles","archived":false,"fork":false,"pushed_at":"2025-10-01T20:58:41.000Z","size":45647,"stargazers_count":55,"open_issues_count":2,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-01T22:27:05.249Z","etag":null,"topics":["basemaps","map","maplibre","protomaps"],"latest_commit_sha":null,"homepage":"https://s.jnth.io/s/basemapkit","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jonathanlurie.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-06-18T20:12:57.000Z","updated_at":"2025-09-30T12:17:43.000Z","dependencies_parsed_at":"2025-06-22T10:27:42.996Z","dependency_job_id":"7fe64f9b-797f-4c47-8d01-ba618013192f","html_url":"https://github.com/jonathanlurie/basemapkit","commit_stats":null,"previous_names":["jonathanlurie/pmbm","jonathanlurie/basemapkit"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/jonathanlurie/basemapkit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonathanlurie%2Fbasemapkit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonathanlurie%2Fbasemapkit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonathanlurie%2Fbasemapkit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonathanlurie%2Fbasemapkit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jonathanlurie","download_url":"https://codeload.github.com/jonathanlurie/basemapkit/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonathanlurie%2Fbasemapkit/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278317141,"owners_count":25967087,"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","status":"online","status_checked_at":"2025-10-04T02:00:05.491Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["basemaps","map","maplibre","protomaps"],"created_at":"2025-10-04T13:18:01.845Z","updated_at":"2025-10-04T13:18:06.572Z","avatar_url":"https://github.com/jonathanlurie.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./public/logo.svg\" alt=\"Basemapkit logo\" width=\"400px\"\u003e\u003c/img\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\nBasemaps for \u003ca href=\"https://maplibre.org/maplibre-gl-js/docs/\"\u003eMaplibre GL JS\u003c/a\u003e + \u003ca href=\"https://protomaps.com/\"\u003eProtomaps\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://img.shields.io/npm/v/basemapkit\"\u003e\u003c/img\u003e\n\u003c/p\u003e\n\n**Basemapkit** generates customizable styles compatible with **Maplibre GL JS** that relies on the **Protomaps** Planet schemas when it comes to [vector layers and feature properties](https://docs.protomaps.com/basemaps/layers). You can download your own PMtiles copy of the planet on the official [Protomaps build page](https://maps.protomaps.com/builds/). \n\n| |  |  |\n| :----------------: | :------: | ----: |\n| ![](./public/screenshots/eu-avenue.jpg) | ![](./public/screenshots/eu-avenue-pop.jpg) | ![](./public/screenshots/eu-avenue-night.jpg) |\n| ![](./public/screenshots/eu-avenue-bright.jpg) | ![](./public/screenshots/eu-avenue-saturated.jpg)| ![](./public/screenshots/eu-avenue-warm.jpg) |\n| ![](./public/screenshots/eu-avenue-vintage.jpg) | ![](./public/screenshots/eu-avenue-bnw.jpg) | ![](./public/screenshots/eu-avenue-blueprint.jpg) |\n| ![](./public/screenshots/terrain-zermatt.jpg) | ![](./public/screenshots/terrain-new-zealand.jpg) | ![](./public/screenshots/terrain-japan.jpg) |\n\n![](./public/screenshots/terrain-corsica.jpg)\n\n## Getting started 👷\n### Install\nOn an existing ES project:\n```bash\nnpm install basemapkit\n```\n\n### Add some style\nThe following example instantiates a Maplibre `Map`, then initializes the Protomaps protocol and then generates a style with Basemapkit:\n\n```ts\nimport \"maplibre-gl/dist/maplibre-gl.css\";\n\nimport maplibregl from \"maplibre-gl\";\nimport { Protocol } from \"pmtiles\";\nimport { getStyle, getStyleList } from \"basemapkit\";\n\n// Adds the Protomaps protocol:\nmaplibregl.addProtocol(\"pmtiles\", new Protocol().tile);\n\n// Build the Basemapkit style\nconst style = getStyle(\n// One of the main syle:\n\"avenue\", \n{\n  // URL to the pmtiles\n  pmtiles: \"https://my-s3-bucket.com/planet.pmtiles\",\n\n  // URL to the sprites (for POIs)\n  sprite: \"https://raw.githubusercontent.com/jonathanlurie/phosphor-mlgl-sprite/refs/heads/main/sprite/phosphor-diecut\",\n\n  // URL to the glyphs (for labels)\n  glyphs: \"https://protomaps.github.io/basemaps-assets/fonts/{fontstack}/{range}.pbf\";\n\n  // Language (you can ommit to use the platform language)\n  lang: \"en\",\n});\n\n// Instantiate the Map:\nconst map = new maplibregl.Map({\n  container: \"map\",\n  center: [0, 0],\n  zoom: 3,\n  \n  // Add the Basemapkit style:\n  style,\n});\n```\n\nIf using a traditional `tile.json` and `z/x/y` tiles instead of http-range-requesting a `pmtiles` file, then replace the option `pmtiles` by `tilejson`, as in the example below:\n\n```ts\nconst style = getStyle(\n// One of the main syle:\n\"avenue\", \n{\n  // URL to the tile.json\n  tilejson: \"https://example.com/tile.json\",\n\n  // URL to the sprites (for POIs)\n  sprite: \"https://raw.githubusercontent.com/jonathanlurie/phosphor-mlgl-sprite/refs/heads/main/sprite/phosphor-diecut\",\n\n  // URL to the glyphs (for labels)\n  glyphs: \"https://protomaps.github.io/basemaps-assets/fonts/{fontstack}/{range}.pbf\";\n\n  // Language (you can ommit to use the platform language)\n  lang: \"en\",\n});\n```\nThis can get particularly handy when using the [Protomaps CLI](https://docs.protomaps.com/pmtiles/cli) or [Martin](https://martin.maplibre.org/) to serve `z/x/y` vector tiles.\n\n## Language 📣\nBasemakit styles are compatible with Protomaps languages properties and uses [`@protomaps/basemaps`](https://docs.protomaps.com/basemaps/flavors) under the hood. \n\nThe only addition from **Basemapkit** is the capability to detect the end user's platform language, so if the `lang` option is omitted, it will automatically use the language set by the user at the browser or OS level.\n\nHere is the list of supported languages:\n```ts\n\"ar\" | \"cs\" | \"bg\" | \"da\" | \"de\" | \"el\" | \"en\" | \"es\" | \"et\" | \"fa\" | \"fi\" | \"fr\" | \"ga\" | \"he\" | \"hi\" | \"hr\" | \"hu\" | \"id\" | \"it\" | \"ja\" | \"ko\" | \"lt\" | \"lv\" | \"ne\" | \"nl\" | \"no\" | \"mr\" | \"mt\" | \"pl\" | \"pt\" | \"ro\" | \"ru\" | \"sk\" | \"sl\" | \"sv\" | \"tr\" | \"uk\" | \"ur\" | \"vi\" | \"zh-Hans\" | \"zh-Hant\"\n```\n\n## Terrain 🏔️\nBasemapkit can feature hillshading and/or terrain bumps when a terrain tileset is provided. The terrain tiles can be encoded as `\"mapbox\"` (default) or `\"terrarium\"`, in either PNG or WebP format. Then, the terrain tileset can be packed as *pmtiles* (``options.terrain.pmtiles`) or left as individual tiles, using a *tiles.json* as entry point (`options.terrain.tilejson`).  \n\n| |  |  | |  |\n| :----------------: | :------: | :----: | :------: | :----: |\n| ![](./public/screenshots/terrain-bask-country.jpg) | ![](./public/screenshots/terrain-zermatt.jpg) | ![](./public/screenshots/terrain-new-zealand.jpg) | ![](./public/screenshots/terrain-japan.jpg) | ![](./public/screenshots/terrain-grand-canyon.jpg) |\n\n![](./public/screenshots/terrain-corsica.jpg)\n\nHere is how to add hillshading but keep the terrain flat. Those settings are actually the default when the terrain tiles are provided.\n\n```ts\nconst style = getStyle(\n\"avenue\", \n{\n  pmtiles: \"\",\n  sprite: \"...\",\n  glyphs: \"...\";\n  lang: \"...\",\n\n  /**\n   * The terrain options:\n   */\n  terrain: {\n    /**\n     * The public URL of a pmtiles files for raster terrain, encoded on RGB channels of either PNG or WebP. To use if sourcing tiles directly with\n     * range-request using the `pmtiles`'s protocol. Alternatively, the option `tileJson` can be used and will take precedence.\n     */\n    pmtiles: \"https://my-s3-bucket.com/terrain.pmtiles\";\n\n    /**\n     * Enable or disable the hillshading. Enabled by default if one of the source options `terrain.pmtiles` or `terrain.tilejson` is provided.\n     * It cannot be enabled if none of the source option is provided.\n     */\n    hillshading: true,\n\n    /**\n     * The terrain exaggeration is disabled by default, making the terrain flat even if one of the source options `terrain.pmtiles` or `terrain.tilejson` is provided.\n     * A value of `1` produces at-scale realistic terrain elevation.\n     * It cannot be enabled if none of the source option is provided.\n     */\n    exaggeration: 0,\n\n    /**\n     * Encoding of the terrain raster data. Can be \"mapbox\" or \"terrarium\". Default: \"mapbox\"\n     */\n    encoding: \"mapbox\",\n  }\n});\n```\n\nAlternatively, if the terrain tiles are refered to with a `tiles.json`:\n\n```ts\nconst style = getStyle(\n\"avenue\", \n{\n  pmtiles: \"\",\n  sprite: \"...\",\n  glyphs: \"...\";\n  lang: \"...\",\n\n  /**\n   * The terrain options:\n   */\n  terrain: {\n    /**\n     * The public URL to a tile JSON file for raster terrain tiles, encoded on RGB channels of either PNG or WebP. To use if classic z/x/y MVT tiles are served through\n     * Maplibre's Martin or the pmtiles CLI. Will take precedence on the option `pmtiles` if both are provided.\n     */\n    tilejson: \"https://example.com/terrain-tile.json\";\n  }\n});\n```\n\n## POIs and labels 📍\nThere are options to hide the points of interests and labels. By default, both are shown, meaning the options goes like this:\n```ts\ngetStyle(\n  \"avenue\", \n  {\n    pmtiles: \"...\",\n    sprite: \"...\",\n    glyphs: \"...\",\n\n    hidePOIs: false,\n    hideLabels: false,\n  });\n```\nSo by default, London looks like this:\n![a part of the city of London with both POIs and labels](./public/screenshots/hide-nothing.jpg)\n\nBut POIs can be hidden by doing this:\n```ts\ngetStyle(\n  \"avenue\", \n  {\n    pmtiles: \"...\",\n    sprite: \"...\",\n    glyphs: \"...\",\n\n    hidePOIs: true,\n    hideLabels: false,\n  });\n```\nThen the same locations looks like this:\n![a part of the city of London with both POIs hidden](./public/screenshots/hide-pois.jpg)\n\nAlternatively, the labels can be hidden, this includes POIs' labels, so only POIs' icons will be shown by doing this:\n```ts\ngetStyle(\n  \"avenue\", \n  {\n    pmtiles: \"...\",\n    sprite: \"...\",\n    glyphs: \"...\",\n\n    hidePOIs: false,\n    hideLabels: true,\n  });\n```\nhere is how it looks like:\n![a part of the city of London with labels hidden](./public/screenshots/hide-labels.jpg)\n\nAnd finally, both labels and POIs can be hidden, resulting in a somewhat mysterious map:\n```ts\ngetStyle(\n  \"avenue\", \n  {\n    pmtiles: \"...\",\n    sprite: \"...\",\n    glyphs: \"...\",\n\n    hidePOIs: true,\n    hideLabels: true,\n  });\n```\n![a part of the city of London with labels and POIs hidden](./public/screenshots/hide-pois-labels.jpg)\n\nNote that the corresponding layers are removed from the style and not just made invisible. If hiding POIs or label, the options `sprite` and `glyph` are unnecessary.\n\n## Getting creative with `buildStyle()`\nIn addition to language and hiding POIs/labels, Basmapkit exposes some methods to modify the colors of the original style (`avenue`) to create *presets*. When the style is generated with some non-default `colorEdit`, a brand new Maplibre style is created and can be directly injected into a Maplibre `Map` instance's `.setStyle()` method, or even written as a static json file.\n\n```ts\n\nbuildStyle({\n    pmtiles: \"...\",\n    sprite: \"...\",\n    glyphs: \"...\",\n    terrain: {...},\n    hidePOIs: false,\n    hideLabels: false,\n  \n    // At the moment, \"avenue\" is the only style to start from\n    baseStyleName: \"avenue\",\n\n    colorEdit: {\n      // Invert the colors:\n      negate: false,\n\n      // In the range [-1, 1]:\n      brightness: 0,\n\n      // In the range [-1, 1]:\n      brightnessShift: 0,\n\n      // In the range [-1, 1]:\n      exposure: 0,\n\n      contrast: [\n        // intensity in the range [-1, 1]:\n        0,\n        // Midpoint in [0, 255]\n        127\n      ],\n\n      // Rotate around the hue wheel, in range [0, 360]\n      hueRotation: 0,\n\n      // In the range [-1, 1] \n      // with -1 being gray levels and 1 being extra boosted colors\n      saturation: 0,\n\n      // Color blending with a multiply method\n      multiplyColor: [\n        // Color to multiply with\n        \"#ff0000\",\n\n        // blending factor in [0, 1]\n        // with 0 being the original color and 1 being the the color above\n        0\n      ],\n\n      // Linear color blending\n      mixColor: [\n        // Color to blend with\n        \"#ff0000\",\n\n        // blending factor in [0, 1]\n        // with 0 being the original color and 1 being the the color above\n        0\n      ]\n    }\n\n  }\n);\n```\nFor instance, let's create a TMNT toxic N.Y.C. kind of map:\n```json\n{\n  \"baseStyleName\": \"avenue\",\n  \"lang\": \"en\",\n  \"hidePOIs\": true,\n  \"hideLabels\": false,\n  \"colorEdit\": {\n    \"negate\": true,\n    \"brightness\": 0.4,\n    \"brightnessShift\": 0,\n    \"exposure\": 0.8,\n    \"contrast\": [\n      0.4,\n      160\n    ],\n    \"hueRotation\": 80,\n    \"saturation\": 0.12,\n    \"multiplyColor\": [\n      \"#ff00ff\",\n      0.6\n    ],\n    \"mixColor\": [\n      \"#00ff00\",\n      0.3\n    ]\n  }\n}\n```\nAnd here is the result:\n![](./public/screenshots/tmnt-toxic-nyc.jpg)\n\nYou can live play with these on [basemapkit.jnth.io](https://s.jnth.io/s/basemapkit) and selecting the style `🖌️ custom 🎨`.  \nAnd from this \"color editor\" were created the built-in styles available below...\n\n\n## Style presets available\nSome custom `colorEdit` recipes are already built in Basemapkit and can be accessed directly from the `getStyle()` function.\n### `avenue` ⤵️\nThis one is the default, with all the `colorEdit` options set to default:\n```ts\n// Create the style\nconst style = getStyle(\"avenue\", options);\n```\n![](./public/screenshots/eu-avenue.jpg)\n![](./public/screenshots/nyc-avenue.jpg)\n![](./public/screenshots/jp-avenue.jpg)\n![](./public/screenshots/eiffel-avenue.jpg)\n![](./public/screenshots/alps-avenue.jpg)\n\n### `avenue-pop` ⤵️\n```ts\n// Create the style\nconst style = getStyle(\"avenue-pop\", options);\n```\n![](./public/screenshots/eu-avenue-pop.jpg)\n![](./public/screenshots/nyc-avenue-pop.jpg)\n![](./public/screenshots/jp-avenue-pop.jpg)\n![](./public/screenshots/eiffel-avenue-pop.jpg)\n![](./public/screenshots/alps-avenue-pop.jpg)\n \n \n### `avenue-night` ⤵️\n```ts\n// Create the style\nconst style = getStyle(\"avenue-night\", options);\n```\n![](./public/screenshots/eu-avenue-night.jpg)\n![](./public/screenshots/nyc-avenue-night.jpg)\n![](./public/screenshots/jp-avenue-night.jpg)\n![](./public/screenshots/eiffel-avenue-night.jpg)\n![](./public/screenshots/alps-avenue-night.jpg)\n \n### `avenue-bright` ⤵️\n```ts\n// Create the style\nconst style = getStyle(\"avenue-bright\", options);\n```\n![](./public/screenshots/eu-avenue-bright.jpg)\n![](./public/screenshots/nyc-avenue-bright.jpg)\n![](./public/screenshots/jp-avenue-bright.jpg)\n![](./public/screenshots/eiffel-avenue-bright.jpg)\n![](./public/screenshots/alps-avenue-bright.jpg)\n\n### `avenue-saturated` ⤵️\n```ts\n// Create the style\nconst style = getStyle(\"avenue-saturated\", options);\n```\n![](./public/screenshots/eu-avenue-saturated.jpg)\n![](./public/screenshots/nyc-avenue-saturated.jpg)\n![](./public/screenshots/jp-avenue-saturated.jpg)\n![](./public/screenshots/eiffel-avenue-saturated.jpg)\n![](./public/screenshots/alps-avenue-saturated.jpg)\n\n### `avenue-warm` ⤵️\n```ts\n// Create the style\nconst style = getStyle(\"avenue-warm\", options);\n```\n![](./public/screenshots/eu-avenue-warm.jpg)\n![](./public/screenshots/nyc-avenue-warm.jpg)\n![](./public/screenshots/jp-avenue-warm.jpg)\n![](./public/screenshots/eiffel-avenue-warm.jpg)\n![](./public/screenshots/alps-avenue-warm.jpg)\n\n### `avenue-vintage` ⤵️\n```ts\n// Create the style\nconst style = getStyle(\"avenue-vintage\", options);\n```\n![](./public/screenshots/eu-avenue-vintage.jpg)\n![](./public/screenshots/nyc-avenue-vintage.jpg)\n![](./public/screenshots/jp-avenue-vintage.jpg)\n![](./public/screenshots/eiffel-avenue-vintage.jpg)\n![](./public/screenshots/alps-avenue-vintage.jpg)\n \n### `avenue-bnw` ⤵️\n```ts\n// Create the style\nconst style = getStyle(\"avenue-bnw\", options);\n```\n![](./public/screenshots/eu-avenue-bnw.jpg)\n![](./public/screenshots/nyc-avenue-bnw.jpg)\n![](./public/screenshots/jp-avenue-bnw.jpg)\n![](./public/screenshots/eiffel-avenue-bnw.jpg)\n![](./public/screenshots/alps-avenue-bnw.jpg)\n\n### `avenue-blueprint` ⤵️\n```ts\n// Create the style\nconst style = getStyle(\"avenue-blueprint\", options);\n```\n![](./public/screenshots/eu-avenue-blueprint.jpg)\n![](./public/screenshots/nyc-avenue-blueprint.jpg)\n![](./public/screenshots/jp-avenue-blueprint.jpg)\n![](./public/screenshots/eiffel-avenue-blueprint.jpg)\n![](./public/screenshots/alps-avenue-blueprint.jpg)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonathanlurie%2Fbasemapkit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjonathanlurie%2Fbasemapkit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonathanlurie%2Fbasemapkit/lists"}