@tileflow/core 0.1.0-alpha.12 → 0.1.0-alpha.14

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 CHANGED
@@ -51,7 +51,7 @@ export default defineTileflow({
51
51
  icons: 'brand',
52
52
  theme: 'light',
53
53
  modules: [
54
- labels({roads: 'minimal'}),
54
+ labels({roads: 'major'}),
55
55
  poi({categories: ['food', 'coffee', 'culture', 'transit'], icons: 'essential'}),
56
56
  roads({detail: 'streets', hierarchy: 'clear', outline: 'subtle', weight: 'regular'}),
57
57
  ],
@@ -113,7 +113,35 @@ Without options, `roads()` uses `detail: 'streets'`, `hierarchy: 'clear'`,
113
113
  `weight: 'regular'`, `outline: 'subtle'`, and all extras disabled. Named visual recipes belong
114
114
  in examples rather than the runtime API, so the resolved behavior is always visible in config.
115
115
 
116
- Colors never belong in `roads()`. Put common colors in `theme.colors` and granular road colors in `theme.modules.roads`. Road-label density remains a `labels({roads: ...})` concern.
116
+ Colors never belong in `roads()`. Put common colors in `theme.colors` and granular road colors in `theme.modules.roads`. Road-label detail remains a `labels({roads: ...})` concern.
117
+
118
+ ## Labels module
119
+
120
+ Control label families independently without a preset:
121
+
122
+ ```ts
123
+ labels({
124
+ language: 'auto',
125
+ places: 'major',
126
+ roads: 'major',
127
+ water: 'major',
128
+ });
129
+ ```
130
+
131
+ `places` and `water` accept `none`, `major`, or `all`. Road labels follow the road hierarchy and
132
+ accept `none`, `highways`, `major`, `streets`, or `all`: `highways` includes motorway and trunk
133
+ labels, `major` adds primary through tertiary, `streets` adds minor streets, and `all` adds service
134
+ roads and tracks. Path labels are eligible at `all` only when path geometry is visible; when
135
+ `roads()` is present, enable it with `roads({extras: {paths: true}})`.
136
+
137
+ Detail values select which feature classes may be labeled; they do not force every name onto the
138
+ map. Labels keep the template's natural zoom ranges and MapLibre collision handling. Road labels
139
+ are also clamped to the classes currently visible through `roads({detail})`, so the effective result
140
+ is the intersection of both modules. `language` accepts `auto`, `local`, or a language code such as
141
+ `en` or `es`.
142
+
143
+ Without options, `labels()` uses `language: 'auto'` and `major` for all three label families.
144
+ Label colors remain in `theme.modules.labels`; font family and weight remain in `typography`.
117
145
 
118
146
  ## Public API
119
147
 
package/dist/index.d.ts CHANGED
@@ -4,7 +4,8 @@ type TileflowDensity = 'clean' | 'balanced' | 'dense';
4
4
  type TileflowRoads = 'hidden' | 'soft' | 'standard' | 'detailed';
5
5
  type TileflowLabels = 'none' | 'essential' | 'balanced' | 'full';
6
6
  type TileflowPoi = 'none' | 'minimal' | 'balanced' | 'full';
7
- type TileflowLabelDensity = 'none' | 'minimal' | 'balanced' | 'detailed';
7
+ type TileflowLabelDetail = 'none' | 'major' | 'all';
8
+ type TileflowRoadLabelDetail = TileflowRoadDetail;
8
9
  type TileflowLabelLanguage = 'auto' | 'local' | 'en' | (string & {});
9
10
  type TileflowPoiCategory = 'food' | 'coffee' | 'culture' | 'transit' | 'shopping' | 'lodging' | 'health' | 'education' | 'services' | (string & {});
10
11
  type TileflowPoiIcons = boolean | 'essential' | 'full';
@@ -151,7 +152,10 @@ type TileflowSourceLayers = {
151
152
  landuse?: string;
152
153
  park?: string;
153
154
  water?: string;
155
+ waterName?: string;
156
+ waterway?: string;
154
157
  road?: string;
158
+ roadName?: string;
155
159
  building?: string;
156
160
  boundary?: string;
157
161
  place?: string;
@@ -212,10 +216,9 @@ type TileflowRoadsModuleOptions = Omit<TileflowRoadsModuleConfig, 'type'>;
212
216
  type TileflowLabelsModuleConfig = {
213
217
  type: 'labels';
214
218
  language?: TileflowLabelLanguage;
215
- places?: TileflowLabelDensity;
216
- preset?: TileflowLabels;
217
- roads?: TileflowLabelDensity;
218
- water?: TileflowLabelDensity;
219
+ places?: TileflowLabelDetail;
220
+ roads?: TileflowRoadLabelDetail;
221
+ water?: TileflowLabelDetail;
219
222
  };
220
223
  type TileflowLabelsModuleOptions = Omit<TileflowLabelsModuleConfig, 'type'>;
221
224
  type TileflowPoiModuleConfig = {
@@ -543,7 +546,10 @@ declare const tileflowMapSchema: z.ZodObject<{
543
546
  landuse: z.ZodOptional<z.ZodString>;
544
547
  park: z.ZodOptional<z.ZodString>;
545
548
  water: z.ZodOptional<z.ZodString>;
549
+ waterName: z.ZodOptional<z.ZodString>;
550
+ waterway: z.ZodOptional<z.ZodString>;
546
551
  road: z.ZodOptional<z.ZodString>;
552
+ roadName: z.ZodOptional<z.ZodString>;
547
553
  building: z.ZodOptional<z.ZodString>;
548
554
  boundary: z.ZodOptional<z.ZodString>;
549
555
  place: z.ZodOptional<z.ZodString>;
@@ -752,28 +758,21 @@ declare const tileflowMapSchema: z.ZodObject<{
752
758
  type: z.ZodLiteral<"labels">;
753
759
  language: z.ZodOptional<z.ZodString>;
754
760
  places: z.ZodOptional<z.ZodEnum<{
755
- balanced: "balanced";
756
- none: "none";
757
- minimal: "minimal";
758
- detailed: "detailed";
759
- }>>;
760
- preset: z.ZodOptional<z.ZodEnum<{
761
- balanced: "balanced";
762
761
  none: "none";
763
- essential: "essential";
764
- full: "full";
762
+ major: "major";
763
+ all: "all";
765
764
  }>>;
766
765
  roads: z.ZodOptional<z.ZodEnum<{
767
- balanced: "balanced";
768
766
  none: "none";
769
- minimal: "minimal";
770
- detailed: "detailed";
767
+ major: "major";
768
+ highways: "highways";
769
+ streets: "streets";
770
+ all: "all";
771
771
  }>>;
772
772
  water: z.ZodOptional<z.ZodEnum<{
773
- balanced: "balanced";
774
773
  none: "none";
775
- minimal: "minimal";
776
- detailed: "detailed";
774
+ major: "major";
775
+ all: "all";
777
776
  }>>;
778
777
  }, z.core.$strict>, z.ZodObject<{
779
778
  type: z.ZodLiteral<"poi">;
@@ -1015,7 +1014,10 @@ declare const tileflowMapSchema: z.ZodObject<{
1015
1014
  landuse: z.ZodOptional<z.ZodString>;
1016
1015
  park: z.ZodOptional<z.ZodString>;
1017
1016
  water: z.ZodOptional<z.ZodString>;
1017
+ waterName: z.ZodOptional<z.ZodString>;
1018
+ waterway: z.ZodOptional<z.ZodString>;
1018
1019
  road: z.ZodOptional<z.ZodString>;
1020
+ roadName: z.ZodOptional<z.ZodString>;
1019
1021
  building: z.ZodOptional<z.ZodString>;
1020
1022
  boundary: z.ZodOptional<z.ZodString>;
1021
1023
  place: z.ZodOptional<z.ZodString>;
@@ -1039,7 +1041,10 @@ declare const configSchema: z.ZodObject<{
1039
1041
  landuse: z.ZodOptional<z.ZodString>;
1040
1042
  park: z.ZodOptional<z.ZodString>;
1041
1043
  water: z.ZodOptional<z.ZodString>;
1044
+ waterName: z.ZodOptional<z.ZodString>;
1045
+ waterway: z.ZodOptional<z.ZodString>;
1042
1046
  road: z.ZodOptional<z.ZodString>;
1047
+ roadName: z.ZodOptional<z.ZodString>;
1043
1048
  building: z.ZodOptional<z.ZodString>;
1044
1049
  boundary: z.ZodOptional<z.ZodString>;
1045
1050
  place: z.ZodOptional<z.ZodString>;
@@ -1248,28 +1253,21 @@ declare const configSchema: z.ZodObject<{
1248
1253
  type: z.ZodLiteral<"labels">;
1249
1254
  language: z.ZodOptional<z.ZodString>;
1250
1255
  places: z.ZodOptional<z.ZodEnum<{
1251
- balanced: "balanced";
1252
1256
  none: "none";
1253
- minimal: "minimal";
1254
- detailed: "detailed";
1255
- }>>;
1256
- preset: z.ZodOptional<z.ZodEnum<{
1257
- balanced: "balanced";
1258
- none: "none";
1259
- essential: "essential";
1260
- full: "full";
1257
+ major: "major";
1258
+ all: "all";
1261
1259
  }>>;
1262
1260
  roads: z.ZodOptional<z.ZodEnum<{
1263
- balanced: "balanced";
1264
1261
  none: "none";
1265
- minimal: "minimal";
1266
- detailed: "detailed";
1262
+ major: "major";
1263
+ highways: "highways";
1264
+ streets: "streets";
1265
+ all: "all";
1267
1266
  }>>;
1268
1267
  water: z.ZodOptional<z.ZodEnum<{
1269
- balanced: "balanced";
1270
1268
  none: "none";
1271
- minimal: "minimal";
1272
- detailed: "detailed";
1269
+ major: "major";
1270
+ all: "all";
1273
1271
  }>>;
1274
1272
  }, z.core.$strict>, z.ZodObject<{
1275
1273
  type: z.ZodLiteral<"poi">;
@@ -1511,7 +1509,10 @@ declare const configSchema: z.ZodObject<{
1511
1509
  landuse: z.ZodOptional<z.ZodString>;
1512
1510
  park: z.ZodOptional<z.ZodString>;
1513
1511
  water: z.ZodOptional<z.ZodString>;
1512
+ waterName: z.ZodOptional<z.ZodString>;
1513
+ waterway: z.ZodOptional<z.ZodString>;
1514
1514
  road: z.ZodOptional<z.ZodString>;
1515
+ roadName: z.ZodOptional<z.ZodString>;
1515
1516
  building: z.ZodOptional<z.ZodString>;
1516
1517
  boundary: z.ZodOptional<z.ZodString>;
1517
1518
  place: z.ZodOptional<z.ZodString>;
@@ -1728,7 +1729,10 @@ declare const tileflowProjectSchema: z.ZodObject<{
1728
1729
  landuse: z.ZodOptional<z.ZodString>;
1729
1730
  park: z.ZodOptional<z.ZodString>;
1730
1731
  water: z.ZodOptional<z.ZodString>;
1732
+ waterName: z.ZodOptional<z.ZodString>;
1733
+ waterway: z.ZodOptional<z.ZodString>;
1731
1734
  road: z.ZodOptional<z.ZodString>;
1735
+ roadName: z.ZodOptional<z.ZodString>;
1732
1736
  building: z.ZodOptional<z.ZodString>;
1733
1737
  boundary: z.ZodOptional<z.ZodString>;
1734
1738
  place: z.ZodOptional<z.ZodString>;
@@ -1745,7 +1749,10 @@ declare const tileflowProjectSchema: z.ZodObject<{
1745
1749
  landuse: z.ZodOptional<z.ZodString>;
1746
1750
  park: z.ZodOptional<z.ZodString>;
1747
1751
  water: z.ZodOptional<z.ZodString>;
1752
+ waterName: z.ZodOptional<z.ZodString>;
1753
+ waterway: z.ZodOptional<z.ZodString>;
1748
1754
  road: z.ZodOptional<z.ZodString>;
1755
+ roadName: z.ZodOptional<z.ZodString>;
1749
1756
  building: z.ZodOptional<z.ZodString>;
1750
1757
  boundary: z.ZodOptional<z.ZodString>;
1751
1758
  place: z.ZodOptional<z.ZodString>;
@@ -1954,28 +1961,21 @@ declare const tileflowProjectSchema: z.ZodObject<{
1954
1961
  type: z.ZodLiteral<"labels">;
1955
1962
  language: z.ZodOptional<z.ZodString>;
1956
1963
  places: z.ZodOptional<z.ZodEnum<{
1957
- balanced: "balanced";
1958
- none: "none";
1959
- minimal: "minimal";
1960
- detailed: "detailed";
1961
- }>>;
1962
- preset: z.ZodOptional<z.ZodEnum<{
1963
- balanced: "balanced";
1964
1964
  none: "none";
1965
- essential: "essential";
1966
- full: "full";
1965
+ major: "major";
1966
+ all: "all";
1967
1967
  }>>;
1968
1968
  roads: z.ZodOptional<z.ZodEnum<{
1969
- balanced: "balanced";
1970
1969
  none: "none";
1971
- minimal: "minimal";
1972
- detailed: "detailed";
1970
+ major: "major";
1971
+ highways: "highways";
1972
+ streets: "streets";
1973
+ all: "all";
1973
1974
  }>>;
1974
1975
  water: z.ZodOptional<z.ZodEnum<{
1975
- balanced: "balanced";
1976
1976
  none: "none";
1977
- minimal: "minimal";
1978
- detailed: "detailed";
1977
+ major: "major";
1978
+ all: "all";
1979
1979
  }>>;
1980
1980
  }, z.core.$strict>, z.ZodObject<{
1981
1981
  type: z.ZodLiteral<"poi">;
@@ -2217,7 +2217,10 @@ declare const tileflowProjectSchema: z.ZodObject<{
2217
2217
  landuse: z.ZodOptional<z.ZodString>;
2218
2218
  park: z.ZodOptional<z.ZodString>;
2219
2219
  water: z.ZodOptional<z.ZodString>;
2220
+ waterName: z.ZodOptional<z.ZodString>;
2221
+ waterway: z.ZodOptional<z.ZodString>;
2220
2222
  road: z.ZodOptional<z.ZodString>;
2223
+ roadName: z.ZodOptional<z.ZodString>;
2221
2224
  building: z.ZodOptional<z.ZodString>;
2222
2225
  boundary: z.ZodOptional<z.ZodString>;
2223
2226
  place: z.ZodOptional<z.ZodString>;
@@ -2241,12 +2244,12 @@ declare function defineTileflow<const TConfig extends TileflowProjectConfig>(con
2241
2244
 
2242
2245
  declare function osm(options?: TileflowOsmBasemapOptions): TileflowOsmBasemapConfig;
2243
2246
 
2247
+ declare function roads(options?: TileflowRoadsModuleOptions): TileflowRoadsModuleConfig;
2248
+
2244
2249
  declare function labels(options?: TileflowLabelsModuleOptions): TileflowLabelsModuleConfig;
2245
2250
 
2246
2251
  declare function poi(options?: TileflowPoiModuleOptions): TileflowPoiModuleConfig;
2247
2252
 
2248
- declare function roads(options?: TileflowRoadsModuleOptions): TileflowRoadsModuleConfig;
2249
-
2250
2253
  declare function styleOverride(options?: TileflowStyleOverrideOptions): TileflowStyleOverrideModuleConfig;
2251
2254
 
2252
2255
  declare const defaultTileflowManifestUrl = "/tileflow/manifest.json";
@@ -2357,4 +2360,4 @@ declare function normalizeTileflowStaticImageSize(input: {
2357
2360
  };
2358
2361
  declare function inferTileflowAnalyticsFromStyleUrl(styleUrl: string): TileflowAnalytics | undefined;
2359
2362
 
2360
- export { type MapLibreSprite, type MapLibreStyle, type TileflowAdvancedColorConfig, type TileflowAnalytics, type TileflowBaseColors, type TileflowBasemapConfig, type TileflowBoundaryColorConfig, type TileflowBuildingColorConfig, type TileflowBuildings, type TileflowColor, type TileflowColorConfig, type TileflowColors, type TileflowConfig, type TileflowDensity, type TileflowFontWeight, type TileflowFonts, type TileflowHydroColorConfig, type TileflowIconSet, type TileflowIconSetConfig, type TileflowLabelColorConfig, type TileflowLabelDensity, type TileflowLabelLanguage, type TileflowLabels, type TileflowLabelsModuleConfig, type TileflowLabelsModuleOptions, type TileflowLandcoverColorConfig, type TileflowLanduseColorConfig, type TileflowManifest, type TileflowManifestLoadOptions, type TileflowMapConfig, type TileflowMapMarker, type TileflowMapMode, type TileflowMapModeOptions, type TileflowModuleConfig, type TileflowOsmBasemapConfig, type TileflowOsmBasemapOptions, type TileflowPoi, type TileflowPoiCategory, type TileflowPoiColorConfig, type TileflowPoiIcons, type TileflowPoiLabels, type TileflowPoiModuleConfig, type TileflowPoiModuleOptions, type TileflowProjectConfig, type TileflowProjectIconSets, type TileflowProjectThemes, type TileflowRenderer, type TileflowRoadColorConfig, type TileflowRoadDetail, type TileflowRoadExtras, type TileflowRoadHierarchy, type TileflowRoadOutline, type TileflowRoadWeight, type TileflowRoads, type TileflowRoadsModuleConfig, type TileflowRoadsModuleOptions, type TileflowRuntimeManifest, type TileflowRuntimeManifestMap, type TileflowRuntimeManifestMapEntry, type TileflowRuntimeStyle, type TileflowRuntimeStyleOptions, type TileflowSourceLayers, type TileflowStyleLayerOverride, type TileflowStyleOptions, type TileflowStyleOverrideModuleConfig, type TileflowStyleOverrideOptions, type TileflowTerrain, type TileflowTerrainConfig, type TileflowTerrainEncoding, type TileflowTerrainMode, type TileflowTheme, type TileflowThemeConfig, type TileflowThemeMode, type TileflowThemeModulesConfig, type TileflowThemeName, type TileflowTiles, type TileflowTilesetConfig, type TileflowTypography, type TileflowViewConfig, type ValidationMessage, type ValidationResult, configSchema, createManifest, createStyle, createStyleFromProject, createTileflowSessionId, defaultTileflowManifestUrl, defaultTileflowStyleBaseUrl, defineTileflow, getDefaultColors, getDefaultTileflowStyleBaseUrl, getTileflowAnalyticsApiUrl, inferTileflowAnalyticsFromStyleUrl, isTileflowLocalDevHost, labels, loadTileflowManifest, mergeTileflowAnalytics, normalizeTileflowStaticImageSize, normalizeTileflowUrl, osm, parseTileflowMap, parseTileflowProject, poi, resolveTileflowAnalyticsRequestUrl, resolveTileflowManifestMap, resolveTileflowMapMode, resolveTileflowRuntimeStyle, resolveTileflowStaticImageUrl, resolveTileflowStyleUrl, roads, shouldLoadTileflowManifest, startTileflowSession, styleOverride, tileflowMapSchema, tileflowProjectSchema, tileflowThemeSchema, validateConfig, validateTileflowConfig };
2363
+ export { type MapLibreSprite, type MapLibreStyle, type TileflowAdvancedColorConfig, type TileflowAnalytics, type TileflowBaseColors, type TileflowBasemapConfig, type TileflowBoundaryColorConfig, type TileflowBuildingColorConfig, type TileflowBuildings, type TileflowColor, type TileflowColorConfig, type TileflowColors, type TileflowConfig, type TileflowDensity, type TileflowFontWeight, type TileflowFonts, type TileflowHydroColorConfig, type TileflowIconSet, type TileflowIconSetConfig, type TileflowLabelColorConfig, type TileflowLabelDetail, type TileflowLabelLanguage, type TileflowLabels, type TileflowLabelsModuleConfig, type TileflowLabelsModuleOptions, type TileflowLandcoverColorConfig, type TileflowLanduseColorConfig, type TileflowManifest, type TileflowManifestLoadOptions, type TileflowMapConfig, type TileflowMapMarker, type TileflowMapMode, type TileflowMapModeOptions, type TileflowModuleConfig, type TileflowOsmBasemapConfig, type TileflowOsmBasemapOptions, type TileflowPoi, type TileflowPoiCategory, type TileflowPoiColorConfig, type TileflowPoiIcons, type TileflowPoiLabels, type TileflowPoiModuleConfig, type TileflowPoiModuleOptions, type TileflowProjectConfig, type TileflowProjectIconSets, type TileflowProjectThemes, type TileflowRenderer, type TileflowRoadColorConfig, type TileflowRoadDetail, type TileflowRoadExtras, type TileflowRoadHierarchy, type TileflowRoadLabelDetail, type TileflowRoadOutline, type TileflowRoadWeight, type TileflowRoads, type TileflowRoadsModuleConfig, type TileflowRoadsModuleOptions, type TileflowRuntimeManifest, type TileflowRuntimeManifestMap, type TileflowRuntimeManifestMapEntry, type TileflowRuntimeStyle, type TileflowRuntimeStyleOptions, type TileflowSourceLayers, type TileflowStyleLayerOverride, type TileflowStyleOptions, type TileflowStyleOverrideModuleConfig, type TileflowStyleOverrideOptions, type TileflowTerrain, type TileflowTerrainConfig, type TileflowTerrainEncoding, type TileflowTerrainMode, type TileflowTheme, type TileflowThemeConfig, type TileflowThemeMode, type TileflowThemeModulesConfig, type TileflowThemeName, type TileflowTiles, type TileflowTilesetConfig, type TileflowTypography, type TileflowViewConfig, type ValidationMessage, type ValidationResult, configSchema, createManifest, createStyle, createStyleFromProject, createTileflowSessionId, defaultTileflowManifestUrl, defaultTileflowStyleBaseUrl, defineTileflow, getDefaultColors, getDefaultTileflowStyleBaseUrl, getTileflowAnalyticsApiUrl, inferTileflowAnalyticsFromStyleUrl, isTileflowLocalDevHost, labels, loadTileflowManifest, mergeTileflowAnalytics, normalizeTileflowStaticImageSize, normalizeTileflowUrl, osm, parseTileflowMap, parseTileflowProject, poi, resolveTileflowAnalyticsRequestUrl, resolveTileflowManifestMap, resolveTileflowMapMode, resolveTileflowRuntimeStyle, resolveTileflowStaticImageUrl, resolveTileflowStyleUrl, roads, shouldLoadTileflowManifest, startTileflowSession, styleOverride, tileflowMapSchema, tileflowProjectSchema, tileflowThemeSchema, validateConfig, validateTileflowConfig };
package/dist/index.js CHANGED
@@ -512,245 +512,6 @@ function rgbToHex(r, g, b) {
512
512
  return `#${[r, g, b].map((value) => value.toString(16).padStart(2, "0")).join("")}`;
513
513
  }
514
514
 
515
- // src/modules/labels/index.ts
516
- function labels(options = {}) {
517
- return {
518
- type: "labels",
519
- ...options.language ? { language: options.language } : {},
520
- ...options.places ? { places: options.places } : {},
521
- ...options.preset ? { preset: options.preset } : {},
522
- ...options.roads ? { roads: options.roads } : {},
523
- ...options.water ? { water: options.water } : {}
524
- };
525
- }
526
- function resolveLabels(legacyLabels, moduleConfig, fallback) {
527
- const mode = moduleConfig?.preset ?? legacyLabels ?? fallback;
528
- const density = labelDensityFromMode(mode);
529
- return {
530
- language: moduleConfig?.language ?? "auto",
531
- mode,
532
- places: moduleConfig?.places ?? density,
533
- roads: moduleConfig?.roads ?? density,
534
- water: moduleConfig?.water ?? density
535
- };
536
- }
537
- function labelLayers(source, sourceLayer, colors, labelsConfig, typography) {
538
- if (labelsConfig.mode === "none" || labelsConfig.places === "none") {
539
- return [];
540
- }
541
- const maxRank = labelsConfig.places === "minimal" ? 3 : labelsConfig.places === "balanced" ? 7 : 12;
542
- return [
543
- {
544
- id: "place-labels",
545
- type: "symbol",
546
- source,
547
- "source-layer": sourceLayer,
548
- minzoom: 3,
549
- filter: ["<=", ["coalesce", ["get", "rank"], 10], maxRank],
550
- layout: {
551
- "text-field": labelTextField(labelsConfig.language),
552
- "text-font": textFont(typography),
553
- "text-size": ["interpolate", ["linear"], ["zoom"], 3, 10, 8, 13, 14, 17],
554
- "text-letter-spacing": 0,
555
- "text-padding": 4
556
- },
557
- paint: {
558
- "text-color": colors.labels.primary,
559
- "text-halo-color": colors.labels.halo,
560
- "text-halo-width": 1.2
561
- }
562
- }
563
- ];
564
- }
565
- function labelDensityFromMode(mode) {
566
- if (mode === "none") return "none";
567
- if (mode === "essential") return "minimal";
568
- if (mode === "full") return "detailed";
569
- return "balanced";
570
- }
571
- function labelTextField(language) {
572
- if (language === "local") {
573
- return ["coalesce", ["get", "name"], ["get", "name:latin"], ["get", "name:en"]];
574
- }
575
- if (language === "auto") {
576
- return ["coalesce", ["get", "name:latin"], ["get", "name"], ["get", "name:en"]];
577
- }
578
- return ["coalesce", ["get", `name:${language}`], ["get", "name:latin"], ["get", "name"]];
579
- }
580
-
581
- // src/modules/poi/index.ts
582
- var defaultPoiMinZoom = {
583
- none: 24,
584
- minimal: 15,
585
- balanced: 13,
586
- full: 11
587
- };
588
- function poi(options = {}) {
589
- return {
590
- type: "poi",
591
- ...options.categories ? { categories: options.categories } : {},
592
- ...options.icons !== void 0 ? { icons: options.icons } : {},
593
- ...options.labels ? { labels: options.labels } : {},
594
- ...options.minZoom !== void 0 ? { minZoom: options.minZoom } : {},
595
- ...options.preset ? { preset: options.preset } : {}
596
- };
597
- }
598
- function resolvePoi(legacyPoi, moduleConfig, fallback) {
599
- const mode = moduleConfig?.preset ?? legacyPoi ?? fallback;
600
- return {
601
- categories: moduleConfig?.categories,
602
- icons: moduleConfig?.icons ?? (mode === "none" ? false : "essential"),
603
- labels: moduleConfig?.labels ?? poiLabelsFromMode(mode),
604
- minZoom: moduleConfig?.minZoom ?? defaultPoiMinZoom[mode],
605
- mode
606
- };
607
- }
608
- function poiLayers(source, sourceLayer, colors, poiConfig, typography, customIconSpriteId2, customIconMapping) {
609
- if (poiConfig.mode === "none") {
610
- return [];
611
- }
612
- const layout = {
613
- "text-field": labelTextField("auto"),
614
- "text-font": textFont(typography),
615
- "text-size": ["interpolate", ["linear"], ["zoom"], poiConfig.minZoom, 10, 17, 12],
616
- "text-letter-spacing": 0,
617
- "text-padding": 2
618
- };
619
- if (poiConfig.labels === "none") {
620
- delete layout["text-field"];
621
- }
622
- if (poiConfig.icons) {
623
- layout["icon-image"] = poiIconImageExpression(customIconSpriteId2, customIconMapping);
624
- layout["icon-optional"] = true;
625
- layout["icon-padding"] = 2;
626
- layout["icon-size"] = ["interpolate", ["linear"], ["zoom"], poiConfig.minZoom, 0.85, 17, 1];
627
- layout["text-anchor"] = "top";
628
- layout["text-offset"] = [0, 0.75];
629
- }
630
- return [
631
- {
632
- id: "poi-labels",
633
- type: "symbol",
634
- source,
635
- "source-layer": sourceLayer,
636
- minzoom: poiConfig.minZoom,
637
- ...poiConfig.categories ? { filter: poiCategoryFilter(poiConfig.categories) } : {},
638
- layout,
639
- paint: {
640
- "text-color": colors.poi.label,
641
- "text-halo-color": colors.poi.halo ?? alpha(colors.background, 0.78),
642
- "text-halo-width": 1
643
- }
644
- }
645
- ];
646
- }
647
- function poiLabelsFromMode(mode) {
648
- if (mode === "none") return "none";
649
- if (mode === "minimal") return "minimal";
650
- if (mode === "full") return "full";
651
- return "balanced";
652
- }
653
- function poiCategoryFilter(categories) {
654
- return ["in", "class", ...categories.flatMap((category) => poiCategoryClasses(category))];
655
- }
656
- function poiIconImageExpression(customIconSpriteId2, customIconMapping = {}) {
657
- const matchParts = [];
658
- for (const [className, iconName] of Object.entries(poiClassIconNames)) {
659
- matchParts.push(
660
- className,
661
- poiIconImageFallback(iconName, customIconSpriteId2, customIconMapping)
662
- );
663
- }
664
- return [
665
- "match",
666
- ["get", "class"],
667
- ...matchParts,
668
- poiIconImageFallback("default", customIconSpriteId2, customIconMapping)
669
- ];
670
- }
671
- function poiCategoryClasses(category) {
672
- return poiCategoryClassNames[category] ?? [category];
673
- }
674
- function poiIconImageFallback(iconName, customIconSpriteId2, customIconMapping) {
675
- const osmBrightIcon = `${osmBrightIconNameForSemanticIcon(iconName)}_11`;
676
- const customIconName = customIconMapping[iconName] ?? iconName;
677
- if (!customIconSpriteId2) {
678
- return ["coalesce", ["image", customIconName], ["image", osmBrightIcon]];
679
- }
680
- return [
681
- "coalesce",
682
- ["image", `${customIconSpriteId2}:${customIconName}`],
683
- ["image", osmBrightIcon]
684
- ];
685
- }
686
- function osmBrightIconNameForSemanticIcon(iconName) {
687
- return semanticToOsmBrightIconName[iconName] ?? "marker";
688
- }
689
- var poiCategoryClassNames = {
690
- coffee: ["cafe"],
691
- culture: ["museum", "gallery", "theatre", "cinema", "library", "attraction"],
692
- education: ["school", "college", "university"],
693
- food: ["restaurant", "fast_food", "bar", "pub", "biergarten"],
694
- health: ["hospital", "pharmacy", "doctors", "dentist"],
695
- lodging: ["hotel", "motel", "hostel", "guest_house"],
696
- services: ["bank", "post", "toilet", "police", "fire_station", "town_hall"],
697
- shopping: ["shop", "mall", "supermarket", "marketplace"],
698
- transit: ["railway", "station", "bus", "bus_stop", "subway", "tram"]
699
- };
700
- var poiClassIconNames = {
701
- attraction: "culture",
702
- bank: "services",
703
- bar: "food",
704
- biergarten: "food",
705
- bus: "transit",
706
- bus_stop: "transit",
707
- cafe: "coffee",
708
- cinema: "culture",
709
- college: "education",
710
- dentist: "health",
711
- doctors: "health",
712
- fast_food: "food",
713
- fire_station: "services",
714
- gallery: "culture",
715
- guest_house: "lodging",
716
- hospital: "health",
717
- hostel: "lodging",
718
- hotel: "lodging",
719
- library: "culture",
720
- mall: "shopping",
721
- marketplace: "shopping",
722
- motel: "lodging",
723
- museum: "culture",
724
- pharmacy: "health",
725
- police: "services",
726
- post: "services",
727
- pub: "food",
728
- railway: "transit",
729
- restaurant: "food",
730
- school: "education",
731
- shop: "shopping",
732
- station: "transit",
733
- subway: "transit",
734
- supermarket: "shopping",
735
- theatre: "culture",
736
- toilet: "services",
737
- town_hall: "services",
738
- tram: "transit",
739
- university: "education"
740
- };
741
- var semanticToOsmBrightIconName = {
742
- coffee: "cafe",
743
- culture: "museum",
744
- default: "marker",
745
- education: "school",
746
- food: "restaurant",
747
- health: "hospital",
748
- lodging: "lodging",
749
- services: "marker",
750
- shopping: "shop",
751
- transit: "railway"
752
- };
753
-
754
515
  // src/modules/roads/index.ts
755
516
  var moduleRoadDefaults = {
756
517
  detail: "streets",
@@ -764,6 +525,18 @@ var arterialRoadClasses = ["primary", "secondary", "tertiary"];
764
525
  var majorRoadClasses = [...highwayRoadClasses, ...arterialRoadClasses];
765
526
  var streetRoadClasses = ["minor"];
766
527
  var serviceRoadClasses = ["service", "track"];
528
+ var majorRoadClassSet = new Set(majorRoadClasses);
529
+ function roadClassesForDetail(detail) {
530
+ if (detail === "none") return [];
531
+ return [
532
+ ...detail === "highways" ? highwayRoadClasses : majorRoadClasses,
533
+ ...detail === "streets" || detail === "all" ? streetRoadClasses : [],
534
+ ...detail === "all" ? serviceRoadClasses : []
535
+ ];
536
+ }
537
+ function isMajorRoadClass(roadClass) {
538
+ return majorRoadClassSet.has(roadClass);
539
+ }
767
540
  function roads(options = {}) {
768
541
  return {
769
542
  type: "roads",
@@ -835,14 +608,10 @@ function roadLayers(source, sourceLayer, colors, options) {
835
608
  return [];
836
609
  }
837
610
  const metrics = roadStyleMetrics(options);
838
- const selectedMajorRoadClasses = options.detail === "highways" ? [...highwayRoadClasses] : [...majorRoadClasses];
839
- const showsStreetRoads = options.detail === "streets" || options.detail === "all";
840
- const showsServiceRoads = options.detail === "all";
841
- const roadClasses = [
842
- ...selectedMajorRoadClasses,
843
- ...showsStreetRoads ? streetRoadClasses : [],
844
- ...showsServiceRoads ? serviceRoadClasses : []
845
- ];
611
+ const roadClasses = roadClassesForDetail(options.detail);
612
+ const selectedMajorRoadClasses = roadClasses.filter(isMajorRoadClass);
613
+ const showsStreetRoads = roadClasses.includes("minor");
614
+ const showsServiceRoads = roadClasses.includes("service");
846
615
  const transportClasses = [
847
616
  ...roadClasses,
848
617
  ...options.extras.paths ? ["path"] : [],
@@ -1083,104 +852,521 @@ function roadLayers(source, sourceLayer, colors, options) {
1083
852
  );
1084
853
  return layers;
1085
854
  }
1086
- function roadDefaultsFromLegacyMode(mode) {
1087
- if (mode === "hidden") {
1088
- return {
1089
- detail: "none",
1090
- extras: { ferry: false, paths: false, rail: false },
1091
- hierarchy: "clear",
1092
- outline: "subtle",
1093
- weight: "regular"
1094
- };
855
+ function roadDefaultsFromLegacyMode(mode) {
856
+ if (mode === "hidden") {
857
+ return {
858
+ detail: "none",
859
+ extras: { ferry: false, paths: false, rail: false },
860
+ hierarchy: "clear",
861
+ outline: "subtle",
862
+ weight: "regular"
863
+ };
864
+ }
865
+ if (mode === "soft") {
866
+ return {
867
+ detail: "streets",
868
+ extras: { ferry: false, paths: false, rail: false },
869
+ hierarchy: "subtle",
870
+ outline: "subtle",
871
+ weight: "thin"
872
+ };
873
+ }
874
+ if (mode === "detailed") {
875
+ return {
876
+ detail: "all",
877
+ extras: { ferry: true, paths: true, rail: true },
878
+ hierarchy: "clear",
879
+ outline: "strong",
880
+ weight: "regular"
881
+ };
882
+ }
883
+ return {
884
+ detail: "streets",
885
+ extras: { ferry: true, paths: true, rail: true },
886
+ hierarchy: "clear",
887
+ outline: "subtle",
888
+ weight: "regular"
889
+ };
890
+ }
891
+ function legacyModeFromResolvedRoads(options) {
892
+ if (options.detail === "none") return "hidden";
893
+ if (options.hierarchy === "subtle") return "soft";
894
+ if (options.hierarchy === "strong" || options.detail === "all") return "detailed";
895
+ return "standard";
896
+ }
897
+ function classFilter(classes) {
898
+ return ["match", ["get", "class"], classes, true, false];
899
+ }
900
+ function roadOpacityExpression(majorOpacity, minorOpacity) {
901
+ return [
902
+ "case",
903
+ classFilter(majorRoadClasses),
904
+ majorOpacity,
905
+ classFilter(streetRoadClasses),
906
+ ["step", ["zoom"], 0, 12, minorOpacity],
907
+ classFilter(serviceRoadClasses),
908
+ ["step", ["zoom"], 0, 14, minorOpacity],
909
+ minorOpacity
910
+ ];
911
+ }
912
+ function roadWidthExpression(stops, metrics) {
913
+ return [
914
+ "case",
915
+ classFilter(majorRoadClasses),
916
+ zoomWidth(stops, metrics.weightScale * metrics.majorWidthScale),
917
+ zoomWidth(stops, metrics.weightScale * metrics.minorWidthScale)
918
+ ];
919
+ }
920
+ function zoomWidth(stops, scale) {
921
+ return [
922
+ "interpolate",
923
+ ["linear"],
924
+ ["zoom"],
925
+ ...stops.flatMap(([zoom, width]) => [zoom, width * scale])
926
+ ];
927
+ }
928
+ function roadClassColorExpression(colors) {
929
+ return [
930
+ "match",
931
+ ["get", "class"],
932
+ "motorway",
933
+ colors.roads.motorway,
934
+ "trunk",
935
+ colors.roads.trunk,
936
+ "primary",
937
+ colors.roads.primary,
938
+ "secondary",
939
+ colors.roads.secondary,
940
+ "rail",
941
+ colors.roads.rail,
942
+ "transit",
943
+ colors.roads.rail,
944
+ "ferry",
945
+ colors.roads.ferry,
946
+ "path",
947
+ colors.roads.path,
948
+ "track",
949
+ colors.roads.path,
950
+ colors.roads.minor
951
+ ];
952
+ }
953
+
954
+ // src/modules/labels/index.ts
955
+ var moduleLabelDefaults = {
956
+ language: "auto",
957
+ places: "major",
958
+ roads: "major",
959
+ water: "major"
960
+ };
961
+ var majorPlaceClasses = ["continent", "country", "state", "city", "town"];
962
+ var majorWaterClasses = ["ocean", "sea", "lake"];
963
+ var majorWaterwayClasses = ["river"];
964
+ function labels(options = {}) {
965
+ return {
966
+ type: "labels",
967
+ ...options.language ? { language: options.language } : {},
968
+ ...options.places ? { places: options.places } : {},
969
+ ...options.roads ? { roads: options.roads } : {},
970
+ ...options.water ? { water: options.water } : {}
971
+ };
972
+ }
973
+ function resolveLabels(legacyLabels, moduleConfig, fallback) {
974
+ const fallbackMode = legacyLabels ?? fallback;
975
+ const defaults = moduleConfig ? moduleLabelDefaults : labelDefaultsFromLegacyMode(fallbackMode);
976
+ const resolved = {
977
+ language: moduleConfig?.language ?? defaults.language,
978
+ places: moduleConfig?.places ?? defaults.places,
979
+ roads: moduleConfig?.roads ?? defaults.roads,
980
+ water: moduleConfig?.water ?? defaults.water
981
+ };
982
+ return {
983
+ ...resolved,
984
+ mode: moduleConfig ? legacyModeFromResolvedLabels(resolved) : fallbackMode
985
+ };
986
+ }
987
+ function labelLayers(source, sourceLayers, colors, labelsConfig, roadsConfig, typography) {
988
+ return [
989
+ ...placeLabelLayers(source, sourceLayers.place, colors, labelsConfig, typography),
990
+ ...roadLabelLayers(
991
+ source,
992
+ sourceLayers.roadName,
993
+ colors,
994
+ labelsConfig,
995
+ roadsConfig,
996
+ typography
997
+ ),
998
+ ...waterLabelLayers(
999
+ source,
1000
+ sourceLayers.waterName,
1001
+ sourceLayers.waterway,
1002
+ colors,
1003
+ labelsConfig,
1004
+ typography
1005
+ )
1006
+ ];
1007
+ }
1008
+ function placeLabelLayers(source, sourceLayer, colors, labelsConfig, typography) {
1009
+ if (labelsConfig.places === "none") return [];
1010
+ return [
1011
+ {
1012
+ id: "place-labels",
1013
+ type: "symbol",
1014
+ source,
1015
+ "source-layer": sourceLayer,
1016
+ minzoom: 0,
1017
+ filter: labelsConfig.places === "major" ? ["all", ["has", "name"], classFilter2(majorPlaceClasses)] : ["has", "name"],
1018
+ layout: {
1019
+ "text-field": labelTextField(labelsConfig.language),
1020
+ "text-font": textFont(typography),
1021
+ "text-size": ["interpolate", ["linear"], ["zoom"], 3, 10, 8, 13, 14, 17],
1022
+ "text-letter-spacing": 0,
1023
+ "text-padding": 4
1024
+ },
1025
+ paint: {
1026
+ "text-color": colors.labels.primary,
1027
+ "text-halo-color": colors.labels.halo,
1028
+ "text-halo-width": 1.2
1029
+ }
1030
+ }
1031
+ ];
1032
+ }
1033
+ function roadLabelLayers(source, sourceLayer, colors, labelsConfig, roadsConfig, typography) {
1034
+ const classes = visibleRoadLabelClasses(labelsConfig.roads, roadsConfig);
1035
+ const majorClasses = classes.filter(isMajorRoadClass);
1036
+ const minorClasses = classes.filter((roadClass) => !isMajorRoadClass(roadClass));
1037
+ if (classes.length === 0) return [];
1038
+ const layers = [];
1039
+ const sharedLayout = {
1040
+ "symbol-placement": "line",
1041
+ "text-field": labelTextField(labelsConfig.language),
1042
+ "text-font": textFont(typography),
1043
+ "text-rotation-alignment": "map",
1044
+ "text-size": ["interpolate", ["linear"], ["zoom"], 11, 11, 14, 13, 17, 15]
1045
+ };
1046
+ const sharedPaint = {
1047
+ "text-color": colors.labels.road,
1048
+ "text-halo-color": colors.labels.halo,
1049
+ "text-halo-width": 1
1050
+ };
1051
+ if (majorClasses.length > 0) {
1052
+ const namedMajorClasses = majorClasses.filter((roadClass) => roadClass !== "motorway");
1053
+ layers.push({
1054
+ id: "road-shields",
1055
+ type: "symbol",
1056
+ source,
1057
+ "source-layer": sourceLayer,
1058
+ minzoom: 8,
1059
+ filter: ["all", ["has", "ref"], classFilter2(majorClasses)],
1060
+ layout: {
1061
+ "symbol-placement": "line",
1062
+ "symbol-spacing": 240,
1063
+ "text-field": ["get", "ref"],
1064
+ "text-font": textFont(typography),
1065
+ "text-rotation-alignment": "viewport",
1066
+ "text-size": 10
1067
+ },
1068
+ paint: sharedPaint
1069
+ });
1070
+ if (namedMajorClasses.length > 0) {
1071
+ layers.unshift({
1072
+ id: "road-labels-major",
1073
+ type: "symbol",
1074
+ source,
1075
+ "source-layer": sourceLayer,
1076
+ minzoom: 10,
1077
+ filter: ["all", ["has", "name"], classFilter2(namedMajorClasses)],
1078
+ layout: sharedLayout,
1079
+ paint: sharedPaint
1080
+ });
1081
+ }
1082
+ }
1083
+ if (minorClasses.length > 0) {
1084
+ layers.push({
1085
+ id: "road-labels-minor",
1086
+ type: "symbol",
1087
+ source,
1088
+ "source-layer": sourceLayer,
1089
+ minzoom: 14,
1090
+ filter: ["all", ["has", "name"], classFilter2(minorClasses)],
1091
+ layout: sharedLayout,
1092
+ paint: sharedPaint
1093
+ });
1094
+ }
1095
+ return layers;
1096
+ }
1097
+ function waterLabelLayers(source, waterNameSourceLayer, waterwaySourceLayer, colors, labelsConfig, typography) {
1098
+ if (labelsConfig.water === "none") return [];
1099
+ const waterFilter = labelsConfig.water === "major" ? ["all", ["has", "name"], classFilter2(majorWaterClasses)] : ["has", "name"];
1100
+ const waterwayFilter = labelsConfig.water === "major" ? ["all", ["has", "name"], classFilter2(majorWaterwayClasses)] : ["has", "name"];
1101
+ const paint = {
1102
+ "text-color": colors.labels.water,
1103
+ "text-halo-color": colors.labels.halo,
1104
+ "text-halo-width": 1
1105
+ };
1106
+ return [
1107
+ {
1108
+ id: "water-labels",
1109
+ type: "symbol",
1110
+ source,
1111
+ "source-layer": waterNameSourceLayer,
1112
+ minzoom: 3,
1113
+ filter: ["all", ["==", ["geometry-type"], "Point"], waterFilter],
1114
+ layout: {
1115
+ "text-field": labelTextField(labelsConfig.language),
1116
+ "text-font": textFont(typography),
1117
+ "text-size": ["interpolate", ["linear"], ["zoom"], 3, 11, 8, 14]
1118
+ },
1119
+ paint
1120
+ },
1121
+ {
1122
+ id: "water-line-labels",
1123
+ type: "symbol",
1124
+ source,
1125
+ "source-layer": waterNameSourceLayer,
1126
+ minzoom: 5,
1127
+ filter: ["all", ["==", ["geometry-type"], "LineString"], waterFilter],
1128
+ layout: {
1129
+ "symbol-placement": "line",
1130
+ "text-field": labelTextField(labelsConfig.language),
1131
+ "text-font": textFont(typography),
1132
+ "text-size": 13
1133
+ },
1134
+ paint
1135
+ },
1136
+ {
1137
+ id: "waterway-labels",
1138
+ type: "symbol",
1139
+ source,
1140
+ "source-layer": waterwaySourceLayer,
1141
+ minzoom: 10,
1142
+ filter: ["all", ["==", ["geometry-type"], "LineString"], waterwayFilter],
1143
+ layout: {
1144
+ "symbol-placement": "line",
1145
+ "symbol-spacing": 320,
1146
+ "text-field": labelTextField(labelsConfig.language),
1147
+ "text-font": textFont(typography),
1148
+ "text-size": 12
1149
+ },
1150
+ paint
1151
+ }
1152
+ ];
1153
+ }
1154
+ function visibleRoadLabelClasses(detail, roadsConfig) {
1155
+ if (detail === "none" || roadsConfig.detail === "none") return [];
1156
+ const visibleRoadClasses = [
1157
+ ...roadClassesForDetail(roadsConfig.detail),
1158
+ ...roadsConfig.extras.paths ? ["path"] : []
1159
+ ];
1160
+ const requestedClasses = roadLabelClasses(detail);
1161
+ const visible = new Set(visibleRoadClasses);
1162
+ return requestedClasses.filter((roadClass) => visible.has(roadClass));
1163
+ }
1164
+ function roadLabelClasses(detail) {
1165
+ return [...roadClassesForDetail(detail), ...detail === "all" ? ["path"] : []];
1166
+ }
1167
+ function labelDefaultsFromLegacyMode(mode) {
1168
+ if (mode === "none") {
1169
+ return { language: "auto", places: "none", roads: "none", water: "none" };
1170
+ }
1171
+ if (mode === "essential") {
1172
+ return { language: "auto", places: "major", roads: "major", water: "major" };
1173
+ }
1174
+ return { language: "auto", places: "all", roads: "all", water: "all" };
1175
+ }
1176
+ function legacyModeFromResolvedLabels(options) {
1177
+ if (options.places === "none" && options.roads === "none" && options.water === "none") {
1178
+ return "none";
1095
1179
  }
1096
- if (mode === "soft") {
1097
- return {
1098
- detail: "streets",
1099
- extras: { ferry: false, paths: false, rail: false },
1100
- hierarchy: "subtle",
1101
- outline: "subtle",
1102
- weight: "thin"
1103
- };
1180
+ if (options.places === "major" && options.roads === "major" && options.water === "major") {
1181
+ return "essential";
1104
1182
  }
1105
- if (mode === "detailed") {
1106
- return {
1107
- detail: "all",
1108
- extras: { ferry: true, paths: true, rail: true },
1109
- hierarchy: "clear",
1110
- outline: "strong",
1111
- weight: "regular"
1112
- };
1183
+ return "balanced";
1184
+ }
1185
+ function classFilter2(classes) {
1186
+ return ["match", ["get", "class"], classes, true, false];
1187
+ }
1188
+ function labelTextField(language) {
1189
+ if (language === "local") {
1190
+ return ["coalesce", ["get", "name"], ["get", "name:latin"], ["get", "name:en"]];
1191
+ }
1192
+ if (language === "auto") {
1193
+ return ["coalesce", ["get", "name:latin"], ["get", "name"], ["get", "name:en"]];
1113
1194
  }
1195
+ return ["coalesce", ["get", `name:${language}`], ["get", "name:latin"], ["get", "name"]];
1196
+ }
1197
+
1198
+ // src/modules/poi/index.ts
1199
+ var defaultPoiMinZoom = {
1200
+ none: 24,
1201
+ minimal: 15,
1202
+ balanced: 13,
1203
+ full: 11
1204
+ };
1205
+ function poi(options = {}) {
1114
1206
  return {
1115
- detail: "streets",
1116
- extras: { ferry: true, paths: true, rail: true },
1117
- hierarchy: "clear",
1118
- outline: "subtle",
1119
- weight: "regular"
1207
+ type: "poi",
1208
+ ...options.categories ? { categories: options.categories } : {},
1209
+ ...options.icons !== void 0 ? { icons: options.icons } : {},
1210
+ ...options.labels ? { labels: options.labels } : {},
1211
+ ...options.minZoom !== void 0 ? { minZoom: options.minZoom } : {},
1212
+ ...options.preset ? { preset: options.preset } : {}
1120
1213
  };
1121
1214
  }
1122
- function legacyModeFromResolvedRoads(options) {
1123
- if (options.detail === "none") return "hidden";
1124
- if (options.hierarchy === "subtle") return "soft";
1125
- if (options.hierarchy === "strong" || options.detail === "all") return "detailed";
1126
- return "standard";
1127
- }
1128
- function classFilter(classes) {
1129
- return ["match", ["get", "class"], classes, true, false];
1215
+ function resolvePoi(legacyPoi, moduleConfig, fallback) {
1216
+ const mode = moduleConfig?.preset ?? legacyPoi ?? fallback;
1217
+ return {
1218
+ categories: moduleConfig?.categories,
1219
+ icons: moduleConfig?.icons ?? (mode === "none" ? false : "essential"),
1220
+ labels: moduleConfig?.labels ?? poiLabelsFromMode(mode),
1221
+ minZoom: moduleConfig?.minZoom ?? defaultPoiMinZoom[mode],
1222
+ mode
1223
+ };
1130
1224
  }
1131
- function roadOpacityExpression(majorOpacity, minorOpacity) {
1225
+ function poiLayers(source, sourceLayer, colors, poiConfig, typography, customIconSpriteId2, customIconMapping) {
1226
+ if (poiConfig.mode === "none") {
1227
+ return [];
1228
+ }
1229
+ const layout = {
1230
+ "text-field": labelTextField("auto"),
1231
+ "text-font": textFont(typography),
1232
+ "text-size": ["interpolate", ["linear"], ["zoom"], poiConfig.minZoom, 10, 17, 12],
1233
+ "text-letter-spacing": 0,
1234
+ "text-padding": 2
1235
+ };
1236
+ if (poiConfig.labels === "none") {
1237
+ delete layout["text-field"];
1238
+ }
1239
+ if (poiConfig.icons) {
1240
+ layout["icon-image"] = poiIconImageExpression(customIconSpriteId2, customIconMapping);
1241
+ layout["icon-optional"] = true;
1242
+ layout["icon-padding"] = 2;
1243
+ layout["icon-size"] = ["interpolate", ["linear"], ["zoom"], poiConfig.minZoom, 0.85, 17, 1];
1244
+ layout["text-anchor"] = "top";
1245
+ layout["text-offset"] = [0, 0.75];
1246
+ }
1132
1247
  return [
1133
- "case",
1134
- classFilter(majorRoadClasses),
1135
- majorOpacity,
1136
- classFilter(streetRoadClasses),
1137
- ["step", ["zoom"], 0, 12, minorOpacity],
1138
- classFilter(serviceRoadClasses),
1139
- ["step", ["zoom"], 0, 14, minorOpacity],
1140
- minorOpacity
1248
+ {
1249
+ id: "poi-labels",
1250
+ type: "symbol",
1251
+ source,
1252
+ "source-layer": sourceLayer,
1253
+ minzoom: poiConfig.minZoom,
1254
+ ...poiConfig.categories ? { filter: poiCategoryFilter(poiConfig.categories) } : {},
1255
+ layout,
1256
+ paint: {
1257
+ "text-color": colors.poi.label,
1258
+ "text-halo-color": colors.poi.halo ?? alpha(colors.background, 0.78),
1259
+ "text-halo-width": 1
1260
+ }
1261
+ }
1141
1262
  ];
1142
1263
  }
1143
- function roadWidthExpression(stops, metrics) {
1144
- return [
1145
- "case",
1146
- classFilter(majorRoadClasses),
1147
- zoomWidth(stops, metrics.weightScale * metrics.majorWidthScale),
1148
- zoomWidth(stops, metrics.weightScale * metrics.minorWidthScale)
1149
- ];
1264
+ function poiLabelsFromMode(mode) {
1265
+ if (mode === "none") return "none";
1266
+ if (mode === "minimal") return "minimal";
1267
+ if (mode === "full") return "full";
1268
+ return "balanced";
1150
1269
  }
1151
- function zoomWidth(stops, scale) {
1152
- return [
1153
- "interpolate",
1154
- ["linear"],
1155
- ["zoom"],
1156
- ...stops.flatMap(([zoom, width]) => [zoom, width * scale])
1157
- ];
1270
+ function poiCategoryFilter(categories) {
1271
+ return ["in", "class", ...categories.flatMap((category) => poiCategoryClasses(category))];
1158
1272
  }
1159
- function roadClassColorExpression(colors) {
1273
+ function poiIconImageExpression(customIconSpriteId2, customIconMapping = {}) {
1274
+ const matchParts = [];
1275
+ for (const [className, iconName] of Object.entries(poiClassIconNames)) {
1276
+ matchParts.push(
1277
+ className,
1278
+ poiIconImageFallback(iconName, customIconSpriteId2, customIconMapping)
1279
+ );
1280
+ }
1160
1281
  return [
1161
1282
  "match",
1162
1283
  ["get", "class"],
1163
- "motorway",
1164
- colors.roads.motorway,
1165
- "trunk",
1166
- colors.roads.trunk,
1167
- "primary",
1168
- colors.roads.primary,
1169
- "secondary",
1170
- colors.roads.secondary,
1171
- "rail",
1172
- colors.roads.rail,
1173
- "transit",
1174
- colors.roads.rail,
1175
- "ferry",
1176
- colors.roads.ferry,
1177
- "path",
1178
- colors.roads.path,
1179
- "track",
1180
- colors.roads.path,
1181
- colors.roads.minor
1284
+ ...matchParts,
1285
+ poiIconImageFallback("default", customIconSpriteId2, customIconMapping)
1286
+ ];
1287
+ }
1288
+ function poiCategoryClasses(category) {
1289
+ return poiCategoryClassNames[category] ?? [category];
1290
+ }
1291
+ function poiIconImageFallback(iconName, customIconSpriteId2, customIconMapping) {
1292
+ const osmBrightIcon = `${osmBrightIconNameForSemanticIcon(iconName)}_11`;
1293
+ const customIconName = customIconMapping[iconName] ?? iconName;
1294
+ if (!customIconSpriteId2) {
1295
+ return ["coalesce", ["image", customIconName], ["image", osmBrightIcon]];
1296
+ }
1297
+ return [
1298
+ "coalesce",
1299
+ ["image", `${customIconSpriteId2}:${customIconName}`],
1300
+ ["image", osmBrightIcon]
1182
1301
  ];
1183
1302
  }
1303
+ function osmBrightIconNameForSemanticIcon(iconName) {
1304
+ return semanticToOsmBrightIconName[iconName] ?? "marker";
1305
+ }
1306
+ var poiCategoryClassNames = {
1307
+ coffee: ["cafe"],
1308
+ culture: ["museum", "gallery", "theatre", "cinema", "library", "attraction"],
1309
+ education: ["school", "college", "university"],
1310
+ food: ["restaurant", "fast_food", "bar", "pub", "biergarten"],
1311
+ health: ["hospital", "pharmacy", "doctors", "dentist"],
1312
+ lodging: ["hotel", "motel", "hostel", "guest_house"],
1313
+ services: ["bank", "post", "toilet", "police", "fire_station", "town_hall"],
1314
+ shopping: ["shop", "mall", "supermarket", "marketplace"],
1315
+ transit: ["railway", "station", "bus", "bus_stop", "subway", "tram"]
1316
+ };
1317
+ var poiClassIconNames = {
1318
+ attraction: "culture",
1319
+ bank: "services",
1320
+ bar: "food",
1321
+ biergarten: "food",
1322
+ bus: "transit",
1323
+ bus_stop: "transit",
1324
+ cafe: "coffee",
1325
+ cinema: "culture",
1326
+ college: "education",
1327
+ dentist: "health",
1328
+ doctors: "health",
1329
+ fast_food: "food",
1330
+ fire_station: "services",
1331
+ gallery: "culture",
1332
+ guest_house: "lodging",
1333
+ hospital: "health",
1334
+ hostel: "lodging",
1335
+ hotel: "lodging",
1336
+ library: "culture",
1337
+ mall: "shopping",
1338
+ marketplace: "shopping",
1339
+ motel: "lodging",
1340
+ museum: "culture",
1341
+ pharmacy: "health",
1342
+ police: "services",
1343
+ post: "services",
1344
+ pub: "food",
1345
+ railway: "transit",
1346
+ restaurant: "food",
1347
+ school: "education",
1348
+ shop: "shopping",
1349
+ station: "transit",
1350
+ subway: "transit",
1351
+ supermarket: "shopping",
1352
+ theatre: "culture",
1353
+ toilet: "services",
1354
+ town_hall: "services",
1355
+ tram: "transit",
1356
+ university: "education"
1357
+ };
1358
+ var semanticToOsmBrightIconName = {
1359
+ coffee: "cafe",
1360
+ culture: "museum",
1361
+ default: "marker",
1362
+ education: "school",
1363
+ food: "restaurant",
1364
+ health: "hospital",
1365
+ lodging: "lodging",
1366
+ services: "marker",
1367
+ shopping: "shop",
1368
+ transit: "railway"
1369
+ };
1184
1370
 
1185
1371
  // src/basemaps/osm/layers.ts
1186
1372
  function openMapTilesLayers(source, sourceLayers, colors, options) {
@@ -1204,7 +1390,7 @@ function openMapTilesLayers(source, sourceLayers, colors, options) {
1204
1390
  ...buildingLayers(source, sourceLayers.building, colors, options.density, options.buildings),
1205
1391
  ...roadLayers(source, sourceLayers.road, colors, options.roads),
1206
1392
  ...boundaryLayers(source, sourceLayers.boundary, colors),
1207
- ...labelLayers(source, sourceLayers.place, colors, options.labels, options.typography),
1393
+ ...labelLayers(source, sourceLayers, colors, options.labels, options.roads, options.typography),
1208
1394
  ...poiLayers(
1209
1395
  source,
1210
1396
  sourceLayers.poi,
@@ -1568,7 +1754,10 @@ var sourceLayersSchema = z.object({
1568
1754
  landuse: z.string().optional(),
1569
1755
  park: z.string().optional(),
1570
1756
  water: z.string().optional(),
1757
+ waterName: z.string().optional(),
1758
+ waterway: z.string().optional(),
1571
1759
  road: z.string().optional(),
1760
+ roadName: z.string().optional(),
1572
1761
  building: z.string().optional(),
1573
1762
  boundary: z.string().optional(),
1574
1763
  place: z.string().optional(),
@@ -1584,9 +1773,10 @@ var basemapSchema = z.object({
1584
1773
  tileset: z.string().trim().min(1).optional(),
1585
1774
  url: z.url().optional()
1586
1775
  }).strict();
1776
+ var roadDetailSchema = z.enum(["none", "highways", "major", "streets", "all"]);
1587
1777
  var roadsModuleSchema = z.object({
1588
1778
  type: z.literal("roads"),
1589
- detail: z.enum(["none", "highways", "major", "streets", "all"]).optional(),
1779
+ detail: roadDetailSchema.optional(),
1590
1780
  extras: z.object({
1591
1781
  ferry: z.boolean().optional(),
1592
1782
  paths: z.boolean().optional(),
@@ -1596,14 +1786,13 @@ var roadsModuleSchema = z.object({
1596
1786
  outline: z.enum(["none", "subtle", "strong"]).optional(),
1597
1787
  weight: z.enum(["thin", "regular", "bold"]).optional()
1598
1788
  }).strict();
1599
- var labelDensitySchema = z.enum(["none", "minimal", "balanced", "detailed"]);
1789
+ var labelDetailSchema = z.enum(["none", "major", "all"]);
1600
1790
  var labelsModuleSchema = z.object({
1601
1791
  type: z.literal("labels"),
1602
1792
  language: z.string().trim().min(1).optional(),
1603
- places: labelDensitySchema.optional(),
1604
- preset: z.enum(["none", "essential", "balanced", "full"]).optional(),
1605
- roads: labelDensitySchema.optional(),
1606
- water: labelDensitySchema.optional()
1793
+ places: labelDetailSchema.optional(),
1794
+ roads: roadDetailSchema.optional(),
1795
+ water: labelDetailSchema.optional()
1607
1796
  }).strict();
1608
1797
  var poiModuleSchema = z.object({
1609
1798
  type: z.literal("poi"),
@@ -1956,7 +2145,7 @@ function applyOsmBrightControls(layers, options) {
1956
2145
  );
1957
2146
  }
1958
2147
  if (isLabelLayer(id, sourceLayer)) {
1959
- return applyLabelLayerControls(layer, options.labels);
2148
+ return applyLabelLayerControls(layer, options.labels, options.roads);
1960
2149
  }
1961
2150
  return layer;
1962
2151
  });
@@ -2043,12 +2232,12 @@ function scaleStyleValue(value, scale, fallback) {
2043
2232
  }
2044
2233
  return fallback * scale;
2045
2234
  }
2046
- function applyLabelLayerControls(layer, labels2) {
2235
+ function applyLabelLayerControls(layer, labels2, roads2) {
2047
2236
  const id = stringValue(layer.id);
2048
2237
  const sourceLayer = stringValue(layer["source-layer"]);
2049
2238
  const group = labelGroup(id, sourceLayer);
2050
- const density = labelGroupDensity(labels2, group);
2051
- if (labels2.mode === "none" || density === "none") {
2239
+ const detail = labelGroupDetail(labels2, group);
2240
+ if (detail === "none" || detail === "major" && isMinorLabelLayer(id, group)) {
2052
2241
  return setLayerVisibility(layer, "none");
2053
2242
  }
2054
2243
  const nextLayer = setLayerVisibility(layer, "visible");
@@ -2056,13 +2245,18 @@ function applyLabelLayerControls(layer, labels2) {
2056
2245
  if ("text-field" in layout && !isHighwayShieldLayer(id)) {
2057
2246
  layout["text-field"] = labelTextField(labels2.language);
2058
2247
  }
2059
- if (density === "minimal") {
2060
- nextLayer.minzoom = Math.max(numberValue(nextLayer.minzoom) ?? 0, minimalMinZoom(group));
2248
+ if (group === "roads") {
2249
+ const allowedClasses = (roads2 ? visibleRoadLabelClasses(detail, roads2) : roadLabelClasses(detail)).filter((roadClass) => roadLabelLayerClasses(id).includes(roadClass));
2250
+ if (allowedClasses.length === 0) {
2251
+ return setLayerVisibility(layer, "none");
2252
+ }
2253
+ nextLayer.filter = combineFilters(nextLayer.filter, ["in", "class", ...allowedClasses]);
2061
2254
  }
2062
- if (density === "detailed") {
2063
- const currentMinZoom = numberValue(nextLayer.minzoom);
2064
- if (currentMinZoom !== void 0) {
2065
- nextLayer.minzoom = Math.max(0, currentMinZoom - 1);
2255
+ if (group === "water" && detail === "major") {
2256
+ if (sourceLayer === "waterway") {
2257
+ nextLayer.filter = combineFilters(nextLayer.filter, ["==", "class", "river"]);
2258
+ } else if (id === "water-name-other") {
2259
+ nextLayer.filter = combineFilters(nextLayer.filter, ["in", "class", "sea", "lake"]);
2066
2260
  }
2067
2261
  }
2068
2262
  nextLayer.layout = layout;
@@ -2092,7 +2286,7 @@ function applyPoiLayerControls(layer, poi2, customIconSpriteId2, customIconMappi
2092
2286
  nextLayer.layout = layout;
2093
2287
  return nextLayer;
2094
2288
  }
2095
- function labelGroupDensity(labels2, group) {
2289
+ function labelGroupDetail(labels2, group) {
2096
2290
  if (group === "roads") return labels2.roads;
2097
2291
  if (group === "water") return labels2.water;
2098
2292
  return labels2.places;
@@ -2106,10 +2300,15 @@ function labelGroup(id, sourceLayer) {
2106
2300
  }
2107
2301
  return "places";
2108
2302
  }
2109
- function minimalMinZoom(group) {
2110
- if (group === "roads") return 13;
2111
- if (group === "water") return 5;
2112
- return 4;
2303
+ function isMinorLabelLayer(id, group) {
2304
+ if (group === "places") return id === "place-other" || id === "place-village";
2305
+ if (group === "roads") return id.includes("path") || id.includes("minor");
2306
+ return false;
2307
+ }
2308
+ function roadLabelLayerClasses(id) {
2309
+ if (id.includes("path")) return ["path"];
2310
+ if (id.includes("minor")) return ["minor", "service", "track"];
2311
+ return ["motorway", "trunk", "primary", "secondary", "tertiary"];
2113
2312
  }
2114
2313
  function isLabelLayer(id, sourceLayer) {
2115
2314
  return sourceLayer === "place" || sourceLayer === "transportation_name" || sourceLayer === "water_name" || sourceLayer === "waterway" || id.includes("highway-name") || id.includes("highway-shield") || id.includes("water-name");
@@ -5041,7 +5240,10 @@ var defaultSourceLayers = {
5041
5240
  landuse: "landuse",
5042
5241
  park: "park",
5043
5242
  water: "water",
5243
+ waterName: "water_name",
5244
+ waterway: "waterway",
5044
5245
  road: "transportation",
5246
+ roadName: "transportation_name",
5045
5247
  building: "building",
5046
5248
  boundary: "boundary",
5047
5249
  place: "place",
@@ -5105,8 +5307,9 @@ function createStyle(input = {}, options = {}) {
5105
5307
  labelsModule,
5106
5308
  theme.preset.labels ?? defaultConfig.labels
5107
5309
  );
5310
+ const { mode: resolvedLabelsMode, ...resolvedLabelsModule } = resolvedLabels;
5108
5311
  const resolvedPoi = resolvePoi(config.poi, poiModule, theme.preset.poi ?? defaultConfig.poi);
5109
- const labels2 = resolvedLabels.mode;
5312
+ const labels2 = resolvedLabelsMode;
5110
5313
  const poi2 = resolvedPoi.mode;
5111
5314
  const buildings = config.buildings ?? "flat";
5112
5315
  const colors = resolveColors(config.colors, theme.colors, {}, theme.modules);
@@ -5139,7 +5342,7 @@ function createStyle(input = {}, options = {}) {
5139
5342
  "tileflow:roads": resolvedRoadMode,
5140
5343
  "tileflow:roadsModule": roadModule ? resolvedRoadModule : void 0,
5141
5344
  "tileflow:labels": labels2,
5142
- "tileflow:labelsModule": labelsModule,
5345
+ "tileflow:labelsModule": labelsModule ? resolvedLabelsModule : void 0,
5143
5346
  "tileflow:poi": poi2,
5144
5347
  "tileflow:poiModule": poiModule,
5145
5348
  "tileflow:icons": iconSet ? {
@@ -5177,7 +5380,6 @@ function createStyle(input = {}, options = {}) {
5177
5380
  theme,
5178
5381
  tilesUrl,
5179
5382
  typography,
5180
- labels: labels2,
5181
5383
  labelsModule: resolvedLabels,
5182
5384
  poi: poi2,
5183
5385
  poiModule: resolvedPoi,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tileflow/core",
3
- "version": "0.1.0-alpha.12",
3
+ "version": "0.1.0-alpha.14",
4
4
  "description": "Typed Tileflow config and MapLibre style compiler.",
5
5
  "type": "module",
6
6
  "sideEffects": false,