@tileflow/core 0.1.0-alpha.11 → 0.1.0-alpha.13

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/dist/index.js CHANGED
@@ -513,40 +513,75 @@ function rgbToHex(r, g, b) {
513
513
  }
514
514
 
515
515
  // src/modules/labels/index.ts
516
+ var moduleLabelDefaults = {
517
+ language: "auto",
518
+ places: "major",
519
+ roads: "major",
520
+ water: "major"
521
+ };
522
+ var majorPlaceClasses = ["continent", "country", "state", "city", "town"];
523
+ var highwayRoadClasses = ["motorway", "trunk"];
524
+ var arterialRoadClasses = ["primary", "secondary", "tertiary"];
525
+ var majorRoadClasses = [...highwayRoadClasses, ...arterialRoadClasses];
526
+ var majorRoadClassSet = new Set(majorRoadClasses);
527
+ var streetRoadClasses = ["minor"];
528
+ var serviceRoadClasses = ["service", "track"];
529
+ var majorWaterClasses = ["ocean", "sea", "lake"];
530
+ var majorWaterwayClasses = ["river"];
516
531
  function labels(options = {}) {
517
532
  return {
518
533
  type: "labels",
519
534
  ...options.language ? { language: options.language } : {},
520
535
  ...options.places ? { places: options.places } : {},
521
- ...options.preset ? { preset: options.preset } : {},
522
536
  ...options.roads ? { roads: options.roads } : {},
523
537
  ...options.water ? { water: options.water } : {}
524
538
  };
525
539
  }
526
540
  function resolveLabels(legacyLabels, moduleConfig, fallback) {
527
- const mode = moduleConfig?.preset ?? legacyLabels ?? fallback;
528
- const density = labelDensityFromMode(mode);
541
+ const fallbackMode = legacyLabels ?? fallback;
542
+ const defaults = moduleConfig ? moduleLabelDefaults : labelDefaultsFromLegacyMode(fallbackMode);
543
+ const resolved = {
544
+ language: moduleConfig?.language ?? defaults.language,
545
+ places: moduleConfig?.places ?? defaults.places,
546
+ roads: moduleConfig?.roads ?? defaults.roads,
547
+ water: moduleConfig?.water ?? defaults.water
548
+ };
529
549
  return {
530
- language: moduleConfig?.language ?? "auto",
531
- mode,
532
- places: moduleConfig?.places ?? density,
533
- roads: moduleConfig?.roads ?? density,
534
- water: moduleConfig?.water ?? density
550
+ ...resolved,
551
+ mode: moduleConfig ? legacyModeFromResolvedLabels(resolved) : fallbackMode
535
552
  };
536
553
  }
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;
554
+ function labelLayers(source, sourceLayers, colors, labelsConfig, roadsConfig, typography) {
555
+ return [
556
+ ...placeLabelLayers(source, sourceLayers.place, colors, labelsConfig, typography),
557
+ ...roadLabelLayers(
558
+ source,
559
+ sourceLayers.roadName,
560
+ colors,
561
+ labelsConfig,
562
+ roadsConfig,
563
+ typography
564
+ ),
565
+ ...waterLabelLayers(
566
+ source,
567
+ sourceLayers.waterName,
568
+ sourceLayers.waterway,
569
+ colors,
570
+ labelsConfig,
571
+ typography
572
+ )
573
+ ];
574
+ }
575
+ function placeLabelLayers(source, sourceLayer, colors, labelsConfig, typography) {
576
+ if (labelsConfig.places === "none") return [];
542
577
  return [
543
578
  {
544
579
  id: "place-labels",
545
580
  type: "symbol",
546
581
  source,
547
582
  "source-layer": sourceLayer,
548
- minzoom: 3,
549
- filter: ["<=", ["coalesce", ["get", "rank"], 10], maxRank],
583
+ minzoom: 0,
584
+ filter: labelsConfig.places === "major" ? ["all", ["has", "name"], classFilter(majorPlaceClasses)] : ["has", "name"],
550
585
  layout: {
551
586
  "text-field": labelTextField(labelsConfig.language),
552
587
  "text-font": textFont(typography),
@@ -562,12 +597,160 @@ function labelLayers(source, sourceLayer, colors, labelsConfig, typography) {
562
597
  }
563
598
  ];
564
599
  }
565
- function labelDensityFromMode(mode) {
566
- if (mode === "none") return "none";
567
- if (mode === "essential") return "minimal";
568
- if (mode === "full") return "detailed";
600
+ function roadLabelLayers(source, sourceLayer, colors, labelsConfig, roadsConfig, typography) {
601
+ const classes = visibleRoadLabelClasses(labelsConfig.roads, roadsConfig);
602
+ const majorClasses = classes.filter((roadClass) => majorRoadClassSet.has(roadClass));
603
+ const minorClasses = classes.filter((roadClass) => !majorRoadClassSet.has(roadClass));
604
+ if (classes.length === 0) return [];
605
+ const layers = [];
606
+ const sharedLayout = {
607
+ "symbol-placement": "line",
608
+ "text-field": labelTextField(labelsConfig.language),
609
+ "text-font": textFont(typography),
610
+ "text-rotation-alignment": "map",
611
+ "text-size": ["interpolate", ["linear"], ["zoom"], 11, 11, 14, 13, 17, 15]
612
+ };
613
+ const sharedPaint = {
614
+ "text-color": colors.labels.road,
615
+ "text-halo-color": colors.labels.halo,
616
+ "text-halo-width": 1
617
+ };
618
+ if (majorClasses.length > 0) {
619
+ const namedMajorClasses = majorClasses.filter((roadClass) => roadClass !== "motorway");
620
+ layers.push({
621
+ id: "road-shields",
622
+ type: "symbol",
623
+ source,
624
+ "source-layer": sourceLayer,
625
+ minzoom: 8,
626
+ filter: ["all", ["has", "ref"], classFilter(majorClasses)],
627
+ layout: {
628
+ "symbol-placement": "line",
629
+ "symbol-spacing": 240,
630
+ "text-field": ["get", "ref"],
631
+ "text-font": textFont(typography),
632
+ "text-rotation-alignment": "viewport",
633
+ "text-size": 10
634
+ },
635
+ paint: sharedPaint
636
+ });
637
+ if (namedMajorClasses.length > 0) {
638
+ layers.unshift({
639
+ id: "road-labels-major",
640
+ type: "symbol",
641
+ source,
642
+ "source-layer": sourceLayer,
643
+ minzoom: 10,
644
+ filter: ["all", ["has", "name"], classFilter(namedMajorClasses)],
645
+ layout: sharedLayout,
646
+ paint: sharedPaint
647
+ });
648
+ }
649
+ }
650
+ if (minorClasses.length > 0) {
651
+ layers.push({
652
+ id: "road-labels-minor",
653
+ type: "symbol",
654
+ source,
655
+ "source-layer": sourceLayer,
656
+ minzoom: 14,
657
+ filter: ["all", ["has", "name"], classFilter(minorClasses)],
658
+ layout: sharedLayout,
659
+ paint: sharedPaint
660
+ });
661
+ }
662
+ return layers;
663
+ }
664
+ function waterLabelLayers(source, waterNameSourceLayer, waterwaySourceLayer, colors, labelsConfig, typography) {
665
+ if (labelsConfig.water === "none") return [];
666
+ const waterFilter = labelsConfig.water === "major" ? ["all", ["has", "name"], classFilter(majorWaterClasses)] : ["has", "name"];
667
+ const waterwayFilter = labelsConfig.water === "major" ? ["all", ["has", "name"], classFilter(majorWaterwayClasses)] : ["has", "name"];
668
+ const paint = {
669
+ "text-color": colors.labels.water,
670
+ "text-halo-color": colors.labels.halo,
671
+ "text-halo-width": 1
672
+ };
673
+ return [
674
+ {
675
+ id: "water-labels",
676
+ type: "symbol",
677
+ source,
678
+ "source-layer": waterNameSourceLayer,
679
+ minzoom: 3,
680
+ filter: ["all", ["==", ["geometry-type"], "Point"], waterFilter],
681
+ layout: {
682
+ "text-field": labelTextField(labelsConfig.language),
683
+ "text-font": textFont(typography),
684
+ "text-size": ["interpolate", ["linear"], ["zoom"], 3, 11, 8, 14]
685
+ },
686
+ paint
687
+ },
688
+ {
689
+ id: "water-line-labels",
690
+ type: "symbol",
691
+ source,
692
+ "source-layer": waterNameSourceLayer,
693
+ minzoom: 5,
694
+ filter: ["all", ["==", ["geometry-type"], "LineString"], waterFilter],
695
+ layout: {
696
+ "symbol-placement": "line",
697
+ "text-field": labelTextField(labelsConfig.language),
698
+ "text-font": textFont(typography),
699
+ "text-size": 13
700
+ },
701
+ paint
702
+ },
703
+ {
704
+ id: "waterway-labels",
705
+ type: "symbol",
706
+ source,
707
+ "source-layer": waterwaySourceLayer,
708
+ minzoom: 10,
709
+ filter: ["all", ["==", ["geometry-type"], "LineString"], waterwayFilter],
710
+ layout: {
711
+ "symbol-placement": "line",
712
+ "symbol-spacing": 320,
713
+ "text-field": labelTextField(labelsConfig.language),
714
+ "text-font": textFont(typography),
715
+ "text-size": 12
716
+ },
717
+ paint
718
+ }
719
+ ];
720
+ }
721
+ function visibleRoadLabelClasses(detail, roadsConfig) {
722
+ if (detail === "none" || roadsConfig.detail === "none") return [];
723
+ const visibleRoadClasses = roadsConfig.detail === "highways" ? [...highwayRoadClasses] : [
724
+ ...majorRoadClasses,
725
+ ...roadsConfig.detail === "streets" || roadsConfig.detail === "all" ? streetRoadClasses : [],
726
+ ...roadsConfig.detail === "all" ? serviceRoadClasses : [],
727
+ ...roadsConfig.extras.paths ? ["path"] : []
728
+ ];
729
+ const requestedClasses = detail === "major" ? [...majorRoadClasses] : [...majorRoadClasses, ...streetRoadClasses, ...serviceRoadClasses, "path"];
730
+ const visible = new Set(visibleRoadClasses);
731
+ return requestedClasses.filter((roadClass) => visible.has(roadClass));
732
+ }
733
+ function labelDefaultsFromLegacyMode(mode) {
734
+ if (mode === "none") {
735
+ return { language: "auto", places: "none", roads: "none", water: "none" };
736
+ }
737
+ if (mode === "essential") {
738
+ return { language: "auto", places: "major", roads: "major", water: "major" };
739
+ }
740
+ return { language: "auto", places: "all", roads: "all", water: "all" };
741
+ }
742
+ function legacyModeFromResolvedLabels(options) {
743
+ if (options.places === "none" && options.roads === "none" && options.water === "none") {
744
+ return "none";
745
+ }
746
+ if (options.places === "major" && options.roads === "major" && options.water === "major") {
747
+ return "essential";
748
+ }
569
749
  return "balanced";
570
750
  }
751
+ function classFilter(classes) {
752
+ return ["match", ["get", "class"], classes, true, false];
753
+ }
571
754
  function labelTextField(language) {
572
755
  if (language === "local") {
573
756
  return ["coalesce", ["get", "name"], ["get", "name:latin"], ["get", "name:en"]];
@@ -752,66 +935,40 @@ var semanticToOsmBrightIconName = {
752
935
  };
753
936
 
754
937
  // 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
- }
938
+ var moduleRoadDefaults = {
939
+ detail: "streets",
940
+ extras: { ferry: false, paths: false, rail: false },
941
+ hierarchy: "clear",
942
+ outline: "subtle",
943
+ weight: "regular"
784
944
  };
785
- var majorRoadClasses = ["motorway", "trunk", "primary", "secondary", "tertiary"];
786
- var minorRoadClasses = ["minor", "service", "track"];
945
+ var highwayRoadClasses2 = ["motorway", "trunk"];
946
+ var arterialRoadClasses2 = ["primary", "secondary", "tertiary"];
947
+ var majorRoadClasses2 = [...highwayRoadClasses2, ...arterialRoadClasses2];
948
+ var streetRoadClasses2 = ["minor"];
949
+ var serviceRoadClasses2 = ["service", "track"];
787
950
  function roads(options = {}) {
788
951
  return {
789
952
  type: "roads",
790
- preset: options.preset ?? "standard",
791
- ...options.casing ? { casing: options.casing } : {},
792
953
  ...options.detail ? { detail: options.detail } : {},
793
- ...options.density ? { density: options.density } : {},
794
954
  ...options.extras ? { extras: { ...options.extras } } : {},
795
955
  ...options.hierarchy ? { hierarchy: options.hierarchy } : {},
956
+ ...options.outline ? { outline: options.outline } : {},
796
957
  ...options.weight ? { weight: options.weight } : {}
797
958
  };
798
959
  }
799
960
  function resolveRoads(legacyRoads, moduleConfig, fallback) {
800
961
  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);
962
+ const defaults = moduleConfig ? moduleRoadDefaults : roadDefaultsFromLegacyMode(fallbackMode);
805
963
  const resolved = {
806
- casing: moduleConfig?.casing ?? defaults.casing,
807
- detail,
964
+ detail: moduleConfig?.detail ?? defaults.detail,
808
965
  extras: {
809
966
  ferry: moduleConfig?.extras?.ferry ?? defaults.extras.ferry,
810
967
  paths: moduleConfig?.extras?.paths ?? defaults.extras.paths,
811
968
  rail: moduleConfig?.extras?.rail ?? defaults.extras.rail
812
969
  },
813
- hierarchy,
814
- preset,
970
+ hierarchy: moduleConfig?.hierarchy ?? defaults.hierarchy,
971
+ outline: moduleConfig?.outline ?? defaults.outline,
815
972
  weight: moduleConfig?.weight ?? defaults.weight
816
973
  };
817
974
  return {
@@ -822,22 +979,22 @@ function resolveRoads(legacyRoads, moduleConfig, fallback) {
822
979
  function roadStyleMetrics(options) {
823
980
  const hierarchy = {
824
981
  subtle: {
825
- majorOpacity: 0.72,
826
- majorWidthScale: 0.95,
827
- minorOpacity: 0.6336,
828
- minorWidthScale: 0.88
982
+ majorOpacity: 0.92,
983
+ majorWidthScale: 1,
984
+ minorOpacity: 0.82,
985
+ minorWidthScale: 0.94
829
986
  },
830
987
  clear: {
831
- majorOpacity: 0.84,
988
+ majorOpacity: 0.92,
832
989
  majorWidthScale: 1,
833
- minorOpacity: 0.7392,
834
- minorWidthScale: 0.9
990
+ minorOpacity: 0.68,
991
+ minorWidthScale: 0.84
835
992
  },
836
993
  strong: {
837
- majorOpacity: 0.98,
838
- majorWidthScale: 1.16,
839
- minorOpacity: 0.56,
840
- minorWidthScale: 0.72
994
+ majorOpacity: 0.92,
995
+ majorWidthScale: 1,
996
+ minorOpacity: 0.52,
997
+ minorWidthScale: 0.7
841
998
  }
842
999
  }[options.hierarchy];
843
1000
  const weightScale = {
@@ -845,28 +1002,30 @@ function roadStyleMetrics(options) {
845
1002
  regular: 1,
846
1003
  bold: 1.24
847
1004
  }[options.weight];
848
- const casingOpacity = {
1005
+ const outlineOpacity = {
849
1006
  none: 0,
850
- subtle: 0.16,
851
- strong: 0.3
852
- }[options.casing];
1007
+ subtle: 0.18,
1008
+ strong: 0.34
1009
+ }[options.outline];
853
1010
  return {
854
- casingOpacity,
855
1011
  ...hierarchy,
1012
+ outlineOpacity,
856
1013
  weightScale
857
1014
  };
858
1015
  }
859
- function roadMinorMinZoom(detail) {
860
- return detail === "all" ? 6 : 12;
861
- }
862
1016
  function roadLayers(source, sourceLayer, colors, options) {
863
1017
  if (options.detail === "none") {
864
1018
  return [];
865
1019
  }
866
1020
  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];
1021
+ const selectedMajorRoadClasses = options.detail === "highways" ? [...highwayRoadClasses2] : [...majorRoadClasses2];
1022
+ const showsStreetRoads = options.detail === "streets" || options.detail === "all";
1023
+ const showsServiceRoads = options.detail === "all";
1024
+ const roadClasses = [
1025
+ ...selectedMajorRoadClasses,
1026
+ ...showsStreetRoads ? streetRoadClasses2 : [],
1027
+ ...showsServiceRoads ? serviceRoadClasses2 : []
1028
+ ];
870
1029
  const transportClasses = [
871
1030
  ...roadClasses,
872
1031
  ...options.extras.paths ? ["path"] : [],
@@ -875,12 +1034,13 @@ function roadLayers(source, sourceLayer, colors, options) {
875
1034
  ];
876
1035
  const roadClassColor = roadClassColorExpression(colors);
877
1036
  const nonTunnelRoadFilter = ["!=", ["get", "brunnel"], "tunnel"];
878
- const majorFilter = classFilter(majorRoadClasses);
879
- const minorFilter = classFilter(minorRoadClasses);
880
- const roadFilter = classFilter(roadClasses);
881
- const transportFilter = classFilter(transportClasses);
1037
+ const majorFilter = classFilter2(selectedMajorRoadClasses);
1038
+ const streetFilter = classFilter2(streetRoadClasses2);
1039
+ const serviceFilter = classFilter2(serviceRoadClasses2);
1040
+ const roadFilter = classFilter2(roadClasses);
1041
+ const transportFilter = classFilter2(transportClasses);
882
1042
  const layers = [];
883
- if (options.casing !== "none") {
1043
+ if (options.outline !== "none") {
884
1044
  layers.push({
885
1045
  id: "roads-tunnels-casing",
886
1046
  type: "line",
@@ -891,14 +1051,17 @@ function roadLayers(source, sourceLayer, colors, options) {
891
1051
  paint: {
892
1052
  "line-color": colors.roads.tunnel,
893
1053
  "line-dasharray": [0.6, 0.8],
894
- "line-opacity": metrics.casingOpacity * 0.85,
895
- "line-width": zoomWidth(
1054
+ "line-opacity": roadOpacityExpression(
1055
+ metrics.outlineOpacity * 0.85,
1056
+ metrics.outlineOpacity * 0.85
1057
+ ),
1058
+ "line-width": roadWidthExpression(
896
1059
  [
897
- [10, 0.5],
898
- [14, 1.7],
899
- [16, 3.8]
1060
+ [10, 0.8],
1061
+ [14, 2.2],
1062
+ [16, 4.8]
900
1063
  ],
901
- metrics.weightScale
1064
+ metrics
902
1065
  )
903
1066
  }
904
1067
  });
@@ -913,18 +1076,21 @@ function roadLayers(source, sourceLayer, colors, options) {
913
1076
  paint: {
914
1077
  "line-color": roadClassColor,
915
1078
  "line-dasharray": [0.6, 0.8],
916
- "line-opacity": metrics.majorOpacity * 0.28,
917
- "line-width": zoomWidth(
1079
+ "line-opacity": roadOpacityExpression(
1080
+ metrics.majorOpacity * 0.28,
1081
+ metrics.minorOpacity * 0.28
1082
+ ),
1083
+ "line-width": roadWidthExpression(
918
1084
  [
919
1085
  [10, 0.25],
920
1086
  [14, 1],
921
1087
  [16, 2.4]
922
1088
  ],
923
- metrics.weightScale
1089
+ metrics
924
1090
  )
925
1091
  }
926
1092
  });
927
- if (options.casing !== "none") {
1093
+ if (options.outline !== "none") {
928
1094
  layers.push({
929
1095
  id: "roads-casing",
930
1096
  type: "line",
@@ -934,14 +1100,14 @@ function roadLayers(source, sourceLayer, colors, options) {
934
1100
  filter: ["all", nonTunnelRoadFilter, roadFilter],
935
1101
  paint: {
936
1102
  "line-color": colors.roads.casing,
937
- "line-opacity": roadCasingOpacityExpression(metrics.casingOpacity, minorMinZoom),
938
- "line-width": zoomWidth(
1103
+ "line-opacity": roadOpacityExpression(metrics.outlineOpacity, metrics.outlineOpacity),
1104
+ "line-width": roadWidthExpression(
939
1105
  [
940
- [6, 0.4],
941
- [12, 1.2],
942
- [16, 4]
1106
+ [6, 0.7],
1107
+ [12, 1.8],
1108
+ [16, 5.4]
943
1109
  ],
944
- metrics.weightScale
1110
+ metrics
945
1111
  )
946
1112
  }
947
1113
  });
@@ -953,7 +1119,7 @@ function roadLayers(source, sourceLayer, colors, options) {
953
1119
  source,
954
1120
  "source-layer": sourceLayer,
955
1121
  minzoom: 5,
956
- filter: ["all", nonTunnelRoadFilter, classFilter(["ferry"])],
1122
+ filter: ["all", nonTunnelRoadFilter, classFilter2(["ferry"])],
957
1123
  paint: {
958
1124
  "line-color": colors.roads.ferry,
959
1125
  "line-dasharray": [1.2, 1.1],
@@ -975,8 +1141,8 @@ function roadLayers(source, sourceLayer, colors, options) {
975
1141
  type: "line",
976
1142
  source,
977
1143
  "source-layer": sourceLayer,
978
- minzoom: options.detail === "all" ? 8 : 10,
979
- filter: ["all", nonTunnelRoadFilter, classFilter(["path"])],
1144
+ minzoom: 10,
1145
+ filter: ["all", nonTunnelRoadFilter, classFilter2(["path"])],
980
1146
  paint: {
981
1147
  "line-color": colors.roads.path,
982
1148
  "line-dasharray": [0.7, 0.7],
@@ -998,8 +1164,8 @@ function roadLayers(source, sourceLayer, colors, options) {
998
1164
  type: "line",
999
1165
  source,
1000
1166
  "source-layer": sourceLayer,
1001
- minzoom: options.detail === "all" ? 6 : 7,
1002
- filter: ["all", nonTunnelRoadFilter, classFilter(["rail", "transit"])],
1167
+ minzoom: 7,
1168
+ filter: ["all", nonTunnelRoadFilter, classFilter2(["rail", "transit"])],
1003
1169
  paint: {
1004
1170
  "line-color": colors.roads.rail,
1005
1171
  "line-opacity": metrics.minorOpacity * 0.65,
@@ -1014,20 +1180,40 @@ function roadLayers(source, sourceLayer, colors, options) {
1014
1180
  }
1015
1181
  });
1016
1182
  }
1017
- if (showsMinorRoads) {
1183
+ if (showsServiceRoads) {
1184
+ layers.push({
1185
+ id: "roads-service",
1186
+ type: "line",
1187
+ source,
1188
+ "source-layer": sourceLayer,
1189
+ minzoom: 14,
1190
+ filter: ["all", nonTunnelRoadFilter, serviceFilter],
1191
+ paint: {
1192
+ "line-color": roadClassColor,
1193
+ "line-opacity": metrics.minorOpacity,
1194
+ "line-width": zoomWidth(
1195
+ [
1196
+ [14, 0.6],
1197
+ [16, 2.2]
1198
+ ],
1199
+ metrics.weightScale * metrics.minorWidthScale
1200
+ )
1201
+ }
1202
+ });
1203
+ }
1204
+ if (showsStreetRoads) {
1018
1205
  layers.push({
1019
1206
  id: "roads-minor",
1020
1207
  type: "line",
1021
1208
  source,
1022
1209
  "source-layer": sourceLayer,
1023
- minzoom: minorMinZoom,
1024
- filter: ["all", nonTunnelRoadFilter, minorFilter],
1210
+ minzoom: 12,
1211
+ filter: ["all", nonTunnelRoadFilter, streetFilter],
1025
1212
  paint: {
1026
- "line-color": colors.roads.minor,
1213
+ "line-color": roadClassColor,
1027
1214
  "line-opacity": metrics.minorOpacity,
1028
1215
  "line-width": zoomWidth(
1029
1216
  [
1030
- [6, 0.2],
1031
1217
  [12, 0.8],
1032
1218
  [16, 3]
1033
1219
  ],
@@ -1066,33 +1252,55 @@ function roadLayers(source, sourceLayer, colors, options) {
1066
1252
  filter: ["all", ["==", ["get", "brunnel"], "bridge"], transportFilter],
1067
1253
  paint: {
1068
1254
  "line-color": colors.roads.bridge,
1069
- "line-opacity": metrics.majorOpacity,
1070
- "line-width": zoomWidth(
1255
+ "line-opacity": roadOpacityExpression(metrics.majorOpacity, metrics.minorOpacity),
1256
+ "line-width": roadWidthExpression(
1071
1257
  [
1072
1258
  [10, 0.45],
1073
1259
  [14, 1.6],
1074
1260
  [16, 4.4]
1075
1261
  ],
1076
- metrics.weightScale * metrics.majorWidthScale
1262
+ metrics
1077
1263
  )
1078
1264
  }
1079
1265
  }
1080
1266
  );
1081
1267
  return layers;
1082
1268
  }
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;
1269
+ function roadDefaultsFromLegacyMode(mode) {
1270
+ if (mode === "hidden") {
1271
+ return {
1272
+ detail: "none",
1273
+ extras: { ferry: false, paths: false, rail: false },
1274
+ hierarchy: "clear",
1275
+ outline: "subtle",
1276
+ weight: "regular"
1277
+ };
1278
+ }
1279
+ if (mode === "soft") {
1280
+ return {
1281
+ detail: "streets",
1282
+ extras: { ferry: false, paths: false, rail: false },
1283
+ hierarchy: "subtle",
1284
+ outline: "subtle",
1285
+ weight: "thin"
1286
+ };
1287
+ }
1288
+ if (mode === "detailed") {
1289
+ return {
1290
+ detail: "all",
1291
+ extras: { ferry: true, paths: true, rail: true },
1292
+ hierarchy: "clear",
1293
+ outline: "strong",
1294
+ weight: "regular"
1295
+ };
1296
+ }
1297
+ return {
1298
+ detail: "streets",
1299
+ extras: { ferry: true, paths: true, rail: true },
1300
+ hierarchy: "clear",
1301
+ outline: "subtle",
1302
+ weight: "regular"
1303
+ };
1096
1304
  }
1097
1305
  function legacyModeFromResolvedRoads(options) {
1098
1306
  if (options.detail === "none") return "hidden";
@@ -1100,18 +1308,27 @@ function legacyModeFromResolvedRoads(options) {
1100
1308
  if (options.hierarchy === "strong" || options.detail === "all") return "detailed";
1101
1309
  return "standard";
1102
1310
  }
1103
- function classFilter(classes) {
1311
+ function classFilter2(classes) {
1104
1312
  return ["match", ["get", "class"], classes, true, false];
1105
1313
  }
1106
- function roadCasingOpacityExpression(opacity, minorMinZoom) {
1314
+ function roadOpacityExpression(majorOpacity, minorOpacity) {
1107
1315
  return [
1108
- "interpolate",
1109
- ["linear"],
1110
- ["zoom"],
1111
- minorMinZoom - 0.5,
1112
- ["case", classFilter(majorRoadClasses), opacity, 0],
1113
- minorMinZoom,
1114
- opacity
1316
+ "case",
1317
+ classFilter2(majorRoadClasses2),
1318
+ majorOpacity,
1319
+ classFilter2(streetRoadClasses2),
1320
+ ["step", ["zoom"], 0, 12, minorOpacity],
1321
+ classFilter2(serviceRoadClasses2),
1322
+ ["step", ["zoom"], 0, 14, minorOpacity],
1323
+ minorOpacity
1324
+ ];
1325
+ }
1326
+ function roadWidthExpression(stops, metrics) {
1327
+ return [
1328
+ "case",
1329
+ classFilter2(majorRoadClasses2),
1330
+ zoomWidth(stops, metrics.weightScale * metrics.majorWidthScale),
1331
+ zoomWidth(stops, metrics.weightScale * metrics.minorWidthScale)
1115
1332
  ];
1116
1333
  }
1117
1334
  function zoomWidth(stops, scale) {
@@ -1170,7 +1387,7 @@ function openMapTilesLayers(source, sourceLayers, colors, options) {
1170
1387
  ...buildingLayers(source, sourceLayers.building, colors, options.density, options.buildings),
1171
1388
  ...roadLayers(source, sourceLayers.road, colors, options.roads),
1172
1389
  ...boundaryLayers(source, sourceLayers.boundary, colors),
1173
- ...labelLayers(source, sourceLayers.place, colors, options.labels, options.typography),
1390
+ ...labelLayers(source, sourceLayers, colors, options.labels, options.roads, options.typography),
1174
1391
  ...poiLayers(
1175
1392
  source,
1176
1393
  sourceLayers.poi,
@@ -1534,7 +1751,10 @@ var sourceLayersSchema = z.object({
1534
1751
  landuse: z.string().optional(),
1535
1752
  park: z.string().optional(),
1536
1753
  water: z.string().optional(),
1754
+ waterName: z.string().optional(),
1755
+ waterway: z.string().optional(),
1537
1756
  road: z.string().optional(),
1757
+ roadName: z.string().optional(),
1538
1758
  building: z.string().optional(),
1539
1759
  boundary: z.string().optional(),
1540
1760
  place: z.string().optional(),
@@ -1552,26 +1772,23 @@ var basemapSchema = z.object({
1552
1772
  }).strict();
1553
1773
  var roadsModuleSchema = z.object({
1554
1774
  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(),
1775
+ detail: z.enum(["none", "highways", "major", "streets", "all"]).optional(),
1558
1776
  extras: z.object({
1559
1777
  ferry: z.boolean().optional(),
1560
1778
  paths: z.boolean().optional(),
1561
1779
  rail: z.boolean().optional()
1562
1780
  }).strict().optional(),
1563
1781
  hierarchy: z.enum(["subtle", "clear", "strong"]).optional(),
1564
- preset: z.enum(["minimal", "standard", "navigation", "detailed", "modern"]).optional(),
1782
+ outline: z.enum(["none", "subtle", "strong"]).optional(),
1565
1783
  weight: z.enum(["thin", "regular", "bold"]).optional()
1566
1784
  }).strict();
1567
- var labelDensitySchema = z.enum(["none", "minimal", "balanced", "detailed"]);
1785
+ var labelDetailSchema = z.enum(["none", "major", "all"]);
1568
1786
  var labelsModuleSchema = z.object({
1569
1787
  type: z.literal("labels"),
1570
1788
  language: z.string().trim().min(1).optional(),
1571
- places: labelDensitySchema.optional(),
1572
- preset: z.enum(["none", "essential", "balanced", "full"]).optional(),
1573
- roads: labelDensitySchema.optional(),
1574
- water: labelDensitySchema.optional()
1789
+ places: labelDetailSchema.optional(),
1790
+ roads: labelDetailSchema.optional(),
1791
+ water: labelDetailSchema.optional()
1575
1792
  }).strict();
1576
1793
  var poiModuleSchema = z.object({
1577
1794
  type: z.literal("poi"),
@@ -1924,7 +2141,7 @@ function applyOsmBrightControls(layers, options) {
1924
2141
  );
1925
2142
  }
1926
2143
  if (isLabelLayer(id, sourceLayer)) {
1927
- return applyLabelLayerControls(layer, options.labels);
2144
+ return applyLabelLayerControls(layer, options.labels, options.roads);
1928
2145
  }
1929
2146
  return layer;
1930
2147
  });
@@ -1934,14 +2151,15 @@ function applyRoadLayerControls(layer, roads2) {
1934
2151
  const type = stringValue(layer.type);
1935
2152
  const group = osmBrightRoadGroup(id);
1936
2153
  const isCasing = id.includes("casing");
1937
- if (!isRoadLayerVisible(group, roads2) || isCasing && roads2.casing === "none") {
2154
+ const showsSharedHighwayLink = roads2.detail === "highways" && isSharedMajorLinkLayer(id);
2155
+ if (!isRoadLayerVisible(group, roads2) && !showsSharedHighwayLink || isCasing && roads2.outline === "none") {
1938
2156
  return setLayerVisibility(layer, "none");
1939
2157
  }
1940
2158
  const metrics = roadStyleMetrics(roads2);
1941
2159
  const nextLayer = setLayerVisibility(layer, "visible");
1942
2160
  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;
2161
+ const widthScale = metrics.weightScale * (group === "highways" || group === "major" ? metrics.majorWidthScale : metrics.minorWidthScale);
2162
+ 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
2163
  if (type === "line") {
1946
2164
  paint["line-opacity"] = scaleStyleValue(paint["line-opacity"], opacityScale, 1);
1947
2165
  paint["line-width"] = scaleStyleValue(paint["line-width"], widthScale, 1);
@@ -1951,18 +2169,18 @@ function applyRoadLayerControls(layer, roads2) {
1951
2169
  paint["fill-opacity"] = scaleStyleValue(paint["fill-opacity"], opacityScale, 1);
1952
2170
  nextLayer.paint = paint;
1953
2171
  }
1954
- if (group === "minor" && roads2.detail === "streets") {
1955
- nextLayer.minzoom = Math.max(
1956
- numberValue(nextLayer.minzoom) ?? 0,
1957
- roadMinorMinZoom(roads2.detail)
1958
- );
2172
+ if (roads2.detail === "highways" && (group === "highways" || showsSharedHighwayLink)) {
2173
+ nextLayer.filter = combineFilters(nextLayer.filter, ["in", "class", "motorway", "trunk"]);
1959
2174
  }
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);
2175
+ if (group === "streets" && id.includes("minor")) {
2176
+ nextLayer.minzoom = Math.max(numberValue(nextLayer.minzoom) ?? 0, 12);
2177
+ if (roads2.detail === "streets") {
2178
+ nextLayer.filter = combineFilters(nextLayer.filter, ["==", "class", "minor"]);
1964
2179
  }
1965
2180
  }
2181
+ if (group === "service") {
2182
+ nextLayer.minzoom = Math.max(numberValue(nextLayer.minzoom) ?? 0, 14);
2183
+ }
1966
2184
  return nextLayer;
1967
2185
  }
1968
2186
  function isRoadLayerVisible(group, roads2) {
@@ -1970,17 +2188,24 @@ function isRoadLayerVisible(group, roads2) {
1970
2188
  if (group === "paths") return roads2.extras.paths;
1971
2189
  if (group === "rail") return roads2.extras.rail;
1972
2190
  if (group === "ferry") return roads2.extras.ferry;
1973
- if (group === "minor") return roads2.detail === "streets" || roads2.detail === "all";
2191
+ if (group === "service") return roads2.detail === "all";
2192
+ if (group === "streets") return roads2.detail === "streets" || roads2.detail === "all";
2193
+ if (group === "major") return roads2.detail !== "highways";
1974
2194
  return true;
1975
2195
  }
1976
2196
  function osmBrightRoadGroup(id) {
1977
2197
  if (id === "ferry") return "ferry";
1978
2198
  if (id.includes("railway") || id.includes("cablecar")) return "rail";
1979
2199
  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")) {
2200
+ if (id.includes("service") || id.includes("track")) return "service";
2201
+ if (id.includes("motorway") || id.includes("trunk")) return "highways";
2202
+ if (id.includes("primary") || id.includes("secondary") || id.includes("tertiary") || id.includes("link")) {
1981
2203
  return "major";
1982
2204
  }
1983
- return "minor";
2205
+ return "streets";
2206
+ }
2207
+ function isSharedMajorLinkLayer(id) {
2208
+ return id.includes("-link") && !id.includes("motorway-link");
1984
2209
  }
1985
2210
  function scaleStyleValue(value, scale, fallback) {
1986
2211
  if (typeof value === "number" && Number.isFinite(value)) {
@@ -2003,12 +2228,12 @@ function scaleStyleValue(value, scale, fallback) {
2003
2228
  }
2004
2229
  return fallback * scale;
2005
2230
  }
2006
- function applyLabelLayerControls(layer, labels2) {
2231
+ function applyLabelLayerControls(layer, labels2, roads2) {
2007
2232
  const id = stringValue(layer.id);
2008
2233
  const sourceLayer = stringValue(layer["source-layer"]);
2009
2234
  const group = labelGroup(id, sourceLayer);
2010
- const density = labelGroupDensity(labels2, group);
2011
- if (labels2.mode === "none" || density === "none") {
2235
+ const detail = labelGroupDetail(labels2, group);
2236
+ if (detail === "none" || detail === "major" && isMinorLabelLayer(id, group)) {
2012
2237
  return setLayerVisibility(layer, "none");
2013
2238
  }
2014
2239
  const nextLayer = setLayerVisibility(layer, "visible");
@@ -2016,13 +2241,20 @@ function applyLabelLayerControls(layer, labels2) {
2016
2241
  if ("text-field" in layout && !isHighwayShieldLayer(id)) {
2017
2242
  layout["text-field"] = labelTextField(labels2.language);
2018
2243
  }
2019
- if (density === "minimal") {
2020
- nextLayer.minzoom = Math.max(numberValue(nextLayer.minzoom) ?? 0, minimalMinZoom(group));
2244
+ if (group === "roads" && roads2) {
2245
+ const allowedClasses = visibleRoadLabelClasses(detail, roads2).filter(
2246
+ (roadClass) => roadLabelLayerClasses(id).includes(roadClass)
2247
+ );
2248
+ if (allowedClasses.length === 0) {
2249
+ return setLayerVisibility(layer, "none");
2250
+ }
2251
+ nextLayer.filter = combineFilters(nextLayer.filter, ["in", "class", ...allowedClasses]);
2021
2252
  }
2022
- if (density === "detailed") {
2023
- const currentMinZoom = numberValue(nextLayer.minzoom);
2024
- if (currentMinZoom !== void 0) {
2025
- nextLayer.minzoom = Math.max(0, currentMinZoom - 1);
2253
+ if (group === "water" && detail === "major") {
2254
+ if (sourceLayer === "waterway") {
2255
+ nextLayer.filter = combineFilters(nextLayer.filter, ["==", "class", "river"]);
2256
+ } else if (id === "water-name-other") {
2257
+ nextLayer.filter = combineFilters(nextLayer.filter, ["in", "class", "sea", "lake"]);
2026
2258
  }
2027
2259
  }
2028
2260
  nextLayer.layout = layout;
@@ -2052,7 +2284,7 @@ function applyPoiLayerControls(layer, poi2, customIconSpriteId2, customIconMappi
2052
2284
  nextLayer.layout = layout;
2053
2285
  return nextLayer;
2054
2286
  }
2055
- function labelGroupDensity(labels2, group) {
2287
+ function labelGroupDetail(labels2, group) {
2056
2288
  if (group === "roads") return labels2.roads;
2057
2289
  if (group === "water") return labels2.water;
2058
2290
  return labels2.places;
@@ -2066,10 +2298,15 @@ function labelGroup(id, sourceLayer) {
2066
2298
  }
2067
2299
  return "places";
2068
2300
  }
2069
- function minimalMinZoom(group) {
2070
- if (group === "roads") return 13;
2071
- if (group === "water") return 5;
2072
- return 4;
2301
+ function isMinorLabelLayer(id, group) {
2302
+ if (group === "places") return id === "place-other" || id === "place-village";
2303
+ if (group === "roads") return id.includes("path") || id.includes("minor");
2304
+ return false;
2305
+ }
2306
+ function roadLabelLayerClasses(id) {
2307
+ if (id.includes("path")) return ["path"];
2308
+ if (id.includes("minor")) return ["minor", "service", "track"];
2309
+ return ["motorway", "trunk", "primary", "secondary", "tertiary"];
2073
2310
  }
2074
2311
  function isLabelLayer(id, sourceLayer) {
2075
2312
  return sourceLayer === "place" || sourceLayer === "transportation_name" || sourceLayer === "water_name" || sourceLayer === "waterway" || id.includes("highway-name") || id.includes("highway-shield") || id.includes("water-name");
@@ -5001,7 +5238,10 @@ var defaultSourceLayers = {
5001
5238
  landuse: "landuse",
5002
5239
  park: "park",
5003
5240
  water: "water",
5241
+ waterName: "water_name",
5242
+ waterway: "waterway",
5004
5243
  road: "transportation",
5244
+ roadName: "transportation_name",
5005
5245
  building: "building",
5006
5246
  boundary: "boundary",
5007
5247
  place: "place",
@@ -5059,13 +5299,15 @@ function createStyle(input = {}, options = {}) {
5059
5299
  roadModule,
5060
5300
  theme.preset.roads ?? defaultConfig.roads
5061
5301
  );
5302
+ const { mode: resolvedRoadMode, ...resolvedRoadModule } = resolvedRoads;
5062
5303
  const resolvedLabels = resolveLabels(
5063
5304
  config.labels,
5064
5305
  labelsModule,
5065
5306
  theme.preset.labels ?? defaultConfig.labels
5066
5307
  );
5308
+ const { mode: resolvedLabelsMode, ...resolvedLabelsModule } = resolvedLabels;
5067
5309
  const resolvedPoi = resolvePoi(config.poi, poiModule, theme.preset.poi ?? defaultConfig.poi);
5068
- const labels2 = resolvedLabels.mode;
5310
+ const labels2 = resolvedLabelsMode;
5069
5311
  const poi2 = resolvedPoi.mode;
5070
5312
  const buildings = config.buildings ?? "flat";
5071
5313
  const colors = resolveColors(config.colors, theme.colors, {}, theme.modules);
@@ -5095,10 +5337,10 @@ function createStyle(input = {}, options = {}) {
5095
5337
  "tileflow:theme": theme.name,
5096
5338
  "tileflow:themeMode": theme.mode,
5097
5339
  "tileflow:density": density,
5098
- "tileflow:roads": resolvedRoads.mode,
5099
- "tileflow:roadsModule": roadModule ? resolvedRoads : void 0,
5340
+ "tileflow:roads": resolvedRoadMode,
5341
+ "tileflow:roadsModule": roadModule ? resolvedRoadModule : void 0,
5100
5342
  "tileflow:labels": labels2,
5101
- "tileflow:labelsModule": labelsModule,
5343
+ "tileflow:labelsModule": labelsModule ? resolvedLabelsModule : void 0,
5102
5344
  "tileflow:poi": poi2,
5103
5345
  "tileflow:poiModule": poiModule,
5104
5346
  "tileflow:icons": iconSet ? {
@@ -5136,7 +5378,6 @@ function createStyle(input = {}, options = {}) {
5136
5378
  theme,
5137
5379
  tilesUrl,
5138
5380
  typography,
5139
- labels: labels2,
5140
5381
  labelsModule: resolvedLabels,
5141
5382
  poi: poi2,
5142
5383
  poiModule: resolvedPoi,