@versatiles/svelte 1.0.1 → 1.0.2

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.
Files changed (78) hide show
  1. package/dist/components/BBoxMap/AutoComplete.svelte +116 -89
  2. package/dist/components/BBoxMap/BBoxMap.svelte +50 -40
  3. package/dist/components/BasicMap/BasicMap.svelte +59 -34
  4. package/dist/components/LocatorMap/LocatorMap.svelte +67 -60
  5. package/dist/components/MapEditor/MapEditor.svelte +35 -80
  6. package/dist/components/MapEditor/components/Editor.svelte +53 -0
  7. package/dist/components/MapEditor/{Editor.svelte.d.ts → components/Editor.svelte.d.ts} +1 -1
  8. package/dist/components/MapEditor/components/EditorFill.svelte +28 -0
  9. package/dist/components/MapEditor/components/EditorFill.svelte.d.ts +7 -0
  10. package/dist/components/MapEditor/components/EditorStroke.svelte +28 -0
  11. package/dist/components/MapEditor/components/EditorStroke.svelte.d.ts +7 -0
  12. package/dist/components/MapEditor/components/EditorSymbol.svelte +43 -0
  13. package/dist/components/MapEditor/components/EditorSymbol.svelte.d.ts +7 -0
  14. package/dist/components/MapEditor/components/Sidebar.svelte +179 -0
  15. package/dist/components/MapEditor/components/Sidebar.svelte.d.ts +8 -0
  16. package/dist/components/MapEditor/components/SymbolSelector.svelte +118 -0
  17. package/dist/components/MapEditor/components/SymbolSelector.svelte.d.ts +8 -0
  18. package/dist/components/MapEditor/lib/__mocks__/cursor.d.ts +5 -0
  19. package/dist/components/MapEditor/lib/__mocks__/cursor.js +6 -0
  20. package/dist/components/MapEditor/lib/__mocks__/geometry_manager.d.ts +22 -0
  21. package/dist/components/MapEditor/lib/__mocks__/geometry_manager.js +21 -0
  22. package/dist/components/MapEditor/lib/__mocks__/map.d.ts +36 -0
  23. package/dist/components/MapEditor/lib/__mocks__/map.js +26 -0
  24. package/dist/components/MapEditor/lib/cursor.d.ts +9 -0
  25. package/dist/components/MapEditor/lib/cursor.js +31 -0
  26. package/dist/components/MapEditor/lib/element/abstract.d.ts +21 -0
  27. package/dist/components/MapEditor/lib/element/abstract.js +39 -0
  28. package/dist/components/MapEditor/lib/element/abstract_path.d.ts +11 -0
  29. package/dist/components/MapEditor/lib/element/abstract_path.js +79 -0
  30. package/dist/components/MapEditor/lib/element/line.d.ts +16 -0
  31. package/dist/components/MapEditor/lib/element/line.js +53 -0
  32. package/dist/components/MapEditor/lib/element/marker.d.ts +18 -0
  33. package/dist/components/MapEditor/lib/element/marker.js +62 -0
  34. package/dist/components/MapEditor/lib/element/polygon.d.ts +17 -0
  35. package/dist/components/MapEditor/lib/element/polygon.js +63 -0
  36. package/dist/components/MapEditor/lib/element/types.d.ts +11 -0
  37. package/dist/components/MapEditor/lib/geometry_manager.d.ts +20 -10
  38. package/dist/components/MapEditor/lib/geometry_manager.js +158 -57
  39. package/dist/components/MapEditor/lib/map_layer/abstract.d.ts +30 -0
  40. package/dist/components/MapEditor/lib/map_layer/abstract.js +90 -0
  41. package/dist/components/MapEditor/lib/map_layer/fill.d.ts +24 -0
  42. package/dist/components/MapEditor/lib/map_layer/fill.js +104 -0
  43. package/dist/components/MapEditor/lib/map_layer/line.d.ts +20 -0
  44. package/dist/components/MapEditor/lib/map_layer/line.js +90 -0
  45. package/dist/components/MapEditor/lib/map_layer/symbol.d.ts +19 -0
  46. package/dist/components/MapEditor/lib/map_layer/symbol.js +123 -0
  47. package/dist/components/MapEditor/lib/{types.d.ts → map_layer/types.d.ts} +7 -15
  48. package/dist/components/MapEditor/lib/map_layer/types.js +1 -0
  49. package/dist/components/MapEditor/lib/state/reader.d.ts +17 -0
  50. package/dist/components/MapEditor/lib/state/reader.js +161 -0
  51. package/dist/components/MapEditor/lib/state/types.d.ts +20 -0
  52. package/dist/components/MapEditor/lib/state/types.js +1 -0
  53. package/dist/components/MapEditor/lib/state/writer.d.ts +17 -0
  54. package/dist/components/MapEditor/lib/state/writer.js +178 -0
  55. package/dist/components/MapEditor/lib/symbols.d.ts +16 -0
  56. package/dist/components/MapEditor/lib/symbols.js +173 -0
  57. package/dist/components/MapEditor/lib/utils.d.ts +8 -1
  58. package/dist/components/MapEditor/lib/utils.js +33 -2
  59. package/dist/utils/draw/bbox.d.ts +1 -0
  60. package/dist/utils/draw/bbox.js +1 -1
  61. package/package.json +17 -17
  62. package/dist/components/MapEditor/Editor.svelte +0 -25
  63. package/dist/components/MapEditor/EditorLine.svelte +0 -27
  64. package/dist/components/MapEditor/EditorLine.svelte.d.ts +0 -7
  65. package/dist/components/MapEditor/EditorMarker.svelte +0 -42
  66. package/dist/components/MapEditor/EditorMarker.svelte.d.ts +0 -7
  67. package/dist/components/MapEditor/editor.scss +0 -16
  68. package/dist/components/MapEditor/lib/element_abstract.d.ts +0 -20
  69. package/dist/components/MapEditor/lib/element_abstract.js +0 -58
  70. package/dist/components/MapEditor/lib/element_line.d.ts +0 -14
  71. package/dist/components/MapEditor/lib/element_line.js +0 -76
  72. package/dist/components/MapEditor/lib/element_marker.d.ts +0 -20
  73. package/dist/components/MapEditor/lib/element_marker.js +0 -191
  74. package/dist/components/MapEditor/lib/map_layer.d.ts +0 -14
  75. package/dist/components/MapEditor/lib/map_layer.js +0 -61
  76. package/dist/utils/sprite_library.d.ts +0 -19
  77. package/dist/utils/sprite_library.js +0 -30
  78. /package/dist/components/MapEditor/lib/{types.js → element/types.js} +0 -0
@@ -1,21 +1,30 @@
1
1
  import { get, writable } from 'svelte/store';
2
- import { MarkerElement } from './element_marker.js';
3
- import { LineElement } from './element_line.js';
2
+ import { MarkerElement } from './element/marker.js';
3
+ import { LineElement } from './element/line.js';
4
+ import { PolygonElement } from './element/polygon.js';
5
+ import { Cursor } from './cursor.js';
6
+ import { StateWriter } from './state/writer.js';
7
+ import { StateReader } from './state/reader.js';
8
+ import { SymbolLibrary } from './symbols.js';
4
9
  export class GeometryManager {
5
10
  elements;
6
11
  map;
7
- activeElement = writable(undefined);
8
- selection_nodes;
12
+ selectedElement = writable(undefined);
9
13
  canvas;
14
+ cursor;
15
+ symbolLibrary;
16
+ selectionNodes;
10
17
  constructor(map) {
11
18
  this.elements = writable([]);
12
19
  this.map = map;
13
20
  this.canvas = this.map.getCanvasContainer();
21
+ this.cursor = new Cursor(this.canvas);
22
+ this.symbolLibrary = new SymbolLibrary(map);
14
23
  map.addSource('selection_nodes', {
15
24
  type: 'geojson',
16
25
  data: { type: 'FeatureCollection', features: [] }
17
26
  });
18
- this.selection_nodes = map.getSource('selection_nodes');
27
+ this.selectionNodes = map.getSource('selection_nodes');
19
28
  map.addLayer({
20
29
  id: 'selection_nodes',
21
30
  source: 'selection_nodes',
@@ -24,54 +33,62 @@ export class GeometryManager {
24
33
  paint: {
25
34
  'circle-color': '#ffffff',
26
35
  'circle-opacity': ['get', 'opacity'],
27
- 'circle-radius': 4,
36
+ 'circle-radius': 3,
28
37
  'circle-stroke-color': '#000000',
29
38
  'circle-stroke-opacity': ['get', 'opacity'],
30
- 'circle-stroke-width': 1.5
39
+ 'circle-stroke-width': 1
31
40
  }
32
41
  });
33
42
  map.on('mousedown', 'selection_nodes', (e) => {
34
- const element = get(this.activeElement);
43
+ const element = get(this.selectedElement);
35
44
  if (element == undefined)
36
45
  return;
37
46
  const feature = map.queryRenderedFeatures(e.point, { layers: ['selection_nodes'] })[0];
38
- const updateSelectionNode = element.getSelectionNodeUpdater(feature.properties);
39
- if (updateSelectionNode == undefined)
47
+ const selectedNode = element.getSelectionNodeUpdater(feature.properties);
48
+ if (selectedNode == undefined)
40
49
  return;
50
+ // @ts-expect-error ensure that the event is ignored by other layers
51
+ e.ignore = true;
41
52
  e.preventDefault();
42
- const onMove = (e) => {
43
- e.preventDefault();
44
- this.canvas.style.cursor = 'grabbing';
45
- updateSelectionNode(e.lngLat.lng, e.lngLat.lat);
46
- this.drawSelectionNodes(element.getSelectionNodes());
47
- };
48
- const onUp = () => {
49
- map.off('mousemove', onMove);
50
- this.canvas.style.cursor = 'default';
51
- };
52
- map.once('mouseup', onUp);
53
- map.on('mousemove', onMove);
53
+ if (e.originalEvent.shiftKey) {
54
+ selectedNode.delete();
55
+ this.drawSelectionNodes();
56
+ this.saveState();
57
+ }
58
+ else {
59
+ const onMove = (e) => {
60
+ e.preventDefault();
61
+ selectedNode.update(e.lngLat.lng, e.lngLat.lat);
62
+ this.drawSelectionNodes();
63
+ };
64
+ map.on('mousemove', onMove);
65
+ map.once('mouseup', () => {
66
+ this.saveState();
67
+ map.off('mousemove', onMove);
68
+ });
69
+ }
54
70
  });
55
- map.on('mouseover', 'selection_nodes', () => (this.canvas.style.cursor = 'move'));
56
- map.on('mouseout', 'selection_nodes', () => (this.canvas.style.cursor = 'default'));
71
+ map.on('mouseenter', 'selection_nodes', () => this.cursor.precise(true));
72
+ map.on('mouseleave', 'selection_nodes', () => this.cursor.precise(false));
57
73
  map.on('click', (e) => {
58
- this.setActiveElement(undefined);
74
+ if (!e.originalEvent.shiftKey)
75
+ this.selectElement(undefined);
59
76
  e.preventDefault();
60
77
  });
78
+ map.on('moveend', () => this.saveState());
79
+ const hash = location.hash.slice(1);
80
+ if (hash)
81
+ this.loadState(hash);
61
82
  }
62
- setActiveElement(element) {
63
- if (element) {
64
- if (!get(this.elements).includes(element))
65
- throw new Error('Element not in list');
66
- this.drawSelectionNodes(element.getSelectionNodes());
67
- }
68
- else {
69
- this.drawSelectionNodes([]);
70
- }
71
- this.activeElement.set(element);
83
+ selectElement(element) {
84
+ const elements = get(this.elements);
85
+ elements.forEach((e) => e.select(e == element));
86
+ this.selectedElement.set(element);
87
+ this.drawSelectionNodes();
72
88
  }
73
- drawSelectionNodes(nodes) {
74
- this.selection_nodes.setData({
89
+ drawSelectionNodes() {
90
+ const nodes = get(this.selectedElement)?.getSelectionNodes() ?? [];
91
+ this.selectionNodes.setData({
75
92
  type: 'FeatureCollection',
76
93
  features: nodes.map((n) => ({
77
94
  type: 'Feature',
@@ -80,38 +97,122 @@ export class GeometryManager {
80
97
  }))
81
98
  });
82
99
  }
100
+ getState() {
101
+ const center = this.map.getCenter();
102
+ return {
103
+ map: {
104
+ point: [center.lng, center.lat],
105
+ zoom: this.map.getZoom()
106
+ },
107
+ elements: get(this.elements).map((element) => element.getState())
108
+ };
109
+ }
110
+ async saveState() {
111
+ const writer = new StateWriter();
112
+ writer.writeObject(this.getState());
113
+ location.hash = await writer.getBase64compressed();
114
+ }
115
+ async loadState(hash) {
116
+ if (!hash)
117
+ return;
118
+ try {
119
+ const reader = await StateReader.fromBase64compressed(hash);
120
+ const state = reader.readObject();
121
+ if (!state)
122
+ return;
123
+ if (state.map?.zoom)
124
+ this.map.setZoom(state.map.zoom);
125
+ if (state.map?.point) {
126
+ this.map.setCenter({ lng: state.map.point[0], lat: state.map.point[1] });
127
+ }
128
+ if (state.elements) {
129
+ this.elements.set(state.elements.map((element) => {
130
+ switch (element.type) {
131
+ case 'marker':
132
+ return MarkerElement.fromState(this, element);
133
+ case 'line':
134
+ return LineElement.fromState(this, element);
135
+ case 'polygon':
136
+ return PolygonElement.fromState(this, element);
137
+ default:
138
+ throw new Error('Unknown element type');
139
+ }
140
+ }));
141
+ }
142
+ }
143
+ catch (error) {
144
+ console.error(error);
145
+ }
146
+ }
83
147
  getElement(index) {
84
148
  return get(this.elements)[index];
85
149
  }
86
- getNewMarker() {
87
- const element = new MarkerElement(this, this.newName('Marker '));
150
+ addNewMarker() {
151
+ const element = new MarkerElement(this);
88
152
  this.addElement(element);
89
153
  return element;
90
154
  }
91
- getNewLine() {
92
- const element = new LineElement(this, this.newName('Line '));
155
+ addNewLine() {
156
+ const element = new LineElement(this);
157
+ this.addElement(element);
158
+ return element;
159
+ }
160
+ addNewPolygon() {
161
+ const element = new PolygonElement(this);
93
162
  this.addElement(element);
94
163
  return element;
95
164
  }
96
165
  addElement(element) {
97
166
  this.elements.update((elements) => [...elements, element]);
167
+ this.saveState();
98
168
  }
99
- newName(prefix) {
100
- const set = new Set();
101
- const elements = get(this.elements);
102
- elements.forEach((e) => {
103
- const name = e.name;
104
- if (!name.startsWith(prefix))
105
- return;
106
- const index = name.substring(prefix.length);
107
- if (!/^[0-9]+/.test(index))
108
- return;
109
- set.add(parseInt(index, 10));
110
- });
111
- for (let i = 1; i <= elements.length + 1; i++) {
112
- if (!set.has(i))
113
- return prefix + i;
169
+ deleteElement(element) {
170
+ this.elements.update((elements) => elements.filter((e) => e !== element));
171
+ if (get(this.selectedElement) === element)
172
+ this.selectElement(undefined);
173
+ this.saveState();
174
+ }
175
+ getGeoJSON() {
176
+ const center = this.map.getCenter();
177
+ return {
178
+ type: 'FeatureCollection',
179
+ map: {
180
+ center: [center.lng, center.lat],
181
+ zoom: this.map.getZoom()
182
+ },
183
+ features: get(this.elements).map((element) => element.getFeature(true))
184
+ };
185
+ }
186
+ addGeoJSON(geojson) {
187
+ if ('map' in geojson) {
188
+ const { map } = geojson;
189
+ if (typeof map.zoom === 'number') {
190
+ this.map.setZoom(map.zoom);
191
+ }
192
+ if (Array.isArray(map.center)) {
193
+ const [lng, lat] = map.center;
194
+ if (typeof lng === 'number' && typeof lat === 'number') {
195
+ this.map.setCenter({ lng, lat });
196
+ }
197
+ }
198
+ }
199
+ for (const feature of geojson.features) {
200
+ let element;
201
+ switch (feature.geometry.type) {
202
+ case 'Point':
203
+ element = MarkerElement.fromGeoJSON(this, feature);
204
+ break;
205
+ case 'LineString':
206
+ element = LineElement.fromGeoJSON(this, feature);
207
+ break;
208
+ case 'Polygon':
209
+ element = PolygonElement.fromGeoJSON(this, feature);
210
+ break;
211
+ default:
212
+ throw new Error(`Unknown geometry type "${feature.geometry.type}"`);
213
+ }
214
+ this.addElement(element);
114
215
  }
115
- throw new Error('Unreachable');
216
+ this.saveState();
116
217
  }
117
218
  }
@@ -0,0 +1,30 @@
1
+ import type { LayerFill, LayerLine, LayerSymbol } from './types.js';
2
+ import type { GeometryManager } from '../geometry_manager.js';
3
+ import type { StateObject } from '../state/types.js';
4
+ type LayerSpec = LayerFill | LayerLine | LayerSymbol;
5
+ type Events = 'click' | 'mousedown' | 'mousemove' | 'mouseup';
6
+ type MouseEventHandler = (event: maplibregl.MapMouseEvent) => void;
7
+ export declare abstract class MapLayer<T extends LayerSpec> {
8
+ private layout;
9
+ private paint;
10
+ protected readonly id: string;
11
+ readonly manager: GeometryManager;
12
+ protected readonly map: maplibregl.Map;
13
+ eventHandlers: Map<Events, MouseEventHandler[]>;
14
+ isSelected: boolean;
15
+ constructor(manager: GeometryManager, id: string);
16
+ addLayer(source: string, type: 'symbol' | 'line' | 'fill', layout: T['layout'], paint: T['paint']): void;
17
+ on(event: Events, handler: MouseEventHandler): void;
18
+ off(event: Events, handler: MouseEventHandler): void;
19
+ private dispatchEvent;
20
+ private addEvents;
21
+ setPaint(paint: T['paint']): void;
22
+ updatePaint<K extends keyof T['paint'], V extends T['paint'][K]>(key: K, value: V): void;
23
+ setLayout(layout: T['layout']): void;
24
+ updateLayout<K extends keyof T['layout'], V extends T['layout'][K]>(key: K, value: V): void;
25
+ destroy(): void;
26
+ abstract getState(): StateObject | undefined;
27
+ abstract getGeoJSONProperties(): GeoJSON.GeoJsonProperties;
28
+ abstract setGeoJSONProperties(properties: GeoJSON.GeoJsonProperties): void;
29
+ }
30
+ export {};
@@ -0,0 +1,90 @@
1
+ import { Color } from '@versatiles/style';
2
+ export class MapLayer {
3
+ layout = {};
4
+ paint = {};
5
+ id;
6
+ manager;
7
+ map;
8
+ eventHandlers = new Map();
9
+ isSelected = false;
10
+ constructor(manager, id) {
11
+ this.manager = manager;
12
+ this.map = manager.map;
13
+ this.id = id;
14
+ }
15
+ addLayer(source, type, layout, paint) {
16
+ this.layout = layout;
17
+ this.paint = paint;
18
+ this.map.addLayer({ id: this.id, source, type, layout, paint }, 'selection_nodes');
19
+ this.addEvents();
20
+ }
21
+ on(event, handler) {
22
+ if (!this.eventHandlers.has(event))
23
+ this.eventHandlers.set(event, []);
24
+ this.eventHandlers.get(event).push(handler);
25
+ }
26
+ off(event, handler) {
27
+ if (!this.eventHandlers.has(event))
28
+ return;
29
+ const handlers = this.eventHandlers.get(event);
30
+ this.eventHandlers.set(event, handlers.filter((h) => h !== handler));
31
+ }
32
+ dispatchEvent(event, e) {
33
+ const handlers = this.eventHandlers.get(event);
34
+ if (handlers)
35
+ handlers.forEach((handler) => handler(e));
36
+ }
37
+ addEvents() {
38
+ this.map.on('mouseenter', this.id, () => {
39
+ if (this.isSelected)
40
+ this.manager.cursor.grab(true);
41
+ this.manager.cursor.hover(true);
42
+ });
43
+ this.map.on('mouseleave', this.id, () => {
44
+ if (this.isSelected)
45
+ this.manager.cursor.grab(false);
46
+ this.manager.cursor.hover(false);
47
+ });
48
+ this.map.on('click', this.id, (e) => {
49
+ this.dispatchEvent('click', e);
50
+ if (this.isSelected)
51
+ this.manager.cursor.grab(true);
52
+ this.manager.cursor.hover(true);
53
+ e.preventDefault();
54
+ });
55
+ this.map.on('mousedown', this.id, (e) => this.dispatchEvent('mousedown', e));
56
+ this.map.on('mouseup', this.id, (e) => this.dispatchEvent('mouseup', e));
57
+ this.map.on('mousemove', this.id, (e) => this.dispatchEvent('mousemove', e));
58
+ }
59
+ setPaint(paint) {
60
+ if (paint === undefined)
61
+ return;
62
+ const keys = new Set(Object.keys(paint).concat(Object.keys(this.paint)));
63
+ for (const key of keys.values())
64
+ this.updatePaint(key, paint[key]);
65
+ }
66
+ updatePaint(key, value) {
67
+ if (value instanceof Color)
68
+ value = value.asString();
69
+ if (this.paint[key] == value)
70
+ return;
71
+ this.map.setPaintProperty(this.id, key, value);
72
+ this.paint[key] = value;
73
+ }
74
+ setLayout(layout) {
75
+ if (layout === undefined)
76
+ return;
77
+ const keys = new Set(Object.keys(layout).concat(Object.keys(this.layout)));
78
+ for (const key of keys.values())
79
+ this.updateLayout(key, layout[key]);
80
+ }
81
+ updateLayout(key, value) {
82
+ if (this.layout[key] == value)
83
+ return;
84
+ this.map.setLayoutProperty(this.id, key, value);
85
+ this.layout[key] = value;
86
+ }
87
+ destroy() {
88
+ this.map.removeLayer(this.id);
89
+ }
90
+ }
@@ -0,0 +1,24 @@
1
+ import type { LayerFill } from './types.js';
2
+ import { MapLayer } from './abstract.js';
3
+ import type { GeometryManager } from '../geometry_manager.js';
4
+ import type { StateObject } from '../state/types.js';
5
+ interface Fill {
6
+ xf: number;
7
+ yf: number;
8
+ pattern: string;
9
+ }
10
+ export declare const fillPatterns: Map<number, {
11
+ name: string;
12
+ fill: Fill | undefined;
13
+ }>;
14
+ export declare class MapLayerFill extends MapLayer<LayerFill> {
15
+ color: import("svelte/store").Writable<string>;
16
+ opacity: import("svelte/store").Writable<number>;
17
+ pattern: import("svelte/store").Writable<number>;
18
+ constructor(manager: GeometryManager, id: string, source: string);
19
+ getState(): StateObject | undefined;
20
+ setState(state: StateObject): void;
21
+ getGeoJSONProperties(): GeoJSON.GeoJsonProperties;
22
+ setGeoJSONProperties(properties: GeoJSON.GeoJsonProperties): void;
23
+ }
24
+ export {};
@@ -0,0 +1,104 @@
1
+ import { get, writable } from 'svelte/store';
2
+ import { MapLayer } from './abstract.js';
3
+ import { Color } from '@versatiles/style';
4
+ import { removeDefaultFields } from '../utils.js';
5
+ const size = 32;
6
+ export const fillPatterns = new Map([
7
+ [0, { name: 'solid', fill: undefined }],
8
+ [1, { name: 'diagonal', fill: { xf: 1, yf: 1, pattern: '00002552' } }],
9
+ [2, { name: 'diagonal-thin', fill: { xf: 1, yf: 1, pattern: '0252' } }]
10
+ ]);
11
+ export class MapLayerFill extends MapLayer {
12
+ color = writable('#ff0000');
13
+ opacity = writable(1);
14
+ pattern = writable(0);
15
+ constructor(manager, id, source) {
16
+ super(manager, id);
17
+ this.addLayer(source, 'fill', {}, {
18
+ 'fill-color': Color.parse(get(this.color)).asHex(),
19
+ 'fill-opacity': get(this.opacity)
20
+ });
21
+ const updatePattern = () => {
22
+ const fill = fillPatterns.get(get(this.pattern))?.fill ?? undefined;
23
+ const color = Color.parse(get(this.color));
24
+ if (fill == null) {
25
+ this.updatePaint('fill-color', color);
26
+ this.updatePaint('fill-pattern', undefined);
27
+ return;
28
+ }
29
+ const { xf, yf, pattern: p } = fill;
30
+ const alpha = p.split('').map((c) => parseInt(c, 10) * 51);
31
+ const length = alpha.length;
32
+ const data = new Uint8ClampedArray(size * size * 4);
33
+ const c = color.asRGB().asArray();
34
+ for (let y = 0; y < size; y++) {
35
+ for (let x = 0; x < size; x++) {
36
+ const v = x * xf + y * yf;
37
+ const i = (y * size + x) * 4;
38
+ data[i] = c[0];
39
+ data[i + 1] = c[1];
40
+ data[i + 2] = c[2];
41
+ data[i + 3] = alpha[v % length];
42
+ }
43
+ }
44
+ const name = 'fill-pattern-' + this.id;
45
+ if (this.map.hasImage(name))
46
+ this.map.removeImage(name);
47
+ this.map.addImage(name, { width: size, height: size, data });
48
+ this.updatePaint('fill-pattern', name);
49
+ };
50
+ this.color.subscribe(() => {
51
+ updatePattern();
52
+ this.manager.saveState();
53
+ });
54
+ this.pattern.subscribe(() => {
55
+ updatePattern();
56
+ this.manager.saveState();
57
+ });
58
+ this.opacity.subscribe((value) => {
59
+ this.updatePaint('fill-opacity', value);
60
+ this.manager.saveState();
61
+ });
62
+ }
63
+ getState() {
64
+ return removeDefaultFields({
65
+ color: get(this.color),
66
+ opacity: get(this.opacity),
67
+ pattern: get(this.pattern)
68
+ }, {
69
+ color: '#ff0000',
70
+ opacity: 1,
71
+ pattern: 0
72
+ });
73
+ }
74
+ setState(state) {
75
+ if (state.color)
76
+ this.color.set(state.color);
77
+ if (state.opacity)
78
+ this.opacity.set(state.opacity);
79
+ if (state.pattern)
80
+ this.pattern.set(state.pattern);
81
+ }
82
+ getGeoJSONProperties() {
83
+ return {
84
+ 'fill-color': get(this.color),
85
+ 'fill-opacity': get(this.opacity),
86
+ 'fill-pattern': fillPatterns.get(get(this.pattern))?.name
87
+ };
88
+ }
89
+ setGeoJSONProperties(properties) {
90
+ if (properties == null)
91
+ return;
92
+ if (properties['fill-color'])
93
+ this.color.set(properties['fill-color']);
94
+ if (properties['fill-opacity'])
95
+ this.opacity.set(properties['fill-opacity']);
96
+ if (properties['fill-pattern']) {
97
+ const pattern = fillPatterns
98
+ .entries()
99
+ .find(([, { name }]) => name === properties['fill-pattern']);
100
+ if (pattern)
101
+ this.pattern.set(pattern[0]);
102
+ }
103
+ }
104
+ }
@@ -0,0 +1,20 @@
1
+ import type { LayerLine } from './types.js';
2
+ import { MapLayer } from './abstract.js';
3
+ import type { GeometryManager } from '../geometry_manager.js';
4
+ import type { StateObject } from '../state/types.js';
5
+ export declare const dashArrays: Map<number, {
6
+ name: string;
7
+ array: number[] | undefined;
8
+ }>;
9
+ export declare class MapLayerLine extends MapLayer<LayerLine> {
10
+ color: import("svelte/store").Writable<string>;
11
+ dashed: import("svelte/store").Writable<number>;
12
+ visible: import("svelte/store").Writable<boolean>;
13
+ width: import("svelte/store").Writable<number>;
14
+ dashArray: import("svelte/store").Readable<number[]>;
15
+ constructor(manager: GeometryManager, id: string, source: string);
16
+ getState(): StateObject | undefined;
17
+ setState(state: StateObject): void;
18
+ getGeoJSONProperties(): GeoJSON.GeoJsonProperties;
19
+ setGeoJSONProperties(properties: GeoJSON.GeoJsonProperties): void;
20
+ }
@@ -0,0 +1,90 @@
1
+ import { derived, get, writable } from 'svelte/store';
2
+ import { MapLayer } from './abstract.js';
3
+ import { Color } from '@versatiles/style';
4
+ import { removeDefaultFields } from '../utils.js';
5
+ export const dashArrays = new Map([
6
+ [0, { name: 'solid', array: [100] }],
7
+ [1, { name: 'dashed', array: [2, 4] }],
8
+ [2, { name: 'dotted', array: [0, 2] }]
9
+ ]);
10
+ export class MapLayerLine extends MapLayer {
11
+ color = writable('#ff0000');
12
+ dashed = writable(0);
13
+ visible = writable(true);
14
+ width = writable(2);
15
+ dashArray = derived(this.dashed, (dashed) => dashArrays.get(dashed)?.array ?? [100]);
16
+ constructor(manager, id, source) {
17
+ super(manager, id);
18
+ this.addLayer(source, 'line', {
19
+ 'line-cap': 'round',
20
+ 'line-join': 'round',
21
+ visibility: get(this.visible) ? 'visible' : 'none'
22
+ }, {
23
+ 'line-color': Color.parse(get(this.color)).asHex(),
24
+ 'line-dasharray': get(this.dashArray),
25
+ 'line-width': get(this.width)
26
+ });
27
+ this.color.subscribe((v) => {
28
+ this.updatePaint('line-color', Color.parse(v));
29
+ this.manager.saveState();
30
+ });
31
+ this.dashArray.subscribe((v) => {
32
+ this.updatePaint('line-dasharray', v);
33
+ this.manager.saveState();
34
+ });
35
+ this.visible.subscribe((v) => {
36
+ this.updateLayout('visibility', v ? 'visible' : 'none');
37
+ this.manager.saveState();
38
+ });
39
+ this.width.subscribe((v) => {
40
+ this.updatePaint('line-width', v);
41
+ this.manager.saveState();
42
+ });
43
+ }
44
+ getState() {
45
+ return removeDefaultFields({
46
+ color: get(this.color),
47
+ pattern: get(this.dashed),
48
+ visible: get(this.visible),
49
+ width: get(this.width)
50
+ }, {
51
+ color: '#ff0000',
52
+ pattern: 0,
53
+ visible: true,
54
+ width: 2
55
+ });
56
+ }
57
+ setState(state) {
58
+ if (state.color)
59
+ this.color.set(state.color);
60
+ if (state.pattern)
61
+ this.dashed.set(state.pattern);
62
+ if (state.visible)
63
+ this.visible.set(state.visible);
64
+ if (state.width)
65
+ this.width.set(state.width);
66
+ }
67
+ getGeoJSONProperties() {
68
+ return {
69
+ 'stroke-color': get(this.color),
70
+ 'stroke-style': dashArrays.get(get(this.dashed))?.name,
71
+ 'stroke-width': get(this.width),
72
+ 'stroke-visibility': get(this.visible)
73
+ };
74
+ }
75
+ setGeoJSONProperties(properties) {
76
+ if (properties == null)
77
+ return;
78
+ if (properties['stroke-color'])
79
+ this.color.set(properties['stroke-color']);
80
+ if (properties['stroke-style']) {
81
+ const dash = dashArrays.entries().find(([, { name }]) => name === properties['stroke-style']);
82
+ if (dash)
83
+ this.dashed.set(dash[0]);
84
+ }
85
+ if (properties['stroke-width'])
86
+ this.width.set(properties['stroke-width']);
87
+ if (properties['stroke-visibility'])
88
+ this.visible.set(properties['stroke-visibility']);
89
+ }
90
+ }
@@ -0,0 +1,19 @@
1
+ import type { LayerSymbol } from './types.js';
2
+ import { MapLayer } from './abstract.js';
3
+ import type { GeometryManager } from '../geometry_manager.js';
4
+ import type { StateObject } from '../state/types.js';
5
+ export declare class MapLayerSymbol extends MapLayer<LayerSymbol> {
6
+ color: import("svelte/store").Writable<string>;
7
+ halo: import("svelte/store").Writable<number>;
8
+ rotate: import("svelte/store").Writable<number>;
9
+ size: import("svelte/store").Writable<number>;
10
+ symbolIndex: import("svelte/store").Writable<number>;
11
+ label: import("svelte/store").Writable<string>;
12
+ haloWidth: import("svelte/store").Readable<number>;
13
+ symbolInfo: import("svelte/store").Readable<import("../symbols.js").SymbolInfo>;
14
+ constructor(manager: GeometryManager, id: string, source: string);
15
+ getState(): StateObject | undefined;
16
+ setState(state: StateObject): void;
17
+ getGeoJSONProperties(): GeoJSON.GeoJsonProperties;
18
+ setGeoJSONProperties(properties: GeoJSON.GeoJsonProperties): void;
19
+ }