@maptiler/geocoding-control 0.0.32 → 0.0.36

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.
@@ -0,0 +1,282 @@
1
+ import type MapLibreGL from "maplibre-gl";
2
+ import type {
3
+ FitBoundsOptions,
4
+ MapMouseEvent,
5
+ LngLat,
6
+ Map,
7
+ Marker,
8
+ FlyToOptions,
9
+ GeoJSONSource,
10
+ } from "maplibre-gl";
11
+ import MarkerIcon from "./MarkerIcon.svelte";
12
+ import type { Feature, MapController, Proximity } from "./types";
13
+ import mask from "@turf/mask";
14
+ import union from "@turf/union";
15
+ import type {
16
+ Polygon,
17
+ MultiPolygon,
18
+ LineString,
19
+ MultiLineString,
20
+ } from "@turf/helpers";
21
+
22
+ let emptyGeojson: GeoJSON.FeatureCollection = {
23
+ type: "FeatureCollection",
24
+ features: [],
25
+ };
26
+
27
+ export function createMaplibreglMapController(
28
+ map: Map,
29
+ maplibregl?: typeof MapLibreGL | undefined,
30
+ marker: boolean | maplibregl.MarkerOptions = true,
31
+ showResultMarkers: boolean | maplibregl.MarkerOptions = true,
32
+ flyToOptions: FlyToOptions = {},
33
+ fitBoundsOptions: FitBoundsOptions = {}
34
+ ) {
35
+ let proximityChangeHandler: ((proximity: Proximity) => void) | undefined;
36
+
37
+ let mapClickHandler: ((coordinates: [number, number]) => void) | undefined;
38
+
39
+ let prevProximity: Proximity = undefined;
40
+
41
+ let markers: Marker[] = [];
42
+
43
+ let selectedMarker: maplibregl.Marker | undefined;
44
+
45
+ function addPreviewLayer() {
46
+ map.addSource("preview", {
47
+ type: "geojson",
48
+ data: emptyGeojson,
49
+ });
50
+
51
+ map.addLayer({
52
+ id: "preview-fill",
53
+ type: "fill",
54
+ source: "preview",
55
+ layout: {},
56
+ paint: {
57
+ "fill-color": "#000",
58
+ "fill-opacity": 0.1,
59
+ },
60
+ filter: ["==", ["geometry-type"], "Polygon"],
61
+ });
62
+
63
+ map.addLayer({
64
+ id: "preview-line",
65
+ type: "line",
66
+ source: "preview",
67
+ layout: {
68
+ "line-cap": "square",
69
+ },
70
+ paint: {
71
+ "line-width": ["case", ["==", ["geometry-type"], "Polygon"], 2, 3],
72
+ "line-dasharray": [1, 1],
73
+ "line-color": "#3170fe",
74
+ },
75
+ });
76
+ }
77
+
78
+ if (map.loaded()) {
79
+ addPreviewLayer();
80
+ } else {
81
+ map.once("load", () => {
82
+ addPreviewLayer();
83
+ });
84
+ }
85
+
86
+ const handleMapClick = (e: MapMouseEvent) => {
87
+ mapClickHandler?.([e.lngLat.lng, e.lngLat.lat]);
88
+ };
89
+
90
+ const handleMoveEnd = () => {
91
+ let c: LngLat;
92
+
93
+ const proximity =
94
+ map.getZoom() > 9
95
+ ? ([(c = map.getCenter().wrap()).lng, c.lat] as [number, number])
96
+ : undefined;
97
+
98
+ if (prevProximity !== proximity) {
99
+ prevProximity = proximity;
100
+
101
+ proximityChangeHandler?.(proximity);
102
+ }
103
+ };
104
+
105
+ const ctrl: MapController = {
106
+ setProximityChangeHandler(
107
+ _proximityChangeHandler: ((proximity: Proximity) => void) | undefined
108
+ ): void {
109
+ if (_proximityChangeHandler) {
110
+ proximityChangeHandler = _proximityChangeHandler;
111
+
112
+ map.on("moveend", handleMoveEnd);
113
+
114
+ handleMoveEnd();
115
+ } else {
116
+ map.off("moveend", handleMoveEnd);
117
+
118
+ proximityChangeHandler?.(undefined);
119
+
120
+ proximityChangeHandler = undefined;
121
+ }
122
+ },
123
+
124
+ setMapClickHandler(
125
+ _mapClickHandler: ((coordinates: [number, number]) => void) | undefined
126
+ ): void {
127
+ mapClickHandler = _mapClickHandler;
128
+
129
+ if (mapClickHandler) {
130
+ map.on("click", handleMapClick);
131
+ } else {
132
+ map.off("click", handleMapClick);
133
+ }
134
+ },
135
+
136
+ flyTo(center: [number, number], zoom: number): void {
137
+ map.flyTo({ center, zoom, ...flyToOptions });
138
+ },
139
+
140
+ fitBounds(bbox: [number, number, number, number], padding: number): void {
141
+ map.fitBounds(
142
+ [
143
+ [bbox[0], bbox[1]],
144
+ [bbox[2], bbox[3]],
145
+ ],
146
+ { padding, ...fitBoundsOptions }
147
+ );
148
+ },
149
+
150
+ indicateReverse(reverse: boolean): void {
151
+ map.getCanvas().style.cursor = reverse ? "crosshair" : "";
152
+ },
153
+
154
+ setMarkers(
155
+ markedFeatures: Feature[] | undefined,
156
+ picked: Feature | undefined
157
+ ): void {
158
+ function setData(data: GeoJSON.GeoJSON) {
159
+ (map.getSource("preview") as GeoJSONSource)?.setData(data);
160
+ }
161
+
162
+ for (const marker of markers) {
163
+ marker.remove();
164
+ }
165
+
166
+ setData(emptyGeojson);
167
+
168
+ markers.length = 0;
169
+
170
+ if (!maplibregl) {
171
+ return;
172
+ }
173
+
174
+ if (picked) {
175
+ let handled = false;
176
+
177
+ if (picked.geometry.type === "GeometryCollection") {
178
+ const geoms = picked.geometry.geometries.filter(
179
+ (geometry) =>
180
+ geometry.type === "Polygon" || geometry.type === "MultiPolygon"
181
+ ) as (Polygon | MultiPolygon)[];
182
+
183
+ if (geoms.length > 0) {
184
+ let geometry = geoms.pop()!;
185
+
186
+ for (const geom of geoms) {
187
+ geometry = union(geometry, geom) as unknown as
188
+ | Polygon
189
+ | MultiPolygon; // union actually returns geometry
190
+ }
191
+
192
+ setData(mask({ ...picked, geometry }));
193
+
194
+ handled = true;
195
+ } else {
196
+ const geometries = picked.geometry.geometries.filter(
197
+ (geometry) =>
198
+ geometry.type === "LineString" ||
199
+ geometry.type === "MultiLineString"
200
+ ) as (LineString | MultiLineString)[];
201
+
202
+ if (geometries.length > 0) {
203
+ setData({
204
+ ...picked,
205
+ geometry: { type: "GeometryCollection", geometries },
206
+ });
207
+
208
+ handled = true;
209
+ }
210
+ }
211
+ }
212
+
213
+ let m: Marker | undefined = undefined;
214
+
215
+ if (handled) {
216
+ // nothing
217
+ } else if (
218
+ picked.geometry.type === "Polygon" ||
219
+ picked.geometry.type === "MultiPolygon"
220
+ ) {
221
+ setData(mask(picked as any));
222
+ } else if (
223
+ picked.geometry.type === "LineString" ||
224
+ picked.geometry.type === "MultiLineString"
225
+ ) {
226
+ setData(picked as any);
227
+
228
+ return;
229
+ }
230
+
231
+ if (typeof marker === "object") {
232
+ m = new maplibregl.Marker(marker);
233
+ } else {
234
+ const element = document.createElement("div");
235
+
236
+ new MarkerIcon({
237
+ props: { displayIn: "maplibre" },
238
+ target: element,
239
+ });
240
+
241
+ m = new maplibregl.Marker({ element });
242
+ }
243
+
244
+ if (m) {
245
+ markers.push(m.setLngLat(picked.center).addTo(map));
246
+ }
247
+ }
248
+
249
+ for (const feature of markedFeatures ?? []) {
250
+ if (feature === picked) {
251
+ continue;
252
+ }
253
+
254
+ let m: Marker;
255
+
256
+ if (typeof showResultMarkers === "object") {
257
+ m = new maplibregl.Marker(showResultMarkers);
258
+ } else {
259
+ const element = document.createElement("div");
260
+
261
+ new MarkerIcon({ props: { displayIn: "maplibre" }, target: element });
262
+
263
+ m = new maplibregl.Marker({ element });
264
+ }
265
+
266
+ markers.push(m.setLngLat(feature.center).addTo(map));
267
+ }
268
+ },
269
+
270
+ setSelectedMarker(index: number): void {
271
+ if (selectedMarker) {
272
+ selectedMarker.getElement().classList.toggle("marker-selected", false);
273
+ }
274
+
275
+ selectedMarker = index > -1 ? markers[index] : undefined;
276
+
277
+ selectedMarker?.getElement().classList.toggle("marker-selected", true);
278
+ },
279
+ };
280
+
281
+ return ctrl;
282
+ }
package/src/lib/types.ts CHANGED
@@ -1,13 +1,15 @@
1
- export type Feature = {
1
+ export type Feature = GeoJSON.Feature & {
2
2
  id: string;
3
3
  text: string;
4
4
  place_name: string;
5
5
  place_type: string;
6
6
  center: [number, number];
7
7
  bbox: [number, number, number, number];
8
+ address?: string;
8
9
  };
9
10
 
10
11
  export type FeatureCollection = {
12
+ type: "FeatureCollection";
11
13
  features: Feature[];
12
14
  };
13
15
 
@@ -1,149 +0,0 @@
1
- import type MapLibreGL from "maplibre-gl";
2
- import type {
3
- FitBoundsOptions,
4
- MapMouseEvent,
5
- LngLat,
6
- Map,
7
- Marker,
8
- FlyToOptions,
9
- } from "maplibre-gl";
10
- import MarkerIcon from "./MarkerIcon.svelte";
11
- import type { Feature, MapController, Proximity } from "./types";
12
-
13
- export function createMaplibreMapController(
14
- map: Map,
15
- maplibregl?: typeof MapLibreGL | undefined,
16
- marker: boolean | maplibregl.MarkerOptions = true,
17
- showResultMarkers: boolean | maplibregl.MarkerOptions = true,
18
- flyToOptions: FlyToOptions = {},
19
- fitBoundsOptions: FitBoundsOptions = {}
20
- ) {
21
- let proximityChangeHandler: ((proximity: Proximity) => void) | undefined;
22
-
23
- let mapClickHandler: ((coordinates: [number, number]) => void) | undefined;
24
-
25
- let prevProximity: Proximity = undefined;
26
-
27
- let markers: Marker[] = [];
28
-
29
- let selectedMarker: maplibregl.Marker | undefined;
30
-
31
- const handleMapClick = (e: MapMouseEvent) => {
32
- mapClickHandler?.([e.lngLat.lng, e.lngLat.lat]);
33
- };
34
-
35
- const handleMoveEnd = () => {
36
- let c: LngLat;
37
-
38
- const proximity =
39
- map.getZoom() > 9
40
- ? ([(c = map.getCenter().wrap()).lng, c.lat] as [number, number])
41
- : undefined;
42
-
43
- if (prevProximity !== proximity) {
44
- prevProximity = proximity;
45
-
46
- proximityChangeHandler?.(proximity);
47
- }
48
- };
49
-
50
- const ctrl: MapController = {
51
- setProximityChangeHandler(
52
- _proximityChangeHandler: ((proximity: Proximity) => void) | undefined
53
- ): void {
54
- if (_proximityChangeHandler) {
55
- proximityChangeHandler = _proximityChangeHandler;
56
-
57
- map.on("moveend", handleMoveEnd);
58
-
59
- handleMoveEnd();
60
- } else {
61
- map.off("moveend", handleMoveEnd);
62
-
63
- proximityChangeHandler?.(undefined);
64
-
65
- proximityChangeHandler = undefined;
66
- }
67
- },
68
-
69
- setMapClickHandler(
70
- _mapClickHandler: ((coordinates: [number, number]) => void) | undefined
71
- ): void {
72
- mapClickHandler = _mapClickHandler;
73
-
74
- if (mapClickHandler) {
75
- map.on("click", handleMapClick);
76
- } else {
77
- map.off("click", handleMapClick);
78
- }
79
- },
80
-
81
- flyTo(center: [number, number], zoom: number): void {
82
- map.flyTo({ center, zoom, ...flyToOptions });
83
- },
84
-
85
- fitBounds(bbox: [number, number, number, number], padding: number): void {
86
- map.fitBounds(
87
- [
88
- [bbox[0], bbox[1]],
89
- [bbox[2], bbox[3]],
90
- ],
91
- { ...fitBoundsOptions, padding }
92
- );
93
- },
94
-
95
- indicateReverse(reverse: boolean): void {
96
- map.getCanvas().style.cursor = reverse ? "crosshair" : "";
97
- },
98
-
99
- setMarkers(
100
- markedFeatures: Feature[] | undefined,
101
- picked: Feature | undefined
102
- ): void {
103
- for (const marker of markers) {
104
- marker.remove();
105
- }
106
-
107
- markers.length = 0;
108
-
109
- if (!maplibregl) {
110
- return;
111
- }
112
-
113
- for (const feature of picked
114
- ? [...(markedFeatures ?? []), picked]
115
- : markedFeatures ?? []) {
116
- let m: Marker;
117
-
118
- if (feature === picked && typeof marker === "object") {
119
- m = new maplibregl.Marker(marker);
120
- } else if (
121
- feature !== picked &&
122
- typeof showResultMarkers === "object"
123
- ) {
124
- m = new maplibregl.Marker(showResultMarkers);
125
- } else {
126
- const element = document.createElement("div");
127
-
128
- new MarkerIcon({ props: { displayIn: "maplibre" }, target: element });
129
-
130
- m = new maplibregl.Marker({ element });
131
- }
132
-
133
- markers.push(m.setLngLat(feature.center).addTo(map));
134
- }
135
- },
136
-
137
- setSelectedMarker(index: number): void {
138
- if (selectedMarker) {
139
- selectedMarker.getElement().classList.toggle("marker-selected", false);
140
- }
141
-
142
- selectedMarker = index > -1 ? markers[index] : undefined;
143
-
144
- selectedMarker?.getElement().classList.toggle("marker-selected", true);
145
- },
146
- };
147
-
148
- return ctrl;
149
- }