diff --git a/biome.json b/biome.json index 4bc75d2b..fae07516 100644 --- a/biome.json +++ b/biome.json @@ -3,6 +3,7 @@ "indentStyle": "space" }, "linter": { + "ignore": ["src/default_style/themes.ts"], "rules": { "style": { "useNamingConvention": {} diff --git a/examples/multi_language.html b/examples/multi_language.html deleted file mode 100644 index 92888a9d..00000000 --- a/examples/multi_language.html +++ /dev/null @@ -1,209 +0,0 @@ - - - - - - - - - - - - -
-
-
- - - - - - - - - multilingual demo of protomaps-leaflet -
-
- - - \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 7cee9489..cb8a73e5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@mapbox/unitbezier": "^0.0.0", "@mapbox/vector-tile": "^1.3.1", "@types/css-font-loading-module": "^0.0.7", + "color2k": "^2.0.3", "pbf": "^3.2.1", "pmtiles": "2.7.0", "potpack": "^1.0.2", @@ -298,6 +299,11 @@ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", "dev": true }, + "node_modules/color2k": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/color2k/-/color2k-2.0.3.tgz", + "integrity": "sha512-zW190nQTIoXcGCaU08DvVNFTmQhUpnJfVuAKfWqUQkflXKpaDdpaYoM0iluLS9lgJNHyBF58KKA2FBEwkD7wog==" + }, "node_modules/esbuild": { "version": "0.15.18", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz", @@ -943,6 +949,11 @@ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", "dev": true }, + "color2k": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/color2k/-/color2k-2.0.3.tgz", + "integrity": "sha512-zW190nQTIoXcGCaU08DvVNFTmQhUpnJfVuAKfWqUQkflXKpaDdpaYoM0iluLS9lgJNHyBF58KKA2FBEwkD7wog==" + }, "esbuild": { "version": "0.15.18", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz", diff --git a/package.json b/package.json index a22d433b..d762c15b 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "@mapbox/unitbezier": "^0.0.0", "@mapbox/vector-tile": "^1.3.1", "@types/css-font-loading-module": "^0.0.7", + "color2k": "^2.0.3", "pbf": "^3.2.1", "pmtiles": "2.7.0", "potpack": "^1.0.2", diff --git a/src/default_style/dark.ts b/src/default_style/dark.ts deleted file mode 100644 index 2565cd90..00000000 --- a/src/default_style/dark.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { DefaultStyleParams } from "./style"; - -export const dark: DefaultStyleParams = { - earth: "#151515", - glacier: "#1c1c1c", - residential: "#252B2F", - hospital: "#3E2C2C", - cemetery: "#36483D", - school: "#2C3440", - industrial: "#33312C", - wood: "#3A3E38", - grass: "#4E604D", - park: "#2C4034", - water: "#4D5B73", - sand: "#777777", - buildings: "#464545", - highwayCasing: "#000000", - majorRoadCasing: "#1C1B1B", - mediumRoadCasing: "#3E3E3E", - minorRoadCasing: "#000000", - highway: "#5B5B5B", - majorRoad: "#595959", - mediumRoad: "#4F4F4F", - minorRoad: "#393939", - boundaries: "#666666", - mask: "#dddddd", - countryLabel: "#ffffff", - cityLabel: "#FFFFFF", - stateLabel: "#ffffff", - neighbourhoodLabel: "#FDFDFD", - landuseLabel: "#DDDDDD", - waterLabel: "#707E95", - naturalLabel: "#4c4c4c", - roadsLabel: "#C4C4C4", - poisLabel: "#959393", -}; diff --git a/src/default_style/light.ts b/src/default_style/light.ts deleted file mode 100644 index b30f1d5b..00000000 --- a/src/default_style/light.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { DefaultStyleParams } from "./style"; - -export const light: DefaultStyleParams = { - earth: "#FFFBF6", - glacier: "#ffffff", - residential: "#F4F4F8", - hospital: "#FFF6F6", - cemetery: "#EFF2EE", - school: "#F7F6FF", - industrial: "#FFF9EF", - wood: "#F4F9EF", - grass: "#EBF9E3", - park: "#E5F9D5", - water: "#B7DFF2", - sand: "#ebebeb", - buildings: "#F2EDE8", - highwayCasing: "#FFC3C3", - majorRoadCasing: "#FFB9B9", - mediumRoadCasing: "#FFCE8E", - minorRoadCasing: "#cccccc", - highway: "#FFCEBB", - majorRoad: "#FFE4B3", - mediumRoad: "#FFF2C8", - minorRoad: "#ffffff", - boundaries: "#9e9e9e", - mask: "#dddddd", - countryLabel: "#aaaaaa", - cityLabel: "#6C6C6C", - stateLabel: "#999999", - neighbourhoodLabel: "#888888", - landuseLabel: "#898989", - waterLabel: "#41ABDC", - naturalLabel: "#4B8F14", - roadsLabel: "#888888", - poisLabel: "#606060", -}; diff --git a/src/default_style/style.ts b/src/default_style/style.ts index ab7b2407..7eea8122 100644 --- a/src/default_style/style.ts +++ b/src/default_style/style.ts @@ -1,527 +1,559 @@ +import { mix } from "color2k"; import { LabelRule } from "../labeler"; import { PaintRule } from "../painter"; import { CenteredTextSymbolizer, CircleSymbolizer, - FlexSymbolizer, GroupSymbolizer, - LabelSymbolizer, LineLabelSymbolizer, LineSymbolizer, OffsetTextSymbolizer, PolygonSymbolizer, - ShieldSymbolizer, exp, + linear, } from "../symbolizer"; -import { Feature } from "../tilecache"; +import { Feature, JsonObject } from "../tilecache"; +import { Theme } from "./themes"; -export interface DefaultStyleParams { - earth: string; - glacier: string; - residential: string; - hospital: string; - cemetery: string; - school: string; - industrial: string; - wood: string; - grass: string; - park: string; - water: string; - sand: string; - buildings: string; - highwayCasing: string; - majorRoadCasing: string; - mediumRoadCasing: string; - minorRoadCasing: string; - highway: string; - majorRoad: string; - mediumRoad: string; - minorRoad: string; - boundaries: string; - mask: string; - countryLabel: string; - cityLabel: string; - stateLabel: string; - neighbourhoodLabel: string; - landuseLabel: string; - waterLabel: string; - naturalLabel: string; - roadsLabel: string; - poisLabel: string; -} +const getString = (props: JsonObject, key: string): string => { + const val = props[key]; + if (typeof val === "string") return val; + return ""; +}; -interface PlacesFeature { - "pmap:rank": number; -} +const getNumber = (props: JsonObject, key: string): number => { + const val = props[key]; + if (typeof val === "number") return val; + return 0; +}; -export const paintRules = (params: DefaultStyleParams): PaintRule[] => { +export const paintRules = (t: Theme): PaintRule[] => { return [ { dataLayer: "earth", symbolizer: new PolygonSymbolizer({ - fill: params.earth, + fill: t.earth, }), }, { - dataLayer: "natural", + dataLayer: "landuse", symbolizer: new PolygonSymbolizer({ - fill: params.glacier, + fill: (z, f) => { + return mix(t.park_a, t.park_b, Math.min(Math.max(z / 12.0, 12), 0)); + }, }), filter: (z, f) => { - return f.props.natural === "glacier"; + const kind = getString(f.props, "pmap:kind"); + return ["allotments", "village_green", "playground"].includes(kind); }, }, { + // landuse_urban_green dataLayer: "landuse", symbolizer: new PolygonSymbolizer({ - fill: params.residential, + fill: t.park_b, + opacity: 0.7, }), filter: (z, f) => { - return ( - f.props.landuse === "residential" || f.props.place === "neighbourhood" - ); + const kind = getString(f.props, "pmap:kind"); + return [ + "national_park", + "park", + "cemetery", + "protected_area", + "nature_reserve", + "forest", + "golf_course", + ].includes(kind); }, }, { dataLayer: "landuse", symbolizer: new PolygonSymbolizer({ - fill: params.hospital, + fill: t.hospital, }), filter: (z, f) => { - return f.props.amenity === "hospital"; + return f.props["pmap:kind"] === "hospital"; }, }, { dataLayer: "landuse", symbolizer: new PolygonSymbolizer({ - fill: params.cemetery, + fill: t.industrial, }), filter: (z, f) => { - return f.props.landuse === "cemetery"; + return f.props["pmap:kind"] === "industrial"; }, }, { dataLayer: "landuse", symbolizer: new PolygonSymbolizer({ - fill: params.school, + fill: t.school, }), filter: (z, f) => { - return ( - f.props.amenity === "school" || - f.props.amenity === "kindergarten" || - f.props.amenity === "university" || - f.props.amenity === "college" - ); + const kind = getString(f.props, "pmap:kind"); + return ["school", "university", "college"].includes(kind); }, }, { dataLayer: "landuse", symbolizer: new PolygonSymbolizer({ - fill: params.industrial, + fill: t.beach, }), filter: (z, f) => { - return f.props.landuse === "industrial"; + return f.props["pmap:kind"] === "beach"; }, }, { - dataLayer: "natural", + dataLayer: "landuse", symbolizer: new PolygonSymbolizer({ - fill: params.wood, + fill: t.zoo, }), filter: (z, f) => { - return f.props.natural === "wood"; + return f.props["pmap:kind"] === "zoo"; }, }, { dataLayer: "landuse", symbolizer: new PolygonSymbolizer({ - fill: params.grass, + fill: t.zoo, }), filter: (z, f) => { - return f.props.landuse === "grass"; + const kind = getString(f.props, "pmap:kind"); + return ["military", "naval_base", "airfield"].includes(kind); }, }, { - dataLayer: "landuse", + dataLayer: "natural", symbolizer: new PolygonSymbolizer({ - fill: params.park, + fill: (z, f) => { + return mix(t.wood_a, t.wood_b, Math.min(Math.max(z / 12.0, 12), 0)); + }, }), filter: (z, f) => { - return f.props.leisure === "park"; + const kind = getString(f.props, "pmap:kind"); + return ["wood", "nature_reserve", "forest"].includes(kind); }, }, { - dataLayer: "water", + dataLayer: "natural", symbolizer: new PolygonSymbolizer({ - fill: params.water, + fill: (z, f) => { + return mix(t.scrub_a, t.scrub_b, Math.min(Math.max(z / 12.0, 12), 0)); + }, }), + filter: (z, f) => { + const kind = getString(f.props, "pmap:kind"); + return ["scrub", "grassland", "grass"].includes(kind); + }, }, { dataLayer: "natural", symbolizer: new PolygonSymbolizer({ - fill: params.sand, + fill: t.scrub_b, }), filter: (z, f) => { - return f.props.natural === "sand"; + const kind = getString(f.props, "pmap:kind"); + return ["scrub", "grassland", "grass"].includes(kind); }, }, { - dataLayer: "buildings", + dataLayer: "natural", symbolizer: new PolygonSymbolizer({ - fill: params.buildings, + fill: t.glacier, }), + filter: (z, f) => { + return f.props["pmap:kind"] === "glacier"; + }, }, { - dataLayer: "roads", - symbolizer: new LineSymbolizer({ - color: params.highwayCasing, - width: exp(1.4, [ - [5, 1.5], - [11, 4], - [16, 9], - [20, 40], - ]), + dataLayer: "natural", + symbolizer: new PolygonSymbolizer({ + fill: t.sand, }), filter: (z, f) => { - return f.props["pmap:kind"] === "highway"; + return f.props["pmap:kind"] === "sand"; }, }, { - dataLayer: "roads", - symbolizer: new LineSymbolizer({ - color: params.majorRoadCasing, - width: exp(1.4, [ - [9, 3], - [12, 4], - [17, 8], - [20, 22], - ]), + dataLayer: "landuse", + symbolizer: new PolygonSymbolizer({ + fill: t.aerodrome, }), filter: (z, f) => { - return f.props["pmap:kind"] === "major_road"; + return f.props["pmap:kind"] === "aerodrome"; }, }, { - dataLayer: "roads", + dataLayer: "water", + symbolizer: new PolygonSymbolizer({ + fill: t.water, + }), + }, + { + // transit_runway + dataLayer: "transit", symbolizer: new LineSymbolizer({ - color: params.mediumRoadCasing, - width: exp(1.4, [ - [13, 3], - [17, 6], - [20, 18], - ]), + color: t.runway, + width: (z, f) => { + return exp(1.6, [ + [11, 0], + [13, 4], + [19, 30], + ])(z); + }, }), filter: (z, f) => { - return f.props["pmap:kind"] === "medium_road"; + return f.props["pmap:kind_detail"] === "runway"; }, }, { - dataLayer: "roads", + // transit_taxiway + dataLayer: "transit", symbolizer: new LineSymbolizer({ - color: params.minorRoadCasing, - width: exp(1.4, [ - [14, 2], - [17, 5], - [20, 15], - ]), + color: t.runway, + width: (z, f) => { + return exp(1.6, [ + [14, 0], + [14.5, 1], + [16, 6], + ])(z); + }, }), filter: (z, f) => { - return f.props["pmap:kind"] === "minor_road"; + return f.props["pmap:kind_detail"] === "taxiway"; }, }, { - dataLayer: "roads", + // transit_pier + dataLayer: "transit", symbolizer: new LineSymbolizer({ - color: params.minorRoad, - width: exp(1.4, [ - [14, 1], - [17, 3], - [20, 13], - ]), + color: t.pier, + width: (z, f) => { + return exp(1.6, [ + [13, 0], + [13.5, 0, 5], + [21, 16], + ])(z); + }, }), filter: (z, f) => { - return f.props["pmap:kind"] === "minor_road"; + return f.props["pmap:kind"] === "pier"; }, }, { - dataLayer: "roads", + // physical_line_river + dataLayer: "physical_line", + minzoom: 14, symbolizer: new LineSymbolizer({ - color: params.mediumRoad, - width: exp(1.4, [ - [13, 2], - [17, 4], - [20, 15], - ]), + color: t.water, + width: (z, f) => { + return exp(1.6, [ + [9, 0], + [9.5, 1.0], + [18, 12], + ])(z); + }, }), filter: (z, f) => { - return f.props["pmap:kind"] === "medium_road"; + return f.props["pmap:kind"] === "river"; }, }, { - dataLayer: "roads", + // physical_line_river + dataLayer: "physical_line", + minzoom: 14, symbolizer: new LineSymbolizer({ - color: params.majorRoad, - width: exp(1.4, [ - [9, 2], - [12, 3], - [17, 6], - [20, 20], - ]), + color: t.water, + width: 0.5, }), filter: (z, f) => { - return f.props["pmap:kind"] === "major_road"; + return f.props["pmap:kind"] === "stream"; }, }, + { + dataLayer: "landuse", + symbolizer: new PolygonSymbolizer({ + fill: t.pedestrian, + }), + filter: (z, f) => { + return f.props["pmap:kind"] === "pedestrian"; + }, + }, + { + dataLayer: "landuse", + symbolizer: new PolygonSymbolizer({ + fill: t.pier, + }), + filter: (z, f) => { + return f.props["pmap:kind"] === "pier"; + }, + }, + { + dataLayer: "buildings", + symbolizer: new PolygonSymbolizer({ + fill: t.buildings, + opacity: 0.5, + }), + }, { dataLayer: "roads", symbolizer: new LineSymbolizer({ - color: params.highway, - width: exp(1.4, [ - [5, 0.5], - [11, 2.5], - [16, 7], - [20, 30], - ]), + color: t.major, + width: 0.5, + }), + }, + { + dataLayer: "boundaries", + symbolizer: new LineSymbolizer({ + dash: [3, 2], + color: t.boundaries, + width: 1, }), filter: (z, f) => { - return f.props["pmap:kind"] === "highway"; + const minAdminLevel = f.props["pmap:min_admin_level"]; + return typeof minAdminLevel === "number" && minAdminLevel <= 2; }, }, { - dataLayer: "boundaries", + dataLayer: "transit", symbolizer: new LineSymbolizer({ - color: params.boundaries, - width: 2, - opacity: 0.4, + dash: [0.3, 0.75], + color: t.railway, + dashWidth: (z, f) => { + return exp(1.6, [ + [4, 0], + [7, 0.15], + [19, 9], + ])(z); + }, + opacity: 0.5, }), + filter: (z, f) => { + return f.props["pmap:kind"] === "rail"; + }, }, { - dataLayer: "mask", - symbolizer: new PolygonSymbolizer({ - fill: params.mask, + dataLayer: "boundaries", + symbolizer: new LineSymbolizer({ + dash: [3, 2], + color: t.boundaries, + width: 0.5, }), + filter: (z, f) => { + const minAdminLevel = f.props["pmap:min_admin_level"]; + return typeof minAdminLevel === "number" && minAdminLevel > 2; + }, }, ]; }; -export const labelRules = ( - params: DefaultStyleParams, - language1?: string[], - language2?: string[], -): LabelRule[] => { - let nametags = ["name"]; - if (language1) nametags = language1; - - const languageStack = (symbolizer: LabelSymbolizer, fill: string) => { - if (!language2) return symbolizer; - if (symbolizer instanceof OffsetTextSymbolizer) { - return new FlexSymbolizer([ - symbolizer, - new OffsetTextSymbolizer({ - fill: fill, - labelProps: language2, - }), - ]); - } - return new FlexSymbolizer([ - symbolizer, - new CenteredTextSymbolizer({ - fill: fill, - labelProps: language2, - }), - ]); - }; +export const labelRules = (t: Theme): LabelRule[] => { + const nametags = ["name"]; return [ + // { + // id: "neighbourhood", + // dataLayer: "places", + // symbolizer: languageStack( + // new CenteredTextSymbolizer({ + // labelProps: nametags, + // fill: params.neighbourhoodLabel, + // font: "500 10px sans-serif", + // textTransform: "uppercase", + // }), + // params.neighbourhoodLabel, + // ), + // filter: (z, f) => { + // return f.props["pmap:kind"] === "neighbourhood"; + // }, + // }, { - dataLayer: "places", - symbolizer: languageStack( - new CenteredTextSymbolizer({ - labelProps: nametags, - fill: params.countryLabel, - lineHeight: 1.5, - font: (z: number, f?: Feature) => { - if (z < 6) return "200 14px sans-serif"; - return "200 20px sans-serif"; - }, - textTransform: "uppercase", - }), - params.countryLabel, - ), + dataLayer: "roads", + symbolizer: new LineLabelSymbolizer({ + labelProps: nametags, + fill: t.roads_label_minor, + font: "400 12px sans-serif", + width: 2, + stroke: t.roads_label_minor_halo, + }), + // TODO: sort by minzoom + minzoom: 16, filter: (z, f) => { - return f.props["pmap:kind"] === "country"; + const kind = getString(f.props, "pmap:kind"); + return ["minor_road", "other", "path"].includes(kind); }, }, { - dataLayer: "places", - symbolizer: languageStack( - new CenteredTextSymbolizer({ - labelProps: nametags, - fill: params.stateLabel, - font: "300 16px sans-serif", - }), - params.stateLabel, - ), + dataLayer: "roads", + symbolizer: new LineLabelSymbolizer({ + labelProps: nametags, + fill: t.roads_label_major, + font: "400 12px sans-serif", + width: 2, + stroke: t.roads_label_major_halo, + }), + // TODO: sort by minzoom + minzoom: 12, filter: (z, f) => { - return f.props["pmap:kind"] === "state"; + const kind = getString(f.props, "pmap:kind"); + return ["highway", "major_road", "medium_road"].includes(kind); }, }, { - id: "cities_high", - dataLayer: "places", + dataLayer: "roads", + symbolizer: new LineLabelSymbolizer({ + labelProps: nametags, + fill: t.roads_label_major, + font: "400 12px sans-serif", + width: 2, + stroke: t.roads_label_major_halo, + }), + // TODO: sort by minzoom + minzoom: 12, filter: (z, f) => { - return f.props["pmap:kind"] === "city"; - }, - minzoom: 7, - symbolizer: languageStack( - new CenteredTextSymbolizer({ - labelProps: nametags, - fill: params.cityLabel, - font: (z: number, f?: Feature) => { - if (f?.props["pmap:rank"] === 1) { - if (z > 8) return "600 20px sans-serif"; - return "600 12px sans-serif"; - } - if (z > 8) return "600 16px sans-serif"; - return "600 10px sans-serif"; - }, - }), - params.cityLabel, - ), - sort: (a, b) => { - return ( - (a as PlacesFeature)["pmap:rank"] - (b as PlacesFeature)["pmap:rank"] - ); + const kind = getString(f.props, "pmap:kind"); + return ["highway", "major_road", "medium_road"].includes(kind); }, }, { - id: "cities_low", - dataLayer: "places", + dataLayer: "physical_point", + symbolizer: new CenteredTextSymbolizer({ + labelProps: nametags, + fill: t.ocean_label, + lineHeight: 1.5, + letterSpacing: 1, + font: (z, f) => { + const size = linear([ + [3, 10], + [10, 12], + ])(z); + return `400 ${size}px sans-serif`; + }, + textTransform: "uppercase", + }), filter: (z, f) => { - return f.props["pmap:kind"] === "city"; + const kind = getString(f.props, "pmap:kind"); + return ["ocean", "bay", "strait", "fjord"].includes(kind); }, - maxzoom: 6, - symbolizer: new GroupSymbolizer([ - new CircleSymbolizer({ - radius: 2, - fill: params.cityLabel, - }), - languageStack( - new OffsetTextSymbolizer({ - labelProps: nametags, - fill: params.cityLabel, - offsetX: 2, - offsetY: 2, - font: (z: number, f?: Feature) => { - if (f?.props["pmap:rank"] === 1) { - if (z > 8) return "600 20px sans-serif"; - return "600 12px sans-serif"; - } - if (z > 8) return "600 16px sans-serif"; - return "600 10px sans-serif"; - }, - }), - params.cityLabel, - ), - ]), - sort: (a, b) => { - return ( - (a as PlacesFeature)["pmap:rank"] - (b as PlacesFeature)["pmap:rank"] - ); + }, + { + dataLayer: "physical_point", + symbolizer: new CenteredTextSymbolizer({ + labelProps: nametags, + fill: t.ocean_label, + lineHeight: 1.5, + letterSpacing: 1, + font: (z, f) => { + const size = linear([ + [3, 0], + [6, 12], + [10, 12], + ])(z); + return `400 ${size}px sans-serif`; + }, + }), + filter: (z, f) => { + const kind = getString(f.props, "pmap:kind"); + return ["sea", "lake", "water"].includes(kind); }, }, { - id: "neighbourhood", dataLayer: "places", - symbolizer: languageStack( - new CenteredTextSymbolizer({ - labelProps: nametags, - fill: params.neighbourhoodLabel, - font: "500 10px sans-serif", - textTransform: "uppercase", - }), - params.neighbourhoodLabel, - ), + symbolizer: new CenteredTextSymbolizer({ + labelProps: (z, f) => { + if (z < 6) { + return ["name:short"]; + } + return nametags; + }, + fill: t.state_label, + stroke: t.state_label_halo, + width: 1, + lineHeight: 1.5, + font: (z: number, f?: Feature) => { + if (z < 6) return "400 16px sans-serif"; + return "400 12px sans-serif"; + }, + textTransform: "uppercase", + }), filter: (z, f) => { - return f.props["pmap:kind"] === "neighbourhood"; + return f.props["pmap:kind"] === "region"; }, }, - // { - // dataLayer: "landuse", - // symbolizer: languageStack( - // new PolygonLabelSymbolizer({ - // label_props: nametags, - // fill: params.landuseLabel, - // font: "300 12px sans-serif", - // }), - // params.landuseLabel, - // ), - // }, - // { - // dataLayer: "water", - // symbolizer: languageStack( - // new PolygonLabelSymbolizer({ - // label_props: nametags, - // fill: params.waterLabel, - // font: "italic 600 12px sans-serif", - // }), - // params.waterLabel, - // ), - // }, - // { - // dataLayer: "natural", - // symbolizer: languageStack( - // new PolygonLabelSymbolizer({ - // label_props: nametags, - // fill: params.naturalLabel, - // font: "italic 300 12px sans-serif", - // }), - // params.naturalLabel, - // ), - // }, { - dataLayer: "roads", - symbolizer: languageStack( - new LineLabelSymbolizer({ - labelProps: nametags, - fill: params.roadsLabel, - font: "500 12px sans-serif", - }), - params.roadsLabel, - ), - minzoom: 12, + dataLayer: "places", + symbolizer: new CenteredTextSymbolizer({ + labelProps: nametags, + fill: t.country_label, + lineHeight: 1.5, + font: (z: number, f?: Feature) => { + if (z < 6) return "600 12px sans-serif"; + return "600 12px sans-serif"; + }, + textTransform: "uppercase", + }), + filter: (z, f) => { + return f.props["pmap:kind"] === "country"; + }, }, { - dataLayer: "roads", - symbolizer: new ShieldSymbolizer({ - labelProps: ["ref"], - font: "600 9px sans-serif", - background: params.highway, - padding: 2, - fill: params.neighbourhoodLabel, + // places_locality + dataLayer: "places", + minzoom: 9, + symbolizer: new CenteredTextSymbolizer({ + labelProps: nametags, + fill: t.city_label, + lineHeight: 1.5, + font: (z: number, f?: Feature) => { + if (!f) return "400 12px sans-serif"; + const minZoom = f.props["pmap:min_zoom"]; + let weight = 400; + if (minZoom && minZoom <= 5) { + weight = 600; + } + let size = 12; + const popRank = f.props["pmap:population_rank"]; + if (popRank && popRank > 9) { + size = 16; + } + return `${weight} ${size}px sans-serif`; + }, }), + sort: (a, b) => { + const aRank = getNumber(a, "pmap:population_rank"); + const bRank = getNumber(b, "pmap:population_rank"); + return aRank - bRank; + }, filter: (z, f) => { - return f.props["pmap:kind"] === "highway"; + return f.props["pmap:kind"] === "locality"; }, }, { - dataLayer: "pois", + dataLayer: "places", + maxzoom: 8, symbolizer: new GroupSymbolizer([ new CircleSymbolizer({ radius: 2, - fill: params.poisLabel, + fill: t.city_circle, + stroke: t.city_circle_stroke, + width: 1.5, + }), + new OffsetTextSymbolizer({ + labelProps: nametags, + fill: t.city_label, + stroke: t.city_label_halo, + width: 1, + offsetX: 6, + offsetY: 4.5, + font: (z, f) => { + return "400 12px sans-serif"; + }, }), - languageStack( - new OffsetTextSymbolizer({ - labelProps: nametags, - fill: params.poisLabel, - offsetX: 2, - offsetY: 2, - font: "300 10px sans-serif", - }), - params.poisLabel, - ), ]), + filter: (z, f) => { + return f.props["pmap:kind"] === "locality"; + }, }, ]; }; diff --git a/src/default_style/themes.ts b/src/default_style/themes.ts new file mode 100644 index 00000000..5dc80f89 --- /dev/null +++ b/src/default_style/themes.ts @@ -0,0 +1,549 @@ +export interface Theme { + background: string; + earth: string; + park_a: string; + park_b: string; + hospital: string; + industrial: string; + school: string; + wood_a: string; + wood_b: string; + pedestrian: string; + scrub_a: string; + scrub_b: string; + glacier: string; + sand: string; + beach: string; + aerodrome: string; + runway: string; + water: string; + pier: string; + zoo: string; + military: string; + + tunnel_other_casing: string; + tunnel_minor_casing: string; + tunnel_link_casing: string; + tunnel_medium_casing: string; + tunnel_major_casing: string; + tunnel_highway_casing: string; + tunnel_other: string; + tunnel_minor: string; + tunnel_link: string; + tunnel_medium: string; + tunnel_major: string; + tunnel_highway: string; + + transit_pier: string; + buildings: string; + + minor_service_casing: string; + minor_casing: string; + link_casing: string; + medium_casing: string; + major_casing_late: string; + highway_casing_late: string; + other: string; + minor_service: string; + minor_a: string; + minor_b: string; + link: string; + medium: string; + major_casing_early: string; + major: string; + highway_casing_early: string; + highway: string; + + railway: string; + boundaries: string; + waterway_label: string; + + bridges_other_casing: string; + bridges_minor_casing: string; + bridges_link_casing: string; + bridges_medium_casing: string; + bridges_major_casing: string; + bridges_highway_casing: string; + bridges_other: string; + bridges_minor: string; + bridges_link: string; + bridges_medium: string; + bridges_major: string; + bridges_highway: string; + + roads_label_minor: string; + roads_label_minor_halo: string; + roads_label_major: string; + roads_label_major_halo: string; + ocean_label: string; + peak_label: string; + subplace_label: string; + subplace_label_halo: string; + city_circle: string; + city_circle_stroke: string; + city_label: string; + city_label_halo: string; + state_label: string; + state_label_halo: string; + country_label: string; +} + +export const LIGHT: Theme = { + background: "#cccccc", + earth: "#e0e0e0", + park_a: "#cfddd5", + park_b: "#9cd3b4", + hospital: "#e4dad9", + industrial: "#d1dde1", + school: "#e4ded7", + wood_a: "#d0ded0", + wood_b: "#a0d9a0", + pedestrian: "#e3e0d4", + scrub_a: "#cedcd7", + scrub_b: "#99d2bb", + glacier: "#e7e7e7", + sand: "#e2e0d7", + beach: "#e8e4d0", + aerodrome: "#dadbdf", + runway: "#e9e9ed", + water: "#80deea", + pier: "#e0e0e0", + zoo: "#c6dcdc", + military: "#dcdcdc", + + tunnel_other_casing: "#e0e0e0", + tunnel_minor_casing: "#e0e0e0", + tunnel_link_casing: "#e0e0e0", + tunnel_medium_casing: "#e0e0e0", + tunnel_major_casing: "#e0e0e0", + tunnel_highway_casing: "#e0e0e0", + tunnel_other: "#d5d5d5", + tunnel_minor: "#d5d5d5", + tunnel_link: "#d5d5d5", + tunnel_medium: "#d5d5d5", + tunnel_major: "#d5d5d5", + tunnel_highway: "#d5d5d5", + + transit_pier: "#e0e0e0", + buildings: "#cccccc", + + minor_service_casing: "#e0e0e0", + minor_casing: "#e0e0e0", + link_casing: "#e0e0e0", + medium_casing: "#e0e0e0", + major_casing_late: "#e0e0e0", + highway_casing_late: "#e0e0e0", + other: "#ebebeb", + minor_service: "#ebebeb", + minor_a: "#ebebeb", + minor_b: "#ffffff", + link: "#ffffff", + medium: "#f5f5f5", + major_casing_early: "#e0e0e0", + major: "#ffffff", + highway_casing_early: "#e0e0e0", + highway: "#ffffff", + + railway: "#a7b1b3", + boundaries: "#adadad", + waterway_label: "#ffffff", + + bridges_other_casing: "#e0e0e0", + bridges_minor_casing: "#e0e0e0", + bridges_link_casing: "#e0e0e0", + bridges_medium_casing: "#e0e0e0", + bridges_major_casing: "#e0e0e0", + bridges_highway_casing: "#e0e0e0", + bridges_other: "#ebebeb", + bridges_minor: "#ffffff", + bridges_link: "#ffffff", + bridges_medium: "#f0eded", + bridges_major: "#f5f5f5", + bridges_highway: "#ffffff", + + roads_label_minor: "#91888b", + roads_label_minor_halo: "#ffffff", + roads_label_major: "#938a8d", + roads_label_major_halo: "#ffffff", + ocean_label: "#ffffff", + peak_label: "#7e9aa0", + subplace_label: "#8f8f8f", + subplace_label_halo: "#e0e0e0", + city_circle: "#ffffff", + city_circle_stroke: "#a3a3a3", + city_label: "#5c5c5c", + city_label_halo: "#e0e0e0", + state_label: "#b3b3b3", + state_label_halo: "#e0e0e0", + country_label: "#a3a3a3", +}; + +export const DARK: Theme = { + background: "#34373d", + earth: "#1f1f1f", + park_a: "#232325", + park_b: "#232325", + hospital: "#252424", + industrial: "#222222", + school: "#262323", + wood_a: "#202121", + wood_b: "#202121", + pedestrian: "#1e1e1e", + scrub_a: "#222323", + scrub_b: "#222323", + glacier: "#1c1c1c", + sand: "#212123", + beach: "#28282a", + aerodrome: "#1e1e1e", + runway: "#333333", + water: "#34373d", + pier: "#222222", + zoo: "#222323", + military: "#242323", + + tunnel_other_casing: "#141414", + tunnel_minor_casing: "#141414", + tunnel_link_casing: "#141414", + tunnel_medium_casing: "#141414", + tunnel_major_casing: "#141414", + tunnel_highway_casing: "#141414", + tunnel_other: "#292929", + tunnel_minor: "#292929", + tunnel_link: "#292929", + tunnel_medium: "#292929", + tunnel_major: "#292929", + tunnel_highway: "#292929", + + transit_pier: "#333333", + buildings: "#111111", + + minor_service_casing: "#1f1f1f", + minor_casing: "#1f1f1f", + link_casing: "#1f1f1f", + medium_casing: "#1f1f1f", + major_casing_late: "#1f1f1f", + highway_casing_late: "#1f1f1f", + other: "#333333", + minor_service: "#333333", + minor_a: "#3d3d3d", + minor_b: "#333333", + link: "#3d3d3d", + medium: "#3d3d3d", + major_casing_early: "#1f1f1f", + major: "#3d3d3d", + highway_casing_early: "#1f1f1f", + highway: "#474747", + + railway: "#000000", + boundaries: "#5b6374", + waterway_label: "#717784", + + bridges_other_casing: "#2b2b2b", + bridges_minor_casing: "#1f1f1f", + bridges_link_casing: "#1f1f1f", + bridges_medium_casing: "#1f1f1f", + bridges_major_casing: "#1f1f1f", + bridges_highway_casing: "#1f1f1f", + bridges_other: "#333333", + bridges_minor: "#333333", + bridges_link: "#3d3d3d", + bridges_medium: "#3d3d3d", + bridges_major: "#3d3d3d", + bridges_highway: "#474747", + + roads_label_minor: "#525252", + roads_label_minor_halo: "#1f1f1f", + roads_label_major: "#666666", + roads_label_major_halo: "#1f1f1f", + ocean_label: "#717784", + peak_label: "#898080", + subplace_label: "#525252", + subplace_label_halo: "#1f1f1f", + city_circle: "#000000", + city_circle_stroke: "#7a7a7a", + city_label: "#7a7a7a", + city_label_halo: "#212121", + state_label: "#3d3d3d", + state_label_halo: "#1f1f1f", + country_label: "#5c5c5c", +}; + +export const WHITE: Theme = { + background: "#ffffff", + earth: "#ffffff", + park_a: "#fcfcfc", + park_b: "#fcfcfc", + hospital: "#f8f8f8", + industrial: "#fcfcfc", + school: "#f8f8f8", + wood_a: "#fafafa", + wood_b: "#fafafa", + pedestrian: "#fdfdfd", + scrub_a: "#fafafa", + scrub_b: "#fafafa", + glacier: "#fcfcfc", + sand: "#fafafa", + beach: "#f6f6f6", + aerodrome: "#fdfdfd", + runway: "#efefef", + water: "#dcdcdc", + pier: "#f5f5f5", + zoo: "#f7f7f7", + military: "#fcfcfc", + + tunnel_other_casing: "#d6d6d6", + tunnel_minor_casing: "#fcfcfc", + tunnel_link_casing: "#fcfcfc", + tunnel_medium_casing: "#fcfcfc", + tunnel_major_casing: "#fcfcfc", + tunnel_highway_casing: "#fcfcfc", + tunnel_other: "#d6d6d6", + tunnel_minor: "#d6d6d6", + tunnel_link: "#d6d6d6", + tunnel_medium: "#d6d6d6", + tunnel_major: "#d6d6d6", + tunnel_highway: "#d6d6d6", + + transit_pier: "#efefef", + buildings: "#efefef", + + minor_service_casing: "#ffffff", + minor_casing: "#ffffff", + link_casing: "#ffffff", + medium_casing: "#ffffff", + major_casing_late: "#ffffff", + highway_casing_late: "#ffffff", + other: "#f5f5f5", + minor_service: "#f5f5f5", + minor_a: "#ebebeb", + minor_b: "#f5f5f5", + link: "#ebebeb", + medium: "#ebebeb", + major_casing_early: "#ffffff", + major: "#ebebeb", + highway_casing_early: "#ffffff", + highway: "#ebebeb", + + railway: "#d6d6d6", + boundaries: "#adadad", + waterway_label: "#adadad", + + bridges_other_casing: "#ffffff", + bridges_minor_casing: "#ffffff", + bridges_link_casing: "#ffffff", + bridges_medium_casing: "#ffffff", + bridges_major_casing: "#ffffff", + bridges_highway_casing: "#ffffff", + bridges_other: "#f5f5f5", + bridges_minor: "#f5f5f5", + bridges_link: "#ebebeb", + bridges_medium: "#ebebeb", + bridges_major: "#ebebeb", + bridges_highway: "#ebebeb", + + roads_label_minor: "#adadad", + roads_label_minor_halo: "#ffffff", + roads_label_major: "#999999", + roads_label_major_halo: "#ffffff", + ocean_label: "#adadad", + peak_label: "#adadad", + subplace_label: "#8f8f8f", + subplace_label_halo: "#ffffff", + city_circle: "#ffffff", + city_circle_stroke: "#adadad", + city_label: "#5c5c5c", + city_label_halo: "#ffffff", + state_label: "#b3b3b3", + state_label_halo: "#ffffff", + country_label: "#b8b8b8", +}; + +export const GRAYSCALE: Theme = { + background: "#a3a3a3", + earth: "#cccccc", + park_a: "#c2c2c2", + park_b: "#c2c2c2", + hospital: "#d0d0d0", + industrial: "#c6c6c6", + school: "#d0d0d0", + wood_a: "#c2c2c2", + wood_b: "#c2c2c2", + pedestrian: "#c4c4c4", + scrub_a: "#c2c2c2", + scrub_b: "#c2c2c2", + glacier: "#d2d2d2", + sand: "#d2d2d2", + beach: "#d2d2d2", + aerodrome: "#c9c9c9", + runway: "#f5f5f5", + water: "#a3a3a3", + pier: "#b8b8b8", + zoo: "#c7c7c7", + military: "#bfbfbf", + + tunnel_other_casing: "#b8b8b8", + tunnel_minor_casing: "#b8b8b8", + tunnel_link_casing: "#b8b8b8", + tunnel_medium_casing: "#b8b8b8", + tunnel_major_casing: "#b8b8b8", + tunnel_highway_casing: "#b8b8b8", + tunnel_other: "#d6d6d6", + tunnel_minor: "#d6d6d6", + tunnel_link: "#d6d6d6", + tunnel_medium: "#d6d6d6", + tunnel_major: "#d6d6d6", + tunnel_highway: "#d6d6d6", + + transit_pier: "#b8b8b8", + buildings: "#e0e0e0", + + minor_service_casing: "#cccccc", + minor_casing: "#cccccc", + link_casing: "#cccccc", + medium_casing: "#cccccc", + major_casing_late: "#cccccc", + highway_casing_late: "#cccccc", + other: "#e0e0e0", + minor_service: "#e0e0e0", + minor_a: "#ebebeb", + minor_b: "#e0e0e0", + link: "#ebebeb", + medium: "#ebebeb", + major_casing_early: "#cccccc", + major: "#ebebeb", + highway_casing_early: "#cccccc", + highway: "#ebebeb", + + railway: "#f5f5f5", + boundaries: "#5c5c5c", + waterway_label: "#7a7a7a", + + bridges_other_casing: "#cccccc", + bridges_minor_casing: "#cccccc", + bridges_link_casing: "#cccccc", + bridges_medium_casing: "#cccccc", + bridges_major_casing: "#cccccc", + bridges_highway_casing: "#cccccc", + bridges_other: "#e0e0e0", + bridges_minor: "#e0e0e0", + bridges_link: "#ebebeb", + bridges_medium: "#ebebeb", + bridges_major: "#ebebeb", + bridges_highway: "#ebebeb", + + roads_label_minor: "#999999", + roads_label_minor_halo: "#e0e0e0", + roads_label_major: "#8f8f8f", + roads_label_major_halo: "#ebebeb", + ocean_label: "#7a7a7a", + peak_label: "#5c5c5c", + subplace_label: "#7a7a7a", + subplace_label_halo: "#cccccc", + city_circle: "#c2c2c2", + city_circle_stroke: "#7a7a7a", + city_label: "#474747", + city_label_halo: "#cccccc", + state_label: "#999999", + state_label_halo: "#cccccc", + country_label: "#858585", +}; + +export const BLACK: Theme = { + background: "#2b2b2b", + earth: "#141414", + park_a: "#181818", + park_b: "#181818", + hospital: "#1d1d1d", + industrial: "#101010", + school: "#111111", + wood_a: "#1a1a1a", + wood_b: "#1a1a1a", + pedestrian: "#191919", + scrub_a: "#1c1c1c", + scrub_b: "#1c1c1c", + glacier: "#191919", + sand: "#161616", + beach: "#1f1f1f", + aerodrome: "#191919", + runway: "#323232", + water: "#333333", + pier: "#0a0a0a", + zoo: "#191919", + military: "#121212", + + tunnel_other_casing: "#101010", + tunnel_minor_casing: "#101010", + tunnel_link_casing: "#101010", + tunnel_medium_casing: "#101010", + tunnel_major_casing: "#101010", + tunnel_highway_casing: "#101010", + tunnel_other: "#292929", + tunnel_minor: "#292929", + tunnel_link: "#292929", + tunnel_medium: "#292929", + tunnel_major: "#292929", + tunnel_highway: "#292929", + + transit_pier: "#0a0a0a", + buildings: "#0a0a0a", + + minor_service_casing: "#141414", + minor_casing: "#141414", + link_casing: "#141414", + medium_casing: "#141414", + major_casing_late: "#141414", + highway_casing_late: "#141414", + other: "#1f1f1f", + minor_service: "#1f1f1f", + minor_a: "#292929", + minor_b: "#1f1f1f", + link: "#1f1f1f", + medium: "#292929", + major_casing_early: "#141414", + major: "#292929", + highway_casing_early: "#141414", + highway: "#292929", + + railway: "#292929", + boundaries: "#707070", + waterway_label: "#707070", + + bridges_other_casing: "#141414", + bridges_minor_casing: "#141414", + bridges_link_casing: "#141414", + bridges_medium_casing: "#141414", + bridges_major_casing: "#141414", + bridges_highway_casing: "#141414", + bridges_other: "#1f1f1f", + bridges_minor: "#1f1f1f", + bridges_link: "#292929", + bridges_medium: "#292929", + bridges_major: "#292929", + bridges_highway: "#292929", + + roads_label_minor: "#525252", + roads_label_minor_halo: "#141414", + roads_label_major: "#5c5c5c", + roads_label_major_halo: "#141414", + ocean_label: "#707070", + peak_label: "#707070", + subplace_label: "#5c5c5c", + subplace_label_halo: "#141414", + city_circle: "#000000", + city_circle_stroke: "#666666", + city_label: "#999999", + city_label_halo: "#141414", + state_label: "#3d3d3d", + state_label_halo: "#141414", + country_label: "#707070", +}; + +const themes: Record = { + light: LIGHT, + dark: DARK, + white: WHITE, + grayscale: GRAYSCALE, + black: BLACK, +}; + +export default themes; diff --git a/src/frontends/leaflet.ts b/src/frontends/leaflet.ts index 28c0b535..9c0fbffc 100644 --- a/src/frontends/leaflet.ts +++ b/src/frontends/leaflet.ts @@ -5,9 +5,8 @@ import Point from "@mapbox/point-geometry"; import type { Coords } from "leaflet"; import { PMTiles } from "pmtiles"; -import { dark } from "../default_style/dark"; -import { light } from "../default_style/light"; import { labelRules, paintRules } from "../default_style/style"; +import themes from "../default_style/themes"; import { LabelRule, Labelers } from "../labeler"; import { PaintRule, paint } from "../painter"; import { PreparedTile, SourceOptions, sourcesToViews } from "../view"; @@ -49,19 +48,16 @@ interface LeafletLayerOptions { debug?: string; lang?: string; tileDelay?: number; - backgroundColor?: string; - language1?: string[]; - language2?: string[]; - dark?: boolean; + language?: string[]; noWrap?: boolean; paintRules?: PaintRule[]; labelRules?: LabelRule[]; tasks?: Promise[]; - - levelDiff?: number; maxDataZoom?: number; url?: PMTiles | string; sources?: Record; + theme?: string; + backgroundColor?: string; } const leafletLayer = (options: LeafletLayerOptions = {}): unknown => { @@ -77,12 +73,17 @@ const leafletLayer = (options: LeafletLayerOptions = {}): unknown => { 'Protomaps © OpenStreetMap'; super(options); - const theme = options.dark ? dark : light; - this.paintRules = options.paintRules || paintRules(theme); - this.labelRules = - options.labelRules || - labelRules(theme, options.language1, options.language2); - this.backgroundColor = options.backgroundColor; + if (options.theme) { + const theme = themes[options.theme]; + this.paintRules = paintRules(theme); + this.labelRules = labelRules(theme); + this.backgroundColor = theme.background; + } else { + this.paintRules = options.paintRules || []; + this.labelRules = options.labelRules || []; + this.backgroundColor = options.backgroundColor; + } + this.lastRequestedZ = undefined; this.tasks = options.tasks || []; @@ -107,16 +108,6 @@ const leafletLayer = (options: LeafletLayerOptions = {}): unknown => { this.lang = options.lang; } - public setDefaultStyle( - darkOption: boolean, - language1: string[], - language2: string[], - ) { - const theme = darkOption ? dark : light; - this.paintRules = paintRules(theme); - this.labelRules = labelRules(theme, language1, language2); - } - public async renderTile( coords: Coords, element: KeyedHtmlCanvasElement, diff --git a/src/frontends/static.ts b/src/frontends/static.ts index e8823ba4..d8b0a535 100644 --- a/src/frontends/static.ts +++ b/src/frontends/static.ts @@ -1,9 +1,8 @@ import Point from "@mapbox/point-geometry"; import { PMTiles } from "pmtiles"; -import { dark } from "../default_style/dark"; -import { light } from "../default_style/light"; import { labelRules, paintRules } from "../default_style/style"; +import themes from "../default_style/themes"; import { LabelRule, Labeler } from "../labeler"; import { PaintRule, paint } from "../painter"; import { PreparedTile, SourceOptions, View, sourcesToViews } from "../view"; @@ -62,16 +61,14 @@ export const getZoom = (degreesLng: number, cssPixels: number): number => { interface StaticOptions { debug?: string; lang?: string; - levelDiff?: number; maxDataZoom?: number; url?: PMTiles | string; sources?: Record; paintRules?: PaintRule[]; - dark?: boolean; labelRules?: LabelRule[]; - language1?: string[]; - language2?: string[]; + language?: string[]; backgroundColor?: string; + theme?: string; } export class Static { @@ -82,12 +79,16 @@ export class Static { backgroundColor?: string; constructor(options: StaticOptions) { - const theme = options.dark ? dark : light; - this.paintRules = options.paintRules || paintRules(theme); - this.labelRules = - options.labelRules || - labelRules(theme, options.language1, options.language2); - this.backgroundColor = options.backgroundColor; + if (options.theme) { + const theme = themes[options.theme]; + this.paintRules = paintRules(theme); + this.labelRules = labelRules(theme); + this.backgroundColor = theme.background; + } else { + this.paintRules = options.paintRules || []; + this.labelRules = options.labelRules || []; + this.backgroundColor = options.backgroundColor; + } this.views = sourcesToViews(options); this.debug = options.debug || ""; diff --git a/src/index.ts b/src/index.ts index e9ba1503..89ff42eb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,8 +2,6 @@ export * from "./frontends/static"; export * from "./frontends/leaflet"; export * from "./symbolizer"; export * from "./task"; -export * from "./default_style/light"; -export * from "./default_style/dark"; export * from "./default_style/style"; export * from "./painter"; export * from "./tilecache"; diff --git a/src/labeler.ts b/src/labeler.ts index 592e2597..b8527d4f 100644 --- a/src/labeler.ts +++ b/src/labeler.ts @@ -2,7 +2,7 @@ import Point from "@mapbox/point-geometry"; import rBush from "rbush"; import { Filter } from "./painter"; import { DrawExtra, LabelSymbolizer } from "./symbolizer"; -import { Bbox, toIndex } from "./tilecache"; +import { Bbox, JsonObject, toIndex } from "./tilecache"; import { PreparedTile, transformGeom } from "./view"; type TileInvalidationCallback = (tiles: Set) => void; @@ -47,7 +47,7 @@ export interface LabelRule { symbolizer: LabelSymbolizer; filter?: Filter; visible?: boolean; - sort?: (a: unknown, b: unknown) => number; + sort?: (a: JsonObject, b: JsonObject) => number; } export const covering = ( diff --git a/src/symbolizer.ts b/src/symbolizer.ts index c49621a6..cce9ab2e 100644 --- a/src/symbolizer.ts +++ b/src/symbolizer.ts @@ -16,9 +16,6 @@ import { Sheet } from "./task"; import { linebreak } from "./text"; import { Bbox, Feature, GeomType } from "./tilecache"; -// https://bugs.webkit.org/show_bug.cgi?id=230751 -const MAX_VERTICES_PER_DRAW_CALL = 5400; - export interface PaintSymbolizer { before?(ctx: CanvasRenderingContext2D, z: number): void; draw( @@ -143,22 +140,15 @@ export class PolygonSymbolizer implements PaintSymbolizer { } }; - let verticesInPath = 0; ctx.beginPath(); for (const poly of geom) { - if (verticesInPath + poly.length > MAX_VERTICES_PER_DRAW_CALL) { - drawPath(); - verticesInPath = 0; - ctx.beginPath(); - } for (let p = 0; p < poly.length; p++) { const pt = poly[p]; if (p === 0) ctx.moveTo(pt.x, pt.y); else ctx.lineTo(pt.x, pt.y); } - verticesInPath += poly.length; } - if (verticesInPath > 0) drawPath(); + drawPath(); } } @@ -336,22 +326,15 @@ export class LineSymbolizer implements PaintSymbolizer { } }; - let verticesInPath = 0; ctx.beginPath(); for (const ls of geom) { - if (verticesInPath + ls.length > MAX_VERTICES_PER_DRAW_CALL) { - strokePath(); - verticesInPath = 0; - ctx.beginPath(); - } for (let p = 0; p < ls.length; p++) { const pt = ls[p]; if (p === 0) ctx.moveTo(pt.x, pt.y); else ctx.lineTo(pt.x, pt.y); } - verticesInPath += ls.length; } - if (verticesInPath > 0) strokePath(); + strokePath(); } } diff --git a/src/view.ts b/src/view.ts index 6180cc23..5630acf9 100644 --- a/src/view.ts +++ b/src/view.ts @@ -225,8 +225,8 @@ export interface SourceOptions { export const sourcesToViews = (options: SourceOptions) => { const sourceToViews = (o: SourceOptions): View => { - const levelDiff = o.levelDiff === undefined ? 2 : o.levelDiff; - const maxDataZoom = o.maxDataZoom || 14; + const levelDiff = o.levelDiff === undefined ? 1 : o.levelDiff; + const maxDataZoom = o.maxDataZoom || 15; let source: TileSource; if (typeof o.url === "string") { if (o.url.endsWith(".pmtiles")) { diff --git a/test/view.test.ts b/test/view.test.ts index 2f62b3a9..6ebdf540 100644 --- a/test/view.test.ts +++ b/test/view.test.ts @@ -92,7 +92,7 @@ test("wrap", async () => { test("sources to views", async () => { let v = sourcesToViews({ url: "http://example.com/{z}/{x}/{y}.mvt" }); - assert.equal(v.get("").levelDiff, 2); + assert.equal(v.get("").levelDiff, 1); v = sourcesToViews({ sources: { source1: { @@ -100,5 +100,5 @@ test("sources to views", async () => { }, }, }); - assert.equal(v.get("source1").levelDiff, 2); + assert.equal(v.get("source1").levelDiff, 1); });