stipple-maplibre 0.1.1 → 0.3.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 +75 -16
- package/dist/index.d.ts +33 -2
- package/dist/index.global.js +78 -0
- package/dist/index.js +78 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,9 +8,10 @@ It supports geometric patterns, repeated text, and SVG symbols. Patterns can
|
|
|
8
8
|
be configured in JavaScript or prepared in the playground and exported as
|
|
9
9
|
MapLibre code.
|
|
10
10
|
|
|
11
|
-
<
|
|
11
|
+
<img width="1456" alt="Stipple playground showing pattern customization on a MapLibre map" src="./assets/readme/playground.png" />
|
|
12
|
+
|
|
13
|
+
<p align="center"><strong><a href="https://stipple.pages.dev/">➞ Run the playground</a></strong></p>
|
|
12
14
|
|
|
13
|
-
<img width="1456" height="858" alt="Stipple playground showing a patterned polygon on a MapLibre map" src="https://github.com/user-attachments/assets/f7d28a0f-267a-4bd3-b7a2-30b49eabf709" />
|
|
14
15
|
|
|
15
16
|
## What problem does it solve?
|
|
16
17
|
|
|
@@ -20,8 +21,7 @@ map, and kept at an appropriate resolution when the display or style changes.
|
|
|
20
21
|
|
|
21
22
|
Stipple generates this image from a small set of pattern parameters. It draws
|
|
22
23
|
the tile in the browser, installs it with MapLibre's public `addImage` API,
|
|
23
|
-
and provides the corresponding `fill-pattern` value.
|
|
24
|
-
fork MapLibre.
|
|
24
|
+
and provides the corresponding `fill-pattern` value.
|
|
25
25
|
|
|
26
26
|
## What can I make with it?
|
|
27
27
|
|
|
@@ -30,6 +30,47 @@ fork MapLibre.
|
|
|
30
30
|
- SVG fills: repeat one of the bundled symbols or bring your own SVG.
|
|
31
31
|
- A background colour and polygon outline to go with the pattern.
|
|
32
32
|
|
|
33
|
+
### Examples
|
|
34
|
+
|
|
35
|
+
<table>
|
|
36
|
+
<tr>
|
|
37
|
+
<td align="center" width="25%">
|
|
38
|
+
<img src="./assets/readme/example-solid.png" alt="Polygon with a solid fill" width="100%"><br>
|
|
39
|
+
<sub><em>Solid</em></sub>
|
|
40
|
+
</td>
|
|
41
|
+
<td align="center" width="25%">
|
|
42
|
+
<img src="./assets/readme/example-stipple.png" alt="Polygon filled with a stipple pattern" width="100%"><br>
|
|
43
|
+
<sub><em>Stipple</em></sub>
|
|
44
|
+
</td>
|
|
45
|
+
<td align="center" width="25%">
|
|
46
|
+
<img src="./assets/readme/example-hatches.png" alt="Polygon filled with diagonal hatches" width="100%"><br>
|
|
47
|
+
<sub><em>Hatches</em></sub>
|
|
48
|
+
</td>
|
|
49
|
+
<td align="center" width="25%">
|
|
50
|
+
<img src="./assets/readme/example-dots.png" alt="Polygon filled with a regular dot pattern" width="100%"><br>
|
|
51
|
+
<sub><em>Dots</em></sub>
|
|
52
|
+
</td>
|
|
53
|
+
</tr>
|
|
54
|
+
<tr>
|
|
55
|
+
<td align="center" width="25%">
|
|
56
|
+
<img src="./assets/readme/example-grid.png" alt="Polygon filled with a grid pattern" width="100%"><br>
|
|
57
|
+
<sub><em>Grid</em></sub>
|
|
58
|
+
</td>
|
|
59
|
+
<td align="center" width="25%">
|
|
60
|
+
<img src="./assets/readme/example-font-fill.png" alt="Polygon filled with repeated text" width="100%"><br>
|
|
61
|
+
<sub><em>Font fill</em></sub>
|
|
62
|
+
</td>
|
|
63
|
+
<td align="center" width="25%">
|
|
64
|
+
<img src="./assets/readme/example-svg-symbol.png" alt="Polygon filled with a bundled SVG motif" width="100%"><br>
|
|
65
|
+
<sub><em>SVG fill</em></sub>
|
|
66
|
+
</td>
|
|
67
|
+
<td align="center" width="25%">
|
|
68
|
+
<img src="./assets/readme/example-custom-svg.png" alt="Polygon filled with a custom SVG motif" width="100%"><br>
|
|
69
|
+
<sub><em>Custom</em></sub>
|
|
70
|
+
</td>
|
|
71
|
+
</tr>
|
|
72
|
+
</table>
|
|
73
|
+
|
|
33
74
|
You can change the colour, opacity, spacing, weight, angle, scale, and layout.
|
|
34
75
|
Font fills also let you choose the typeface, style, and letter spacing. SVG
|
|
35
76
|
fills can use regular rows, offset rows, or a more natural-looking seeded
|
|
@@ -39,7 +80,6 @@ The playground includes 34 SVG motifs covering vegetation, trees,
|
|
|
39
80
|
agriculture, water, terrain, land use, and simple shapes. The same seed always
|
|
40
81
|
produces the same arrangement.
|
|
41
82
|
|
|
42
|
-
<!-- Add a small gallery here: geometric, font, SVG, and custom SVG. -->
|
|
43
83
|
|
|
44
84
|
## But can’t I just ask AI to do this?
|
|
45
85
|
|
|
@@ -64,16 +104,17 @@ locally in the browser and are not uploaded to a server.
|
|
|
64
104
|
|
|
65
105
|
### Run it locally
|
|
66
106
|
|
|
67
|
-
|
|
68
|
-
|
|
107
|
+
From a clone of this repository, install the development dependencies and
|
|
108
|
+
build Stipple:
|
|
69
109
|
|
|
70
110
|
```sh
|
|
71
111
|
npm install
|
|
72
112
|
npm run build
|
|
73
113
|
```
|
|
74
114
|
|
|
75
|
-
|
|
76
|
-
|
|
115
|
+
The repository itself is the `stipple-maplibre` package, so you do not need to
|
|
116
|
+
install it separately. Then open the local
|
|
117
|
+
[`demo/index.html`](./demo/index.html) file in your browser.
|
|
77
118
|
|
|
78
119
|
## Supported file formats
|
|
79
120
|
|
|
@@ -117,7 +158,7 @@ map.on("load", () => {
|
|
|
117
158
|
syncPatternTexture(map, {
|
|
118
159
|
imageId: "my-hatches",
|
|
119
160
|
pattern: "hachures",
|
|
120
|
-
size:
|
|
161
|
+
size: 33,
|
|
121
162
|
color: "#2c6a5b",
|
|
122
163
|
weight: 2,
|
|
123
164
|
angle: 45,
|
|
@@ -186,12 +227,30 @@ The playground can export:
|
|
|
186
227
|
|
|
187
228
|
- ready-to-use MapLibre code;
|
|
188
229
|
- a reusable Stipple configuration;
|
|
189
|
-
- a
|
|
190
|
-
|
|
230
|
+
- a static bundle containing style layers, generated pattern images, and an
|
|
231
|
+
integration helper.
|
|
232
|
+
|
|
233
|
+
The copied code uses `addPatternFill`, which waits for the map style, installs
|
|
234
|
+
the generated textures, adapts the exported layers to an existing source, and
|
|
235
|
+
adds them in order:
|
|
236
|
+
|
|
237
|
+
```js
|
|
238
|
+
import { addPatternFill } from "stipple-maplibre";
|
|
239
|
+
|
|
240
|
+
await addPatternFill(map, {
|
|
241
|
+
sourceId: "my-polygons",
|
|
242
|
+
sourceLayer: null, // Use the source-layer name for vector tiles.
|
|
243
|
+
pattern: exportedPattern,
|
|
244
|
+
});
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
The source must already exist in the map. An optional `beforeId` places the
|
|
248
|
+
pattern layers below an existing label or symbol layer. The returned
|
|
249
|
+
`layerIds` list contains every layer added to the map.
|
|
191
250
|
|
|
192
|
-
For code-driven styles, `buildStyleFragment` creates the
|
|
193
|
-
and outline layers together. The resulting style metadata
|
|
194
|
-
|
|
251
|
+
For lower-level, code-driven styles, `buildStyleFragment` creates the
|
|
252
|
+
background, pattern, and outline layers together. The resulting style metadata
|
|
253
|
+
carries the pattern recipe. `installPatternFills` reads those recipes and
|
|
195
254
|
installs every texture the map needs:
|
|
196
255
|
|
|
197
256
|
```js
|
|
@@ -280,7 +339,7 @@ in Node.
|
|
|
280
339
|
|
|
281
340
|
## Project status
|
|
282
341
|
|
|
283
|
-
Version `0.
|
|
342
|
+
Version `0.3.0` is available on
|
|
284
343
|
[npm](https://www.npmjs.com/package/stipple-maplibre). The whole-symbol
|
|
285
344
|
scatter API is the only part currently marked experimental.
|
|
286
345
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Map } from 'maplibre-gl';
|
|
1
|
+
import { Map, LayerSpecification } from 'maplibre-gl';
|
|
2
2
|
|
|
3
3
|
type PatternType = "solid" | "stipple" | "hachures" | "cross" | "grid" | "dots";
|
|
4
4
|
/** Tile edge length in px. MapLibre does not require power-of-two fill-pattern images. Larger tiles read as a lower-density pattern. */
|
|
@@ -396,6 +396,37 @@ interface StyleLike {
|
|
|
396
396
|
*/
|
|
397
397
|
declare function installPatternFills(map: Map, style: StyleLike): Promise<void>;
|
|
398
398
|
|
|
399
|
+
interface PatternFillLayerTemplate {
|
|
400
|
+
id: string;
|
|
401
|
+
type: LayerSpecification["type"];
|
|
402
|
+
metadata?: Record<string, unknown>;
|
|
403
|
+
[key: string]: unknown;
|
|
404
|
+
}
|
|
405
|
+
interface PatternFillFragment {
|
|
406
|
+
layers: PatternFillLayerTemplate[];
|
|
407
|
+
}
|
|
408
|
+
interface AddPatternFillOptions {
|
|
409
|
+
/** Id of a source that already exists in the map. */
|
|
410
|
+
sourceId: string;
|
|
411
|
+
/** Omit or use null for GeoJSON. Required for vector tile sources. */
|
|
412
|
+
sourceLayer?: string | null;
|
|
413
|
+
/** Insert every generated layer immediately before this existing layer. */
|
|
414
|
+
beforeId?: string;
|
|
415
|
+
/** Source-neutral layer templates exported by Stipple. */
|
|
416
|
+
pattern: PatternFillFragment;
|
|
417
|
+
}
|
|
418
|
+
interface AddedPatternFill {
|
|
419
|
+
layerIds: string[];
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Installs a Stipple pattern export on an existing MapLibre source.
|
|
423
|
+
*
|
|
424
|
+
* The helper waits for the style, resolves the source placeholders, installs
|
|
425
|
+
* every generated texture, then adds the exported background, fill, and
|
|
426
|
+
* outline layers in order.
|
|
427
|
+
*/
|
|
428
|
+
declare function addPatternFill(map: Map, options: AddPatternFillOptions): Promise<AddedPatternFill>;
|
|
429
|
+
|
|
399
430
|
interface ObservePatternFillsOptions {
|
|
400
431
|
/** Defaults to `map.getStyle()`. Useful when metadata is stored separately. */
|
|
401
432
|
getStyle?: () => StyleLike;
|
|
@@ -602,4 +633,4 @@ interface InstallSvgIconScatterOptions {
|
|
|
602
633
|
*/
|
|
603
634
|
declare function installSvgIconScatter(map: Map, options: InstallSvgIconScatterOptions): Promise<void>;
|
|
604
635
|
|
|
605
|
-
export { type BackgroundFillConfig, type BuildStyleFragmentOptions, type FontPatternDefinition, type FontPatternOptions, type GeoJsonPolygonImport, type GeometricPatternDefinition, type GeometricPatternType, type HachureAngle, type IconScaleMode, type ImportedPolygonFeature, type InstallSvgIconScatterOptions, type MakeTileOptions, type ObservePatternFillsOptions, type OutlineConfig, PATTERN_METADATA_KEY, type PatternDefinition, type PatternFillConfig, type PatternFillObserver, type PatternMetadataV1, type PatternScaleMode, type PatternScaleOptions, type PatternScaleResult, type PatternType, type PatternVariant, type PointFeature, type PointFeatureCollection, type PolygonGeometry, type Ring, type ScatterIconPointsOptions, type ScatterPointsOptions, type ScatteredPoint, type ScreenPatternPhase, type StyleLike, type SvgDistributionMode, type SvgPatternDefinition, type SvgPatternOptions, type SvgScatterLayoutOptions, type SvgStampPlacement, type SyncPatternTextureOptions, type TileContext, type TileImage, type TileSize, buildStyleFragment, createFontPatternDefinition, createFontPatternTile, createMiniContext, createSvgPatternDefinition, createSvgScatterLayout, createSvgScatterTile, hashStringToSeed, importGeoJsonPolygons, installFontPatternFill, installPatternFills, installSvgIconScatter, installSvgPatternFill, makeTile, mulberry32, observePatternFills, parsePatternDefinition, parsePatternMetadata, patternDefinitionId, scalePatternForZoom, scatterIconPoints, scatterPointsInPolygon, screenPatternPhase, serializePatternDefinition, syncPatternTexture };
|
|
636
|
+
export { type AddPatternFillOptions, type AddedPatternFill, type BackgroundFillConfig, type BuildStyleFragmentOptions, type FontPatternDefinition, type FontPatternOptions, type GeoJsonPolygonImport, type GeometricPatternDefinition, type GeometricPatternType, type HachureAngle, type IconScaleMode, type ImportedPolygonFeature, type InstallSvgIconScatterOptions, type MakeTileOptions, type ObservePatternFillsOptions, type OutlineConfig, PATTERN_METADATA_KEY, type PatternDefinition, type PatternFillConfig, type PatternFillFragment, type PatternFillLayerTemplate, type PatternFillObserver, type PatternMetadataV1, type PatternScaleMode, type PatternScaleOptions, type PatternScaleResult, type PatternType, type PatternVariant, type PointFeature, type PointFeatureCollection, type PolygonGeometry, type Ring, type ScatterIconPointsOptions, type ScatterPointsOptions, type ScatteredPoint, type ScreenPatternPhase, type StyleLike, type SvgDistributionMode, type SvgPatternDefinition, type SvgPatternOptions, type SvgScatterLayoutOptions, type SvgStampPlacement, type SyncPatternTextureOptions, type TileContext, type TileImage, type TileSize, addPatternFill, buildStyleFragment, createFontPatternDefinition, createFontPatternTile, createMiniContext, createSvgPatternDefinition, createSvgScatterLayout, createSvgScatterTile, hashStringToSeed, importGeoJsonPolygons, installFontPatternFill, installPatternFills, installSvgIconScatter, installSvgPatternFill, makeTile, mulberry32, observePatternFills, parsePatternDefinition, parsePatternMetadata, patternDefinitionId, scalePatternForZoom, scatterIconPoints, scatterPointsInPolygon, screenPatternPhase, serializePatternDefinition, syncPatternTexture };
|
package/dist/index.global.js
CHANGED
|
@@ -22,6 +22,7 @@ var MaplibrePatternFills = (() => {
|
|
|
22
22
|
var src_exports = {};
|
|
23
23
|
__export(src_exports, {
|
|
24
24
|
PATTERN_METADATA_KEY: () => PATTERN_METADATA_KEY,
|
|
25
|
+
addPatternFill: () => addPatternFill,
|
|
25
26
|
buildStyleFragment: () => buildStyleFragment,
|
|
26
27
|
createFontPatternDefinition: () => createFontPatternDefinition,
|
|
27
28
|
createFontPatternTile: () => createFontPatternTile,
|
|
@@ -1480,6 +1481,83 @@ var MaplibrePatternFills = (() => {
|
|
|
1480
1481
|
await Promise.all(pending);
|
|
1481
1482
|
}
|
|
1482
1483
|
|
|
1484
|
+
// src/maplibre/addPatternFill.ts
|
|
1485
|
+
function nonEmptyString2(value, name) {
|
|
1486
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
1487
|
+
throw new TypeError(`${name} must be a non-empty string`);
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
async function waitForStyle(map) {
|
|
1491
|
+
if (map.getStyle()) return;
|
|
1492
|
+
await new Promise((resolve) => {
|
|
1493
|
+
map.once("style.load", () => resolve());
|
|
1494
|
+
});
|
|
1495
|
+
}
|
|
1496
|
+
async function addPatternFill(map, options) {
|
|
1497
|
+
nonEmptyString2(options.sourceId, "sourceId");
|
|
1498
|
+
if (options.sourceLayer !== void 0 && options.sourceLayer !== null) {
|
|
1499
|
+
nonEmptyString2(options.sourceLayer, "sourceLayer");
|
|
1500
|
+
}
|
|
1501
|
+
if (options.beforeId !== void 0) {
|
|
1502
|
+
nonEmptyString2(options.beforeId, "beforeId");
|
|
1503
|
+
}
|
|
1504
|
+
if (!options.pattern || !Array.isArray(options.pattern.layers)) {
|
|
1505
|
+
throw new TypeError("pattern.layers must be an array");
|
|
1506
|
+
}
|
|
1507
|
+
if (options.pattern.layers.length === 0) {
|
|
1508
|
+
throw new RangeError("pattern.layers must contain at least one layer");
|
|
1509
|
+
}
|
|
1510
|
+
await waitForStyle(map);
|
|
1511
|
+
if (!map.getSource(options.sourceId)) {
|
|
1512
|
+
throw new Error(`MapLibre source "${options.sourceId}" was not found`);
|
|
1513
|
+
}
|
|
1514
|
+
if (options.beforeId && !map.getLayer(options.beforeId)) {
|
|
1515
|
+
throw new Error(`MapLibre layer "${options.beforeId}" was not found`);
|
|
1516
|
+
}
|
|
1517
|
+
const layers = options.pattern.layers.map((template, index) => {
|
|
1518
|
+
if (!template || typeof template !== "object") {
|
|
1519
|
+
throw new TypeError(`pattern.layers[${index}] must be an object`);
|
|
1520
|
+
}
|
|
1521
|
+
nonEmptyString2(template.id, `pattern.layers[${index}].id`);
|
|
1522
|
+
nonEmptyString2(template.type, `pattern.layers[${index}].type`);
|
|
1523
|
+
const layer = {
|
|
1524
|
+
...template,
|
|
1525
|
+
id: template.id.split("<source>").join(options.sourceId),
|
|
1526
|
+
source: options.sourceId
|
|
1527
|
+
};
|
|
1528
|
+
if (options.sourceLayer === void 0 || options.sourceLayer === null) {
|
|
1529
|
+
delete layer["source-layer"];
|
|
1530
|
+
} else {
|
|
1531
|
+
layer["source-layer"] = options.sourceLayer;
|
|
1532
|
+
}
|
|
1533
|
+
return layer;
|
|
1534
|
+
});
|
|
1535
|
+
const layerIds = layers.map((layer) => layer.id);
|
|
1536
|
+
const uniqueIds = new Set(layerIds);
|
|
1537
|
+
if (uniqueIds.size !== layerIds.length) {
|
|
1538
|
+
throw new Error("Pattern fill contains duplicate layer ids");
|
|
1539
|
+
}
|
|
1540
|
+
for (const layerId of layerIds) {
|
|
1541
|
+
if (map.getLayer(layerId)) {
|
|
1542
|
+
throw new Error(`MapLibre layer "${layerId}" already exists`);
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
await installPatternFills(map, { layers });
|
|
1546
|
+
const addedLayerIds = [];
|
|
1547
|
+
try {
|
|
1548
|
+
for (const layer of layers) {
|
|
1549
|
+
map.addLayer(layer, options.beforeId);
|
|
1550
|
+
addedLayerIds.push(layer.id);
|
|
1551
|
+
}
|
|
1552
|
+
} catch (error) {
|
|
1553
|
+
for (const layerId of addedLayerIds.reverse()) {
|
|
1554
|
+
if (map.getLayer(layerId)) map.removeLayer(layerId);
|
|
1555
|
+
}
|
|
1556
|
+
throw error;
|
|
1557
|
+
}
|
|
1558
|
+
return { layerIds };
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1483
1561
|
// src/maplibre/observePatternFills.ts
|
|
1484
1562
|
function observePatternFills(map, options = {}) {
|
|
1485
1563
|
const getStyle = options.getStyle ?? (() => map.getStyle());
|
package/dist/index.js
CHANGED
|
@@ -1429,6 +1429,83 @@ async function installPatternFills(map, style) {
|
|
|
1429
1429
|
await Promise.all(pending);
|
|
1430
1430
|
}
|
|
1431
1431
|
|
|
1432
|
+
// src/maplibre/addPatternFill.ts
|
|
1433
|
+
function nonEmptyString2(value, name) {
|
|
1434
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
1435
|
+
throw new TypeError(`${name} must be a non-empty string`);
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
async function waitForStyle(map) {
|
|
1439
|
+
if (map.getStyle()) return;
|
|
1440
|
+
await new Promise((resolve) => {
|
|
1441
|
+
map.once("style.load", () => resolve());
|
|
1442
|
+
});
|
|
1443
|
+
}
|
|
1444
|
+
async function addPatternFill(map, options) {
|
|
1445
|
+
nonEmptyString2(options.sourceId, "sourceId");
|
|
1446
|
+
if (options.sourceLayer !== void 0 && options.sourceLayer !== null) {
|
|
1447
|
+
nonEmptyString2(options.sourceLayer, "sourceLayer");
|
|
1448
|
+
}
|
|
1449
|
+
if (options.beforeId !== void 0) {
|
|
1450
|
+
nonEmptyString2(options.beforeId, "beforeId");
|
|
1451
|
+
}
|
|
1452
|
+
if (!options.pattern || !Array.isArray(options.pattern.layers)) {
|
|
1453
|
+
throw new TypeError("pattern.layers must be an array");
|
|
1454
|
+
}
|
|
1455
|
+
if (options.pattern.layers.length === 0) {
|
|
1456
|
+
throw new RangeError("pattern.layers must contain at least one layer");
|
|
1457
|
+
}
|
|
1458
|
+
await waitForStyle(map);
|
|
1459
|
+
if (!map.getSource(options.sourceId)) {
|
|
1460
|
+
throw new Error(`MapLibre source "${options.sourceId}" was not found`);
|
|
1461
|
+
}
|
|
1462
|
+
if (options.beforeId && !map.getLayer(options.beforeId)) {
|
|
1463
|
+
throw new Error(`MapLibre layer "${options.beforeId}" was not found`);
|
|
1464
|
+
}
|
|
1465
|
+
const layers = options.pattern.layers.map((template, index) => {
|
|
1466
|
+
if (!template || typeof template !== "object") {
|
|
1467
|
+
throw new TypeError(`pattern.layers[${index}] must be an object`);
|
|
1468
|
+
}
|
|
1469
|
+
nonEmptyString2(template.id, `pattern.layers[${index}].id`);
|
|
1470
|
+
nonEmptyString2(template.type, `pattern.layers[${index}].type`);
|
|
1471
|
+
const layer = {
|
|
1472
|
+
...template,
|
|
1473
|
+
id: template.id.split("<source>").join(options.sourceId),
|
|
1474
|
+
source: options.sourceId
|
|
1475
|
+
};
|
|
1476
|
+
if (options.sourceLayer === void 0 || options.sourceLayer === null) {
|
|
1477
|
+
delete layer["source-layer"];
|
|
1478
|
+
} else {
|
|
1479
|
+
layer["source-layer"] = options.sourceLayer;
|
|
1480
|
+
}
|
|
1481
|
+
return layer;
|
|
1482
|
+
});
|
|
1483
|
+
const layerIds = layers.map((layer) => layer.id);
|
|
1484
|
+
const uniqueIds = new Set(layerIds);
|
|
1485
|
+
if (uniqueIds.size !== layerIds.length) {
|
|
1486
|
+
throw new Error("Pattern fill contains duplicate layer ids");
|
|
1487
|
+
}
|
|
1488
|
+
for (const layerId of layerIds) {
|
|
1489
|
+
if (map.getLayer(layerId)) {
|
|
1490
|
+
throw new Error(`MapLibre layer "${layerId}" already exists`);
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
await installPatternFills(map, { layers });
|
|
1494
|
+
const addedLayerIds = [];
|
|
1495
|
+
try {
|
|
1496
|
+
for (const layer of layers) {
|
|
1497
|
+
map.addLayer(layer, options.beforeId);
|
|
1498
|
+
addedLayerIds.push(layer.id);
|
|
1499
|
+
}
|
|
1500
|
+
} catch (error) {
|
|
1501
|
+
for (const layerId of addedLayerIds.reverse()) {
|
|
1502
|
+
if (map.getLayer(layerId)) map.removeLayer(layerId);
|
|
1503
|
+
}
|
|
1504
|
+
throw error;
|
|
1505
|
+
}
|
|
1506
|
+
return { layerIds };
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1432
1509
|
// src/maplibre/observePatternFills.ts
|
|
1433
1510
|
function observePatternFills(map, options = {}) {
|
|
1434
1511
|
const getStyle = options.getStyle ?? (() => map.getStyle());
|
|
@@ -1734,6 +1811,7 @@ async function installSvgIconScatter(map, options) {
|
|
|
1734
1811
|
}
|
|
1735
1812
|
export {
|
|
1736
1813
|
PATTERN_METADATA_KEY,
|
|
1814
|
+
addPatternFill,
|
|
1737
1815
|
buildStyleFragment,
|
|
1738
1816
|
createFontPatternDefinition,
|
|
1739
1817
|
createFontPatternTile,
|
package/package.json
CHANGED