diff --git a/app/domain/urls.ts b/app/domain/urls.ts index 498ba615..0f883207 100644 --- a/app/domain/urls.ts +++ b/app/domain/urls.ts @@ -44,6 +44,7 @@ interface CountrySpecificUrls { apiDocs: string; statistics: string; limit: string; + landing: string; } type Urls = { @@ -60,6 +61,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/estadisticas', + landing: '/landing', limit: '/limit' }, au: { @@ -71,6 +73,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/statistics', + landing: '/landing', limit: '/limit' }, bd: { @@ -82,6 +85,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/statistics', + landing: '/landing', limit: '/limit' }, br: { @@ -93,6 +97,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/estatisticas', + landing: '/landing', limit: '/limit' }, ca: { @@ -104,6 +109,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/statistics', + landing: '/landing', limit: '/limit' }, cl: { @@ -115,6 +121,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/estadisticas', + landing: '/landing', limit: '/limit' }, co: { @@ -126,6 +133,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/estadisticas', + landing: '/landing', limit: '/limit' }, cz: { @@ -137,6 +145,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/statistiky', + landing: '/landing', limit: '/limit' }, de: { @@ -148,6 +157,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/statistiken', + landing: '/landing', limit: '/limit' }, dk: { @@ -159,6 +169,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/statistikker', + landing: '/landing', limit: '/limit' }, en: { @@ -170,6 +181,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/statistics', + landing: '/landing', limit: '/limit' }, es: { @@ -181,6 +193,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/estadisticas', + landing: '/landing', limit: '/limit' }, fr: { @@ -192,6 +205,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/statistiques', + landing: '/landing', limit: '/limit' }, id: { @@ -203,6 +217,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/statistik', + landing: '/landing', limit: '/limit' }, in: { @@ -214,6 +229,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/statistics', + landing: '/landing', limit: '/limit' }, it: { @@ -225,6 +241,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/statistiche', + landing: '/landing', limit: '/limit' }, lt: { @@ -236,6 +253,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/statistika', + landing: '/landing', limit: '/limit' }, mt: { @@ -247,6 +265,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/statistics', + landing: '/landing', limit: '/limit' }, mx: { @@ -258,6 +277,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/estadisticas', + landing: '/landing', limit: '/limit' }, my: { @@ -269,6 +289,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/statistik', + landing: '/landing', limit: '/limit' }, ng: { @@ -280,6 +301,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/statistics', + landing: '/landing', limit: '/limit' }, nl: { @@ -291,6 +313,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/statistieken', + landing: '/landing', limit: '/limit' }, no: { @@ -302,6 +325,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/statistikk', + landing: '/landing', limit: '/limit' }, np: { @@ -313,6 +337,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/statistics', + landing: '/landing', limit: '/limit' }, ph: { @@ -324,6 +349,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/statistics', + landing: '/landing', limit: '/limit' }, pt: { @@ -335,6 +361,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/estatistica', + landing: '/landing', limit: '/limit' }, ro: { @@ -346,6 +373,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/statistici', + landing: '/landing', limit: '/limit' }, se: { @@ -357,6 +385,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/statistik', + landing: '/landing', limit: '/limit' }, sg: { @@ -368,6 +397,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/statistics', + landing: '/landing', limit: '/limit' }, sk: { @@ -379,6 +409,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/statistiky', + landing: '/landing', limit: '/limit' }, tr: { @@ -390,6 +421,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/istatistikler', + landing: '/landing', limit: '/limit' }, ua: { @@ -401,6 +433,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/statistics', + landing: '/landing', limit: '/limit' }, us: { @@ -412,6 +445,7 @@ export const countrySpecificUrls: Urls = { api: '/api', apiDocs: '/api-docs', statistics: '/statistics', + landing: '/landing', limit: '/limit' } }; diff --git a/app/openAPI/reports.swagger.ts b/app/openAPI/reports.swagger.ts index f61be6e2..11e9c349 100644 --- a/app/openAPI/reports.swagger.ts +++ b/app/openAPI/reports.swagger.ts @@ -48,3 +48,20 @@ export const getReportsCsv = { } } }; + +export const getCountries = { + tags: ['Countries'], + description: 'Returns list of countries where coronastatus is available', + operationId: 'getCountries', + version: '0.0.1', + responses: { + '200': { + description: 'Countries', + content: { + 'application/json': { + schema: {} + } + } + } + } +}; diff --git a/app/repository/CountryRepository.ts b/app/repository/CountryRepository.ts new file mode 100644 index 00000000..953e3c09 --- /dev/null +++ b/app/repository/CountryRepository.ts @@ -0,0 +1,58 @@ +/* eslint-disable global-require, import/no-dynamic-require */ +import { readdirSync, Dirent } from 'fs'; +import path from 'path'; +import { Config, CountrySpecificTexts, Coordinate } from '../domain/types'; +import { CountryCode } from '../domain/urls'; + +const countriesDir = path.join(__dirname, '..', 'countrySpecific'); + +interface Country { + url: string; + countryCode: CountryCode; + mapCenter: Coordinate; + countryName: string; +} + +interface ConfigFile { + countrySpecificConfig: Config; +} + +interface TextVariablesFile { + countrySpecificTexts: CountrySpecificTexts; +} + +const readFromFile = (dirent: Dirent, fileName: string): T => + require(`../countrySpecific/${dirent.name}/${fileName}`); + +export class CountryRepository { + private countries: Country[]; + + constructor() { + console.log('Reading countries from files...'); + this.countries = this.readCountriesFromFiles(); + } + + private readCountriesFromFiles = (): Country[] => + readdirSync(countriesDir, { withFileTypes: true }) + .filter(dirent => dirent.isDirectory()) + .map(dirent => { + const { countrySpecificConfig } = readFromFile( + dirent, + 'config.ts' + ); + const { countrySpecificTexts } = readFromFile( + dirent, + 'text-variables.ts' + ); + return { + url: countrySpecificConfig.BASE_URL, + countryCode: countrySpecificConfig.COUNTRY_CODE, + mapCenter: countrySpecificConfig.MAP_CENTER, + countryName: countrySpecificTexts.COUNTRY_NAME + }; + }); + + public getCountries(): Country[] { + return this.countries; + } +} diff --git a/app/routes/api-routes.ts b/app/routes/api-routes.ts index 371ee85b..04a0d631 100644 --- a/app/routes/api-routes.ts +++ b/app/routes/api-routes.ts @@ -19,9 +19,11 @@ import { SmokingHabit, IsolationStatus } from '../domain/types'; +import { CountryRepository } from '../repository/CountryRepository'; const router = express.Router(); const reportRepo = new CovidReportRepository(); +const countryRepo = new CountryRepository(); router.get('/aggregated', cors(), async (req, res) => { const reports = await reportRepo.getLatestCovidReports(); @@ -183,6 +185,11 @@ router.get('/reports', cors(), async (req, res) => { return res.json(result); }); +router.get('/countries', cors(), async (req, res) => { + const countries = countryRepo.getCountries(); + res.json(countries); +}); + router.get('*', (req, res) => { res.status(404).json({ status: 404, diff --git a/app/routes/various-routes.ts b/app/routes/various-routes.ts index 2e8d15d9..6aa37103 100644 --- a/app/routes/various-routes.ts +++ b/app/routes/various-routes.ts @@ -24,6 +24,10 @@ router.get(urls.contributors, (req, res) => { return res.render('pages/contributors'); }); +router.get(`${urls.landing}`, (req, res) => { + return res.render('pages/landing'); +}); + router.get(urls.limit, (req, res) => { return res.render('pages/limit'); }); diff --git a/app/swagger.ts b/app/swagger.ts index 42e1230a..32708d96 100644 --- a/app/swagger.ts +++ b/app/swagger.ts @@ -4,7 +4,11 @@ import { } from './openAPI/aggregated.swagger'; import config from './config'; import { countryCodeToUrls } from './domain/urls'; -import { getReports, getReportsCsv } from './openAPI/reports.swagger'; +import { + getReports, + getReportsCsv, + getCountries +} from './openAPI/reports.swagger'; const urls = countryCodeToUrls(config.COUNTRY_CODE); @@ -53,6 +57,9 @@ export const swaggerDocument = { }, '/reports/reports.csv': { get: getReportsCsv + }, + '/countries': { + get: getCountries } } }; diff --git a/app/views/pages/landing.ejs b/app/views/pages/landing.ejs new file mode 100644 index 00000000..9563784b --- /dev/null +++ b/app/views/pages/landing.ejs @@ -0,0 +1,542 @@ +<% + const { + profile, + passcode, + baseUrl, + formatNumber, + urls + } = locals; + const shouldAddAnalytics = !locals.profile; +%> +<% const { show = true, menu = true, home = false, fullscreen = false, lastCommit = false, showLocaleSelector = true, tryStartOnUserLocation = false } = locals; %> + + + + + +<%- include('partials/head') -%> + + + + + + + +
+ + <% if (menu) { %> +
+
+
+ +
    +
  • + + + +
  • +
+
+
+
+ <% } %> + + +
+
+

+ <%- __("Help us uncover and predict the corona spread across the world").replace(//g, '') %> +

+ +
+
+ + + +
+ +
+
+
+
+ <%= __("Animation") %>: + +
+
+ + + + +
+ +

+ <%- __("How can I contribute?").replace(//g, '') %> +

+ + +
+
+

<%= __("1. Survey") %>

+

<%= __("Select your *country and fill out the anonymous survey.") %>

+
    +
  • + +
  • +
+
+
+

<%= __("2. Share") %>

+

<%= __("Share the link with your friends, family, and encourage them to fill it in.") %>

+ <%- include('partials/share', { amount: formatNumber(10000) }) -%> +
+
+

<%= __("3. Build") %>

+

<%= __("Don't see your country on the map?") %>
Why not set it up? We can help you do it.

+ + + +
+
+ +
+ +
+ + +

+ <%- __("About coronastatus").replace(//g, '') %> +

+ +
+ + + <%- include('partials/app-feature-block', { + image: '/static/images/hospital.svg', + title: __('Unreported cases'), + description: __('The spread of COVID-19 in our world is unknown. Help us create a better overview.') + }) -%> + + <%- include('partials/app-feature-block', { + image: '/static/images/doctor.svg', + title: __('Contribute anonymously'), + description: __('Report your health condition anonymously') + }) -%> + + <%- include('partials/app-feature-block', { + image: '/static/images/research.svg', + title: __('Insight for everyone'), + description: __('The data is open to everyone. Look at maps and graphs, or download the data and create your own visualizations.') + }) -%> + + <%- include('partials/app-feature-block', { + image: '/static/images/mobile.svg', + title: __('Everything is open'), + description: __('The website is open source and built by volunteers. Feel free to join.') + }) -%> +
+
+ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/static/images/logo-marker.png b/static/images/logo-marker.png new file mode 100644 index 00000000..28100e82 Binary files /dev/null and b/static/images/logo-marker.png differ diff --git a/static/landing-map-theme.json b/static/landing-map-theme.json new file mode 100644 index 00000000..17f57d18 --- /dev/null +++ b/static/landing-map-theme.json @@ -0,0 +1,553 @@ +{ + "version": 8, + "name": "Alidade Smooth", + "metadata": { + "mapbox:autocomposite": false, + "mapbox:groups": { + "101da9f13b64a08fa4b6ac1168e89e5f": { + "collapsed": false, + "name": "Places" + }, + "a14c9607bc7954ba1df7205bf660433f": {"name": "Boundaries"}, + "b6371a3f2f5a9932464fa3867530a2e5": { + "collapsed": false, + "name": "Transportation" + } + }, + "mapbox:type": "template", + "openmaptiles:mapbox:owner": "openmaptiles", + "openmaptiles:mapbox:source:url": "mapbox://openmaptiles.4qljc88t", + "openmaptiles:version": "3.x", + "maputnik:renderer": "mbgljs" + }, + "center": [10.184401828277087, -1.1368683772161605e-13], + "zoom": 0.8902641636539237, + "bearing": 0, + "pitch": 0, + "sources": { + "openmaptiles": { + "type": "vector", + "url": "https://tiles.stadiamaps.com/data/openmaptiles.json" + } + }, + "sprite": "https://tiles.stadiamaps.com/styles/alidade-smooth/sprite", + "glyphs": "https://tiles.stadiamaps.com/fonts/{fontstack}/{range}.pbf", + "layers": [ + { + "id": "background", + "type": "background", + "paint": {"background-color": "hsl(0, 0%, 20%)"} + }, + { + "id": "park_fill", + "type": "fill", + "source": "openmaptiles", + "source-layer": "park", + "filter": ["==", "$type", "Polygon"], + "layout": {"visibility": "visible"}, + "paint": {"fill-color": "hsla(109, 41%, 55%, 8%)"} + }, + { + "id": "landcover_ice_shelf", + "type": "fill", + "source": "openmaptiles", + "source-layer": "landcover", + "maxzoom": 8, + "filter": [ + "all", + ["==", "$type", "Polygon"], + ["==", "subclass", "ice_shelf"] + ], + "layout": {"visibility": "visible"}, + "paint": {"fill-color": "hsla(0, 0%, 75%, 60%)", "fill-opacity": 0.7} + }, + { + "id": "landcover_glacier", + "type": "fill", + "source": "openmaptiles", + "source-layer": "landcover", + "maxzoom": 8, + "filter": [ + "all", + ["==", "$type", "Polygon"], + ["==", "subclass", "glacier"] + ], + "layout": {"visibility": "visible"}, + "paint": { + "fill-color": "hsla(0, 0%, 75%, 50%)", + "fill-opacity": {"base": 1, "stops": [[0, 1], [8, 0.5]]} + } + }, + { + "id": "landuse_residential", + "type": "fill", + "source": "openmaptiles", + "source-layer": "landuse", + "maxzoom": 16, + "filter": [ + "all", + ["==", "$type", "Polygon"], + ["==", "class", "residential"] + ], + "layout": {"visibility": "visible"}, + "paint": { + "fill-color": "hsla(60, 8%, 22%, 20%)", + "fill-opacity": {"base": 0.6, "stops": [[8, 0.8], [9, 0.6]]} + } + }, + { + "id": "landcover_wood", + "type": "fill", + "source": "openmaptiles", + "source-layer": "landcover", + "minzoom": 10, + "filter": ["all", ["==", "$type", "Polygon"], ["==", "class", "wood"]], + "layout": {"visibility": "visible"}, + "paint": { + "fill-color": "hsla(120, 23%, 59%, 10%)", + "fill-opacity": {"base": 1, "stops": [[8, 0], [12, 1]]} + } + }, + { + "id": "landcover_park", + "type": "fill", + "source": "openmaptiles", + "source-layer": "landcover", + "minzoom": 10, + "filter": ["all", ["==", "$type", "Polygon"], ["==", "subclass", "park"]], + "layout": {"visibility": "visible"}, + "paint": { + "fill-color": "hsla(120, 23%, 55%, 8%)", + "fill-opacity": {"base": 1, "stops": [[8, 0], [12, 0.8]]} + } + }, + { + "id": "boundary_state", + "type": "line", + "metadata": {"mapbox:group": "a14c9607bc7954ba1df7205bf660433f"}, + "source": "openmaptiles", + "source-layer": "boundary", + "minzoom": 4, + "filter": ["==", "admin_level", 4], + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-blur": 0.4, + "line-color": "hsla(353, 34%, 80%, 30%)", + "line-dasharray": [2, 2], + "line-opacity": 1, + "line-width": {"base": 1.3, "stops": [[3, 1], [22, 15]]} + } + }, + { + "id": "boundary_country", + "type": "line", + "metadata": {"mapbox:group": "a14c9607bc7954ba1df7205bf660433f"}, + "source": "openmaptiles", + "source-layer": "boundary", + "filter": ["==", "admin_level", 2], + "layout": {"line-cap": "round", "line-join": "round"}, + "paint": { + "line-blur": {"base": 1, "stops": [[0, 0.4], [22, 4]]}, + "line-color": "hsla(353, 34%, 80%, 30%)", + "line-opacity": 1, + "line-width": {"base": 1.1, "stops": [[3, 1], [22, 20]]} + } + }, + { + "id": "water", + "type": "fill", + "source": "openmaptiles", + "source-layer": "water", + "filter": ["==", "$type", "Polygon"], + "layout": {"visibility": "visible"}, + "paint": {"fill-antialias": true, "fill-color": "#222"} + }, + { + "id": "waterway", + "type": "line", + "source": "openmaptiles", + "source-layer": "waterway", + "filter": ["==", "$type", "LineString"], + "layout": {"visibility": "visible"}, + "paint": {"line-color": "#222"} + }, + { + "id": "building", + "type": "fill", + "source": "openmaptiles", + "source-layer": "building", + "minzoom": 12, + "filter": ["==", "$type", "Polygon"], + "paint": { + "fill-antialias": true, + "fill-color": "hsl(95, 5%, 22%)", + "fill-outline-color": "hsla(0, 0%, 40%, 50%)" + } + }, + { + "id": "tunnel_motorway_casing", + "type": "line", + "metadata": {"mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5"}, + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 6, + "filter": [ + "all", + ["==", "$type", "LineString"], + ["all", ["==", "brunnel", "tunnel"], ["==", "class", "motorway"]] + ], + "layout": { + "line-cap": "butt", + "line-join": "miter", + "visibility": "visible" + }, + "paint": { + "line-color": "hsla(0, 0%, 40%, 20%)", + "line-opacity": 1, + "line-width": {"base": 1.4, "stops": [[5.8, 0], [6, 3], [20, 35]]} + } + }, + { + "id": "tunnel_motorway_inner", + "type": "line", + "metadata": {"mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5"}, + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 6, + "filter": [ + "all", + ["==", "$type", "LineString"], + ["all", ["==", "brunnel", "tunnel"], ["==", "class", "motorway"]] + ], + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "hsla(60, 1%, 21%, 10%)", + "line-width": {"base": 1.4, "stops": [[4, 2], [6, 1.3], [20, 30]]} + } + }, + { + "id": "highway_path", + "type": "line", + "metadata": {"mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5"}, + "source": "openmaptiles", + "source-layer": "transportation", + "filter": [ + "all", + ["==", "$type", "LineString"], + ["in", "class", "path", "construction"] + ], + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "hsl(0, 0%, 30%)", + "line-opacity": 0.9, + "line-width": {"base": 1.2, "stops": [[13, 1], [20, 10]]} + } + }, + { + "id": "highway_minor", + "type": "line", + "metadata": {"mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5"}, + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 8, + "filter": [ + "all", + ["==", "$type", "LineString"], + ["in", "class", "minor", "service", "track"] + ], + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "base": 1, + "stops": [[13, "hsl(0, 0%, 32%)"], [16, "hsl(0, 0%, 30%)"]] + }, + "line-opacity": 0.9, + "line-width": {"base": 1.55, "stops": [[13, 1], [18, 8]]} + } + }, + { + "id": "highway_major_casing", + "type": "line", + "metadata": {"mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5"}, + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 12, + "filter": [ + "all", + ["==", "$type", "LineString"], + ["in", "class", "primary", "secondary", "tertiary", "trunk"] + ], + "layout": { + "line-cap": "butt", + "line-join": "miter", + "visibility": "visible" + }, + "paint": { + "line-color": "hsla(0, 0%, 30%, 80%)", + "line-dasharray": [12, 0], + "line-width": {"base": 1.3, "stops": [[10, 3], [20, 20]]} + } + }, + { + "id": "highway_major_inner", + "type": "line", + "metadata": {"mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5"}, + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 12, + "filter": [ + "all", + ["==", "$type", "LineString"], + ["in", "class", "primary", "secondary", "tertiary", "trunk"] + ], + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "hsla(60, 1%, 16%, 90%)", + "line-width": {"base": 1.3, "stops": [[10, 2], [20, 18]]} + } + }, + { + "id": "highway_major_subtle", + "type": "line", + "metadata": {"mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5"}, + "source": "openmaptiles", + "source-layer": "transportation", + "maxzoom": 12, + "filter": [ + "all", + ["==", "$type", "LineString"], + ["in", "class", "primary", "secondary", "tertiary", "trunk"] + ], + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": {"line-color": "hsla(0, 0%, 32%, 70%)", "line-width": 1} + }, + { + "id": "highway_motorway_casing", + "type": "line", + "metadata": {"mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5"}, + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 6, + "filter": [ + "all", + ["==", "$type", "LineString"], + [ + "all", + ["!in", "brunnel", "bridge", "tunnel"], + ["==", "class", "motorway"] + ] + ], + "layout": { + "line-cap": "butt", + "line-join": "miter", + "visibility": "visible" + }, + "paint": { + "line-color": "hsla(0, 0%, 30%, 80%)", + "line-dasharray": [2, 0], + "line-opacity": 1, + "line-width": {"base": 1.4, "stops": [[5.8, 0], [6, 3], [20, 30]]} + } + }, + { + "id": "highway_motorway_inner", + "type": "line", + "metadata": {"mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5"}, + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 6, + "filter": [ + "all", + ["==", "$type", "LineString"], + [ + "all", + ["!in", "brunnel", "bridge", "tunnel"], + ["==", "class", "motorway"] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "hsla(60, 1%, 16%, 90%)", + "line-width": {"base": 1.4, "stops": [[4, 2], [6, 1.3], [20, 25]]} + } + }, + { + "id": "highway_motorway_subtle", + "type": "line", + "metadata": {"mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5"}, + "source": "openmaptiles", + "source-layer": "transportation", + "maxzoom": 6, + "filter": [ + "all", + ["==", "$type", "LineString"], + ["==", "class", "motorway"] + ], + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "hsla(0, 0%, 35%, 0.53)", + "line-width": {"base": 1.4, "stops": [[4, 0.75], [5, 1.5]]} + } + }, + { + "id": "railway_service", + "type": "line", + "metadata": {"mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5"}, + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 16, + "filter": [ + "all", + ["==", "$type", "LineString"], + ["all", ["==", "class", "rail"], ["has", "service"]] + ], + "layout": {"line-join": "round", "visibility": "visible"}, + "paint": {"line-color": "#545353", "line-width": 3} + }, + { + "id": "railway_service_dashline", + "type": "line", + "metadata": {"mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5"}, + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 16, + "filter": [ + "all", + ["==", "$type", "LineString"], + ["==", "class", "rail"], + ["has", "service"] + ], + "layout": {"line-join": "round", "visibility": "visible"}, + "paint": { + "line-color": "#7f7d7e", + "line-dasharray": [3, 3], + "line-width": 2 + } + }, + { + "id": "railway", + "type": "line", + "metadata": {"mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5"}, + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 13, + "filter": [ + "all", + ["==", "$type", "LineString"], + ["all", ["!has", "service"], ["==", "class", "rail"]] + ], + "layout": {"line-join": "round", "visibility": "visible"}, + "paint": { + "line-color": "#545353", + "line-width": {"base": 1.3, "stops": [[16, 3], [20, 7]]} + } + }, + { + "id": "railway_dashline", + "type": "line", + "metadata": {"mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5"}, + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 13, + "filter": [ + "all", + ["==", "$type", "LineString"], + ["all", ["!has", "service"], ["==", "class", "rail"]] + ], + "layout": {"line-join": "round", "visibility": "visible"}, + "paint": { + "line-color": "#7f7d7e", + "line-dasharray": [3, 3], + "line-width": {"base": 1.3, "stops": [[16, 2], [20, 6]]} + } + }, + { + "id": "aeroway_line", + "type": "line", + "source": "openmaptiles", + "source-layer": "aeroway", + "filter": ["==", "$type", "LineString"], + "layout": {"visibility": "visible"}, + "paint": {"line-color": "#545353"} + }, + { + "id": "highway_motorway_bridge_casing", + "type": "line", + "metadata": {"mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5"}, + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 6, + "filter": [ + "all", + ["==", "$type", "LineString"], + ["all", ["==", "brunnel", "bridge"], ["==", "class", "motorway"]] + ], + "layout": { + "line-cap": "butt", + "line-join": "miter", + "visibility": "visible" + }, + "paint": { + "line-color": "hsla(0, 0%, 30%, 80%)", + "line-dasharray": [2, 0], + "line-opacity": 1, + "line-width": {"base": 1.4, "stops": [[5.8, 0], [6, 5], [20, 35]]} + } + }, + { + "id": "highway_motorway_bridge_inner", + "type": "line", + "metadata": {"mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5"}, + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 6, + "filter": [ + "all", + ["==", "$type", "LineString"], + ["all", ["==", "brunnel", "bridge"], ["==", "class", "motorway"]] + ], + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "hsla(60, 1%, 16%, 90%)", + "line-width": {"base": 1.4, "stops": [[4, 2], [6, 1.3], [20, 30]]} + } + } + ], + "id": "ciwf3o3u2008z2pmq7pmvm6xq" +} \ No newline at end of file