@versatiles/style 5.8.4 → 5.9.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/README.md +24 -18
- package/dist/index.d.ts +20 -14
- package/dist/index.js +151 -83
- package/dist/index.js.map +1 -1
- package/package.json +16 -16
- package/src/color/hsl.test.ts +1 -2
- package/src/color/hsl.ts +0 -8
- package/src/color/hsv.ts +1 -1
- package/src/color/index.test.ts +89 -1
- package/src/color/index.ts +3 -1
- package/src/color/random.test.ts +54 -0
- package/src/color/random.ts +3 -1
- package/src/color/rgb.test.ts +5 -2
- package/src/color/rgb.ts +0 -9
- package/src/color/utils.ts +1 -1
- package/src/guess_style/guess_style.ts +3 -1
- package/src/index.test.ts +1 -1
- package/src/index.ts +12 -2
- package/src/lib/utils.test.ts +84 -1
- package/src/lib/utils.ts +10 -19
- package/src/style_builder/decorator.ts +18 -11
- package/src/style_builder/recolor.ts +18 -11
- package/src/style_builder/style_builder.ts +6 -3
- package/src/style_builder/types.ts +2 -43
- package/src/styles/index.ts +2 -0
- package/src/styles/satellite.test.ts +146 -0
- package/src/styles/satellite.ts +106 -0
- package/src/types/tilejson.test.ts +10 -10
- package/src/types/tilejson.ts +46 -24
- package/src/types/vector_layer.test.ts +1 -1
- package/src/types/vector_layer.ts +2 -4
package/README.md
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
| **eclipse** | <img width="384" src="https://versatiles.org/versatiles-style/eclipse.png" alt="eclipse style" /> |
|
|
19
19
|
| **neutrino** | <img width="384" src="https://versatiles.org/versatiles-style/neutrino.png" alt="neutrino style" /> |
|
|
20
20
|
| **shadow** | <img width="384" src="https://versatiles.org/versatiles-style/shadow.png" alt="shadow style" /> |
|
|
21
|
+
| **satellite** | <img width="384" src="https://versatiles.org/versatiles-style/satellite.png" alt="satellite style" /> |
|
|
21
22
|
|
|
22
23
|
---
|
|
23
24
|
|
|
@@ -95,8 +96,10 @@ The library offers the following style generation methods:
|
|
|
95
96
|
- `graybeard(options)` - [Documentation](https://versatiles.org/versatiles-style/functions/graybeard.html)
|
|
96
97
|
- `neutrino(options)` - [Documentation](https://versatiles.org/versatiles-style/functions/neutrino.html)
|
|
97
98
|
- `shadow(options)` - [Documentation](https://versatiles.org/versatiles-style/functions/shadow.html)
|
|
99
|
+
- `satellite(options)` - [Documentation](https://versatiles.org/versatiles-style/functions/satellite.html)
|
|
98
100
|
|
|
99
101
|
**`options`**: An optional object to customize the styles. [Learn more](https://versatiles.org/versatiles-style/interfaces/StyleBuilderOptions.html)
|
|
102
|
+
`satellite` uses a different options type: [SatelliteStyleOptions](https://versatiles.org/versatiles-style/interfaces/SatelliteStyleOptions.html)
|
|
100
103
|
|
|
101
104
|
### Guess Style Method
|
|
102
105
|
|
|
@@ -159,7 +162,7 @@ subgraph 1["color"]
|
|
|
159
162
|
end
|
|
160
163
|
subgraph 9["guess_style"]
|
|
161
164
|
A["guess_style.ts"]
|
|
162
|
-
|
|
165
|
+
10["index.ts"]
|
|
163
166
|
end
|
|
164
167
|
subgraph B["lib"]
|
|
165
168
|
C["utils.ts"]
|
|
@@ -171,7 +174,8 @@ Q["eclipse.ts"]
|
|
|
171
174
|
R["empty.ts"]
|
|
172
175
|
S["graybeard.ts"]
|
|
173
176
|
T["neutrino.ts"]
|
|
174
|
-
U["
|
|
177
|
+
U["satellite.ts"]
|
|
178
|
+
V["shadow.ts"]
|
|
175
179
|
end
|
|
176
180
|
subgraph G["style_builder"]
|
|
177
181
|
H["style_builder.ts"]
|
|
@@ -185,13 +189,13 @@ K["layers.ts"]
|
|
|
185
189
|
L["template.ts"]
|
|
186
190
|
N["properties.ts"]
|
|
187
191
|
end
|
|
188
|
-
subgraph
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
192
|
+
subgraph W["types"]
|
|
193
|
+
X["index.ts"]
|
|
194
|
+
Y["tilejson.ts"]
|
|
195
|
+
Z["vector_layer.ts"]
|
|
196
|
+
12["maplibre.ts"]
|
|
193
197
|
end
|
|
194
|
-
|
|
198
|
+
11["index.ts"]
|
|
195
199
|
end
|
|
196
200
|
3-->2
|
|
197
201
|
3-->4
|
|
@@ -215,8 +219,8 @@ end
|
|
|
215
219
|
A-->5
|
|
216
220
|
A-->C
|
|
217
221
|
A-->E
|
|
218
|
-
A-->W
|
|
219
222
|
A-->X
|
|
223
|
+
A-->Y
|
|
220
224
|
C-->8
|
|
221
225
|
E-->F
|
|
222
226
|
E-->Q
|
|
@@ -224,6 +228,7 @@ E-->R
|
|
|
224
228
|
E-->S
|
|
225
229
|
E-->T
|
|
226
230
|
E-->U
|
|
231
|
+
E-->V
|
|
227
232
|
F-->H
|
|
228
233
|
H-->8
|
|
229
234
|
H-->C
|
|
@@ -241,15 +246,16 @@ Q-->F
|
|
|
241
246
|
R-->F
|
|
242
247
|
S-->F
|
|
243
248
|
T-->F
|
|
244
|
-
U-->
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
Z
|
|
248
|
-
10-->
|
|
249
|
-
|
|
250
|
-
10
|
|
251
|
-
|
|
252
|
-
|
|
249
|
+
U-->S
|
|
250
|
+
V-->F
|
|
251
|
+
X-->Y
|
|
252
|
+
X-->Z
|
|
253
|
+
10-->A
|
|
254
|
+
11-->8
|
|
255
|
+
11-->10
|
|
256
|
+
11-->E
|
|
257
|
+
|
|
258
|
+
class 0,1,9,B,D,G,I,W subgraphs;
|
|
253
259
|
classDef subgraphs fill-opacity:0.1, fill:#888, color:#888, stroke:#888;
|
|
254
260
|
```
|
|
255
261
|
|
package/dist/index.d.ts
CHANGED
|
@@ -88,12 +88,6 @@ declare class RGB extends Color {
|
|
|
88
88
|
* @returns The current RGB instance.
|
|
89
89
|
*/
|
|
90
90
|
asRGB(): RGB;
|
|
91
|
-
/**
|
|
92
|
-
* Returns the RGB color.
|
|
93
|
-
*
|
|
94
|
-
* @returns The current RGB instance.
|
|
95
|
-
*/
|
|
96
|
-
toRGB(): RGB;
|
|
97
91
|
/**
|
|
98
92
|
* Parses a string or Color instance into an RGB color.
|
|
99
93
|
*
|
|
@@ -306,11 +300,6 @@ declare class HSL extends Color {
|
|
|
306
300
|
* @returns The current HSL color.
|
|
307
301
|
*/
|
|
308
302
|
asHSL(): HSL;
|
|
309
|
-
/**
|
|
310
|
-
* Returns the current HSL color.
|
|
311
|
-
* @returns The current HSL color.
|
|
312
|
-
*/
|
|
313
|
-
toHSL(): HSL;
|
|
314
303
|
/**
|
|
315
304
|
* Converts the HSL color to an HSV color.
|
|
316
305
|
* @returns A new HSV color representing the same color.
|
|
@@ -667,7 +656,8 @@ interface StyleBuilderOptions {
|
|
|
667
656
|
*/
|
|
668
657
|
recolor?: RecolorOptions;
|
|
669
658
|
}
|
|
670
|
-
|
|
659
|
+
declare const styleBuilderColorKeys: readonly ["agriculture", "boundary", "building", "buildingbg", "burial", "commercial", "construction", "cycle", "danger", "disputed", "education", "foot", "glacier", "grass", "hospital", "industrial", "label", "labelHalo", "land", "leisure", "motorway", "motorwaybg", "park", "parking", "poi", "prison", "rail", "residential", "rock", "sand", "shield", "street", "streetbg", "subway", "symbol", "trunk", "trunkbg", "waste", "water", "wetland", "wood"];
|
|
660
|
+
type StyleBuilderColorKey = (typeof styleBuilderColorKeys)[number];
|
|
671
661
|
/** Records string values for color properties in a style builder. */
|
|
672
662
|
type StyleBuilderColors<T = Color | string> = Record<StyleBuilderColorKey, T>;
|
|
673
663
|
/** Records string values for font properties in a style builder. */
|
|
@@ -676,6 +666,21 @@ type StyleBuilderFonts = {
|
|
|
676
666
|
bold: string;
|
|
677
667
|
};
|
|
678
668
|
|
|
669
|
+
interface SatelliteStyleOptions {
|
|
670
|
+
baseUrl?: string;
|
|
671
|
+
rasterTiles?: string[];
|
|
672
|
+
overlayTiles?: string[];
|
|
673
|
+
overlay?: boolean;
|
|
674
|
+
language?: Language;
|
|
675
|
+
rasterOpacity?: number;
|
|
676
|
+
rasterHueRotate?: number;
|
|
677
|
+
rasterBrightnessMin?: number;
|
|
678
|
+
rasterBrightnessMax?: number;
|
|
679
|
+
rasterSaturation?: number;
|
|
680
|
+
rasterContrast?: number;
|
|
681
|
+
}
|
|
682
|
+
declare function buildSatelliteStyle(options?: SatelliteStyleOptions): StyleSpecification;
|
|
683
|
+
|
|
679
684
|
interface StyleBuilderFunction {
|
|
680
685
|
(options?: StyleBuilderOptions): StyleSpecification;
|
|
681
686
|
getOptions(): StyleBuilderOptions;
|
|
@@ -757,7 +762,8 @@ declare const styles: {
|
|
|
757
762
|
graybeard: StyleBuilderFunction;
|
|
758
763
|
shadow: StyleBuilderFunction;
|
|
759
764
|
neutrino: StyleBuilderFunction;
|
|
765
|
+
satellite: typeof buildSatelliteStyle;
|
|
760
766
|
};
|
|
761
767
|
|
|
762
|
-
export { Color, HSL, HSV, RGB, colorful, eclipse, graybeard, guessStyle, neutrino, shadow, styles };
|
|
763
|
-
export type { GuessStyleOptions, Language, RandomColorOptions, RecolorOptions, StyleBuilderColorKey, StyleBuilderColors, StyleBuilderFonts, StyleBuilderFunction, StyleBuilderOptions, TileJSONSpecification, TileJSONSpecificationRaster, TileJSONSpecificationVector, VectorLayer };
|
|
768
|
+
export { Color, HSL, HSV, RGB, colorful, eclipse, graybeard, guessStyle, neutrino, buildSatelliteStyle as satellite, shadow, styles };
|
|
769
|
+
export type { GuessStyleOptions, Language, RandomColorOptions, RecolorOptions, SatelliteStyleOptions, StyleBuilderColorKey, StyleBuilderColors, StyleBuilderFonts, StyleBuilderFunction, StyleBuilderOptions, TileJSONSpecification, TileJSONSpecificationRaster, TileJSONSpecificationVector, VectorLayer };
|
package/dist/index.js
CHANGED
|
@@ -145,7 +145,7 @@ function mod(value, max) {
|
|
|
145
145
|
value = value % max;
|
|
146
146
|
if (value < 0)
|
|
147
147
|
value += max;
|
|
148
|
-
if (value
|
|
148
|
+
if (value === 0)
|
|
149
149
|
return 0;
|
|
150
150
|
return value;
|
|
151
151
|
}
|
|
@@ -347,7 +347,7 @@ function getColorInfo(hue) {
|
|
|
347
347
|
return color;
|
|
348
348
|
}
|
|
349
349
|
}
|
|
350
|
-
throw Error(
|
|
350
|
+
throw new Error(`getColorInfo: No color info found for hue value ${hue}. This indicates a gap in the color dictionary hue ranges.`);
|
|
351
351
|
}
|
|
352
352
|
|
|
353
353
|
/**
|
|
@@ -519,14 +519,6 @@ class RGB extends Color {
|
|
|
519
519
|
asRGB() {
|
|
520
520
|
return this.clone();
|
|
521
521
|
}
|
|
522
|
-
/**
|
|
523
|
-
* Returns the RGB color.
|
|
524
|
-
*
|
|
525
|
-
* @returns The current RGB instance.
|
|
526
|
-
*/
|
|
527
|
-
toRGB() {
|
|
528
|
-
return this;
|
|
529
|
-
}
|
|
530
522
|
/**
|
|
531
523
|
* Parses a string or Color instance into an RGB color.
|
|
532
524
|
*
|
|
@@ -750,7 +742,7 @@ class HSV extends Color {
|
|
|
750
742
|
const v = this.v / 100;
|
|
751
743
|
const k = (2 - s) * v;
|
|
752
744
|
const q = k < 1 ? k : 2 - k;
|
|
753
|
-
return new HSL(this.h, q
|
|
745
|
+
return new HSL(this.h, q === 0 ? 0 : (100 * s * v) / q, (100 * k) / 2, this.a);
|
|
754
746
|
}
|
|
755
747
|
/**
|
|
756
748
|
* Returns the current HSV color.
|
|
@@ -917,13 +909,6 @@ class HSL extends Color {
|
|
|
917
909
|
asHSL() {
|
|
918
910
|
return this.clone();
|
|
919
911
|
}
|
|
920
|
-
/**
|
|
921
|
-
* Returns the current HSL color.
|
|
922
|
-
* @returns The current HSL color.
|
|
923
|
-
*/
|
|
924
|
-
toHSL() {
|
|
925
|
-
return this;
|
|
926
|
-
}
|
|
927
912
|
/**
|
|
928
913
|
* Converts the HSL color to an HSV color.
|
|
929
914
|
* @returns A new HSV color representing the same color.
|
|
@@ -1031,7 +1016,7 @@ Color.parse = function (input) {
|
|
|
1031
1016
|
case 'hsla(':
|
|
1032
1017
|
return HSL.parse(input);
|
|
1033
1018
|
default:
|
|
1034
|
-
throw Error(
|
|
1019
|
+
throw new Error(`Color.parse: Unknown color format "${input}". Expected formats: "#RRGGBB", "#RGB", "rgb(...)", "rgba(...)", "hsl(...)", or "hsla(...)".`);
|
|
1035
1020
|
}
|
|
1036
1021
|
};
|
|
1037
1022
|
Color.HSL = HSL;
|
|
@@ -2070,9 +2055,7 @@ function deepClone(obj) {
|
|
|
2070
2055
|
}
|
|
2071
2056
|
if (obj == null)
|
|
2072
2057
|
return obj;
|
|
2073
|
-
|
|
2074
|
-
console.log('obj.prototype', Object.getPrototypeOf(obj));
|
|
2075
|
-
throw Error();
|
|
2058
|
+
throw new Error(`deepClone: Unsupported object type "${Object.getPrototypeOf(obj)?.constructor?.name ?? 'unknown'}"`);
|
|
2076
2059
|
}
|
|
2077
2060
|
function isSimpleObject(item) {
|
|
2078
2061
|
if (item === null)
|
|
@@ -2098,7 +2081,7 @@ function isBasicType(item) {
|
|
|
2098
2081
|
case 'object':
|
|
2099
2082
|
return false;
|
|
2100
2083
|
default:
|
|
2101
|
-
throw Error(
|
|
2084
|
+
throw new Error(`isBasicType: Unknown type "${typeof item}"`);
|
|
2102
2085
|
}
|
|
2103
2086
|
}
|
|
2104
2087
|
function deepMerge(source0, ...sources) {
|
|
@@ -2110,9 +2093,7 @@ function deepMerge(source0, ...sources) {
|
|
|
2110
2093
|
if (!(key in source))
|
|
2111
2094
|
continue;
|
|
2112
2095
|
const sourceValue = source[key];
|
|
2113
|
-
//
|
|
2114
|
-
// overwrite
|
|
2115
|
-
// *********
|
|
2096
|
+
// Handle basic types (number, string, boolean) - always overwrite
|
|
2116
2097
|
switch (typeof sourceValue) {
|
|
2117
2098
|
case 'number':
|
|
2118
2099
|
case 'string':
|
|
@@ -2120,28 +2101,23 @@ function deepMerge(source0, ...sources) {
|
|
|
2120
2101
|
target[key] = sourceValue;
|
|
2121
2102
|
continue;
|
|
2122
2103
|
}
|
|
2104
|
+
// If target is a basic type, overwrite with deep clone of source
|
|
2123
2105
|
if (isBasicType(target[key])) {
|
|
2124
2106
|
target[key] = deepClone(sourceValue);
|
|
2125
2107
|
continue;
|
|
2126
2108
|
}
|
|
2109
|
+
// Handle Color instances - clone the source color
|
|
2127
2110
|
if (sourceValue instanceof Color) {
|
|
2128
2111
|
target[key] = sourceValue.clone();
|
|
2129
2112
|
continue;
|
|
2130
2113
|
}
|
|
2114
|
+
// If both are simple objects, merge them recursively
|
|
2131
2115
|
if (isSimpleObject(target[key]) && isSimpleObject(sourceValue)) {
|
|
2132
2116
|
target[key] = deepMerge(target[key], sourceValue);
|
|
2133
2117
|
continue;
|
|
2134
2118
|
}
|
|
2135
|
-
//
|
|
2136
|
-
|
|
2137
|
-
// *********
|
|
2138
|
-
if (isSimpleObject(target[key]) && isSimpleObject(sourceValue)) {
|
|
2139
|
-
target[key] = deepMerge(target[key], sourceValue);
|
|
2140
|
-
continue;
|
|
2141
|
-
}
|
|
2142
|
-
console.log('target[key]:', target[key]);
|
|
2143
|
-
console.log('source[key]:', source[key]);
|
|
2144
|
-
throw Error('unpredicted case');
|
|
2119
|
+
// Incompatible types - throw error
|
|
2120
|
+
throw new Error(`deepMerge: Cannot merge incompatible types for key "${String(key)}" (target: ${typeof target[key]}, source: ${typeof sourceValue})`);
|
|
2145
2121
|
}
|
|
2146
2122
|
}
|
|
2147
2123
|
return target;
|
|
@@ -2183,7 +2159,7 @@ function decorate(layers, rules, recolor) {
|
|
|
2183
2159
|
const regExpString = id.replace(/[^a-z_:-]/g, (c) => {
|
|
2184
2160
|
if (c === '*')
|
|
2185
2161
|
return '[a-z_-]*';
|
|
2186
|
-
throw new Error(
|
|
2162
|
+
throw new Error(`decorator: Invalid character ${JSON.stringify(c)} in layer ID pattern "${id}". Only alphanumeric, underscore, colon, hyphen, and asterisk are allowed.`);
|
|
2187
2163
|
});
|
|
2188
2164
|
const regExp = new RegExp(`^${regExpString}$`, 'i');
|
|
2189
2165
|
return layerIds.filter((layerId) => regExp.test(layerId));
|
|
@@ -2233,27 +2209,24 @@ function decorate(layers, rules, recolor) {
|
|
|
2233
2209
|
value = processExpression(value);
|
|
2234
2210
|
break;
|
|
2235
2211
|
default:
|
|
2236
|
-
throw new Error(`
|
|
2212
|
+
throw new Error(`decorator: Unknown property value type "${propertyDef.valueType}" for key "${key}" on layer type "${layer.type}". This may indicate a MapLibre property definition mismatch.`);
|
|
2237
2213
|
}
|
|
2238
2214
|
switch (propertyDef.parent) {
|
|
2239
2215
|
case 'layer':
|
|
2240
|
-
// @ts-expect-error: too complex to handle
|
|
2241
2216
|
layer[key] = value;
|
|
2242
2217
|
break;
|
|
2243
2218
|
case 'layout':
|
|
2244
2219
|
if (!layer.layout)
|
|
2245
2220
|
layer.layout = {};
|
|
2246
|
-
// @ts-expect-error: too complex to handle
|
|
2247
2221
|
layer.layout[key] = value;
|
|
2248
2222
|
break;
|
|
2249
2223
|
case 'paint':
|
|
2250
2224
|
if (!layer.paint)
|
|
2251
2225
|
layer.paint = {};
|
|
2252
|
-
// @ts-expect-error: too complex to handle
|
|
2253
2226
|
layer.paint[key] = value;
|
|
2254
2227
|
break;
|
|
2255
2228
|
default:
|
|
2256
|
-
throw new Error(`
|
|
2229
|
+
throw new Error(`decorator: Unknown property parent "${propertyDef.parent}" for key "${key}" on layer type "${layer.type}". Expected "layer", "layout", or "paint".`);
|
|
2257
2230
|
}
|
|
2258
2231
|
});
|
|
2259
2232
|
}
|
|
@@ -2264,12 +2237,12 @@ function decorate(layers, rules, recolor) {
|
|
|
2264
2237
|
const color = recolor.do(value);
|
|
2265
2238
|
return color.asString();
|
|
2266
2239
|
}
|
|
2267
|
-
throw new Error(`
|
|
2240
|
+
throw new Error(`decorator.processColor: Expected a color string or Color instance, but got ${typeof value}. Value: ${JSON.stringify(value)}`);
|
|
2268
2241
|
}
|
|
2269
2242
|
function processFont(value) {
|
|
2270
2243
|
if (typeof value === 'string')
|
|
2271
2244
|
return [value];
|
|
2272
|
-
throw new Error(`
|
|
2245
|
+
throw new Error(`decorator.processFont: Expected a font name string, but got ${typeof value}. Value: ${JSON.stringify(value)}`);
|
|
2273
2246
|
}
|
|
2274
2247
|
function processExpression(value, cbValue) {
|
|
2275
2248
|
if (typeof value === 'object') {
|
|
@@ -2316,7 +2289,7 @@ function getDefaultRecolorFlags() {
|
|
|
2316
2289
|
* Checks if the given options object contains any active recolor transformations.
|
|
2317
2290
|
* @param opt The recolor options to validate.
|
|
2318
2291
|
*/
|
|
2319
|
-
function
|
|
2292
|
+
function hasActiveRecolorOptions(opt) {
|
|
2320
2293
|
if (!opt)
|
|
2321
2294
|
return false;
|
|
2322
2295
|
return (opt.invertBrightness ||
|
|
@@ -2337,14 +2310,20 @@ class CachedRecolor {
|
|
|
2337
2310
|
skip;
|
|
2338
2311
|
opt;
|
|
2339
2312
|
cache;
|
|
2313
|
+
parsedTintColor;
|
|
2314
|
+
parsedBlendColor;
|
|
2340
2315
|
/**
|
|
2341
2316
|
* Creates a cached recolor instance.
|
|
2342
2317
|
* @param opt Optional recolor options.
|
|
2343
2318
|
*/
|
|
2344
2319
|
constructor(opt) {
|
|
2345
|
-
this.skip = !
|
|
2320
|
+
this.skip = !hasActiveRecolorOptions(opt);
|
|
2346
2321
|
this.cache = new Map();
|
|
2347
2322
|
this.opt = opt;
|
|
2323
|
+
if (opt?.tint && opt.tintColor != null)
|
|
2324
|
+
this.parsedTintColor = Color.parse(opt.tintColor);
|
|
2325
|
+
if (opt?.blend && opt.blendColor != null)
|
|
2326
|
+
this.parsedBlendColor = Color.parse(opt.blendColor);
|
|
2348
2327
|
}
|
|
2349
2328
|
/**
|
|
2350
2329
|
* Applies cached recoloring to a given color.
|
|
@@ -2357,7 +2336,7 @@ class CachedRecolor {
|
|
|
2357
2336
|
const key = color.asHex();
|
|
2358
2337
|
if (this.cache.has(key))
|
|
2359
2338
|
return this.cache.get(key);
|
|
2360
|
-
const recolored = recolor(color, this.opt);
|
|
2339
|
+
const recolored = recolor(color, this.opt, this.parsedTintColor, this.parsedBlendColor);
|
|
2361
2340
|
this.cache.set(key, recolored);
|
|
2362
2341
|
return recolored;
|
|
2363
2342
|
}
|
|
@@ -2366,10 +2345,12 @@ class CachedRecolor {
|
|
|
2366
2345
|
* Applies the specified recoloring transformations to a single color.
|
|
2367
2346
|
* @param color The original color.
|
|
2368
2347
|
* @param opt Optional recolor options.
|
|
2348
|
+
* @param parsedTintColor Optional pre-parsed tint color to avoid repeated parsing.
|
|
2349
|
+
* @param parsedBlendColor Optional pre-parsed blend color to avoid repeated parsing.
|
|
2369
2350
|
* @returns A new `Color` instance with applied transformations.
|
|
2370
2351
|
*/
|
|
2371
|
-
function recolor(color, opt) {
|
|
2372
|
-
if (!
|
|
2352
|
+
function recolor(color, opt, parsedTintColor, parsedBlendColor) {
|
|
2353
|
+
if (!hasActiveRecolorOptions(opt))
|
|
2373
2354
|
return color;
|
|
2374
2355
|
if (opt.invertBrightness)
|
|
2375
2356
|
color = color.invertLuminosity();
|
|
@@ -2377,16 +2358,16 @@ function recolor(color, opt) {
|
|
|
2377
2358
|
color = color.rotateHue(opt.rotate);
|
|
2378
2359
|
if (opt.saturate)
|
|
2379
2360
|
color = color.saturate(opt.saturate);
|
|
2380
|
-
if (opt.gamma != null && opt.gamma
|
|
2361
|
+
if (opt.gamma != null && opt.gamma !== 1)
|
|
2381
2362
|
color = color.gamma(opt.gamma);
|
|
2382
|
-
if (opt.contrast != null && opt.contrast
|
|
2363
|
+
if (opt.contrast != null && opt.contrast !== 1)
|
|
2383
2364
|
color = color.contrast(opt.contrast);
|
|
2384
2365
|
if (opt.brightness)
|
|
2385
2366
|
color = color.brightness(opt.brightness);
|
|
2386
2367
|
if (opt.tint && opt.tintColor != null)
|
|
2387
|
-
color = color.tint(opt.tint, Color.parse(opt.tintColor));
|
|
2368
|
+
color = color.tint(opt.tint, parsedTintColor ?? Color.parse(opt.tintColor));
|
|
2388
2369
|
if (opt.blend && opt.blendColor != null)
|
|
2389
|
-
color = color.blend(opt.blend, Color.parse(opt.blendColor));
|
|
2370
|
+
color = color.blend(opt.blend, parsedBlendColor ?? Color.parse(opt.blendColor));
|
|
2390
2371
|
return color;
|
|
2391
2372
|
}
|
|
2392
2373
|
|
|
@@ -2478,7 +2459,8 @@ class StyleBuilder {
|
|
|
2478
2459
|
// get shortbread layers
|
|
2479
2460
|
const layerDefinitions = getShortbreadLayers({ language });
|
|
2480
2461
|
let layers = layerDefinitions.map((layer) => {
|
|
2481
|
-
|
|
2462
|
+
const { type, id } = layer;
|
|
2463
|
+
switch (type) {
|
|
2482
2464
|
case 'background':
|
|
2483
2465
|
return layer;
|
|
2484
2466
|
case 'fill':
|
|
@@ -2489,7 +2471,7 @@ class StyleBuilder {
|
|
|
2489
2471
|
...layer,
|
|
2490
2472
|
};
|
|
2491
2473
|
}
|
|
2492
|
-
throw Error(
|
|
2474
|
+
throw new Error(`StyleBuilder: Unknown layer type "${type}" for layer "${id}". Expected "background", "fill", "line", or "symbol".`);
|
|
2493
2475
|
});
|
|
2494
2476
|
// apply layer rules
|
|
2495
2477
|
layers = decorate(layers, layerStyleRules, new CachedRecolor(recolorOptions));
|
|
@@ -2499,7 +2481,7 @@ class StyleBuilder {
|
|
|
2499
2481
|
style.layers = layers;
|
|
2500
2482
|
style.name = 'versatiles-' + this.name.toLowerCase();
|
|
2501
2483
|
style.glyphs = resolveUrl(baseUrl, glyphs);
|
|
2502
|
-
if (typeof sprite
|
|
2484
|
+
if (typeof sprite === 'string') {
|
|
2503
2485
|
style.sprite = [{ id: basename(sprite), url: resolveUrl(baseUrl, sprite) }];
|
|
2504
2486
|
}
|
|
2505
2487
|
else {
|
|
@@ -4061,6 +4043,92 @@ class Empty extends Colorful {
|
|
|
4061
4043
|
}
|
|
4062
4044
|
}
|
|
4063
4045
|
|
|
4046
|
+
function buildSatelliteStyle(options) {
|
|
4047
|
+
options ??= {};
|
|
4048
|
+
const baseUrl = options.baseUrl ?? 'https://tiles.versatiles.org';
|
|
4049
|
+
const rasterTiles = options.rasterTiles ?? [`${baseUrl}/tiles/satellite/{z}/{x}/{y}`];
|
|
4050
|
+
const overlay = options.overlay ?? true;
|
|
4051
|
+
let style;
|
|
4052
|
+
if (overlay) {
|
|
4053
|
+
// Generate graybeard style for overlay
|
|
4054
|
+
style = new Graybeard().build({
|
|
4055
|
+
baseUrl,
|
|
4056
|
+
tiles: options.overlayTiles,
|
|
4057
|
+
language: options.language,
|
|
4058
|
+
});
|
|
4059
|
+
// Filter out background, fill layers, and unwanted layer groups
|
|
4060
|
+
style.layers = style.layers.filter((l) => l.id !== 'background' && l.type !== 'fill' && !/^(land|water|site|airport|tunnel)-/.test(l.id));
|
|
4061
|
+
// Modify remaining layers
|
|
4062
|
+
for (const layer of style.layers) {
|
|
4063
|
+
if (layer.type === 'symbol') {
|
|
4064
|
+
// Bold font, white text, black halo
|
|
4065
|
+
if (layer.layout?.['text-font']) {
|
|
4066
|
+
layer.layout['text-font'] = ['noto_sans_bold'];
|
|
4067
|
+
}
|
|
4068
|
+
if (layer.paint) {
|
|
4069
|
+
layer.paint['text-color'] = '#fff';
|
|
4070
|
+
layer.paint['text-halo-color'] = '#000';
|
|
4071
|
+
if ('text-halo-blur' in layer.paint)
|
|
4072
|
+
layer.paint['text-halo-blur'] = 0;
|
|
4073
|
+
if ('text-halo-width' in layer.paint)
|
|
4074
|
+
layer.paint['text-halo-width'] = 1;
|
|
4075
|
+
}
|
|
4076
|
+
}
|
|
4077
|
+
if (layer.type === 'line' && layer.paint) {
|
|
4078
|
+
// Multiply existing opacity by 0.2
|
|
4079
|
+
const v = layer.paint['line-opacity'];
|
|
4080
|
+
if (v == null) {
|
|
4081
|
+
layer.paint['line-opacity'] = 0.2;
|
|
4082
|
+
}
|
|
4083
|
+
else if (typeof v === 'number') {
|
|
4084
|
+
layer.paint['line-opacity'] = v * 0.2;
|
|
4085
|
+
}
|
|
4086
|
+
else if (typeof v === 'object' && 'stops' in v) {
|
|
4087
|
+
v.stops = v.stops.map((s) => [s[0], s[1] * 0.2]);
|
|
4088
|
+
}
|
|
4089
|
+
}
|
|
4090
|
+
}
|
|
4091
|
+
}
|
|
4092
|
+
else {
|
|
4093
|
+
// Minimal style with no overlay
|
|
4094
|
+
style = { version: 8, sources: {}, layers: [] };
|
|
4095
|
+
}
|
|
4096
|
+
// Build raster paint properties
|
|
4097
|
+
const rasterPaint = {};
|
|
4098
|
+
if (options.rasterOpacity != null)
|
|
4099
|
+
rasterPaint['raster-opacity'] = options.rasterOpacity;
|
|
4100
|
+
if (options.rasterHueRotate != null)
|
|
4101
|
+
rasterPaint['raster-hue-rotate'] = options.rasterHueRotate;
|
|
4102
|
+
if (options.rasterBrightnessMin != null)
|
|
4103
|
+
rasterPaint['raster-brightness-min'] = options.rasterBrightnessMin;
|
|
4104
|
+
if (options.rasterBrightnessMax != null)
|
|
4105
|
+
rasterPaint['raster-brightness-max'] = options.rasterBrightnessMax;
|
|
4106
|
+
if (options.rasterSaturation != null)
|
|
4107
|
+
rasterPaint['raster-saturation'] = options.rasterSaturation;
|
|
4108
|
+
if (options.rasterContrast != null)
|
|
4109
|
+
rasterPaint['raster-contrast'] = options.rasterContrast;
|
|
4110
|
+
// Add raster source
|
|
4111
|
+
style.sources.satellite = {
|
|
4112
|
+
type: 'raster',
|
|
4113
|
+
tiles: rasterTiles,
|
|
4114
|
+
tileSize: 512,
|
|
4115
|
+
attribution: "<a href='https://versatiles.org/sources/'>VersaTiles sources</a>",
|
|
4116
|
+
bounds: [-178.187256, -21.401934, 55.846252, 58.061897],
|
|
4117
|
+
minzoom: 0,
|
|
4118
|
+
maxzoom: 17,
|
|
4119
|
+
};
|
|
4120
|
+
// Add raster layer at bottom
|
|
4121
|
+
style.layers.unshift({
|
|
4122
|
+
id: 'satellite',
|
|
4123
|
+
type: 'raster',
|
|
4124
|
+
source: 'satellite',
|
|
4125
|
+
minzoom: 0,
|
|
4126
|
+
...(Object.keys(rasterPaint).length > 0 ? { paint: rasterPaint } : {}),
|
|
4127
|
+
});
|
|
4128
|
+
style.name = 'versatiles-satellite';
|
|
4129
|
+
return style;
|
|
4130
|
+
}
|
|
4131
|
+
|
|
4064
4132
|
// import styles
|
|
4065
4133
|
function getStyleBuilder(styleBuilder) {
|
|
4066
4134
|
const fn = function (options) {
|
|
@@ -4083,76 +4151,76 @@ getStyleBuilder(Empty);
|
|
|
4083
4151
|
*/
|
|
4084
4152
|
function isTileJSONSpecification(spec) {
|
|
4085
4153
|
if (typeof spec !== 'object' || spec === null) {
|
|
4086
|
-
throw Error(
|
|
4154
|
+
throw new Error(`TileJSON validation: spec must be an object, but got ${typeof spec}`);
|
|
4087
4155
|
}
|
|
4088
4156
|
const obj = spec;
|
|
4089
4157
|
// Common property validation
|
|
4090
4158
|
if (obj.data != null && obj.tilejson !== '3.0.0') {
|
|
4091
|
-
throw Error(
|
|
4159
|
+
throw new Error(`TileJSON validation: spec.tilejson must be "3.0.0", but got "${obj.tilejson}"`);
|
|
4092
4160
|
}
|
|
4093
4161
|
if (obj.attribution != null && typeof obj.attribution !== 'string') {
|
|
4094
|
-
throw Error(
|
|
4162
|
+
throw new Error(`TileJSON validation: spec.attribution must be a string if present, but got ${typeof obj.attribution}`);
|
|
4095
4163
|
}
|
|
4096
4164
|
if (obj.bounds != null) {
|
|
4097
4165
|
if (!Array.isArray(obj.bounds) || obj.bounds.length !== 4 || obj.bounds.some((num) => typeof num !== 'number')) {
|
|
4098
|
-
throw Error(
|
|
4166
|
+
throw new Error(`TileJSON validation: spec.bounds must be an array of four numbers if present, but got ${JSON.stringify(obj.bounds)}`);
|
|
4099
4167
|
}
|
|
4100
4168
|
const a = obj.bounds;
|
|
4101
4169
|
if (a[0] < -180 || a[0] > 180)
|
|
4102
|
-
throw Error(
|
|
4170
|
+
throw new Error(`TileJSON validation: spec.bounds[0] (longitude) must be between -180 and 180, but got ${a[0]}`);
|
|
4103
4171
|
if (a[1] < -90 || a[1] > 90)
|
|
4104
|
-
throw Error(
|
|
4172
|
+
throw new Error(`TileJSON validation: spec.bounds[1] (latitude) must be between -90 and 90, but got ${a[1]}`);
|
|
4105
4173
|
if (a[2] < -180 || a[2] > 180)
|
|
4106
|
-
throw Error(
|
|
4174
|
+
throw new Error(`TileJSON validation: spec.bounds[2] (longitude) must be between -180 and 180, but got ${a[2]}`);
|
|
4107
4175
|
if (a[3] < -90 || a[3] > 90)
|
|
4108
|
-
throw Error(
|
|
4176
|
+
throw new Error(`TileJSON validation: spec.bounds[3] (latitude) must be between -90 and 90, but got ${a[3]}`);
|
|
4109
4177
|
if (a[0] > a[2])
|
|
4110
|
-
throw Error(
|
|
4178
|
+
throw new Error(`TileJSON validation: spec.bounds[0] must be smaller than spec.bounds[2] (min longitude < max longitude), but got [${a[0]}, ${a[2]}]`);
|
|
4111
4179
|
if (a[1] > a[3])
|
|
4112
|
-
throw Error(
|
|
4180
|
+
throw new Error(`TileJSON validation: spec.bounds[1] must be smaller than spec.bounds[3] (min latitude < max latitude), but got [${a[1]}, ${a[3]}]`);
|
|
4113
4181
|
}
|
|
4114
4182
|
if (obj.center != null) {
|
|
4115
4183
|
if (!Array.isArray(obj.center) || obj.center.length !== 2 || obj.center.some((num) => typeof num !== 'number')) {
|
|
4116
|
-
throw Error(
|
|
4184
|
+
throw new Error(`TileJSON validation: spec.center must be an array of two numbers if present, but got ${JSON.stringify(obj.center)}`);
|
|
4117
4185
|
}
|
|
4118
4186
|
const a = obj.center;
|
|
4119
4187
|
if (a[0] < -180 || a[0] > 180)
|
|
4120
|
-
throw Error(
|
|
4188
|
+
throw new Error(`TileJSON validation: spec.center[0] (longitude) must be between -180 and 180, but got ${a[0]}`);
|
|
4121
4189
|
if (a[1] < -90 || a[1] > 90)
|
|
4122
|
-
throw Error(
|
|
4190
|
+
throw new Error(`TileJSON validation: spec.center[1] (latitude) must be between -90 and 90, but got ${a[1]}`);
|
|
4123
4191
|
}
|
|
4124
4192
|
if (obj.data != null && (!Array.isArray(obj.data) || obj.data.some((url) => typeof url !== 'string'))) {
|
|
4125
|
-
throw Error('spec.data must be an array of strings if present');
|
|
4193
|
+
throw new Error('TileJSON validation: spec.data must be an array of strings if present');
|
|
4126
4194
|
}
|
|
4127
4195
|
if (obj.description != null && typeof obj.description !== 'string') {
|
|
4128
|
-
throw Error(
|
|
4196
|
+
throw new Error(`TileJSON validation: spec.description must be a string if present, but got ${typeof obj.description}`);
|
|
4129
4197
|
}
|
|
4130
4198
|
if (obj.fillzoom != null && (typeof obj.fillzoom !== 'number' || obj.fillzoom < 0)) {
|
|
4131
|
-
throw Error(
|
|
4199
|
+
throw new Error(`TileJSON validation: spec.fillzoom must be a positive integer if present, but got ${obj.fillzoom}`);
|
|
4132
4200
|
}
|
|
4133
4201
|
if (obj.grids != null && (!Array.isArray(obj.grids) || obj.grids.some((url) => typeof url !== 'string'))) {
|
|
4134
|
-
throw Error('spec.grids must be an array of strings if present');
|
|
4202
|
+
throw new Error('TileJSON validation: spec.grids must be an array of strings if present');
|
|
4135
4203
|
}
|
|
4136
4204
|
if (obj.legend != null && typeof obj.legend !== 'string') {
|
|
4137
|
-
throw Error(
|
|
4205
|
+
throw new Error(`TileJSON validation: spec.legend must be a string if present, but got ${typeof obj.legend}`);
|
|
4138
4206
|
}
|
|
4139
4207
|
if (obj.minzoom != null && (typeof obj.minzoom !== 'number' || obj.minzoom < 0)) {
|
|
4140
|
-
throw Error(
|
|
4208
|
+
throw new Error(`TileJSON validation: spec.minzoom must be a positive integer if present, but got ${obj.minzoom}`);
|
|
4141
4209
|
}
|
|
4142
4210
|
if (obj.maxzoom != null && (typeof obj.maxzoom !== 'number' || obj.maxzoom < 0)) {
|
|
4143
|
-
throw Error(
|
|
4211
|
+
throw new Error(`TileJSON validation: spec.maxzoom must be a positive integer if present, but got ${obj.maxzoom}`);
|
|
4144
4212
|
}
|
|
4145
4213
|
if (obj.name != null && typeof obj.name !== 'string') {
|
|
4146
|
-
throw Error(
|
|
4214
|
+
throw new Error(`TileJSON validation: spec.name must be a string if present, but got ${typeof obj.name}`);
|
|
4147
4215
|
}
|
|
4148
4216
|
if (obj.scheme != null && obj.scheme !== 'xyz' && obj.scheme !== 'tms') {
|
|
4149
|
-
throw Error(
|
|
4217
|
+
throw new Error(`TileJSON validation: spec.scheme must be "tms" or "xyz" if present, but got "${obj.scheme}"`);
|
|
4150
4218
|
}
|
|
4151
4219
|
if (obj.template != null && typeof obj.template !== 'string') {
|
|
4152
|
-
throw Error(
|
|
4220
|
+
throw new Error(`TileJSON validation: spec.template must be a string if present, but got ${typeof obj.template}`);
|
|
4153
4221
|
}
|
|
4154
4222
|
if (!Array.isArray(obj.tiles) || obj.tiles.length === 0 || obj.tiles.some((url) => typeof url !== 'string')) {
|
|
4155
|
-
throw Error('spec.tiles must be
|
|
4223
|
+
throw new Error('TileJSON validation: spec.tiles must be a non-empty array of strings');
|
|
4156
4224
|
}
|
|
4157
4225
|
return true;
|
|
4158
4226
|
}
|
|
@@ -4181,8 +4249,7 @@ function guessStyle(tileJSON, options) {
|
|
|
4181
4249
|
const { baseUrl } = options;
|
|
4182
4250
|
tileJSON.tiles = tileJSON.tiles.map((url) => resolveUrl(baseUrl, url));
|
|
4183
4251
|
}
|
|
4184
|
-
if (!isTileJSONSpecification(tileJSON))
|
|
4185
|
-
;
|
|
4252
|
+
if (!isTileJSONSpecification(tileJSON)) ;
|
|
4186
4253
|
let style;
|
|
4187
4254
|
if (isRasterTileJSONSpecification(tileJSON)) {
|
|
4188
4255
|
style = getRasterStyle(tileJSON);
|
|
@@ -4445,7 +4512,8 @@ const styles = {
|
|
|
4445
4512
|
graybeard,
|
|
4446
4513
|
shadow,
|
|
4447
4514
|
neutrino,
|
|
4515
|
+
satellite: buildSatelliteStyle,
|
|
4448
4516
|
};
|
|
4449
4517
|
|
|
4450
|
-
export { Color, colorful, eclipse, graybeard, guessStyle, neutrino, shadow, styles };
|
|
4518
|
+
export { Color, colorful, eclipse, graybeard, guessStyle, neutrino, buildSatelliteStyle as satellite, shadow, styles };
|
|
4451
4519
|
//# sourceMappingURL=index.js.map
|