@geogdev/styles 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,320 @@
1
+ import languagesConfig from "./config/languages.json";
2
+
3
+ // Type definition for language entries
4
+ interface LanguageScriptPair {
5
+ lang: string;
6
+ full_name: string;
7
+ script: string;
8
+ }
9
+
10
+ // Export the language/script pairs from JSON config
11
+ export const LANGUAGE_SCRIPT_PAIRS: LanguageScriptPair[] = languagesConfig;
12
+
13
+ function get_name_block(
14
+ script_segment: "name" | "name2" | "name3",
15
+ regular?: string,
16
+ ) {
17
+ let script = "script";
18
+
19
+ if (script_segment === "name") {
20
+ script = "script";
21
+ } else if (script_segment === "name2") {
22
+ script = "script2";
23
+ } else if (script_segment === "name3") {
24
+ script = "script3";
25
+ }
26
+
27
+ return [
28
+ ["coalesce", ["get", `pgf:${script_segment}`], ["get", script_segment]],
29
+ {
30
+ "text-font": [
31
+ "case",
32
+ ["==", ["get", script], "Devanagari"],
33
+ ["literal", ["Noto Sans Devanagari Regular v1"]],
34
+ ["literal", [regular || "Noto Sans Regular"]],
35
+ ],
36
+ },
37
+ ];
38
+ }
39
+
40
+ function is_not_in_target_script(
41
+ lang: string,
42
+ script: string,
43
+ script_segment: "name" | "name2" | "name3",
44
+ ) {
45
+ let suffix = "name";
46
+ if (script_segment === "name") {
47
+ suffix = "";
48
+ } else if (script_segment === "name2") {
49
+ suffix = "2";
50
+ } else if (script_segment === "name3") {
51
+ suffix = "3";
52
+ }
53
+
54
+ if (script === "Latin") {
55
+ return ["has", `script${suffix}`];
56
+ }
57
+
58
+ if (lang === "ja") {
59
+ return [
60
+ "all",
61
+ ["!=", ["get", `script${suffix}`], "Han"],
62
+ ["!=", ["get", `script${suffix}`], "Hiragana"],
63
+ ["!=", ["get", `script${suffix}`], "Katakana"],
64
+ ["!=", ["get", `script${suffix}`], "Mixed-Japanese"],
65
+ ];
66
+ }
67
+
68
+ return ["!=", ["get", `script${suffix}`], script];
69
+ }
70
+
71
+ function get_font_formatting(script: string) {
72
+ if (script === "Devanagari") {
73
+ return {
74
+ "text-font": ["literal", ["Noto Sans Devanagari Regular v1"]],
75
+ };
76
+ }
77
+ return {};
78
+ }
79
+
80
+ function getDefaultScript(lang: string) {
81
+ const pair = LANGUAGE_SCRIPT_PAIRS.find((d) => d.lang === lang);
82
+ return pair === undefined ? "Latin" : pair.script;
83
+ }
84
+
85
+ export function getCountryName(lang: string, script?: string) {
86
+ const _script = script || getDefaultScript(lang);
87
+ let name_prefix: string;
88
+ if (_script === "Devanagari") {
89
+ name_prefix = "pgf:";
90
+ } else {
91
+ name_prefix = "";
92
+ }
93
+ return [
94
+ "format",
95
+ ["coalesce", ["get", `${name_prefix}name:${lang}`], ["get", "name:en"]],
96
+ get_font_formatting(_script),
97
+ ];
98
+ }
99
+
100
+ export function getMultilineName(
101
+ lang: string,
102
+ script?: string,
103
+ regular?: string,
104
+ ) {
105
+ const _script = script || getDefaultScript(lang);
106
+ let name_prefix: string;
107
+ if (_script === "Devanagari") {
108
+ name_prefix = "pgf:";
109
+ } else {
110
+ name_prefix = "";
111
+ }
112
+
113
+ const result = [
114
+ "case",
115
+ [
116
+ "all",
117
+ ["any", ["has", "name"], ["has", "pgf:name"]],
118
+ ["!", ["any", ["has", "name2"], ["has", "pgf:name2"]]],
119
+ ["!", ["any", ["has", "name3"], ["has", "pgf:name3"]]],
120
+ ],
121
+ // The local name has 1 script segment: `name`
122
+ [
123
+ "case",
124
+ is_not_in_target_script(lang, _script, "name"),
125
+ // `name` is not in the target script
126
+ [
127
+ "case",
128
+ ["any", ["is-supported-script", ["get", "name"]], ["has", "pgf:name"]],
129
+ // `name` can be rendered correctly
130
+ [
131
+ "format",
132
+ [
133
+ "coalesce",
134
+ ["get", `${name_prefix}name:${lang}`],
135
+ ["get", "name:en"], // Always fallback to English
136
+ ],
137
+ get_font_formatting(_script),
138
+ "\n",
139
+ {},
140
+ [
141
+ "case",
142
+ [
143
+ "all",
144
+ ["!", ["has", `${name_prefix}name:${lang}`]],
145
+ ["has", "name:en"],
146
+ ["!", ["has", "script"]],
147
+ ],
148
+ // We did fallback to English in the first line and `name` is Latin
149
+ "",
150
+ ["coalesce", ["get", "pgf:name"], ["get", "name"]],
151
+ ],
152
+ {
153
+ "text-font": [
154
+ "case",
155
+ ["==", ["get", "script"], "Devanagari"],
156
+ ["literal", ["Noto Sans Devanagari Regular v1"]],
157
+ ["literal", [regular || "Noto Sans Regular"]],
158
+ ],
159
+ },
160
+ ],
161
+ // `name` cannot be rendered correctly, fallback to `name:en`
162
+ ["get", "name:en"],
163
+ ],
164
+ // `name` is in the target script
165
+ [
166
+ "format",
167
+ [
168
+ "coalesce",
169
+ ["get", `${name_prefix}name:${lang}`],
170
+ ["get", "pgf:name"],
171
+ ["get", "name"],
172
+ ],
173
+ get_font_formatting(_script),
174
+ ],
175
+ ],
176
+ [
177
+ "all",
178
+ ["any", ["has", "name"], ["has", "pgf:name"]],
179
+ ["any", ["has", "name2"], ["has", "pgf:name2"]],
180
+ ["!", ["any", ["has", "name3"], ["has", "pgf:name3"]]],
181
+ ],
182
+ // The local name has 2 script segments: `name` and `name2`
183
+ [
184
+ "case",
185
+ [
186
+ "all",
187
+ is_not_in_target_script(lang, _script, "name"),
188
+ is_not_in_target_script(lang, _script, "name2"),
189
+ ],
190
+ // Both `name` and `name2` are not in the target script
191
+ [
192
+ "format",
193
+ ["get", `${name_prefix}name:${lang}`],
194
+ get_font_formatting(_script),
195
+ "\n",
196
+ {},
197
+ ...get_name_block("name", regular),
198
+ "\n",
199
+ {},
200
+ ...get_name_block("name2", regular),
201
+ ],
202
+ // Either `name` or `name2` is in the target script
203
+ [
204
+ "case",
205
+ is_not_in_target_script(lang, _script, "name2"),
206
+ // `name2` is not in the target script, therefore `name` is in the target script
207
+ [
208
+ "format",
209
+ [
210
+ "coalesce",
211
+ ["get", `${name_prefix}name:${lang}`],
212
+ ["get", "pgf:name"],
213
+ ["get", "name"],
214
+ ],
215
+ get_font_formatting(_script),
216
+ "\n",
217
+ {},
218
+ ...get_name_block("name2", regular),
219
+ ],
220
+ // `name2` is in the target script, therefore `name` is not in the target script
221
+ [
222
+ "format",
223
+ [
224
+ "coalesce",
225
+ ["get", `${name_prefix}name:${lang}`],
226
+ ["get", "pgf:name2"],
227
+ ["get", "name2"],
228
+ ],
229
+ get_font_formatting(_script),
230
+ "\n",
231
+ {},
232
+ ...get_name_block("name", regular),
233
+ ],
234
+ ],
235
+ ],
236
+ // The local name has 3 script segments: `name`, `name2`, and `name3`
237
+ [
238
+ "case",
239
+ [
240
+ "all",
241
+ is_not_in_target_script(lang, _script, "name"),
242
+ is_not_in_target_script(lang, _script, "name2"),
243
+ is_not_in_target_script(lang, _script, "name3"),
244
+ ],
245
+ // All three `name`, `name2`, and `name3` are not in the target script
246
+ [
247
+ "format",
248
+ ["get", `${name_prefix}name:${lang}`],
249
+ get_font_formatting(_script),
250
+ "\n",
251
+ {},
252
+ ...get_name_block("name", regular),
253
+ "\n",
254
+ {},
255
+ ...get_name_block("name2", regular),
256
+ "\n",
257
+ {},
258
+ ...get_name_block("name3", regular),
259
+ ],
260
+ // Exactly one of the 3 script segments `name`, `name2`, or `name3` is in the target script
261
+ [
262
+ "case",
263
+ ["!", is_not_in_target_script(lang, _script, "name")],
264
+ // `name` is in the target script, and `name2` and `name3` are not
265
+ [
266
+ "format",
267
+ [
268
+ "coalesce",
269
+ ["get", `${name_prefix}name:${lang}`],
270
+ ["get", "pgf:name"],
271
+ ["get", "name"],
272
+ ],
273
+ get_font_formatting(_script),
274
+ "\n",
275
+ {},
276
+ ...get_name_block("name2", regular),
277
+ "\n",
278
+ {},
279
+ ...get_name_block("name3", regular),
280
+ ],
281
+ ["!", is_not_in_target_script(lang, _script, "name2")],
282
+ // `name2` is in the target script, and `name` and `name3` are not
283
+ [
284
+ "format",
285
+ [
286
+ "coalesce",
287
+ ["get", `${name_prefix}name:${lang}`],
288
+ ["get", "pgf:name2"],
289
+ ["get", "name2"],
290
+ ],
291
+ get_font_formatting(_script),
292
+ "\n",
293
+ {},
294
+ ...get_name_block("name", regular),
295
+ "\n",
296
+ {},
297
+ ...get_name_block("name3", regular),
298
+ ],
299
+ // `name3` is in the target script, and `name` and `name2` are not
300
+ [
301
+ "format",
302
+ [
303
+ "coalesce",
304
+ ["get", `${name_prefix}name:${lang}`],
305
+ ["get", "pgf:name3"],
306
+ ["get", "name3"],
307
+ ],
308
+ get_font_formatting(_script),
309
+ "\n",
310
+ {},
311
+ ...get_name_block("name", regular),
312
+ "\n",
313
+ {},
314
+ ...get_name_block("name2", regular),
315
+ ],
316
+ ],
317
+ ],
318
+ ];
319
+ return result;
320
+ }
@@ -0,0 +1,91 @@
1
+ import type { LayerSpecification } from "@maplibre/maplibre-gl-style-spec";
2
+ import { FILTERS, ZOOM } from "../constants";
3
+ import type { Style } from "../styles";
4
+
5
+ /**
6
+ * Creates background and base terrain layers
7
+ */
8
+ export function createBackgroundLayers(
9
+ source: string,
10
+ style: Style,
11
+ ): LayerSpecification[] {
12
+ return [
13
+ // Background color
14
+ {
15
+ id: "background",
16
+ type: "background",
17
+ paint: {
18
+ "background-color": style.background,
19
+ },
20
+ },
21
+ // Earth (land) polygons
22
+ {
23
+ id: "earth",
24
+ type: "fill",
25
+ filter: FILTERS.IS_POLYGON,
26
+ source,
27
+ "source-layer": "earth",
28
+ paint: {
29
+ "fill-color": style.earth,
30
+ },
31
+ },
32
+ ];
33
+ }
34
+
35
+ /**
36
+ * Creates water layers (polygons and waterways)
37
+ */
38
+ export function createWaterLayers(
39
+ source: string,
40
+ style: Style,
41
+ ): LayerSpecification[] {
42
+ return [
43
+ // Water polygons
44
+ {
45
+ id: "water",
46
+ type: "fill",
47
+ filter: FILTERS.IS_POLYGON,
48
+ source,
49
+ "source-layer": "water",
50
+ paint: {
51
+ "fill-color": style.water,
52
+ },
53
+ },
54
+ // Stream waterways
55
+ {
56
+ id: "water_stream",
57
+ type: "line",
58
+ source,
59
+ "source-layer": "waterway",
60
+ minzoom: ZOOM.WATERWAY_STREAM_MIN,
61
+ filter: ["==", "class", "stream"],
62
+ paint: {
63
+ "line-color": style.water,
64
+ "line-width": 0.5,
65
+ },
66
+ },
67
+ // River waterways
68
+ {
69
+ id: "water_river",
70
+ type: "line",
71
+ source,
72
+ "source-layer": "waterway",
73
+ minzoom: ZOOM.WATERWAY_RIVER_MIN,
74
+ filter: ["==", "class", "river"],
75
+ paint: {
76
+ "line-color": style.water,
77
+ "line-width": [
78
+ "interpolate",
79
+ ["exponential", 1.6],
80
+ ["zoom"],
81
+ 9,
82
+ 0,
83
+ 9.5,
84
+ 1.0,
85
+ 18,
86
+ 12,
87
+ ],
88
+ },
89
+ },
90
+ ];
91
+ }
@@ -0,0 +1,51 @@
1
+ import type { LayerSpecification } from "@maplibre/maplibre-gl-style-spec";
2
+ import type { Style } from "../styles";
3
+
4
+ /**
5
+ * Creates administrative boundary layers
6
+ */
7
+ export function createBoundaryLayers(
8
+ source: string,
9
+ style: Style,
10
+ ): LayerSpecification[] {
11
+ return [
12
+ // Country boundaries (admin_level <= 2)
13
+ {
14
+ id: "boundaries_country",
15
+ type: "line",
16
+ source,
17
+ "source-layer": "boundary",
18
+ filter: ["<=", "admin_level", 2],
19
+ paint: {
20
+ "line-color": style.boundaries,
21
+ "line-width": 0.7,
22
+ "line-dasharray": [
23
+ "step",
24
+ ["zoom"],
25
+ ["literal", [2, 0]],
26
+ 4,
27
+ ["literal", [2, 1]],
28
+ ],
29
+ },
30
+ },
31
+ // Other administrative boundaries (admin_level > 2)
32
+ {
33
+ id: "boundaries",
34
+ type: "line",
35
+ source,
36
+ "source-layer": "boundary",
37
+ filter: [">", "admin_level", 2],
38
+ paint: {
39
+ "line-color": style.boundaries,
40
+ "line-width": 0.4,
41
+ "line-dasharray": [
42
+ "step",
43
+ ["zoom"],
44
+ ["literal", [2, 0]],
45
+ 4,
46
+ ["literal", [2, 1]],
47
+ ],
48
+ },
49
+ },
50
+ ];
51
+ }
@@ -0,0 +1,23 @@
1
+ import type { LayerSpecification } from "@maplibre/maplibre-gl-style-spec";
2
+ import type { Style } from "../styles";
3
+
4
+ /**
5
+ * Creates building layers
6
+ */
7
+ export function createBuildingLayers(
8
+ source: string,
9
+ style: Style,
10
+ ): LayerSpecification[] {
11
+ return [
12
+ {
13
+ id: "buildings",
14
+ type: "fill",
15
+ source,
16
+ "source-layer": "building",
17
+ paint: {
18
+ "fill-color": style.buildings,
19
+ "fill-opacity": 0.5,
20
+ },
21
+ },
22
+ ];
23
+ }
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Layer generation module
3
+ *
4
+ * This module coordinates the creation of all MapLibre GL layers
5
+ * from the component modules.
6
+ */
7
+
8
+ import type { LayerSpecification } from "@maplibre/maplibre-gl-style-spec";
9
+ import type { Style } from "../styles";
10
+
11
+ // Import layer creators from component modules
12
+ import { createBackgroundLayers, createWaterLayers } from "./background";
13
+ import { createBoundaryLayers } from "./boundaries";
14
+ import { createBuildingLayers } from "./buildings";
15
+ import {
16
+ createMiscLabels,
17
+ createPlaceLabels,
18
+ createRoadLabels,
19
+ createWaterLabels,
20
+ } from "./labels";
21
+ import {
22
+ createAerowayLayers,
23
+ createLandcoverLayers,
24
+ createLanduseLayers,
25
+ createParkLayers,
26
+ } from "./landcover";
27
+ import { createPoiLayers } from "./pois";
28
+ import {
29
+ createBridgeLayers,
30
+ createPierLayer,
31
+ createRoadLayers,
32
+ createTunnelLayers,
33
+ } from "./roads";
34
+ import { createTransitLayers } from "./transit";
35
+
36
+ /**
37
+ * Creates all non-label layers (fills, lines, symbols without text)
38
+ */
39
+ export function nolabelsLayers(
40
+ source: string,
41
+ style: Style,
42
+ ): LayerSpecification[] {
43
+ return [
44
+ // Background and earth
45
+ ...createBackgroundLayers(source, style),
46
+
47
+ // Natural features
48
+ ...createLandcoverLayers(source, style),
49
+ ...createParkLayers(source, style),
50
+ ...createLanduseLayers(source, style),
51
+ ...createAerowayLayers(source, style),
52
+
53
+ // Water
54
+ ...createWaterLayers(source, style),
55
+
56
+ // Roads - tunnels (below other features)
57
+ ...createTunnelLayers(source, style),
58
+
59
+ // Buildings
60
+ ...createBuildingLayers(source, style),
61
+
62
+ // Roads - surface level
63
+ createPierLayer(source, style),
64
+ ...createRoadLayers(source, style),
65
+
66
+ // Transit
67
+ ...createTransitLayers(source, style),
68
+
69
+ // Boundaries
70
+ ...createBoundaryLayers(source, style),
71
+
72
+ // Roads - bridges (above other features)
73
+ ...createBridgeLayers(source, style),
74
+ ];
75
+ }
76
+
77
+ /**
78
+ * Creates all label layers (text symbols)
79
+ */
80
+ export function labelsLayers(
81
+ source: string,
82
+ style: Style,
83
+ lang: string,
84
+ script?: string,
85
+ ): LayerSpecification[] {
86
+ return [
87
+ // Misc labels (addresses, islands)
88
+ ...createMiscLabels(source, style, lang, script),
89
+
90
+ // Water labels
91
+ ...createWaterLabels(source, style, lang, script),
92
+
93
+ // Road labels
94
+ ...createRoadLabels(source, style, lang, script),
95
+
96
+ // POI labels
97
+ ...createPoiLayers(source, style, lang, script),
98
+
99
+ // Place labels (subplaces, cities, regions, countries)
100
+ ...createPlaceLabels(source, style, lang, script),
101
+ ];
102
+ }
103
+
104
+ // Re-export component modules for direct access if needed
105
+ export {
106
+ createBackgroundLayers,
107
+ createWaterLayers,
108
+ } from "./background";
109
+ export { createBoundaryLayers } from "./boundaries";
110
+ export { createBuildingLayers } from "./buildings";
111
+ export {
112
+ createLabelLayer,
113
+ createMiscLabels,
114
+ createPlaceLabels,
115
+ createRoadLabels,
116
+ createWaterLabels,
117
+ } from "./labels";
118
+ export {
119
+ createAerowayLayers,
120
+ createLandcoverLayers,
121
+ createLanduseLayers,
122
+ createParkLayers,
123
+ } from "./landcover";
124
+ export { createPoiLayers } from "./pois";
125
+ export {
126
+ createBridgeLayers,
127
+ createPierLayer,
128
+ createRoadLayer,
129
+ createRoadLayers,
130
+ createTunnelLayers,
131
+ } from "./roads";
132
+ export { createTransitLayers } from "./transit";