@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.
- package/dist/components/BBoxMap/BBoxMap.svelte +23 -12
- package/dist/components/BBoxMap/lib/bbox.d.ts +1 -1
- package/dist/components/BBoxMap/lib/bbox.js +19 -17
- package/dist/components/BasicMap/BasicMap.svelte +22 -9
- package/dist/components/BasicMap/BasicMap.svelte.d.ts +2 -1
- package/dist/components/MapEditor/MapEditor.svelte +51 -20
- package/dist/components/MapEditor/components/Dialog.svelte +92 -0
- package/dist/components/MapEditor/components/Dialog.svelte.d.ts +17 -0
- package/dist/components/MapEditor/components/DialogFile.svelte +112 -0
- package/dist/components/MapEditor/components/DialogFile.svelte.d.ts +6 -0
- package/dist/components/MapEditor/components/DialogShare.svelte +216 -0
- package/dist/components/MapEditor/components/DialogShare.svelte.d.ts +10 -0
- package/dist/components/MapEditor/components/Editor.svelte +16 -14
- package/dist/components/MapEditor/components/EditorFill.svelte +35 -3
- package/dist/components/MapEditor/components/EditorStroke.svelte +35 -3
- package/dist/components/MapEditor/components/EditorSymbol.svelte +85 -8
- package/dist/components/MapEditor/components/InputRow.svelte +2 -3
- package/dist/components/MapEditor/components/PanelFile.svelte +73 -0
- package/dist/components/MapEditor/components/PanelFile.svelte.d.ts +7 -0
- package/dist/components/MapEditor/components/PanelSymbolSelector.svelte +82 -0
- package/dist/components/MapEditor/components/PanelSymbolSelector.svelte.d.ts +8 -0
- package/dist/components/MapEditor/components/Sidebar.svelte +59 -103
- package/dist/components/MapEditor/components/Sidebar.svelte.d.ts +3 -3
- package/dist/components/MapEditor/components/SidebarPanel.svelte +26 -9
- package/dist/components/MapEditor/lib/element/abstract.d.ts +8 -4
- package/dist/components/MapEditor/lib/element/abstract.js +11 -2
- package/dist/components/MapEditor/lib/element/abstract_path.d.ts +3 -2
- package/dist/components/MapEditor/lib/element/abstract_path.js +6 -3
- package/dist/components/MapEditor/lib/element/circle.d.ts +25 -0
- package/dist/components/MapEditor/lib/element/circle.js +118 -0
- package/dist/components/MapEditor/lib/element/line.d.ts +2 -2
- package/dist/components/MapEditor/lib/element/line.js +1 -1
- package/dist/components/MapEditor/lib/element/marker.d.ts +4 -3
- package/dist/components/MapEditor/lib/element/marker.js +2 -2
- package/dist/components/MapEditor/lib/element/polygon.d.ts +2 -2
- package/dist/components/MapEditor/lib/element/polygon.js +2 -2
- package/dist/components/MapEditor/lib/element/types.d.ts +2 -3
- package/dist/components/MapEditor/lib/geometry_manager.d.ts +12 -29
- package/dist/components/MapEditor/lib/geometry_manager.js +44 -162
- package/dist/components/MapEditor/lib/geometry_manager_interactive.d.ts +33 -0
- package/dist/components/MapEditor/lib/geometry_manager_interactive.js +102 -0
- package/dist/components/MapEditor/lib/map_layer/abstract.d.ts +2 -2
- package/dist/components/MapEditor/lib/map_layer/abstract.js +25 -25
- package/dist/components/MapEditor/lib/map_layer/fill.js +5 -16
- package/dist/components/MapEditor/lib/map_layer/line.js +5 -17
- package/dist/components/MapEditor/lib/map_layer/symbol.js +1 -1
- package/dist/components/MapEditor/lib/selection.d.ts +11 -0
- package/dist/components/MapEditor/lib/selection.js +70 -0
- package/dist/components/MapEditor/lib/state/constants.js +5 -6
- package/dist/components/MapEditor/lib/state/history.d.ts +14 -0
- package/dist/components/MapEditor/lib/state/history.js +53 -0
- package/dist/components/MapEditor/lib/state/manager.d.ts +8 -10
- package/dist/components/MapEditor/lib/state/manager.js +24 -48
- package/dist/components/MapEditor/lib/state/reader.d.ts +6 -4
- package/dist/components/MapEditor/lib/state/reader.js +70 -18
- package/dist/components/MapEditor/lib/state/types.d.ts +19 -2
- package/dist/components/MapEditor/lib/state/utils.d.ts +2 -0
- package/dist/components/MapEditor/lib/state/utils.js +12 -0
- package/dist/components/MapEditor/lib/state/writer.d.ts +6 -4
- package/dist/components/MapEditor/lib/state/writer.js +59 -19
- package/dist/components/MapEditor/lib/symbols.d.ts +1 -1
- package/dist/components/MapEditor/lib/symbols.js +47 -28
- package/dist/components/MapEditor/lib/utils/event_handler.d.ts +10 -0
- package/dist/components/MapEditor/lib/utils/event_handler.js +39 -0
- package/dist/components/MapEditor/lib/utils/geometry.d.ts +12 -0
- package/dist/components/MapEditor/lib/utils/geometry.js +87 -0
- package/dist/components/MapEditor/lib/utils/types.d.ts +2 -0
- package/dist/components/MapEditor/lib/utils/types.js +1 -0
- package/dist/components/MapEditor/style/button.scss +115 -0
- package/dist/components/MapEditor/style/index.scss +3 -0
- package/dist/components/MapEditor/style/layout.scss +20 -0
- package/dist/components/MapEditor/style/other.scss +10 -0
- package/dist/utils/location.d.ts +1 -0
- package/dist/utils/location.js +181 -0
- package/dist/utils/map_style.d.ts +2 -2
- package/dist/utils/map_style.js +2 -2
- package/package.json +29 -29
- package/dist/components/MapEditor/components/SymbolSelector.svelte +0 -110
- package/dist/components/MapEditor/components/SymbolSelector.svelte.d.ts +0 -8
- package/dist/components/MapEditor/lib/utils.d.ts +0 -6
- package/dist/components/MapEditor/lib/utils.js +0 -23
- /package/dist/components/MapEditor/lib/{geocoder.d.ts → utils/geocoder.d.ts} +0 -0
- /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
|
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
|
50
|
-
|
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"
|
59
|
+
<div class="sidebar">
|
72
60
|
<div style="margin-bottom: 36px;">
|
73
|
-
<div class="
|
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
|
-
<
|
78
|
-
|
79
|
-
|
80
|
-
|
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
|
-
<
|
85
|
-
|
86
|
-
<
|
87
|
-
|
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="
|
91
|
-
<button onclick={() =>
|
92
|
-
<button onclick={() =>
|
93
|
-
<button onclick={() =>
|
94
|
-
<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="
|
100
|
-
<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
|
-
<
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
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-
|
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:
|
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
|
1
|
+
import '../style/index.scss';
|
2
|
+
import type { GeometryManagerInteractive } from '../lib/geometry_manager_interactive.js';
|
2
3
|
type $$ComponentProps = {
|
3
|
-
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:
|
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 {
|
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):
|
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 {
|
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:
|
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.
|
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', () =>
|
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 {
|
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?:
|
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 {
|
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:
|
9
|
-
constructor(manager: GeometryManager, point?:
|
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
|
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 {
|
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?:
|
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;
|