├── .editorconfig
├── .gitignore
├── CONTRIBUTING.md
├── INSTALL.md
├── LICENSE
├── README.md
├── STRUCTURE.md
├── docs
└── chromajs.png
├── expire.lua
├── external-data.yml
├── scripts
└── get-external-data.py
├── serve.py
├── shortbread.lua
├── shortbread.yaml
├── shortbread
├── boundaries.sql.jinja2
├── boundary_labels.sql.jinja2
├── buildings.sql.jinja2
├── dam_lines.sql.jinja2
├── dam_polygons.sql.jinja2
├── land.sql.jinja2
├── ocean.sql.jinja2
├── public_transport.sql.jinja2
├── sites.sql.jinja2
├── street_labels.sql.jinja2
├── street_labels_points.sql.jinja2
├── street_polygons.sql.jinja2
├── streets.sql.jinja2
├── streets_polygons_labels.sql.jinja2
├── water_polygons.sql.jinja2
└── water_polygons_labels.sql.jinja2
├── shortbread_original
├── addresses.14-14.sql.jinja2
├── aerialways.12-14.sql.jinja2
├── bridges.12-14.sql.jinja2
├── ferries.10-14.sql.jinja2
├── pier_lines.12-14.sql.jinja2
├── pier_polygons.12-14.sql.jinja2
├── place_labels.04-14.sql.jinja2
├── pois.14-14.sql.jinja2
├── water_lines.09-14.sql.jinja2
└── water_lines_labels.12-14.sql.jinja2
├── spirit.lua
├── spirit.yaml
├── spirit
├── admin-names.sql.jinja2
├── admin.sql.jinja2
├── aeroways.sql.jinja2
├── building-names.sql.jinja2
├── buildings.sql.jinja2
├── education-names.sql.jinja2
├── education.sql.jinja2
├── food.sql.jinja2
├── landuse-names.sql.jinja2
├── landuse.sql.jinja2
├── leisure-names.sql.jinja2
├── leisure.sql.jinja2
├── railways-high.sql.jinja2
├── roads-high.sql.jinja2
├── settlements.sql.jinja2
├── transit-points.sql.jinja2
├── vegetation-names.sql.jinja2
├── vegetation.sql.jinja2
├── water-lines.sql.jinja2
├── water-names.sql.jinja2
└── water.sql.jinja2
├── sprites
├── airport.svg
├── bus_station.svg
├── bus_stop.svg
├── food.svg
├── subway.svg
├── train.svg
├── wetland.md
└── wetland.svg
├── style.yaml
├── style
├── admin-2.yaml
├── admin-4.yaml
├── admin-6.yaml
├── admin-8.yaml
├── admin-background-dashed.yaml
├── admin-background.yaml
├── admin-names.yaml
├── aeroway-text.yaml
├── aeroways.yaml
├── background.yaml
├── building-fill.yaml
├── building-names.yaml
├── building-outline.yaml
├── education-names.yaml
├── education-outline.yaml
├── education.yaml
├── food.yaml
├── inc
│ ├── regular-font.yaml
│ ├── road-casing-color.yaml
│ └── road-fill-color.yaml
├── landuse-names.yaml
├── landuse.yaml
├── leisure-names.yaml
├── leisure.yaml
├── rail-bridge-casing.yaml
├── rail-bridge-fill.yaml
├── rail-bridge-outer-casing.yaml
├── rail-casing.yaml
├── rail-fill.yaml
├── road-base-casing.yaml
├── road-base-fill.yaml
├── road-bridge-casing.yaml
├── road-bridge-fill.yaml
├── road-casing.yaml
├── road-fill.yaml
├── road-text.yaml
├── road-tunnel-casing.yaml
├── settlement-names.yaml
├── transit-points.yaml
├── vegetation-names.yaml
├── vegetation-pattern.yaml
├── vegetation.yaml
├── water-line-text.yaml
├── water-lines.yaml
├── water-names.yaml
└── water.yaml
└── themes
├── shortbread
├── init.lua
└── topics
│ ├── addresses.lua
│ ├── aerialways.lua
│ ├── boundaries.lua
│ ├── boundary_labels.lua
│ ├── bridges.lua
│ ├── dams.lua
│ ├── ferries.lua
│ ├── land.lua
│ ├── piers.lua
│ ├── places.lua
│ ├── pois.lua
│ ├── public_transport.lua
│ ├── sites.lua
│ ├── streets.lua
│ └── water.lua
└── spirit
├── common.lua
├── init.lua
└── topics
├── admin.lua
├── aeroway.lua
├── buildings.lua
├── education.lua
├── food.lua
├── landuse.lua
├── leisure.lua
├── places.lua
├── railway.lua
├── roads.lua
├── transit.lua
├── vegetation.lua
└── water.lua
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://EditorConfig.org
2 |
3 | root = true
4 |
5 | # Defaults apply to all files
6 | [*]
7 | charset = utf-8
8 | end_of_line = lf
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 | indent_style = space
12 | indent_size = 4
13 |
14 | [*.{sql.jinja2,yaml}]
15 | indent_size = 2
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | *.xml
3 | *.osm.pbf
4 | spirit.json
5 | sprites.png
6 | sprites.json
7 | sprites@2x.png
8 | sprites@2x.json
9 | z*.txt
10 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Street Spirit contribution guidelines
2 |
3 | ## Cartography
4 |
5 | ### Purpose
6 |
7 | Street Spirit aims to be a general purpose map style for OpenStreetMap data, suitable
8 | - for use as a locator map,
9 | - to show off what can be done with OpenStreetMap data,
10 | - to be up-to-date with the latest OpenStreetMap data, and
11 | - for using to orient a viewer to a location they are at.
12 |
13 | There is no ranking of these goals, and they may require compromises between the different goals.
14 |
15 | It does not seek to
16 | - be a suitable style for overlaying complex data on,
17 | - drive OpenStreetMap tagging practices, or
18 | - be a replacement for maps with a specialized topic.
19 |
20 | ### Cartographic guidelines
21 |
22 | As this style doesn't aim to have data overlayed on it, it can use all the available cartographic space. It does not need to avoid particular colours or symbols, except for icons that look like typical locators pins, as these might be used by locator maps, and would be confusing regardless.
23 |
24 | This enables a wider range of colour and saturation than typical general-purpose web maps, more similar to topographic maps and atlases.
25 |
26 | ### Technical targets
27 |
28 | We target Maplibre GL JS usage as part of a web-page that is either focused on the map, or has the map as part of a larger page. Usage across desktops, tablets, and phones is supported, with support for high-DPI displays. Smart watches and print are not targeted.
29 |
30 | ## Icons
31 |
32 | - All icons must be SVG, saved as standards compliant SVG without any proprietary tags. In Inkscape software, you will need to "Save As..." and choose the format Optimized SVG (preferable) or Plain SVG.
33 | - Use a common canvas size, which is usually 15x15 px.
34 | - Align vectors to the pixel grid.
35 | - Make a clean design, so reduced complexity where possible.
36 |
37 | ## Colours
38 |
39 | Work out colours in the LCH colour space, because RGB and other non-perceptual colourspaces are hard to reason about.
40 |
41 | The [chroma.js documentation](https://gka.github.io/chroma.js/) can function as a colour calculator.
42 |
43 | 
44 |
45 | ### Hues
46 |
47 | Use a consistent hue angle for a class of features. The following hue angles are used in this style
48 |
49 |
50 |
51 |
52 | Features
53 | Hue
54 |
55 |
56 |
57 |
58 | Water
59 | 240°
60 |
61 |
62 | Transit
63 | 280°
64 |
65 |
66 | Admin borders
67 | 330°
68 |
69 |
70 | Buildings
71 | 100°
72 |
73 |
74 | Food
75 | 70°
76 |
77 |
78 |
79 |
80 | ## Required technical knowledge
81 |
82 | Contributing to the style requires some technical knowledge in the following areas
83 |
84 | - [MapLibre GL style specification](https://maplibre.org/maplibre-gl-js-docs/style-spec/), focusing on layers and expressions, including data-driven expressions;
85 | - YAML, in particular appropriate indentation for arrays. MapLibre GL styles tend to feature deeply nested arrays; and
86 | - SQL for writing read-only PostGIS queries if modifying vector tiles.
87 |
--------------------------------------------------------------------------------
/INSTALL.md:
--------------------------------------------------------------------------------
1 | # Install
2 |
3 | *These instructions are for installing the **shortbread** code, not street spirit itself.*
4 |
5 | ## Requirements
6 |
7 | - [osm2pgsql](https://osm2pgsql.org/) 1.9.2+
8 | - [osm2pgsql themepark](https://osm2pgsql.org/themepark/)
9 | - [Python](https://www.python.org/) 3.10+
10 | - [PostgreSQL](https://www.postgresql.org/) 10+
11 | - [PostGIS](https://postgis.net/) 3.3+
12 | - [Tilekiln](https://github.com/pnorman/tilekiln) 0.3.0+
13 |
14 | ## Installing dependencies
15 |
16 | Install tilekiln into a Python virtualenv with
17 |
18 | ```sh
19 | python3 -m venv venv
20 | venv/bin/pip install tilekiln
21 | ```
22 |
23 | ### Themepark
24 |
25 | Install themepark to somewhere on your system, e.g. `$HOME/osm2pgsql-themepark`
26 |
27 | ```sh
28 | git clone https://github.com/osm2pgsql-dev/osm2pgsql-themepark.git $HOME/osm2pgsql-themepark
29 | ```
30 |
31 | Set your LUA_PATH to include themepark, e.g.
32 |
33 | ```sh
34 | export LUA_PATH="$HOME/osm2pgsql-themepark/lua/?.lua;;"
35 | ```
36 | ## Loading Data
37 |
38 | Data can be loaded to create tiles for Street Spirit, Shortbread, or both.
39 |
40 | ### OpenStreetMap Data
41 |
42 | You need OpenStreetMap data loaded into a PostGIS database. These stylesheets expect a database generated with osm2pgsql using the flex backend with the supplied Lua scripts.
43 |
44 | Start by creating a database and enabling PostGIS
45 |
46 | ```
47 | sudo -u postgres createuser -s $USER
48 | createdb spirit
49 | psql -d spirit -c 'CREATE EXTENSION postgis;'
50 | ```
51 |
52 | Grab some OpenStreetMap data. It's probably easiest to grab a PBF of OSM data from [Geofabrik](https://download.geofabrik.de/). Once you've done that, import with osm2pgsql:
53 |
54 | ```
55 | osm2pgsql --output flex --style shortbread.lua -d spirit ~/path/to/data.osm.pbf
56 | ```
57 |
58 | If you are only creating Shortbread tiles, instead of ``--style shortbread.lua`` use ``--style spirit.lua``
59 |
60 | ### Scripted download
61 | Some features are rendered using preprocessed shapefiles.
62 |
63 | To download them and import them into the database you can run the following script
64 |
65 | ```
66 | scripts/get-external-data.py
67 | ```
68 |
69 | The script downloads shapefiles, loads them into the database and sets up the tables for rendering. Additional script option documentation can be seen with `scripts/get-external-data.py --help`.
70 |
71 | ## Serving tiles
72 |
73 | Once tilekiln is installed, run it in development mode with
74 |
75 | ```sh
76 | venv/bin/tilekiln serve dev --config shortbread.yaml --source-dbname spirit
77 | ```
78 |
79 | To create shortbread tiles, instead use ``--config spirit.yaml``. If only creating shortbread tiles you do not need to serve sprites or view the style.
80 |
81 |
82 | ## Street spirit notes
83 |
84 | *You only need these if not generating shortbread*
85 |
86 | - [charites](https://github.com/unvt/charites)
87 | - [@basemaps/sprites](https://www.npmjs.com/package/@basemaps/sprites)
88 |
89 |
90 | Install charites with
91 |
92 | ```sh
93 | npm install @unvt/charites @basemaps/sprites
94 | ```
95 |
96 | ## Serving sprites
97 |
98 | In another terminal window, run
99 |
100 | ```sh
101 | node_modules/.bin/basemaps-sprites sprites && python3 serve.py
102 | ```
103 |
104 | ## Viewing the style
105 |
106 | With tiles being served in one terminal session, run a local style server with
107 |
108 | ```sh
109 | node_modules/.bin/charites serve style.yaml
110 | ```
111 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Creative Commons Legal Code
2 |
3 | CC0 1.0 Universal
4 |
5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
12 | HEREUNDER.
13 |
14 | Statement of Purpose
15 |
16 | The laws of most jurisdictions throughout the world automatically confer
17 | exclusive Copyright and Related Rights (defined below) upon the creator
18 | and subsequent owner(s) (each and all, an "owner") of an original work of
19 | authorship and/or a database (each, a "Work").
20 |
21 | Certain owners wish to permanently relinquish those rights to a Work for
22 | the purpose of contributing to a commons of creative, cultural and
23 | scientific works ("Commons") that the public can reliably and without fear
24 | of later claims of infringement build upon, modify, incorporate in other
25 | works, reuse and redistribute as freely as possible in any form whatsoever
26 | and for any purposes, including without limitation commercial purposes.
27 | These owners may contribute to the Commons to promote the ideal of a free
28 | culture and the further production of creative, cultural and scientific
29 | works, or to gain reputation or greater distribution for their Work in
30 | part through the use and efforts of others.
31 |
32 | For these and/or other purposes and motivations, and without any
33 | expectation of additional consideration or compensation, the person
34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she
35 | is an owner of Copyright and Related Rights in the Work, voluntarily
36 | elects to apply CC0 to the Work and publicly distribute the Work under its
37 | terms, with knowledge of his or her Copyright and Related Rights in the
38 | Work and the meaning and intended legal effect of CC0 on those rights.
39 |
40 | 1. Copyright and Related Rights. A Work made available under CC0 may be
41 | protected by copyright and related or neighboring rights ("Copyright and
42 | Related Rights"). Copyright and Related Rights include, but are not
43 | limited to, the following:
44 |
45 | i. the right to reproduce, adapt, distribute, perform, display,
46 | communicate, and translate a Work;
47 | ii. moral rights retained by the original author(s) and/or performer(s);
48 | iii. publicity and privacy rights pertaining to a person's image or
49 | likeness depicted in a Work;
50 | iv. rights protecting against unfair competition in regards to a Work,
51 | subject to the limitations in paragraph 4(a), below;
52 | v. rights protecting the extraction, dissemination, use and reuse of data
53 | in a Work;
54 | vi. database rights (such as those arising under Directive 96/9/EC of the
55 | European Parliament and of the Council of 11 March 1996 on the legal
56 | protection of databases, and under any national implementation
57 | thereof, including any amended or successor version of such
58 | directive); and
59 | vii. other similar, equivalent or corresponding rights throughout the
60 | world based on applicable law or treaty, and any national
61 | implementations thereof.
62 |
63 | 2. Waiver. To the greatest extent permitted by, but not in contravention
64 | of, applicable law, Affirmer hereby overtly, fully, permanently,
65 | irrevocably and unconditionally waives, abandons, and surrenders all of
66 | Affirmer's Copyright and Related Rights and associated claims and causes
67 | of action, whether now known or unknown (including existing as well as
68 | future claims and causes of action), in the Work (i) in all territories
69 | worldwide, (ii) for the maximum duration provided by applicable law or
70 | treaty (including future time extensions), (iii) in any current or future
71 | medium and for any number of copies, and (iv) for any purpose whatsoever,
72 | including without limitation commercial, advertising or promotional
73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
74 | member of the public at large and to the detriment of Affirmer's heirs and
75 | successors, fully intending that such Waiver shall not be subject to
76 | revocation, rescission, cancellation, termination, or any other legal or
77 | equitable action to disrupt the quiet enjoyment of the Work by the public
78 | as contemplated by Affirmer's express Statement of Purpose.
79 |
80 | 3. Public License Fallback. Should any part of the Waiver for any reason
81 | be judged legally invalid or ineffective under applicable law, then the
82 | Waiver shall be preserved to the maximum extent permitted taking into
83 | account Affirmer's express Statement of Purpose. In addition, to the
84 | extent the Waiver is so judged Affirmer hereby grants to each affected
85 | person a royalty-free, non transferable, non sublicensable, non exclusive,
86 | irrevocable and unconditional license to exercise Affirmer's Copyright and
87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the
88 | maximum duration provided by applicable law or treaty (including future
89 | time extensions), (iii) in any current or future medium and for any number
90 | of copies, and (iv) for any purpose whatsoever, including without
91 | limitation commercial, advertising or promotional purposes (the
92 | "License"). The License shall be deemed effective as of the date CC0 was
93 | applied by Affirmer to the Work. Should any part of the License for any
94 | reason be judged legally invalid or ineffective under applicable law, such
95 | partial invalidity or ineffectiveness shall not invalidate the remainder
96 | of the License, and in such case Affirmer hereby affirms that he or she
97 | will not (i) exercise any of his or her remaining Copyright and Related
98 | Rights in the Work or (ii) assert any associated claims and causes of
99 | action with respect to the Work, in either case contrary to Affirmer's
100 | express Statement of Purpose.
101 |
102 | 4. Limitations and Disclaimers.
103 |
104 | a. No trademark or patent rights held by Affirmer are waived, abandoned,
105 | surrendered, licensed or otherwise affected by this document.
106 | b. Affirmer offers the Work as-is and makes no representations or
107 | warranties of any kind concerning the Work, express, implied,
108 | statutory or otherwise, including without limitation warranties of
109 | title, merchantability, fitness for a particular purpose, non
110 | infringement, or the absence of latent or other defects, accuracy, or
111 | the present or absence of errors, whether or not discoverable, all to
112 | the greatest extent permissible under applicable law.
113 | c. Affirmer disclaims responsibility for clearing rights of other persons
114 | that may apply to the Work or any use thereof, including without
115 | limitation any person's Copyright and Related Rights in the Work.
116 | Further, Affirmer disclaims responsibility for obtaining any necessary
117 | consents, permissions or other rights required for any use of the
118 | Work.
119 | d. Affirmer understands and acknowledges that Creative Commons is not a
120 | party to this document and has no duty or obligation with respect to
121 | this CC0 or use of the Work.
122 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Street Spirit style
2 |
3 | ## About
4 |
5 | Street Spirit aims to be a general purpose map style for OpenStreetMap data, using client-side rendering and vector tiles. It is starting from a mostly fresh start, taking advantage of the decade of lessons in designing an open source stylesheet in [OpenStreetMap Carto](https://github.com/gravitystorm/openstreetmap-carto).
6 |
7 | In addition to the Street Spirit style, this code can optionally generate [Shortbread](https://shortbread-tiles.org/) schema vector tiles.
8 |
9 | ## Installing
10 |
11 | Installation instructions are in [INSTALL.md](INSTALL.md).
12 |
13 | ## Contributing
14 |
15 | Contributions to this project are welcome, see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. The project is currently in a state of flux, so it's best to ask before starting work.
16 |
17 | ## License
18 |
19 | ### Style and documentation
20 |
21 | Copyright Paul Norman 2022-2025
22 |
23 | This map style, map icons, and associated documentation files (the "Style") is
24 | released under the CC0 Public Domain Dedication, version 1.0, as
25 | published by Creative Commons. To the extent possible under law, the
26 | author(s) have dedicated all copyright and related and neighboring
27 | rights to the Style to the public domain worldwide. The Style is
28 | distributed WITHOUT ANY WARRANTY.
29 |
30 | You should have received a copy of the CC0 legalcode along with this
31 | work. If not, see .
32 |
33 | ### Code implementation
34 |
35 | The code is licensed under the Apache License, Version 2.0 (the "License");
36 | you may not use this file except in compliance with the License. You may obtain a copy of the License at
37 |
38 | https://www.apache.org/licenses/LICENSE-2.0
39 |
40 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
41 |
42 | For avoidance of doubt "the code" includes the code that is compiled into a Maplibre GL style as well as all other code in this repository.
43 |
--------------------------------------------------------------------------------
/STRUCTURE.md:
--------------------------------------------------------------------------------
1 | # Project Structure
2 |
3 | ## Overview
4 |
5 | The Street Spirit contains the Street Spirit style with underlying vector tiles, code for generating [Shortbread Tiles](https://shortbread-tiles.org/), and code for generating the underlying database that powers both.
6 |
7 | ## Street Spirit client-side style
8 |
9 | Street Spirit has a client-side Maplibre GL style. The root file for this is [`style.yaml`](/style.yaml) which relies on the included files in [`/style/`](/style/). These files are built with [Charites](https://unvt.github.io/charites/) to produce the Maplibre style.
10 |
11 | ## Street Spirit vector tiles
12 |
13 | The client-side style is applied to vector tiles which are generated by [Tilekiln](https://github.com/pnorman/tilekiln) with the configuration file [`spirit.yaml`](/spirit.yaml). Layers are generated using the files in [`spirit/`](/spirit/).
14 |
15 | ## Shortbread vector tiles
16 |
17 | Shortbread tiles implement the [Shortbread tile schema](https://shortbread-tiles.org/), a basic, lean, general-purpose vector tile schema for OpenStreetMap data. Unlike the vector tiles used internally by Street Spirit, these tiles are written with the goal of being usable by many styles. The Tilekiln configuration is [`shortbread.yaml`](/shortbread.yaml) with layer files in [`shortbread/`](/shortbread/) and [`shortbread_original/`](/shortbread_original/). These files are based on ones generated by osm2pgsql-themepark and the files in `shortbread_original/` have not yet been modified.
18 |
19 | ## Database loading
20 |
21 | The database for both Street Spirit and Shortbread tiles is loaded with osm2pgsql, with utility scripts for other data sources like oceans. The lua transforms are [`spirit.lua`](/spirit.lua) and [`shortbread.lua`](/shortbread.lua). They rely on osm2pgsql themepark themes in [`themes/`](/themes/) and some themes in osm2pgsql-themepark itself. Some themes are common between the Street Spirit and Shortbread databases.
22 |
23 | In the future there will be a lua transform which combines the Street Spirit and Shortbread ones, producing a database that can be used for either type of tiles.
24 |
25 | Ocean polygons are loaded by [`scripts/get-external-data.py`](/scripts/get-external-data.py) as specified in [`external-data.yml`](/external-data.yml).
26 |
--------------------------------------------------------------------------------
/docs/chromajs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pnorman/spirit/033a4117ec3604e28824b3f3608f5d163ef1b450/docs/chromajs.png
--------------------------------------------------------------------------------
/expire.lua:
--------------------------------------------------------------------------------
1 | local expire = {}
2 |
3 | local function shortbread(minzoom, maxzoom, layer, mode)
4 | -- osm2pgsql requies a minzoom of at least 1
5 | if minzoom == 0 then
6 | minzoom = 1
7 | end
8 | expire[layer] = {}
9 | for zoom=minzoom, maxzoom do
10 | table.insert(expire[layer], {output=osm2pgsql.define_expire_output({
11 | maxzoom = zoom,
12 | filename = "z"..zoom.."-"..layer..".txt",
13 | mode = mode
14 | })})
15 | end
16 | return expire[layer]
17 | end
18 |
19 | return {shortbread = shortbread}
20 |
--------------------------------------------------------------------------------
/external-data.yml:
--------------------------------------------------------------------------------
1 | settings:
2 | temp_schema: loading
3 | schema: public
4 | data_dir: data
5 | database: spirit
6 | metadata_table: external_data
7 | sources:
8 | simplified_water_polygons:
9 | # The type of file this source is
10 | type: shp
11 | # Where to get it
12 | url: https://osmdata.openstreetmap.de/download/simplified-water-polygons-split-3857.zip
13 | # The location within the archive
14 | file: simplified-water-polygons-split-3857/simplified_water_polygons.shp
15 | archive:
16 | format: zip
17 | # Files to extract from the archive
18 | files:
19 | - simplified-water-polygons-split-3857/simplified_water_polygons.cpg
20 | - simplified-water-polygons-split-3857/simplified_water_polygons.dbf
21 | - simplified-water-polygons-split-3857/simplified_water_polygons.prj
22 | - simplified-water-polygons-split-3857/simplified_water_polygons.shp
23 | - simplified-water-polygons-split-3857/simplified_water_polygons.shx
24 | water_polygons:
25 | type: shp
26 | url: https://osmdata.openstreetmap.de/download/water-polygons-split-3857.zip
27 | file: water-polygons-split-3857/water_polygons.shp
28 | archive:
29 | format: zip
30 | files:
31 | - water-polygons-split-3857/water_polygons.cpg
32 | - water-polygons-split-3857/water_polygons.dbf
33 | - water-polygons-split-3857/water_polygons.prj
34 | - water-polygons-split-3857/water_polygons.shp
35 | - water-polygons-split-3857/water_polygons.shx
36 | icesheet_polygons:
37 | type: shp
38 | url: https://osmdata.openstreetmap.de/download/antarctica-icesheet-polygons-3857.zip
39 | file: antarctica-icesheet-polygons-3857/icesheet_polygons.shp
40 | archive:
41 | format: zip
42 | files:
43 | - antarctica-icesheet-polygons-3857/icesheet_polygons.cpg
44 | - antarctica-icesheet-polygons-3857/icesheet_polygons.dbf
45 | - antarctica-icesheet-polygons-3857/icesheet_polygons.prj
46 | - antarctica-icesheet-polygons-3857/icesheet_polygons.shp
47 | - antarctica-icesheet-polygons-3857/icesheet_polygons.shx
48 | icesheet_outlines:
49 | type: shp
50 | url: https://osmdata.openstreetmap.de/download/antarctica-icesheet-outlines-3857.zip
51 | file: antarctica-icesheet-outlines-3857/icesheet_outlines.shp
52 | ogropts:
53 | - "-explodecollections"
54 | archive:
55 | format: zip
56 | files:
57 | - antarctica-icesheet-outlines-3857/icesheet_outlines.cpg
58 | - antarctica-icesheet-outlines-3857/icesheet_outlines.dbf
59 | - antarctica-icesheet-outlines-3857/icesheet_outlines.prj
60 | - antarctica-icesheet-outlines-3857/icesheet_outlines.shp
61 | - antarctica-icesheet-outlines-3857/icesheet_outlines.shx
62 |
63 | ne_110m_admin_0_boundary_lines_land:
64 | type: shp
65 | url: https://naturalearth.s3.amazonaws.com/110m_cultural/ne_110m_admin_0_boundary_lines_land.zip
66 | file: ne_110m_admin_0_boundary_lines_land.shp
67 | ogropts: &ne_opts
68 | - "--config"
69 | - "SHAPE_ENCODING"
70 | - "WINDOWS-1252"
71 | - "-explodecollections"
72 | # needs reprojecting
73 | - '-t_srs'
74 | - 'EPSG:3857'
75 | archive:
76 | format: zip
77 | files:
78 | - ne_110m_admin_0_boundary_lines_land.dbf
79 | - ne_110m_admin_0_boundary_lines_land.prj
80 | - ne_110m_admin_0_boundary_lines_land.shp
81 | - ne_110m_admin_0_boundary_lines_land.shx
82 |
--------------------------------------------------------------------------------
/serve.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import http.server
3 |
4 | class CachelessHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
5 | def end_headers(self):
6 | self.send_header("Cache-Control", "no-cache, no-store, must-revalidate")
7 | self.send_header("Pragma", "no-cache")
8 | self.send_header("Expires", "0")
9 | self.send_header("Access-Control-Allow-Origin", "*")
10 | http.server.SimpleHTTPRequestHandler.end_headers(self)
11 |
12 |
13 | if __name__ == '__main__':
14 | http.server.test(HandlerClass=CachelessHTTPRequestHandler, port=8081, bind="127.0.0.1")
15 |
--------------------------------------------------------------------------------
/shortbread.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Shortbread theme
4 | --
5 | -- Configuration for the osm2pgsql Themepark framework
6 | --
7 | -- ---------------------------------------------------------------------------
8 |
9 | local themepark = require('themepark')
10 |
11 | themepark.debug = false
12 |
13 | script_path = debug.getinfo(1, "S").source:sub(2):match("(.*/)")
14 | if script_path ~= nil then
15 | -- osm2pgsql was told to load foo/shortbread.lua, so we need to add foo to the path
16 | package.path = script_path.."?.lua;"..package.path
17 | end
18 |
19 | -- Tell themepark where the themes are
20 | themepark:add_theme_dir('themes')
21 |
22 | themepark:add_topic('core/name-with-fallback', {
23 | keys = {
24 | name = { 'name', 'name:en', 'name:de' },
25 | name_de = { 'name:de', 'name', 'name:en' },
26 | name_en = { 'name:en', 'name', 'name:de' },
27 | }
28 | })
29 |
30 | -- --------------------------------------------------------------------------
31 |
32 | themepark:add_topic('spirit/buildings')
33 | themepark:add_topic('spirit/roads')
34 | themepark:add_topic('spirit/railway')
35 | themepark:add_topic('spirit/aeroway')
36 |
37 | themepark:add_topic('core/layer')
38 |
39 | themepark:add_topic('shortbread/aerialways')
40 | themepark:add_topic('shortbread/boundary_labels')
41 | themepark:add_topic('shortbread/bridges')
42 | themepark:add_topic('shortbread/dams')
43 | themepark:add_topic('shortbread/ferries')
44 | themepark:add_topic('shortbread/land')
45 | themepark:add_topic('shortbread/piers')
46 | themepark:add_topic('shortbread/places')
47 | themepark:add_topic('shortbread/public_transport')
48 | themepark:add_topic('shortbread/sites')
49 | themepark:add_topic('shortbread/water')
50 |
51 | themepark:add_topic('shortbread/pois')
52 | -- Must be after "pois" layer, because as per Shortbread spec addresses that
53 | -- are already in "pois" should not be in the "addresses" layer.
54 | themepark:add_topic('shortbread/addresses')
55 | themepark:add_topic('shortbread/boundaries')
56 | themepark:add_topic('shortbread/streets')
57 |
58 | -- ---------------------------------------------------------------------------
59 |
--------------------------------------------------------------------------------
/shortbread/boundaries.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | admin_level,
4 | maritime,
5 | disputed
6 | FROM boundaries
7 | WHERE geom && {{bbox}}
8 | {% if zoom < 7 %}
9 | AND admin_level = 2
10 | {% endif %}
11 |
--------------------------------------------------------------------------------
/shortbread/boundary_labels.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | name,
4 | name_de,
5 | name_en,
6 | admin_level,
7 | way_area
8 | FROM boundary_labels
9 | WHERE geom && {{bbox}}
10 | AND {{zoom}} >= minzoom
11 | ORDER BY way_area DESC
12 |
--------------------------------------------------------------------------------
/shortbread/buildings.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_Collect(merged.geom) AS way
3 | {#
4 | The order of merging is important. If you ST_AsMVTGeom(ST_Collect) you can effectively get a
5 | ST_Union in some cases. To avoid this, we first transform to the VT coords, then collect. But
6 | because a geom might be a multipolygon or st_asmvtgeom could turn a polygon into a MP (e.g.
7 | way 391482237), we have to ST_Dump the geoms to turn multis into polys. Unfortunately, we can't
8 | just ST_Collect(ST_Dump(...)) because you can't mix an aggregate and set-returning function like
9 | this. So we have to do a LATERAL join.
10 | #}
11 | FROM buildings,
12 | LATERAL ST_Dump(ST_AsMVTGeom(geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}})) AS merged
13 | WHERE buildings.geom && {{bbox}}
14 | AND way_area > {{coordinate_area}}
15 |
--------------------------------------------------------------------------------
/shortbread/dam_lines.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | kind
4 | FROM dam_lines
5 | WHERE geom && {{bbox}}
6 |
--------------------------------------------------------------------------------
/shortbread/dam_polygons.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | kind
4 | FROM dam_polygons
5 | WHERE geom && {{bbox}}
6 |
--------------------------------------------------------------------------------
/shortbread/land.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(ST_SimplifyPreserveTopology(ST_Collect(geom), {{ coordinate_length }}), {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | kind
4 | FROM land
5 | WHERE geom && {{bbox}}
6 | AND {{zoom}} >= minzoom
7 | AND ST_Area(geom) > 4*{{ coordinate_area }}
8 | GROUP BY kind
9 |
--------------------------------------------------------------------------------
/shortbread/ocean.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(ST_SimplifyPreserveTopology(ST_Union(way), {{ coordinate_length }}), {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way
3 | {% if zoom <= 8 %}
4 | FROM simplified_water_polygons
5 | {% else %}
6 | FROM water_polygons
7 | {% endif %}
8 | WHERE way && {{bbox}}
9 |
--------------------------------------------------------------------------------
/shortbread/public_transport.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | name,
4 | name_de,
5 | name_en,
6 | kind
7 | FROM public_transport
8 | WHERE geom && {{bbox}}
9 | AND {{zoom}} >= minzoom
10 |
--------------------------------------------------------------------------------
/shortbread/sites.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | kind
4 | FROM sites
5 | WHERE geom && {{bbox}}
6 |
--------------------------------------------------------------------------------
/shortbread/street_labels.sql.jinja2:
--------------------------------------------------------------------------------
1 | WITH road AS (
2 | SELECT
3 | geom,
4 | highway AS kind,
5 | name,
6 | ref,
7 | z_order
8 | FROM roads
9 | WHERE geom && {{bbox}}
10 | AND (ref IS NOT NULL OR name IS NOT NULL)
11 | AND highway IN
12 | (
13 | {#
14 | Formatting this way is slightly awkward since the `,' only goes on lines with something following.
15 | Better would be to template this with Jinja somehow
16 | #}
17 | 'motorway'
18 | {% if zoom >=12 %}, 'trunk', 'primary'{% endif %}
19 | {% if zoom >=13 %}, 'motorway_link', 'trunk_link', 'primary_link', 'secondary', 'secondary_link', 'tertiary'{% endif %}
20 | {% if zoom >=14 %}, 'tertiary_link', 'unclassified', 'residential', 'living_street', 'busway', 'bus_guideway',
21 | 'service', 'pedestrian', 'track', 'footway', 'steps', 'path', 'cycleway'{% endif %}
22 | )
23 | ),
24 | rail AS (
25 | SELECT
26 | geom,
27 | railway AS kind,
28 | NULL AS name, -- TODO: add railway names
29 | ref,
30 | z_order
31 | FROM railways
32 | WHERE geom && {{bbox}}
33 | AND (ref IS NOT NULL OR name IS NOT NULL)
34 | AND railway IN ('rail', 'narrow_gauge', 'tram', 'light_rail', 'funicular', 'subway', 'monorail')
35 | ),
36 | {% if zoom >=11 %}
37 | aeroway AS (
38 | SELECT
39 | geom,
40 | aeroway AS kind,
41 | NULL AS name,
42 | ref,
43 | CASE WHEN aeroway = 'runway' THEN 510 ELSE 500 END AS z_order
44 | FROM aeroways
45 | WHERE geom && {{bbox}}
46 | AND (ref IS NOT NULL)
47 | AND aeroway IN ('runway'{% if zoom >=13 %}, 'taxiway'{% endif %})
48 | ),
49 | {% endif %}
50 | {% set columns = 'kind, name, ref' %}
51 | all_geoms AS (
52 | SELECT
53 | geom, {{columns}}, z_order
54 | FROM road
55 | UNION ALL
56 | SELECT
57 | geom, {{columns}}, z_order
58 | FROM rail
59 | {% if zoom >=11 %}
60 | UNION ALL
61 | SELECT
62 | geom, {{columns}}, z_order
63 | FROM aeroway
64 | {% endif %}
65 | ),
66 | merged AS (
67 | SELECT
68 | ST_LineMerge(ST_Collect(geom)) AS geom,
69 | kind,
70 | name,
71 | string_to_array(ref, ';') AS refs,
72 | z_order
73 | FROM all_geoms
74 | GROUP BY {{columns}}, z_order
75 | )
76 | SELECT
77 | ST_AsMVTGeom(ST_Simplify((ST_Dump(geom)).geom, {{ coordinate_length }}), {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
78 | kind,
79 | name,
80 | array_to_string(refs, E'\n') AS ref,
81 | array_length(refs,1) AS ref_rows,
82 | (SELECT MAX(char_length(ref)) FROM unnest(refs) AS u(ref)) AS ref_cols
83 | FROM merged
84 | ORDER BY z_order DESC
85 |
--------------------------------------------------------------------------------
/shortbread/street_labels_points.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | name,
4 | name_de,
5 | name_en,
6 | kind,
7 | ref
8 | FROM street_labels_points
9 | WHERE geom && {{bbox}}
10 |
--------------------------------------------------------------------------------
/shortbread/street_polygons.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | kind,
4 | rail,
5 | tunnel,
6 | bridge,
7 | surface
8 | FROM street_polygons
9 | WHERE geom && {{bbox}}
10 | {% if zoom < 13 %}
11 | AND kind IN ('runway')
12 | {% elif zoom < 14 %}
13 | AND kind IN ('runway', 'taxiway')
14 | {% else %}
15 | AND kind IN ('runway', 'taxiway', 'service', 'pedestrian')
16 | {% endif %}
17 | ORDER BY z_order DESC
18 |
--------------------------------------------------------------------------------
/shortbread/streets.sql.jinja2:
--------------------------------------------------------------------------------
1 | WITH road AS (
2 | SELECT
3 | CASE WHEN oneway = '-1' THEN ST_Reverse(geom) ELSE geom END AS geom,
4 | CASE highway
5 | WHEN 'motorway_link' THEN 'motorway'
6 | WHEN 'trunk_link' THEN 'trunk'
7 | WHEN 'primary_link' THEN 'primary'
8 | WHEN 'secondary_link' THEN 'secondary'
9 | WHEN 'tertiary_link' THEN 'tertiary'
10 | ELSE highway
11 | END AS kind,
12 | CASE WHEN highway IN ('motorway_link', 'trunk_link', 'primary_link', 'secondary_link', 'tertiary_link') THEN true END AS link,
13 | false AS rail,
14 | coalesce(tunnel, false) AS tunnel,
15 | coalesce(bridge, false) AS bridge,
16 | coalesce(oneway IN ('yes', '1', 'true', '-1'), false) AS oneway,
17 | false AS oneway_reverse,
18 | tracktype,
19 | surface,
20 | service,
21 | bicycle,
22 | horse,
23 | z_order
24 | FROM roads
25 | WHERE geom && {{bbox}}
26 | AND highway IN
27 | (
28 | {#
29 | Formatting this way is slightly awkward since the `,' only goes on lines with something following.
30 | Better would be to template this with Jinja somehow
31 | #}
32 | 'motorway', 'motorway_link'
33 | {% if zoom >=6 %}, 'trunk', 'trunk_link'{% endif %}
34 | {% if zoom >=8 %}, 'primary', 'primary_link'{% endif %}
35 | {% if zoom >=9 %}, 'secondary', 'secondary_link'{% endif %}
36 | {% if zoom >=10 %}, 'tertiary', 'tertiary_link'{% endif %}
37 | {% if zoom >=12 %}, 'unclassified', 'residential', 'busway', 'bus_guideway'{% endif %}
38 | {% if zoom >= 13 %}, 'living_street', 'service', 'pedestrian', 'track', 'footway', 'steps', 'path', 'cycleway'{% endif %}
39 | )
40 | ),
41 | {% if zoom >=8 %}
42 | rail AS (
43 | SELECT
44 | geom,
45 | railway AS kind,
46 | false AS link,
47 | true AS rail,
48 | coalesce(tunnel, false) AS tunnel,
49 | coalesce(bridge, false) AS bridge,
50 | false AS oneway,
51 | false AS oneway_reverse,
52 | NULL as tracktype,
53 | service,
54 | NULL AS bicycle,
55 | NULL AS horse,
56 | z_order
57 | FROM railways
58 | WHERE geom && {{bbox}}
59 | AND railway IN
60 | (
61 | 'rail', 'narrow_gauge'
62 | {% if zoom >=10 %}, 'tram', 'light_rail', 'funicular', 'subway', 'monorail'{% endif %}
63 | )
64 | ),
65 | {% endif %}
66 | {% if zoom >=11 %}
67 | aeroway AS (
68 | SELECT
69 | geom,
70 | aeroway AS kind,
71 | false AS link,
72 | true AS rail,
73 | false AS tunnel,
74 | false AS bridge,
75 | false AS oneway,
76 | false AS oneway_reverse,
77 | NULL as tracktype,
78 | NULL AS service,
79 | NULL AS bicycle,
80 | NULL AS horse,
81 | CASE WHEN aeroway = 'runway' THEN 510 ELSE 500 END AS z_order
82 | FROM aeroways
83 | WHERE geom && {{bbox}}
84 | AND aeroway IN ('runway'{% if zoom >=11 %}, 'taxiway'{% endif %})
85 | ),
86 | {% endif %}
87 | {% if zoom >= 14 %}
88 | {% set columns = 'kind, link, rail, tunnel, bridge, oneway, oneway_reverse, tracktype, service' %}
89 | {% elif zoom >= 11 %}
90 | {% set columns = 'kind, link, rail, tunnel, bridge, tracktype, service, bicycle, horse' %}
91 | {% else %} {# zoom >= 5 #}
92 | {% set columns = 'kind, rail' %}
93 | {% endif %}
94 |
95 | all_geoms AS (
96 | SELECT
97 | geom, {{columns}}, z_order
98 | FROM road
99 | {% if zoom >=8 %}
100 | UNION ALL
101 | SELECT
102 | geom, {{columns}}, z_order
103 | FROM rail
104 | {% endif %}
105 | {% if zoom >=11 %}
106 | UNION ALL
107 | SELECT
108 | geom, {{columns}}, z_order
109 | FROM aeroway
110 | {% endif %}
111 | ),
112 | merged AS (
113 | SELECT
114 | {% if zoom >= 14 %}
115 | ST_LineMerge(ST_Collect(geom), oneway IS NOT DISTINCT FROM true) AS geom,
116 | {% else %}
117 | ST_LineMerge(ST_Collect(geom)) AS geom,
118 | {% endif %}
119 | {{columns}}, z_order
120 | FROM all_geoms
121 | GROUP BY {{columns}}, z_order
122 | )
123 | SELECT
124 | ST_AsMVTGeom(ST_Simplify((ST_Dump(geom)).geom, {{ coordinate_length }}), {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
125 | {{columns}}
126 | FROM merged
127 | ORDER BY z_order DESC
128 |
--------------------------------------------------------------------------------
/shortbread/streets_polygons_labels.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | name,
4 | name_de,
5 | name_en,
6 | kind
7 | FROM streets_polygons_labels
8 | WHERE geom && {{bbox}}
9 |
--------------------------------------------------------------------------------
/shortbread/water_polygons.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | kind,
4 | way_area
5 | FROM water_areas
6 | WHERE geom && {{bbox}}
7 | AND ST_Area(geom) > 4*{{ coordinate_area }}
8 |
--------------------------------------------------------------------------------
/shortbread/water_polygons_labels.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | name,
4 | name_de,
5 | name_en,
6 | kind,
7 | way_area
8 | FROM water_area_labels
9 | WHERE geom && {{bbox}} ORDER BY way_area desc
10 | {% if zoom < 14 %}
11 | LIMIT 256
12 | {% endif %}
13 |
--------------------------------------------------------------------------------
/shortbread_original/addresses.14-14.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | housename,
4 | housenumber
5 | FROM addresses
6 | WHERE geom && {{bbox}}
7 |
--------------------------------------------------------------------------------
/shortbread_original/aerialways.12-14.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | kind
4 | FROM aerialways
5 | WHERE geom && {{bbox}}
6 |
--------------------------------------------------------------------------------
/shortbread_original/bridges.12-14.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | kind
4 | FROM bridges
5 | WHERE geom && {{bbox}}
6 |
--------------------------------------------------------------------------------
/shortbread_original/ferries.10-14.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | name,
4 | name_de,
5 | name_en,
6 | kind
7 | FROM ferries
8 | WHERE geom && {{bbox}}
9 | AND {{zoom}} >= minzoom
10 |
--------------------------------------------------------------------------------
/shortbread_original/pier_lines.12-14.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | kind
4 | FROM pier_lines
5 | WHERE geom && {{bbox}}
6 |
--------------------------------------------------------------------------------
/shortbread_original/pier_polygons.12-14.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | kind
4 | FROM pier_polygons
5 | WHERE geom && {{bbox}}
6 |
--------------------------------------------------------------------------------
/shortbread_original/place_labels.04-14.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | name,
4 | name_de,
5 | name_en,
6 | kind,
7 | population
8 | FROM place_labels
9 | WHERE geom && {{bbox}}
10 | AND {{zoom}} >= minzoom
11 | ORDER BY population desc
12 |
--------------------------------------------------------------------------------
/shortbread_original/pois.14-14.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | name,
4 | name_de,
5 | name_en,
6 | amenity,
7 | leisure,
8 | tourism,
9 | shop,
10 | man_made,
11 | historic,
12 | emergency,
13 | highway,
14 | office,
15 | housename,
16 | housenumber,
17 | cuisine,
18 | sport,
19 | vending,
20 | information,
21 | "tower:type",
22 | religion,
23 | denomination,
24 | "recycling:glass_bottles",
25 | "recycling:paper",
26 | "recycling:clothes",
27 | "recycling:scrap_metal",
28 | atm
29 | FROM pois
30 | WHERE geom && {{bbox}}
31 |
--------------------------------------------------------------------------------
/shortbread_original/water_lines.09-14.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | kind,
4 | tunnel,
5 | bridge,
6 | layer
7 | FROM water_lines
8 | WHERE geom && {{bbox}}
9 | AND {{zoom}} >= minzoom
10 | ORDER BY layer ASC
11 |
--------------------------------------------------------------------------------
/shortbread_original/water_lines_labels.12-14.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT ST_AsMVTGeom(geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
2 | name,
3 | name_de,
4 | name_en,
5 | kind,
6 | minzoom
7 | FROM water_lines_labels
8 | WHERE geom && {{bbox}}
9 |
--------------------------------------------------------------------------------
/spirit.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Street Spirit theme
4 | --
5 | -- Configuration for the osm2pgsql Themepark framework
6 | --
7 | -- ---------------------------------------------------------------------------
8 |
9 | local themepark = require('themepark')
10 |
11 | themepark.debug = false
12 |
13 | script_path = debug.getinfo(1, "S").source:sub(2):match("(.*/)")
14 | if script_path ~= nil then
15 | -- osm2pgsql was told to load foo/spirit.lua, so we need to add foo to the path
16 | package.path = script_path.."?.lua;"..package.path
17 | end
18 |
19 | -- Tell themepark where the themes are
20 | themepark:add_theme_dir('themes')
21 |
22 | themepark:add_topic('spirit/buildings')
23 | themepark:add_topic('spirit/water')
24 | themepark:add_topic('spirit/education')
25 | themepark:add_topic('spirit/food')
26 | themepark:add_topic('spirit/leisure')
27 | themepark:add_topic('spirit/vegetation')
28 | themepark:add_topic('spirit/landuse')
29 | themepark:add_topic('spirit/roads')
30 | themepark:add_topic('spirit/railway')
31 | themepark:add_topic('spirit/transit')
32 | themepark:add_topic('spirit/aeroway')
33 | themepark:add_topic('spirit/places')
34 | themepark:add_topic('spirit/admin')
35 |
--------------------------------------------------------------------------------
/spirit.yaml:
--------------------------------------------------------------------------------
1 | metadata:
2 | id: v1
3 | name: Street Spirit
4 | attribution: © OpenStreetMap contributors
5 | version: 0.0.1
6 | center: [0, 0, 4]
7 | vector_layers:
8 | admin:
9 | description: Administrative boundaries
10 | sql:
11 | - minzoom: 4
12 | maxzoom: 14
13 | buffer: 16
14 | extent: 1024
15 | file: spirit/admin.sql.jinja2
16 | - minzoom: 15
17 | maxzoom: 15
18 | buffer: 16
19 | extent: 4096
20 | file: spirit/admin.sql.jinja2
21 | admin-names:
22 | description: Administrative area names
23 | sql:
24 | - minzoom: 0
25 | maxzoom: 14
26 | buffer: 16
27 | extent: 512
28 | file: spirit/admin-names.sql.jinja2
29 |
30 | buildings:
31 | description: Building polygons
32 | sql:
33 | - minzoom: 13
34 | maxzoom: 14
35 | extent: 1024
36 | buffer: 2
37 | file: spirit/buildings.sql.jinja2
38 | - minzoom: 15
39 | maxzoom: 15
40 | extent: 4096
41 | buffer: 8
42 | file: spirit/buildings.sql.jinja2
43 | building-names:
44 | description: Building names
45 | sql:
46 | - minzoom: 15
47 | maxzoom: 15
48 | extent: 2048
49 | buffer: 8
50 | file: spirit/building-names.sql.jinja2
51 | water:
52 | description: Ocean and water polygons
53 | sql:
54 | - minzoom: 0
55 | maxzoom: 14
56 | extent: 1024
57 | file: spirit/water.sql.jinja2
58 | - minzoom: 15
59 | maxzoom: 15
60 | extent: 4096
61 | file: spirit/water.sql.jinja2
62 | water-lines:
63 | description: Water linestrings
64 | fields:
65 | waterway: Type of waterway
66 | name: Name of waterway
67 | sql:
68 | - minzoom: 8
69 | maxzoom: 14
70 | extent: 1024
71 | buffer: 2
72 | file: spirit/water-lines.sql.jinja2
73 | - minzoom: 15
74 | maxzoom: 15
75 | extent: 4096
76 | buffer: 8
77 | file: spirit/water-lines.sql.jinja2
78 | water-names:
79 | description: Names for water bodies
80 | fields:
81 | name: Name of water body
82 | sql:
83 | - minzoom: 8
84 | maxzoom: 14
85 | extent: 1024
86 | buffer: 2
87 | file: spirit/water-names.sql.jinja2
88 | - minzoom: 15
89 | maxzoom: 15
90 | extent: 4096
91 | buffer: 8
92 | file: spirit/water-names.sql.jinja2
93 | roads:
94 | description: Linear road features
95 | fields:
96 | highway: Type of highway
97 | sql:
98 | - minzoom: 10
99 | maxzoom: 14
100 | extent: 1024
101 | buffer: 4
102 | file: spirit/roads-high.sql.jinja2
103 | - minzoom: 15
104 | maxzoom: 15
105 | extent: 4096
106 | buffer: 16
107 | file: spirit/roads-high.sql.jinja2
108 | railways:
109 | description: Linear railway features
110 | fields:
111 | highway: Type of highway
112 | sql:
113 | - minzoom: 12
114 | maxzoom: 14
115 | extent: 1024
116 | buffer: 4
117 | file: spirit/railways-high.sql.jinja2
118 | - minzoom: 15
119 | maxzoom: 15
120 | extent: 4096
121 | buffer: 16
122 | file: spirit/railways-high.sql.jinja2
123 | aeroways:
124 | description: Runways and other aeroways
125 | fields: {}
126 | sql:
127 | - minzoom: 10
128 | maxzoom: 14
129 | extent: 1024
130 | buffer: 8
131 | file: spirit/aeroways.sql.jinja2
132 | - minzoom: 15
133 | maxzoom: 15
134 | extent: 4096
135 | buffer: 64 # Runways get wide at high zooms, so they need an extra-large buffer
136 | file: spirit/aeroways.sql.jinja2
137 | transit-points:
138 | description: Transit-oriented points
139 | fields:
140 | station: If the point is a station or stop
141 | mode: Mode of transportation
142 | sql:
143 | - minzoom: 12
144 | maxzoom: 14
145 | extent: 512
146 | buffer: 0
147 | file: spirit/transit-points.sql.jinja2
148 | - minzoom: 15
149 | maxzoom: 15
150 | extent: 2048
151 | buffer: 0
152 | file: spirit/transit-points.sql.jinja2
153 | landuse:
154 | description: Various types of landuse
155 | fields:
156 | landuse: Type of landuse
157 | sql:
158 | - minzoom: 10
159 | maxzoom: 14
160 | extent: 1024
161 | buffer: 4
162 | file: spirit/landuse.sql.jinja2
163 | - minzoom: 15
164 | maxzoom: 15
165 | extent: 4096
166 | buffer: 16
167 | file: spirit/landuse.sql.jinja2
168 | landuse-names:
169 | description: Various types of landuse
170 | fields:
171 | landuse: Type of landuse
172 | name: Name of landuse
173 | way_area: Area in square meters in web mercator
174 | sql:
175 | - minzoom: 10
176 | maxzoom: 14
177 | extent: 256
178 | buffer: 0
179 | file: spirit/landuse-names.sql.jinja2
180 | - minzoom: 15
181 | maxzoom: 15
182 | extent: 1024
183 | buffer: 0
184 | file: spirit/landuse-names.sql.jinja2
185 | education:
186 | description: Various types of educational areas
187 | fields:
188 | landuse: Type of landuse
189 | sql:
190 | - minzoom: 10
191 | maxzoom: 14
192 | extent: 1024
193 | buffer: 4
194 | file: spirit/education.sql.jinja2
195 | - minzoom: 15
196 | maxzoom: 15
197 | extent: 4096
198 | buffer: 16
199 | file: spirit/education.sql.jinja2
200 | education-names:
201 | description: Names of educational facilities
202 | fields:
203 | education: Type of landuse
204 | name: Name of education
205 | sql:
206 | - minzoom: 10
207 | maxzoom: 14
208 | extent: 256
209 | buffer: 0
210 | file: spirit/education-names.sql.jinja2
211 | - minzoom: 15
212 | maxzoom: 15
213 | extent: 1024
214 | buffer: 0
215 | file: spirit/education-names.sql.jinja2
216 | leisure:
217 | description: Parks and other leisure areas
218 | sql:
219 | - minzoom: 10
220 | maxzoom: 14
221 | extent: 1024
222 | buffer: 4
223 | file: spirit/leisure.sql.jinja2
224 | - minzoom: 15
225 | maxzoom: 15
226 | extent: 4096
227 | buffer: 16
228 | file: spirit/leisure.sql.jinja2
229 | leisure-names:
230 | description: Parks and other leisure area names
231 | fields:
232 | way_area: Area in square meters in web mercator
233 | sql:
234 | - minzoom: 10
235 | maxzoom: 14
236 | extent: 256
237 | buffer: 0
238 | file: spirit/leisure-names.sql.jinja2
239 | - minzoom: 15
240 | maxzoom: 15
241 | extent: 1024
242 | buffer: 0
243 | file: spirit/leisure-names.sql.jinja2
244 | settlements:
245 | description: Populated places
246 | fields: {}
247 | sql:
248 | sql:
249 | - minzoom: 8
250 | maxzoom: 14
251 | extent: 1024
252 | buffer: 4
253 | file: spirit/settlements.sql.jinja2
254 | - minzoom: 15
255 | maxzoom: 15
256 | extent: 4096
257 | buffer: 16
258 | file: spirit/settlements.sql.jinja2
259 | vegetation:
260 | description: vegetation areas
261 | fields:
262 | vegetation: Type of vegetation. One of wood, heath, scrub, wetland, or grass
263 | wetland: Wetland tag, if the object is a wetland
264 | sql:
265 | - minzoom: 10
266 | maxzoom: 14
267 | extent: 1024
268 | buffer: 4
269 | file: spirit/vegetation.sql.jinja2
270 | - minzoom: 15
271 | maxzoom: 15
272 | extent: 4096
273 | buffer: 16
274 | file: spirit/vegetation.sql.jinja2
275 | vegetation-names:
276 | description: vegetation names
277 | fields:
278 | vegetation: Type of vegetation. One of wood, heath, scrub, wetland, or grass
279 | wetland: Wetland tag, if the object is a wetland
280 | sql:
281 | - minzoom: 10
282 | maxzoom: 14
283 | extent: 256
284 | buffer: 4
285 | file: spirit/vegetation-names.sql.jinja2
286 | - minzoom: 15
287 | maxzoom: 15
288 | extent: 1024
289 | buffer: 16
290 | file: spirit/vegetation-names.sql.jinja2
291 | food:
292 | description: Food-related POIs
293 | fields:
294 | name: Name of food POI
295 | sql:
296 | - minzoom: 15
297 | maxzoom: 15
298 | extent: 1024
299 | buffer: 16
300 | file: spirit/food.sql.jinja2
301 |
--------------------------------------------------------------------------------
/spirit/admin-names.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | name,
4 | way_area
5 | FROM admin
6 | WHERE geom && {{bbox}}
7 | AND name IS NOT NULL
8 | AND way_area > 0.25 * {{tile_area}}
9 | {% if zoom <= 5 %}
10 | AND admin_level IN (2)
11 | {% elif zoom <= 8 %}
12 | AND admin_level IN (2, 3, 4)
13 | {% else %}
14 | AND admin_level IN (2, 3, 4, 5, 6)
15 | {% endif %}
16 | ORDER BY admin_level ASC, way_area DESC
17 |
--------------------------------------------------------------------------------
/spirit/admin.sql.jinja2:
--------------------------------------------------------------------------------
1 | WITH boundaries AS (
2 | SELECT
3 | geom,
4 | min_admin_level,
5 | multiple_relations
6 | FROM admin_lines
7 | WHERE geom && {{bbox}}
8 | {% if zoom >= 10 -%}
9 | AND min_admin_level IN (2, 3, 4, 5, 6, 7, 8)
10 | {% elif zoom >= 7 -%}
11 | AND min_admin_level IN (2, 3, 4, 5, 6)
12 | {% elif zoom >= 4 -%}
13 | AND min_admin_level IN (2, 3, 4) -- later filtered down more based on parent area
14 | {% else -%}
15 | AND min_admin_level = 2
16 | {% endif -%}
17 | ), adm2_boundaries AS (
18 | SELECT
19 | geom,
20 | min_admin_level,
21 | NOT multiple_relations AS in_other_country -- if it wasn't in multiple relations, it had to of met the subquery for being in 2+ countries
22 | FROM boundaries b
23 | WHERE min_admin_level = 2
24 | AND (multiple_relations -- avoid line in polygon checks where they're not necessary.
25 | OR (SELECT COUNT(*) FROM admin p
26 | WHERE p.admin_level = 2
27 | AND ST_Covers(p.area, b.geom)) >= 2)
28 | ), other_boundaries AS (
29 | SELECT
30 | geom,
31 | b.min_admin_level
32 | FROM boundaries b
33 | {% if zoom >= 6 -%}
34 | WHERE multiple_relations AND b.min_admin_level != 2
35 | {% else -%}
36 | WHERE multiple_relations AND b.min_admin_level NOT IN (2, 3, 4) -- 2 and 3,4 have special treatment
37 | OR (multiple_relations AND b.min_admin_level IN (3, 4) AND
38 | (SELECT true FROM admin p
39 | WHERE p.admin_level = 2
40 | AND p.way_area > 8e+12
41 | AND ST_Covers(p.area, b.geom)
42 | LIMIT 1))
43 | {% endif -%}
44 | ), unioned_boundaries AS (
45 | SELECT
46 | ST_LineMerge(ST_Collect(geom)) AS geom,
47 | min_admin_level,
48 | in_other_country
49 | FROM adm2_boundaries a
50 | GROUP BY min_admin_level, in_other_country
51 | UNION ALL
52 | SELECT
53 | ST_LineMerge(ST_Collect(geom)) AS geom,
54 | min_admin_level,
55 | NULL
56 | FROM other_boundaries
57 | GROUP BY min_admin_level
58 | )
59 | SELECT
60 | {% if zoom >= 15 -%}
61 | ST_AsMVTGeom(geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
62 | {% else -%}
63 | ST_AsMVTGeom(ST_Simplify(geom, {{tile_length}}/750), {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
64 | {% endif -%}
65 | min_admin_level AS admin_level,
66 | in_other_country
67 | FROM unioned_boundaries
68 |
--------------------------------------------------------------------------------
/spirit/aeroways.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom((ST_Dump(ST_LineMerge(ST_Collect(geom)))).geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | {% if zoom <= 12%}
4 | NULL as ref,-- No ref rendering on low zooms
5 | {% elif zoom <= 13 %}
6 | CASE WHEN aeroway = 'runway' THEN ref END AS ref,
7 | {% else %}
8 | ref,
9 | {% endif %}
10 | aeroway
11 | FROM aeroways
12 | WHERE geom && {{bbox}}
13 | GROUP BY aeroway, ref
14 |
--------------------------------------------------------------------------------
/spirit/building-names.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(point, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | name,
4 | way_area
5 | FROM buildings
6 | WHERE point && {{bbox}}
7 | AND way_area > {{coordinate_area}}*16
8 | ORDER BY way_area DESC
9 |
--------------------------------------------------------------------------------
/spirit/buildings.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_Collect(merged.geom) AS way
3 | {#
4 | The order of merging is important. If you ST_AsMVTGeom(ST_Collect) you can effectively get a
5 | ST_Union in some cases. To avoid this, we first transform to the VT coords, then collect. But
6 | because a geom might be a multipolygon or st_asmvtgeom could turn a polygon into a MP (e.g.
7 | way 391482237), we have to ST_Dump the geoms to turn multis into polys. Unfortunately, we can't
8 | just ST_Collect(ST_Dump(...)) because you can't mix an aggregate and set-returning function like
9 | this. So we have to do a LATERAL join.
10 | #}
11 | FROM buildings,
12 | LATERAL ST_Dump(ST_AsMVTGeom(geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}})) AS merged
13 | WHERE buildings.geom && {{bbox}}
14 | AND way_area > {{coordinate_area}}
15 |
--------------------------------------------------------------------------------
/spirit/education-names.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(point, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | name,
4 | education,
5 | way_area
6 | FROM education
7 | WHERE point && {{bbox}} AND name IS NOT NULL
8 |
--------------------------------------------------------------------------------
/spirit/education.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(ST_Collect(geom), {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | education
4 | FROM education
5 | WHERE geom && {{bbox}}
6 | GROUP BY education
7 |
--------------------------------------------------------------------------------
/spirit/food.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(point, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | name,
4 | food
5 | FROM food
6 | WHERE point && {{bbox}}
7 |
--------------------------------------------------------------------------------
/spirit/landuse-names.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(point, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | name,
4 | landuse,
5 | way_area
6 | FROM landuse
7 | WHERE point && {{bbox}}
8 | AND name IS NOT NULL
9 |
--------------------------------------------------------------------------------
/spirit/landuse.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(ST_Collect(geom), {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | landuse
4 | FROM landuse
5 | WHERE geom && {{bbox}}
6 | GROUP BY landuse
7 |
--------------------------------------------------------------------------------
/spirit/leisure-names.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(point, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | name,
4 | way_area
5 | FROM leisure
6 | WHERE point && {{bbox}}
7 | AND name IS NOT NULL
8 | AND leisure = 'park'
9 |
--------------------------------------------------------------------------------
/spirit/leisure.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(ST_Collect(geom), {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | leisure
4 | FROM leisure
5 | WHERE geom && {{bbox}}
6 | AND (
7 | {% if zoom <= 14 %}
8 | leisure IN ('park', 'stadium')
9 | {% else %}
10 | leisure IN ('park', 'stadium', 'playground')
11 | {% endif %}
12 | )
13 | GROUP BY leisure
14 |
--------------------------------------------------------------------------------
/spirit/railways-high.sql.jinja2:
--------------------------------------------------------------------------------
1 | WITH merged_rail AS (
2 | SELECT
3 | ST_LineMerge(ST_Collect(geom)) AS way,
4 | railway,
5 | COALESCE(minor, FALSE) AS minor,
6 | COALESCE(tunnel, FALSE) AS tunnel,
7 | COALESCE(bridge, FALSE) AS bridge,
8 | COALESCE(layer, 0) AS layer,
9 | z_order
10 | FROM railways
11 | WHERE geom && {{bbox}}
12 | GROUP BY railway, minor, tunnel, bridge, layer, z_order
13 | )
14 | SELECT
15 | ST_AsMVTGeom((ST_Dump(way)).geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
16 | railway, minor, tunnel, bridge, layer, z_order
17 | FROM merged_rail
18 | ORDER BY layer, z_order
19 |
--------------------------------------------------------------------------------
/spirit/roads-high.sql.jinja2:
--------------------------------------------------------------------------------
1 | WITH all_roads AS (
2 | SELECT
3 | CASE WHEN oneway = '-1' THEN ST_Reverse(geom) ELSE geom END AS geom,
4 | name,
5 | ref,
6 | CASE -- normalize highway values
7 | WHEN highway IN ('motorway', 'motorway_link') THEN 'motorway'
8 | WHEN highway IN ('trunk', 'trunk_link') THEN 'trunk'
9 | WHEN highway IN ('primary', 'primary_link') THEN 'primary'
10 | WHEN highway IN ('secondary', 'secondary_link') THEN 'secondary'
11 | WHEN highway IN ('tertiary', 'tertiary_link') THEN 'tertiary'
12 | WHEN highway IN ('unclassified', 'residential', 'living_street', 'service', 'bridleway', 'footway', 'cycleway', 'path') THEN highway
13 | END as highway,
14 | CASE WHEN highway IN ('motorway_link', 'trunk_link', 'primary_link', 'secondary_link', 'tertiary_link') THEN true END AS link,
15 | minor,
16 | CASE
17 | WHEN oneway = '-1' THEN 'yes'
18 | WHEN oneway = 'no' THEN NULL
19 | ELSE oneway END AS oneway,
20 | tunnel,
21 | bridge,
22 | layer,
23 | (SELECT ref FROM road_routes
24 | WHERE roads.way_id = road_routes.member_id
25 | ORDER BY cardinality(string_to_array(network, ':')) ASC, char_length(network) ASC, network, road_routes.relation_id
26 | LIMIT 1) AS route_ref,
27 | z_order
28 | FROM roads
29 | WHERE geom && {{bbox}}
30 | {% if zoom <= 10 %}
31 | AND highway IN ('motorway', 'motorway_link', 'trunk', 'trunk_link', 'primary', 'primary_link',
32 | 'secondary', 'secondary_link', 'tertiary', 'tertiary_link')
33 | {% elif zoom <= 13 %}
34 | AND highway IN ('motorway', 'motorway_link', 'trunk', 'trunk_link', 'primary', 'primary_link',
35 | 'secondary', 'secondary_link', 'tertiary', 'tertiary_link', 'unclassified', 'residential',
36 | 'living_street')
37 | {% else %}
38 | AND highway IN ('motorway', 'motorway_link', 'trunk', 'trunk_link', 'primary', 'primary_link',
39 | 'secondary', 'secondary_link', 'tertiary', 'tertiary_link', 'unclassified', 'residential',
40 | 'living_street', 'service', 'bridleway', 'footway', 'cycleway', 'path')
41 | {% endif %}
42 | ), reffed_roads AS (
43 | SELECT
44 | geom,
45 | {# Omitting name and ref from small roads allows for better merging and smaller tiles #}
46 | {% if zoom <= 11 %}
47 | CASE WHEN highway IN ('motorway', 'trunk', 'primary', 'secondary') THEN name END AS name,
48 | CASE WHEN highway IN ('motorway', 'trunk', 'primary', 'secondary') THEN COALESCE(route_ref, ref) END AS ref,
49 | {% elif zoom <= 12 %}
50 | CASE WHEN highway IN ('motorway', 'trunk', 'primary', 'secondary', 'tertiary') THEN name END AS name,
51 | CASE WHEN highway IN ('motorway', 'trunk', 'primary', 'secondary', 'tertiary') THEN COALESCE(route_ref, ref) END AS ref,
52 | {% else %}
53 | name,
54 | COALESCE(route_ref, ref) AS ref,
55 | {% endif %}
56 | highway,
57 | link,
58 | minor,
59 | oneway,
60 | tunnel,
61 | bridge,
62 | layer,
63 | z_order
64 | FROM all_roads
65 | ), oneway_roads AS (
66 | SELECT
67 | geom, -- linemerge with geos 3.11+ when we require it
68 | name,
69 | highway,
70 | link,
71 | minor,
72 | ref,
73 | oneway,
74 | tunnel,
75 | bridge,
76 | layer,
77 | z_order
78 | FROM reffed_roads
79 | WHERE oneway = 'yes'
80 | ), other_roads AS (
81 | SELECT
82 | ST_LineMerge(ST_Collect(geom)) AS geom,
83 | name,
84 | highway,
85 | link,
86 | minor,
87 | ref,
88 | oneway,
89 | tunnel,
90 | bridge,
91 | layer,
92 | z_order
93 | FROM reffed_roads
94 | WHERE oneway IS DISTINCT FROM 'yes'
95 | GROUP BY name, highway, link, minor, ref, oneway, tunnel, bridge, layer, z_order
96 | ), grouped_roads AS (
97 | SELECT
98 | geom, name, highway, link, minor, ref, oneway, tunnel, bridge, layer, z_order
99 | FROM oneway_roads
100 | UNION ALL
101 | SELECT
102 | (ST_Dump(geom)).geom AS way, name, highway, link, minor, ref, oneway, tunnel, bridge, layer, z_order
103 | FROM other_roads
104 | )
105 | SELECT
106 | ST_AsMVTGeom(geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
107 | name, highway, link, minor, ref, oneway, tunnel, bridge, layer, z_order
108 | FROM grouped_roads
109 | ORDER BY z_order
110 |
--------------------------------------------------------------------------------
/spirit/settlements.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | name,
4 | place
5 | FROM settlements
6 | WHERE geom && {{bbox}}
7 | AND name IS NOT NULL
8 | {% if zoom <= 9 %}
9 | AND place IN ('city')
10 | {% elif zoom <= 10 %}
11 | AND place IN ('city', 'town')
12 | {% elif zoom <= 11 %}
13 | AND place IN ('city', 'town', 'village')
14 | {% elif zoom <= 12 %}
15 | AND place IN ('city', 'town', 'village', 'hamlet')
16 | {% else %}
17 | AND place IN ('city', 'town', 'village', 'hamlet', 'isolated_dwelling')
18 | {% endif %}
19 |
--------------------------------------------------------------------------------
/spirit/transit-points.sql.jinja2:
--------------------------------------------------------------------------------
1 | WITH transit_normalized AS (
2 | SELECT
3 | geom,
4 | name,
5 | station,
6 | mode
7 | FROM transit
8 | WHERE geom && {{bbox}}
9 | AND (
10 | {% if zoom >= 15 %}
11 | mode = 'bus' OR
12 | {% elif zoom >= 13 %}
13 | (mode = 'bus' AND station) OR
14 | {% endif %}
15 | {% if zoom >= 14 %}
16 | (mode = 'tram' AND NOT station) OR
17 | {% endif %}
18 | mode = 'subway' OR mode = 'airplane')
19 | )
20 | SELECT
21 | ST_AsMVTGeom(geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
22 | name,
23 | station,
24 | mode
25 | FROM transit_normalized
26 | ORDER BY station DESC,
27 | CASE mode
28 | WHEN 'airplane' THEN 1
29 | WHEN 'subway' THEN 3
30 | WHEN 'tram' THEN 4
31 | WHEN 'bus' THEN 5
32 | ELSE 10
33 | END
34 |
--------------------------------------------------------------------------------
/spirit/vegetation-names.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(geom, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | name,
4 | vegetation,
5 | wetland,
6 | way_area
7 | FROM vegetation
8 | WHERE point && {{bbox}}
9 |
--------------------------------------------------------------------------------
/spirit/vegetation.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(ST_Collect(geom), {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | vegetation
4 | FROM vegetation
5 | WHERE geom && {{bbox}}
6 | GROUP BY vegetation
7 |
--------------------------------------------------------------------------------
/spirit/water-lines.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(ST_RemoveRepeatedPoints(ST_LineMerge(ST_Collect(geom)), 4*{{coordinate_length}}), {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | name,
4 | waterway
5 | FROM waterways
6 | WHERE geom && {{bbox}}
7 | {% if zoom <= 10 %}
8 | AND waterway = 'river'
9 | {% elif zoom <= 12 %}
10 | AND waterway IN ('river', 'canal')
11 | {% elif zoom <= 13 %}
12 | AND waterway IN ('river', 'canal', 'stream')
13 | {% else %}
14 | AND waterway IN ('river', 'canal', 'stream', 'drain', 'ditch')
15 | {% endif %}
16 | GROUP BY waterway, name
17 |
--------------------------------------------------------------------------------
/spirit/water-names.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(point, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way,
3 | name,
4 | way_area
5 | FROM water
6 | WHERE point && {{bbox}}
7 | AND way_area > 16 * {{coordinate_area}}
8 | ORDER BY way_area DESC
9 |
--------------------------------------------------------------------------------
/spirit/water.sql.jinja2:
--------------------------------------------------------------------------------
1 | SELECT
2 | ST_AsMVTGeom(way, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way
3 | {% if zoom <= 8 %}
4 | FROM simplified_water_polygons
5 | {% else %}
6 | FROM water_polygons
7 | {% endif %}
8 | WHERE way && {{bbox}}
9 | UNION ALL
10 | SELECT
11 | ST_AsMVTGeom(ST_Collect(geom), {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS way
12 | FROM water
13 | WHERE geom && {{bbox}}
14 | AND way_area > 16 * {{coordinate_area}}
15 |
--------------------------------------------------------------------------------
/sprites/airport.svg:
--------------------------------------------------------------------------------
1 |
6 |
8 |
11 |
12 |
13 |
17 |
18 |
--------------------------------------------------------------------------------
/sprites/bus_station.svg:
--------------------------------------------------------------------------------
1 |
6 |
8 |
11 |
12 |
13 |
17 |
18 |
--------------------------------------------------------------------------------
/sprites/bus_stop.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/sprites/food.svg:
--------------------------------------------------------------------------------
1 |
6 |
8 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/sprites/subway.svg:
--------------------------------------------------------------------------------
1 |
6 |
8 |
11 |
12 |
13 |
17 |
18 |
--------------------------------------------------------------------------------
/sprites/train.svg:
--------------------------------------------------------------------------------
1 |
6 |
8 |
11 |
12 |
13 |
17 |
18 |
--------------------------------------------------------------------------------
/sprites/wetland.md:
--------------------------------------------------------------------------------
1 |
2 | Wetland patterns are built from two separately generated pattern files by means of raster processing. The basic principle is shown by the following ImageMagick commands:
3 |
4 | ```
5 | convert -density 720 pattern.svg -morphology Erode Disk:5.3 \( +clone -fill black -draw 'color 0,0 floodfill' -negate \) +swap -morphology Erode Disk:10.3 -compose Darken -composite -scale 12.5% -depth 8 pattern_casing.png
6 |
7 | convert -depth 8 -density 90 wetland.svg wetland_tile.png
8 |
9 | montage wetland_tile.png wetland_tile.png wetland_tile.png wetland_tile.png -geometry 256x256+0+0 wetland_512.png
10 |
11 | convert wetland_512.png \( pattern_casing.png -negate \) -compose Lighten -composite -threshold 50% \( +clone -negate -morphology hitandmiss peaks:1.9 \) -compose Lighten -composite +level 20%,100% wetland_pattern_bkg.png
12 |
13 | convert -depth 8 -size 512x512 xc:"$SYMBOL" \( pattern.png -negate \) -set colorspace RGB -alpha Off -compose CopyOpacity -composite pattern_col.png
14 |
15 | convert -depth 8 -size 512x512 xc:"$WETLAND" \( wetland_pattern_bkg.png -negate \) -set colorspace RGB -alpha Off -compose CopyOpacity -composite +compose pattern_col.png -compose Over -composite wetland_pattern.png
16 | ```
17 |
18 | In some cases, which has not been elucidated (https://github.com/gravitystorm/openstreetmap-carto/pull/3051), the SVG conversions produce files with erroneous sizes. In this case, the following command sequence may work, by using Inkscape to rasterize the SVGs:
19 |
20 | ```
21 | inkscape -z --export-png=swamp.png --export-dpi=96 --export-background=white swamp.svg
22 |
23 | inkscape -z --export-png=swamp_hr.png --export-dpi=768 --export-background=white swamp.svg
24 |
25 | convert swamp_hr.png -morphology Erode Disk:5.3 \( +clone -fill black -draw 'color 0,0 floodfill' -negate \) +swap -morphology Erode Disk:10.3 -compose Darken -composite -scale 12.5% -depth 8 pattern_casing.png
26 |
27 | inkscape -z --export-png=wetland_tile.png --export-dpi=96 --export-background=white wetland.svg
28 |
29 | montage wetland_tile.png wetland_tile.png wetland_tile.png wetland_tile.png -geometry 256x256+0+0 wetland_512.png
30 |
31 | convert wetland_512.png \( pattern_casing.png -negate \) -compose Lighten -composite -threshold 50% \( +clone -negate -morphology hitandmiss peaks:1.9 \) -compose Lighten -composite +level 20%,100% wetland_pattern_bkg.png
32 |
33 | convert -depth 8 -size 512x512 xc:"#93b685" \( swamp.png -negate \) -set colorspace RGB -alpha Off -compose CopyOpacity -composite pattern_col.png
34 |
35 | convert -depth 8 -size 512x512 xc:"#4aa5fa" \( wetland_pattern_bkg.png -negate \) -set colorspace RGB -alpha Off -compose CopyOpacity -composite +compose pattern_col.png -compose Over -composite +gamma - -strip wetland_pattern.png
36 | ```
37 |
--------------------------------------------------------------------------------
/style.yaml:
--------------------------------------------------------------------------------
1 | name: Spirit
2 | version: 8
3 | sources:
4 | spirit:
5 | type: vector
6 | url: http://127.0.0.1:8000/tilejson.json
7 | sprite: http://127.0.0.1:8081/sprites
8 | glyphs: https://pnorman.dev.openstreetmap.org/spirit/fonts/{fontstack}/{range}.pbf
9 | layers:
10 | - !!inc/file style/background.yaml
11 | - !!inc/file style/landuse.yaml
12 | - !!inc/file style/education.yaml
13 | - !!inc/file style/education-outline.yaml
14 | - !!inc/file style/leisure.yaml
15 | - !!inc/file style/vegetation.yaml
16 | - !!inc/file style/vegetation-pattern.yaml
17 | - !!inc/file style/water.yaml
18 | - !!inc/file style/water-lines.yaml
19 | - !!inc/file style/building-fill.yaml
20 | - !!inc/file style/building-outline.yaml
21 | - !!inc/file style/road-base-casing.yaml
22 | - !!inc/file style/road-tunnel-casing.yaml
23 | - !!inc/file style/road-base-fill.yaml
24 | - !!inc/file style/road-casing.yaml
25 | - !!inc/file style/road-fill.yaml
26 | - !!inc/file style/rail-casing.yaml
27 | - !!inc/file style/rail-fill.yaml
28 | - !!inc/file style/road-bridge-casing.yaml
29 | - !!inc/file style/road-bridge-fill.yaml
30 | - !!inc/file style/rail-bridge-outer-casing.yaml
31 | - !!inc/file style/rail-bridge-casing.yaml
32 | - !!inc/file style/rail-bridge-fill.yaml
33 | - !!inc/file style/aeroways.yaml
34 | - !!inc/file style/transit-points.yaml
35 | - !!inc/file style/admin-background.yaml
36 | - !!inc/file style/admin-background-dashed.yaml
37 | - !!inc/file style/admin-2.yaml
38 | - !!inc/file style/admin-4.yaml
39 | - !!inc/file style/admin-6.yaml
40 | - !!inc/file style/admin-8.yaml
41 | - !!inc/file style/settlement-names.yaml
42 | - !!inc/file style/admin-names.yaml
43 | - !!inc/file style/aeroway-text.yaml
44 | - !!inc/file style/road-text.yaml
45 | - !!inc/file style/water-line-text.yaml
46 | - !!inc/file style/water-names.yaml
47 | - !!inc/file style/vegetation-names.yaml
48 | - !!inc/file style/leisure-names.yaml
49 | - !!inc/file style/education-names.yaml
50 | - !!inc/file style/landuse-names.yaml
51 | - !!inc/file style/food.yaml
52 | - !!inc/file style/building-names.yaml
53 |
--------------------------------------------------------------------------------
/style/admin-2.yaml:
--------------------------------------------------------------------------------
1 | id: admin-2
2 | source: spirit
3 | source-layer: admin
4 | type: line
5 | filter: [==, [get, admin_level], 2]
6 | layout:
7 | line-join: bevel
8 | line-cap: square
9 | paint:
10 | line-color: black
11 | line-width:
12 | - interpolate
13 | - [exponential, 1.35]
14 | - [zoom]
15 | - 4
16 | - 1.1
17 | - 12
18 | - 1.8
19 | - 20
20 | - 4
21 | line-dasharray: [12, 3, 4, 3, 4, 3]
22 |
--------------------------------------------------------------------------------
/style/admin-4.yaml:
--------------------------------------------------------------------------------
1 | id: admin-4
2 | source: spirit
3 | source-layer: admin
4 | type: line
5 | filter: [in, [get, admin_level], [literal, [3, 4]]]
6 | layout:
7 | line-join: bevel
8 | line-cap: square
9 | paint:
10 | line-color: black
11 | line-width:
12 | - interpolate
13 | - [exponential, 1.35]
14 | - [zoom]
15 | - 4
16 | - .9
17 | - 12
18 | - 1.2
19 | - 20
20 | - 3
21 | line-dasharray: [10, 3]
22 |
--------------------------------------------------------------------------------
/style/admin-6.yaml:
--------------------------------------------------------------------------------
1 | id: admin-6
2 | source: spirit
3 | source-layer: admin
4 | type: line
5 | filter: [in, [get, admin_level], [literal, [5, 6]]]
6 | layout:
7 | line-join: bevel
8 | line-cap: square
9 | paint:
10 | line-color: black
11 | line-width:
12 | - interpolate
13 | - [exponential, 1.35]
14 | - [zoom]
15 | - 4
16 | - .6
17 | - 12
18 | - 1
19 | - 20
20 | - 3
21 | line-dasharray: [7, 2, 3, 2]
22 |
--------------------------------------------------------------------------------
/style/admin-8.yaml:
--------------------------------------------------------------------------------
1 | id: admin-8
2 | source: spirit
3 | source-layer: admin
4 | type: line
5 | filter: [in, [get, admin_level], [literal, [7, 8]]]
6 | layout:
7 | line-join: bevel
8 | line-cap: square
9 | paint:
10 | line-color: black
11 | line-width:
12 | - interpolate
13 | - [exponential, 1.3]
14 | - [zoom]
15 | - 10
16 | - 1
17 | - 20
18 | - 2.5
19 | line-dasharray: [3, 3]
20 |
--------------------------------------------------------------------------------
/style/admin-background-dashed.yaml:
--------------------------------------------------------------------------------
1 | id: admin-background-dashed
2 | source: spirit
3 | source-layer: admin
4 | type: line
5 | layout:
6 | line-join: bevel
7 | line-cap: square
8 | filter: [all, [get, in_other_country], [==, [get, admin_level], 2]]
9 | paint:
10 | line-dasharray: [1.0, 1.0]
11 | line-color: rgba(194,94,184, 0.6) # lch(55,60,330)
12 | line-width:
13 | - interpolate
14 | - [exponential, 1.35]
15 | - [zoom]
16 | - 4
17 | - - match
18 | - [get, admin_level]
19 | - 2
20 | - 5
21 | - 4
22 | - 3
23 | - 6
24 | - 0
25 | - 3
26 | - 10
27 | - - match
28 | - [get, admin_level]
29 | - 2
30 | - 10
31 | - 4
32 | - 7
33 | - 6
34 | - 6
35 | - 3
36 | - 20
37 | - - match
38 | - [get, admin_level]
39 | - 2
40 | - 30
41 | - 4
42 | - 15
43 | - 6
44 | - 10
45 | - 3
46 |
47 |
--------------------------------------------------------------------------------
/style/admin-background.yaml:
--------------------------------------------------------------------------------
1 | id: admin-background
2 | source: spirit
3 | source-layer: admin
4 | type: line
5 | layout:
6 | line-join: bevel
7 | line-cap: square
8 | filter: [any, ['!=', [get, admin_level], 2], ['!', [get, in_other_country]]]
9 | paint:
10 | line-color: rgba(194,94,184, 0.6) # lch(55,60,330)
11 | line-width:
12 | - interpolate
13 | - [exponential, 1.35]
14 | - [zoom]
15 | - 4
16 | - - match
17 | - [get, admin_level]
18 | - 2
19 | - 5
20 | - 4
21 | - 3
22 | - 6
23 | - 0
24 | - 3
25 | - 10
26 | - - match
27 | - [get, admin_level]
28 | - 2
29 | - 10
30 | - 4
31 | - 7
32 | - 6
33 | - 6
34 | - 3
35 | - 20
36 | - - match
37 | - [get, admin_level]
38 | - 2
39 | - 30
40 | - 4
41 | - 15
42 | - 6
43 | - 10
44 | - 3
45 |
46 |
--------------------------------------------------------------------------------
/style/admin-names.yaml:
--------------------------------------------------------------------------------
1 | id: admin-names
2 | source: spirit
3 | source-layer: admin-names
4 | type: symbol
5 | paint:
6 | text-color: '#5a0756' # lch(35,70,330)
7 | text-halo-color: '#efddec' # lch(90,10,330)
8 | text-halo-width: 1.5
9 | text-halo-blur: 1
10 | layout:
11 | text-font: !!inc/file style/inc/regular-font.yaml
12 | text-field: '{name}'
13 | symbol-placement: point
14 | text-size: 14
15 | filter:
16 | - '>='
17 | - [get, way_area]
18 | - ['*', 750, 6126430366.1, ['^', 0.25, [zoom]]]
19 |
--------------------------------------------------------------------------------
/style/aeroway-text.yaml:
--------------------------------------------------------------------------------
1 | id: aeroway-text
2 | source: spirit
3 | source-layer: aeroways
4 | type: symbol
5 | paint:
6 | text-color: '#4d5262' #lch(35,10,280)
7 | text-halo-color: '#eef0fa' #lch(95,5,280)
8 | text-halo-width: 1.5
9 | text-halo-blur: 1
10 | minzoom: 12
11 | layout:
12 | text-font: !!inc/file style/inc/regular-font.yaml
13 | text-field: '{ref}'
14 | symbol-placement: line
15 | text-size:
16 | - interpolate
17 | - - exponential
18 | - 1.35
19 | - - zoom
20 | - 12
21 | - - match
22 | - - get
23 | - aeroway
24 | - runway
25 | - 10
26 | - 10
27 | - 20
28 | - - match
29 | - - get
30 | - aeroway
31 | - runway
32 | - 18
33 | - 14
34 | symbol-spacing: 400
35 | text-letter-spacing: 0.2
36 |
--------------------------------------------------------------------------------
/style/aeroways.yaml:
--------------------------------------------------------------------------------
1 | id: aeroways
2 | source: spirit
3 | source-layer: aeroways
4 | type: line
5 | paint:
6 | line-color: '#b3b8cb' # lch(75,10,280)
7 | line-width:
8 | - interpolate
9 | - - exponential
10 | - 1.2
11 | - - zoom
12 | - 12
13 | - - match
14 | - [get, aeroway]
15 | - runway
16 | - 4
17 | - 2
18 | - 20
19 | - - match
20 | - [get, aeroway]
21 | - runway
22 | - 72
23 | - 18
24 | layout:
25 | line-cap: butt
26 |
--------------------------------------------------------------------------------
/style/background.yaml:
--------------------------------------------------------------------------------
1 | id: background
2 | type: background
3 | layout:
4 | visibility: visible
5 | paint:
6 | background-color: '#eff2ec' # lch(95,3,130)
7 |
--------------------------------------------------------------------------------
/style/building-fill.yaml:
--------------------------------------------------------------------------------
1 | id: building-fill
2 | source: spirit
3 | source-layer: buildings
4 | layout:
5 | visibility: visible
6 | minzoom: 13
7 | type: fill
8 | paint:
9 | fill-color: '#d9d5c2' # lch(85,10,100)
10 |
--------------------------------------------------------------------------------
/style/building-names.yaml:
--------------------------------------------------------------------------------
1 | id: building-names
2 | source: spirit
3 | source-layer: building-names
4 | minzoom: 13
5 | type: symbol
6 | filter:
7 | - '>='
8 | - [get, way_area]
9 | - ['*', 750, 6126430366.1, ['^', 0.25, [zoom]]]
10 | layout:
11 | text-field: '{name}'
12 | text-font: !!inc/file style/inc/regular-font.yaml
13 | text-size: 12
14 | text-max-width:
15 | - interpolate
16 | - [linear]
17 | - [length, [get, name]]
18 | - 20
19 | - 7
20 | - 40
21 | - 10
22 | - 60
23 | - 15
24 | paint:
25 | text-color: '#3e3c2c' # lch(25,10,100)
26 | text-halo-width: 1
27 | text-halo-color: '#d9d5c2' # lch(85,10,100)
28 |
--------------------------------------------------------------------------------
/style/building-outline.yaml:
--------------------------------------------------------------------------------
1 | id: building-outline
2 | source: spirit
3 | source-layer: buildings
4 | type: line
5 | paint:
6 | line-color: '#c1b994' # lch(75,20,100)
7 | line-width: 1
8 |
--------------------------------------------------------------------------------
/style/education-names.yaml:
--------------------------------------------------------------------------------
1 | id: education-names
2 | source: spirit
3 | source-layer: education-names
4 | minzoom: 13
5 | type: symbol
6 | filter:
7 | - '>='
8 | - [get, way_area]
9 | - ['*', 1500, 6126430366.1, ['^', 0.25, [zoom]]]
10 | layout:
11 | text-field: '{name}'
12 | text-font: !!inc/file style/inc/regular-font.yaml
13 | text-size: 12
14 | text-max-width:
15 | - interpolate
16 | - [linear]
17 | - [length, [get, name]]
18 | - 20
19 | - 7
20 | - 40
21 | - 10
22 | - 60
23 | - 15
24 | paint:
25 | text-color: '#353109' # lch(20,25,100)
26 | text-halo-width: 1
27 | text-halo-color: '#ece3b3' # lch(90,25,100)
28 |
--------------------------------------------------------------------------------
/style/education-outline.yaml:
--------------------------------------------------------------------------------
1 | id: education-outline
2 | source: spirit
3 | source-layer: education
4 | type: line
5 | paint:
6 | line-color: '#cdc7ab' # lch(80,15,100)
7 | line-width: 1
8 |
--------------------------------------------------------------------------------
/style/education.yaml:
--------------------------------------------------------------------------------
1 | id: education
2 | source: spirit
3 | source-layer: education
4 | type: fill
5 | layout:
6 | fill-sort-key:
7 | - match
8 | - [get, education]
9 | - kindergarten
10 | - 4
11 | - school
12 | - 3
13 | - college
14 | - 2
15 | - university
16 | - 1
17 | - 0
18 | paint:
19 | fill-color: '#ece3b3' # lch(90,25,100)
20 |
--------------------------------------------------------------------------------
/style/food.yaml:
--------------------------------------------------------------------------------
1 | id: food
2 | source: spirit
3 | source-layer: food
4 | type: symbol
5 | layout:
6 | icon-image: food
7 | icon-allow-overlap: false
8 | icon-size: 1
9 | text-optional: true
10 | text-field: '{name}'
11 | text-font: !!inc/file style/inc/regular-font.yaml
12 | text-size: 11
13 | text-offset: [0, 0.45]
14 | text-anchor: top
15 | symbol-sort-key:
16 | - match
17 | - [get, food]
18 | - restaurant
19 | - 10
20 | - 100
21 | paint:
22 | text-color: '#663d06' # lch(30,40,70)
23 | text-halo-width: 1
24 | text-halo-color: '#f1e0d1'
25 |
--------------------------------------------------------------------------------
/style/inc/regular-font.yaml:
--------------------------------------------------------------------------------
1 | - Noto Sans Regular
2 |
--------------------------------------------------------------------------------
/style/inc/road-casing-color.yaml:
--------------------------------------------------------------------------------
1 | - match
2 | - [get, highway]
3 | - motorway
4 | - '#b40017' # lch(35,80,30)
5 | - trunk
6 | - '#bd5c00' # lch(50,70,60)
7 | - primary
8 | - '#bd5c00' # lch(50,70,60)
9 | - secondary
10 | - '#c9952c' # lch(65,60,80)
11 | - tertiary
12 | - '#c9952c' # lch(65,60,80)
13 | - unclassified
14 | - '#b7bab4' # lch(75,3,130)
15 | - residential
16 | - '#b7bab4' # lch(75,3,130)
17 | - living_street
18 | - '#b7bab4' # lch(75,3,130)
19 | - service
20 | - '#b7bab4' # lch(75,3,130)
21 | - footway
22 | - '#696b67' # lch(75,3,130)
23 | - cycleway
24 | - '#696b67' # lch(75,3,130)
25 | - red
26 |
--------------------------------------------------------------------------------
/style/inc/road-fill-color.yaml:
--------------------------------------------------------------------------------
1 | - match
2 | - [get, highway]
3 | - motorway
4 | - '#e52838' # lch(50,80,30)
5 | - trunk
6 | - '#ec822c' # lch(65,70,60)
7 | - primary
8 | - '#ec822c' # lch(65,70,60)
9 | - secondary
10 | - '#f7bd55' # lch(80,60,80)
11 | - tertiary
12 | - '#f7bd55' # lch(80,60,80)
13 | - unclassified
14 | - '#e0e3de' # lch(90,3,130)
15 | - residential
16 | - '#e0e3de' # lch(90,3,130)
17 | - living_street
18 | - '#e0e3de' # lch(90,3,130)
19 | - service
20 | - '#e0e3de' # lch(90,3,130)
21 | - red
22 |
--------------------------------------------------------------------------------
/style/landuse-names.yaml:
--------------------------------------------------------------------------------
1 | id: landuse-names
2 | source: spirit
3 | source-layer: landuse-names
4 | minzoom: 13
5 | type: symbol
6 | filter:
7 | - '>='
8 | - [get, way_area]
9 | - ['*', 1500, 6126430366.1, ['^', 0.25, [zoom]]]
10 | layout:
11 | text-field: '{name}'
12 | text-font: !!inc/file style/inc/regular-font.yaml
13 | text-size: 12
14 | text-max-width:
15 | - interpolate
16 | - [linear]
17 | - [length, [get, name]]
18 | - 20
19 | - 7
20 | - 40
21 | - 10
22 | - 60
23 | - 15
24 | paint:
25 | text-color: black
26 | text-halo-width: 1
27 | text-halo-color:
28 | - match
29 | - [get, landuse]
30 | - residential
31 | - '#f9f0dd' # lch(95,10,90)
32 | - commercial
33 | - '#fde2e5' # lch(92,10,10)
34 | - retail
35 | - '#fde2e5' # lch(92,10,10)
36 | - industrial
37 | - '#e0e3de' # lch(90,3,130)
38 | - red
39 |
--------------------------------------------------------------------------------
/style/landuse.yaml:
--------------------------------------------------------------------------------
1 | id: landuse
2 | source: spirit
3 | source-layer: landuse
4 | type: fill
5 | layout:
6 | fill-sort-key:
7 | - match
8 | - [get, landuse]
9 | - commercial
10 | - 4
11 | - retail
12 | - 3
13 | - residential
14 | - 2
15 | - industrial
16 | - 1
17 | - 0
18 | paint:
19 | fill-color:
20 | - match
21 | - [get, landuse]
22 | - residential
23 | - '#f9f0dd' # lch(95,10,90)
24 | - commercial
25 | - '#fde2e5' # lch(92,10,10)
26 | - retail
27 | - '#fde2e5' # lch(92,10,10)
28 | - industrial
29 | - '#e0e3de' # lch(90,3,130)
30 | - red
31 |
--------------------------------------------------------------------------------
/style/leisure-names.yaml:
--------------------------------------------------------------------------------
1 | id: leisure-names
2 | source: spirit
3 | source-layer: leisure-names
4 | minzoom: 13
5 | type: symbol
6 | filter:
7 | - '>='
8 | - [get, way_area]
9 | - ['*', 1500, 6126430366.1, ['^', 0.25, [zoom]]]
10 | layout:
11 | text-field: '{name}'
12 | text-font: !!inc/file style/inc/regular-font.yaml
13 | text-size: 12
14 | text-max-width:
15 | - interpolate
16 | - [linear]
17 | - [length, [get, name]]
18 | - 20
19 | - 7
20 | - 40
21 | - 10
22 | - 60
23 | - 15
24 | paint:
25 | text-color: '#20342e' # lch(20,10,170)
26 | text-halo-width: 1
27 | text-halo-color: '#cfe8df' # lch(90,10,170)
28 |
--------------------------------------------------------------------------------
/style/leisure.yaml:
--------------------------------------------------------------------------------
1 | id: leisure
2 | source: spirit
3 | source-layer: leisure
4 | type: fill
5 | filter:
6 | - in
7 | - [get, leisure]
8 | - [literal, [park, stadium]]
9 | layout:
10 | fill-sort-key:
11 | - match
12 | - [get, leisure]
13 | - park
14 | - 20
15 | - stadium
16 | - 10
17 | - playground
18 | - 3
19 | - 0
20 | paint:
21 | fill-color: '#cfe8df' # lch(90,10,170)
22 | fill-outline-color: '#78a897' # lch(65,20,170)
23 |
--------------------------------------------------------------------------------
/style/rail-bridge-casing.yaml:
--------------------------------------------------------------------------------
1 | id: rail-bridge-casing
2 | source: spirit
3 | source-layer: railways
4 | type: line
5 | filter:
6 | - all
7 | - - in
8 | - [get, railway]
9 | - - literal
10 | - - rail
11 | - [get, bridge]
12 | paint:
13 | line-color:
14 | - case
15 | - [get, minor]
16 | - '#aaa'
17 | - '#5e5e5e'
18 | line-width:
19 | - interpolate
20 | - - exponential
21 | - 1.35
22 | - - zoom
23 | - 12
24 | - 3
25 | - 20
26 | - 14
27 | layout:
28 | line-cap: butt
29 | line-sort-key:
30 | - +
31 | - - case
32 | - [get, minor]
33 | - 0
34 | - 10
35 | - - '*'
36 | - [get, layer]
37 | - 1000
38 |
--------------------------------------------------------------------------------
/style/rail-bridge-fill.yaml:
--------------------------------------------------------------------------------
1 | id: rail-bridge-fill
2 | source: spirit
3 | source-layer: railways
4 | type: line
5 | filter:
6 | - all
7 | - - in
8 | - [get, railway]
9 | - - literal
10 | - - rail
11 | - [get, bridge]
12 | paint:
13 | line-dasharray: [3, 3]
14 | line-color: white
15 | line-width:
16 | - interpolate
17 | - - exponential
18 | - 1.35
19 | - - zoom
20 | - 12
21 | - 1
22 | - 20
23 | - 8
24 | layout:
25 | line-cap: butt
26 | line-sort-key:
27 | - +
28 | - [get, z_order]
29 | - - '*'
30 | - [get, layer]
31 | - 1000
32 |
--------------------------------------------------------------------------------
/style/rail-bridge-outer-casing.yaml:
--------------------------------------------------------------------------------
1 | id: rail-bridge-outer-casing
2 | source: spirit
3 | source-layer: railways
4 | type: line
5 | filter:
6 | - all
7 | - - in
8 | - [get, railway]
9 | - - literal
10 | - - rail
11 | - [get, bridge]
12 | paint:
13 | line-color: black
14 | line-width:
15 | - interpolate
16 | - - exponential
17 | - 1.35
18 | - - zoom
19 | - 12
20 | - 5
21 | - 20
22 | - 20
23 | layout:
24 | line-cap: butt
25 | line-sort-key:
26 | - +
27 | - - case
28 | - [get, minor]
29 | - 0
30 | - 10
31 | - - '*'
32 | - [get, layer]
33 | - 1000
34 |
--------------------------------------------------------------------------------
/style/rail-casing.yaml:
--------------------------------------------------------------------------------
1 | id: rail-casing
2 | source: spirit
3 | source-layer: railways
4 | type: line
5 | filter:
6 | - all
7 | - - in
8 | - [get, railway]
9 | - - literal
10 | - - rail
11 | - ['!', [get, tunnel]]
12 | - ['!', [get, bridge]]
13 | paint:
14 | line-color:
15 | - case
16 | - [get, minor]
17 | - '#aaa'
18 | - '#5e5e5e'
19 | line-width:
20 | - interpolate
21 | - - exponential
22 | - 1.35
23 | - - zoom
24 | - 12
25 | - 3
26 | - 20
27 | - 14
28 | layout:
29 | line-cap: butt
30 | line-sort-key:
31 | - +
32 | - - case
33 | - [get, minor]
34 | - 0
35 | - 10
36 | - - '*'
37 | - [get, layer]
38 | - 1000
39 |
--------------------------------------------------------------------------------
/style/rail-fill.yaml:
--------------------------------------------------------------------------------
1 | id: rail-fill
2 | source: spirit
3 | source-layer: railways
4 | type: line
5 | filter:
6 | - all
7 | - - in
8 | - [get, railway]
9 | - - literal
10 | - - rail
11 | - ['!', [get, tunnel]]
12 | - ['!', [get, bridge]]
13 | paint:
14 | line-dasharray: [3, 3]
15 | line-color: white
16 | line-width:
17 | - interpolate
18 | - - exponential
19 | - 1.35
20 | - - zoom
21 | - 12
22 | - 1
23 | - 20
24 | - 8
25 | layout:
26 | line-cap: butt
27 | line-sort-key:
28 | - +
29 | - [get, z_order]
30 | - - '*'
31 | - [get, layer]
32 | - 1000
33 |
--------------------------------------------------------------------------------
/style/road-base-casing.yaml:
--------------------------------------------------------------------------------
1 | # A layer at the base of the transport stack, covering tunnels, as well as round end-caps for anything else
2 | id: road-base-casing
3 | source: spirit
4 | source-layer: roads
5 | type: line
6 | filter:
7 | # This messy filter could be done with a [step, [zoom], ..., N, ...] but this
8 | # would require re-stating the conditions for all lower zoom for each step.
9 | # Instead, it requires one of the any branches is met, and each branch involves
10 | # a zoom level conditional. If the zoom-level part is omitted, the results look
11 | # odd when zooming in and out.
12 | - any
13 | - - all # Allow certain classifications to appear when they are included in the source
14 | - - in
15 | - [get, highway]
16 | - - literal
17 | - - motorway
18 | - trunk
19 | - primary
20 | - secondary
21 | - tertiary
22 | - unclassified
23 | - residential
24 | - living_street
25 | - - all
26 | - [==, [get, highway], service]
27 | - [==, [get, minor], null]
28 | - [">=", [zoom], 14]
29 | - - all
30 | - [==, [get, highway], service]
31 | - [">=", [zoom], 15]
32 | - - all
33 | - - in
34 | - [get, highway]
35 | - - literal
36 | - - footway
37 | - cycleway
38 | - [">=", [zoom], 15]
39 | paint:
40 | line-color:
41 | - match
42 | - [get, highway]
43 | - motorway
44 | - '#b40017' # lch(35,80,30)
45 | - trunk
46 | - '#bd5c00' # lch(50,70,60)
47 | - primary
48 | - '#bd5c00' # lch(50,70,60)
49 | - secondary
50 | - '#c9952c' # lch(65,60,80)
51 | - tertiary
52 | - '#c9952c' # lch(65,60,80)
53 | - unclassified
54 | - '#b7bab4' # lch(75,3,130)
55 | - residential
56 | - '#b7bab4' # lch(75,3,130)
57 | - living_street
58 | - '#b7bab4' # lch(75,3,130)
59 | - service
60 | - '#b7bab4' # lch(75,3,130)
61 | - footway
62 | - '#696b67' # lch(75,3,130)
63 | - cycleway
64 | - '#696b67' # lch(75,3,130)
65 | - red
66 | line-width:
67 | - interpolate
68 | - - exponential
69 | - 1.35
70 | - - zoom
71 | - 12
72 | - - match
73 | - [get, highway]
74 | - motorway
75 | - 4
76 | - trunk
77 | - 3.5
78 | - primary
79 | - 3.5
80 | - secondary
81 | - 2
82 | - tertiary
83 | - 2
84 | - unclassified
85 | - 1.5
86 | - residential
87 | - 1.5
88 | - living_street
89 | - 1.5
90 | - service
91 | - - match
92 | - [to-string, [get, minor]]
93 | - "true"
94 | - 0.5
95 | - 1
96 | - footway
97 | - .2
98 | - cycleway
99 | - .2
100 | - 1.5
101 | - 20
102 | - - match
103 | - [get, highway]
104 | - motorway
105 | - 40
106 | - trunk
107 | - 30
108 | - primary
109 | - 30
110 | - secondary
111 | - 25
112 | - tertiary
113 | - 25
114 | - unclassified
115 | - 20
116 | - residential
117 | - 20
118 | - living_street
119 | - 20
120 | - service
121 | - - match
122 | - [to-string, [get, minor]]
123 | - "true"
124 | - 10
125 | - 14
126 | - footway
127 | - 4
128 | - cycleway
129 | - 4
130 | - 20
131 | layout:
132 | line-cap: round
133 | line-sort-key:
134 | - +
135 | - [get, z_order]
136 | - - '*'
137 | - [get, layer]
138 | - 1000
139 |
--------------------------------------------------------------------------------
/style/road-base-fill.yaml:
--------------------------------------------------------------------------------
1 | # A layer at the base of the transport stack, covering tunnels, as well as round end-caps for anything else.
2 | id: road-base-fill
3 | source: spirit
4 | source-layer: roads
5 | type: line
6 | # This messy filter could be done with a [step, [zoom], ..., N, ...] but this
7 | # would require re-stating the conditions for all lower zoom for each step.
8 | # Instead, it requires one of the any branches is met, and each branch involves
9 | # a zoom level conditional. If the zoom-level part is omitted, the results look
10 | # odd when zooming in and out.
11 | filter:
12 | - any
13 | - - all # Allow certain classifications to appear when they are included in the source
14 | - - in
15 | - [get, highway]
16 | - - literal
17 | - - motorway
18 | - trunk
19 | - primary
20 | - secondary
21 | - tertiary
22 | - unclassified
23 | - residential
24 | - living_street
25 | - - all
26 | - [==, [get, highway], service]
27 | - [==, [get, minor], null]
28 | - [">=", [zoom], 14]
29 | - - all
30 | - [==, [get, highway], service]
31 | - [">=", [zoom], 15]
32 | paint:
33 | line-color: !!inc/file style/inc/road-fill-color.yaml
34 | line-width:
35 | - interpolate
36 | - - exponential
37 | - 1.35
38 | - - zoom
39 | - 12
40 | - - match
41 | - [get, highway]
42 | - motorway
43 | - 2
44 | - trunk
45 | - 1.5
46 | - primary
47 | - 1.5
48 | - secondary
49 | - 1
50 | - tertiary
51 | - 1
52 | - unclassified
53 | - 1
54 | - residential
55 | - 1
56 | - living_street
57 | - 1
58 | - service
59 | - - match
60 | - [to-string, [get, minor]]
61 | - "true"
62 | - 0.2
63 | - 0.5
64 | - 1
65 | - 20
66 | - - match
67 | - [get, highway]
68 | - motorway
69 | - 25
70 | - trunk
71 | - 20
72 | - primary
73 | - 20
74 | - secondary
75 | - 15
76 | - tertiary
77 | - 15
78 | - unclassified
79 | - 10
80 | - residential
81 | - 10
82 | - living_street
83 | - 10
84 | - service
85 | - - match
86 | - [to-string, [get, minor]]
87 | - "true"
88 | - 3
89 | - 6
90 | - 10
91 |
92 | layout:
93 | line-cap: round
94 | line-sort-key:
95 | - +
96 | - ['-', [get, z_order]]
97 | - - '*'
98 | - [get, layer]
99 | - -1000
100 |
--------------------------------------------------------------------------------
/style/road-bridge-casing.yaml:
--------------------------------------------------------------------------------
1 | id: road-bridge-casing
2 | source: spirit
3 | source-layer: roads
4 | type: line
5 | filter:
6 | # This messy filter could be done with a [step, [zoom], ..., N, ...] but this
7 | # would require re-stating the conditions for all lower zoom for each step.
8 | # Instead, it requires one of the any branches is met, and each branch involves
9 | # a zoom level conditional. If the zoom-level part is omitted, the results look
10 | # odd when zooming in and out.
11 | - all
12 | - - any
13 | - - all # Allow certain classifications to appear when they are included in the source
14 | - - in
15 | - [get, highway]
16 | - - literal
17 | - - motorway
18 | - trunk
19 | - primary
20 | - secondary
21 | - tertiary
22 | - unclassified
23 | - residential
24 | - living_street
25 | - - all
26 | - [==, [get, highway], service]
27 | - [==, [get, minor], null]
28 | - [">=", [zoom], 14]
29 | - - all
30 | - [==, [get, highway], service]
31 | - [">=", [zoom], 15]
32 | - - all
33 | - - in
34 | - [get, highway]
35 | - - literal
36 | - - footway
37 | - cycleway
38 | - [">=", [zoom], 15]
39 | - [get, bridge]
40 | paint:
41 | line-color: black
42 | line-width:
43 | - interpolate
44 | - - exponential
45 | - 1.35
46 | - - zoom
47 | - 12
48 | - - match
49 | - [get, highway]
50 | - motorway
51 | - 4
52 | - trunk
53 | - 3.5
54 | - primary
55 | - 3.5
56 | - secondary
57 | - 2
58 | - tertiary
59 | - 2
60 | - unclassified
61 | - 1.5
62 | - residential
63 | - 1.5
64 | - living_street
65 | - 1.5
66 | - service
67 | - - match
68 | - [to-string, [get, minor]]
69 | - "true"
70 | - 0.5
71 | - 1
72 | - footway
73 | - .2
74 | - cycleway
75 | - .2
76 | - 1.5
77 | - 20
78 | - - match
79 | - [get, highway]
80 | - motorway
81 | - 40
82 | - trunk
83 | - 30
84 | - primary
85 | - 30
86 | - secondary
87 | - 25
88 | - tertiary
89 | - 25
90 | - unclassified
91 | - 20
92 | - residential
93 | - 20
94 | - living_street
95 | - 20
96 | - service
97 | - - match
98 | - [to-string, [get, minor]]
99 | - "true"
100 | - 10
101 | - 14
102 | - footway
103 | - 4
104 | - cycleway
105 | - 4
106 | - 20
107 | layout:
108 | line-cap: butt
109 | line-sort-key:
110 | - +
111 | - [get, z_order]
112 | - - '*'
113 | - [get, layer]
114 | - 1000
115 |
--------------------------------------------------------------------------------
/style/road-bridge-fill.yaml:
--------------------------------------------------------------------------------
1 | id: road-bridge-fill
2 | source: spirit
3 | source-layer: roads
4 | type: line
5 | # This messy filter could be done with a [step, [zoom], ..., N, ...] but this
6 | # would require re-stating the conditions for all lower zoom for each step.
7 | # Instead, it requires one of the any branches is met, and each branch involves
8 | # a zoom level conditional. If the zoom-level part is omitted, the results look
9 | # odd when zooming in and out.
10 | filter:
11 | - all
12 | - - any
13 | - - all # Allow certain classifications to appear when they are included in the source
14 | - - in
15 | - [get, highway]
16 | - - literal
17 | - - motorway
18 | - trunk
19 | - primary
20 | - secondary
21 | - tertiary
22 | - unclassified
23 | - residential
24 | - living_street
25 | - - all
26 | - [==, [get, highway], service]
27 | - [==, [get, minor], null]
28 | - [">=", [zoom], 14]
29 | - - all
30 | - [==, [get, highway], service]
31 | - [">=", [zoom], 15]
32 | - [get, bridge]
33 | paint:
34 | line-color: !!inc/file style/inc/road-fill-color.yaml
35 | line-width:
36 | - interpolate
37 | - - exponential
38 | - 1.35
39 | - - zoom
40 | - 12
41 | - - match
42 | - [get, highway]
43 | - motorway
44 | - 2
45 | - trunk
46 | - 1.5
47 | - primary
48 | - 1.5
49 | - secondary
50 | - 1
51 | - tertiary
52 | - 1
53 | - unclassified
54 | - 1
55 | - residential
56 | - 1
57 | - living_street
58 | - 1
59 | - service
60 | - - match
61 | - [to-string, [get, minor]]
62 | - "true"
63 | - 0.2
64 | - 0.5
65 | - 1
66 | - 20
67 | - - match
68 | - [get, highway]
69 | - motorway
70 | - 25
71 | - trunk
72 | - 20
73 | - primary
74 | - 20
75 | - secondary
76 | - 15
77 | - tertiary
78 | - 15
79 | - unclassified
80 | - 10
81 | - residential
82 | - 10
83 | - living_street
84 | - 10
85 | - service
86 | - - match
87 | - [to-string, [get, minor]]
88 | - "true"
89 | - 3
90 | - 6
91 | - 10
92 |
93 | layout:
94 | line-cap: butt
95 | line-sort-key:
96 | - +
97 | - [get, z_order]
98 | - - '*'
99 | - [get, layer]
100 | - 1000
101 |
--------------------------------------------------------------------------------
/style/road-casing.yaml:
--------------------------------------------------------------------------------
1 | id: road-casing
2 | source: spirit
3 | source-layer: roads
4 | type: line
5 | filter:
6 | # This messy filter could be done with a [step, [zoom], ..., N, ...] but this
7 | # would require re-stating the conditions for all lower zoom for each step.
8 | # Instead, it requires one of the any branches is met, and each branch involves
9 | # a zoom level conditional. If the zoom-level part is omitted, the results look
10 | # odd when zooming in and out.
11 | - all
12 | - - any
13 | - - all # Allow certain classifications to appear when they are included in the source
14 | - - in
15 | - [get, highway]
16 | - - literal
17 | - - motorway
18 | - trunk
19 | - primary
20 | - secondary
21 | - tertiary
22 | - unclassified
23 | - residential
24 | - living_street
25 | - - all
26 | - [==, [get, highway], service]
27 | - [==, [get, minor], null]
28 | - [">=", [zoom], 14]
29 | - - all
30 | - [==, [get, highway], service]
31 | - [">=", [zoom], 15]
32 | - - all
33 | - - in
34 | - [get, highway]
35 | - - literal
36 | - - footway
37 | - cycleway
38 | - [">=", [zoom], 15]
39 | - ['!', [get, tunnel]]
40 | - ['!', [get, bridge]]
41 | paint:
42 | line-color: !!inc/file style/inc/road-casing-color.yaml
43 | line-width:
44 | - interpolate
45 | - - exponential
46 | - 1.35
47 | - - zoom
48 | - 12
49 | - - match
50 | - [get, highway]
51 | - motorway
52 | - 4
53 | - trunk
54 | - 3.5
55 | - primary
56 | - 3.5
57 | - secondary
58 | - 2
59 | - tertiary
60 | - 2
61 | - unclassified
62 | - 1.5
63 | - residential
64 | - 1.5
65 | - living_street
66 | - 1.5
67 | - service
68 | - - match
69 | - [to-string, [get, minor]]
70 | - "true"
71 | - 0.5
72 | - 1
73 | - footway
74 | - .2
75 | - cycleway
76 | - .2
77 | - 1.5
78 | - 20
79 | - - match
80 | - [get, highway]
81 | - motorway
82 | - 40
83 | - trunk
84 | - 30
85 | - primary
86 | - 30
87 | - secondary
88 | - 25
89 | - tertiary
90 | - 25
91 | - unclassified
92 | - 20
93 | - residential
94 | - 20
95 | - living_street
96 | - 20
97 | - service
98 | - - match
99 | - [to-string, [get, minor]]
100 | - "true"
101 | - 10
102 | - 14
103 | - footway
104 | - 4
105 | - cycleway
106 | - 4
107 | - 20
108 | layout:
109 | line-cap: butt
110 | line-sort-key:
111 | - +
112 | - [get, z_order]
113 | - - '*'
114 | - [get, layer]
115 | - 1000
116 |
--------------------------------------------------------------------------------
/style/road-fill.yaml:
--------------------------------------------------------------------------------
1 | id: road-fill
2 | source: spirit
3 | source-layer: roads
4 | type: line
5 | filter:
6 | # This messy filter could be done with a [step, [zoom], ..., N, ...] but this
7 | # would require re-stating the conditions for all lower zoom for each step.
8 | # Instead, it requires one of the any branches is met, and each branch involves
9 | # a zoom level conditional. If the zoom-level part is omitted, the results look
10 | # odd when zooming in and out.
11 | - all
12 | - - any
13 | - - all # Allow certain classifications to appear when they are included in the source
14 | - - in
15 | - [get, highway]
16 | - - literal
17 | - - motorway
18 | - trunk
19 | - primary
20 | - secondary
21 | - tertiary
22 | - unclassified
23 | - residential
24 | - living_street
25 | - - all
26 | - [==, [get, highway], service]
27 | - [==, [get, minor], null]
28 | - [">=", [zoom], 14]
29 | - - all
30 | - [==, [get, highway], service]
31 | - [">=", [zoom], 15]
32 | - ['!', [get, tunnel]]
33 | - ['!', [get, bridge]]
34 | paint:
35 | line-color: !!inc/file style/inc/road-fill-color.yaml
36 | line-width:
37 | - interpolate
38 | - - exponential
39 | - 1.35
40 | - - zoom
41 | - 12
42 | - - match
43 | - [get, highway]
44 | - motorway
45 | - 2
46 | - trunk
47 | - 1.5
48 | - primary
49 | - 1.5
50 | - secondary
51 | - 1
52 | - tertiary
53 | - 1
54 | - unclassified
55 | - 1
56 | - residential
57 | - 1
58 | - living_street
59 | - 1
60 | - service
61 | - - match
62 | - [to-string, [get, minor]]
63 | - "true"
64 | - 0.2
65 | - 0.5
66 | - 1
67 | - 20
68 | - - match
69 | - [get, highway]
70 | - motorway
71 | - 25
72 | - trunk
73 | - 20
74 | - primary
75 | - 20
76 | - secondary
77 | - 15
78 | - tertiary
79 | - 15
80 | - unclassified
81 | - 10
82 | - residential
83 | - 10
84 | - living_street
85 | - 10
86 | - service
87 | - - match
88 | - [to-string, [get, minor]]
89 | - "true"
90 | - 3
91 | - 6
92 | - 10
93 |
94 | layout:
95 | line-cap: butt
96 | line-sort-key:
97 | - +
98 | - [get, z_order]
99 | - - '*'
100 | - [get, layer]
101 | - 1000
102 |
--------------------------------------------------------------------------------
/style/road-text.yaml:
--------------------------------------------------------------------------------
1 | id: road-text
2 | source: spirit
3 | source-layer: roads
4 | type: symbol
5 | filter:
6 | # This messy filter could be done with a [step, [zoom], ..., N, ...] but this
7 | # would require re-stating the conditions for all lower zoom for each step.
8 | # Instead, it requires one of the any branches is met, and each branch involves
9 | # a zoom level conditional. If the zoom-level part is omitted, the results look
10 | # odd when zooming in and out.
11 | - all
12 | - - any
13 | - - all # Allow certain classifications to appear when they are included in the source
14 | - - in
15 | - [get, highway]
16 | - - literal
17 | - - motorway
18 | - trunk
19 | - primary
20 | - secondary
21 | - tertiary
22 | - unclassified
23 | - residential
24 | - living_street
25 | - - all
26 | - [==, [get, highway], service]
27 | - [==, [get, minor], null]
28 | - [">=", [zoom], 14]
29 | - - all
30 | - [==, [get, highway], service]
31 | - [">=", [zoom], 15]
32 | - ['!', [get, tunnel]]
33 | - ['!', [get, bridge]]
34 | paint:
35 | text-color: black
36 | text-halo-color: !!inc/file style/inc/road-fill-color.yaml
37 | text-halo-width: 1.5
38 | text-halo-blur: 1
39 | layout:
40 | text-font: !!inc/file style/inc/regular-font.yaml
41 | text-field: '{name}'
42 | symbol-placement: line
43 | text-size:
44 | - interpolate
45 | - - exponential
46 | - 1.35
47 | - [zoom]
48 | - 12
49 | - 12
50 | - 20
51 | - 18
52 | symbol-spacing: 300
53 |
--------------------------------------------------------------------------------
/style/road-tunnel-casing.yaml:
--------------------------------------------------------------------------------
1 | id: road-tunnel-casing
2 | source: spirit
3 | source-layer: roads
4 | type: line
5 | filter:
6 | - all
7 | - - any
8 | - - all # Allow certain classifications to appear when they are included in the source
9 | - - in
10 | - [get, highway]
11 | - - literal
12 | - - motorway
13 | - trunk
14 | - primary
15 | - secondary
16 | - tertiary
17 | - unclassified
18 | - residential
19 | - living_street
20 | - - all
21 | - [==, [get, highway], service]
22 | - [==, [get, minor], null]
23 | - [">=", [zoom], 14]
24 | - - all
25 | - [==, [get, highway], service]
26 | - [">=", [zoom], 15]
27 | - - all
28 | - - in
29 | - [get, highway]
30 | - - literal
31 | - - footway
32 | - cycleway
33 | - [">=", [zoom], 15]
34 | - [get, tunnel]
35 | paint:
36 | line-dasharray: [.5, .5]
37 | line-color:
38 | - match
39 | - [get, highway]
40 | - motorway
41 | - '#e19991' #
42 | - trunk
43 | - '#bd5c00' # lch(50,70,60)
44 | - primary
45 | - '#bd5c00' # lch(50,70,60)
46 | - secondary
47 | - '#c9952c' # lch(65,60,80)
48 | - tertiary
49 | - '#c9952c' # lch(65,60,80)
50 | - unclassified
51 | - '#b7bab4' # lch(75,3,130)
52 | - residential
53 | - '#b7bab4' # lch(75,3,130)
54 | - living_street
55 | - '#b7bab4' # lch(75,3,130)
56 | - service
57 | - '#b7bab4' # lch(75,3,130)
58 | - footway
59 | - '#696b67' # lch(75,3,130)
60 | - cycleway
61 | - '#696b67' # lch(75,3,130)
62 | - red
63 | line-width:
64 | - interpolate
65 | - - exponential
66 | - 1.35
67 | - - zoom
68 | - 12
69 | - - match
70 | - [get, highway]
71 | - motorway
72 | - 4
73 | - trunk
74 | - 3.5
75 | - primary
76 | - 3.5
77 | - secondary
78 | - 2
79 | - tertiary
80 | - 2
81 | - unclassified
82 | - 1.5
83 | - residential
84 | - 1.5
85 | - living_street
86 | - 1.5
87 | - service
88 | - - match
89 | - [to-string, [get, minor]]
90 | - "true"
91 | - 0.5
92 | - 1
93 | - footway
94 | - .2
95 | - cycleway
96 | - .2
97 | - 1.5
98 | - 20
99 | - - match
100 | - [get, highway]
101 | - motorway
102 | - 40
103 | - trunk
104 | - 30
105 | - primary
106 | - 30
107 | - secondary
108 | - 25
109 | - tertiary
110 | - 25
111 | - unclassified
112 | - 20
113 | - residential
114 | - 20
115 | - living_street
116 | - 20
117 | - service
118 | - - match
119 | - [to-string, [get, minor]]
120 | - "true"
121 | - 10
122 | - 14
123 | - footway
124 | - 4
125 | - cycleway
126 | - 4
127 | - 20
128 | layout:
129 | line-cap: butt
130 | line-sort-key:
131 | - +
132 | - [get, z_order]
133 | - - '*'
134 | - [get, layer]
135 | - 1000
136 |
--------------------------------------------------------------------------------
/style/settlement-names.yaml:
--------------------------------------------------------------------------------
1 | id: settlement-names
2 | source: spirit
3 | source-layer: settlements
4 | type: symbol
5 | layout:
6 | text-field: '{name}'
7 | text-font: !!inc/file style/inc/regular-font.yaml
8 | text-size:
9 | - match
10 | - [get, place]
11 | - city
12 | - 14
13 | - 12
14 | paint:
15 | text-color: black
16 | text-halo-width: 1
17 | text-halo-color: white
18 |
--------------------------------------------------------------------------------
/style/transit-points.yaml:
--------------------------------------------------------------------------------
1 | id: transit-points
2 | source: spirit
3 | source-layer: transit-points
4 | type: symbol
5 | layout:
6 | icon-image:
7 | - match
8 | - [get, mode]
9 | - bus
10 | - - case
11 | - - get
12 | - station
13 | - bus_station
14 | - bus_stop
15 | - tram
16 | - bus_stop
17 | - subway
18 | - subway
19 | - train
20 | - train
21 | - airplane
22 | - airport
23 | - wetland
24 | icon-allow-overlap: true
25 | text-optional: true
26 | text-field: '{name}'
27 | text-font: !!inc/file style/inc/regular-font.yaml
28 | text-size: 12
29 | text-offset: [0, 0.4]
30 | text-anchor: top
31 | paint:
32 | text-color: '#003c77'
33 | text-halo-width: 1
34 | text-halo-color: white
35 |
--------------------------------------------------------------------------------
/style/vegetation-names.yaml:
--------------------------------------------------------------------------------
1 | id: vegetation-names
2 | source: spirit
3 | source-layer: vegetation-names
4 | minzoom: 13
5 | type: symbol
6 | filter:
7 | - '>='
8 | - [get, way_area]
9 | - ['*', 1500, 6126430366.1, ['^', 0.25, [zoom]]]
10 | layout:
11 | text-field: '{name}'
12 | text-font: !!inc/file style/inc/regular-font.yaml
13 | text-size: 12
14 | text-max-width:
15 | - interpolate
16 | - [linear]
17 | - [length, [get, name]]
18 | - 20
19 | - 7
20 | - 40
21 | - 10
22 | - 60
23 | - 15
24 | paint:
25 | text-color: black
26 | text-halo-width: 1
27 | text-halo-color:
28 | - match
29 | - [get, vegetation]
30 | - wood
31 | - '#56b155' # lch(65,60,140)
32 | - heath
33 | - '#9cc47f' # lch(75,40,130)
34 | - scrub
35 | - '#9cc47f' # lch(75,40,130)
36 | - grass
37 | - '#d5e9c5' # lch(90,20,130)
38 | - wetland
39 | - '#d5e9c5'
40 | - red
41 |
--------------------------------------------------------------------------------
/style/vegetation-pattern.yaml:
--------------------------------------------------------------------------------
1 | # The pattern selection for this layer has to align with
2 |
3 | id: vegetation-pattern
4 | source: spirit
5 | source-layer: vegetation
6 | type: fill
7 | filter: ["==", ["get", "vegetation"], "wetland"]
8 | paint:
9 | fill-pattern: wetland
10 |
--------------------------------------------------------------------------------
/style/vegetation.yaml:
--------------------------------------------------------------------------------
1 | id: vegetation
2 | source: spirit
3 | source-layer: vegetation
4 | type: fill
5 | paint:
6 | fill-color:
7 | - match
8 | - [get, vegetation]
9 | - wood
10 | - '#56b155' # lch(65,60,140)
11 | - heath
12 | - '#9cc47f' # lch(75,40,130)
13 | - scrub
14 | - '#9cc47f' # lch(75,40,130)
15 | - grass
16 | - '#d5e9c5' # lch(90,20,130)
17 | - wetland
18 | - '#d5e9c5'
19 | - red
20 |
--------------------------------------------------------------------------------
/style/water-line-text.yaml:
--------------------------------------------------------------------------------
1 | id: water-line-text
2 | source: spirit
3 | source-layer: water-lines
4 | type: symbol
5 | paint:
6 | text-color: '#24586d' # lch(35,20,240)
7 | text-halo-color: '#e7f2f9' # lch(95,5,240)
8 | text-halo-width: 1.5
9 | text-halo-blur: 1
10 | minzoom: 12
11 | layout:
12 | text-font: !!inc/file style/inc/regular-font.yaml
13 | text-field: '{name}'
14 | symbol-placement: line
15 | text-size:
16 | - interpolate
17 | - - exponential
18 | - 1.35
19 | - - zoom
20 | - 12
21 | - - match
22 | - - get
23 | - waterway
24 | - river
25 | - 10
26 | - canal
27 | - 10
28 | - 0
29 | - 15
30 | - - match
31 | - - get
32 | - waterway
33 | - river
34 | - 14
35 | - canal
36 | - 14
37 | - stream
38 | - 10
39 | - 0
40 | - 18
41 | - - match
42 | - - get
43 | - waterway
44 | - river
45 | - 18
46 | - canal
47 | - 18
48 | - stream
49 | - 14
50 | - 0
51 | symbol-spacing: 400
52 | text-letter-spacing: 0.2
53 | filter:
54 | - step
55 | - - zoom
56 | - - any
57 | - - '=='
58 | - [get, waterway]
59 | - river
60 | - 14
61 | - - any
62 | - - '=='
63 | - [get, waterway]
64 | - river
65 | - - '=='
66 | - [get, waterway]
67 | - canal
68 | - 15
69 | - - any
70 | - - '=='
71 | - [get, waterway]
72 | - river
73 | - - '=='
74 | - [get, waterway]
75 | - canal
76 | - - '=='
77 | - [get, waterway]
78 | - stream
79 |
--------------------------------------------------------------------------------
/style/water-lines.yaml:
--------------------------------------------------------------------------------
1 | id: water-lines
2 | source: spirit
3 | source-layer: water-lines
4 | type: line
5 | paint:
6 | line-color: '#4cb7e1' #lch(70,35,240)
7 | line-width:
8 | - interpolate
9 | - - exponential
10 | - 1.35
11 | - - zoom
12 | - 8
13 | - - match
14 | - [get, waterway]
15 | - river
16 | - 0.2
17 | - canal
18 | - 0.2
19 | - 0
20 | - 14
21 | - - match
22 | - [get, waterway]
23 | - river
24 | - 2
25 | - canal
26 | - 2
27 | - stream
28 | - 0.8
29 | - drain
30 | - 0.1
31 | - ditch
32 | - 0.1
33 | - 0
34 | - 20
35 | - - match
36 | - [get, waterway]
37 | - river
38 | - 10
39 | - canal
40 | - 10
41 | - stream
42 | - 6
43 | - drain
44 | - 3
45 | - ditch
46 | - 3
47 | - 0
48 |
--------------------------------------------------------------------------------
/style/water-names.yaml:
--------------------------------------------------------------------------------
1 | id: water-names
2 | source: spirit
3 | source-layer: water-names
4 | type: symbol
5 | paint:
6 | text-color: '#24586d' # lch(35,20,240)
7 | text-halo-color: '#e7f2f9' # lch(95,5,240)
8 | text-halo-width: 1.5
9 | text-halo-blur: 1
10 | layout:
11 | text-font: !!inc/file style/inc/regular-font.yaml
12 | text-field: '{name}'
13 | symbol-placement: point
14 | text-size: 12
15 | filter:
16 | - '>='
17 | - [get, way_area]
18 | - ['*', 750, 6126430366.1, ['^', 0.25, [zoom]]]
19 |
--------------------------------------------------------------------------------
/style/water.yaml:
--------------------------------------------------------------------------------
1 | id: water
2 | source: spirit
3 | source-layer: water
4 | type: fill
5 | paint:
6 | fill-color: '#4cb7e1' #lch(70,35,240)
7 |
--------------------------------------------------------------------------------
/themes/shortbread/init.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Theme: shortbread
4 | --
5 | -- ---------------------------------------------------------------------------
6 |
7 | local theme = {}
8 |
9 | return theme
10 |
11 | -- ---------------------------------------------------------------------------
12 |
--------------------------------------------------------------------------------
/themes/shortbread/topics/addresses.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Theme: shortbread_v1
4 | -- Topic: addresses
5 | --
6 | -- ---------------------------------------------------------------------------
7 |
8 | local themepark, theme, cfg = ...
9 | local expire = require('expire')
10 |
11 | themepark:add_table{
12 | name = 'addresses',
13 | ids_type = 'any',
14 | geom = 'point',
15 | columns = themepark:columns({
16 | { column = 'housename', type = 'text' },
17 | { column = 'housenumber', type = 'text' },
18 | }),
19 | tags = {
20 | { key = 'addr:housename' },
21 | { key = 'addr:housenumber' },
22 | },
23 | tiles = {
24 | minzoom = 14,
25 | },
26 | expire = expire.shortbread(14, 14, 'addresses', 'full-area')
27 | }
28 |
29 | -- ---------------------------------------------------------------------------
30 |
31 | local function process(t)
32 | if not t['addr:housenumber'] and not t['addr:housename'] then
33 | return nil
34 | end
35 |
36 | return {
37 | housenumber = t['addr:housenumber'],
38 | housename = t['addr:housename'],
39 | }
40 | end
41 |
42 | -- ---------------------------------------------------------------------------
43 |
44 | themepark:add_proc('node', function(object, data)
45 | -- Shortbread spec: Ignore addresses that are already in "pois" layer.
46 | if data.shortbread_in_pois then
47 | return
48 | end
49 |
50 | local a = process(object.tags)
51 | if a then
52 | a.geom = object:as_point()
53 | themepark:insert('addresses', a, object.tags)
54 | end
55 | end)
56 |
57 | themepark:add_proc('way', function(object, data)
58 | -- Shortbread spec: Ignore addresses that are already in "pois" layer.
59 | if data.shortbread_in_pois or not object.is_closed then
60 | return
61 | end
62 |
63 | local a = process(object.tags)
64 | if a then
65 | a.geom = object:as_polygon():pole_of_inaccessibility()
66 | themepark:insert('addresses', a, object.tags)
67 | end
68 | end)
69 |
70 | themepark:add_proc('relation', function(object, data)
71 | -- Shortbread spec: Ignore addresses that are already in "pois" layer.
72 | if data.shortbread_in_pois then
73 | return
74 | end
75 |
76 | local a = process(object.tags)
77 | if a then
78 | for sgeom in object:as_multipolygon():geometries() do
79 | a.geom = sgeom:pole_of_inaccessibility()
80 | themepark:insert('addresses', a, object.tags)
81 | end
82 | end
83 | end)
84 |
85 | -- ---------------------------------------------------------------------------
86 |
--------------------------------------------------------------------------------
/themes/shortbread/topics/aerialways.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Theme: shortbread_v1
4 | -- Topic: aerialways
5 | --
6 | -- ---------------------------------------------------------------------------
7 |
8 | local themepark, theme, cfg = ...
9 | local expire = require('expire')
10 | local aerialway_values = { 'cable_car', 'gondola', 'goods', 'chair_lift',
11 | 'drag_lift', 't-bar', 'j-bar', 'platter', 'rope_tow' }
12 |
13 | local tags = {}
14 |
15 | for _, value in ipairs(aerialway_values) do
16 | table.insert(tags, { key = 'aerialway', value = value, on = 'w' })
17 | end
18 |
19 | themepark:add_table{
20 | name = 'aerialways',
21 | ids_type = 'way',
22 | geom = 'linestring',
23 | columns = themepark:columns({
24 | { column = 'kind', type = 'text', not_null = true },
25 | }),
26 | tags = tags,
27 | tiles = {
28 | minzoom = 12,
29 | },
30 | expire = expire.shortbread(12, 14, 'aerialways', 'full-area')
31 | }
32 |
33 | -- ---------------------------------------------------------------------------
34 |
35 | local get_aerialway_value = osm2pgsql.make_check_values_func(aerialway_values)
36 |
37 | -- ---------------------------------------------------------------------------
38 |
39 | themepark:add_proc('way', function(object, data)
40 | local t = object.tags
41 |
42 | local aerialway = get_aerialway_value(t.aerialway)
43 | if aerialway then
44 | local a = {
45 | kind = aerialway,
46 | geom = object:as_linestring()
47 | }
48 |
49 | themepark:insert('aerialways', a, t)
50 | end
51 | end)
52 |
53 | -- ---------------------------------------------------------------------------
54 |
--------------------------------------------------------------------------------
/themes/shortbread/topics/boundaries.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Theme: shortbread_v1
4 | -- Topic: boundaries
5 | --
6 | -- ---------------------------------------------------------------------------
7 |
8 | local themepark, theme, cfg = ...
9 | local expire = require('expire')
10 |
11 | themepark:add_table{
12 | name = 'boundaries',
13 | ids_type = 'way',
14 | geom = 'linestring',
15 | columns = themepark:columns({
16 | { column = 'admin_level', type = 'int', not_null = true },
17 | { column = 'maritime', type = 'bool' },
18 | { column = 'disputed', type = 'bool' },
19 | }),
20 | tags = {
21 | { key = 'admin_level', values = { '2', '4' }, on = 'r' },
22 | { key = 'boundary', values = { 'administrative', 'disputed' }, on = 'r' },
23 | { key = 'disputed', value = 'yes', on = 'w' },
24 | { key = 'maritime', value = 'yes', on = 'w' },
25 | { key = 'natural', value = 'coastline', on = 'w' },
26 | { key = 'type', value = 'boundary', on = 'r' },
27 | },
28 | tiles = {
29 | minzoom = 2
30 | },
31 | expire = expire.shortbread(0, 14, 'boundaries', 'boundary-only')
32 | }
33 |
34 | local rinfos = {}
35 |
36 | -- ---------------------------------------------------------------------------
37 |
38 | -- Check the (string) admin level. Change this depending on which admin
39 | -- levels you want to process. Shortbread only shows 2 and 4.
40 | -- valid values must work with tonumber!
41 | local function valid_admin_level(level)
42 | return level == '2' or level == '4'
43 | end
44 |
45 | -- Check if this looks like a boundary and return admin_level as number
46 | -- Return nil if this is not a valid administrative boundary.
47 | local function get_admin_level(tags)
48 | local type = tags.type
49 |
50 | if type == 'boundary' or type == 'multipolygon' then
51 | local boundary = tags.boundary
52 | if boundary == 'administrative' and valid_admin_level(tags.admin_level) then
53 | return tonumber(tags.admin_level)
54 | end
55 | end
56 | end
57 |
58 |
59 | local function valid_disputed(tags)
60 | local type = tags.type
61 | return (type == 'boundary' or type == 'multipolygon') and tags.boundary == 'disputed'
62 | end
63 |
64 | -- ---------------------------------------------------------------------------
65 |
66 | themepark:add_proc('way', function(object, data)
67 | if osm2pgsql.stage == 1 then
68 | return
69 | end
70 |
71 | local info = rinfos[object.id]
72 | if not info then
73 | return
74 | end
75 |
76 | local t = object.tags
77 | if not info.admin_level then
78 | return
79 | end
80 | local a = {
81 | admin_level = info.admin_level,
82 | maritime = (t.maritime and (t.maritime == 'yes' or t.natural == 'coastline')),
83 | disputed = info.disputed or (t.disputed and t.disputed == 'yes'),
84 | geom = object:as_linestring()
85 | }
86 | themepark.themes.core.add_name(a, object)
87 | themepark:insert('boundaries', a, t)
88 | end)
89 |
90 | themepark:add_proc('select_relation_members', function(relation)
91 | -- It isn't necessary to process boundary=disputed relations separately because
92 | -- if they have an admin_level from another relation they will get added anyways.
93 | if valid_admin_level(relation.tags.admin_level) then
94 | return { ways = osm2pgsql.way_member_ids(relation) }
95 | end
96 | end)
97 |
98 | themepark:add_proc('relation', function(object, data)
99 | local t = object.tags
100 |
101 | local admin_level = get_admin_level(t)
102 | local disputed = valid_disputed(t)
103 | -- If a relation is not an admin boundary or disputed boundary it has
104 | -- nothing to tell us and we don't need the ways.
105 | if not admin_level and not disputed then
106 | return
107 | end
108 |
109 | for _, member in ipairs(object.members) do
110 | if member.type == 'w' then
111 | if not rinfos[member.ref] then
112 | rinfos[member.ref] = { disputed = false }
113 | end
114 | if admin_level ~= nil and
115 | (rinfos[member.ref].admin_level == nil or rinfos[member.ref].admin_level > admin_level) then
116 | rinfos[member.ref].admin_level = admin_level
117 | end
118 | rinfos[member.ref].disputed = disputed or rinfos[member.ref].disputed
119 | end
120 | end
121 | end)
122 |
123 | -- ---------------------------------------------------------------------------
124 |
--------------------------------------------------------------------------------
/themes/shortbread/topics/boundary_labels.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Theme: shortbread_v1
4 | -- Topic: boundaries
5 | --
6 | -- ---------------------------------------------------------------------------
7 |
8 | local themepark, theme, cfg = ...
9 | local expire = require('expire')
10 |
11 | themepark:add_table{
12 | name = 'boundary_labels',
13 | ids_type = 'relation',
14 | geom = 'point',
15 | columns = themepark:columns('core/name', {
16 | { column = 'admin_level', type = 'int' },
17 | { column = 'way_area', type = 'real' },
18 | { column = 'minzoom', type = 'int', tiles = 'minzoom' },
19 | }),
20 | tags = {
21 | { key = 'admin_level', values = { '2', '4' }, on = 'r' },
22 | { key = 'boundary', value = 'administrative', on = 'r' },
23 | },
24 | expire = expire.shortbread(0, 14, 'boundary_labels', 'full-area')
25 | }
26 |
27 | -- ---------------------------------------------------------------------------
28 |
29 | themepark:add_proc('relation', function(object, data)
30 | local t = object.tags
31 | if t.boundary == 'administrative' then
32 | local admin_level = tonumber(t.admin_level)
33 | if admin_level == nil or admin_level > 4 or admin_level < 2 or admin_level == 3 then
34 | return
35 | end
36 |
37 | local mgeom = object:as_multipolygon()
38 |
39 | if mgeom then
40 | local a = { admin_level = admin_level }
41 |
42 | themepark.themes.core.add_name(a, object)
43 |
44 | local best_geom
45 | local best_area = 0
46 | for sgeom in mgeom:geometries() do
47 | local this_area = sgeom:spherical_area()
48 | if this_area > best_area then
49 | best_area = this_area
50 | best_geom = sgeom
51 | end
52 | end
53 |
54 | if best_geom then
55 | a.way_area = best_area
56 |
57 | if admin_level == 2 then
58 | if a.way_area > 2000000 then
59 | a.minzoom = 2
60 | elseif a.way_area > 700000 then
61 | a.minzoom = 3
62 | elseif a.way_area > 100000 then
63 | a.minzoom = 4
64 | else
65 | a.minzoom = 5
66 | end
67 | else -- == 4
68 | if a.way_area > 700000 then
69 | a.minzoom = 3
70 | elseif a.way_area > 100000 then
71 | a.minzoom = 4
72 | else
73 | a.minzoom = 5
74 | end
75 | end
76 |
77 | a.geom = best_geom:transform(3857):pole_of_inaccessibility()
78 | themepark:insert('boundary_labels', a, t)
79 | end
80 | end
81 | end
82 | end)
83 |
84 | -- ---------------------------------------------------------------------------
85 |
--------------------------------------------------------------------------------
/themes/shortbread/topics/bridges.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Theme: shortbread_v1
4 | -- Topic: bridges
5 | --
6 | -- ---------------------------------------------------------------------------
7 |
8 | local themepark, theme, cfg = ...
9 | local expire = require('expire')
10 |
11 | themepark:add_table{
12 | name = 'bridges',
13 | ids_type = 'area',
14 | geom = 'polygon',
15 | columns = themepark:columns({
16 | { column = 'kind', type = 'text', not_null = true },
17 | { column = 'layer', type = 'int2', tiles = false },
18 | }),
19 | tags = {
20 | { key = 'man_made', value = 'bridge', on = 'a' },
21 | },
22 | tiles = {
23 | minzoom = 12,
24 | },
25 | expire = expire.shortbread(12, 14, 'bridges', 'full-area')
26 | }
27 |
28 | -- ---------------------------------------------------------------------------
29 |
30 | themepark:add_proc('area', function(object, data)
31 | local t = object.tags
32 |
33 | if t.man_made == 'bridge' then
34 | local a = {
35 | kind = 'bridge',
36 | layer = data.core.layer
37 | }
38 |
39 | for sgeom in object:as_area():geometries() do
40 | a.geom = sgeom
41 | themepark:insert('bridges', a, t)
42 | end
43 | end
44 | end)
45 |
46 | -- ---------------------------------------------------------------------------
47 |
--------------------------------------------------------------------------------
/themes/shortbread/topics/dams.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Theme: shortbread_v1
4 | -- Topic: dams
5 | --
6 | -- ---------------------------------------------------------------------------
7 |
8 | local themepark, theme, cfg = ...
9 | local expire = require('expire')
10 |
11 | themepark:add_table{
12 | name = 'dam_lines',
13 | ids_type = 'way',
14 | geom = 'linestring',
15 | columns = themepark:columns({
16 | { column = 'kind', type = 'text', not_null = true },
17 | }),
18 | tags = {
19 | { key = 'waterway', value = 'dam', on = 'w' },
20 | },
21 | tiles = {
22 | minzoom = 12,
23 | },
24 | expire = expire.shortbread(14, 14, 'dam_lines', 'full-area')
25 | }
26 |
27 | themepark:add_table{
28 | name = 'dam_polygons',
29 | ids_type = 'way',
30 | geom = 'polygon',
31 | columns = themepark:columns({
32 | { column = 'kind', type = 'text', not_null = true },
33 | }),
34 | tags = {
35 | { key = 'waterway', value = 'dam', on = 'a' },
36 | },
37 | tiles = {
38 | minzoom = 12,
39 | },
40 | expire = expire.shortbread(12, 14, 'dam_polygons', 'full-area')
41 | }
42 |
43 | -- ---------------------------------------------------------------------------
44 |
45 | themepark:add_proc('way', function(object, data)
46 | if object.is_closed then
47 | return
48 | end
49 |
50 | local t = object.tags
51 | local waterway = t.waterway
52 |
53 | if waterway == 'dam' then
54 | local a = { kind = waterway }
55 | a.geom = object:as_linestring()
56 | themepark:insert('dam_lines', a, t)
57 | end
58 | end)
59 |
60 | themepark:add_proc('area', function(object, data)
61 | local t = object.tags
62 | local waterway = t.waterway
63 |
64 | if waterway == 'dam' then
65 | local a = { kind = waterway }
66 |
67 | for sgeom in object:as_area():geometries() do
68 | a.geom = sgeom
69 | themepark:insert('dam_polygons', a, t)
70 | end
71 | end
72 | end)
73 |
74 | -- ---------------------------------------------------------------------------
75 |
--------------------------------------------------------------------------------
/themes/shortbread/topics/ferries.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Theme: shortbread_v1
4 | -- Topic: ferries
5 | --
6 | -- ---------------------------------------------------------------------------
7 |
8 | local themepark, theme, cfg = ...
9 | local expire = require('expire')
10 |
11 | themepark:add_table{
12 | name = 'ferries',
13 | ids_type = 'way',
14 | geom = 'linestring',
15 | columns = themepark:columns('core/name', {
16 | { column = 'kind', type = 'text', not_null = true },
17 | { column = 'minzoom', type = 'int', tiles = 'minzoom' },
18 | }),
19 | tags = {
20 | { key = 'route', value = 'ferry', on = 'w' },
21 | { key = 'motor_vehicle', on = 'w' },
22 | },
23 | tiles = {
24 | minzoom = 10,
25 | },
26 | expire = expire.shortbread(10, 14, 'ferries', 'full-area')
27 | }
28 |
29 | -- ---------------------------------------------------------------------------
30 |
31 | themepark:add_proc('way', function(object, data)
32 | local t = object.tags
33 |
34 | if t.route == 'ferry' then
35 | local a = {
36 | kind = 'ferry',
37 | geom = object:as_linestring()
38 | }
39 |
40 | if t.motor_vehicle and t.motor_vehicle ~= 'no' then
41 | a.minzoom = 10
42 | else
43 | a.minzoom = 12
44 | end
45 |
46 | themepark.themes.core.add_name(a, object)
47 | themepark:insert('ferries', a, t)
48 | end
49 | end)
50 |
51 | -- ---------------------------------------------------------------------------
52 |
--------------------------------------------------------------------------------
/themes/shortbread/topics/land.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Theme: shortbread_v1
4 | -- Topic: land
5 | --
6 | -- ---------------------------------------------------------------------------
7 |
8 | local themepark, theme, cfg = ...
9 | local expire = require('expire')
10 |
11 | -- ---------------------------------------------------------------------------
12 |
13 | local landuse_lookup = {
14 | forest = 7,
15 | grass = 11,
16 | meadow = 11,
17 | orchard = 11,
18 | vineyard = 11,
19 | allotments = 11,
20 | cemetery = 13,
21 |
22 | village_green = 11,
23 | recreation_ground = 11,
24 | greenhouse_horticulture = 11,
25 | plant_nursery = 11,
26 |
27 | residential = 10,
28 | industrial = 10,
29 | commercial = 10,
30 | garages = 10,
31 | retail = 10,
32 | railway = 10,
33 | landfill = 10,
34 |
35 | quarry = 11,
36 |
37 | brownfield = 10,
38 | greenfield = 10,
39 | farmyard = 10,
40 | farmland = 10,
41 | }
42 |
43 | local natural_lookup = {
44 | sand = 10,
45 | beach = 10,
46 | heath = 11,
47 | scrub = 11,
48 | grassland = 11,
49 | bare_rock = 11,
50 | scree = 11,
51 | shingle = 11,
52 | }
53 |
54 | local wetland_values = { "swamp", "bog", "string_bog", "wet_meadow", "marsh" }
55 |
56 | local leisure_values = { "golf_course", "park", "garden", "playground", "miniature_golf" }
57 |
58 | -- ---------------------------------------------------------------------------
59 |
60 | local landuse_values = {}
61 | for k, _ in pairs(landuse_lookup) do
62 | table.insert(landuse_values, k)
63 | end
64 |
65 | local natural_values = {}
66 | for k, _ in pairs(natural_lookup) do
67 | table.insert(natural_values, k)
68 | end
69 |
70 | local check_wetland = osm2pgsql.make_check_values_func(wetland_values)
71 |
72 | local check_leisure = osm2pgsql.make_check_values_func(leisure_values)
73 |
74 | -- ---------------------------------------------------------------------------
75 |
76 | themepark:add_table{
77 | name = 'land',
78 | ids_type = 'area',
79 | geom = 'geometry',
80 | columns = themepark:columns({
81 | { column = 'kind', type = 'text', not_null = true },
82 | { column = 'minzoom', type = 'int', not_null = true, tiles = 'minzoom' },
83 | }),
84 | tags = {
85 | { key = 'landuse', values = landuse_values, on = 'a' },
86 | { key = 'leisure', values = leisure_values, on = 'a' },
87 | { key = 'natural', values = natural_values, on = 'a' },
88 | { key = 'wetland', values = wetland_values, on = 'a' },
89 | },
90 | tiles = {
91 | minzoom = 7
92 | },
93 | expire = expire.shortbread(7, 14, 'land', 'full-area')
94 | }
95 |
96 | -- ---------------------------------------------------------------------------
97 |
98 | themepark:add_proc('area', function(object, data)
99 | local t = object.tags
100 | local a = { geom = object:as_area() }
101 |
102 | local minzoom = landuse_lookup[t.landuse]
103 | if minzoom then
104 | a.kind = t.landuse
105 | a.minzoom = minzoom
106 | elseif t.natural == 'wood' then
107 | a.kind = 'forest'
108 | a.minzoom = 7
109 | elseif t.amenity == 'grave_yard' then
110 | a.kind = 'grave_yard'
111 | a.minzoom = 13
112 | else
113 | minzoom = natural_lookup[t.natural]
114 | if minzoom then
115 | a.kind = t.natural
116 | a.minzoom = minzoom
117 | else
118 | local wetland = check_wetland(t.wetland)
119 | if wetland then
120 | a.kind = wetland
121 | a.minzoom = 11
122 | else
123 | local leisure = check_leisure(t.leisure)
124 | if leisure then
125 | a.kind = leisure
126 | a.minzoom = 11
127 | end
128 | end
129 | end
130 | end
131 |
132 | if a.kind then
133 | themepark:insert('land', a, t)
134 | end
135 | end)
136 |
137 | -- ---------------------------------------------------------------------------
138 |
--------------------------------------------------------------------------------
/themes/shortbread/topics/piers.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Theme: shortbread_v1
4 | -- Topic: piers
5 | --
6 | -- ---------------------------------------------------------------------------
7 |
8 | local themepark, theme, cfg = ...
9 | local expire = require('expire')
10 |
11 | local man_made_values = { 'pier', 'breakwater', 'groyne' }
12 |
13 | themepark:add_table{
14 | name = 'pier_lines',
15 | ids_type = 'way',
16 | geom = 'linestring',
17 | columns = themepark:columns({
18 | { column = 'kind', type = 'text', not_null = true },
19 | }),
20 | tags = {
21 | { key = 'man_made', values = man_made_values, on = 'w' },
22 | },
23 | tiles = {
24 | minzoom = 12,
25 | },
26 | expire = expire.shortbread(12, 14, 'pier_lines', 'full-area')
27 | }
28 |
29 | themepark:add_table{
30 | name = 'pier_polygons',
31 | ids_type = 'way',
32 | geom = 'polygon',
33 | columns = themepark:columns({
34 | { column = 'kind', type = 'text', not_null = true },
35 | }),
36 | tags = {
37 | { key = 'man_made', values = man_made_values, on = 'a' },
38 | },
39 | tiles = {
40 | minzoom = 12,
41 | },
42 | expire = expire.shortbread(12, 14, 'pier_polygons', 'full-area')
43 | }
44 |
45 | -- ---------------------------------------------------------------------------
46 |
47 | themepark:add_proc('way', function(object, data)
48 | if object.is_closed then
49 | return
50 | end
51 |
52 | local t = object.tags
53 | local man_made = t.man_made
54 |
55 | if man_made == 'pier' or man_made == 'breakwater' or man_made == 'groyne' then
56 | local a = { kind = man_made }
57 | a.geom = object:as_linestring()
58 | themepark:insert('pier_lines', a, t)
59 | end
60 | end)
61 |
62 | themepark:add_proc('area', function(object, data)
63 | local t = object.tags
64 | local man_made = t.man_made
65 |
66 | if man_made == 'pier' or man_made == 'breakwater' or man_made == 'groyne' then
67 | local a = { kind = man_made }
68 |
69 | for sgeom in object:as_area():geometries() do
70 | a.geom = sgeom
71 | themepark:insert('pier_polygons', a, t)
72 | end
73 | end
74 | end)
75 |
76 | -- ---------------------------------------------------------------------------
77 |
--------------------------------------------------------------------------------
/themes/shortbread/topics/places.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Theme: shortbread_v1
4 | -- Topic: places
5 | --
6 | -- ---------------------------------------------------------------------------
7 |
8 | local themepark, theme, cfg = ...
9 | local expire = require('expire')
10 |
11 | -- ---------------------------------------------------------------------------
12 |
13 | local place_types = {
14 | city = { pop = 100000, minzoom = 6 },
15 | town = { pop = 5000, minzoom = 7 },
16 | village = { pop = 100, minzoom = 10 },
17 | hamlet = { pop = 10, minzoom = 10 },
18 | suburb = { pop = 1000, minzoom = 10 },
19 | quarter = { pop = 500, minzoom = 10 },
20 | neighborhood = { pop = 100, minzoom = 10 },
21 | isolated_dwelling = { pop = 5, minzoom = 10 },
22 | farm = { pop = 5, minzoom = 10 },
23 | island = { pop = 0, minzoom = 10 },
24 | locality = { pop = 0, minzoom = 10 },
25 | }
26 |
27 | -- ---------------------------------------------------------------------------
28 |
29 | local place_values = {}
30 |
31 | for key, _ in pairs(place_types) do
32 | table.insert(place_values, key)
33 | end
34 |
35 | themepark:add_table{
36 | name = 'place_labels',
37 | ids_type = 'any',
38 | geom = 'point',
39 | columns = themepark:columns('core/name', {
40 | { column = 'kind', type = 'text', not_null = true },
41 | { column = 'population', type = 'int', not_null = true },
42 | { column = 'minzoom', type = 'int', not_null = true, tiles = 'minzoom' },
43 | }),
44 | tags = {
45 | { key = 'capital', on = 'n' },
46 | { key = 'place', on = 'n' },
47 | { key = 'population', on = 'n' },
48 | },
49 | tiles = {
50 | minzoom = 4,
51 | order_by = 'population',
52 | order_dir = 'desc',
53 | },
54 | expire = expire.shortbread(4, 14, 'place_labels', 'full-area')
55 | }
56 |
57 | -- ---------------------------------------------------------------------------
58 |
59 | local function place_columns(tags)
60 | if not tags.place then
61 | return nil
62 | end
63 |
64 | local place_type = place_types[tags.place]
65 | if not place_type then
66 | return nil
67 | end
68 |
69 | local attributes = {
70 | kind = tags.place,
71 | population = tonumber(tags.population) or place_type.pop,
72 | minzoom = place_type.minzoom
73 | }
74 |
75 | if tags.capital == 'yes' then
76 | if tags.place == 'city' or tags.place == 'town' or tags.place == 'village' or tags.place == 'hamlet' then
77 | attributes.kind = 'capital'
78 | attributes.minzoom = 4
79 | end
80 | elseif tags.capital == '4' then
81 | if tags.place == 'city' or tags.place == 'town' or tags.place == 'village' or tags.place == 'hamlet' then
82 | attributes.kind = 'state_capital'
83 | attributes.minzoom = 4
84 | end
85 | end
86 | return attributes
87 | end
88 |
89 | themepark:add_proc('node', function(object, data)
90 | local a = place_columns(object.tags)
91 | if not a then
92 | return
93 | end
94 |
95 | a.geom = object:as_point()
96 |
97 | themepark.themes.core.add_name(a, object)
98 | themepark:insert('place_labels', a, tags)
99 | end)
100 |
101 | themepark:add_proc('area', function(object, data)
102 | local a = place_columns(object.tags)
103 | if not a then
104 | return
105 | end
106 |
107 | a.geom = object:as_area():transform(3857):pole_of_inaccessibility()
108 |
109 | themepark.themes.core.add_name(a, object)
110 | themepark:insert('place_labels', a, tags)
111 | end)
112 | -- ---------------------------------------------------------------------------
113 |
--------------------------------------------------------------------------------
/themes/shortbread/topics/pois.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Theme: shortbread_v1
4 | -- Topic: pois
5 | --
6 | -- ---------------------------------------------------------------------------
7 |
8 | local themepark, theme, cfg = ...
9 | local expire = require('expire')
10 |
11 | themepark:add_table{
12 | name = 'pois',
13 | ids_type = 'any',
14 | geom = 'point',
15 | columns = themepark:columns('core/name', {
16 | { column = 'amenity', type = 'text' },
17 | { column = 'leisure', type = 'text' },
18 | { column = 'tourism', type = 'text' },
19 | { column = 'shop', type = 'text' },
20 | { column = 'man_made', type = 'text' },
21 | { column = 'historic', type = 'text' },
22 | { column = 'emergency', type = 'text' },
23 | { column = 'highway', type = 'text' },
24 | { column = 'office', type = 'text' },
25 | { column = 'housename', type = 'text' },
26 | { column = 'housenumber', type = 'text' },
27 | { column = 'cuisine', type = 'text' },
28 | { column = 'sport', type = 'text' },
29 | { column = 'vending', type = 'text' },
30 | { column = 'information', type = 'text' },
31 | { column = 'tower:type', type = 'text' },
32 | { column = 'religion', type = 'text' },
33 | { column = 'denomination', type = 'text' },
34 | { column = 'recycling:glass_bottles', type = 'bool' },
35 | { column = 'recycling:paper', type = 'bool' },
36 | { column = 'recycling:clothes', type = 'bool' },
37 | { column = 'recycling:scrap_metal', type = 'bool' },
38 | { column = 'atm', type = 'bool' },
39 | }),
40 | tags = {
41 | },
42 | tiles = {
43 | minzoom = 14,
44 | },
45 | expire = expire.shortbread(14, 14, 'pois', 'full-area')
46 |
47 | }
48 |
49 | -- ---------------------------------------------------------------------------
50 |
51 | local get_value = {}
52 |
53 | get_value.amenity = osm2pgsql.make_check_values_func({
54 | 'police', 'fire_station', 'post_box', 'post_office', 'telephone', 'library',
55 | 'townhall', 'courthouse', 'prison', 'embassy', 'community_centre',
56 | 'nursing_home', 'arts_centre', 'grave_yard', 'marketplace', 'recycling',
57 | 'university', 'school', 'college', 'public_building', 'pharmacy',
58 | 'hospital', 'clinic', 'doctors', 'dentist', 'veterinary', 'theatre',
59 | 'nightclub', 'cinema', 'restaurant', 'fast_food', 'cafe', 'pub', 'bar',
60 | 'food_court', 'biergarten', 'shelter', 'car_rental', 'car_wash',
61 | 'car_sharing', 'bicycle_rental', 'vending_machine', 'bank', 'atm',
62 | 'toilets', 'bench', 'drinking_water', 'fountain', 'hunting_stand',
63 | 'waste_basket', 'place_of_worship', 'playground', 'dog_park'
64 | })
65 |
66 | get_value.leisure = osm2pgsql.make_check_values_func({
67 | 'sports_centre', 'pitch', 'swimming_pool', 'water_park', 'golf_course',
68 | 'stadium', 'ice_rink',
69 | })
70 |
71 | get_value.tourism = osm2pgsql.make_check_values_func({
72 | 'hotel', 'motel', 'bed_and_breakfast', 'guest_house', 'hostel', 'chalet',
73 | 'camp_site', 'alpine_hut', 'caravan_site', 'information', 'picnic_site',
74 | 'viewpoint', 'zoo', 'theme_park',
75 | })
76 |
77 | get_value.shop = osm2pgsql.make_check_values_func({
78 | 'supermarket', 'bakery', 'kiosk', 'mall', 'department_store', 'general',
79 | 'convenience', 'clothes', 'florist', 'chemist', 'books', 'butcher',
80 | 'shoes', 'alcohol', 'beverages', 'optician', 'jewelry', 'gift', 'sports',
81 | 'stationery', 'outdoor', 'mobile_phone', 'toys', 'newsagent', 'greengrocer',
82 | 'beauty', 'video', 'car', 'bicycle', 'doityourself', 'hardware',
83 | 'furniture', 'computer', 'garden_centre', 'hairdresser', 'travel_agency',
84 | 'laundry', 'dry_cleaning',
85 | })
86 |
87 | get_value.man_made = osm2pgsql.make_check_values_func({
88 | 'surveillance', 'tower', 'windmill', 'lighthouse', 'wastewater_plant',
89 | 'water_well', 'watermill', 'water_works',
90 | })
91 |
92 | get_value.historic = osm2pgsql.make_check_values_func({
93 | 'monument', 'memorial', 'artwork', 'castle', 'ruins', 'archaelogical_site',
94 | 'wayside_cross', 'wayside_shrine', 'battlefield', 'fort',
95 | })
96 |
97 | get_value.emergency = osm2pgsql.make_check_values_func({
98 | 'phone', 'fire_hydrant', 'defibrillator'
99 | })
100 |
101 | get_value.highway = osm2pgsql.make_check_values_func({
102 | 'emergency_access_point'
103 | })
104 |
105 | get_value.office = osm2pgsql.make_check_values_func({
106 | 'diplomatic'
107 | })
108 |
109 | -- ---------------------------------------------------------------------------
110 |
111 | local add_extra_attributes = {}
112 |
113 | add_extra_attributes.amenity = function(a, t)
114 | if t.amenity == 'vending_machine' then
115 | a.vending = t.vending
116 | elseif t.amenity == 'place_of_worship' then
117 | a.religion = t.religion
118 | a.denomination = t.denomination
119 | elseif t.amenity == 'restaurant' or t.amenity == 'fast_food' or
120 | t.amenity == 'pub' or t.amenity == 'bar' or t.amenity == 'cafe' then
121 | a.cuisine = t.cuisine
122 | elseif t.amenity == 'recycling' then
123 | a['recycling:glass_bottles'] = t['recycling:glass_bottles'] == 'yes'
124 | a['recycling:paper'] = t['recycling:paper'] == 'yes'
125 | a['recycling:clothes'] = t['recycling:clothes'] == 'yes'
126 | a['recycling:scrap_metal'] = t['recycling:scrap_metal'] == 'yes'
127 | elseif t.amenity == 'bank' then
128 | a.atm = t.atm == 'yes'
129 | end
130 | end
131 |
132 | add_extra_attributes.tourism = function(a, t)
133 | if t.tourism == 'information' then
134 | a.information = t.information
135 | end
136 | end
137 |
138 | add_extra_attributes.man_made = function(a, t)
139 | if t.man_made == 'tower' then
140 | a['tower:type'] = t['tower:type']
141 | end
142 | end
143 |
144 | -- ---------------------------------------------------------------------------
145 |
146 | local get_attributes = function(object)
147 | local t = object.tags
148 | local a = {}
149 |
150 | local is_poi = false
151 | for _, k in ipairs({'amenity', 'leisure', 'tourism', 'shop', 'man_made',
152 | 'historic', 'emergency', 'highway', 'office'}) do
153 | local v = get_value[k](t[k])
154 | if v then
155 | a[k] = v
156 | if add_extra_attributes[k] then
157 | add_extra_attributes[k](a, t)
158 | end
159 | is_poi = true
160 | end
161 | end
162 |
163 | if not is_poi then
164 | return nil
165 | end
166 |
167 | a.housename = t['addr:housename']
168 | a.housenumber = t['addr:housenumber']
169 |
170 | themepark.themes.core.add_name(a, object)
171 | themepark:add_debug_info(a, t)
172 |
173 | return a
174 | end
175 |
176 | -- ---------------------------------------------------------------------------
177 |
178 | themepark:add_proc('node', function(object, data)
179 | local a = get_attributes(object)
180 | if a then
181 | a.geom = object:as_point()
182 | themepark:insert('pois', a)
183 | data.shortbread_in_pois = true
184 | end
185 | end)
186 |
187 | themepark:add_proc('area', function(object, data)
188 | local a = get_attributes(object)
189 | if a then
190 | a.geom = object:as_area():centroid()
191 | themepark:insert('pois', a)
192 | data.shortbread_in_pois = true
193 | end
194 | end)
195 |
196 | -- ---------------------------------------------------------------------------
197 |
--------------------------------------------------------------------------------
/themes/shortbread/topics/public_transport.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Theme: shortbread_v1
4 | -- Topic: public_transport
5 | --
6 | -- ---------------------------------------------------------------------------
7 |
8 | local themepark, theme, cfg = ...
9 | local expire = require('expire')
10 |
11 | themepark:add_table{
12 | name = 'public_transport',
13 | ids_type = 'any',
14 | geom = 'point',
15 | columns = themepark:columns('core/name', {
16 | { column = 'kind', type = 'text', not_null = true },
17 | { column = 'minzoom', type = 'int', tiles = 'minzoom' }
18 | }),
19 | tags = {
20 | { key = 'aerialway', value = 'station', on = 'na' },
21 | { key = 'aeroway', values = { 'aerodrome', 'helipad' }, on = 'na' },
22 | { key = 'amenity', values = { 'ferry_terminal', 'bus_station' }, on = 'na' },
23 | { key = 'highway', value = 'bus_stop', on = 'na' },
24 | { key = 'railway', values = { 'station', 'halt', 'tram_stop' }, on = 'na' },
25 | },
26 | tiles = {
27 | minzoom = 11,
28 | },
29 | expire = expire.shortbread(11, 14, 'public_transport', 'full-area')
30 | }
31 |
32 | -- ---------------------------------------------------------------------------
33 |
34 | local get_attributes = function(object)
35 | local t = object.tags
36 | local a = {}
37 |
38 | if t.aeroway then
39 | if t.aeroway == 'aerodrome' then
40 | a.kind = 'aerodrome'
41 | a.minzoom = 11
42 | elseif t.aeroway == 'helipad' then
43 | a.kind = 'helipad'
44 | a.minzoom = 13
45 | else
46 | return nil
47 | end
48 | elseif t.railway then
49 | if t.railway == 'station' then
50 | a.kind = 'station'
51 | a.minzoom = 13
52 | elseif t.railway == 'halt' then
53 | a.kind = 'halt'
54 | a.minzoom = 13
55 | elseif t.railway == 'tram_stop' then
56 | a.kind = 'tram_stop'
57 | a.minzoom = 14
58 | else
59 | return nil
60 | end
61 | elseif t.amenity then
62 | if t.amenity == 'bus_station' then
63 | a.kind = 'bus_station'
64 | a.minzoom = 13
65 | elseif t.amenity == 'ferry_terminal' then
66 | a.kind = 'ferry_terminal'
67 | a.minzoom = 12
68 | else
69 | return nil
70 | end
71 | elseif t.highway and t.highway == 'bus_stop' then
72 | a.kind = 'bus_stop'
73 | a.minzoom = 14
74 | elseif t.aerialway and t.aerialway == 'station' then
75 | a.kind = 'aerialway_station'
76 | a.minzoom = 13
77 | else
78 | return nil
79 | end
80 |
81 | themepark.themes.core.add_name(a, object)
82 |
83 | return a
84 | end
85 |
86 | -- ---------------------------------------------------------------------------
87 |
88 | themepark:add_proc('node', function(object, data)
89 | local a = get_attributes(object)
90 | if a then
91 | a.geom = object:as_point()
92 | themepark:insert('public_transport', a, object.tags)
93 | end
94 | end)
95 |
96 | themepark:add_proc('area', function(object, data)
97 | local a = get_attributes(object)
98 | if a then
99 | a.geom = object:as_area():centroid()
100 | themepark:insert('public_transport', a, object.tags)
101 | end
102 | end)
103 |
104 | -- ---------------------------------------------------------------------------
105 |
--------------------------------------------------------------------------------
/themes/shortbread/topics/sites.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Theme: shortbread_v1
4 | -- Topic: sites
5 | --
6 | -- ---------------------------------------------------------------------------
7 |
8 | local themepark, theme, cfg = ...
9 | local expire = require('expire')
10 |
11 | -- ---------------------------------------------------------------------------
12 |
13 | local amenity_values = { 'university', 'college', 'school', 'hospital',
14 | 'prison', 'parking', 'bicycle_parking' }
15 |
16 | -- ---------------------------------------------------------------------------
17 |
18 | themepark:add_table{
19 | name = 'sites',
20 | ids_type = 'area',
21 | geom = 'multipolygon',
22 | columns = themepark:columns('core/name', {
23 | { column = 'kind', type = 'text', not_null = true },
24 | }),
25 | tags = {
26 | { key = 'amenity', values = amenity_values, on = 'a' },
27 | { key = 'landuse', value = 'construction', on = 'a' },
28 | { key = 'leisure', value = 'sports_centre', on = 'a' },
29 | { key = 'military', value = 'danger_area', on = 'a' },
30 | },
31 | tiles = {
32 | minzoom = 14,
33 | },
34 | expire = expire.shortbread(14, 14, 'sites', 'full-area')
35 | }
36 |
37 | -- ---------------------------------------------------------------------------
38 |
39 | local get_amenity_value = osm2pgsql.make_check_values_func(amenity_values)
40 |
41 | -- ---------------------------------------------------------------------------
42 |
43 | themepark:add_proc('area', function(object, data)
44 | local t = object.tags
45 | local a = {
46 | kind = get_amenity_value(t.amenity)
47 | }
48 |
49 | if not a.kind then
50 | if t.military == 'danger_area' then
51 | a.kind = 'danger_area'
52 | elseif t.leisure == 'sports_centre' then
53 | a.kind = 'sports_centre'
54 | elseif t.landuse == 'construction' then
55 | a.kind = 'construction'
56 | else
57 | return
58 | end
59 | end
60 |
61 | a.geom = object:as_area()
62 | themepark.themes.core.add_name(a, object)
63 | themepark:insert('sites', a, t)
64 | end)
65 |
66 | -- ---------------------------------------------------------------------------
67 |
--------------------------------------------------------------------------------
/themes/shortbread/topics/streets.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Theme: shortbread_v1
4 | -- Topic: streets
5 | --
6 | -- ---------------------------------------------------------------------------
7 |
8 | local themepark, theme, cfg = ...
9 | local expire = require('expire')
10 |
11 | themepark:add_table{
12 | name = 'street_polygons',
13 | ids_type = 'way',
14 | geom = 'polygon',
15 | columns = themepark:columns({
16 | { column = 'kind', type = 'text', not_null = true },
17 | { column = 'rail', type = 'bool' },
18 | { column = 'tunnel', type = 'bool' },
19 | { column = 'bridge', type = 'bool' },
20 | { column = 'surface', type = 'text' },
21 | { column = 'z_order', type = 'int' },
22 | }),
23 | tiles = {
24 | minzoom = 11,
25 | order_by = 'z_order',
26 | order_dir = 'desc',
27 | },
28 | expire = expire.shortbread(11, 14, 'street_polygons', 'full-area')
29 | }
30 |
31 | themepark:add_table{
32 | name = 'streets_polygons_labels',
33 | ids_type = 'area',
34 | geom = 'point',
35 | columns = themepark:columns('core/name', {
36 | { column = 'kind', type = 'text', not_null = true },
37 | }),
38 | tiles = {
39 | minzoom = 14
40 | },
41 | expire = expire.shortbread(14, 14, 'streets_polygons_labels', 'full-area')
42 | }
43 |
44 | themepark:add_table{
45 | name = 'street_labels_points',
46 | ids_type = 'node',
47 | geom = 'point',
48 | columns = themepark:columns('core/name', {
49 | { column = 'kind', type = 'text' },
50 | { column = 'ref', type = 'text' },
51 | }),
52 | tiles = {
53 | minzoom = 12,
54 | },
55 | expire = expire.shortbread(12, 14, 'street_labels_points', 'full-area')
56 | }
57 |
58 | -- ---------------------------------------------------------------------------
59 |
60 | local Z_STEP_PER_LAYER = 100
61 |
62 | local highway_lookup = {
63 | -- highway tag z minzoom
64 | motorway = { 34, 5 },
65 | trunk = { 33, 6 },
66 | primary = { 32, 8 },
67 | secondary = { 31, 9 },
68 | tertiary = { 30, 10 },
69 |
70 | unclassified = { 20, 12 },
71 | residential = { 20, 12 },
72 | busway = { 20, 12 },
73 | busway_guideway = { 20, 12 },
74 | road = { 20, 12 },
75 |
76 | tertiary_link = { 10, 12 },
77 | secondary_link = { 10, 12 },
78 | primary_link = { 10, 12 },
79 | trunk_link = { 10, 12 },
80 | motorway_link = { 10, 12 },
81 |
82 | living_street = { 4, 13 },
83 | pedestrian = { 4, 13 },
84 |
85 | service = { 3, 13 },
86 | track = { 3, 13 },
87 |
88 | footway = { 2, 13 },
89 | path = { 2, 13 },
90 | cycleway = { 2, 13 },
91 | bridleway = { 2, 13 },
92 |
93 | steps = { 1, 13 },
94 | platform = { 1, 13 },
95 | }
96 |
97 | local railway_lookup = {
98 | rail = { 52, 8 },
99 | narrow_gauge = { 51, 8 },
100 | tram = { 51, 10 },
101 | light_rail = { 51, 10 },
102 | funicular = { 51, 10 },
103 | subway = { 51, 10 },
104 | monorail = { 51, 10 },
105 | }
106 |
107 | local aeroway_lookup = {
108 | runway = 11,
109 | taxiway = 13,
110 | }
111 |
112 | local as_bool = function(value)
113 | return value == 'yes' or value == 'true' or value == '1'
114 | end
115 |
116 | local set_ref_attributes = function(a, t)
117 | if not t.ref then
118 | return
119 | end
120 |
121 | local refs = {}
122 | local rows = 0
123 | local cols = 0
124 |
125 | for word in string.gmatch(t.ref, "([^;]+);?") do
126 | word = word:gsub('^[%s]+', '', 1):gsub('[%s]+$', '', 1)
127 | rows = rows + 1
128 | cols = math.max(cols, string.len(word))
129 | table.insert(refs, word)
130 | end
131 |
132 | a.ref = table.concat(refs, '\n')
133 | a.ref_rows = rows
134 | a.ref_cols = cols
135 | end
136 |
137 | -- ---------------------------------------------------------------------------
138 |
139 | themepark:add_proc('node', function(object, data)
140 | local t = object.tags
141 |
142 | if t.highway and t.highway == 'motorway_junction' then
143 | local a = {
144 | kind = t.highway,
145 | ref = t.ref,
146 | geom = object:as_point()
147 | }
148 | themepark.themes.core.add_name(a, object)
149 | themepark:insert('street_labels_points', a, t)
150 | end
151 | end)
152 |
153 | local process_as_area = function(object, data)
154 | if not object.is_closed then
155 | return
156 | end
157 |
158 | local t = object.tags
159 | local a = {
160 | layer = data.core.layer,
161 | }
162 | a.z_order = Z_STEP_PER_LAYER * a.layer
163 |
164 | if t.highway == 'pedestrian' or t.highway == 'service' then
165 | a.kind = t.highway
166 | elseif t.aeroway == 'runway' or t.aeroway == 'taxiway' then
167 | a.kind = t.aeroway
168 | else
169 | return
170 | end
171 |
172 | a.surface = t.surface
173 |
174 | a.tunnel = as_bool(t.tunnel) or t.tunnel == 'building_passage' or t.covered == 'yes'
175 | a.bridge = as_bool(t.bridge)
176 |
177 | a.geom = object:as_polygon():transform(3857)
178 | local has_name = themepark.themes.core.add_name(a, object)
179 | themepark:insert('street_polygons', a, t)
180 |
181 | if has_name then
182 | a.geom = a.geom:pole_of_inaccessibility()
183 | themepark:insert('streets_polygons_labels', a, t)
184 | end
185 | end
186 |
187 | themepark:add_proc('way', function(object, data)
188 | local t = object.tags
189 | if t.area == 'yes' then
190 | process_as_area(object, data)
191 | return
192 | end
193 | end)
194 |
195 | -- ---------------------------------------------------------------------------
196 |
--------------------------------------------------------------------------------
/themes/shortbread/topics/water.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Theme: shortbread_v1
4 | -- Topic: water
5 | --
6 | -- ---------------------------------------------------------------------------
7 |
8 | local themepark, theme, cfg = ...
9 | local expire = require('expire')
10 |
11 | -- ---------------------------------------------------------------------------
12 |
13 | local waterway_values = { "river", "canal", "stream", "ditch" }
14 |
15 | local bridge_values = {
16 | 'yes', 'viaduct', 'boardwalk', 'cantilever', 'covered', 'low_water_crossing', 'movable', 'trestle'
17 | }
18 |
19 | local tunnel_values = { 'yes', 'building_passage' }
20 |
21 | -- ---------------------------------------------------------------------------
22 |
23 | themepark:add_table{
24 | name = 'water_areas',
25 | ids_type = 'area',
26 | geom = 'multipolygon',
27 | columns = themepark:columns({
28 | { column = 'kind', type = 'text', not_null = true },
29 | { column = 'way_area', type = 'real' },
30 | }),
31 | tags = {
32 | { key = 'landuse', values = { 'basin', 'reservoir' }, on = 'a' },
33 | { key = 'natural', values = { 'water', 'glacier' }, on = 'a' },
34 | { key = 'water', value = 'river', on = 'a' },
35 | { key = 'waterway', values = { 'riverbank', 'dock', 'canal' }, on = 'a' },
36 | },
37 | tiles = {
38 | minzoom = 5
39 | },
40 | expire = expire.shortbread(4, 14, 'water_polygons', 'full-area')
41 | }
42 |
43 | themepark:add_table{
44 | name = 'water_area_labels',
45 | ids_type = 'area',
46 | geom = 'point',
47 | columns = themepark:columns('core/name', {
48 | { column = 'kind', type = 'text', not_null = true },
49 | { column = 'way_area', type = 'real' },
50 | }),
51 | tiles = {
52 | minzoom = 5,
53 | order_by = 'way_area',
54 | order_dir = 'desc',
55 | },
56 | expire = expire.shortbread(5, 14, 'water_polygons_labels', 'full-area')
57 | }
58 |
59 | themepark:add_table{
60 | name = 'water_lines',
61 | ids_type = 'way',
62 | geom = 'linestring',
63 | columns = themepark:columns({
64 | { column = 'kind', type = 'text', not_null = true },
65 | { column = 'tunnel', type = 'bool', not_null = true },
66 | { column = 'bridge', type = 'bool', not_null = true },
67 | { column = 'layer', type = 'int', not_null = true },
68 | { column = 'minzoom', type = 'int', not_null = true, tiles = 'minzoom' },
69 | }),
70 | tags = {
71 | { key = 'bridge', values = bridge_values, on = 'w' },
72 | { key = 'covered', value = 'yes', on = 'w' },
73 | { key = 'tunnel', values = tunnel_values, on = 'w' },
74 | { key = 'waterway', values = waterway_values, on = 'w' },
75 | },
76 | tiles = {
77 | minzoom = 9,
78 | order_by = 'layer',
79 | order_dir = 'asc',
80 | },
81 | expire = expire.shortbread(9, 14, 'water_lines', 'boundary-only')
82 | }
83 |
84 | themepark:add_table{
85 | name = 'water_lines_labels',
86 | ids_type = 'way',
87 | geom = 'linestring',
88 | columns = themepark:columns('core/name', {
89 | { column = 'kind', type = 'text', not_null = true },
90 | { column = 'minzoom', type = 'int', not_null = true },
91 | }),
92 | tiles = {
93 | minzoom = 12,
94 | },
95 | expire = expire.shortbread(12, 14, 'water_lines_labels', 'boundary-only')
96 | }
97 |
98 | -- ---------------------------------------------------------------------------
99 |
100 | local check_waterway = osm2pgsql.make_check_values_func(waterway_values)
101 |
102 | local round = function(value)
103 | return math.floor(value + 0.5)
104 | end
105 |
106 | local get_bridge_value = osm2pgsql.make_check_values_func(bridge_values, false)
107 |
108 | -- ---------------------------------------------------------------------------
109 |
110 | themepark:add_proc('way', function(object, data)
111 | local t = object.tags
112 | local waterway = t.waterway
113 | if check_waterway(waterway) then
114 | local a = {
115 | kind = waterway,
116 | geom = object:as_linestring(),
117 | layer = data.core.layer,
118 | bridge = get_bridge_value(t.bridge),
119 | tunnel = false,
120 | }
121 |
122 | if t.tunnel == 'yes' or t.tunnel == 'building_passage' or t.covered == 'yes' then
123 | a.tunnel = true
124 | end
125 |
126 | if a.kind == 'stream' or a.kind == 'ditch' then
127 | a.minzoom = 14
128 | else
129 | a.minzoom = 9
130 | end
131 |
132 | themepark:add_debug_info(a, t)
133 | themepark:insert('water_lines', a)
134 |
135 | if themepark.themes.core.add_name(a, object) then
136 | themepark:insert('water_lines_labels', a)
137 | end
138 | end
139 | end)
140 |
141 | themepark:add_proc('area', function(object, data)
142 | local t = object.tags
143 | local kind
144 |
145 | if t.natural == 'glacier' then
146 | kind = 'glacier'
147 | elseif t.natural == 'water' then
148 | if t.water == 'river' then
149 | kind = 'river'
150 | else
151 | kind = 'water'
152 | end
153 | elseif t.waterway == 'riverbank' then
154 | kind = 'river'
155 | elseif t.waterway == 'dock' or t.waterway == 'canal' then
156 | kind = t.waterway
157 | elseif t.landuse == 'basin' or t.landuse == 'reservoir' then
158 | kind = t.landuse
159 | end
160 |
161 | if not kind then
162 | return
163 | end
164 |
165 | local g = object:as_area():transform(3857)
166 | local a = {
167 | kind = kind,
168 | way_area = round(g:area()),
169 | geom = g
170 | }
171 | themepark:insert('water_areas', a)
172 |
173 | if themepark.themes.core.add_name(a, object) then
174 | a.geom = g:pole_of_inaccessibility()
175 | themepark:insert('water_area_labels', a)
176 | end
177 | end)
178 |
179 | -- ---------------------------------------------------------------------------
180 |
--------------------------------------------------------------------------------
/themes/spirit/common.lua:
--------------------------------------------------------------------------------
1 | local function contains(list, x)
2 | for i = 1, #list do
3 | if list[i] == x then return true end
4 | end
5 | return false
6 | end
7 |
8 | --- Normalizes layer tags to integers
9 | -- @param v The layer tag value
10 | -- @return The input value if it is an integer between -100 and 100, or nil otherwise
11 | local function layer (v)
12 | if v and string.find(v, "^-?%d+$") and tonumber(v) < 100 and tonumber(v) > -100 then -- check if value exists, is numeric, and is in range
13 | return v
14 | end
15 | return nil
16 | end
17 |
18 | return { contains=contains, layer=layer}
19 |
--------------------------------------------------------------------------------
/themes/spirit/init.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Theme: spirit
4 | --
5 | -- ---------------------------------------------------------------------------
6 |
7 | local theme = {}
8 |
9 | return theme
10 |
11 | -- ---------------------------------------------------------------------------
12 |
--------------------------------------------------------------------------------
/themes/spirit/topics/admin.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Theme: spirit
4 | -- Topic: admin
5 | --
6 | -- ---------------------------------------------------------------------------
7 |
8 | local themepark, theme, cfg = ...
9 |
10 | --- Normalizes admin_level tags
11 | -- @param v The admin_level tag value
12 | -- @return The input value if it is an integer between 0 and 100, or nil otherwise
13 | local function admin_level (v)
14 | if v and string.find(v, "^%d+$") and tonumber(v) < 100 and tonumber(v) > 0 then
15 | return tonumber(v)
16 | end
17 | return nil
18 | end
19 |
20 | local phase2_admin_ways_level = {}
21 | local phase2_admin_ways_parents = {}
22 |
23 | themepark:add_table{
24 | name = 'admin',
25 | ids_type = 'relation',
26 | geom = 'point', -- the primary geom used is the label, but areas are needed for admin line processing
27 | columns = themepark:columns({
28 | { column = 'name', type = 'text' },
29 | { column = 'admin_level', type = 'smallint'},
30 | { column = 'way_area', type = 'real' },
31 | { column = 'area', type = 'geometry'}
32 | }),
33 | }
34 |
35 | themepark:add_table{
36 | name = 'admin_lines',
37 | ids_type = 'way',
38 | geom = 'linestring',
39 | columns = themepark:columns({
40 | { column = 'min_admin_level', type = 'smallint' },
41 | { column = 'multiple_relations', type = 'boolean'}
42 | })
43 | }
44 |
45 | themepark:add_proc('area', function(object, data)
46 | if object.type == 'relation' and object.tags.type == 'boundary'
47 | and object.tags.boundary == 'administrative' then
48 | local admin = admin_level(object.tags.admin_level)
49 | if admin and admin >= 2 and admin <= 12 then
50 | g = object:as_area():transform(3857)
51 | local a = {
52 | geom = g:pole_of_inaccessibility(),
53 | area = g,
54 | way_area = g:area(),
55 | admin_level = admin,
56 | name = object.tags.name
57 | }
58 | themepark:add_debug_info(a, object.tags)
59 | themepark:insert('admin', a)
60 | end
61 | end
62 | end)
63 |
64 | themepark:add_proc('relation', function(object, data)
65 | if object.tags.type == 'boundary' and object.tags.boundary == 'administrative' then
66 | local admin = admin_level(object.tags.admin_level)
67 | if admin ~= nil then
68 | for _, member in ipairs(object.members) do
69 | if member.type == 'w' then
70 | -- Store the lowest admin_level, and how many relations it used in
71 | if not phase2_admin_ways_level[member.ref] then
72 | phase2_admin_ways_level[member.ref] = admin
73 | phase2_admin_ways_parents[member.ref] = 1
74 | else
75 | if phase2_admin_ways_level[member.ref] == admin then
76 | phase2_admin_ways_parents[member.ref] = phase2_admin_ways_parents[member.ref] + 1
77 | elseif admin < phase2_admin_ways_level[member.ref] then
78 | phase2_admin_ways_level[member.ref] = admin
79 | phase2_admin_ways_parents[member.ref] = 1
80 | end
81 | end
82 | end
83 | end
84 | end
85 | end
86 | end)
87 |
88 | themepark:add_proc('select_relation_members', function(relation)
89 | if relation.tags.type == 'boundary' and relation.tags.boundary == 'administrative'
90 | and admin_level(relation.tags.admin_level) ~= nil then
91 | return { ways = osm2pgsql.way_member_ids(relation) }
92 | end
93 | end)
94 |
95 | themepark:add_proc('way', function(object, data)
96 | if osm2pgsql.stage == 1 then
97 | return
98 | end
99 |
100 | if phase2_admin_ways_level[object.id] and object.tags.closure_segment ~= 'yes' then
101 | local a = {
102 | geom = object:as_linestring(),
103 | min_admin_level = phase2_admin_ways_level[object.id],
104 | multiple_relations = (phase2_admin_ways_parents[object.id] > 1)
105 | }
106 | themepark:insert('admin_lines', a)
107 | end
108 | end)
109 |
--------------------------------------------------------------------------------
/themes/spirit/topics/aeroway.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Theme: spirit
4 | -- Topic: aeroway
5 | --
6 | -- Airports are already present in the transit layer, so this is just more airport-focused stuff
7 | --
8 | -- ---------------------------------------------------------------------------
9 |
10 |
11 | local themepark, theme, cfg = ...
12 | local common = require('themes.spirit.common')
13 | local expire = require('expire')
14 |
15 | themepark:add_table{
16 | name = 'aeroways',
17 | ids_type = 'way',
18 | geom = 'linestring',
19 | columns = themepark:columns({
20 | { column = 'ref', type = 'text' },
21 | { column = 'aeroway', type = 'text' },
22 | }),
23 | expire = expire.shortbread(11, 14, 'streets', 'boundary-only')
24 | }
25 |
26 | themepark:add_proc('way', function(object, data)
27 | if object.tags.aeroway == 'runway'
28 | or object.tags.aeroway == 'taxiway' then
29 | local a = { aeroway = object.tags.aeroway,
30 | ref = object.tags.ref,
31 | geom = object:as_linestring() }
32 | themepark:add_debug_info(a, object.tags)
33 | themepark:insert('aeroways', a)
34 | end
35 | end)
36 |
--------------------------------------------------------------------------------
/themes/spirit/topics/buildings.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Theme: spirit
4 | -- Topic: buildings
5 | --
6 | -- ---------------------------------------------------------------------------
7 |
8 | local themepark, theme, cfg = ...
9 | local common = require('themes.spirit.common')
10 | local expire = require('expire')
11 |
12 | themepark:add_table{
13 | name = 'buildings',
14 | ids_type = 'area',
15 | geom = 'polygon',
16 | columns = themepark:columns({
17 | { column = 'name', type = 'text' },
18 | { column = 'way_area', type = 'real' },
19 | { column = 'point', type = 'point' },
20 | }),
21 | indexes = {
22 | { method = 'gist', column = 'point' },
23 | },
24 | expire = expire.shortbread(14, 14, 'buildings', 'full-area')
25 | }
26 |
27 | themepark:add_proc('area', function(object, data)
28 | if object.tags.building and object.tags.building ~= 'no' then
29 | for g in object:as_area():geometries() do
30 | local g_transform = g:transform(3857)
31 | local name = object.tags.name
32 | local a = { name = name, way_area = g_transform:area(), geom = g_transform }
33 | -- Only add points for buildings that need labels
34 | if name then
35 | a.point = g_transform:pole_of_inaccessibility()
36 | end
37 | themepark:add_debug_info(a, object.tags)
38 | themepark:insert('buildings', a)
39 | end
40 | end
41 | end)
42 |
43 |
--------------------------------------------------------------------------------
/themes/spirit/topics/education.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Theme: spirit
4 | -- Topic: education
5 | --
6 | -- ---------------------------------------------------------------------------
7 |
8 | local common = require ('themes.spirit.common')
9 |
10 | local themepark, theme, cfg = ...
11 |
12 | themepark:add_table{
13 | name = 'education',
14 | ids_type = 'any',
15 | geom = 'multipolygon',
16 | columns = themepark:columns({
17 | { column = 'name', type = 'text' },
18 | { column = 'education', type = 'text' },
19 | { column = 'way_area', type = 'real' },
20 | { column = 'point', type = 'point' },
21 | }),
22 | indexes = {
23 | { method = 'gist', column = 'point' },
24 | }
25 | }
26 |
27 | themepark:add_proc('node', function(object, data)
28 | local education
29 | if object.tags.amenity == 'school' then
30 | education = 'school'
31 | elseif object.tags.amenity == 'kindergarten' then
32 | education = 'kindergarten'
33 | elseif object.tags.amenity == 'university' then
34 | education = 'university'
35 | elseif object.tags.amenity == 'college' then
36 | education = 'college'
37 | end
38 | if education ~= nil then
39 | local a = {
40 | point = object:as_point(),
41 | name = object.tags.name,
42 | education = education }
43 | themepark:add_debug_info(a, object.tags)
44 | themepark:insert('education', a)
45 | end
46 | end)
47 |
48 | themepark:add_proc('area', function(object, data)
49 | local education
50 | if object.tags.amenity == 'school' then
51 | education = 'school'
52 | elseif object.tags.amenity == 'kindergarten' then
53 | education = 'kindergarten'
54 | elseif object.tags.amenity == 'university' then
55 | education = 'university'
56 | elseif object.tags.amenity == 'college' then
57 | education = 'college'
58 | end
59 | if education ~= nil then
60 | local g_transform = object:as_area():transform(3857)
61 | local a = {
62 | geom = g_transform,
63 | point = g_transform:pole_of_inaccessibility(),
64 | way_area = g_transform:area(),
65 | name = object.tags.name,
66 | education = education }
67 | themepark:add_debug_info(a, object.tags)
68 | themepark:insert('education', a)
69 | end
70 | end)
71 |
--------------------------------------------------------------------------------
/themes/spirit/topics/food.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Theme: spirit
4 | -- Topic: food
5 | --
6 | -- ---------------------------------------------------------------------------
7 |
8 | local common = require ('themes.spirit.common')
9 |
10 | local themepark, theme, cfg = ...
11 |
12 | themepark:add_table{
13 | name = 'food',
14 | ids_type = 'any',
15 | geom = 'multipolygon',
16 | columns = themepark:columns({
17 | { column = 'name', type = 'text' },
18 | { column = 'food', type = 'text' },
19 | { column = 'way_area', type = 'real' },
20 | { column = 'point', type = 'point' },
21 | }),
22 | indexes = {
23 | { method = 'gist', column = 'point' },
24 | }
25 | }
26 |
27 | local amenities = { 'bar', 'biergarten', 'cafe', 'fast_food', 'food_court', 'ice_cream', 'pub', 'restaurant' }
28 |
29 | themepark:add_proc('node', function(object, data)
30 | if object.tags.amenity and common.contains(amenities, object.tags.amenity) then
31 | local a = {
32 | point = object:as_point(),
33 | name = object.tags.name,
34 | food = object.tags.amenity }
35 | themepark:add_debug_info(a, object.tags)
36 | themepark:insert('food', a)
37 | end
38 | end)
39 |
40 | themepark:add_proc('area', function(object, data)
41 | if object.tags.amenity and common.contains(amenities, object.tags.amenity) then
42 | local g_transform = object:as_area():transform(3857)
43 | local a = {
44 | geom = g_transform,
45 | point = g_transform:pole_of_inaccessibility(),
46 | way_area = g_transform:area(),
47 | name = object.tags.name,
48 | food = object.tags.amenity }
49 | themepark:add_debug_info(a, object.tags)
50 | themepark:insert('food', a)
51 | end
52 | end)
53 |
--------------------------------------------------------------------------------
/themes/spirit/topics/landuse.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Theme: spirit
4 | -- Topic: landuse
5 | --
6 | -- ---------------------------------------------------------------------------
7 |
8 | local common = require ('themes.spirit.common')
9 |
10 | local themepark, theme, cfg = ...
11 |
12 | themepark:add_table{
13 | name = 'landuse',
14 | ids_type = 'area',
15 | geom = 'multipolygon',
16 | columns = themepark:columns({
17 | { column = 'name', type = 'text' },
18 | { column = 'landuse', type = 'text' },
19 | { column = 'way_area', type = 'real' },
20 | { column = 'point', type = 'point' },
21 | }),
22 | indexes = {
23 | { method = 'gist', column = 'point' },
24 | }
25 | }
26 |
27 | themepark:add_proc('area', function(object, data)
28 | local landuse
29 | if object.tags.landuse == 'residential' then
30 | landuse = 'residential'
31 | elseif object.tags.natural == 'commercial' then
32 | landuse = 'commercial'
33 | elseif object.tags.natural == 'retail' then
34 | landuse = 'retail'
35 | elseif object.tags.natural == 'industrial' then
36 | landuse = 'industrial'
37 | end
38 |
39 | if landuse ~= nil then
40 | local g_transform = object:as_area():transform(3857)
41 | local a = {
42 | name = object.tags.name,
43 | landuse = landuse,
44 | way_area = g_transform:area(),
45 | geom = g_transform }
46 |
47 | if object.tags.name then
48 | a.point = g_transform:pole_of_inaccessibility()
49 | end
50 | themepark:add_debug_info(a, object.tags)
51 | themepark:insert('landuse', a)
52 | end
53 | end)
54 |
--------------------------------------------------------------------------------
/themes/spirit/topics/leisure.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Theme: spirit
4 | -- Topic: leisure
5 | --
6 | -- ---------------------------------------------------------------------------
7 |
8 | local common = require ('themes.spirit.common')
9 |
10 | local themepark, theme, cfg = ...
11 |
12 | themepark:add_table{
13 | name = 'leisure',
14 | ids_type = 'area',
15 | geom = 'multipolygon',
16 | columns = themepark:columns({
17 | { column = 'name', type = 'text' },
18 | { column = 'leisure', type = 'text' },
19 | { column = 'way_area', type = 'real' },
20 | { column = 'point', type = 'point' },
21 | }),
22 | indexes = {
23 | { method = 'gist', column = 'point' },
24 | }
25 | }
26 |
27 | themepark:add_proc('area', function(object, data)
28 | local leisure
29 | if object.tags.leisure == 'park' then
30 | leisure = 'park'
31 | elseif object.tags.natural == 'stadium' then
32 | leisure = 'stadium'
33 | elseif object.tags.natural == 'playground' then
34 | leisure = 'playground'
35 | end
36 |
37 | if leisure ~= nil then
38 | local g_transform = object:as_area():transform(3857)
39 | local a = {
40 | name = object.tags.name,
41 | leisure = leisure,
42 | way_area = g_transform:area(),
43 | geom = g_transform }
44 |
45 | if object.tags.name then
46 | a.point = g_transform:pole_of_inaccessibility()
47 | end
48 | themepark:add_debug_info(a, object.tags)
49 | themepark:insert('leisure', a)
50 | end
51 | end)
52 |
--------------------------------------------------------------------------------
/themes/spirit/topics/places.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Theme: spirit
4 | -- Topic: places
5 | --
6 | -- ---------------------------------------------------------------------------
7 |
8 | local common = require ('themes.spirit.common')
9 |
10 | local themepark, theme, cfg = ...
11 |
12 | themepark:add_table{
13 | name = 'settlements',
14 | ids_type = 'any',
15 | way_area, 'real',
16 | geom = 'point',
17 | columns = themepark:columns({
18 | { column = 'name', type = 'text' },
19 | { column = 'place', type = 'text' },
20 | { column = 'way_area', type = 'real' },
21 | }),
22 | }
23 |
24 | themepark:add_proc('node', function(object, data)
25 | local place
26 | if object.tags.place == 'city' then
27 | place = 'city'
28 | elseif object.tags.place == 'town' then
29 | place = 'town'
30 | elseif object.tags.place == 'village' then
31 | place = 'village'
32 | elseif object.tags.place == 'hamlet' then
33 | place = 'hamlet'
34 | elseif object.tags.place == 'isolated_dwelling' then
35 | place = 'isolated_dwelling'
36 | end
37 |
38 | if place ~= nil then
39 | local a = {
40 | geom = object:as_point(),
41 | place = place,
42 | name = object.tags.name }
43 | themepark:add_debug_info(a, object.tags)
44 | themepark:insert('settlements', a)
45 | end
46 | end)
47 |
48 | themepark:add_proc('area', function(object, data)
49 | local place
50 | if object.tags.place == 'city' then
51 | place = 'city'
52 | elseif object.tags.place == 'town' then
53 | place = 'town'
54 | elseif object.tags.place == 'village' then
55 | place = 'village'
56 | elseif object.tags.place == 'hamlet' then
57 | place = 'hamlet'
58 | elseif object.tags.place == 'isolated_dwelling' then
59 | place = 'isolated_dwelling'
60 | end
61 |
62 | if place ~= nil then
63 | local g = object:as_area():transform(3857)
64 | local a = {
65 | geom = g:pole_of_inaccessibility(),
66 | way_area = g:area(),
67 | place = place,
68 | name = object.tags.name }
69 | themepark:add_debug_info(a, object.tags)
70 | themepark:insert('settlements', a)
71 | end
72 | end)
73 |
--------------------------------------------------------------------------------
/themes/spirit/topics/railway.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Theme: spirit
4 | -- Topic: railway
5 | --
6 | -- ---------------------------------------------------------------------------
7 |
8 | local themepark, theme, cfg = ...
9 | local common = require('themes.spirit.common')
10 | local expire = require('expire')
11 |
12 | themepark:add_table{
13 | name = 'railways',
14 | ids_type = 'way',
15 | geom = 'linestring',
16 | columns = themepark:columns({
17 | { column = 'railway', type = 'text' },
18 | { column = 'name', type = 'text' },
19 | { column = 'ref', type = 'text' },
20 | { column = 'minor', type = 'boolean' },
21 | { column = 'bridge', type = 'boolean' },
22 | { column = 'tunnel', type = 'boolean' },
23 | { column = 'layer', type = 'smallint' },
24 | { column = 'z_order', type = 'smallint' },
25 | { column = 'service', type = 'text' },
26 | }),
27 | expire = expire.shortbread(8, 14, 'streets', 'boundary-only')
28 | }
29 |
30 | local z_order = {
31 | rail = 440,
32 | narrow_gauge = 430,
33 | light_rail = 420,
34 | funicular = 420,
35 | subway = 420,
36 | monorail = 420,
37 | tram = 410
38 | }
39 |
40 | local ssy = {'spur', 'siding', 'yard'}
41 | themepark:add_proc('way', function(object, data)
42 | local z = z_order[object.tags.railway]
43 | if z then
44 | local a = { name = object.tags.name,
45 | ref = object.tags.ref,
46 | railway = object.tags.railway,
47 | service = object.tags.service,
48 | layer = common.layer(object.tags.layer),
49 | z_order = z,
50 | geom = object:as_linestring() }
51 | if common.contains(ssy, object.tags.service) then
52 | a.minor = true
53 | end
54 | if object.tags.bridge and object.tags.bridge ~= 'no' then
55 | a.bridge = true
56 | end
57 | if object.tags.tunnel and object.tags.tunnel ~= 'no' then
58 | a.tunnel = true
59 | end
60 |
61 | themepark:add_debug_info(a, object.tags)
62 | themepark:insert('railways', a)
63 | end
64 | end)
65 |
66 |
67 |
--------------------------------------------------------------------------------
/themes/spirit/topics/roads.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Theme: spirit
4 | -- Topic: roads
5 | --
6 | -- ---------------------------------------------------------------------------
7 |
8 | local themepark, theme, cfg = ...
9 | local common = require('themes.spirit.common')
10 | local expire = require('expire')
11 |
12 | themepark:add_table{
13 | name = 'roads',
14 | ids_type = 'way',
15 | geom = 'linestring',
16 | columns = themepark:columns({
17 | { column = 'highway', type = 'text' },
18 | { column = 'name', type = 'text' },
19 | { column = 'ref', type = 'text' },
20 | { column = 'oneway', type = 'text' },
21 | { column = 'minor', type = 'boolean' },
22 | { column = 'bridge', type = 'boolean' },
23 | { column = 'tunnel', type = 'boolean' },
24 | { column = 'layer', type = 'smallint' },
25 | { column = 'z_order', type = 'smallint' },
26 | { column = 'tracktype', type = 'text' },
27 | { column = 'surface', type = 'text' },
28 | { column = 'service', type = 'text' },
29 | { column = 'bicycle', type = 'text' },
30 | { column = 'horse', type = 'text' }
31 | }),
32 | indexes = {
33 | {
34 | column = 'geom',
35 | method = 'gist',
36 | where = "highway IN ('motorway', 'motorway_link', 'trunk', 'trunk_link', 'primary', 'primary_link', 'secondary', 'secondary_link', 'tertiary', 'tertiary_link')"
37 | }
38 | },
39 | expire = expire.shortbread(5, 14, 'streets', 'boundary-only')
40 | }
41 |
42 | themepark:add_table{
43 | name = 'road_routes',
44 | ids_type = 'relation',
45 | columns = themepark:columns({
46 | { column = 'member_id', type = 'int8' },
47 | { column = 'member_position', type = 'int4' },
48 | { column = 'ref', type = 'text' },
49 | { column = 'network', type = 'text' }
50 | }),
51 | }
52 |
53 | -- z_order value. Must be a multiple of 10, because construction divides it by 10
54 | local z_order = {
55 | motorway = 380,
56 | trunk = 370,
57 | primary = 360,
58 | secondary = 350,
59 | tertiary = 340,
60 | road = 330,
61 | unclassified = 330,
62 | residential = 330,
63 | living_street = 320,
64 | pedestrian = 310,
65 | motorway_link = 240,
66 | trunk_link = 230,
67 | primary_link = 220,
68 | secondary_link = 210,
69 | tertiary_link = 200,
70 | busway = 170,
71 | bus_guideway = 170,
72 | service = 150,
73 | track = 110,
74 | bridleway = 100,
75 | footway = 100,
76 | cycleway = 100,
77 | path = 100,
78 | steps = 100,
79 | construction = 0
80 | }
81 |
82 | local minor_service = {'parking_aisle', 'drive-through', 'driveway'}
83 | themepark:add_proc('way', function(object, data)
84 | local z = z_order[object.tags.highway]
85 | if z and (not object.tags.area or object.tags.area == 'no') then
86 | if object.tags.highway == 'construction' then
87 | if object.tags.construction and z_order[object.tags.construction] then
88 | z = z_order[object.tags.construction]/10
89 | else
90 | z = z_order['road']/10
91 | end
92 | end
93 | local a = { name = object.tags.name,
94 | highway = object.tags.highway,
95 | ref = object.tags.ref,
96 | oneway = object.tags.oneway,
97 | tracktype = object.tags.oneway,
98 | surface = object.tags.surface,
99 | service = object.tags.service,
100 | bicycle = object.tags.bicycle,
101 | horse = object.tags.horse,
102 | layer = common.layer(object.tags.layer),
103 | z_order = z,
104 | geom = object:as_linestring() }
105 | if common.contains(minor_service, object.tags.service) then
106 | a.minor = true
107 | end
108 | if object.tags.bridge and object.tags.bridge ~= 'no' then
109 | a.bridge = true
110 | end
111 | if object.tags.tunnel and object.tags.tunnel ~= 'no' then
112 | a.tunnel = true
113 | end
114 |
115 | themepark:add_debug_info(a, object.tags)
116 | themepark:insert('roads', a)
117 | end
118 | end)
119 |
120 | themepark:add_proc('relation', function(object)
121 | if object.tags.type == 'route' and object.tags.route == 'road' then
122 | local a = { ref = object.tags.ref, network = object.tags.network }
123 | for i, member in ipairs(object.members) do
124 | if member.type == 'w' then
125 | a.member_id = member.ref
126 | a.member_position = id
127 | themepark:insert('road_routes', a)
128 | end
129 | end
130 | end
131 | end)
132 |
--------------------------------------------------------------------------------
/themes/spirit/topics/transit.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Theme: spirit
4 | -- Topic: transit
5 | --
6 | -- ---------------------------------------------------------------------------
7 |
8 | local common = require ('themes.spirit.common')
9 |
10 | local themepark, theme, cfg = ...
11 |
12 | themepark:add_table{
13 | name = 'transit',
14 | ids_type = 'any',
15 | way_area, 'real',
16 | geom = 'point',
17 | columns = themepark:columns({
18 | { column = 'name', type = 'text' },
19 | { column = 'station', type = 'boolean' },
20 | { column = 'mode', type = 'text' },
21 | { column = 'way_area', type = 'real' },
22 | }),
23 | }
24 |
25 | themepark:add_proc('node', function(object, data)
26 | local mode
27 | local station
28 | if object.tags.aeroway == 'aerodrome' then
29 | mode = 'airplane'
30 | station = true
31 | elseif (object.tags.railway == 'station' and object.tags.station == 'subway')
32 | or (object.tags.public_transport == 'station' and object.tags.subway == 'yes') then
33 | mode = 'subway'
34 | station = true
35 | elseif object.tags.highway == 'tram_stop' or (object.tags.public_transport == 'platform' and object.tags.tram == 'yes') then
36 | mode = 'tram'
37 | station = false
38 | elseif object.tags.highway == 'bus_stop'
39 | or (object.tags.public_transport == 'platform' and object.tags.bus == 'yes') then
40 | mode = 'bus'
41 | station = false
42 | elseif object.tags.highway == 'bus_station'
43 | or (object.tags.public_transport == 'station' and object.tags.bus == 'yes') then
44 | mode = 'bus'
45 | station = true
46 | end
47 |
48 | if mode ~= nil then
49 | local a = {
50 | geom = object:as_point(),
51 | mode = mode,
52 | station = station,
53 | name = object.tags.name }
54 | themepark:add_debug_info(a, object.tags)
55 | themepark:insert('transit', a)
56 | end
57 | end)
58 |
59 | themepark:add_proc('area', function(object, data)
60 | local mode
61 | local station
62 | -- The logic here is similar to node handling, but differs as some tagging is node-only
63 | if object.tags.aeroway == 'aerodrome' then
64 | mode = 'airplane'
65 | station = true
66 | elseif (object.tags.railway == 'station' and object.tags.station == 'subway')
67 | or (object.tags.public_transport == 'station' and object.tags.subway == 'yes') then
68 | mode = 'subway'
69 | station = true
70 | elseif object.tags.highway == 'tram_stop' or (object.tags.public_transport == 'platform' and object.tags.tram == 'yes') then
71 | mode = 'tram'
72 | station = false
73 | elseif object.tags.highway == 'bus_station'
74 | or (object.tags.public_transport == 'station' and object.tags.bus == 'yes') then
75 | mode = 'bus'
76 | station = true
77 | end
78 |
79 | if mode ~= nil then
80 | local g = object:as_area():transform(3857)
81 | local a = {
82 | geom = g:pole_of_inaccessibility(),
83 | way_area = g:area(),
84 | mode = mode,
85 | station = station,
86 | name = object.tags.name }
87 | themepark:add_debug_info(a, object.tags)
88 | themepark:insert('transit', a)
89 | end
90 | end)
91 |
--------------------------------------------------------------------------------
/themes/spirit/topics/vegetation.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Theme: spirit
4 | -- Topic: vegetation
5 | --
6 | -- ---------------------------------------------------------------------------
7 |
8 | local common = require ('themes.spirit.common')
9 |
10 | local themepark, theme, cfg = ...
11 |
12 | themepark:add_table{
13 | name = 'vegetation',
14 | ids_type = 'area',
15 | geom = 'multipolygon',
16 | columns = themepark:columns({
17 | { column = 'name', type = 'text' },
18 | { column = 'vegetation', type = 'text' },
19 | { column = 'wetland', type = 'text' },
20 | { column = 'way_area', type = 'real' },
21 | { column = 'point', type = 'point' },
22 | }),
23 | indexes = {
24 | { method = 'gist', column = 'point' },
25 | }
26 | }
27 |
28 | themepark:add_proc('area', function(object, data)
29 | local vegetation
30 | local wetland
31 | if object.tags.natural == 'wood' or object.tags.landuse == 'forest' then
32 | vegetation = 'wood'
33 | elseif object.tags.natural == 'heath' then
34 | vegetation = 'heath'
35 | elseif object.tags.natural == 'scrub' then
36 | vegetation = 'scrub'
37 | elseif object.tags.natural == 'grassland' or object.tags.landuse == 'meadow' or object.tags.landuse == 'grass' then
38 | vegetation = 'grass'
39 | elseif object.tags.natural == 'mud' then
40 | vegetation = 'wetland'
41 | wetland = 'mud'
42 | elseif object.tags.natural == 'wetland' then
43 | vegetation = 'wetland'
44 | wetland = object.tags.wetland
45 | end
46 |
47 | if vegetation ~= nil then
48 | local g_transform = object:as_area():transform(3857)
49 | local a = {
50 | name = object.tags.name,
51 | vegetation = vegetation,
52 | wetland = wetland,
53 | way_area = g_transform:area(),
54 | geom = g_transform }
55 |
56 | if object.tags.name then
57 | a.point = g_transform:pole_of_inaccessibility()
58 | end
59 | themepark:add_debug_info(a, object.tags)
60 | themepark:insert('vegetation', a)
61 | end
62 | end)
63 |
--------------------------------------------------------------------------------
/themes/spirit/topics/water.lua:
--------------------------------------------------------------------------------
1 | -- ---------------------------------------------------------------------------
2 | --
3 | -- Theme: spirit
4 | -- Topic: water
5 | --
6 | -- ---------------------------------------------------------------------------
7 |
8 | local common = require ('themes.spirit.common')
9 |
10 | local themepark, theme, cfg = ...
11 |
12 | themepark:add_table{
13 | name = 'water',
14 | ids_type = 'area',
15 | geom = 'multipolygon',
16 | columns = themepark:columns({
17 | { column = 'name', type = 'text' },
18 | { column = 'way_area', type = 'real' },
19 | { column = 'point', type = 'point' },
20 | }),
21 | indexes = {
22 | { method = 'gist', column = 'point' },
23 | }
24 | }
25 |
26 | themepark:add_table{
27 | name = 'waterways',
28 | ids_type = 'way',
29 | geom = 'linestring',
30 | columns = themepark:columns({
31 | { column = 'name', type = 'text' },
32 | { column = 'waterway', type = 'text' },
33 | }),
34 | }
35 |
36 | themepark:add_proc('area', function(object, data)
37 | if (object.tags.natural == 'water' or object.tags.waterway == 'dock' or object.tags.waterway == 'basin' or object.tags.waterway == 'reservoir')
38 | then
39 | local g_transform = object:as_area():transform(3857)
40 | local name = object.tags.name
41 | local a = { name = name, way_area = g_transform:area(), geom = g_transform }
42 | -- Only add points for water areas that need labels
43 | if name then
44 | a.point = g_transform:pole_of_inaccessibility()
45 | end
46 | themepark:add_debug_info(a, object.tags)
47 | themepark:insert('water', a)
48 | end
49 | end)
50 |
51 | themepark:add_proc('way', function(object, data)
52 | if (object.tags.waterway == 'river'
53 | or object.tags.waterway == 'canal'
54 | or object.tags.waterway == 'stream'
55 | or object.tags.waterway == 'drain'
56 | or object.tags.waterway == 'ditch'
57 | ) then
58 | local a = { name = object.tags.name, waterway = object.tags.waterway,
59 | geom = object:as_linestring() }
60 | themepark:add_debug_info(a, object.tags)
61 | themepark:insert('waterways', a)
62 | end
63 | end)
64 |
--------------------------------------------------------------------------------