@versatiles/svelte 2.0.0 → 2.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.
Files changed (83) hide show
  1. package/dist/components/BBoxMap/BBoxMap.svelte +23 -12
  2. package/dist/components/BBoxMap/lib/bbox.d.ts +1 -1
  3. package/dist/components/BBoxMap/lib/bbox.js +19 -17
  4. package/dist/components/BasicMap/BasicMap.svelte +22 -9
  5. package/dist/components/BasicMap/BasicMap.svelte.d.ts +2 -1
  6. package/dist/components/MapEditor/MapEditor.svelte +51 -20
  7. package/dist/components/MapEditor/components/Dialog.svelte +92 -0
  8. package/dist/components/MapEditor/components/Dialog.svelte.d.ts +17 -0
  9. package/dist/components/MapEditor/components/DialogFile.svelte +112 -0
  10. package/dist/components/MapEditor/components/DialogFile.svelte.d.ts +6 -0
  11. package/dist/components/MapEditor/components/DialogShare.svelte +216 -0
  12. package/dist/components/MapEditor/components/DialogShare.svelte.d.ts +10 -0
  13. package/dist/components/MapEditor/components/Editor.svelte +16 -14
  14. package/dist/components/MapEditor/components/EditorFill.svelte +35 -3
  15. package/dist/components/MapEditor/components/EditorStroke.svelte +35 -3
  16. package/dist/components/MapEditor/components/EditorSymbol.svelte +85 -8
  17. package/dist/components/MapEditor/components/InputRow.svelte +2 -3
  18. package/dist/components/MapEditor/components/PanelFile.svelte +73 -0
  19. package/dist/components/MapEditor/components/PanelFile.svelte.d.ts +7 -0
  20. package/dist/components/MapEditor/components/PanelSymbolSelector.svelte +82 -0
  21. package/dist/components/MapEditor/components/PanelSymbolSelector.svelte.d.ts +8 -0
  22. package/dist/components/MapEditor/components/Sidebar.svelte +59 -103
  23. package/dist/components/MapEditor/components/Sidebar.svelte.d.ts +3 -3
  24. package/dist/components/MapEditor/components/SidebarPanel.svelte +26 -9
  25. package/dist/components/MapEditor/lib/element/abstract.d.ts +8 -4
  26. package/dist/components/MapEditor/lib/element/abstract.js +11 -2
  27. package/dist/components/MapEditor/lib/element/abstract_path.d.ts +3 -2
  28. package/dist/components/MapEditor/lib/element/abstract_path.js +6 -3
  29. package/dist/components/MapEditor/lib/element/circle.d.ts +25 -0
  30. package/dist/components/MapEditor/lib/element/circle.js +118 -0
  31. package/dist/components/MapEditor/lib/element/line.d.ts +2 -2
  32. package/dist/components/MapEditor/lib/element/line.js +1 -1
  33. package/dist/components/MapEditor/lib/element/marker.d.ts +4 -3
  34. package/dist/components/MapEditor/lib/element/marker.js +2 -2
  35. package/dist/components/MapEditor/lib/element/polygon.d.ts +2 -2
  36. package/dist/components/MapEditor/lib/element/polygon.js +2 -2
  37. package/dist/components/MapEditor/lib/element/types.d.ts +2 -3
  38. package/dist/components/MapEditor/lib/geometry_manager.d.ts +12 -29
  39. package/dist/components/MapEditor/lib/geometry_manager.js +44 -162
  40. package/dist/components/MapEditor/lib/geometry_manager_interactive.d.ts +33 -0
  41. package/dist/components/MapEditor/lib/geometry_manager_interactive.js +102 -0
  42. package/dist/components/MapEditor/lib/map_layer/abstract.d.ts +2 -2
  43. package/dist/components/MapEditor/lib/map_layer/abstract.js +25 -25
  44. package/dist/components/MapEditor/lib/map_layer/fill.js +5 -16
  45. package/dist/components/MapEditor/lib/map_layer/line.js +5 -17
  46. package/dist/components/MapEditor/lib/map_layer/symbol.js +1 -1
  47. package/dist/components/MapEditor/lib/selection.d.ts +11 -0
  48. package/dist/components/MapEditor/lib/selection.js +70 -0
  49. package/dist/components/MapEditor/lib/state/constants.js +5 -6
  50. package/dist/components/MapEditor/lib/state/history.d.ts +14 -0
  51. package/dist/components/MapEditor/lib/state/history.js +53 -0
  52. package/dist/components/MapEditor/lib/state/manager.d.ts +8 -10
  53. package/dist/components/MapEditor/lib/state/manager.js +24 -48
  54. package/dist/components/MapEditor/lib/state/reader.d.ts +6 -4
  55. package/dist/components/MapEditor/lib/state/reader.js +70 -18
  56. package/dist/components/MapEditor/lib/state/types.d.ts +19 -2
  57. package/dist/components/MapEditor/lib/state/utils.d.ts +2 -0
  58. package/dist/components/MapEditor/lib/state/utils.js +12 -0
  59. package/dist/components/MapEditor/lib/state/writer.d.ts +6 -4
  60. package/dist/components/MapEditor/lib/state/writer.js +59 -19
  61. package/dist/components/MapEditor/lib/symbols.d.ts +1 -1
  62. package/dist/components/MapEditor/lib/symbols.js +47 -28
  63. package/dist/components/MapEditor/lib/utils/event_handler.d.ts +10 -0
  64. package/dist/components/MapEditor/lib/utils/event_handler.js +39 -0
  65. package/dist/components/MapEditor/lib/utils/geometry.d.ts +12 -0
  66. package/dist/components/MapEditor/lib/utils/geometry.js +87 -0
  67. package/dist/components/MapEditor/lib/utils/types.d.ts +2 -0
  68. package/dist/components/MapEditor/lib/utils/types.js +1 -0
  69. package/dist/components/MapEditor/style/button.scss +115 -0
  70. package/dist/components/MapEditor/style/index.scss +3 -0
  71. package/dist/components/MapEditor/style/layout.scss +20 -0
  72. package/dist/components/MapEditor/style/other.scss +10 -0
  73. package/dist/utils/location.d.ts +1 -0
  74. package/dist/utils/location.js +181 -0
  75. package/dist/utils/map_style.d.ts +2 -2
  76. package/dist/utils/map_style.js +2 -2
  77. package/package.json +29 -29
  78. package/dist/components/MapEditor/components/SymbolSelector.svelte +0 -110
  79. package/dist/components/MapEditor/components/SymbolSelector.svelte.d.ts +0 -8
  80. package/dist/components/MapEditor/lib/utils.d.ts +0 -6
  81. package/dist/components/MapEditor/lib/utils.js +0 -23
  82. /package/dist/components/MapEditor/lib/{geocoder.d.ts → utils/geocoder.d.ts} +0 -0
  83. /package/dist/components/MapEditor/lib/{geocoder.js → utils/geocoder.js} +0 -0
@@ -0,0 +1,8 @@
1
+ import type { Map as MaplibreMap } from 'maplibre-gl';
2
+ type $$ComponentProps = {
3
+ symbolIndex: number;
4
+ map: MaplibreMap;
5
+ };
6
+ declare const PanelSymbolSelector: import("svelte").Component<$$ComponentProps, {}, "symbolIndex">;
7
+ type PanelSymbolSelector = ReturnType<typeof PanelSymbolSelector>;
8
+ export default PanelSymbolSelector;
@@ -1,14 +1,18 @@
1
1
  <script lang="ts">
2
+ import '../style/index.scss';
2
3
  import Editor from './Editor.svelte';
3
- import type { GeometryManager } from '../lib/geometry_manager.js';
4
4
  import SidebarPanel from './SidebarPanel.svelte';
5
+ import DialogShareMap from './DialogShare.svelte';
6
+ import PanelFile from './PanelFile.svelte';
7
+ import type { GeometryManagerInteractive } from '../lib/geometry_manager_interactive.js';
5
8
 
6
- const { geometryManager, width }: { geometryManager: GeometryManager; width: number } = $props();
9
+ const { geometryManager }: { geometryManager: GeometryManagerInteractive } = $props();
7
10
 
11
+ let panelShareMap: DialogShareMap | null = null;
8
12
  let history = geometryManager.state;
9
- let undoEnabled = $state(geometryManager.state.undoEnabled);
10
- let redoEnabled = $state(geometryManager.state.redoEnabled);
11
- let activeElement = geometryManager.selectedElement;
13
+ let undoEnabled = $state(geometryManager.state.history.undoEnabled);
14
+ let redoEnabled = $state(geometryManager.state.history.redoEnabled);
15
+ let activeElement = geometryManager.selection.selectedElement;
12
16
 
13
17
  function importGeoJSON() {
14
18
  const input = document.createElement('input');
@@ -22,6 +26,7 @@
22
26
  if (!evt.target) return alert('Failed to read file.');
23
27
  const json = JSON.parse(evt.target.result as string);
24
28
  geometryManager.addGeoJSON(json);
29
+ geometryManager.state.log();
25
30
  } catch (error) {
26
31
  console.error(error);
27
32
  return alert('Failed to import GeoJSON. Please check the file format.');
@@ -46,79 +51,76 @@
46
51
  URL.revokeObjectURL(url);
47
52
  }
48
53
 
49
- function getLink() {
50
- const url = new URL(window.location.href);
51
- url.hash = geometryManager.state.getHash();
52
- return url.href;
53
- }
54
-
55
- function copyLink() {
56
- navigator.clipboard.writeText(getLink()).then(
57
- () => alert('Link copied to clipboard!'),
58
- () => alert('Failed to copy link. Please try again.')
59
- );
60
- }
61
-
62
- function copyEmbedCode() {
63
- const html = `<iframe style="width:100%; height:60vh" src="${getLink()}"></iframe>`;
64
- navigator.clipboard.writeText(html).then(
65
- () => alert('Embed code copied to clipboard!'),
66
- () => alert('Failed to copy embed code. Please try again.')
67
- );
54
+ function addNewElement(type: 'marker' | 'line' | 'polygon' | 'circle') {
55
+ activeElement.set(geometryManager.addNewElement(type));
68
56
  }
69
57
  </script>
70
58
 
71
- <div class="sidebar" style="width: {width}px;">
59
+ <div class="sidebar">
72
60
  <div style="margin-bottom: 36px;">
73
- <div class="flex flex-two">
74
- <button onclick={() => history.undo()} disabled={!$undoEnabled}>Undo</button>
75
- <button onclick={() => history.redo()} disabled={!$redoEnabled}>Redo</button>
61
+ <div class="grid2">
62
+ <button class="btn" onclick={() => history.undo()} disabled={!$undoEnabled}>Undo</button>
63
+ <button class="btn" onclick={() => history.redo()} disabled={!$redoEnabled}>Redo</button>
76
64
  </div>
77
- <SidebarPanel title="Share map" open={false}>
78
- <div class="flex flex-one">
79
- <button value="Link" onclick={copyLink}>Link</button>
80
- <button onclick={copyEmbedCode}>Embed Code</button>
65
+ <hr class="thick" />
66
+ <SidebarPanel title="Map">
67
+ <PanelFile manager={geometryManager} />
68
+ <div class="grid1">
69
+ <button class="btn" onclick={() => panelShareMap?.open()}>Share/Embed</button>
70
+ <DialogShareMap bind:this={panelShareMap} bind:state={() => geometryManager.state, () => {}} />
81
71
  </div>
82
72
  </SidebarPanel>
73
+ <hr class="thick" />
83
74
  <SidebarPanel title="Import/Export" open={false}>
84
- <div class="flex flex-one">
85
- <button onclick={importGeoJSON}>Import GeoJSON</button>
86
- <button onclick={exportGeoJSON}>Export GeoJSON</button>
87
- </div>
75
+ <label
76
+ >GeoJSON:
77
+ <div class="grid2">
78
+ <button class="btn" onclick={importGeoJSON}>Import</button>
79
+ <button class="btn" onclick={exportGeoJSON} data-testid="btnExportGeoJSON">Export</button>
80
+ </div>
81
+ </label>
88
82
  </SidebarPanel>
83
+ <hr class="thick" />
89
84
  <SidebarPanel title="Add new">
90
- <div class="flex flex-two">
91
- <button onclick={() => activeElement.set(geometryManager.addNewMarker())}>Marker</button>
92
- <button onclick={() => activeElement.set(geometryManager.addNewLine())}>Line</button>
93
- <button onclick={() => activeElement.set(geometryManager.addNewPolygon())}>Polygon</button>
94
- <button disabled>Circle</button>
85
+ <div class="grid2">
86
+ <button class="btn" onclick={() => addNewElement('marker')}>Marker</button>
87
+ <button class="btn" onclick={() => addNewElement('line')}>Line</button>
88
+ <button class="btn" onclick={() => addNewElement('polygon')}>Polygon</button>
89
+ <button class="btn" onclick={() => addNewElement('circle')}>Circle</button>
95
90
  </div>
96
91
  </SidebarPanel>
92
+ <hr class="thick" />
97
93
  <Editor element={$activeElement} />
94
+ <hr class="thick" />
98
95
  <SidebarPanel title="Actions" disabled={!$activeElement}>
99
- <div class="flex flex-two">
100
- <button onclick={() => $activeElement!.delete()}>Delete</button>
96
+ <div class="grid2">
97
+ <button
98
+ class="btn"
99
+ onclick={() => {
100
+ $activeElement!.delete();
101
+ geometryManager.state.log();
102
+ }}>Delete</button
103
+ >
101
104
  </div>
102
105
  </SidebarPanel>
103
- <SidebarPanel title="Help">
104
- <a
105
- id="github_link"
106
- href="https://github.com/versatiles-org/node-versatiles-svelte/issues"
107
- target="_blank"
108
- aria-label="Repository on GitHub">Report Bugs and Feature Requests as GitHub Issues</a
109
- >
106
+ <hr class="thick" />
107
+ <SidebarPanel title="Help" open={false}>
108
+ <p>
109
+ Submit bugs and feature requests as
110
+ <a
111
+ id="github_link"
112
+ href="https://github.com/versatiles-org/node-versatiles-svelte/issues"
113
+ target="_blank"
114
+ aria-label="Repository on GitHub">GitHub Issues</a
115
+ >
116
+ </p>
110
117
  </SidebarPanel>
111
118
  </div>
112
119
  </div>
113
120
 
114
121
  <style>
115
122
  .sidebar {
116
- --color-btn: #336680;
117
- --color-bg: #ffffff;
118
- --color-text: #000000;
119
- --gap: 10px;
120
-
121
- background-color: rgb(from var(--color-bg) r g b/ 0.7);
123
+ background: color-mix(in srgb, var(--color-bg) 80%, transparent);
122
124
  backdrop-filter: blur(10px);
123
125
  box-sizing: border-box;
124
126
  color: var(--color-text);
@@ -129,56 +131,10 @@
129
131
  position: absolute;
130
132
  right: 0;
131
133
  top: 0;
132
- width: 200px;
133
-
134
- .flex {
135
- --gap: 5px;
136
- align-items: center;
137
- display: flex;
138
- flex-wrap: wrap;
139
- gap: var(--gap);
140
- justify-content: space-between;
141
- margin: var(--gap) 0 var(--gap);
142
- width: 100%;
143
- }
144
- .flex-two button {
145
- flex-grow: 1;
146
- flex-basis: 0;
147
- width: 40%;
148
- }
149
-
150
- .flex-one button {
151
- width: 100%;
152
- }
153
-
154
- button {
155
- background-color: var(--color-btn);
156
- border: 2px solid var(--color-btn);
157
- border-radius: 0.2em;
158
- color: var(--color-bg);
159
- cursor: pointer;
160
- font-weight: 600;
161
- padding: 0.4em 1em;
162
- transition:
163
- background-color 0.1s ease-in-out,
164
- color 0.1s ease-in-out;
165
-
166
- &:not([disabled]):hover {
167
- background-color: var(--color-bg);
168
- color: var(--color-btn);
169
- }
170
- &:disabled {
171
- cursor: not-allowed;
172
- opacity: 0.5;
173
- }
174
- }
134
+ width: 250px;
175
135
  }
176
136
 
177
137
  a {
178
- text-decoration: none;
179
138
  color: var(--fg-color);
180
- &:hover {
181
- text-decoration: underline;
182
- }
183
139
  }
184
140
  </style>
@@ -1,7 +1,7 @@
1
- import type { GeometryManager } from '../lib/geometry_manager.js';
1
+ import '../style/index.scss';
2
+ import type { GeometryManagerInteractive } from '../lib/geometry_manager_interactive.js';
2
3
  type $$ComponentProps = {
3
- geometryManager: GeometryManager;
4
- width: number;
4
+ geometryManager: GeometryManagerInteractive;
5
5
  };
6
6
  declare const Sidebar: import("svelte").Component<$$ComponentProps, {}, "">;
7
7
  type Sidebar = ReturnType<typeof Sidebar>;
@@ -3,9 +3,9 @@
3
3
 
4
4
  let {
5
5
  children,
6
+ disabled = false,
6
7
  open = true,
7
- title,
8
- disabled = false
8
+ title
9
9
  }: { children: Snippet; disabled?: boolean; open?: boolean; title: string } = $props();
10
10
  </script>
11
11
 
@@ -25,7 +25,7 @@
25
25
 
26
26
  <style>
27
27
  .panel {
28
- margin: 1em 0;
28
+ margin: 0;
29
29
 
30
30
  .header {
31
31
  background: none;
@@ -40,10 +40,12 @@
40
40
  width: 100%;
41
41
 
42
42
  .title {
43
- text-transform: uppercase;
44
43
  opacity: 0.8;
44
+ text-transform: uppercase;
45
45
  }
46
+
46
47
  .chevron {
48
+ border-radius: 1em;
47
49
  box-sizing: border-box;
48
50
  display: block;
49
51
  height: 1em;
@@ -51,27 +53,38 @@
51
53
  padding: 0;
52
54
  position: absolute;
53
55
  right: 0.4em;
54
- top: 0;
56
+ top: 0.05em;
55
57
  width: 1em;
58
+
56
59
  svg {
57
60
  fill: var(--color-fg);
58
- width: 100%;
59
61
  height: 100%;
60
62
  rotate: 0deg;
61
- transition: rotate 0.1s linear;
62
63
  transform-origin: 40% 50%;
64
+ transition: rotate 0.1s linear;
65
+ width: 100%;
66
+ }
67
+ }
68
+
69
+ &:focus {
70
+ outline: none;
71
+ .chevron {
72
+ outline: 1px solid var(--color-blue);
73
+ outline-offset: 2px;
63
74
  }
64
75
  }
65
76
  }
77
+
66
78
  .content {
79
+ box-sizing: border-box;
67
80
  height: 0;
81
+ margin: 0;
68
82
  overflow: hidden;
69
83
  padding: 0;
70
- box-sizing: border-box;
71
84
  }
72
85
  }
86
+
73
87
  .open {
74
- margin-bottom: 2em;
75
88
  .header {
76
89
  .chevron {
77
90
  svg {
@@ -79,12 +92,16 @@
79
92
  }
80
93
  }
81
94
  }
95
+
82
96
  .content {
83
97
  height: auto;
98
+ overflow: visible;
84
99
  }
85
100
  }
101
+
86
102
  .disabled {
87
103
  opacity: 0.3;
104
+
88
105
  .content {
89
106
  display: none;
90
107
  }
@@ -1,18 +1,22 @@
1
- import type { ElementPoint, SelectionNode, SelectionNodeUpdater } from './types.js';
1
+ import type { SelectionNode, SelectionNodeUpdater } from './types.js';
2
+ import type { GeoPoint } from '../utils/types.js';
2
3
  import type { GeometryManager } from '../geometry_manager.js';
3
4
  import type { StateElement } from '../state/types.js';
5
+ import type { GeometryManagerInteractive } from '../geometry_manager_interactive.js';
4
6
  export declare abstract class AbstractElement {
5
7
  protected readonly canvas: HTMLElement;
6
- protected readonly manager: GeometryManager;
7
8
  protected readonly map: maplibregl.Map;
8
9
  protected readonly source: maplibregl.GeoJSONSource;
9
10
  protected readonly slug: string;
10
11
  protected isSelected: boolean;
12
+ readonly manager: GeometryManager | GeometryManagerInteractive;
11
13
  readonly sourceId: string;
12
- constructor(manager: GeometryManager);
14
+ constructor(manager: GeometryManager | GeometryManagerInteractive);
13
15
  select(value: boolean): void;
14
- protected randomPositions(length: number): ElementPoint[];
16
+ protected randomPositions(length: number): GeoPoint[];
17
+ protected randomRadius(): number;
15
18
  delete(): void;
19
+ getGeoJSON(): GeoJSON.Feature;
16
20
  abstract destroy(): void;
17
21
  abstract getFeature(includeProperties?: boolean): GeoJSON.Feature;
18
22
  abstract getSelectionNodes(): SelectionNode[];
@@ -1,10 +1,10 @@
1
1
  export class AbstractElement {
2
2
  canvas;
3
- manager;
4
3
  map;
5
4
  source;
6
5
  slug = '_' + Math.random().toString(36).slice(2);
7
6
  isSelected = false;
7
+ manager;
8
8
  sourceId = 'source' + this.slug;
9
9
  constructor(manager) {
10
10
  this.manager = manager;
@@ -21,10 +21,10 @@ export class AbstractElement {
21
21
  }
22
22
  randomPositions(length) {
23
23
  const points = [];
24
+ const bounds = this.map.getBounds();
24
25
  for (let i = 0; i < length; i++) {
25
26
  const xr = Math.random() * 0.5 + 0.25;
26
27
  const yr = Math.random() * 0.5 + 0.25;
27
- const bounds = this.map.getBounds();
28
28
  points.push([
29
29
  (1 - xr) * bounds.getWest() + xr * bounds.getEast(),
30
30
  (1 - yr) * bounds.getSouth() + yr * bounds.getNorth()
@@ -32,8 +32,17 @@ export class AbstractElement {
32
32
  }
33
33
  return points;
34
34
  }
35
+ randomRadius() {
36
+ const bounds = this.map.getBounds();
37
+ const width = bounds.getEast() - bounds.getWest();
38
+ const height = bounds.getNorth() - bounds.getSouth();
39
+ return Math.sqrt(width * height) * 10000;
40
+ }
35
41
  delete() {
36
42
  this.manager.removeElement(this);
37
43
  this.destroy();
38
44
  }
45
+ getGeoJSON() {
46
+ return this.getFeature(true);
47
+ }
39
48
  }
@@ -1,8 +1,9 @@
1
1
  import { AbstractElement } from './abstract.js';
2
2
  import type { GeometryManager } from '../geometry_manager.js';
3
- import type { ElementPath, SelectionNode, SelectionNodeUpdater } from './types.js';
3
+ import type { SelectionNode, SelectionNodeUpdater } from './types.js';
4
+ import type { GeoPath } from '../utils/types.js';
4
5
  export declare abstract class AbstractPathElement extends AbstractElement {
5
- path: ElementPath;
6
+ path: GeoPath;
6
7
  protected readonly isLine: boolean;
7
8
  constructor(manager: GeometryManager, isLine: boolean);
8
9
  protected handleDrag(e: maplibregl.MapMouseEvent): void;
@@ -1,5 +1,5 @@
1
1
  import { AbstractElement } from './abstract.js';
2
- import { getMiddlePoint, lat2mercator, mercator2lat } from '../utils.js';
2
+ import { getMiddlePoint, lat2mercator, mercator2lat } from '../utils/geometry.js';
3
3
  export class AbstractPathElement extends AbstractElement {
4
4
  path = [];
5
5
  isLine;
@@ -20,11 +20,14 @@ export class AbstractPathElement extends AbstractElement {
20
20
  x0 = lng;
21
21
  this.path = this.path.map(([x, y]) => [x + dx, mercator2lat(lat2mercator(y) + dy)]);
22
22
  this.source.setData(this.getFeature(false));
23
- this.manager.drawSelectionNodes();
23
+ this.manager.selection?.updateSelectionNodes();
24
24
  e.preventDefault();
25
25
  };
26
26
  this.manager.map.on('mousemove', moveHandler);
27
- this.manager.map.once('mouseup', () => this.manager.map.off('mousemove', moveHandler));
27
+ this.manager.map.once('mouseup', () => {
28
+ this.manager.map.off('mousemove', moveHandler);
29
+ this.manager.state?.log();
30
+ });
28
31
  e.preventDefault();
29
32
  }
30
33
  getSelectionNodes() {
@@ -0,0 +1,25 @@
1
+ import type { GeometryManager } from '../geometry_manager.js';
2
+ import type { SelectionNode, SelectionNodeUpdater } from './types.js';
3
+ import type { GeoPoint } from '../utils/types.js';
4
+ import { MapLayerFill } from '../map_layer/fill.js';
5
+ import { MapLayerLine } from '../map_layer/line.js';
6
+ import type { StateElementCircle } from '../state/types.js';
7
+ import { AbstractElement } from './abstract.js';
8
+ export declare class CircleElement extends AbstractElement {
9
+ readonly fillLayer: MapLayerFill;
10
+ readonly strokeLayer: MapLayerLine;
11
+ point: GeoPoint;
12
+ radius: number;
13
+ constructor(manager: GeometryManager, point?: GeoPoint, radius?: number);
14
+ getSelectionNodes(): SelectionNode[];
15
+ getSelectionNodeUpdater(properties?: Record<string, unknown>): SelectionNodeUpdater | undefined;
16
+ select(value: boolean): void;
17
+ getFeature(includeProperties?: boolean): GeoJSON.Feature<GeoJSON.Polygon>;
18
+ getGeoJSON(): GeoJSON.Feature<GeoJSON.Point>;
19
+ destroy(): void;
20
+ getState(): StateElementCircle;
21
+ static fromState(manager: GeometryManager, state: StateElementCircle): CircleElement;
22
+ static fromGeoJSON(manager: GeometryManager, feature: GeoJSON.Feature<GeoJSON.Point, {
23
+ radius: number;
24
+ }>): CircleElement;
25
+ }
@@ -0,0 +1,118 @@
1
+ import { MapLayerFill } from '../map_layer/fill.js';
2
+ import { MapLayerLine } from '../map_layer/line.js';
3
+ import { AbstractElement } from './abstract.js';
4
+ import { circle, distance } from '../utils/geometry.js';
5
+ export class CircleElement extends AbstractElement {
6
+ fillLayer;
7
+ strokeLayer;
8
+ point;
9
+ radius;
10
+ constructor(manager, point, radius) {
11
+ super(manager);
12
+ this.point = point ?? this.randomPositions(1)[0];
13
+ this.radius = radius ?? this.randomRadius();
14
+ this.fillLayer = new MapLayerFill(manager, 'fill' + this.slug, this.sourceId);
15
+ this.fillLayer.on('click', () => this.manager.selection?.selectElement(this));
16
+ this.strokeLayer = new MapLayerLine(manager, 'line' + this.slug, this.sourceId);
17
+ this.strokeLayer.on('click', () => this.manager.selection?.selectElement(this));
18
+ this.source.setData(this.getFeature());
19
+ }
20
+ getSelectionNodes() {
21
+ return [
22
+ { index: 0, coordinates: this.point },
23
+ ...circle(this.point, this.radius, 4).map((coordinates) => ({
24
+ index: 1,
25
+ transparent: true,
26
+ coordinates
27
+ }))
28
+ ];
29
+ }
30
+ getSelectionNodeUpdater(properties) {
31
+ if (properties == undefined)
32
+ return;
33
+ if (properties.index == 0) {
34
+ return {
35
+ update: (lng, lat) => {
36
+ this.point[0] = lng;
37
+ this.point[1] = lat;
38
+ this.source.setData(this.getFeature(false));
39
+ },
40
+ delete: () => this.delete()
41
+ };
42
+ }
43
+ else {
44
+ return {
45
+ update: (lng, lat) => {
46
+ this.radius = distance([lng, lat], this.point);
47
+ this.source.setData(this.getFeature(false));
48
+ },
49
+ delete: () => this.delete()
50
+ };
51
+ }
52
+ }
53
+ select(value) {
54
+ super.select(value);
55
+ this.fillLayer.isSelected = value;
56
+ this.strokeLayer.isSelected = value;
57
+ }
58
+ getFeature(includeProperties = false) {
59
+ const coordinates = circle(this.point, this.radius, 72);
60
+ coordinates.push(coordinates[0]); // Close the circle
61
+ return {
62
+ type: 'Feature',
63
+ properties: includeProperties
64
+ ? {
65
+ ...this.fillLayer.getGeoJSONProperties(),
66
+ ...this.strokeLayer.getGeoJSONProperties(),
67
+ 'circle-center-x': this.point[0],
68
+ 'circle-center-y': this.point[1],
69
+ 'circle-radius': this.radius
70
+ }
71
+ : {},
72
+ geometry: { type: 'Polygon', coordinates: [coordinates] }
73
+ };
74
+ }
75
+ getGeoJSON() {
76
+ return {
77
+ type: 'Feature',
78
+ properties: {
79
+ ...this.fillLayer.getGeoJSONProperties(),
80
+ ...this.strokeLayer.getGeoJSONProperties(),
81
+ subType: 'Circle',
82
+ radius: this.radius
83
+ },
84
+ geometry: { type: 'Point', coordinates: this.point }
85
+ };
86
+ }
87
+ destroy() {
88
+ this.fillLayer.destroy();
89
+ this.strokeLayer.destroy();
90
+ this.map.removeSource(this.sourceId);
91
+ }
92
+ getState() {
93
+ return {
94
+ type: 'circle',
95
+ point: this.point,
96
+ radius: this.radius,
97
+ style: this.fillLayer.getState(),
98
+ strokeStyle: this.strokeLayer.getState()
99
+ };
100
+ }
101
+ static fromState(manager, state) {
102
+ const element = new CircleElement(manager, state.point, state.radius);
103
+ if (state.style)
104
+ element.fillLayer.setState(state.style);
105
+ if (state.strokeStyle)
106
+ element.strokeLayer.setState(state.strokeStyle);
107
+ return element;
108
+ }
109
+ static fromGeoJSON(manager, feature) {
110
+ const properties = feature.properties;
111
+ const center = feature.geometry.coordinates;
112
+ const radius = feature.properties.radius;
113
+ const element = new CircleElement(manager, center, radius);
114
+ element.fillLayer.setGeoJSONProperties(properties);
115
+ element.strokeLayer.setGeoJSONProperties(properties);
116
+ return element;
117
+ }
118
+ }
@@ -1,11 +1,11 @@
1
1
  import type { GeometryManager } from '../geometry_manager.js';
2
- import type { ElementPath } from './types.js';
2
+ import type { GeoPath } from '../utils/types.js';
3
3
  import { MapLayerLine } from '../map_layer/line.js';
4
4
  import { AbstractPathElement } from './abstract_path.js';
5
5
  import type { StateElementLine } from '../state/types.js';
6
6
  export declare class LineElement extends AbstractPathElement {
7
7
  readonly layer: MapLayerLine;
8
- constructor(manager: GeometryManager, line?: ElementPath);
8
+ constructor(manager: GeometryManager, line?: GeoPath);
9
9
  select(value: boolean): void;
10
10
  getFeature(includeProperties?: boolean): GeoJSON.Feature<GeoJSON.LineString>;
11
11
  destroy(): void;
@@ -6,7 +6,7 @@ export class LineElement extends AbstractPathElement {
6
6
  super(manager, true);
7
7
  this.path = line ?? this.randomPositions(2);
8
8
  this.layer = new MapLayerLine(manager, 'line' + this.slug, this.sourceId);
9
- this.layer.on('click', () => this.manager.selectElement(this));
9
+ this.layer.on('click', () => this.manager.selection?.selectElement(this));
10
10
  this.layer.on('mousedown', (e) => {
11
11
  if (!this.isSelected)
12
12
  return;
@@ -1,12 +1,13 @@
1
1
  import { AbstractElement } from './abstract.js';
2
2
  import type { GeometryManager } from '../geometry_manager.js';
3
- import type { ElementPoint, SelectionNode, SelectionNodeUpdater } from './types.js';
3
+ import type { SelectionNode, SelectionNodeUpdater } from './types.js';
4
4
  import { MapLayerSymbol } from '../map_layer/symbol.js';
5
5
  import type { StateElementMarker } from '../state/types.js';
6
+ import type { GeoPoint } from '../utils/types.js';
6
7
  export declare class MarkerElement extends AbstractElement {
7
8
  readonly layer: MapLayerSymbol;
8
- point: ElementPoint;
9
- constructor(manager: GeometryManager, point?: ElementPoint);
9
+ point: GeoPoint;
10
+ constructor(manager: GeometryManager, point?: GeoPoint);
10
11
  select(value: boolean): void;
11
12
  getFeature(includeProperties?: boolean): GeoJSON.Feature<GeoJSON.Point>;
12
13
  getSelectionNodes(): SelectionNode[];
@@ -2,12 +2,12 @@ import { AbstractElement } from './abstract.js';
2
2
  import { MapLayerSymbol } from '../map_layer/symbol.js';
3
3
  export class MarkerElement extends AbstractElement {
4
4
  layer;
5
- point = [0, 0];
5
+ point;
6
6
  constructor(manager, point) {
7
7
  super(manager);
8
8
  this.point = point ?? this.randomPositions(1)[0];
9
9
  this.layer = new MapLayerSymbol(manager, 'symbol' + this.slug, this.sourceId);
10
- this.layer.on('click', () => this.manager.selectElement(this));
10
+ this.layer.on('click', () => this.manager.selection?.selectElement(this));
11
11
  this.source.setData(this.getFeature());
12
12
  }
13
13
  select(value) {
@@ -1,5 +1,5 @@
1
1
  import type { GeometryManager } from '../geometry_manager.js';
2
- import type { ElementPath } from './types.js';
2
+ import type { GeoPath } from '../utils/types.js';
3
3
  import { MapLayerFill } from '../map_layer/fill.js';
4
4
  import { MapLayerLine } from '../map_layer/line.js';
5
5
  import { AbstractPathElement } from './abstract_path.js';
@@ -7,7 +7,7 @@ import type { StateElementPolygon } from '../state/types.js';
7
7
  export declare class PolygonElement extends AbstractPathElement {
8
8
  readonly fillLayer: MapLayerFill;
9
9
  readonly strokeLayer: MapLayerLine;
10
- constructor(manager: GeometryManager, polygon?: ElementPath);
10
+ constructor(manager: GeometryManager, polygon?: GeoPath);
11
11
  select(value: boolean): void;
12
12
  getFeature(includeProperties?: boolean): GeoJSON.Feature<GeoJSON.Polygon>;
13
13
  destroy(): void;