stipple-maplibre 0.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/LICENSE +21 -0
- package/README.md +289 -0
- package/dist/index.d.ts +605 -0
- package/dist/index.global.js +1787 -0
- package/dist/index.js +1762 -0
- package/package.json +66 -0
- package/schema/pattern-metadata-v1.schema.json +173 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 François Blanchard
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
<picture><source media="(prefers-color-scheme: dark)" srcset="./assets/stipple-wordmark-dark.svg"><source media="(prefers-color-scheme: light)" srcset="./assets/stipple-wordmark-light.svg"><img alt="Stipple" src="./assets/stipple-wordmark-light.svg" width="154" height="40"></picture>
|
|
2
|
+
|
|
3
|
+
A focused library and visual playground for designing, generating, and
|
|
4
|
+
installing fill patterns in
|
|
5
|
+
[MapLibre GL JS](https://maplibre.org/maplibre-gl-js/docs/).
|
|
6
|
+
|
|
7
|
+
It supports geometric patterns, repeated text, and SVG symbols. Patterns can
|
|
8
|
+
be configured in JavaScript or prepared in the playground and exported as
|
|
9
|
+
MapLibre code.
|
|
10
|
+
|
|
11
|
+
<p align="center"><strong><a href="https://stipple.pages.dev/">Run the playground</a></strong></p>
|
|
12
|
+
|
|
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
|
+
## What problem does it solve?
|
|
16
|
+
|
|
17
|
+
MapLibre already has a `fill-pattern` property that repeats an image inside a
|
|
18
|
+
polygon. That image still has to be created, made seamless, registered on the
|
|
19
|
+
map, and kept at an appropriate resolution when the display or style changes.
|
|
20
|
+
|
|
21
|
+
Stipple generates this image from a small set of pattern parameters. It draws
|
|
22
|
+
the tile in the browser, installs it with MapLibre's public `addImage` API,
|
|
23
|
+
and provides the corresponding `fill-pattern` value. It does not patch or
|
|
24
|
+
fork MapLibre.
|
|
25
|
+
|
|
26
|
+
## What can I make with it?
|
|
27
|
+
|
|
28
|
+
- Geometric fills: solid, stipple, hatches, crosshatch, grid, and dots.
|
|
29
|
+
- Font fills: repeat a letter, an abbreviation, or a short bit of text.
|
|
30
|
+
- SVG fills: repeat one of the bundled symbols or bring your own SVG.
|
|
31
|
+
- A background colour and polygon outline to go with the pattern.
|
|
32
|
+
|
|
33
|
+
You can change the colour, opacity, spacing, weight, angle, scale, and layout.
|
|
34
|
+
Font fills also let you choose the typeface, style, and letter spacing. SVG
|
|
35
|
+
fills can use regular rows, offset rows, or a more natural-looking seeded
|
|
36
|
+
distribution.
|
|
37
|
+
|
|
38
|
+
The playground includes 34 SVG motifs covering vegetation, trees,
|
|
39
|
+
agriculture, water, terrain, land use, and simple shapes. The same seed always
|
|
40
|
+
produces the same arrangement.
|
|
41
|
+
|
|
42
|
+
<!-- Add a small gallery here: geometric, font, SVG, and custom SVG. -->
|
|
43
|
+
|
|
44
|
+
## But can’t I just ask AI to do this?
|
|
45
|
+
|
|
46
|
+
Fair point. But Stipple can fit into that workflow.
|
|
47
|
+
|
|
48
|
+
Whether you build maps by writing code yourself or with the help of AI,
|
|
49
|
+
the playground gives you direct visual control over the result.
|
|
50
|
+
Instead of refining a pattern through a back-and-forth series of prompts
|
|
51
|
+
and corrections, you can adjust it interactively until it looks exactly
|
|
52
|
+
the way you want.
|
|
53
|
+
|
|
54
|
+
Once you're happy with the result, export the corresponding MapLibre code
|
|
55
|
+
or Stipple configuration and use it directly in your project, or feed it
|
|
56
|
+
back into your AI-assisted workflow.
|
|
57
|
+
|
|
58
|
+
## Run the playground
|
|
59
|
+
|
|
60
|
+
[Open the Stipple playground](https://stipple.pages.dev/). No installation or
|
|
61
|
+
coding is required. Start with the included sample polygons or import your own
|
|
62
|
+
data, customize the fills visually, and export the result. Files are processed
|
|
63
|
+
locally in the browser and are not uploaded to a server.
|
|
64
|
+
|
|
65
|
+
### Run it locally
|
|
66
|
+
|
|
67
|
+
To work from the source repository, install the dependencies and build the
|
|
68
|
+
library:
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
npm install
|
|
72
|
+
npm run build
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Then open the local [`demo/index.html`](./demo/index.html) file in your
|
|
76
|
+
browser.
|
|
77
|
+
|
|
78
|
+
## Supported file formats
|
|
79
|
+
|
|
80
|
+
The basic input is GeoJSON. The playground also understands GeoPackage,
|
|
81
|
+
GeoParquet, Shapefile (loose or zipped), FlatGeobuf, KML, GPX, GML, DXF, and
|
|
82
|
+
other common vector formats.
|
|
83
|
+
|
|
84
|
+
Polygon and MultiPolygon features are supported. If a file contains several
|
|
85
|
+
polygon parts, each one can have its own style. Non-polygon features are
|
|
86
|
+
ignored.
|
|
87
|
+
|
|
88
|
+
Imports are limited to 200 MB per dataset and the first 64 polygon parts.
|
|
89
|
+
Formats with a known CRS are reprojected to WGS84 in the browser.
|
|
90
|
+
|
|
91
|
+
## Installation
|
|
92
|
+
|
|
93
|
+
The npm package is being prepared for its first release and is not published
|
|
94
|
+
yet. Once it is available, install it alongside MapLibre GL JS:
|
|
95
|
+
|
|
96
|
+
```sh
|
|
97
|
+
npm install stipple-maplibre maplibre-gl
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
`maplibre-gl` is a peer dependency. Stipple currently supports MapLibre GL JS
|
|
101
|
+
versions 4, 5, and 6.
|
|
102
|
+
|
|
103
|
+
## Basic usage
|
|
104
|
+
|
|
105
|
+
This example adds a 45-degree hatch texture to an existing GeoJSON source
|
|
106
|
+
called `my-polygons`:
|
|
107
|
+
|
|
108
|
+
```js
|
|
109
|
+
import maplibregl from "maplibre-gl";
|
|
110
|
+
import { syncPatternTexture } from "stipple-maplibre";
|
|
111
|
+
|
|
112
|
+
const map = new maplibregl.Map({
|
|
113
|
+
container: "map",
|
|
114
|
+
style: "https://tiles.openfreemap.org/styles/liberty",
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
map.on("load", () => {
|
|
118
|
+
syncPatternTexture(map, {
|
|
119
|
+
imageId: "my-hatches",
|
|
120
|
+
pattern: "hachures",
|
|
121
|
+
size: 16,
|
|
122
|
+
color: "#2c6a5b",
|
|
123
|
+
weight: 2,
|
|
124
|
+
angle: 45,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
map.addLayer({
|
|
128
|
+
id: "my-pattern-layer",
|
|
129
|
+
type: "fill",
|
|
130
|
+
source: "my-polygons",
|
|
131
|
+
paint: {
|
|
132
|
+
"fill-pattern": "my-hatches",
|
|
133
|
+
"fill-opacity": 1,
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
The generated texture is registered as `my-hatches`, and that same name is
|
|
140
|
+
used in the layer's `fill-pattern` property.
|
|
141
|
+
|
|
142
|
+
## SVG patterns
|
|
143
|
+
|
|
144
|
+
Pass Stipple an SVG string and choose how the symbols should be spread out:
|
|
145
|
+
|
|
146
|
+
```js
|
|
147
|
+
import { installSvgPatternFill } from "stipple-maplibre";
|
|
148
|
+
|
|
149
|
+
await installSvgPatternFill(map, {
|
|
150
|
+
imageId: "grass-pattern",
|
|
151
|
+
svg: grassSvgMarkup,
|
|
152
|
+
seed: "parcel-42",
|
|
153
|
+
distribution: "natural",
|
|
154
|
+
minSpacing: 4,
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
You can then use `grass-pattern` as the layer's `fill-pattern`. The playground
|
|
159
|
+
also accepts pasted SVG markup or an uploaded `.svg` file.
|
|
160
|
+
|
|
161
|
+
## Font patterns
|
|
162
|
+
|
|
163
|
+
Font fills are rasterized after the requested web font is ready:
|
|
164
|
+
|
|
165
|
+
```js
|
|
166
|
+
import { installFontPatternFill } from "stipple-maplibre";
|
|
167
|
+
|
|
168
|
+
await installFontPatternFill(map, {
|
|
169
|
+
imageId: "vineyard-letters",
|
|
170
|
+
text: "V",
|
|
171
|
+
fontFamily: "'Source Serif 4', serif",
|
|
172
|
+
fontSize: 20,
|
|
173
|
+
fontStyle: "italic",
|
|
174
|
+
horizontalSpacing: 26,
|
|
175
|
+
verticalSpacing: 22,
|
|
176
|
+
rotationDeg: -20,
|
|
177
|
+
stagger: true,
|
|
178
|
+
color: "#315f2f",
|
|
179
|
+
});
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Use `vineyard-letters` as the layer's `fill-pattern` in the same way.
|
|
183
|
+
|
|
184
|
+
## From the playground to MapLibre
|
|
185
|
+
|
|
186
|
+
The playground can export:
|
|
187
|
+
|
|
188
|
+
- ready-to-use MapLibre code;
|
|
189
|
+
- a reusable Stipple configuration;
|
|
190
|
+
- a MapLibre style document;
|
|
191
|
+
- a bundle containing the generated pattern images.
|
|
192
|
+
|
|
193
|
+
For code-driven styles, `buildStyleFragment` creates the background, pattern,
|
|
194
|
+
and outline layers together. The resulting style metadata carries the pattern
|
|
195
|
+
recipe. After the style loads, `installPatternFills` reads those recipes and
|
|
196
|
+
installs every texture the map needs:
|
|
197
|
+
|
|
198
|
+
```js
|
|
199
|
+
import { installPatternFills } from "stipple-maplibre";
|
|
200
|
+
|
|
201
|
+
map.on("load", async () => {
|
|
202
|
+
await installPatternFills(map, map.getStyle());
|
|
203
|
+
});
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
If your application replaces styles while it is running, use one observer to
|
|
207
|
+
keep the patterns installed:
|
|
208
|
+
|
|
209
|
+
```js
|
|
210
|
+
import { observePatternFills } from "stipple-maplibre";
|
|
211
|
+
|
|
212
|
+
const patterns = observePatternFills(map);
|
|
213
|
+
map.on("load", () => patterns.refresh());
|
|
214
|
+
|
|
215
|
+
// When the map or component is removed:
|
|
216
|
+
patterns.dispose();
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Are exported styles normal MapLibre JSON?
|
|
220
|
+
|
|
221
|
+
The layers are normal MapLibre layers, with one additional runtime step:
|
|
222
|
+
generated textures cannot live inside plain style JSON. Stipple stores their
|
|
223
|
+
recipes in versioned layer metadata, then recreates and installs the images
|
|
224
|
+
when the style loads.
|
|
225
|
+
|
|
226
|
+
MapLibre, Maputnik, and MapLibre Native do not interpret the
|
|
227
|
+
`maplibre-pattern-fills:v1` metadata on their own. Call `installPatternFills`,
|
|
228
|
+
or export the generated PNG bundle when a runtime dependency is not suitable.
|
|
229
|
+
|
|
230
|
+
The metadata format is documented in
|
|
231
|
+
[`schema/pattern-metadata-v1.schema.json`](./schema/pattern-metadata-v1.schema.json).
|
|
232
|
+
|
|
233
|
+
## Rendering details
|
|
234
|
+
|
|
235
|
+
### Zoom behaviour
|
|
236
|
+
|
|
237
|
+
Two scaling modes are available:
|
|
238
|
+
|
|
239
|
+
- **Screen scale** keeps the pattern the same visual size on screen.
|
|
240
|
+
- **Ground scale** makes it behave like something printed on the map itself.
|
|
241
|
+
|
|
242
|
+
Ground-scaled patterns use several texture sizes and let MapLibre blend
|
|
243
|
+
between them. That avoids the sudden jump you normally get at integer zoom
|
|
244
|
+
levels. Stipple patterns also keep the same deterministic layout while they
|
|
245
|
+
scale, so dots and symbols do not reshuffle on every zoom.
|
|
246
|
+
|
|
247
|
+
By default, textures follow the display pixel ratio, capped at 2x. You can
|
|
248
|
+
also force a softer 1x texture when that suits the map better.
|
|
249
|
+
|
|
250
|
+
### Symbols at polygon edges
|
|
251
|
+
|
|
252
|
+
A repeating `fill-pattern` is always clipped at the polygon boundary. That is
|
|
253
|
+
how MapLibre works.
|
|
254
|
+
|
|
255
|
+
Stipple also includes an experimental `installSvgIconScatter` API. Instead of
|
|
256
|
+
drawing one repeating texture, it places individual symbols only where the
|
|
257
|
+
whole icon fits inside the polygon. It is useful when clipped trees, houses,
|
|
258
|
+
or other recognizable symbols would look wrong. This API may still change
|
|
259
|
+
before version 1.0.
|
|
260
|
+
|
|
261
|
+
## Security and browser support
|
|
262
|
+
|
|
263
|
+
If you let people provide their own SVG, treat that markup as untrusted input.
|
|
264
|
+
Apply the same validation and Content Security Policy rules you use elsewhere
|
|
265
|
+
in your application.
|
|
266
|
+
|
|
267
|
+
SVG rasterization uses a temporary `blob:` image URL, so a restrictive CSP
|
|
268
|
+
must allow `blob:` in `img-src`.
|
|
269
|
+
|
|
270
|
+
The package targets modern evergreen browsers and Node.js 18 or newer. SVG
|
|
271
|
+
and font rasterization need a browser; the geometric pattern engine also runs
|
|
272
|
+
in Node.
|
|
273
|
+
|
|
274
|
+
## Repository structure
|
|
275
|
+
|
|
276
|
+
- `src/engine` draws patterns, scatters points, and calculates zoom scaling.
|
|
277
|
+
It has no MapLibre dependency.
|
|
278
|
+
- `src/maplibre` connects those pieces to MapLibre GL JS.
|
|
279
|
+
- `demo/` contains the playground, its motif catalogue, and its interface.
|
|
280
|
+
- `schema/` contains the exported metadata schema.
|
|
281
|
+
|
|
282
|
+
## Project status
|
|
283
|
+
|
|
284
|
+
Version `0.1.0` is functional and being prepared for its first npm release.
|
|
285
|
+
The whole-symbol scatter API is the only part currently marked experimental.
|
|
286
|
+
|
|
287
|
+
## Licence
|
|
288
|
+
|
|
289
|
+
MIT. See [LICENSE](./LICENSE).
|