@mailwoman/cartographer 10.0.0 → 10.1.0
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/lib/base/buildings.ts +3 -4
- package/lib/base/composition.ts +23 -13
- package/lib/base/layers.ts +9 -3
- package/lib/base/theme.ts +22 -13
- package/lib/{bdc/index.ts → bdc.ts} +15 -109
- package/lib/coverage.ts +73 -0
- package/lib/index.ts +6 -6
- package/lib/planetary/layers.ts +22 -26
- package/lib/planetary/sources.ts +6 -6
- package/lib/planetary/style.ts +8 -6
- package/lib/{race-dots/index.ts → race-dots.ts} +19 -20
- package/lib/styles/fonts.ts +19 -8
- package/lib/styles/layers.ts +3 -3
- package/lib/styles/sources.ts +4 -3
- package/lib/{tiger/index.ts → tiger.ts} +3 -2
- package/out/base/buildings.d.ts.map +1 -1
- package/out/base/buildings.js +3 -4
- package/out/base/buildings.js.map +1 -1
- package/out/base/composition.d.ts +18 -8
- package/out/base/composition.d.ts.map +1 -1
- package/out/base/composition.js +13 -9
- package/out/base/composition.js.map +1 -1
- package/out/base/layers.d.ts +1 -1
- package/out/base/layers.d.ts.map +1 -1
- package/out/base/layers.js +9 -3
- package/out/base/layers.js.map +1 -1
- package/out/base/theme.d.ts +9 -5
- package/out/base/theme.d.ts.map +1 -1
- package/out/base/theme.js +21 -13
- package/out/base/theme.js.map +1 -1
- package/out/{base/index.d.ts → base.d.ts} +1 -1
- package/out/base.d.ts.map +1 -0
- package/out/{base/index.js → base.js} +1 -1
- package/out/base.js.map +1 -0
- package/out/{bdc/index.d.ts → bdc.d.ts} +8 -7
- package/out/bdc.d.ts.map +1 -0
- package/out/{bdc/index.js → bdc.js} +9 -102
- package/out/bdc.js.map +1 -0
- package/out/coverage.d.ts +44 -0
- package/out/coverage.d.ts.map +1 -0
- package/out/coverage.js +63 -0
- package/out/coverage.js.map +1 -0
- package/out/{hspa/index.d.ts → hspa.d.ts} +1 -1
- package/out/hspa.d.ts.map +1 -0
- package/out/{hspa/index.js → hspa.js} +1 -1
- package/out/hspa.js.map +1 -0
- package/out/index.d.ts +6 -6
- package/out/index.d.ts.map +1 -1
- package/out/index.js +6 -6
- package/out/index.js.map +1 -1
- package/out/planetary/layers.d.ts +16 -20
- package/out/planetary/layers.d.ts.map +1 -1
- package/out/planetary/layers.js +16 -19
- package/out/planetary/layers.js.map +1 -1
- package/out/planetary/sources.d.ts +5 -5
- package/out/planetary/sources.js +6 -6
- package/out/planetary/sources.js.map +1 -1
- package/out/planetary/style.d.ts +7 -4
- package/out/planetary/style.d.ts.map +1 -1
- package/out/planetary/style.js +2 -4
- package/out/planetary/style.js.map +1 -1
- package/out/{planetary/index.js → planetary.d.ts} +1 -1
- package/out/planetary.d.ts.map +1 -0
- package/out/{planetary/index.d.ts → planetary.js} +1 -1
- package/out/planetary.js.map +1 -0
- package/out/race-dots.d.ts +70 -0
- package/out/race-dots.d.ts.map +1 -0
- package/out/race-dots.js +69 -0
- package/out/race-dots.js.map +1 -0
- package/out/styles/fonts.d.ts +17 -8
- package/out/styles/fonts.d.ts.map +1 -1
- package/out/styles/fonts.js +17 -8
- package/out/styles/fonts.js.map +1 -1
- package/out/styles/layers.js +3 -3
- package/out/styles/layers.js.map +1 -1
- package/out/styles/sources.d.ts.map +1 -1
- package/out/styles/sources.js +3 -2
- package/out/styles/sources.js.map +1 -1
- package/out/{styles/index.d.ts → styles.d.ts} +1 -1
- package/out/styles.d.ts.map +1 -0
- package/out/{styles/index.js → styles.js} +1 -1
- package/out/styles.js.map +1 -0
- package/out/{tiger/index.d.ts → tiger.d.ts} +4 -3
- package/out/tiger.d.ts.map +1 -0
- package/out/{tiger/index.js → tiger.js} +4 -3
- package/out/tiger.js.map +1 -0
- package/out/{tiles/index.d.ts → tiles.d.ts} +1 -1
- package/out/tiles.d.ts.map +1 -0
- package/out/{tiles/index.js → tiles.js} +1 -1
- package/out/tiles.js.map +1 -0
- package/out/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +14 -99
- package/lib/coverage/index.ts +0 -88
- package/out/base/index.d.ts.map +0 -1
- package/out/base/index.js.map +0 -1
- package/out/bdc/index.d.ts.map +0 -1
- package/out/bdc/index.js.map +0 -1
- package/out/coverage/index.d.ts +0 -59
- package/out/coverage/index.d.ts.map +0 -1
- package/out/coverage/index.js +0 -78
- package/out/coverage/index.js.map +0 -1
- package/out/hspa/index.d.ts.map +0 -1
- package/out/hspa/index.js.map +0 -1
- package/out/planetary/index.d.ts.map +0 -1
- package/out/planetary/index.js.map +0 -1
- package/out/race-dots/index.d.ts +0 -72
- package/out/race-dots/index.d.ts.map +0 -1
- package/out/race-dots/index.js +0 -70
- package/out/race-dots/index.js.map +0 -1
- package/out/styles/index.d.ts.map +0 -1
- package/out/styles/index.js.map +0 -1
- package/out/tiger/index.d.ts.map +0 -1
- package/out/tiger/index.js.map +0 -1
- package/out/tiles/index.d.ts.map +0 -1
- package/out/tiles/index.js.map +0 -1
- /package/lib/{base/index.ts → base.ts} +0 -0
- /package/lib/{hspa/index.ts → hspa.ts} +0 -0
- /package/lib/{planetary/index.ts → planetary.ts} +0 -0
- /package/lib/{styles/index.ts → styles.ts} +0 -0
- /package/lib/{tiles/index.ts → tiles.ts} +0 -0
package/lib/base/buildings.ts
CHANGED
|
@@ -11,7 +11,7 @@ import type {
|
|
|
11
11
|
} from "@maplibre/maplibre-gl-style-spec"
|
|
12
12
|
|
|
13
13
|
import { MailwomanBaseTileSetID } from "#base/theme"
|
|
14
|
-
import {
|
|
14
|
+
import { MAP_FONT_REGULAR } from "#styles/fonts"
|
|
15
15
|
import { LayerID } from "#styles/layers"
|
|
16
16
|
|
|
17
17
|
const BuildingLayerID = LayerID.bind(null, "buildings")
|
|
@@ -80,8 +80,7 @@ export const BuildingLayers: LayerSpecification[] = [
|
|
|
80
80
|
"fill-extrusion-vertical-gradient": false,
|
|
81
81
|
"fill-extrusion-translate-anchor": "map",
|
|
82
82
|
"fill-extrusion-opacity": 1,
|
|
83
|
-
// top of the band = full building height
|
|
84
|
-
// "fill-extrusion-height": popHeight(buildingHeight()),
|
|
83
|
+
// top of the band = full building height "fill-extrusion-height": popHeight(buildingHeight()),
|
|
85
84
|
// // base of the band = full height minus the cap thickness (clamped ≥ 0)
|
|
86
85
|
// "fill-extrusion-base": popHeight([ "max", [ "-", buildingHeight(), ROOF_CAP ], 0 ]),
|
|
87
86
|
"fill-extrusion-height": popHeight(["+", buildingHeight(), ROOF_GAP, ROOF_CAP]),
|
|
@@ -115,7 +114,7 @@ export const BuildingLayers: LayerSpecification[] = [
|
|
|
115
114
|
"text-field": ["to-string", ["id"]],
|
|
116
115
|
"text-offset": [0, 0],
|
|
117
116
|
"text-anchor": "center",
|
|
118
|
-
"text-font": [
|
|
117
|
+
"text-font": [MAP_FONT_REGULAR],
|
|
119
118
|
},
|
|
120
119
|
|
|
121
120
|
paint: {
|
package/lib/base/composition.ts
CHANGED
|
@@ -16,9 +16,9 @@ import {
|
|
|
16
16
|
} from "#styles/layers"
|
|
17
17
|
import type { TileSetSourceRecord } from "#styles/sources"
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
// #endregion
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
// #region Spec Creators
|
|
22
22
|
|
|
23
23
|
export function createLightSpec(spec?: Partial<LightSpecification>): LightSpecification {
|
|
24
24
|
return {
|
|
@@ -46,19 +46,23 @@ export function createSkySpec(spec?: Partial<SkySpecification>): SkySpecificatio
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
// #endregion
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
// #region Style Composition
|
|
52
52
|
|
|
53
53
|
/**
|
|
54
|
-
* The glyph host every style composed here reads fonts from.
|
|
55
|
-
*
|
|
54
|
+
* The glyph host every style composed here reads fonts from.
|
|
55
|
+
*
|
|
56
|
+
* The Protomaps font set, mirrored under the public bucket so a style never
|
|
57
|
+
* depends on an upstream host at render time.
|
|
56
58
|
*/
|
|
57
59
|
export const PROTOMAPS_GLYPHS_URL = "https://public.mailwoman.ai/protomaps/fonts/{fontstack}/{range}.pbf"
|
|
58
60
|
|
|
59
61
|
/**
|
|
60
|
-
* The Earth sprite.
|
|
61
|
-
*
|
|
62
|
+
* The Earth sprite.
|
|
63
|
+
*
|
|
64
|
+
* It must match the basemap schema version: the v4 sprite includes the icons the v4 theme's
|
|
65
|
+
* layers reference by name, so a style over a different basemap version needs a different sprite.
|
|
62
66
|
*/
|
|
63
67
|
export const PROTOMAPS_SPRITE_URL = "https://public.mailwoman.ai/protomaps/sprites/v4/light"
|
|
64
68
|
|
|
@@ -69,16 +73,22 @@ export interface StyleSpecificationComposition {
|
|
|
69
73
|
sky?: Partial<SkySpecification>
|
|
70
74
|
terrain?: Partial<TerrainSpecification>
|
|
71
75
|
/**
|
|
72
|
-
* The layer list every `layers` entry inserts into.
|
|
73
|
-
*
|
|
76
|
+
* The layer list every `layers` entry inserts into.
|
|
77
|
+
*
|
|
78
|
+
* Earth's basemap layers by default.
|
|
79
|
+
* A body with no roads, water or buildings brings its own.
|
|
74
80
|
*/
|
|
75
81
|
baseLayers?: LayerSpecification[]
|
|
76
82
|
/**
|
|
77
|
-
* The `hillshade` source.
|
|
83
|
+
* The `hillshade` source.
|
|
84
|
+
*
|
|
85
|
+
* Earth's terrarium DEM by default; `null` adds no such source.
|
|
78
86
|
*/
|
|
79
87
|
hillshadeSource?: SourceSpecification | null
|
|
80
88
|
/**
|
|
81
|
-
* The sprite URL.
|
|
89
|
+
* The sprite URL.
|
|
90
|
+
*
|
|
91
|
+
* Earth's Protomaps v4 sprite by default; `null` omits the key, for a style with no icons.
|
|
82
92
|
*/
|
|
83
93
|
sprite?: string | null
|
|
84
94
|
glyphs?: string
|
|
@@ -146,4 +156,4 @@ export class StyleSpecificationComposer {
|
|
|
146
156
|
}
|
|
147
157
|
}
|
|
148
158
|
|
|
149
|
-
|
|
159
|
+
// #endregion
|
package/lib/base/layers.ts
CHANGED
|
@@ -18,9 +18,15 @@ import { LayerID } from "#styles/layers"
|
|
|
18
18
|
export const HillsLayerID = LayerID(HillshadeTileSetID, "hills")
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
* Splits `layers()` into non-label
|
|
22
|
-
*
|
|
23
|
-
*
|
|
21
|
+
* Splits `layers()` into non-label and label groups.
|
|
22
|
+
*
|
|
23
|
+
* The interval between base geometry and labels holds the footprints.
|
|
24
|
+
* It also holds water outlines and hillshade.
|
|
25
|
+
*
|
|
26
|
+
* `@protomaps/basemaps@5.x` doesn't expose a `noLabels` helper.
|
|
27
|
+
* `labelsOnly: true` gives only the label layers.
|
|
28
|
+
*
|
|
29
|
+
* The full list minus that set yields the non-label group.
|
|
24
30
|
*/
|
|
25
31
|
const allBaseLayers = layers(MailwomanBaseTileSetID, MailwomanBaseFlavor, { lang: "en" })
|
|
26
32
|
const labelLayers = layers(MailwomanBaseTileSetID, MailwomanBaseFlavor, { lang: "en", labelsOnly: true })
|
package/lib/base/theme.ts
CHANGED
|
@@ -6,25 +6,34 @@
|
|
|
6
6
|
|
|
7
7
|
import { type Flavor, namedFlavor } from "@protomaps/basemaps"
|
|
8
8
|
|
|
9
|
+
import { MAP_FONT_ITALIC, MAP_FONT_MEDIUM, MAP_FONT_REGULAR } from "#styles/fonts"
|
|
9
10
|
import { TileSetSourceID } from "#styles/sources"
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
|
-
* Identifier for the Mailwoman base tileset.
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* Identifier for the Mailwoman base tileset.
|
|
14
|
+
*
|
|
15
|
+
* Matches the R2 object basename (`nexus-assets/tiles/basemap-v4.pmtiles`).
|
|
16
|
+
* `tiles.mailwoman.ai/basemap-v4.json` returns its tilejson,
|
|
17
|
+
* `tiles.mailwoman.ai/basemap-v4/{z}/{x}/{y}.mvt` returns vector tiles.
|
|
15
18
|
*/
|
|
16
19
|
export const MailwomanBaseTileSetID = TileSetSourceID("basemap-v4")
|
|
17
20
|
|
|
18
21
|
const darkFlavor = namedFlavor("dark")
|
|
19
22
|
|
|
20
23
|
/**
|
|
21
|
-
* The Mailwoman theme for Protomaps via MapLibre.
|
|
22
|
-
*
|
|
24
|
+
* The Mailwoman theme for Protomaps via MapLibre.
|
|
25
|
+
*
|
|
26
|
+
* Keys follow `@protomaps/basemaps@5.x` which targets the v4 tile schema
|
|
27
|
+
* (`["==","kind","..."]` filters), matching the basemap-v4 PMTiles on R2.
|
|
23
28
|
*/
|
|
24
29
|
export const MailwomanBaseFlavor: Flavor = {
|
|
25
30
|
...darkFlavor,
|
|
26
31
|
|
|
27
|
-
|
|
32
|
+
regular: MAP_FONT_REGULAR,
|
|
33
|
+
bold: MAP_FONT_MEDIUM,
|
|
34
|
+
italic: MAP_FONT_ITALIC,
|
|
35
|
+
|
|
36
|
+
// #region Base
|
|
28
37
|
background: "hsl(0deg 10% 5%)",
|
|
29
38
|
earth: "hsl(0deg 10% 5%)",
|
|
30
39
|
|
|
@@ -51,8 +60,8 @@ export const MailwomanBaseFlavor: Flavor = {
|
|
|
51
60
|
zoo: "#222323",
|
|
52
61
|
military: "#242323",
|
|
53
62
|
|
|
54
|
-
|
|
55
|
-
|
|
63
|
+
// #endregion
|
|
64
|
+
// #region Tunnel
|
|
56
65
|
tunnel_other_casing: "#141414",
|
|
57
66
|
tunnel_minor_casing: "#141414",
|
|
58
67
|
tunnel_link_casing: "#141414",
|
|
@@ -64,10 +73,10 @@ export const MailwomanBaseFlavor: Flavor = {
|
|
|
64
73
|
tunnel_major: "#292929",
|
|
65
74
|
tunnel_highway: "#292929",
|
|
66
75
|
|
|
67
|
-
|
|
76
|
+
// #endregion
|
|
68
77
|
buildings: "#111111",
|
|
69
78
|
|
|
70
|
-
|
|
79
|
+
// #region Casing
|
|
71
80
|
minor_service_casing: "#1f1f1f",
|
|
72
81
|
minor_casing: "#1f1f1f",
|
|
73
82
|
link_casing: "#1f1f1f",
|
|
@@ -83,11 +92,11 @@ export const MailwomanBaseFlavor: Flavor = {
|
|
|
83
92
|
highway_casing_early: "#1f1f1f",
|
|
84
93
|
highway: "hsl(36deg 10% 50%)",
|
|
85
94
|
|
|
86
|
-
|
|
95
|
+
// #endregion
|
|
87
96
|
railway: "#000000",
|
|
88
97
|
boundaries: "hsl(240deg 100% 90%)",
|
|
89
98
|
|
|
90
|
-
|
|
99
|
+
// #region Bridges
|
|
91
100
|
bridges_other_casing: "#2b2b2b",
|
|
92
101
|
bridges_minor_casing: "#1f1f1f",
|
|
93
102
|
bridges_link_casing: "#1f1f1f",
|
|
@@ -99,7 +108,7 @@ export const MailwomanBaseFlavor: Flavor = {
|
|
|
99
108
|
bridges_major: "#3d3d3d",
|
|
100
109
|
bridges_highway: "#474747",
|
|
101
110
|
|
|
102
|
-
|
|
111
|
+
// #endregion
|
|
103
112
|
|
|
104
113
|
ocean_label: "#717784",
|
|
105
114
|
subplace_label: "hsl(50deg 50% 70%)",
|
|
@@ -47,13 +47,14 @@ export type BaseBDCLayerSpecification<T> = T extends BDCLayerSpecificationInput
|
|
|
47
47
|
: never
|
|
48
48
|
|
|
49
49
|
/**
|
|
50
|
-
* The three builders below take a spec
|
|
50
|
+
* The three builders below take a spec without the source fields and return one with them filled in.
|
|
51
51
|
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
52
|
+
* These are specific assertions.
|
|
53
|
+
* Each avoids `unknown`.
|
|
54
|
+
*
|
|
55
|
+
* TypeScript cannot verify that because TypeScript cannot verify that `Omit<T, K>` plus
|
|
56
|
+
* the omitted keys reconstitutes `T` — a `<T>(…): T` builder could only reach its return
|
|
57
|
+
* type by defeating the checker, covering the `id`, `minzoom` and `maxzoom` values too.
|
|
57
58
|
*/
|
|
58
59
|
type BDCLayerBuilderInput = BaseBDCLayerSpecification<BDCLayerSpecificationInput>
|
|
59
60
|
|
|
@@ -101,9 +102,6 @@ const GIGABIT_BROADBAND_SPEED = 1000
|
|
|
101
102
|
export const BroadbandDataCollectionLayers: BDCLayerSpecificationInput[] = [
|
|
102
103
|
{
|
|
103
104
|
afterID: "earth",
|
|
104
|
-
// metadata: {
|
|
105
|
-
// queryable: false,
|
|
106
|
-
// },
|
|
107
105
|
id: "blocks-underserved",
|
|
108
106
|
layout: {
|
|
109
107
|
visibility: "none",
|
|
@@ -112,43 +110,19 @@ export const BroadbandDataCollectionLayers: BDCLayerSpecificationInput[] = [
|
|
|
112
110
|
source: BDCTileSetID,
|
|
113
111
|
type: "fill",
|
|
114
112
|
filter: [
|
|
115
|
-
// ---
|
|
116
113
|
"all",
|
|
117
114
|
["in", 50, ["get", "technology_codes"]], // Fiber only
|
|
118
115
|
[">", ["get", "land_area_sqm"], 0],
|
|
119
|
-
// ["==", ["get", "UR"], "R"],
|
|
120
116
|
],
|
|
121
117
|
paint: {
|
|
122
|
-
// "fill-extrusion-height": [
|
|
123
|
-
// // Our extrusion height represents the magnitude of broadband underservice,
|
|
124
|
-
// // e.g. How many people are underserved per square meter of land area.
|
|
125
|
-
// "let",
|
|
126
|
-
// "population_density",
|
|
127
|
-
// ["/", ["to-number", ["get", "population"]], ["to-number", ["get", "land_area_sqm"]]],
|
|
128
|
-
// ["*", ["var", "population_density"], 10000],
|
|
129
|
-
// ],
|
|
130
118
|
"fill-color": "hsl(60deg, 100%, 50%)",
|
|
131
119
|
"fill-opacity": [
|
|
132
|
-
// Underserved areas are emphasized to their level importance,
|
|
133
|
-
// relative to the impact of the underservice.
|
|
134
120
|
"let",
|
|
135
121
|
"internet_speed_impact",
|
|
136
122
|
["/", ["to-number", ["get", "average_download_speed"]], GIGABIT_BROADBAND_SPEED],
|
|
137
|
-
[
|
|
138
|
-
// ---
|
|
139
|
-
"interpolate",
|
|
140
|
-
["linear"],
|
|
141
|
-
["var", "internet_speed_impact"],
|
|
142
|
-
0,
|
|
143
|
-
0.1,
|
|
144
|
-
1,
|
|
145
|
-
0.5,
|
|
146
|
-
],
|
|
123
|
+
["interpolate", ["linear"], ["var", "internet_speed_impact"], 0, 0.1, 1, 0.5],
|
|
147
124
|
],
|
|
148
125
|
},
|
|
149
|
-
// layout: {
|
|
150
|
-
// visibility: "none",
|
|
151
|
-
// },
|
|
152
126
|
},
|
|
153
127
|
|
|
154
128
|
{
|
|
@@ -163,7 +137,6 @@ export const BroadbandDataCollectionLayers: BDCLayerSpecificationInput[] = [
|
|
|
163
137
|
"source-layer": `bdc_${TIGERLevel.Block}`,
|
|
164
138
|
source: BDCTileSetID,
|
|
165
139
|
type: "line",
|
|
166
|
-
// filter: [">=", ["get", "ALAND"], 0],
|
|
167
140
|
minzoom: 9,
|
|
168
141
|
paint: {
|
|
169
142
|
"line-color": "#000",
|
|
@@ -180,13 +153,7 @@ export const BroadbandDataCollectionLayers: BDCLayerSpecificationInput[] = [
|
|
|
180
153
|
"source-layer": `bdc_${TIGERLevel.Block}`,
|
|
181
154
|
source: BDCTileSetID,
|
|
182
155
|
type: "symbol",
|
|
183
|
-
filter: [
|
|
184
|
-
// ---
|
|
185
|
-
"all",
|
|
186
|
-
// ["in", 50, ["get", "technology_codes"]], // Fiber only
|
|
187
|
-
[">", ["get", "land_area_sqm"], 0],
|
|
188
|
-
// ["==", ["get", "UR"], "R"],
|
|
189
|
-
],
|
|
156
|
+
filter: ["all", [">", ["get", "land_area_sqm"], 0]],
|
|
190
157
|
paint: {
|
|
191
158
|
"text-color": "black",
|
|
192
159
|
"text-halo-color": "white",
|
|
@@ -208,13 +175,7 @@ export const BroadbandDataCollectionLayers: BDCLayerSpecificationInput[] = [
|
|
|
208
175
|
...createBDCLayer({
|
|
209
176
|
id: "coverage-fiber-label",
|
|
210
177
|
beforeID: "places_subplace",
|
|
211
|
-
filter: [
|
|
212
|
-
// ---
|
|
213
|
-
|
|
214
|
-
"all",
|
|
215
|
-
["==", ["get", "provider_id"], 131_425],
|
|
216
|
-
// ["in", 50, ["get", "technology_codes"]],
|
|
217
|
-
],
|
|
178
|
+
filter: ["all", ["==", ["get", "provider_id"], 131_425]],
|
|
218
179
|
type: "symbol",
|
|
219
180
|
layout: {
|
|
220
181
|
visibility: "none",
|
|
@@ -224,13 +185,8 @@ export const BroadbandDataCollectionLayers: BDCLayerSpecificationInput[] = [
|
|
|
224
185
|
" Mbps",
|
|
225
186
|
],
|
|
226
187
|
"text-size": [
|
|
227
|
-
// ---
|
|
228
188
|
"let",
|
|
229
189
|
"multiplier",
|
|
230
|
-
// We scale the text size based on the average download speed.
|
|
231
|
-
// By default, we're no smaller than 14, Any speed above the gigabit threshold
|
|
232
|
-
// acts as a multiplier for the text size.
|
|
233
|
-
// ---
|
|
234
190
|
["/", ["get", "average_download_speed"], GIGABIT_BROADBAND_SPEED / 4],
|
|
235
191
|
["+", 14, ["var", "multiplier"]],
|
|
236
192
|
],
|
|
@@ -246,7 +202,6 @@ export const BroadbandDataCollectionLayers: BDCLayerSpecificationInput[] = [
|
|
|
246
202
|
paint: {
|
|
247
203
|
"text-halo-width": 2,
|
|
248
204
|
"text-halo-color": [
|
|
249
|
-
// ---
|
|
250
205
|
"interpolate",
|
|
251
206
|
["exponential", 0.5],
|
|
252
207
|
["get", "average_download_speed"],
|
|
@@ -258,7 +213,6 @@ export const BroadbandDataCollectionLayers: BDCLayerSpecificationInput[] = [
|
|
|
258
213
|
],
|
|
259
214
|
|
|
260
215
|
"text-color": [
|
|
261
|
-
// ---
|
|
262
216
|
"interpolate",
|
|
263
217
|
["exponential", 0.25],
|
|
264
218
|
["get", "average_download_speed"],
|
|
@@ -287,22 +241,7 @@ export const BroadbandDataCollectionLayers: BDCLayerSpecificationInput[] = [
|
|
|
287
241
|
paint: {
|
|
288
242
|
"line-color": "#ff009c",
|
|
289
243
|
|
|
290
|
-
"line-width": [
|
|
291
|
-
"interpolate",
|
|
292
|
-
["exponential", 1.6],
|
|
293
|
-
["zoom"],
|
|
294
|
-
// ---
|
|
295
|
-
3,
|
|
296
|
-
0,
|
|
297
|
-
6,
|
|
298
|
-
1.1,
|
|
299
|
-
12,
|
|
300
|
-
1.6,
|
|
301
|
-
15,
|
|
302
|
-
5,
|
|
303
|
-
18,
|
|
304
|
-
15,
|
|
305
|
-
],
|
|
244
|
+
"line-width": ["interpolate", ["exponential", 1.6], ["zoom"], 3, 0, 6, 1.1, 12, 1.6, 15, 5, 18, 15],
|
|
306
245
|
},
|
|
307
246
|
layout: {
|
|
308
247
|
"line-cap": "round",
|
|
@@ -312,12 +251,7 @@ export const BroadbandDataCollectionLayers: BDCLayerSpecificationInput[] = [
|
|
|
312
251
|
...createBDCLayer({
|
|
313
252
|
afterID: "earth",
|
|
314
253
|
id: "coverage-fiber-heat",
|
|
315
|
-
filter: [
|
|
316
|
-
// ---
|
|
317
|
-
|
|
318
|
-
"all",
|
|
319
|
-
["==", ["get", "provider_id"], 131_425],
|
|
320
|
-
],
|
|
254
|
+
filter: ["all", ["==", ["get", "provider_id"], 131_425]],
|
|
321
255
|
minzoom: 10,
|
|
322
256
|
type: "heatmap",
|
|
323
257
|
layout: {
|
|
@@ -325,7 +259,6 @@ export const BroadbandDataCollectionLayers: BDCLayerSpecificationInput[] = [
|
|
|
325
259
|
},
|
|
326
260
|
paint: {
|
|
327
261
|
"heatmap-color": [
|
|
328
|
-
// ---
|
|
329
262
|
"interpolate",
|
|
330
263
|
["linear"],
|
|
331
264
|
["heatmap-density"],
|
|
@@ -334,22 +267,8 @@ export const BroadbandDataCollectionLayers: BDCLayerSpecificationInput[] = [
|
|
|
334
267
|
}).flat(),
|
|
335
268
|
],
|
|
336
269
|
|
|
337
|
-
"heatmap-radius": [
|
|
338
|
-
|
|
339
|
-
"interpolate",
|
|
340
|
-
["linear"],
|
|
341
|
-
["zoom"],
|
|
342
|
-
10,
|
|
343
|
-
5,
|
|
344
|
-
15,
|
|
345
|
-
20,
|
|
346
|
-
],
|
|
347
|
-
"heatmap-weight": [
|
|
348
|
-
// ---
|
|
349
|
-
"*",
|
|
350
|
-
["get", "land_area_sqm"],
|
|
351
|
-
0.00001,
|
|
352
|
-
],
|
|
270
|
+
"heatmap-radius": ["interpolate", ["linear"], ["zoom"], 10, 5, 15, 20],
|
|
271
|
+
"heatmap-weight": ["*", ["get", "land_area_sqm"], 0.00001],
|
|
353
272
|
},
|
|
354
273
|
}),
|
|
355
274
|
|
|
@@ -381,30 +300,17 @@ export const BroadbandDataCollectionLayers: BDCLayerSpecificationInput[] = [
|
|
|
381
300
|
source: "composite",
|
|
382
301
|
"source-layer": "DataCentersCombined20220921",
|
|
383
302
|
layout: {
|
|
384
|
-
// visibility: "none",
|
|
385
303
|
"text-field": ["get", "Category"],
|
|
386
304
|
"text-size": 12,
|
|
387
305
|
"text-anchor": "top",
|
|
388
306
|
"text-offset": [0, 0.5],
|
|
389
|
-
// "text-allow-overlap": true,
|
|
390
307
|
"text-font": ["Fira Code Regular"],
|
|
391
308
|
},
|
|
392
309
|
paint: {
|
|
393
310
|
"text-color": "#fff",
|
|
394
311
|
"text-halo-color": "#fb8429",
|
|
395
312
|
"text-halo-width": 1,
|
|
396
|
-
"text-opacity": [
|
|
397
|
-
// We fade out the outline at higher zoom levels
|
|
398
|
-
"interpolate",
|
|
399
|
-
["linear"],
|
|
400
|
-
["zoom"],
|
|
401
|
-
1,
|
|
402
|
-
0.01,
|
|
403
|
-
6,
|
|
404
|
-
0.5,
|
|
405
|
-
16,
|
|
406
|
-
1,
|
|
407
|
-
],
|
|
313
|
+
"text-opacity": ["interpolate", ["linear"], ["zoom"], 1, 0.01, 6, 0.5, 16, 1],
|
|
408
314
|
},
|
|
409
315
|
},
|
|
410
316
|
]
|
package/lib/coverage.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software.
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* Address-coverage overlays rendered from the `mailwoman coverage build` H3 tile set. Each cell
|
|
7
|
+
* supplies optimistic (`fog_opt`) and observed-coverage (`fog`) values. The demo exposes both as
|
|
8
|
+
* default-off layers. Tiles are served from `coverage-v5.pmtiles`.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { FillLayerSpecification, VectorSourceSpecification } from "@maplibre/maplibre-gl-style-spec"
|
|
12
|
+
|
|
13
|
+
import { TileSetSourceID } from "#styles/sources"
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* MapLibre source ID for the v5 coverage tiles.
|
|
17
|
+
* Keep it aligned with the archive and layer specs.
|
|
18
|
+
*/
|
|
19
|
+
export const CoverageTileSetID = TileSetSourceID("coverage-v5")
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Source-layer name stored in the coverage archive.
|
|
23
|
+
*/
|
|
24
|
+
export const COVERAGE_SOURCE_LAYER = "coverage"
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Near-black indigo used to mark unknown or unsurveyed areas.
|
|
28
|
+
*/
|
|
29
|
+
export const COVERAGE_FOG_COLOR = "#663399" // Rebecca Purple
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Maximum opacity for an uncovered cell.
|
|
33
|
+
*/
|
|
34
|
+
export const COVERAGE_MAX_FOG_OPACITY = 0.9
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* IDs for the optimistic and observed-coverage layers.
|
|
38
|
+
*/
|
|
39
|
+
export const CoverageLayerID = {
|
|
40
|
+
optimistic: "coverage-opt-fog",
|
|
41
|
+
honest: "coverage-honest-fog",
|
|
42
|
+
} as const
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Create a vector source from its TileJSON URL.
|
|
46
|
+
*/
|
|
47
|
+
export function createCoverageSource(url: string): VectorSourceSpecification {
|
|
48
|
+
return { type: "vector", url }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function fogFill(id: string, fogProperty: "fog" | "fog_opt"): FillLayerSpecification {
|
|
52
|
+
return {
|
|
53
|
+
id,
|
|
54
|
+
type: "fill",
|
|
55
|
+
source: CoverageTileSetID,
|
|
56
|
+
"source-layer": COVERAGE_SOURCE_LAYER,
|
|
57
|
+
// Keep coverage overlays hidden until selected.
|
|
58
|
+
layout: { visibility: "none" },
|
|
59
|
+
paint: {
|
|
60
|
+
"fill-color": COVERAGE_FOG_COLOR,
|
|
61
|
+
// Rows without coverage values render as clear.
|
|
62
|
+
"fill-opacity": ["*", ["coalesce", ["to-number", ["get", fogProperty]], 0], COVERAGE_MAX_FOG_OPACITY],
|
|
63
|
+
},
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The two hidden fog layers, added beneath place labels by the demo.
|
|
69
|
+
*/
|
|
70
|
+
export const CoverageLayers: FillLayerSpecification[] = [
|
|
71
|
+
fogFill(CoverageLayerID.optimistic, "fog_opt"),
|
|
72
|
+
fogFill(CoverageLayerID.honest, "fog"),
|
|
73
|
+
]
|
package/lib/index.ts
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
export * from "#base
|
|
8
|
-
export * from "#bdc
|
|
9
|
-
export * from "#hspa
|
|
10
|
-
export * from "#styles
|
|
11
|
-
export * from "#tiger
|
|
12
|
-
export * from "#tiles
|
|
7
|
+
export * from "#base"
|
|
8
|
+
export * from "#bdc"
|
|
9
|
+
export * from "#hspa"
|
|
10
|
+
export * from "#styles"
|
|
11
|
+
export * from "#tiger"
|
|
12
|
+
export * from "#tiles"
|
package/lib/planetary/layers.ts
CHANGED
|
@@ -12,24 +12,23 @@ import type {
|
|
|
12
12
|
} from "@maplibre/maplibre-gl-style-spec"
|
|
13
13
|
|
|
14
14
|
import { PlanetaryHillshadeSourceID, PlanetaryNomenclatureSourceID } from "#planetary/sources"
|
|
15
|
-
import {
|
|
15
|
+
import { MAP_FONT_REGULAR } from "#styles/fonts"
|
|
16
16
|
import { LayerID } from "#styles/layers"
|
|
17
17
|
|
|
18
18
|
export interface PlanetaryPalette {
|
|
19
19
|
/**
|
|
20
|
-
* The body's own surface tone, under the relief
|
|
21
|
-
*
|
|
22
|
-
* canvas, and the app's stylesheet paints it.
|
|
20
|
+
* The body's own surface tone, under the relief, painted on the sphere
|
|
21
|
+
* rather than the page behind the canvas.
|
|
23
22
|
*/
|
|
24
23
|
space: string
|
|
25
24
|
/**
|
|
26
|
-
* Where a slope faces the light.
|
|
27
|
-
*
|
|
25
|
+
* Where a slope faces the light.
|
|
26
|
+
*
|
|
27
|
+
* The tiles store encoded elevation and have no color of their own.
|
|
28
28
|
*/
|
|
29
29
|
reliefHighlight: string
|
|
30
30
|
/**
|
|
31
|
-
* Where a slope faces away
|
|
32
|
-
* take the shadow colour everywhere the terrain is level.
|
|
31
|
+
* Where a slope faces away, kept translucent so the surface tone reads through flat ground.
|
|
33
32
|
*/
|
|
34
33
|
reliefShadow: string
|
|
35
34
|
/**
|
|
@@ -42,8 +41,8 @@ export interface PlanetaryPalette {
|
|
|
42
41
|
}
|
|
43
42
|
|
|
44
43
|
/**
|
|
45
|
-
* One palette per body with a published style
|
|
46
|
-
*
|
|
44
|
+
* One palette per body with a published style, so a body without a row here
|
|
45
|
+
* has no style rather than a wrong one.
|
|
47
46
|
*/
|
|
48
47
|
export const PALETTES = {
|
|
49
48
|
moon: {
|
|
@@ -71,9 +70,7 @@ export type PlanetaryStyleBody = keyof typeof PALETTES
|
|
|
71
70
|
const PLANETARY_NAMESPACE = "planetary"
|
|
72
71
|
|
|
73
72
|
/**
|
|
74
|
-
* The body's surface tone, under the relief
|
|
75
|
-
* viewport, so this is the ground the hillshade shades and not the field around the globe; the app's stylesheet paints
|
|
76
|
-
* that behind a transparent canvas.
|
|
73
|
+
* The body's surface tone, under the relief, painted on the sphere rather than the viewport.
|
|
77
74
|
*/
|
|
78
75
|
export const PlanetarySpaceLayerID = LayerID(PLANETARY_NAMESPACE, "space")
|
|
79
76
|
|
|
@@ -93,8 +90,8 @@ export const PlanetaryLabelsLayerID = LayerID(PLANETARY_NAMESPACE, "nomenclature
|
|
|
93
90
|
export const PlanetarySelectionLayerID = LayerID(PLANETARY_NAMESPACE, "selection")
|
|
94
91
|
|
|
95
92
|
/**
|
|
96
|
-
* The IAU feature-type codes
|
|
97
|
-
*
|
|
93
|
+
* The IAU feature-type codes for features that span a region rather than marking
|
|
94
|
+
* a point, drawn as spaced capitals.
|
|
98
95
|
*/
|
|
99
96
|
const REGION_FEATURE_TYPE_CODES = ["ME", "PL", "OC", "TA"]
|
|
100
97
|
|
|
@@ -107,9 +104,8 @@ export function spaceLayer(palette: PlanetaryPalette): BackgroundLayerSpecificat
|
|
|
107
104
|
}
|
|
108
105
|
|
|
109
106
|
/**
|
|
110
|
-
* The relief, shaded at draw time from the body's encoded elevation
|
|
111
|
-
*
|
|
112
|
-
* properties are brightness, contrast, saturation and hue-rotate, and the last two do nothing without chroma.
|
|
107
|
+
* The relief, shaded at draw time from the body's encoded elevation,
|
|
108
|
+
* since the tiles store height and have no color.
|
|
113
109
|
*/
|
|
114
110
|
export function hillshadeLayer(palette: PlanetaryPalette): HillshadeLayerSpecification {
|
|
115
111
|
return {
|
|
@@ -117,8 +113,8 @@ export function hillshadeLayer(palette: PlanetaryPalette): HillshadeLayerSpecifi
|
|
|
117
113
|
type: "hillshade",
|
|
118
114
|
source: PlanetaryHillshadeSourceID,
|
|
119
115
|
paint: {
|
|
120
|
-
// Under 1: these bodies
|
|
121
|
-
// shading reads as noise at globe
|
|
116
|
+
// Under 1: these bodies have relief sharper than Earth's relative to their radius.
|
|
117
|
+
// Unexaggerated shading reads as noise at globe zoom levels.
|
|
122
118
|
"hillshade-exaggeration": 0.6,
|
|
123
119
|
"hillshade-highlight-color": palette.reliefHighlight,
|
|
124
120
|
"hillshade-shadow-color": palette.reliefShadow,
|
|
@@ -128,9 +124,9 @@ export function hillshadeLayer(palette: PlanetaryPalette): HillshadeLayerSpecifi
|
|
|
128
124
|
}
|
|
129
125
|
|
|
130
126
|
/**
|
|
131
|
-
* Labels by feature scale: the tile's per-feature `minzoom` already hides a
|
|
132
|
-
*
|
|
133
|
-
*
|
|
127
|
+
* Labels by feature scale: the tile's per-feature `minzoom` already hides a
|
|
128
|
+
* small crater at a low zoom, so the symbol layer only sizes and prioritizes,
|
|
129
|
+
* reading `featureTypeCode` (the IAU feature-type code) and `diameterKm`.
|
|
134
130
|
*/
|
|
135
131
|
export function labelLayer(palette: PlanetaryPalette): SymbolLayerSpecification {
|
|
136
132
|
return {
|
|
@@ -140,7 +136,7 @@ export function labelLayer(palette: PlanetaryPalette): SymbolLayerSpecification
|
|
|
140
136
|
"source-layer": "nomenclature",
|
|
141
137
|
layout: {
|
|
142
138
|
"text-field": ["get", "name"],
|
|
143
|
-
"text-font": [
|
|
139
|
+
"text-font": [MAP_FONT_REGULAR],
|
|
144
140
|
"text-size": ["interpolate", ["linear"], ["coalesce", ["get", "diameterKm"], 0], 0, 11, 100, 14, 1000, 18],
|
|
145
141
|
"text-transform": ["match", ["get", "featureTypeCode"], REGION_FEATURE_TYPE_CODES, "uppercase", "none"],
|
|
146
142
|
"text-letter-spacing": ["match", ["get", "featureTypeCode"], REGION_FEATURE_TYPE_CODES, 0.15, 0.02],
|
|
@@ -152,8 +148,8 @@ export function labelLayer(palette: PlanetaryPalette): SymbolLayerSpecification
|
|
|
152
148
|
}
|
|
153
149
|
|
|
154
150
|
/**
|
|
155
|
-
* The selection ring
|
|
156
|
-
* the app
|
|
151
|
+
* The selection ring, shipped in the style with a filter that matches no feature
|
|
152
|
+
* until the app sets one by `id`.
|
|
157
153
|
*/
|
|
158
154
|
export function selectionLayer(palette: PlanetaryPalette): CircleLayerSpecification {
|
|
159
155
|
return {
|