@versatiles/svelte 0.2.0 → 1.0.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/README.md +21 -4
- package/dist/components/BBoxMap/AutoComplete.svelte +176 -0
- package/dist/components/{AutoComplete.svelte.d.ts → BBoxMap/AutoComplete.svelte.d.ts} +12 -18
- package/dist/components/BBoxMap/BBoxMap.d.ts +1 -12
- package/dist/components/BBoxMap/BBoxMap.js +1 -200
- package/dist/components/BBoxMap/BBoxMap.svelte +43 -127
- package/dist/components/BBoxMap/BBoxMap.svelte.d.ts +4 -20
- package/dist/components/BasicMap/BasicMap.svelte +34 -37
- package/dist/components/BasicMap/BasicMap.svelte.d.ts +9 -24
- package/dist/components/LocatorMap/LocatorMap.svelte +75 -0
- package/dist/components/LocatorMap/LocatorMap.svelte.d.ts +19 -0
- package/dist/components/MapEditor/Editor.svelte +25 -0
- package/dist/components/MapEditor/Editor.svelte.d.ts +7 -0
- package/dist/components/MapEditor/EditorLine.svelte +27 -0
- package/dist/components/MapEditor/EditorLine.svelte.d.ts +7 -0
- package/dist/components/MapEditor/EditorMarker.svelte +42 -0
- package/dist/components/MapEditor/EditorMarker.svelte.d.ts +7 -0
- package/dist/components/MapEditor/MapEditor.svelte +99 -0
- package/dist/components/MapEditor/MapEditor.svelte.d.ts +4 -0
- package/dist/components/MapEditor/editor.scss +16 -0
- package/dist/components/MapEditor/lib/element_abstract.d.ts +20 -0
- package/dist/components/MapEditor/lib/element_abstract.js +58 -0
- package/dist/components/MapEditor/lib/element_line.d.ts +14 -0
- package/dist/components/MapEditor/lib/element_line.js +76 -0
- package/dist/components/MapEditor/lib/element_marker.d.ts +20 -0
- package/dist/components/MapEditor/lib/element_marker.js +191 -0
- package/dist/components/MapEditor/lib/geocoder.d.ts +1 -0
- package/dist/components/MapEditor/lib/geocoder.js +8 -0
- package/dist/components/MapEditor/lib/geometry_manager.d.ts +18 -0
- package/dist/components/MapEditor/lib/geometry_manager.js +117 -0
- package/dist/components/MapEditor/lib/map_layer.d.ts +14 -0
- package/dist/components/MapEditor/lib/map_layer.js +61 -0
- package/dist/components/MapEditor/lib/types.d.ts +112 -0
- package/dist/components/MapEditor/lib/types.js +1 -0
- package/dist/components/MapEditor/lib/utils.d.ts +2 -0
- package/dist/components/MapEditor/lib/utils.js +11 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/utils/draw/bbox.d.ts +28 -0
- package/dist/utils/draw/bbox.js +193 -0
- package/dist/utils/location.d.ts +2 -1
- package/dist/utils/location.js +19 -10
- package/dist/utils/map_style.d.ts +6 -0
- package/dist/utils/map_style.js +28 -0
- package/dist/utils/sprite_library.d.ts +19 -0
- package/dist/utils/sprite_library.js +30 -0
- package/package.json +19 -14
- package/dist/components/AutoComplete.svelte +0 -197
- package/dist/components/BBoxMap/README.md +0 -70
- package/dist/components/BBoxMap/data/countries.jsonl +0 -258
- package/dist/components/BBoxMap/data/eu.jsonl +0 -1876
- package/dist/components/BBoxMap/data/us.jsonl +0 -52
- package/dist/components/BBoxMap/data/world.jsonl +0 -7
- package/dist/components/BBoxMap/helpers/geojson2bboxes.d.ts +0 -2
- package/dist/components/BBoxMap/helpers/geojson2bboxes.js +0 -183
- package/dist/components/BBoxMap/helpers/merge_bboxes.d.ts +0 -2
- package/dist/components/BBoxMap/helpers/merge_bboxes.js +0 -84
- package/dist/components/BBoxMap/helpers/population.raw.br +0 -0
- package/dist/utils/style.d.ts +0 -3
- package/dist/utils/style.js +0 -21
package/README.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
|
+
[](https://www.npmjs.com/package/@versatiles/svelte)
|
2
|
+
[](https://www.npmjs.com/package/@versatiles/svelte)
|
3
|
+
[](https://codecov.io/gh/versatiles-org/node-versatiles-svelte)
|
4
|
+
[](https://github.com/versatiles-org/node-versatiles-svelte/actions/workflows/ci.yml)
|
5
|
+
|
1
6
|
# Svelte Components for VersaTiles
|
2
7
|
|
8
|
+
Play with them: https://versatiles.org/node-versatiles-svelte/
|
9
|
+
|
3
10
|
## Install
|
4
11
|
|
5
12
|
```bash
|
@@ -16,12 +23,22 @@ npm i @versatiles/svelte
|
|
16
23
|
</tr>
|
17
24
|
<tr>
|
18
25
|
<th>BasicMap</th>
|
19
|
-
<td><img src="
|
20
|
-
<td><img src="
|
26
|
+
<td><img width="512" src="./screenshots/basic-map-light.png"></td>
|
27
|
+
<td><img width="512" src="./screenshots/basic-map-dark.png"></td>
|
21
28
|
</tr>
|
22
29
|
<tr>
|
23
30
|
<th>BBoxMap</th>
|
24
|
-
<td><img src="
|
25
|
-
<td><img src="
|
31
|
+
<td><img width="512" src="./screenshots/bbox-map-light.png"></td>
|
32
|
+
<td><img width="512" src="./screenshots/bbox-map-dark.png"></td>
|
33
|
+
</tr>
|
34
|
+
<tr>
|
35
|
+
<th>LocatorMap</th>
|
36
|
+
<td><img width="512" src="./screenshots/locator-map-light.png"></td>
|
37
|
+
<td><img width="512" src="./screenshots/locator-map-dark.png"></td>
|
38
|
+
</tr>
|
39
|
+
<tr>
|
40
|
+
<th>MapEditor</th>
|
41
|
+
<td><img width="512" src="./screenshots/map-editor-light.png"></td>
|
42
|
+
<td><img width="512" src="./screenshots/map-editor-dark.png"></td>
|
26
43
|
</tr>
|
27
44
|
</table>
|
@@ -0,0 +1,176 @@
|
|
1
|
+
<!-- AutoComplete.svelte -->
|
2
|
+
<script lang="ts" generics="T">import { isDarkMode } from '../../utils/map_style.js';
|
3
|
+
/* eslint svelte/no-at-html-tags: off */
|
4
|
+
let { change, initialInputText, inputText = $bindable(''), items = [], maxItems = 10, minChar = 3, placeholder = '' } = $props();
|
5
|
+
import { onMount } from 'svelte';
|
6
|
+
let inputElement; // Reference to DOM element
|
7
|
+
let autocompleteElement; // Reference to DOM element
|
8
|
+
let isOpen = $state(false);
|
9
|
+
let results = $state([]);
|
10
|
+
let selectedIndex = $state(0);
|
11
|
+
// Escape special characters in search string for use in regex
|
12
|
+
const regExpEscape = (s) => s.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&');
|
13
|
+
if (inputText.length >= minChar) {
|
14
|
+
const r = filterResults();
|
15
|
+
if (r.length > 0) {
|
16
|
+
const { key, value } = r[0];
|
17
|
+
inputText = key;
|
18
|
+
change(JSON.parse(JSON.stringify(value)));
|
19
|
+
}
|
20
|
+
else {
|
21
|
+
inputText = '';
|
22
|
+
}
|
23
|
+
}
|
24
|
+
// Handle input change
|
25
|
+
function onChange() {
|
26
|
+
if (inputText.length >= minChar) {
|
27
|
+
results = filterResults();
|
28
|
+
selectedIndex = 0;
|
29
|
+
isOpen = true;
|
30
|
+
}
|
31
|
+
else {
|
32
|
+
isOpen = false;
|
33
|
+
}
|
34
|
+
}
|
35
|
+
function onFocus() {
|
36
|
+
inputElement.setSelectionRange(0, 1000);
|
37
|
+
}
|
38
|
+
// Filter results based on search query
|
39
|
+
function filterResults() {
|
40
|
+
const searchText = inputText.trim();
|
41
|
+
const searchTextUpper = searchText.toUpperCase();
|
42
|
+
const searchReg = RegExp(regExpEscape(searchText), 'i');
|
43
|
+
return items
|
44
|
+
.filter((item) => item.key.toUpperCase().includes(searchTextUpper))
|
45
|
+
.slice(0, maxItems)
|
46
|
+
.map((item) => ({
|
47
|
+
...item,
|
48
|
+
_label: item.key.replace(searchReg, '<span>$&</span>')
|
49
|
+
}));
|
50
|
+
}
|
51
|
+
// Handle keyboard navigation
|
52
|
+
function onKeyDown(event) {
|
53
|
+
switch (event.key) {
|
54
|
+
case 'ArrowDown':
|
55
|
+
if (selectedIndex < results.length - 1)
|
56
|
+
selectedIndex += 1;
|
57
|
+
break;
|
58
|
+
case 'ArrowUp':
|
59
|
+
if (selectedIndex > 0)
|
60
|
+
selectedIndex -= 1;
|
61
|
+
break;
|
62
|
+
case 'Enter':
|
63
|
+
event.preventDefault();
|
64
|
+
close(selectedIndex);
|
65
|
+
break;
|
66
|
+
case 'Escape':
|
67
|
+
event.preventDefault();
|
68
|
+
close();
|
69
|
+
break;
|
70
|
+
}
|
71
|
+
}
|
72
|
+
// Close the autocomplete and select an item
|
73
|
+
function close(index = -1) {
|
74
|
+
isOpen = false;
|
75
|
+
if (index >= 0 && results[index]) {
|
76
|
+
const { key, value } = results[index];
|
77
|
+
inputText = key;
|
78
|
+
change(JSON.parse(JSON.stringify(value)));
|
79
|
+
}
|
80
|
+
}
|
81
|
+
onMount(() => {
|
82
|
+
const darkMode = isDarkMode(autocompleteElement);
|
83
|
+
autocompleteElement.style.setProperty('--bg-color', darkMode ? '#000' : '#fff');
|
84
|
+
autocompleteElement.style.setProperty('--fg-color', darkMode ? '#fff' : '#000');
|
85
|
+
if (initialInputText) {
|
86
|
+
inputText = initialInputText;
|
87
|
+
results = filterResults();
|
88
|
+
close(0);
|
89
|
+
}
|
90
|
+
});
|
91
|
+
</script>
|
92
|
+
|
93
|
+
<svelte:window on:click={() => close()} />
|
94
|
+
|
95
|
+
<div class="autocomplete" bind:this={autocompleteElement}>
|
96
|
+
<input
|
97
|
+
type="text"
|
98
|
+
bind:value={inputText}
|
99
|
+
{placeholder}
|
100
|
+
autocomplete="off"
|
101
|
+
oninput={onChange}
|
102
|
+
onkeydown={onKeyDown}
|
103
|
+
onfocusin={onFocus}
|
104
|
+
onclick={(e) => e.stopPropagation()}
|
105
|
+
bind:this={inputElement}
|
106
|
+
aria-activedescendant={isOpen ? `result-${selectedIndex}` : undefined}
|
107
|
+
aria-autocomplete="list"
|
108
|
+
aria-controls="autocomplete-results"
|
109
|
+
/>
|
110
|
+
<div class="autocomplete-results" class:hide-results={!isOpen}>
|
111
|
+
{#each results as result, i}
|
112
|
+
<button
|
113
|
+
onclick={() => close(i)}
|
114
|
+
class={i === selectedIndex ? ' is-active' : ''}
|
115
|
+
role="option"
|
116
|
+
aria-selected={i === selectedIndex}
|
117
|
+
>
|
118
|
+
{@html result._label}
|
119
|
+
</button>
|
120
|
+
{/each}
|
121
|
+
</div>
|
122
|
+
</div>
|
123
|
+
|
124
|
+
<style>
|
125
|
+
.autocomplete {
|
126
|
+
position: relative;
|
127
|
+
border-radius: 0.5em;
|
128
|
+
background: color-mix(in srgb, var(--bg-color) 80%, transparent);
|
129
|
+
box-sizing: border-box;
|
130
|
+
line-height: normal;
|
131
|
+
}
|
132
|
+
|
133
|
+
input {
|
134
|
+
width: 100%;
|
135
|
+
display: block;
|
136
|
+
box-sizing: border-box;
|
137
|
+
padding: 0.3em 0.6em;
|
138
|
+
border: none;
|
139
|
+
background: none;
|
140
|
+
color: var(--fg-color);
|
141
|
+
}
|
142
|
+
|
143
|
+
.autocomplete-results {
|
144
|
+
padding: 0;
|
145
|
+
margin: 0;
|
146
|
+
background: none;
|
147
|
+
width: 100%;
|
148
|
+
display: block;
|
149
|
+
border-radius: 0 0 0.5em 0.5em;
|
150
|
+
}
|
151
|
+
|
152
|
+
.autocomplete-results.hide-results {
|
153
|
+
display: none;
|
154
|
+
}
|
155
|
+
|
156
|
+
button {
|
157
|
+
padding: 0.2rem 0.5rem;
|
158
|
+
cursor: pointer;
|
159
|
+
border: none;
|
160
|
+
display: block;
|
161
|
+
background: transparent;
|
162
|
+
font-weight: normal;
|
163
|
+
color: color-mix(in srgb, var(--fg-color) 50%, transparent);
|
164
|
+
width: 100%;
|
165
|
+
text-align: left;
|
166
|
+
}
|
167
|
+
|
168
|
+
button > :global(span) {
|
169
|
+
color: var(--fg-color);
|
170
|
+
}
|
171
|
+
|
172
|
+
button.is-active,
|
173
|
+
button:hover {
|
174
|
+
background-color: color-mix(in srgb, var(--fg-color) 15%, transparent);
|
175
|
+
}
|
176
|
+
</style>
|
@@ -1,32 +1,26 @@
|
|
1
1
|
declare class __sveltets_Render<T> {
|
2
2
|
props(): {
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
items
|
3
|
+
change: (value: T) => void;
|
4
|
+
initialInputText?: string;
|
5
|
+
inputText?: string;
|
6
|
+
items?: {
|
7
7
|
key: string;
|
8
8
|
value: T;
|
9
|
-
}[];
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
change: CustomEvent<any>;
|
14
|
-
} & {
|
15
|
-
[evt: string]: CustomEvent<any>;
|
9
|
+
}[] | undefined;
|
10
|
+
maxItems?: number;
|
11
|
+
minChar?: number;
|
12
|
+
placeholder?: string;
|
16
13
|
};
|
14
|
+
events(): {};
|
17
15
|
slots(): {};
|
18
|
-
bindings():
|
19
|
-
exports(): {
|
20
|
-
setInputText: (text: string) => void;
|
21
|
-
};
|
16
|
+
bindings(): "inputText";
|
17
|
+
exports(): {};
|
22
18
|
}
|
23
19
|
interface $$IsomorphicComponent {
|
24
20
|
new <T>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
|
25
21
|
$$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
|
26
22
|
} & ReturnType<__sveltets_Render<T>['exports']>;
|
27
|
-
<T>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {
|
28
|
-
$$events?: ReturnType<__sveltets_Render<T>['events']>;
|
29
|
-
}): ReturnType<__sveltets_Render<T>['exports']>;
|
23
|
+
<T>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
|
30
24
|
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
31
25
|
}
|
32
26
|
declare const AutoComplete: $$IsomorphicComponent;
|
@@ -1,15 +1,4 @@
|
|
1
|
-
import type { GeoJSON } from 'geojson';
|
2
|
-
import type { LngLat, Point } from 'maplibre-gl';
|
3
|
-
export type BBox = [number, number, number, number];
|
4
|
-
export type BBoxDrag = '0_' | '1_' | '_0' | '_1' | '00' | '01' | '10' | '11' | false;
|
5
|
-
export declare function getBBoxDrag(point: Point, bboxPixel: BBox): BBoxDrag;
|
6
|
-
export declare function dragBBox(bbox: BBox, drag: BBoxDrag, lngLat: LngLat): {
|
7
|
-
bbox: BBox;
|
8
|
-
drag: BBoxDrag;
|
9
|
-
};
|
10
|
-
export declare function getCursor(drag: BBoxDrag): string | false;
|
11
|
-
export declare function getBBoxGeometry(bbox: BBox): GeoJSON;
|
12
1
|
export declare function loadBBoxes(): Promise<{
|
13
2
|
key: string;
|
14
|
-
value:
|
3
|
+
value: [number, number, number, number];
|
15
4
|
}[]>;
|
@@ -1,204 +1,5 @@
|
|
1
|
-
export function getBBoxDrag(point, bboxPixel) {
|
2
|
-
const maxDistance = 5;
|
3
|
-
const { x, y } = point;
|
4
|
-
const [x0, y0, x1, y1] = bboxPixel;
|
5
|
-
// Don't think outside the box
|
6
|
-
if (x < x0 - maxDistance)
|
7
|
-
return false;
|
8
|
-
if (x > x1 + maxDistance)
|
9
|
-
return false;
|
10
|
-
if (y < y0 - maxDistance)
|
11
|
-
return false;
|
12
|
-
if (y > y1 + maxDistance)
|
13
|
-
return false;
|
14
|
-
const drag = [
|
15
|
-
Math.abs(x0 - x) < maxDistance,
|
16
|
-
Math.abs(y0 - y) < maxDistance,
|
17
|
-
Math.abs(x1 - x) < maxDistance,
|
18
|
-
Math.abs(y1 - y) < maxDistance
|
19
|
-
];
|
20
|
-
if (drag[0] && drag[2]) {
|
21
|
-
if (Math.abs(x0 - x) < Math.abs(x1 - x)) {
|
22
|
-
drag[2] = false;
|
23
|
-
}
|
24
|
-
else {
|
25
|
-
drag[0] = false;
|
26
|
-
}
|
27
|
-
}
|
28
|
-
if (drag[1] && drag[3]) {
|
29
|
-
if (Math.abs(y0 - y) < Math.abs(y1 - y)) {
|
30
|
-
drag[3] = false;
|
31
|
-
}
|
32
|
-
else {
|
33
|
-
drag[1] = false;
|
34
|
-
}
|
35
|
-
}
|
36
|
-
if (drag[0]) {
|
37
|
-
if (drag[1])
|
38
|
-
return '01';
|
39
|
-
if (drag[3])
|
40
|
-
return '00';
|
41
|
-
return '0_';
|
42
|
-
}
|
43
|
-
else if (drag[1]) {
|
44
|
-
if (drag[2])
|
45
|
-
return '11';
|
46
|
-
return '_1';
|
47
|
-
}
|
48
|
-
else if (drag[2]) {
|
49
|
-
if (drag[3])
|
50
|
-
return '10';
|
51
|
-
return '1_';
|
52
|
-
}
|
53
|
-
else if (drag[3]) {
|
54
|
-
return '_0';
|
55
|
-
}
|
56
|
-
else
|
57
|
-
return false;
|
58
|
-
}
|
59
|
-
export function dragBBox(bbox, drag, lngLat) {
|
60
|
-
const x = Math.round(lngLat.lng * 1e3) / 1e3;
|
61
|
-
const y = Math.round(lngLat.lat * 1e3) / 1e3;
|
62
|
-
switch (drag) {
|
63
|
-
case '_0':
|
64
|
-
bbox[1] = y;
|
65
|
-
break;
|
66
|
-
case '_1':
|
67
|
-
bbox[3] = y;
|
68
|
-
break;
|
69
|
-
case '0_':
|
70
|
-
bbox[0] = x;
|
71
|
-
break;
|
72
|
-
case '00':
|
73
|
-
bbox[0] = x;
|
74
|
-
bbox[1] = y;
|
75
|
-
break;
|
76
|
-
case '01':
|
77
|
-
bbox[0] = x;
|
78
|
-
bbox[3] = y;
|
79
|
-
break;
|
80
|
-
case '1_':
|
81
|
-
bbox[2] = x;
|
82
|
-
break;
|
83
|
-
case '10':
|
84
|
-
bbox[2] = x;
|
85
|
-
bbox[1] = y;
|
86
|
-
break;
|
87
|
-
case '11':
|
88
|
-
bbox[2] = x;
|
89
|
-
bbox[3] = y;
|
90
|
-
break;
|
91
|
-
}
|
92
|
-
if (bbox[2] < bbox[0]) {
|
93
|
-
// flip horizontal
|
94
|
-
const t = bbox[0];
|
95
|
-
bbox[0] = bbox[2];
|
96
|
-
bbox[2] = t;
|
97
|
-
switch (drag) {
|
98
|
-
case '0_':
|
99
|
-
drag = '1_';
|
100
|
-
break;
|
101
|
-
case '00':
|
102
|
-
drag = '10';
|
103
|
-
break;
|
104
|
-
case '01':
|
105
|
-
drag = '11';
|
106
|
-
break;
|
107
|
-
case '1_':
|
108
|
-
drag = '0_';
|
109
|
-
break;
|
110
|
-
case '10':
|
111
|
-
drag = '00';
|
112
|
-
break;
|
113
|
-
case '11':
|
114
|
-
drag = '01';
|
115
|
-
break;
|
116
|
-
}
|
117
|
-
}
|
118
|
-
if (bbox[3] < bbox[1]) {
|
119
|
-
// flip vertical
|
120
|
-
const t = bbox[1];
|
121
|
-
bbox[1] = bbox[3];
|
122
|
-
bbox[3] = t;
|
123
|
-
switch (drag) {
|
124
|
-
case '_0':
|
125
|
-
drag = '_1';
|
126
|
-
break;
|
127
|
-
case '_1':
|
128
|
-
drag = '_0';
|
129
|
-
break;
|
130
|
-
case '00':
|
131
|
-
drag = '01';
|
132
|
-
break;
|
133
|
-
case '01':
|
134
|
-
drag = '00';
|
135
|
-
break;
|
136
|
-
case '10':
|
137
|
-
drag = '11';
|
138
|
-
break;
|
139
|
-
case '11':
|
140
|
-
drag = '10';
|
141
|
-
break;
|
142
|
-
}
|
143
|
-
}
|
144
|
-
return { drag, bbox };
|
145
|
-
}
|
146
|
-
export function getCursor(drag) {
|
147
|
-
switch (drag) {
|
148
|
-
case '_0':
|
149
|
-
return 'ns-resize';
|
150
|
-
case '_1':
|
151
|
-
return 'ns-resize';
|
152
|
-
case '0_':
|
153
|
-
return 'ew-resize';
|
154
|
-
case '00':
|
155
|
-
return 'nesw-resize';
|
156
|
-
case '01':
|
157
|
-
return 'nwse-resize';
|
158
|
-
case '1_':
|
159
|
-
return 'ew-resize';
|
160
|
-
case '10':
|
161
|
-
return 'nwse-resize';
|
162
|
-
case '11':
|
163
|
-
return 'nesw-resize';
|
164
|
-
}
|
165
|
-
return false;
|
166
|
-
}
|
167
|
-
export function getBBoxGeometry(bbox) {
|
168
|
-
return {
|
169
|
-
type: 'FeatureCollection',
|
170
|
-
features: [polygon(getRing([-180, -86, 180, 86]), getRing(bbox)), linestring(getRing(bbox))]
|
171
|
-
};
|
172
|
-
function polygon(...coordinates) {
|
173
|
-
return {
|
174
|
-
type: 'Feature',
|
175
|
-
geometry: { type: 'Polygon', coordinates },
|
176
|
-
properties: {}
|
177
|
-
};
|
178
|
-
}
|
179
|
-
function linestring(coordinates) {
|
180
|
-
return {
|
181
|
-
type: 'Feature',
|
182
|
-
geometry: { type: 'LineString', coordinates },
|
183
|
-
properties: {}
|
184
|
-
};
|
185
|
-
}
|
186
|
-
function getRing(bbox) {
|
187
|
-
const x0 = Math.min(bbox[0], bbox[2]);
|
188
|
-
const x1 = Math.max(bbox[0], bbox[2]);
|
189
|
-
const y0 = Math.min(bbox[1], bbox[3]);
|
190
|
-
const y1 = Math.max(bbox[1], bbox[3]);
|
191
|
-
return [
|
192
|
-
[x0, y0],
|
193
|
-
[x1, y0],
|
194
|
-
[x1, y1],
|
195
|
-
[x0, y1],
|
196
|
-
[x0, y0]
|
197
|
-
];
|
198
|
-
}
|
199
|
-
}
|
200
1
|
export async function loadBBoxes() {
|
201
|
-
const data = await import('
|
2
|
+
const data = await import('../../components/BBoxMap/bboxes.json');
|
202
3
|
const bboxes = data.default.map((e) => {
|
203
4
|
const key = e[0];
|
204
5
|
const value = e.slice(1, 5);
|
@@ -1,128 +1,44 @@
|
|
1
1
|
<!-- BBoxMap.svelte -->
|
2
|
-
<script lang="ts">
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
});
|
43
|
-
map.addLayer({
|
44
|
-
id: 'bbox-fill',
|
45
|
-
type: 'fill',
|
46
|
-
source: 'bbox',
|
47
|
-
filter: ['==', '$type', 'Polygon'],
|
48
|
-
paint: { 'fill-color': bboxColor, 'fill-opacity': 0.2 }
|
49
|
-
});
|
50
|
-
});
|
51
|
-
|
52
|
-
function getDrag(point: Point): BBoxDrag {
|
53
|
-
const { x: x0, y: y1 } = map.project([selectedBBox[0], selectedBBox[1]]);
|
54
|
-
const { x: x1, y: y0 } = map.project([selectedBBox[2], selectedBBox[3]]);
|
55
|
-
return getBBoxDrag(point, [x0, y0, x1, y1]);
|
56
|
-
}
|
57
|
-
|
58
|
-
let lastDrag: BBoxDrag = false;
|
59
|
-
let dragging = false;
|
60
|
-
map.on('mousemove', (e) => {
|
61
|
-
if (dragging) {
|
62
|
-
if (e.originalEvent.buttons % 2) {
|
63
|
-
const { drag, bbox } = dragBBox(selectedBBox, lastDrag, e.lngLat);
|
64
|
-
lastDrag = drag;
|
65
|
-
selectedBBox = bbox;
|
66
|
-
redrawBBox();
|
67
|
-
e.preventDefault();
|
68
|
-
} else {
|
69
|
-
dragging = false;
|
70
|
-
}
|
71
|
-
} else {
|
72
|
-
const drag = getDrag(e.point);
|
73
|
-
if (drag !== lastDrag) {
|
74
|
-
lastDrag = drag;
|
75
|
-
canvas.style.cursor = getCursor(drag) || 'grab';
|
76
|
-
}
|
77
|
-
}
|
78
|
-
});
|
79
|
-
|
80
|
-
map.on('mousedown', (e) => {
|
81
|
-
if (e.originalEvent.buttons % 2) {
|
82
|
-
const drag = getDrag(e.point);
|
83
|
-
lastDrag = drag;
|
84
|
-
if (drag) {
|
85
|
-
dragging = true;
|
86
|
-
e.preventDefault();
|
87
|
-
}
|
88
|
-
}
|
89
|
-
});
|
90
|
-
|
91
|
-
map.on('mouseup', () => (dragging = false));
|
92
|
-
|
93
|
-
start();
|
94
|
-
}
|
95
|
-
|
96
|
-
function start() {
|
97
|
-
if (!bboxes) return;
|
98
|
-
if (!map) return;
|
99
|
-
if (!autoComplete) return;
|
100
|
-
autoComplete.setInputText(getCountry()); // Initial search text
|
101
|
-
}
|
102
|
-
|
103
|
-
function redrawBBox() {
|
104
|
-
const bboxSource = map.getSource('bbox') as GeoJSONSource;
|
105
|
-
bboxSource.setData(getBBoxGeometry(selectedBBox));
|
106
|
-
}
|
107
|
-
|
108
|
-
function flyTo(bbox: BBox) {
|
109
|
-
selectedBBox = bbox ?? worldBBox;
|
110
|
-
if (map) {
|
111
|
-
if (map.getSource('bbox')) redrawBBox();
|
112
|
-
|
113
|
-
const transform = map.cameraForBounds(selectedBBox) as CameraOptions;
|
114
|
-
if (transform == null) return;
|
115
|
-
transform.zoom = transform.zoom ?? 0 - 0.5;
|
116
|
-
transform.bearing = 0;
|
117
|
-
transform.pitch = 0;
|
118
|
-
|
119
|
-
if (Date.now() - startTime < 1000) {
|
120
|
-
map.jumpTo(transform);
|
121
|
-
} else {
|
122
|
-
map.flyTo({ ...transform, essential: true, speed: 5 });
|
123
|
-
}
|
124
|
-
}
|
125
|
-
}
|
2
|
+
<script lang="ts">import 'maplibre-gl/dist/maplibre-gl.css';
|
3
|
+
import AutoComplete from './AutoComplete.svelte';
|
4
|
+
import { getCountryName } from '../../utils/location.js';
|
5
|
+
import BasicMap from '../BasicMap/BasicMap.svelte';
|
6
|
+
import { isDarkMode } from '../../utils/map_style.js';
|
7
|
+
import { loadBBoxes } from './BBoxMap.js';
|
8
|
+
import { BBoxDrawer } from '../../utils/draw/bbox.js';
|
9
|
+
let { selectedBBox = $bindable() } = $props();
|
10
|
+
const startTime = Date.now();
|
11
|
+
let bbox;
|
12
|
+
let map = $state();
|
13
|
+
let bboxes = $state();
|
14
|
+
let mapContainer;
|
15
|
+
function onMapInit(_map) {
|
16
|
+
map = _map;
|
17
|
+
mapContainer = map.getContainer();
|
18
|
+
map.setPadding({ top: 31 + 5, right: 5, bottom: 5, left: 5 });
|
19
|
+
map.on('load', async () => {
|
20
|
+
bbox = new BBoxDrawer(map, [-180, -86, 180, 86], isDarkMode(mapContainer) ? '#FFFFFF' : '#000000');
|
21
|
+
bboxes = await loadBBoxes();
|
22
|
+
});
|
23
|
+
}
|
24
|
+
function setBBox(newBBox) {
|
25
|
+
selectedBBox = newBBox;
|
26
|
+
if (bbox && map) {
|
27
|
+
bbox.setGeometry(newBBox);
|
28
|
+
const transform = map.cameraForBounds(bbox.getBounds());
|
29
|
+
if (transform == null)
|
30
|
+
return;
|
31
|
+
transform.zoom = transform.zoom ?? 0 - 0.5;
|
32
|
+
transform.bearing = 0;
|
33
|
+
transform.pitch = 0;
|
34
|
+
if (Date.now() - startTime < 1000) {
|
35
|
+
map.jumpTo(transform);
|
36
|
+
}
|
37
|
+
else {
|
38
|
+
map.flyTo({ ...transform, essential: true, speed: 5 });
|
39
|
+
}
|
40
|
+
}
|
41
|
+
}
|
126
42
|
</script>
|
127
43
|
|
128
44
|
<div class="container">
|
@@ -131,12 +47,12 @@
|
|
131
47
|
<AutoComplete
|
132
48
|
items={bboxes}
|
133
49
|
placeholder="Find country, region or city …"
|
134
|
-
|
135
|
-
|
50
|
+
change={setBBox}
|
51
|
+
initialInputText={getCountryName() ?? ''}
|
136
52
|
/>
|
137
53
|
</div>
|
138
54
|
{/if}
|
139
|
-
<BasicMap {
|
55
|
+
<BasicMap {onMapInit}></BasicMap>
|
140
56
|
</div>
|
141
57
|
|
142
58
|
<style>
|