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

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
@@ -3,7 +3,7 @@
3
3
  Typed Tileflow config helpers and the MapLibre style compiler.
4
4
 
5
5
  ```ts
6
- import {defineTileflow, labels, osm, poi} from '@tileflow/core';
6
+ import {defineTileflow, labels, osm, poi, roads} from '@tileflow/core';
7
7
 
8
8
  export default defineTileflow({
9
9
  icons: {
@@ -53,6 +53,7 @@ export default defineTileflow({
53
53
  modules: [
54
54
  labels({roads: 'minimal'}),
55
55
  poi({categories: ['food', 'coffee', 'culture', 'transit'], icons: 'essential'}),
56
+ roads({detail: 'streets', hierarchy: 'clear', outline: 'subtle', weight: 'regular'}),
56
57
  ],
57
58
  layers: {
58
59
  'highway-primary': {
@@ -78,7 +79,7 @@ Tileflow keeps these levels separate:
78
79
  - `theme.colors` contains human-scale base tokens such as `water`, `park`, `road`, `roadMajor`, `roadCasing`, `text`, and `textHalo`.
79
80
  - `theme.modules.<name>` contains advanced, module-specific tokens. These are granular: setting `theme.modules.roads.rail` does not recolor primary roads.
80
81
  - `maps.<name>.icons` can point at a reusable root icon set, a hosted MapLibre sprite URL, or a local icon folder for Vite/build output.
81
- - `maps.<name>.modules` enables semantic behavior such as `labels()`, `poi()`, and `roads()`. Road colors remain in `theme.modules.roads`; `roads()` controls detail, hierarchy, weight, casing, and optional transport extras.
82
+ - `maps.<name>.modules` enables semantic behavior such as `labels()`, `poi()`, and `roads()`. Road colors remain in `theme.modules.roads`; `roads()` controls detail, hierarchy, weight, outline, and optional transport extras.
82
83
  - `maps.<name>.renderer` can be `auto`, `osm-bright`, or `generated`. `auto` preserves the default template when compatible; `generated` always uses Tileflow's generated layers; `osm-bright` throws if the map config cannot be rendered with the template.
83
84
  - `layers` is the raw MapLibre escape hatch for exact layer patches.
84
85
 
@@ -86,15 +87,14 @@ Missing colors preserve the basemap default. Legacy aliases such as `accent`, `c
86
87
 
87
88
  ## Roads module
88
89
 
89
- Use a preset for the overall road-network intent, then override only the behavior you need:
90
+ Control each independent road-network decision directly:
90
91
 
91
92
  ```ts
92
93
  roads({
93
- preset: 'navigation',
94
94
  detail: 'streets',
95
95
  hierarchy: 'strong',
96
96
  weight: 'regular',
97
- casing: 'strong',
97
+ outline: 'strong',
98
98
  extras: {
99
99
  paths: false,
100
100
  rail: false,
@@ -103,14 +103,15 @@ roads({
103
103
  });
104
104
  ```
105
105
 
106
- | Preset | Detail | Hierarchy | Weight | Casing | Extras |
107
- | ------------ | --------- | --------- | --------- | -------- | ------- |
108
- | `minimal` | `streets` | `subtle` | `thin` | `subtle` | all off |
109
- | `standard` | `streets` | `clear` | `regular` | `subtle` | all on |
110
- | `navigation` | `streets` | `strong` | `regular` | `strong` | all off |
111
- | `detailed` | `all` | `clear` | `regular` | `strong` | all on |
106
+ `detail` is progressive: `none` hides roads; `highways` shows motorway and trunk roads;
107
+ `major` adds primary, secondary, and tertiary roads; `streets` adds minor streets; and `all`
108
+ adds service roads and tracks. `hierarchy` accepts `subtle`, `clear`, or `strong`; `weight`
109
+ accepts `thin`, `regular`, or `bold`; and `outline` accepts `none`, `subtle`, or `strong`.
110
+ `extras.paths`, `extras.rail`, and `extras.ferry` are independent and default to `false`.
112
111
 
113
- `detail` accepts `none`, `major`, `streets`, or `all`; `hierarchy` accepts `subtle`, `clear`, or `strong`; `weight` accepts `thin`, `regular`, or `bold`; and `casing` accepts `none`, `subtle`, or `strong`. Explicit options override their preset. `extras.paths`, `extras.rail`, and `extras.ferry` are independent.
112
+ Without options, `roads()` uses `detail: 'streets'`, `hierarchy: 'clear'`,
113
+ `weight: 'regular'`, `outline: 'subtle'`, and all extras disabled. Named visual recipes belong
114
+ in examples rather than the runtime API, so the resolved behavior is always visible in config.
114
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
117
 
package/dist/index.d.ts CHANGED
@@ -191,13 +191,10 @@ type TileflowOsmBasemapConfig = {
191
191
  };
192
192
  type TileflowBasemapConfig = TileflowOsmBasemapConfig;
193
193
  type TileflowOsmBasemapOptions = Omit<TileflowOsmBasemapConfig, 'type'>;
194
- type TileflowRoadsModulePreset = 'minimal' | 'standard' | 'navigation' | 'detailed'
195
- /** @deprecated Use "standard". */
196
- | 'modern';
197
- type TileflowRoadDetail = 'none' | 'major' | 'streets' | 'all';
194
+ type TileflowRoadDetail = 'none' | 'highways' | 'major' | 'streets' | 'all';
198
195
  type TileflowRoadHierarchy = 'subtle' | 'clear' | 'strong';
199
196
  type TileflowRoadWeight = 'thin' | 'regular' | 'bold';
200
- type TileflowRoadCasing = 'none' | 'subtle' | 'strong';
197
+ type TileflowRoadOutline = 'none' | 'subtle' | 'strong';
201
198
  type TileflowRoadExtras = {
202
199
  ferry?: boolean;
203
200
  paths?: boolean;
@@ -205,13 +202,10 @@ type TileflowRoadExtras = {
205
202
  };
206
203
  type TileflowRoadsModuleConfig = {
207
204
  type: 'roads';
208
- casing?: TileflowRoadCasing;
209
205
  detail?: TileflowRoadDetail;
210
- /** @deprecated Use hierarchy. */
211
- density?: TileflowDensity;
212
206
  extras?: TileflowRoadExtras;
213
207
  hierarchy?: TileflowRoadHierarchy;
214
- preset?: TileflowRoadsModulePreset;
208
+ outline?: TileflowRoadOutline;
215
209
  weight?: TileflowRoadWeight;
216
210
  };
217
211
  type TileflowRoadsModuleOptions = Omit<TileflowRoadsModuleConfig, 'type'>;
@@ -803,22 +797,13 @@ declare const tileflowMapSchema: z.ZodObject<{
803
797
  }>>;
804
798
  }, z.core.$strict>, z.ZodObject<{
805
799
  type: z.ZodLiteral<"roads">;
806
- casing: z.ZodOptional<z.ZodEnum<{
807
- none: "none";
808
- subtle: "subtle";
809
- strong: "strong";
810
- }>>;
811
800
  detail: z.ZodOptional<z.ZodEnum<{
812
801
  none: "none";
813
802
  major: "major";
803
+ highways: "highways";
814
804
  streets: "streets";
815
805
  all: "all";
816
806
  }>>;
817
- density: z.ZodOptional<z.ZodEnum<{
818
- clean: "clean";
819
- balanced: "balanced";
820
- dense: "dense";
821
- }>>;
822
807
  extras: z.ZodOptional<z.ZodObject<{
823
808
  ferry: z.ZodOptional<z.ZodBoolean>;
824
809
  paths: z.ZodOptional<z.ZodBoolean>;
@@ -826,15 +811,13 @@ declare const tileflowMapSchema: z.ZodObject<{
826
811
  }, z.core.$strict>>;
827
812
  hierarchy: z.ZodOptional<z.ZodEnum<{
828
813
  subtle: "subtle";
829
- strong: "strong";
830
814
  clear: "clear";
815
+ strong: "strong";
831
816
  }>>;
832
- preset: z.ZodOptional<z.ZodEnum<{
833
- minimal: "minimal";
834
- standard: "standard";
835
- detailed: "detailed";
836
- navigation: "navigation";
837
- modern: "modern";
817
+ outline: z.ZodOptional<z.ZodEnum<{
818
+ none: "none";
819
+ subtle: "subtle";
820
+ strong: "strong";
838
821
  }>>;
839
822
  weight: z.ZodOptional<z.ZodEnum<{
840
823
  regular: "regular";
@@ -1310,22 +1293,13 @@ declare const configSchema: z.ZodObject<{
1310
1293
  }>>;
1311
1294
  }, z.core.$strict>, z.ZodObject<{
1312
1295
  type: z.ZodLiteral<"roads">;
1313
- casing: z.ZodOptional<z.ZodEnum<{
1314
- none: "none";
1315
- subtle: "subtle";
1316
- strong: "strong";
1317
- }>>;
1318
1296
  detail: z.ZodOptional<z.ZodEnum<{
1319
1297
  none: "none";
1320
1298
  major: "major";
1299
+ highways: "highways";
1321
1300
  streets: "streets";
1322
1301
  all: "all";
1323
1302
  }>>;
1324
- density: z.ZodOptional<z.ZodEnum<{
1325
- clean: "clean";
1326
- balanced: "balanced";
1327
- dense: "dense";
1328
- }>>;
1329
1303
  extras: z.ZodOptional<z.ZodObject<{
1330
1304
  ferry: z.ZodOptional<z.ZodBoolean>;
1331
1305
  paths: z.ZodOptional<z.ZodBoolean>;
@@ -1333,15 +1307,13 @@ declare const configSchema: z.ZodObject<{
1333
1307
  }, z.core.$strict>>;
1334
1308
  hierarchy: z.ZodOptional<z.ZodEnum<{
1335
1309
  subtle: "subtle";
1336
- strong: "strong";
1337
1310
  clear: "clear";
1311
+ strong: "strong";
1338
1312
  }>>;
1339
- preset: z.ZodOptional<z.ZodEnum<{
1340
- minimal: "minimal";
1341
- standard: "standard";
1342
- detailed: "detailed";
1343
- navigation: "navigation";
1344
- modern: "modern";
1313
+ outline: z.ZodOptional<z.ZodEnum<{
1314
+ none: "none";
1315
+ subtle: "subtle";
1316
+ strong: "strong";
1345
1317
  }>>;
1346
1318
  weight: z.ZodOptional<z.ZodEnum<{
1347
1319
  regular: "regular";
@@ -2027,22 +1999,13 @@ declare const tileflowProjectSchema: z.ZodObject<{
2027
1999
  }>>;
2028
2000
  }, z.core.$strict>, z.ZodObject<{
2029
2001
  type: z.ZodLiteral<"roads">;
2030
- casing: z.ZodOptional<z.ZodEnum<{
2031
- none: "none";
2032
- subtle: "subtle";
2033
- strong: "strong";
2034
- }>>;
2035
2002
  detail: z.ZodOptional<z.ZodEnum<{
2036
2003
  none: "none";
2037
2004
  major: "major";
2005
+ highways: "highways";
2038
2006
  streets: "streets";
2039
2007
  all: "all";
2040
2008
  }>>;
2041
- density: z.ZodOptional<z.ZodEnum<{
2042
- clean: "clean";
2043
- balanced: "balanced";
2044
- dense: "dense";
2045
- }>>;
2046
2009
  extras: z.ZodOptional<z.ZodObject<{
2047
2010
  ferry: z.ZodOptional<z.ZodBoolean>;
2048
2011
  paths: z.ZodOptional<z.ZodBoolean>;
@@ -2050,15 +2013,13 @@ declare const tileflowProjectSchema: z.ZodObject<{
2050
2013
  }, z.core.$strict>>;
2051
2014
  hierarchy: z.ZodOptional<z.ZodEnum<{
2052
2015
  subtle: "subtle";
2053
- strong: "strong";
2054
2016
  clear: "clear";
2017
+ strong: "strong";
2055
2018
  }>>;
2056
- preset: z.ZodOptional<z.ZodEnum<{
2057
- minimal: "minimal";
2058
- standard: "standard";
2059
- detailed: "detailed";
2060
- navigation: "navigation";
2061
- modern: "modern";
2019
+ outline: z.ZodOptional<z.ZodEnum<{
2020
+ none: "none";
2021
+ subtle: "subtle";
2022
+ strong: "strong";
2062
2023
  }>>;
2063
2024
  weight: z.ZodOptional<z.ZodEnum<{
2064
2025
  regular: "regular";
@@ -2396,4 +2357,4 @@ declare function normalizeTileflowStaticImageSize(input: {
2396
2357
  };
2397
2358
  declare function inferTileflowAnalyticsFromStyleUrl(styleUrl: string): TileflowAnalytics | undefined;
2398
2359
 
2399
- 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 TileflowRoadCasing, type TileflowRoadColorConfig, type TileflowRoadDetail, type TileflowRoadExtras, type TileflowRoadHierarchy, type TileflowRoadWeight, type TileflowRoads, type TileflowRoadsModuleConfig, type TileflowRoadsModuleOptions, type TileflowRoadsModulePreset, 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 };
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 };
package/dist/index.js CHANGED
@@ -752,66 +752,40 @@ var semanticToOsmBrightIconName = {
752
752
  };
753
753
 
754
754
  // src/modules/roads/index.ts
755
- var roadPresets = {
756
- minimal: {
757
- casing: "subtle",
758
- detail: "streets",
759
- extras: { ferry: false, paths: false, rail: false },
760
- hierarchy: "subtle",
761
- weight: "thin"
762
- },
763
- standard: {
764
- casing: "subtle",
765
- detail: "streets",
766
- extras: { ferry: true, paths: true, rail: true },
767
- hierarchy: "clear",
768
- weight: "regular"
769
- },
770
- navigation: {
771
- casing: "strong",
772
- detail: "streets",
773
- extras: { ferry: false, paths: false, rail: false },
774
- hierarchy: "strong",
775
- weight: "regular"
776
- },
777
- detailed: {
778
- casing: "strong",
779
- detail: "all",
780
- extras: { ferry: true, paths: true, rail: true },
781
- hierarchy: "clear",
782
- weight: "regular"
783
- }
755
+ var moduleRoadDefaults = {
756
+ detail: "streets",
757
+ extras: { ferry: false, paths: false, rail: false },
758
+ hierarchy: "clear",
759
+ outline: "subtle",
760
+ weight: "regular"
784
761
  };
785
- var majorRoadClasses = ["motorway", "trunk", "primary", "secondary", "tertiary"];
786
- var minorRoadClasses = ["minor", "service", "track"];
762
+ var highwayRoadClasses = ["motorway", "trunk"];
763
+ var arterialRoadClasses = ["primary", "secondary", "tertiary"];
764
+ var majorRoadClasses = [...highwayRoadClasses, ...arterialRoadClasses];
765
+ var streetRoadClasses = ["minor"];
766
+ var serviceRoadClasses = ["service", "track"];
787
767
  function roads(options = {}) {
788
768
  return {
789
769
  type: "roads",
790
- preset: options.preset ?? "standard",
791
- ...options.casing ? { casing: options.casing } : {},
792
770
  ...options.detail ? { detail: options.detail } : {},
793
- ...options.density ? { density: options.density } : {},
794
771
  ...options.extras ? { extras: { ...options.extras } } : {},
795
772
  ...options.hierarchy ? { hierarchy: options.hierarchy } : {},
773
+ ...options.outline ? { outline: options.outline } : {},
796
774
  ...options.weight ? { weight: options.weight } : {}
797
775
  };
798
776
  }
799
777
  function resolveRoads(legacyRoads, moduleConfig, fallback) {
800
778
  const fallbackMode = legacyRoads ?? fallback;
801
- const preset = moduleConfig ? normalizeRoadPreset(moduleConfig.preset ?? presetFromLegacyMode(fallbackMode)) : presetFromLegacyMode(fallbackMode);
802
- const defaults = roadPresets[preset];
803
- const hierarchy = moduleConfig?.hierarchy ?? hierarchyFromLegacyDensity(moduleConfig?.density) ?? defaults.hierarchy;
804
- const detail = moduleConfig?.detail ?? (fallbackMode === "hidden" && !moduleConfig ? "none" : defaults.detail);
779
+ const defaults = moduleConfig ? moduleRoadDefaults : roadDefaultsFromLegacyMode(fallbackMode);
805
780
  const resolved = {
806
- casing: moduleConfig?.casing ?? defaults.casing,
807
- detail,
781
+ detail: moduleConfig?.detail ?? defaults.detail,
808
782
  extras: {
809
783
  ferry: moduleConfig?.extras?.ferry ?? defaults.extras.ferry,
810
784
  paths: moduleConfig?.extras?.paths ?? defaults.extras.paths,
811
785
  rail: moduleConfig?.extras?.rail ?? defaults.extras.rail
812
786
  },
813
- hierarchy,
814
- preset,
787
+ hierarchy: moduleConfig?.hierarchy ?? defaults.hierarchy,
788
+ outline: moduleConfig?.outline ?? defaults.outline,
815
789
  weight: moduleConfig?.weight ?? defaults.weight
816
790
  };
817
791
  return {
@@ -822,22 +796,22 @@ function resolveRoads(legacyRoads, moduleConfig, fallback) {
822
796
  function roadStyleMetrics(options) {
823
797
  const hierarchy = {
824
798
  subtle: {
825
- majorOpacity: 0.72,
826
- majorWidthScale: 0.95,
827
- minorOpacity: 0.6336,
828
- minorWidthScale: 0.88
799
+ majorOpacity: 0.92,
800
+ majorWidthScale: 1,
801
+ minorOpacity: 0.82,
802
+ minorWidthScale: 0.94
829
803
  },
830
804
  clear: {
831
- majorOpacity: 0.84,
805
+ majorOpacity: 0.92,
832
806
  majorWidthScale: 1,
833
- minorOpacity: 0.7392,
834
- minorWidthScale: 0.9
807
+ minorOpacity: 0.68,
808
+ minorWidthScale: 0.84
835
809
  },
836
810
  strong: {
837
- majorOpacity: 0.98,
838
- majorWidthScale: 1.16,
839
- minorOpacity: 0.56,
840
- minorWidthScale: 0.72
811
+ majorOpacity: 0.92,
812
+ majorWidthScale: 1,
813
+ minorOpacity: 0.52,
814
+ minorWidthScale: 0.7
841
815
  }
842
816
  }[options.hierarchy];
843
817
  const weightScale = {
@@ -845,28 +819,30 @@ function roadStyleMetrics(options) {
845
819
  regular: 1,
846
820
  bold: 1.24
847
821
  }[options.weight];
848
- const casingOpacity = {
822
+ const outlineOpacity = {
849
823
  none: 0,
850
- subtle: 0.16,
851
- strong: 0.3
852
- }[options.casing];
824
+ subtle: 0.18,
825
+ strong: 0.34
826
+ }[options.outline];
853
827
  return {
854
- casingOpacity,
855
828
  ...hierarchy,
829
+ outlineOpacity,
856
830
  weightScale
857
831
  };
858
832
  }
859
- function roadMinorMinZoom(detail) {
860
- return detail === "all" ? 6 : 12;
861
- }
862
833
  function roadLayers(source, sourceLayer, colors, options) {
863
834
  if (options.detail === "none") {
864
835
  return [];
865
836
  }
866
837
  const metrics = roadStyleMetrics(options);
867
- const showsMinorRoads = options.detail === "streets" || options.detail === "all";
868
- const minorMinZoom = roadMinorMinZoom(options.detail);
869
- const roadClasses = showsMinorRoads ? [...majorRoadClasses, ...minorRoadClasses] : [...majorRoadClasses];
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
+ ];
870
846
  const transportClasses = [
871
847
  ...roadClasses,
872
848
  ...options.extras.paths ? ["path"] : [],
@@ -875,12 +851,13 @@ function roadLayers(source, sourceLayer, colors, options) {
875
851
  ];
876
852
  const roadClassColor = roadClassColorExpression(colors);
877
853
  const nonTunnelRoadFilter = ["!=", ["get", "brunnel"], "tunnel"];
878
- const majorFilter = classFilter(majorRoadClasses);
879
- const minorFilter = classFilter(minorRoadClasses);
854
+ const majorFilter = classFilter(selectedMajorRoadClasses);
855
+ const streetFilter = classFilter(streetRoadClasses);
856
+ const serviceFilter = classFilter(serviceRoadClasses);
880
857
  const roadFilter = classFilter(roadClasses);
881
858
  const transportFilter = classFilter(transportClasses);
882
859
  const layers = [];
883
- if (options.casing !== "none") {
860
+ if (options.outline !== "none") {
884
861
  layers.push({
885
862
  id: "roads-tunnels-casing",
886
863
  type: "line",
@@ -891,14 +868,17 @@ function roadLayers(source, sourceLayer, colors, options) {
891
868
  paint: {
892
869
  "line-color": colors.roads.tunnel,
893
870
  "line-dasharray": [0.6, 0.8],
894
- "line-opacity": metrics.casingOpacity * 0.85,
895
- "line-width": zoomWidth(
871
+ "line-opacity": roadOpacityExpression(
872
+ metrics.outlineOpacity * 0.85,
873
+ metrics.outlineOpacity * 0.85
874
+ ),
875
+ "line-width": roadWidthExpression(
896
876
  [
897
- [10, 0.5],
898
- [14, 1.7],
899
- [16, 3.8]
877
+ [10, 0.8],
878
+ [14, 2.2],
879
+ [16, 4.8]
900
880
  ],
901
- metrics.weightScale
881
+ metrics
902
882
  )
903
883
  }
904
884
  });
@@ -913,18 +893,21 @@ function roadLayers(source, sourceLayer, colors, options) {
913
893
  paint: {
914
894
  "line-color": roadClassColor,
915
895
  "line-dasharray": [0.6, 0.8],
916
- "line-opacity": metrics.majorOpacity * 0.28,
917
- "line-width": zoomWidth(
896
+ "line-opacity": roadOpacityExpression(
897
+ metrics.majorOpacity * 0.28,
898
+ metrics.minorOpacity * 0.28
899
+ ),
900
+ "line-width": roadWidthExpression(
918
901
  [
919
902
  [10, 0.25],
920
903
  [14, 1],
921
904
  [16, 2.4]
922
905
  ],
923
- metrics.weightScale
906
+ metrics
924
907
  )
925
908
  }
926
909
  });
927
- if (options.casing !== "none") {
910
+ if (options.outline !== "none") {
928
911
  layers.push({
929
912
  id: "roads-casing",
930
913
  type: "line",
@@ -934,14 +917,14 @@ function roadLayers(source, sourceLayer, colors, options) {
934
917
  filter: ["all", nonTunnelRoadFilter, roadFilter],
935
918
  paint: {
936
919
  "line-color": colors.roads.casing,
937
- "line-opacity": roadCasingOpacityExpression(metrics.casingOpacity, minorMinZoom),
938
- "line-width": zoomWidth(
920
+ "line-opacity": roadOpacityExpression(metrics.outlineOpacity, metrics.outlineOpacity),
921
+ "line-width": roadWidthExpression(
939
922
  [
940
- [6, 0.4],
941
- [12, 1.2],
942
- [16, 4]
923
+ [6, 0.7],
924
+ [12, 1.8],
925
+ [16, 5.4]
943
926
  ],
944
- metrics.weightScale
927
+ metrics
945
928
  )
946
929
  }
947
930
  });
@@ -975,7 +958,7 @@ function roadLayers(source, sourceLayer, colors, options) {
975
958
  type: "line",
976
959
  source,
977
960
  "source-layer": sourceLayer,
978
- minzoom: options.detail === "all" ? 8 : 10,
961
+ minzoom: 10,
979
962
  filter: ["all", nonTunnelRoadFilter, classFilter(["path"])],
980
963
  paint: {
981
964
  "line-color": colors.roads.path,
@@ -998,7 +981,7 @@ function roadLayers(source, sourceLayer, colors, options) {
998
981
  type: "line",
999
982
  source,
1000
983
  "source-layer": sourceLayer,
1001
- minzoom: options.detail === "all" ? 6 : 7,
984
+ minzoom: 7,
1002
985
  filter: ["all", nonTunnelRoadFilter, classFilter(["rail", "transit"])],
1003
986
  paint: {
1004
987
  "line-color": colors.roads.rail,
@@ -1014,20 +997,40 @@ function roadLayers(source, sourceLayer, colors, options) {
1014
997
  }
1015
998
  });
1016
999
  }
1017
- if (showsMinorRoads) {
1000
+ if (showsServiceRoads) {
1001
+ layers.push({
1002
+ id: "roads-service",
1003
+ type: "line",
1004
+ source,
1005
+ "source-layer": sourceLayer,
1006
+ minzoom: 14,
1007
+ filter: ["all", nonTunnelRoadFilter, serviceFilter],
1008
+ paint: {
1009
+ "line-color": roadClassColor,
1010
+ "line-opacity": metrics.minorOpacity,
1011
+ "line-width": zoomWidth(
1012
+ [
1013
+ [14, 0.6],
1014
+ [16, 2.2]
1015
+ ],
1016
+ metrics.weightScale * metrics.minorWidthScale
1017
+ )
1018
+ }
1019
+ });
1020
+ }
1021
+ if (showsStreetRoads) {
1018
1022
  layers.push({
1019
1023
  id: "roads-minor",
1020
1024
  type: "line",
1021
1025
  source,
1022
1026
  "source-layer": sourceLayer,
1023
- minzoom: minorMinZoom,
1024
- filter: ["all", nonTunnelRoadFilter, minorFilter],
1027
+ minzoom: 12,
1028
+ filter: ["all", nonTunnelRoadFilter, streetFilter],
1025
1029
  paint: {
1026
- "line-color": colors.roads.minor,
1030
+ "line-color": roadClassColor,
1027
1031
  "line-opacity": metrics.minorOpacity,
1028
1032
  "line-width": zoomWidth(
1029
1033
  [
1030
- [6, 0.2],
1031
1034
  [12, 0.8],
1032
1035
  [16, 3]
1033
1036
  ],
@@ -1066,33 +1069,55 @@ function roadLayers(source, sourceLayer, colors, options) {
1066
1069
  filter: ["all", ["==", ["get", "brunnel"], "bridge"], transportFilter],
1067
1070
  paint: {
1068
1071
  "line-color": colors.roads.bridge,
1069
- "line-opacity": metrics.majorOpacity,
1070
- "line-width": zoomWidth(
1072
+ "line-opacity": roadOpacityExpression(metrics.majorOpacity, metrics.minorOpacity),
1073
+ "line-width": roadWidthExpression(
1071
1074
  [
1072
1075
  [10, 0.45],
1073
1076
  [14, 1.6],
1074
1077
  [16, 4.4]
1075
1078
  ],
1076
- metrics.weightScale * metrics.majorWidthScale
1079
+ metrics
1077
1080
  )
1078
1081
  }
1079
1082
  }
1080
1083
  );
1081
1084
  return layers;
1082
1085
  }
1083
- function normalizeRoadPreset(preset) {
1084
- return !preset || preset === "modern" ? "standard" : preset;
1085
- }
1086
- function presetFromLegacyMode(mode) {
1087
- if (mode === "soft") return "minimal";
1088
- if (mode === "detailed") return "detailed";
1089
- return "standard";
1090
- }
1091
- function hierarchyFromLegacyDensity(density) {
1092
- if (density === "clean") return "subtle";
1093
- if (density === "dense") return "strong";
1094
- if (density === "balanced") return "clear";
1095
- return void 0;
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
+ };
1095
+ }
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
+ };
1104
+ }
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
+ };
1113
+ }
1114
+ return {
1115
+ detail: "streets",
1116
+ extras: { ferry: true, paths: true, rail: true },
1117
+ hierarchy: "clear",
1118
+ outline: "subtle",
1119
+ weight: "regular"
1120
+ };
1096
1121
  }
1097
1122
  function legacyModeFromResolvedRoads(options) {
1098
1123
  if (options.detail === "none") return "hidden";
@@ -1103,15 +1128,24 @@ function legacyModeFromResolvedRoads(options) {
1103
1128
  function classFilter(classes) {
1104
1129
  return ["match", ["get", "class"], classes, true, false];
1105
1130
  }
1106
- function roadCasingOpacityExpression(opacity, minorMinZoom) {
1131
+ function roadOpacityExpression(majorOpacity, minorOpacity) {
1107
1132
  return [
1108
- "interpolate",
1109
- ["linear"],
1110
- ["zoom"],
1111
- minorMinZoom - 0.5,
1112
- ["case", classFilter(majorRoadClasses), opacity, 0],
1113
- minorMinZoom,
1114
- opacity
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
1141
+ ];
1142
+ }
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)
1115
1149
  ];
1116
1150
  }
1117
1151
  function zoomWidth(stops, scale) {
@@ -1552,16 +1586,14 @@ var basemapSchema = z.object({
1552
1586
  }).strict();
1553
1587
  var roadsModuleSchema = z.object({
1554
1588
  type: z.literal("roads"),
1555
- casing: z.enum(["none", "subtle", "strong"]).optional(),
1556
- detail: z.enum(["none", "major", "streets", "all"]).optional(),
1557
- density: z.enum(["clean", "balanced", "dense"]).optional(),
1589
+ detail: z.enum(["none", "highways", "major", "streets", "all"]).optional(),
1558
1590
  extras: z.object({
1559
1591
  ferry: z.boolean().optional(),
1560
1592
  paths: z.boolean().optional(),
1561
1593
  rail: z.boolean().optional()
1562
1594
  }).strict().optional(),
1563
1595
  hierarchy: z.enum(["subtle", "clear", "strong"]).optional(),
1564
- preset: z.enum(["minimal", "standard", "navigation", "detailed", "modern"]).optional(),
1596
+ outline: z.enum(["none", "subtle", "strong"]).optional(),
1565
1597
  weight: z.enum(["thin", "regular", "bold"]).optional()
1566
1598
  }).strict();
1567
1599
  var labelDensitySchema = z.enum(["none", "minimal", "balanced", "detailed"]);
@@ -1934,14 +1966,15 @@ function applyRoadLayerControls(layer, roads2) {
1934
1966
  const type = stringValue(layer.type);
1935
1967
  const group = osmBrightRoadGroup(id);
1936
1968
  const isCasing = id.includes("casing");
1937
- if (!isRoadLayerVisible(group, roads2) || isCasing && roads2.casing === "none") {
1969
+ const showsSharedHighwayLink = roads2.detail === "highways" && isSharedMajorLinkLayer(id);
1970
+ if (!isRoadLayerVisible(group, roads2) && !showsSharedHighwayLink || isCasing && roads2.outline === "none") {
1938
1971
  return setLayerVisibility(layer, "none");
1939
1972
  }
1940
1973
  const metrics = roadStyleMetrics(roads2);
1941
1974
  const nextLayer = setLayerVisibility(layer, "visible");
1942
1975
  const paint = isRecord3(nextLayer.paint) ? { ...nextLayer.paint } : {};
1943
- const widthScale = metrics.weightScale * (group === "major" ? metrics.majorWidthScale : metrics.minorWidthScale);
1944
- const opacityScale = isCasing ? metrics.casingOpacity : group === "major" ? metrics.majorOpacity : group === "ferry" ? metrics.majorOpacity * 0.8 : group === "paths" ? metrics.minorOpacity * 0.77 : group === "rail" ? metrics.minorOpacity * 0.65 : metrics.minorOpacity;
1976
+ const widthScale = metrics.weightScale * (group === "highways" || group === "major" ? metrics.majorWidthScale : metrics.minorWidthScale);
1977
+ const opacityScale = isCasing ? metrics.outlineOpacity : group === "highways" || group === "major" ? metrics.majorOpacity : group === "ferry" ? metrics.majorOpacity * 0.8 : group === "paths" ? metrics.minorOpacity * 0.77 : group === "rail" ? metrics.minorOpacity * 0.65 : metrics.minorOpacity;
1945
1978
  if (type === "line") {
1946
1979
  paint["line-opacity"] = scaleStyleValue(paint["line-opacity"], opacityScale, 1);
1947
1980
  paint["line-width"] = scaleStyleValue(paint["line-width"], widthScale, 1);
@@ -1951,18 +1984,18 @@ function applyRoadLayerControls(layer, roads2) {
1951
1984
  paint["fill-opacity"] = scaleStyleValue(paint["fill-opacity"], opacityScale, 1);
1952
1985
  nextLayer.paint = paint;
1953
1986
  }
1954
- if (group === "minor" && roads2.detail === "streets") {
1955
- nextLayer.minzoom = Math.max(
1956
- numberValue(nextLayer.minzoom) ?? 0,
1957
- roadMinorMinZoom(roads2.detail)
1958
- );
1987
+ if (roads2.detail === "highways" && (group === "highways" || showsSharedHighwayLink)) {
1988
+ nextLayer.filter = combineFilters(nextLayer.filter, ["in", "class", "motorway", "trunk"]);
1959
1989
  }
1960
- if ((group === "minor" || group === "paths") && roads2.detail === "all") {
1961
- const currentMinZoom = numberValue(nextLayer.minzoom);
1962
- if (currentMinZoom !== void 0) {
1963
- nextLayer.minzoom = Math.max(0, currentMinZoom - 1);
1990
+ if (group === "streets" && id.includes("minor")) {
1991
+ nextLayer.minzoom = Math.max(numberValue(nextLayer.minzoom) ?? 0, 12);
1992
+ if (roads2.detail === "streets") {
1993
+ nextLayer.filter = combineFilters(nextLayer.filter, ["==", "class", "minor"]);
1964
1994
  }
1965
1995
  }
1996
+ if (group === "service") {
1997
+ nextLayer.minzoom = Math.max(numberValue(nextLayer.minzoom) ?? 0, 14);
1998
+ }
1966
1999
  return nextLayer;
1967
2000
  }
1968
2001
  function isRoadLayerVisible(group, roads2) {
@@ -1970,17 +2003,24 @@ function isRoadLayerVisible(group, roads2) {
1970
2003
  if (group === "paths") return roads2.extras.paths;
1971
2004
  if (group === "rail") return roads2.extras.rail;
1972
2005
  if (group === "ferry") return roads2.extras.ferry;
1973
- if (group === "minor") return roads2.detail === "streets" || roads2.detail === "all";
2006
+ if (group === "service") return roads2.detail === "all";
2007
+ if (group === "streets") return roads2.detail === "streets" || roads2.detail === "all";
2008
+ if (group === "major") return roads2.detail !== "highways";
1974
2009
  return true;
1975
2010
  }
1976
2011
  function osmBrightRoadGroup(id) {
1977
2012
  if (id === "ferry") return "ferry";
1978
2013
  if (id.includes("railway") || id.includes("cablecar")) return "rail";
1979
2014
  if (id.includes("path") || id.includes("steps")) return "paths";
1980
- if (id.includes("motorway") || id.includes("trunk") || id.includes("primary") || id.includes("secondary") || id.includes("tertiary") || id.includes("link")) {
2015
+ if (id.includes("service") || id.includes("track")) return "service";
2016
+ if (id.includes("motorway") || id.includes("trunk")) return "highways";
2017
+ if (id.includes("primary") || id.includes("secondary") || id.includes("tertiary") || id.includes("link")) {
1981
2018
  return "major";
1982
2019
  }
1983
- return "minor";
2020
+ return "streets";
2021
+ }
2022
+ function isSharedMajorLinkLayer(id) {
2023
+ return id.includes("-link") && !id.includes("motorway-link");
1984
2024
  }
1985
2025
  function scaleStyleValue(value, scale, fallback) {
1986
2026
  if (typeof value === "number" && Number.isFinite(value)) {
@@ -5059,6 +5099,7 @@ function createStyle(input = {}, options = {}) {
5059
5099
  roadModule,
5060
5100
  theme.preset.roads ?? defaultConfig.roads
5061
5101
  );
5102
+ const { mode: resolvedRoadMode, ...resolvedRoadModule } = resolvedRoads;
5062
5103
  const resolvedLabels = resolveLabels(
5063
5104
  config.labels,
5064
5105
  labelsModule,
@@ -5095,8 +5136,8 @@ function createStyle(input = {}, options = {}) {
5095
5136
  "tileflow:theme": theme.name,
5096
5137
  "tileflow:themeMode": theme.mode,
5097
5138
  "tileflow:density": density,
5098
- "tileflow:roads": resolvedRoads.mode,
5099
- "tileflow:roadsModule": roadModule ? resolvedRoads : void 0,
5139
+ "tileflow:roads": resolvedRoadMode,
5140
+ "tileflow:roadsModule": roadModule ? resolvedRoadModule : void 0,
5100
5141
  "tileflow:labels": labels2,
5101
5142
  "tileflow:labelsModule": labelsModule,
5102
5143
  "tileflow:poi": poi2,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tileflow/core",
3
- "version": "0.1.0-alpha.10",
3
+ "version": "0.1.0-alpha.12",
4
4
  "description": "Typed Tileflow config and MapLibre style compiler.",
5
5
  "type": "module",
6
6
  "sideEffects": false,