@tileflow/core 0.1.0-alpha.0 → 0.1.0-alpha.1
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.
- package/README.md +18 -9
- package/dist/index.d.ts +70 -9
- package/dist/index.js +521 -210
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -3,19 +3,28 @@
|
|
|
3
3
|
Typed Tileflow config helpers and the MapLibre style compiler.
|
|
4
4
|
|
|
5
5
|
```ts
|
|
6
|
-
import {
|
|
6
|
+
import {defineTileflow, osm, roads} from '@tileflow/core';
|
|
7
7
|
|
|
8
8
|
export default defineTileflow({
|
|
9
|
+
themes: {
|
|
10
|
+
light: {
|
|
11
|
+
mode: 'light',
|
|
12
|
+
colors: {
|
|
13
|
+
water: '#8ED6E8',
|
|
14
|
+
park: '#C3F1D5',
|
|
15
|
+
road: '#D8E0E7',
|
|
16
|
+
text: '#566371',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
9
20
|
maps: {
|
|
10
21
|
madrid: {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
text: "#2E2E2E",
|
|
18
|
-
accent: "#C6A15B",
|
|
22
|
+
basemap: osm(),
|
|
23
|
+
theme: 'light',
|
|
24
|
+
modules: [roads({preset: 'modern', density: 'balanced'})],
|
|
25
|
+
view: {
|
|
26
|
+
center: [-3.7038, 40.4168],
|
|
27
|
+
zoom: 12,
|
|
19
28
|
},
|
|
20
29
|
},
|
|
21
30
|
},
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
type TileflowDensity =
|
|
2
|
-
type TileflowRoads =
|
|
3
|
-
type TileflowLabels =
|
|
4
|
-
type TileflowPoi =
|
|
1
|
+
type TileflowDensity = 'clean' | 'balanced' | 'dense';
|
|
2
|
+
type TileflowRoads = 'hidden' | 'soft' | 'standard' | 'detailed';
|
|
3
|
+
type TileflowLabels = 'none' | 'essential' | 'balanced' | 'full';
|
|
4
|
+
type TileflowPoi = 'none' | 'minimal' | 'balanced' | 'full';
|
|
5
|
+
type TileflowTheme = 'standard' | 'light' | 'dark' | 'minimal' | 'osm-bright-2';
|
|
6
|
+
type TileflowBuildings = 'hidden' | 'flat' | '3d';
|
|
7
|
+
type TileflowTerrainMode = 'none' | 'hillshade' | '3d';
|
|
8
|
+
type TileflowTerrainEncoding = 'mapbox' | 'terrarium';
|
|
9
|
+
type TileflowThemeMode = 'light' | 'dark';
|
|
5
10
|
type TileflowColors = {
|
|
6
11
|
canvas: string;
|
|
7
12
|
land: string;
|
|
@@ -15,13 +20,19 @@ type LegacyTileflowColorAliases = {
|
|
|
15
20
|
background?: string;
|
|
16
21
|
park?: string;
|
|
17
22
|
building?: string;
|
|
23
|
+
roadCasing?: string;
|
|
18
24
|
};
|
|
19
25
|
type TileflowColorConfig = Partial<TileflowColors> & LegacyTileflowColorAliases;
|
|
20
|
-
type TileflowFontWeight =
|
|
26
|
+
type TileflowFontWeight = 'regular' | 'medium' | 'semibold' | 'bold';
|
|
21
27
|
type TileflowTypography = {
|
|
22
28
|
font?: string;
|
|
23
29
|
weight?: TileflowFontWeight;
|
|
24
30
|
};
|
|
31
|
+
type TileflowThemeConfig = {
|
|
32
|
+
mode?: TileflowThemeMode;
|
|
33
|
+
colors?: TileflowColorConfig;
|
|
34
|
+
typography?: TileflowTypography;
|
|
35
|
+
};
|
|
25
36
|
type TileflowSourceLayers = {
|
|
26
37
|
landuse?: string;
|
|
27
38
|
park?: string;
|
|
@@ -39,18 +50,59 @@ type TileflowTiles = {
|
|
|
39
50
|
attribution?: string;
|
|
40
51
|
sourceLayers?: TileflowSourceLayers;
|
|
41
52
|
};
|
|
53
|
+
type TileflowTerrainConfig = {
|
|
54
|
+
attribution?: string;
|
|
55
|
+
encoding?: TileflowTerrainEncoding;
|
|
56
|
+
exaggeration?: number;
|
|
57
|
+
mode?: TileflowTerrainMode;
|
|
58
|
+
sourceId?: string;
|
|
59
|
+
tileset?: string;
|
|
60
|
+
url?: string;
|
|
61
|
+
};
|
|
62
|
+
type TileflowTerrain = TileflowTerrainMode | TileflowTerrainConfig;
|
|
63
|
+
type TileflowViewConfig = {
|
|
64
|
+
bearing?: number;
|
|
65
|
+
center?: readonly [number, number];
|
|
66
|
+
zoom?: number;
|
|
67
|
+
};
|
|
68
|
+
type TileflowOsmBasemapConfig = {
|
|
69
|
+
type: 'osm';
|
|
70
|
+
attribution?: string;
|
|
71
|
+
glyphs?: string;
|
|
72
|
+
sourceId?: string;
|
|
73
|
+
sourceLayers?: TileflowSourceLayers;
|
|
74
|
+
sprite?: string;
|
|
75
|
+
tileset?: string;
|
|
76
|
+
url?: string;
|
|
77
|
+
};
|
|
78
|
+
type TileflowBasemapConfig = TileflowOsmBasemapConfig;
|
|
79
|
+
type TileflowOsmBasemapOptions = Omit<TileflowOsmBasemapConfig, 'type'>;
|
|
80
|
+
type TileflowRoadsModulePreset = 'modern' | 'minimal';
|
|
81
|
+
type TileflowRoadsModuleConfig = {
|
|
82
|
+
type: 'roads';
|
|
83
|
+
density?: TileflowDensity;
|
|
84
|
+
preset?: TileflowRoadsModulePreset;
|
|
85
|
+
};
|
|
86
|
+
type TileflowRoadsModuleOptions = Omit<TileflowRoadsModuleConfig, 'type'>;
|
|
87
|
+
type TileflowModuleConfig = TileflowRoadsModuleConfig;
|
|
42
88
|
type TileflowConfig = {
|
|
89
|
+
basemap?: TileflowBasemapConfig;
|
|
43
90
|
name?: string;
|
|
44
91
|
tileset?: string;
|
|
92
|
+
theme?: TileflowTheme | string | TileflowThemeConfig;
|
|
93
|
+
modules?: readonly TileflowModuleConfig[];
|
|
45
94
|
colors?: TileflowColorConfig;
|
|
46
95
|
typography?: TileflowTypography;
|
|
47
96
|
density?: TileflowDensity;
|
|
48
97
|
roads?: TileflowRoads;
|
|
49
98
|
labels?: TileflowLabels;
|
|
50
99
|
poi?: TileflowPoi;
|
|
100
|
+
buildings?: TileflowBuildings;
|
|
101
|
+
terrain?: TileflowTerrain;
|
|
51
102
|
glyphs?: string;
|
|
52
103
|
sprite?: string;
|
|
53
104
|
tiles?: TileflowTiles;
|
|
105
|
+
view?: TileflowViewConfig;
|
|
54
106
|
/**
|
|
55
107
|
* Domains allowed to load this map in production (e.g. "example.com" or
|
|
56
108
|
* "*.example.com"). When set, tile and session requests from other
|
|
@@ -67,10 +119,12 @@ type TileflowTilesetConfig = {
|
|
|
67
119
|
sourceLayers?: TileflowSourceLayers;
|
|
68
120
|
};
|
|
69
121
|
type TileflowProjectConfig = {
|
|
122
|
+
themes?: Record<string, TileflowThemeConfig>;
|
|
70
123
|
tilesets?: Record<string, TileflowTilesetConfig>;
|
|
71
124
|
maps: Record<string, TileflowMapConfig>;
|
|
72
125
|
};
|
|
73
126
|
type TileflowStyleOptions = {
|
|
127
|
+
themes?: Record<string, TileflowThemeConfig>;
|
|
74
128
|
tileBaseUrl?: string;
|
|
75
129
|
tileset?: string;
|
|
76
130
|
};
|
|
@@ -87,10 +141,11 @@ type MapLibreStyle = {
|
|
|
87
141
|
sprite?: string;
|
|
88
142
|
sources: Record<string, Record<string, unknown>>;
|
|
89
143
|
layers: Array<Record<string, unknown>>;
|
|
144
|
+
terrain?: Record<string, unknown>;
|
|
90
145
|
metadata?: Record<string, unknown>;
|
|
91
146
|
};
|
|
92
147
|
type ValidationMessage = {
|
|
93
|
-
level:
|
|
148
|
+
level: 'error' | 'warning';
|
|
94
149
|
path: string;
|
|
95
150
|
message: string;
|
|
96
151
|
};
|
|
@@ -98,8 +153,6 @@ type ValidationResult = {
|
|
|
98
153
|
valid: boolean;
|
|
99
154
|
messages: ValidationMessage[];
|
|
100
155
|
};
|
|
101
|
-
declare function defineTileflow(config: TileflowProjectConfig): TileflowProjectConfig;
|
|
102
|
-
declare function validateConfig(input: unknown): ValidationResult;
|
|
103
156
|
declare function createStyle(input?: TileflowConfig, options?: TileflowStyleOptions): MapLibreStyle;
|
|
104
157
|
declare function createStyleFromProject(projectConfig: TileflowProjectConfig, mapName: string, options?: TileflowStyleOptions): MapLibreStyle;
|
|
105
158
|
declare function createManifest(projectConfig: TileflowProjectConfig, options?: {
|
|
@@ -108,4 +161,12 @@ declare function createManifest(projectConfig: TileflowProjectConfig, options?:
|
|
|
108
161
|
}): TileflowManifest;
|
|
109
162
|
declare function getDefaultColors(): TileflowColors;
|
|
110
163
|
|
|
111
|
-
|
|
164
|
+
declare function validateConfig(input: unknown): ValidationResult;
|
|
165
|
+
|
|
166
|
+
declare function defineTileflow(config: TileflowProjectConfig): TileflowProjectConfig;
|
|
167
|
+
|
|
168
|
+
declare function osm(options?: TileflowOsmBasemapOptions): TileflowOsmBasemapConfig;
|
|
169
|
+
|
|
170
|
+
declare function roads(options?: TileflowRoadsModuleOptions): TileflowRoadsModuleConfig;
|
|
171
|
+
|
|
172
|
+
export { type MapLibreStyle, type TileflowBasemapConfig, type TileflowBuildings, type TileflowColorConfig, type TileflowColors, type TileflowConfig, type TileflowDensity, type TileflowFontWeight, type TileflowLabels, type TileflowManifest, type TileflowMapConfig, type TileflowModuleConfig, type TileflowOsmBasemapConfig, type TileflowOsmBasemapOptions, type TileflowPoi, type TileflowProjectConfig, type TileflowRoads, type TileflowRoadsModuleConfig, type TileflowRoadsModuleOptions, type TileflowRoadsModulePreset, type TileflowSourceLayers, type TileflowStyleOptions, type TileflowTerrain, type TileflowTerrainConfig, type TileflowTerrainEncoding, type TileflowTerrainMode, type TileflowTheme, type TileflowThemeConfig, type TileflowThemeMode, type TileflowTiles, type TileflowTilesetConfig, type TileflowTypography, type TileflowViewConfig, type ValidationMessage, type ValidationResult, createManifest, createStyle, createStyleFromProject, defineTileflow, getDefaultColors, osm, roads, validateConfig };
|
package/dist/index.js
CHANGED
|
@@ -1,31 +1,97 @@
|
|
|
1
|
-
// src/
|
|
1
|
+
// src/basemaps.ts
|
|
2
|
+
function osm(options = {}) {
|
|
3
|
+
return {
|
|
4
|
+
type: "osm",
|
|
5
|
+
...options
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// src/schema.ts
|
|
2
10
|
import { z } from "zod";
|
|
11
|
+
var legacyThemeNames = /* @__PURE__ */ new Set(["standard", "light", "dark", "minimal", "osm-bright-2"]);
|
|
3
12
|
var hexColorSchema = z.string().regex(/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/, {
|
|
4
|
-
message: "Expected a hex color like #
|
|
13
|
+
message: "Expected a hex color like #F6F7F3 or #F6F7F3FF"
|
|
14
|
+
});
|
|
15
|
+
var colorConfigSchema = z.object({
|
|
16
|
+
canvas: hexColorSchema.optional(),
|
|
17
|
+
land: hexColorSchema.optional(),
|
|
18
|
+
background: hexColorSchema.optional(),
|
|
19
|
+
water: hexColorSchema.optional(),
|
|
20
|
+
nature: hexColorSchema.optional(),
|
|
21
|
+
text: hexColorSchema.optional(),
|
|
22
|
+
accent: hexColorSchema.optional(),
|
|
23
|
+
park: hexColorSchema.optional(),
|
|
24
|
+
road: hexColorSchema.optional(),
|
|
25
|
+
roadCasing: hexColorSchema.optional(),
|
|
26
|
+
building: hexColorSchema.optional()
|
|
27
|
+
});
|
|
28
|
+
var typographySchema = z.object({
|
|
29
|
+
font: z.string().trim().min(1).optional(),
|
|
30
|
+
weight: z.enum(["regular", "medium", "semibold", "bold"]).optional()
|
|
31
|
+
});
|
|
32
|
+
var themeConfigSchema = z.object({
|
|
33
|
+
mode: z.enum(["light", "dark"]).optional(),
|
|
34
|
+
colors: colorConfigSchema.optional(),
|
|
35
|
+
typography: typographySchema.optional()
|
|
36
|
+
});
|
|
37
|
+
var sourceLayersSchema = z.object({
|
|
38
|
+
landuse: z.string().optional(),
|
|
39
|
+
park: z.string().optional(),
|
|
40
|
+
water: z.string().optional(),
|
|
41
|
+
road: z.string().optional(),
|
|
42
|
+
building: z.string().optional(),
|
|
43
|
+
boundary: z.string().optional(),
|
|
44
|
+
place: z.string().optional(),
|
|
45
|
+
poi: z.string().optional()
|
|
46
|
+
});
|
|
47
|
+
var basemapSchema = z.object({
|
|
48
|
+
type: z.literal("osm"),
|
|
49
|
+
attribution: z.string().optional(),
|
|
50
|
+
glyphs: z.string().min(1).optional(),
|
|
51
|
+
sourceId: z.string().min(1).optional(),
|
|
52
|
+
sourceLayers: sourceLayersSchema.optional(),
|
|
53
|
+
sprite: z.string().min(1).optional(),
|
|
54
|
+
tileset: z.string().trim().min(1).optional(),
|
|
55
|
+
url: z.url().optional()
|
|
56
|
+
});
|
|
57
|
+
var roadsModuleSchema = z.object({
|
|
58
|
+
type: z.literal("roads"),
|
|
59
|
+
density: z.enum(["clean", "balanced", "dense"]).optional(),
|
|
60
|
+
preset: z.enum(["modern", "minimal"]).optional()
|
|
61
|
+
});
|
|
62
|
+
var moduleSchema = z.discriminatedUnion("type", [roadsModuleSchema]);
|
|
63
|
+
var viewSchema = z.object({
|
|
64
|
+
bearing: z.number().finite().min(-180).max(180).optional(),
|
|
65
|
+
center: z.tuple([z.number().finite(), z.number().finite()]).refine(([lng, lat]) => lng >= -180 && lng <= 180 && lat >= -90 && lat <= 90, {
|
|
66
|
+
message: "Expected center as [lng, lat] within world bounds"
|
|
67
|
+
}).optional(),
|
|
68
|
+
zoom: z.number().finite().min(0).max(24).optional()
|
|
5
69
|
});
|
|
6
70
|
var configSchema = z.object({
|
|
71
|
+
basemap: basemapSchema.optional(),
|
|
7
72
|
name: z.string().min(1).optional(),
|
|
8
73
|
tileset: z.string().trim().min(1).optional(),
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
water: hexColorSchema.optional(),
|
|
14
|
-
nature: hexColorSchema.optional(),
|
|
15
|
-
text: hexColorSchema.optional(),
|
|
16
|
-
accent: hexColorSchema.optional(),
|
|
17
|
-
park: hexColorSchema.optional(),
|
|
18
|
-
road: hexColorSchema.optional(),
|
|
19
|
-
building: hexColorSchema.optional()
|
|
20
|
-
}).optional(),
|
|
21
|
-
typography: z.object({
|
|
22
|
-
font: z.string().trim().min(1).optional(),
|
|
23
|
-
weight: z.enum(["regular", "medium", "semibold", "bold"]).optional()
|
|
24
|
-
}).optional(),
|
|
74
|
+
theme: z.union([z.string().trim().min(1), themeConfigSchema]).optional(),
|
|
75
|
+
modules: z.array(moduleSchema).optional(),
|
|
76
|
+
colors: colorConfigSchema.optional(),
|
|
77
|
+
typography: typographySchema.optional(),
|
|
25
78
|
density: z.enum(["clean", "balanced", "dense"]).optional(),
|
|
26
79
|
roads: z.enum(["hidden", "soft", "standard", "detailed"]).optional(),
|
|
27
80
|
labels: z.enum(["none", "essential", "balanced", "full"]).optional(),
|
|
28
81
|
poi: z.enum(["none", "minimal", "balanced", "full"]).optional(),
|
|
82
|
+
buildings: z.enum(["hidden", "flat", "3d"]).optional(),
|
|
83
|
+
terrain: z.union([
|
|
84
|
+
z.enum(["none", "hillshade", "3d"]),
|
|
85
|
+
z.object({
|
|
86
|
+
attribution: z.string().optional(),
|
|
87
|
+
encoding: z.enum(["mapbox", "terrarium"]).optional(),
|
|
88
|
+
exaggeration: z.number().min(0).max(10).optional(),
|
|
89
|
+
mode: z.enum(["none", "hillshade", "3d"]).optional(),
|
|
90
|
+
sourceId: z.string().min(1).optional(),
|
|
91
|
+
tileset: z.string().trim().min(1).optional(),
|
|
92
|
+
url: z.url().optional()
|
|
93
|
+
})
|
|
94
|
+
]).optional(),
|
|
29
95
|
glyphs: z.string().min(1).optional(),
|
|
30
96
|
sprite: z.string().min(1).optional(),
|
|
31
97
|
tiles: z.object({
|
|
@@ -33,40 +99,88 @@ var configSchema = z.object({
|
|
|
33
99
|
tileset: z.string().trim().min(1).optional(),
|
|
34
100
|
url: z.url().optional(),
|
|
35
101
|
attribution: z.string().optional(),
|
|
36
|
-
sourceLayers:
|
|
37
|
-
landuse: z.string().optional(),
|
|
38
|
-
park: z.string().optional(),
|
|
39
|
-
water: z.string().optional(),
|
|
40
|
-
road: z.string().optional(),
|
|
41
|
-
building: z.string().optional(),
|
|
42
|
-
boundary: z.string().optional(),
|
|
43
|
-
place: z.string().optional(),
|
|
44
|
-
poi: z.string().optional()
|
|
45
|
-
}).optional()
|
|
102
|
+
sourceLayers: sourceLayersSchema.optional()
|
|
46
103
|
}).optional(),
|
|
104
|
+
view: viewSchema.optional(),
|
|
47
105
|
allowedOrigins: z.array(z.string().trim().min(1).max(253)).max(20).optional()
|
|
48
106
|
});
|
|
49
107
|
var tilesetConfigSchema = z.object({
|
|
50
108
|
id: z.string().trim().min(1).optional(),
|
|
51
109
|
name: z.string().trim().min(1).optional(),
|
|
52
110
|
attribution: z.string().optional(),
|
|
53
|
-
sourceLayers:
|
|
54
|
-
landuse: z.string().optional(),
|
|
55
|
-
park: z.string().optional(),
|
|
56
|
-
water: z.string().optional(),
|
|
57
|
-
road: z.string().optional(),
|
|
58
|
-
building: z.string().optional(),
|
|
59
|
-
boundary: z.string().optional(),
|
|
60
|
-
place: z.string().optional(),
|
|
61
|
-
poi: z.string().optional()
|
|
62
|
-
}).optional()
|
|
111
|
+
sourceLayers: sourceLayersSchema.optional()
|
|
63
112
|
});
|
|
64
113
|
var projectConfigSchema = z.object({
|
|
114
|
+
themes: z.record(z.string(), themeConfigSchema).optional(),
|
|
65
115
|
tilesets: z.record(z.string(), tilesetConfigSchema).optional(),
|
|
66
116
|
maps: z.record(z.string(), configSchema).refine((maps) => {
|
|
67
117
|
return Object.keys(maps).length > 0;
|
|
68
118
|
}, "Expected at least one map")
|
|
69
119
|
});
|
|
120
|
+
function validateConfig(input) {
|
|
121
|
+
if (isProjectConfigLike(input)) {
|
|
122
|
+
const parsed2 = projectConfigSchema.safeParse(input);
|
|
123
|
+
if (parsed2.success) {
|
|
124
|
+
const messages = validateProjectThemeReferences(parsed2.data);
|
|
125
|
+
if (messages.length > 0) {
|
|
126
|
+
return { valid: false, messages };
|
|
127
|
+
}
|
|
128
|
+
return { valid: true, messages: [] };
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
valid: false,
|
|
132
|
+
messages: parsed2.error.issues.map((issue) => ({
|
|
133
|
+
level: "error",
|
|
134
|
+
path: issue.path.join(".") || "config",
|
|
135
|
+
message: issue.message
|
|
136
|
+
}))
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
const parsed = configSchema.safeParse(input);
|
|
140
|
+
if (parsed.success) {
|
|
141
|
+
const messages = validateMapThemeReference(parsed.data, void 0, "theme");
|
|
142
|
+
if (messages.length > 0) {
|
|
143
|
+
return { valid: false, messages };
|
|
144
|
+
}
|
|
145
|
+
return { valid: true, messages: [] };
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
valid: false,
|
|
149
|
+
messages: parsed.error.issues.map((issue) => ({
|
|
150
|
+
level: "error",
|
|
151
|
+
path: issue.path.join(".") || "config",
|
|
152
|
+
message: issue.message
|
|
153
|
+
}))
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function isProjectConfigLike(input) {
|
|
157
|
+
return Boolean(
|
|
158
|
+
input && typeof input === "object" && "maps" in input && typeof input.maps === "object"
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
function validateProjectThemeReferences(project) {
|
|
162
|
+
const themeNames = new Set(Object.keys(project.themes ?? {}));
|
|
163
|
+
return Object.entries(project.maps).flatMap(
|
|
164
|
+
([mapName, mapConfig]) => validateMapThemeReference(mapConfig, themeNames, `maps.${mapName}.theme`)
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
function validateMapThemeReference(config, themeNames, path) {
|
|
168
|
+
if (typeof config.theme !== "string") {
|
|
169
|
+
return [];
|
|
170
|
+
}
|
|
171
|
+
if (legacyThemeNames.has(config.theme) || themeNames?.has(config.theme)) {
|
|
172
|
+
return [];
|
|
173
|
+
}
|
|
174
|
+
return [
|
|
175
|
+
{
|
|
176
|
+
level: "error",
|
|
177
|
+
path,
|
|
178
|
+
message: `Unknown Tileflow theme "${config.theme}"`
|
|
179
|
+
}
|
|
180
|
+
];
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// src/compiler.ts
|
|
70
184
|
var defaultSourceLayers = {
|
|
71
185
|
landuse: "landuse",
|
|
72
186
|
park: "park",
|
|
@@ -78,20 +192,97 @@ var defaultSourceLayers = {
|
|
|
78
192
|
poi: "poi"
|
|
79
193
|
};
|
|
80
194
|
var defaultColors = {
|
|
81
|
-
canvas: "#
|
|
82
|
-
land: "#
|
|
83
|
-
water: "#
|
|
84
|
-
nature: "#
|
|
85
|
-
road: "#
|
|
86
|
-
text: "#
|
|
87
|
-
accent: "#
|
|
195
|
+
canvas: "#F6F7F3",
|
|
196
|
+
land: "#F1F3ED",
|
|
197
|
+
water: "#A9D3F5",
|
|
198
|
+
nature: "#CDE8B5",
|
|
199
|
+
road: "#FFFFFF",
|
|
200
|
+
text: "#3C4043",
|
|
201
|
+
accent: "#F4C95D"
|
|
88
202
|
};
|
|
89
203
|
var defaultConfig = {
|
|
90
|
-
density: "
|
|
91
|
-
roads: "
|
|
92
|
-
labels: "
|
|
204
|
+
density: "balanced",
|
|
205
|
+
roads: "standard",
|
|
206
|
+
labels: "balanced",
|
|
93
207
|
poi: "minimal"
|
|
94
208
|
};
|
|
209
|
+
var themePresets = {
|
|
210
|
+
standard: {
|
|
211
|
+
colors: defaultColors,
|
|
212
|
+
density: "balanced",
|
|
213
|
+
labels: "balanced",
|
|
214
|
+
poi: "minimal",
|
|
215
|
+
roads: "standard"
|
|
216
|
+
},
|
|
217
|
+
light: {
|
|
218
|
+
colors: {
|
|
219
|
+
canvas: "#F9FAF8",
|
|
220
|
+
land: "#F6F7F3",
|
|
221
|
+
water: "#C5E1F5",
|
|
222
|
+
nature: "#DDEED2",
|
|
223
|
+
road: "#FFFFFF",
|
|
224
|
+
building: "#ECEAE4",
|
|
225
|
+
text: "#5F6368",
|
|
226
|
+
accent: "#C9D1D9"
|
|
227
|
+
},
|
|
228
|
+
density: "clean",
|
|
229
|
+
labels: "essential",
|
|
230
|
+
poi: "minimal",
|
|
231
|
+
roads: "standard"
|
|
232
|
+
},
|
|
233
|
+
dark: {
|
|
234
|
+
colors: {
|
|
235
|
+
canvas: "#1C2228",
|
|
236
|
+
land: "#232B32",
|
|
237
|
+
water: "#18384D",
|
|
238
|
+
nature: "#274230",
|
|
239
|
+
road: "#39434D",
|
|
240
|
+
building: "#2F363D",
|
|
241
|
+
text: "#D7DEE6",
|
|
242
|
+
accent: "#E4B857"
|
|
243
|
+
},
|
|
244
|
+
density: "balanced",
|
|
245
|
+
labels: "balanced",
|
|
246
|
+
poi: "minimal",
|
|
247
|
+
roads: "standard"
|
|
248
|
+
},
|
|
249
|
+
minimal: {
|
|
250
|
+
colors: {
|
|
251
|
+
canvas: "#FAFAF8",
|
|
252
|
+
land: "#F7F7F2",
|
|
253
|
+
water: "#D6EAF6",
|
|
254
|
+
nature: "#E5EEDB",
|
|
255
|
+
road: "#FFFFFF",
|
|
256
|
+
building: "#F0EEE8",
|
|
257
|
+
text: "#697079",
|
|
258
|
+
accent: "#B8C0CA"
|
|
259
|
+
},
|
|
260
|
+
density: "clean",
|
|
261
|
+
labels: "essential",
|
|
262
|
+
poi: "none",
|
|
263
|
+
roads: "soft"
|
|
264
|
+
},
|
|
265
|
+
// "OSM Bright 2" — Google Maps-inspired palette. Warm-neutral land, soft
|
|
266
|
+
// blue water, muted green parks, white roads on a light gray canvas, and a
|
|
267
|
+
// warm yellow accent that drives the highway color once the class-based road
|
|
268
|
+
// engine lands. Tuned to read as a familiar navigation basemap.
|
|
269
|
+
"osm-bright-2": {
|
|
270
|
+
colors: {
|
|
271
|
+
canvas: "#EBE9E4",
|
|
272
|
+
land: "#F5F4F1",
|
|
273
|
+
water: "#AADAFF",
|
|
274
|
+
nature: "#C7E6A8",
|
|
275
|
+
road: "#FFFFFF",
|
|
276
|
+
building: "#EFEDE7",
|
|
277
|
+
text: "#5F6368",
|
|
278
|
+
accent: "#F9C97C"
|
|
279
|
+
},
|
|
280
|
+
density: "balanced",
|
|
281
|
+
labels: "balanced",
|
|
282
|
+
poi: "balanced",
|
|
283
|
+
roads: "detailed"
|
|
284
|
+
}
|
|
285
|
+
};
|
|
95
286
|
var defaultTypography = {
|
|
96
287
|
font: "Noto Sans",
|
|
97
288
|
weight: "regular"
|
|
@@ -104,23 +295,6 @@ var fontWeightLabels = {
|
|
|
104
295
|
};
|
|
105
296
|
var defaultTileBaseUrl = "https://api.tileflow.dev";
|
|
106
297
|
var defaultTileset = "world";
|
|
107
|
-
function defineTileflow(config) {
|
|
108
|
-
return config;
|
|
109
|
-
}
|
|
110
|
-
function validateConfig(input) {
|
|
111
|
-
const parsed = isProjectConfigLike(input) ? projectConfigSchema.safeParse(input) : configSchema.safeParse(input);
|
|
112
|
-
if (parsed.success) {
|
|
113
|
-
return { valid: true, messages: [] };
|
|
114
|
-
}
|
|
115
|
-
return {
|
|
116
|
-
valid: false,
|
|
117
|
-
messages: parsed.error.issues.map((issue) => ({
|
|
118
|
-
level: "error",
|
|
119
|
-
path: issue.path.join(".") || "config",
|
|
120
|
-
message: issue.message
|
|
121
|
-
}))
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
298
|
function createStyle(input = {}, options = {}) {
|
|
125
299
|
const parsed = configSchema.safeParse(input);
|
|
126
300
|
if (!parsed.success) {
|
|
@@ -128,53 +302,83 @@ function createStyle(input = {}, options = {}) {
|
|
|
128
302
|
throw new Error(`Invalid Tileflow config. ${message}`);
|
|
129
303
|
}
|
|
130
304
|
const config = parsed.data;
|
|
131
|
-
const
|
|
132
|
-
const
|
|
133
|
-
const
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
305
|
+
const basemap = config.basemap ?? osm();
|
|
306
|
+
const theme = resolveTheme(config.theme, options.themes);
|
|
307
|
+
const density = config.density ?? theme.preset.density ?? defaultConfig.density;
|
|
308
|
+
const roadModule = config.modules?.find(
|
|
309
|
+
(moduleConfig) => moduleConfig.type === "roads"
|
|
310
|
+
);
|
|
311
|
+
const roads2 = resolveRoads(config.roads, roadModule, theme.preset.roads ?? defaultConfig.roads);
|
|
312
|
+
const labels = config.labels ?? theme.preset.labels ?? defaultConfig.labels;
|
|
313
|
+
const poi = config.poi ?? theme.preset.poi ?? defaultConfig.poi;
|
|
314
|
+
const buildings = config.buildings ?? "flat";
|
|
315
|
+
const colors = resolveColors(config.colors, theme.colors);
|
|
316
|
+
const sourceId = config.tiles?.sourceId ?? basemap.sourceId ?? "tileflow";
|
|
137
317
|
const tileBaseUrl = normalizeBaseUrl(options.tileBaseUrl);
|
|
138
|
-
const tileset = options.tileset ?? config.tileset ?? config.tiles?.tileset ?? defaultTileset;
|
|
139
|
-
const tilesUrl = config.tiles?.url ?? `${tileBaseUrl}/tiles/${tileset}/tiles.json`;
|
|
140
|
-
const typography = resolveTypography(config.typography,
|
|
318
|
+
const tileset = options.tileset ?? config.tileset ?? config.tiles?.tileset ?? basemap.tileset ?? defaultTileset;
|
|
319
|
+
const tilesUrl = config.tiles?.url ?? basemap.url ?? `${tileBaseUrl}/tiles/${tileset}/tiles.json`;
|
|
320
|
+
const typography = resolveTypography(config.typography, theme.typography);
|
|
321
|
+
const terrain = resolveTerrain(config.terrain, tileBaseUrl);
|
|
141
322
|
const sourceLayers = resolveSourceLayers(
|
|
142
323
|
{
|
|
143
324
|
...defaultSourceLayers,
|
|
325
|
+
...basemap.sourceLayers,
|
|
144
326
|
...config.tiles?.sourceLayers
|
|
145
327
|
},
|
|
146
328
|
void 0
|
|
147
329
|
);
|
|
148
330
|
const layers = openMapTilesLayers(sourceId, sourceLayers, colors, {
|
|
331
|
+
buildings,
|
|
149
332
|
density,
|
|
150
333
|
labels,
|
|
151
334
|
poi,
|
|
152
|
-
roads,
|
|
335
|
+
roads: roads2,
|
|
153
336
|
typography
|
|
154
337
|
});
|
|
338
|
+
const resolvedLayers = terrain ? withTerrainLayers(layers, terrain) : layers;
|
|
155
339
|
const style = {
|
|
156
340
|
version: 8,
|
|
157
341
|
name: config.name ?? "Tileflow",
|
|
158
|
-
glyphs: config.glyphs ?? `${tileBaseUrl}/fonts/{fontstack}/{range}.pbf`,
|
|
159
|
-
...config.sprite ? { sprite: config.sprite } : {},
|
|
342
|
+
glyphs: config.glyphs ?? basemap.glyphs ?? `${tileBaseUrl}/fonts/{fontstack}/{range}.pbf`,
|
|
343
|
+
...config.sprite || basemap.sprite ? { sprite: config.sprite ?? basemap.sprite } : {},
|
|
160
344
|
sources: {
|
|
161
345
|
[sourceId]: {
|
|
162
346
|
type: "vector",
|
|
163
347
|
url: tilesUrl,
|
|
164
|
-
attribution: config.tiles?.attribution ?? "\xA9 OpenMapTiles \xA9 OpenStreetMap contributors"
|
|
165
|
-
}
|
|
348
|
+
attribution: config.tiles?.attribution ?? basemap.attribution ?? "\xA9 OpenMapTiles \xA9 OpenStreetMap contributors"
|
|
349
|
+
},
|
|
350
|
+
...terrain ? {
|
|
351
|
+
[terrain.sourceId]: terrain.source
|
|
352
|
+
} : {}
|
|
166
353
|
},
|
|
167
|
-
layers,
|
|
354
|
+
layers: resolvedLayers,
|
|
355
|
+
...terrain?.mode === "3d" ? {
|
|
356
|
+
terrain: {
|
|
357
|
+
exaggeration: terrain.exaggeration,
|
|
358
|
+
source: terrain.sourceId
|
|
359
|
+
}
|
|
360
|
+
} : {},
|
|
168
361
|
metadata: {
|
|
362
|
+
"tileflow:basemap": basemap.type,
|
|
363
|
+
"tileflow:theme": theme.name,
|
|
364
|
+
"tileflow:themeMode": theme.mode,
|
|
169
365
|
"tileflow:density": density,
|
|
170
|
-
"tileflow:roads":
|
|
366
|
+
"tileflow:roads": roads2,
|
|
171
367
|
"tileflow:labels": labels,
|
|
172
368
|
"tileflow:poi": poi,
|
|
369
|
+
"tileflow:buildings": buildings,
|
|
370
|
+
"tileflow:modules": config.modules?.map((moduleConfig) => moduleConfig.type) ?? [],
|
|
371
|
+
"tileflow:terrain": terrain ? {
|
|
372
|
+
encoding: terrain.source.encoding,
|
|
373
|
+
mode: terrain.mode,
|
|
374
|
+
sourceId: terrain.sourceId
|
|
375
|
+
} : void 0,
|
|
173
376
|
"tileflow:typography": typography,
|
|
174
377
|
"tileflow:colors": colors,
|
|
175
378
|
"tileflow:tileBaseUrl": tileBaseUrl,
|
|
176
379
|
"tileflow:tileset": tileset,
|
|
177
|
-
"tileflow:tiles": "tileflow"
|
|
380
|
+
"tileflow:tiles": "tileflow",
|
|
381
|
+
"tileflow:view": config.view
|
|
178
382
|
}
|
|
179
383
|
};
|
|
180
384
|
return style;
|
|
@@ -184,25 +388,31 @@ function createStyleFromProject(projectConfig, mapName, options = {}) {
|
|
|
184
388
|
if (!config) {
|
|
185
389
|
throw new Error(`Unknown Tileflow map: ${mapName}`);
|
|
186
390
|
}
|
|
187
|
-
const tilesetName = options.tileset ?? config.tileset ?? config.tiles?.tileset;
|
|
391
|
+
const tilesetName = options.tileset ?? config.tileset ?? config.tiles?.tileset ?? config.basemap?.tileset;
|
|
188
392
|
const tilesetConfig = tilesetName ? projectConfig.tilesets?.[tilesetName] : void 0;
|
|
189
393
|
const resolvedTileset = tilesetConfig?.id ?? tilesetName ?? defaultTileset;
|
|
394
|
+
const baseBasemap = config.basemap ?? osm();
|
|
395
|
+
const { sourceLayers: tileSourceLayers, ...tileOptions } = config.tiles ?? {};
|
|
396
|
+
const resolvedTileSourceLayers = baseBasemap.sourceLayers || !tileSourceLayers ? void 0 : resolveSourceLayers(tileSourceLayers, tilesetConfig?.sourceLayers);
|
|
190
397
|
return createStyle(
|
|
191
398
|
{
|
|
192
399
|
...config,
|
|
193
400
|
tileset: resolvedTileset,
|
|
401
|
+
basemap: {
|
|
402
|
+
...baseBasemap,
|
|
403
|
+
tileset: resolvedTileset,
|
|
404
|
+
attribution: baseBasemap.attribution ?? tilesetConfig?.attribution,
|
|
405
|
+
sourceLayers: resolveSourceLayers(baseBasemap.sourceLayers, tilesetConfig?.sourceLayers)
|
|
406
|
+
},
|
|
194
407
|
tiles: {
|
|
195
|
-
...
|
|
408
|
+
...tileOptions,
|
|
196
409
|
tileset: resolvedTileset,
|
|
197
|
-
|
|
198
|
-
sourceLayers: resolveSourceLayers(
|
|
199
|
-
config.tiles?.sourceLayers,
|
|
200
|
-
tilesetConfig?.sourceLayers
|
|
201
|
-
)
|
|
410
|
+
...resolvedTileSourceLayers ? { sourceLayers: resolvedTileSourceLayers } : {}
|
|
202
411
|
}
|
|
203
412
|
},
|
|
204
413
|
{
|
|
205
414
|
...options,
|
|
415
|
+
themes: projectConfig.themes,
|
|
206
416
|
tileset: resolvedTileset
|
|
207
417
|
}
|
|
208
418
|
);
|
|
@@ -215,35 +425,96 @@ function createManifest(projectConfig, options = {}) {
|
|
|
215
425
|
version: 1,
|
|
216
426
|
tileBaseUrl,
|
|
217
427
|
styles: Object.fromEntries(
|
|
218
|
-
maps.map((mapName) => [
|
|
219
|
-
mapName,
|
|
220
|
-
`${styleBaseUrl}/styles/${mapName}.json`
|
|
221
|
-
])
|
|
428
|
+
maps.map((mapName) => [mapName, `${styleBaseUrl}/styles/${mapName}.json`])
|
|
222
429
|
),
|
|
223
430
|
maps: Object.fromEntries(
|
|
224
|
-
maps.map((mapName) => [
|
|
225
|
-
mapName,
|
|
226
|
-
`${styleBaseUrl}/styles/${mapName}.json`
|
|
227
|
-
])
|
|
431
|
+
maps.map((mapName) => [mapName, `${styleBaseUrl}/styles/${mapName}.json`])
|
|
228
432
|
)
|
|
229
433
|
};
|
|
230
434
|
}
|
|
231
435
|
function getDefaultColors() {
|
|
232
436
|
return defaultColors;
|
|
233
437
|
}
|
|
234
|
-
function
|
|
438
|
+
function resolveTheme(theme, projectThemes) {
|
|
439
|
+
if (!theme) {
|
|
440
|
+
return resolveLegacyTheme("standard");
|
|
441
|
+
}
|
|
442
|
+
if (typeof theme === "string") {
|
|
443
|
+
const projectTheme = projectThemes?.[theme];
|
|
444
|
+
if (projectTheme) {
|
|
445
|
+
return resolveThemeConfig(theme, projectTheme);
|
|
446
|
+
}
|
|
447
|
+
if (isLegacyTheme(theme)) {
|
|
448
|
+
return resolveLegacyTheme(theme);
|
|
449
|
+
}
|
|
450
|
+
throw new Error(`Unknown Tileflow theme: ${theme}`);
|
|
451
|
+
}
|
|
452
|
+
return resolveThemeConfig("inline", theme);
|
|
453
|
+
}
|
|
454
|
+
function resolveLegacyTheme(theme) {
|
|
455
|
+
const preset = themePresets[theme];
|
|
456
|
+
return {
|
|
457
|
+
colors: preset.colors,
|
|
458
|
+
mode: theme === "dark" ? "dark" : "light",
|
|
459
|
+
name: theme,
|
|
460
|
+
preset,
|
|
461
|
+
typography: defaultTypography
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
function resolveThemeConfig(name, theme) {
|
|
465
|
+
const colors = normalizeColorAliases(theme.colors);
|
|
466
|
+
const preset = theme.mode === "dark" ? themePresets.dark : theme.mode === "light" ? themePresets.light : themePresets.standard;
|
|
467
|
+
return {
|
|
468
|
+
colors: {
|
|
469
|
+
...preset.colors,
|
|
470
|
+
...colors
|
|
471
|
+
},
|
|
472
|
+
mode: theme.mode,
|
|
473
|
+
name,
|
|
474
|
+
preset,
|
|
475
|
+
typography: resolveTypography(theme.typography, defaultTypography)
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
function isLegacyTheme(value) {
|
|
479
|
+
return value in themePresets;
|
|
480
|
+
}
|
|
481
|
+
function normalizeColorAliases(colors) {
|
|
482
|
+
if (!colors) {
|
|
483
|
+
return {};
|
|
484
|
+
}
|
|
485
|
+
return {
|
|
486
|
+
...colors,
|
|
487
|
+
canvas: colors.canvas ?? colors.background,
|
|
488
|
+
nature: colors.nature ?? colors.park
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
function resolveRoads(legacyRoads, moduleConfig, fallback) {
|
|
492
|
+
if (!moduleConfig) {
|
|
493
|
+
return legacyRoads ?? fallback;
|
|
494
|
+
}
|
|
495
|
+
if (moduleConfig.preset === "minimal" || moduleConfig.density === "clean") {
|
|
496
|
+
return "soft";
|
|
497
|
+
}
|
|
498
|
+
if (moduleConfig.density === "dense") {
|
|
499
|
+
return "detailed";
|
|
500
|
+
}
|
|
501
|
+
return "standard";
|
|
502
|
+
}
|
|
503
|
+
function resolveColors(overrides = {}, defaults = defaultColors) {
|
|
235
504
|
const colors = {
|
|
236
|
-
...
|
|
237
|
-
canvas: overrides.canvas ?? overrides.background ?? defaultColors.canvas,
|
|
238
|
-
land: overrides.land ?? defaultColors.land,
|
|
239
|
-
water: overrides.water ?? defaultColors.water,
|
|
240
|
-
nature: overrides.nature ?? overrides.park ?? defaultColors.nature,
|
|
241
|
-
road: overrides.road ?? defaultColors.road,
|
|
242
|
-
text: overrides.text ?? defaultColors.text,
|
|
243
|
-
accent: overrides.accent ?? defaultColors.accent,
|
|
244
|
-
building: overrides.building
|
|
505
|
+
...defaults,
|
|
506
|
+
canvas: overrides.canvas ?? overrides.background ?? defaults.canvas ?? defaultColors.canvas,
|
|
507
|
+
land: overrides.land ?? defaults.land ?? defaultColors.land,
|
|
508
|
+
water: overrides.water ?? defaults.water ?? defaultColors.water,
|
|
509
|
+
nature: overrides.nature ?? overrides.park ?? defaults.nature ?? defaults.park ?? defaultColors.nature,
|
|
510
|
+
road: overrides.road ?? defaults.road ?? defaultColors.road,
|
|
511
|
+
text: overrides.text ?? defaults.text ?? defaultColors.text,
|
|
512
|
+
accent: overrides.accent ?? defaults.accent ?? defaultColors.accent,
|
|
513
|
+
building: overrides.building ?? defaults.building,
|
|
514
|
+
roadCasing: overrides.roadCasing ?? defaults.roadCasing
|
|
245
515
|
};
|
|
246
|
-
const building = colors.building ?? mix(colors.land, colors.
|
|
516
|
+
const building = colors.building ?? mix(colors.land, colors.text, 0.08);
|
|
517
|
+
const roadCasing = colors.roadCasing ?? mix(colors.road, colors.text, 0.2);
|
|
247
518
|
return {
|
|
248
519
|
canvas: cssColor(colors.canvas),
|
|
249
520
|
land: cssColor(colors.land),
|
|
@@ -253,13 +524,38 @@ function resolveColors(overrides = {}) {
|
|
|
253
524
|
text: cssColor(colors.text),
|
|
254
525
|
accent: cssColor(colors.accent),
|
|
255
526
|
building: cssColor(building),
|
|
256
|
-
roadCasing:
|
|
527
|
+
roadCasing: cssColor(roadCasing),
|
|
257
528
|
roadMajor: mix(colors.road, colors.accent, 0.16),
|
|
258
529
|
roadMinor: cssColor(colors.road),
|
|
259
530
|
textHalo: alpha(colors.canvas, 0.84),
|
|
260
531
|
textMuted: mix(colors.text, colors.canvas, 0.42)
|
|
261
532
|
};
|
|
262
533
|
}
|
|
534
|
+
function resolveTerrain(terrain, tileBaseUrl) {
|
|
535
|
+
if (!terrain || terrain === "none") {
|
|
536
|
+
return void 0;
|
|
537
|
+
}
|
|
538
|
+
const terrainConfig = typeof terrain === "string" ? { mode: terrain } : terrain;
|
|
539
|
+
const mode = terrainConfig.mode ?? "hillshade";
|
|
540
|
+
if (mode === "none") {
|
|
541
|
+
return void 0;
|
|
542
|
+
}
|
|
543
|
+
const sourceId = terrainConfig.sourceId ?? "tileflow-terrain";
|
|
544
|
+
const tileset = terrainConfig.tileset ?? "terrain";
|
|
545
|
+
const url = terrainConfig.url ?? `${tileBaseUrl}/tiles/${tileset}/tiles.json`;
|
|
546
|
+
const encoding = terrainConfig.encoding ?? "terrarium";
|
|
547
|
+
return {
|
|
548
|
+
exaggeration: terrainConfig.exaggeration ?? 1.2,
|
|
549
|
+
mode,
|
|
550
|
+
sourceId,
|
|
551
|
+
source: {
|
|
552
|
+
attribution: terrainConfig.attribution,
|
|
553
|
+
encoding,
|
|
554
|
+
type: "raster-dem",
|
|
555
|
+
url
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
}
|
|
263
559
|
function resolveTypography(overrides = {}, defaults = defaultTypography) {
|
|
264
560
|
return {
|
|
265
561
|
...defaults,
|
|
@@ -282,11 +578,6 @@ function normalizeStyleBaseUrl(value) {
|
|
|
282
578
|
function normalizeUrl(value) {
|
|
283
579
|
return value.replace(/\/+$/, "");
|
|
284
580
|
}
|
|
285
|
-
function isProjectConfigLike(input) {
|
|
286
|
-
return Boolean(
|
|
287
|
-
input && typeof input === "object" && "maps" in input && typeof input.maps === "object"
|
|
288
|
-
);
|
|
289
|
-
}
|
|
290
581
|
function textFont(typography) {
|
|
291
582
|
return [`${typography.font} ${fontWeightLabels[typography.weight]}`];
|
|
292
583
|
}
|
|
@@ -312,34 +603,82 @@ function openMapTilesLayers(source, sourceLayers, colors, options) {
|
|
|
312
603
|
}
|
|
313
604
|
},
|
|
314
605
|
fillLayer("landuse", source, sourceLayers.landuse, colors.land, 0.42),
|
|
315
|
-
fillLayer(
|
|
316
|
-
"parks",
|
|
317
|
-
source,
|
|
318
|
-
sourceLayers.park,
|
|
319
|
-
colors.nature,
|
|
320
|
-
parkOpacity(options.density)
|
|
321
|
-
),
|
|
606
|
+
fillLayer("parks", source, sourceLayers.park, colors.nature, parkOpacity(options.density)),
|
|
322
607
|
fillLayer("water", source, sourceLayers.water, colors.water, 1),
|
|
323
|
-
...buildingLayers(source, sourceLayers.building, colors, options.density),
|
|
608
|
+
...buildingLayers(source, sourceLayers.building, colors, options.density, options.buildings),
|
|
324
609
|
...roadLayers(source, sourceLayers.road, colors, options.roads),
|
|
325
610
|
...boundaryLayers(source, sourceLayers.boundary, colors),
|
|
326
|
-
...labelLayers(
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
611
|
+
...labelLayers(source, sourceLayers.place, colors, options.labels, options.typography),
|
|
612
|
+
...poiLayers(source, sourceLayers.poi, colors, options.poi, options.typography)
|
|
613
|
+
];
|
|
614
|
+
}
|
|
615
|
+
function withTerrainLayers(layers, terrain) {
|
|
616
|
+
const hillshadeLayer = {
|
|
617
|
+
id: "terrain-hillshade",
|
|
618
|
+
type: "hillshade",
|
|
619
|
+
source: terrain.sourceId,
|
|
620
|
+
maxzoom: 15,
|
|
621
|
+
paint: {
|
|
622
|
+
"hillshade-accent-color": "rgba(255, 255, 255, 0.18)",
|
|
623
|
+
"hillshade-exaggeration": terrain.mode === "3d" ? 0.24 : 0.42,
|
|
624
|
+
"hillshade-highlight-color": "rgba(255, 255, 255, 0.28)",
|
|
625
|
+
"hillshade-shadow-color": "rgba(38, 44, 50, 0.34)"
|
|
626
|
+
}
|
|
627
|
+
};
|
|
628
|
+
const insertionIndex = layers.findIndex((layer) => layer.id === "water");
|
|
629
|
+
if (insertionIndex === -1) {
|
|
630
|
+
return [hillshadeLayer, ...layers];
|
|
631
|
+
}
|
|
632
|
+
return [
|
|
633
|
+
...layers.slice(0, insertionIndex + 1),
|
|
634
|
+
hillshadeLayer,
|
|
635
|
+
...layers.slice(insertionIndex + 1)
|
|
340
636
|
];
|
|
341
637
|
}
|
|
342
|
-
function buildingLayers(source, sourceLayer, colors, density) {
|
|
638
|
+
function buildingLayers(source, sourceLayer, colors, density, buildings) {
|
|
639
|
+
if (buildings === "hidden") {
|
|
640
|
+
return [];
|
|
641
|
+
}
|
|
642
|
+
if (buildings === "3d") {
|
|
643
|
+
return [
|
|
644
|
+
{
|
|
645
|
+
id: "buildings-3d",
|
|
646
|
+
type: "fill-extrusion",
|
|
647
|
+
source,
|
|
648
|
+
"source-layer": sourceLayer,
|
|
649
|
+
minzoom: 14,
|
|
650
|
+
filter: ["!=", ["get", "hide_3d"], true],
|
|
651
|
+
paint: {
|
|
652
|
+
"fill-extrusion-base": [
|
|
653
|
+
"case",
|
|
654
|
+
["has", "render_min_height"],
|
|
655
|
+
["to-number", ["get", "render_min_height"]],
|
|
656
|
+
["has", "min_height"],
|
|
657
|
+
["to-number", ["get", "min_height"]],
|
|
658
|
+
0
|
|
659
|
+
],
|
|
660
|
+
"fill-extrusion-color": colors.building,
|
|
661
|
+
"fill-extrusion-height": [
|
|
662
|
+
"interpolate",
|
|
663
|
+
["linear"],
|
|
664
|
+
["zoom"],
|
|
665
|
+
14,
|
|
666
|
+
0,
|
|
667
|
+
15,
|
|
668
|
+
[
|
|
669
|
+
"case",
|
|
670
|
+
["has", "render_height"],
|
|
671
|
+
["to-number", ["get", "render_height"]],
|
|
672
|
+
["has", "height"],
|
|
673
|
+
["to-number", ["get", "height"]],
|
|
674
|
+
18
|
|
675
|
+
]
|
|
676
|
+
],
|
|
677
|
+
"fill-extrusion-opacity": 0.72
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
];
|
|
681
|
+
}
|
|
343
682
|
if (density === "clean") {
|
|
344
683
|
return [
|
|
345
684
|
{
|
|
@@ -381,12 +720,12 @@ function buildingLayers(source, sourceLayer, colors, density) {
|
|
|
381
720
|
}
|
|
382
721
|
];
|
|
383
722
|
}
|
|
384
|
-
function roadLayers(source, sourceLayer, colors,
|
|
385
|
-
if (
|
|
723
|
+
function roadLayers(source, sourceLayer, colors, roads2) {
|
|
724
|
+
if (roads2 === "hidden") {
|
|
386
725
|
return [];
|
|
387
726
|
}
|
|
388
|
-
const opacity =
|
|
389
|
-
const casingOpacity =
|
|
727
|
+
const opacity = roads2 === "soft" ? 0.72 : roads2 === "standard" ? 0.84 : 0.96;
|
|
728
|
+
const casingOpacity = roads2 === "detailed" ? 0.3 : 0.16;
|
|
390
729
|
const majorRoadFilter = [
|
|
391
730
|
"match",
|
|
392
731
|
["get", "class"],
|
|
@@ -395,7 +734,22 @@ function roadLayers(source, sourceLayer, colors, roads) {
|
|
|
395
734
|
false
|
|
396
735
|
];
|
|
397
736
|
const minorRoadFilter = ["!", majorRoadFilter];
|
|
737
|
+
const nonTunnelRoadFilter = ["!=", ["get", "brunnel"], "tunnel"];
|
|
398
738
|
return [
|
|
739
|
+
{
|
|
740
|
+
id: "roads-tunnels-casing",
|
|
741
|
+
type: "line",
|
|
742
|
+
source,
|
|
743
|
+
"source-layer": sourceLayer,
|
|
744
|
+
minzoom: 10,
|
|
745
|
+
filter: ["==", ["get", "brunnel"], "tunnel"],
|
|
746
|
+
paint: {
|
|
747
|
+
"line-color": colors.roadCasing,
|
|
748
|
+
"line-dasharray": [0.6, 0.8],
|
|
749
|
+
"line-opacity": 0.28,
|
|
750
|
+
"line-width": ["interpolate", ["linear"], ["zoom"], 10, 0.5, 14, 1.7, 16, 3.8]
|
|
751
|
+
}
|
|
752
|
+
},
|
|
399
753
|
{
|
|
400
754
|
id: "roads-tunnels",
|
|
401
755
|
type: "line",
|
|
@@ -406,18 +760,8 @@ function roadLayers(source, sourceLayer, colors, roads) {
|
|
|
406
760
|
paint: {
|
|
407
761
|
"line-color": colors.roadMinor,
|
|
408
762
|
"line-dasharray": [0.6, 0.8],
|
|
409
|
-
"line-opacity":
|
|
410
|
-
"line-width": [
|
|
411
|
-
"interpolate",
|
|
412
|
-
["linear"],
|
|
413
|
-
["zoom"],
|
|
414
|
-
10,
|
|
415
|
-
0.25,
|
|
416
|
-
14,
|
|
417
|
-
1,
|
|
418
|
-
16,
|
|
419
|
-
2.4
|
|
420
|
-
]
|
|
763
|
+
"line-opacity": 0.2,
|
|
764
|
+
"line-width": ["interpolate", ["linear"], ["zoom"], 10, 0.25, 14, 1, 16, 2.4]
|
|
421
765
|
}
|
|
422
766
|
},
|
|
423
767
|
{
|
|
@@ -426,20 +770,11 @@ function roadLayers(source, sourceLayer, colors, roads) {
|
|
|
426
770
|
source,
|
|
427
771
|
"source-layer": sourceLayer,
|
|
428
772
|
minzoom: 6,
|
|
773
|
+
filter: nonTunnelRoadFilter,
|
|
429
774
|
paint: {
|
|
430
775
|
"line-color": colors.roadCasing,
|
|
431
776
|
"line-opacity": casingOpacity,
|
|
432
|
-
"line-width": [
|
|
433
|
-
"interpolate",
|
|
434
|
-
["linear"],
|
|
435
|
-
["zoom"],
|
|
436
|
-
6,
|
|
437
|
-
0.4,
|
|
438
|
-
12,
|
|
439
|
-
1.2,
|
|
440
|
-
16,
|
|
441
|
-
4
|
|
442
|
-
]
|
|
777
|
+
"line-width": ["interpolate", ["linear"], ["zoom"], 6, 0.4, 12, 1.2, 16, 4]
|
|
443
778
|
}
|
|
444
779
|
},
|
|
445
780
|
{
|
|
@@ -448,21 +783,11 @@ function roadLayers(source, sourceLayer, colors, roads) {
|
|
|
448
783
|
source,
|
|
449
784
|
"source-layer": sourceLayer,
|
|
450
785
|
minzoom: 6,
|
|
451
|
-
filter: minorRoadFilter,
|
|
786
|
+
filter: ["all", nonTunnelRoadFilter, minorRoadFilter],
|
|
452
787
|
paint: {
|
|
453
788
|
"line-color": colors.roadMinor,
|
|
454
789
|
"line-opacity": opacity * 0.88,
|
|
455
|
-
"line-width": [
|
|
456
|
-
"interpolate",
|
|
457
|
-
["linear"],
|
|
458
|
-
["zoom"],
|
|
459
|
-
6,
|
|
460
|
-
0.2,
|
|
461
|
-
12,
|
|
462
|
-
0.8,
|
|
463
|
-
16,
|
|
464
|
-
3
|
|
465
|
-
]
|
|
790
|
+
"line-width": ["interpolate", ["linear"], ["zoom"], 6, 0.2, 12, 0.8, 16, 3]
|
|
466
791
|
}
|
|
467
792
|
},
|
|
468
793
|
{
|
|
@@ -471,21 +796,11 @@ function roadLayers(source, sourceLayer, colors, roads) {
|
|
|
471
796
|
source,
|
|
472
797
|
"source-layer": sourceLayer,
|
|
473
798
|
minzoom: 5,
|
|
474
|
-
filter: majorRoadFilter,
|
|
799
|
+
filter: ["all", nonTunnelRoadFilter, majorRoadFilter],
|
|
475
800
|
paint: {
|
|
476
801
|
"line-color": colors.roadMajor,
|
|
477
802
|
"line-opacity": opacity,
|
|
478
|
-
"line-width": [
|
|
479
|
-
"interpolate",
|
|
480
|
-
["linear"],
|
|
481
|
-
["zoom"],
|
|
482
|
-
5,
|
|
483
|
-
0.35,
|
|
484
|
-
12,
|
|
485
|
-
1.2,
|
|
486
|
-
16,
|
|
487
|
-
4
|
|
488
|
-
]
|
|
803
|
+
"line-width": ["interpolate", ["linear"], ["zoom"], 5, 0.35, 12, 1.2, 16, 4]
|
|
489
804
|
}
|
|
490
805
|
},
|
|
491
806
|
{
|
|
@@ -498,17 +813,7 @@ function roadLayers(source, sourceLayer, colors, roads) {
|
|
|
498
813
|
paint: {
|
|
499
814
|
"line-color": mix(colors.roadMajor, colors.canvas, 0.14),
|
|
500
815
|
"line-opacity": opacity,
|
|
501
|
-
"line-width": [
|
|
502
|
-
"interpolate",
|
|
503
|
-
["linear"],
|
|
504
|
-
["zoom"],
|
|
505
|
-
10,
|
|
506
|
-
0.45,
|
|
507
|
-
14,
|
|
508
|
-
1.6,
|
|
509
|
-
16,
|
|
510
|
-
4.4
|
|
511
|
-
]
|
|
816
|
+
"line-width": ["interpolate", ["linear"], ["zoom"], 10, 0.45, 14, 1.6, 16, 4.4]
|
|
512
817
|
}
|
|
513
818
|
}
|
|
514
819
|
];
|
|
@@ -545,17 +850,7 @@ function labelLayers(source, sourceLayer, colors, labels, typography) {
|
|
|
545
850
|
layout: {
|
|
546
851
|
"text-field": ["coalesce", ["get", "name:en"], ["get", "name"]],
|
|
547
852
|
"text-font": textFont(typography),
|
|
548
|
-
"text-size": [
|
|
549
|
-
"interpolate",
|
|
550
|
-
["linear"],
|
|
551
|
-
["zoom"],
|
|
552
|
-
3,
|
|
553
|
-
10,
|
|
554
|
-
8,
|
|
555
|
-
13,
|
|
556
|
-
14,
|
|
557
|
-
17
|
|
558
|
-
],
|
|
853
|
+
"text-size": ["interpolate", ["linear"], ["zoom"], 3, 10, 8, 13, 14, 17],
|
|
559
854
|
"text-letter-spacing": 0,
|
|
560
855
|
"text-padding": 4
|
|
561
856
|
},
|
|
@@ -648,11 +943,27 @@ function roundAlpha(value) {
|
|
|
648
943
|
function rgbToHex(r, g, b) {
|
|
649
944
|
return `#${[r, g, b].map((value) => value.toString(16).padStart(2, "0")).join("")}`;
|
|
650
945
|
}
|
|
946
|
+
|
|
947
|
+
// src/config.ts
|
|
948
|
+
function defineTileflow(config) {
|
|
949
|
+
return config;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
// src/modules.ts
|
|
953
|
+
function roads(options = {}) {
|
|
954
|
+
return {
|
|
955
|
+
type: "roads",
|
|
956
|
+
preset: options.preset ?? "modern",
|
|
957
|
+
...options.density ? { density: options.density } : {}
|
|
958
|
+
};
|
|
959
|
+
}
|
|
651
960
|
export {
|
|
652
961
|
createManifest,
|
|
653
962
|
createStyle,
|
|
654
963
|
createStyleFromProject,
|
|
655
964
|
defineTileflow,
|
|
656
965
|
getDefaultColors,
|
|
966
|
+
osm,
|
|
967
|
+
roads,
|
|
657
968
|
validateConfig
|
|
658
969
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tileflow/core",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.1",
|
|
4
4
|
"description": "Typed Tileflow config and MapLibre style compiler.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://tileflow.dev",
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
42
|
+
"verify": "tsx scripts/verify.ts",
|
|
42
43
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
43
44
|
"lint": "tsc -p tsconfig.json --noEmit"
|
|
44
45
|
}
|