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 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).