@maptiler/geocoding-control 0.0.36 → 0.0.37

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.
@@ -1,13 +1,35 @@
1
1
  import * as L from "leaflet";
2
2
  import MarkerIcon from "./MarkerIcon.svelte";
3
3
  import type { Feature, MapController, Proximity } from "./types";
4
+ import type {
5
+ Polygon,
6
+ MultiPolygon,
7
+ LineString,
8
+ MultiLineString,
9
+ } from "@turf/helpers";
10
+ import mask from "@turf/mask";
11
+ import union from "@turf/union";
4
12
 
5
13
  export function createLeafletMapController(
6
14
  map: L.Map,
7
15
  marker: boolean | L.MarkerOptions = true,
8
16
  showResultMarkers: boolean | L.MarkerOptions = true,
9
17
  flyToOptions: L.ZoomPanOptions = {},
10
- flyToBounds: L.FitBoundsOptions = {}
18
+ flyToBounds: L.FitBoundsOptions = {},
19
+ fullGeometryStyle: L.PathOptions | L.StyleFunction = (feature) => {
20
+ const type = feature?.geometry?.type;
21
+
22
+ const weight = type === "LineString" || type === "MultiLineString" ? 3 : 2;
23
+
24
+ return {
25
+ color: "#3170fe",
26
+ fillColor: "#000",
27
+ fillOpacity: 0.1,
28
+ weight,
29
+ dashArray: [weight, weight],
30
+ lineCap: "butt",
31
+ };
32
+ }
11
33
  ) {
12
34
  let proximityChangeHandler: ((proximity: Proximity) => void) | undefined;
13
35
 
@@ -19,6 +41,10 @@ export function createLeafletMapController(
19
41
 
20
42
  let selectedMarker: L.Marker | undefined;
21
43
 
44
+ let resultLayer = L.geoJSON(undefined, {
45
+ style: fullGeometryStyle,
46
+ }).addTo(map);
47
+
22
48
  const handleMoveEnd = () => {
23
49
  if (!proximityChangeHandler) {
24
50
  prevProximity = undefined;
@@ -76,7 +102,7 @@ export function createLeafletMapController(
76
102
  },
77
103
 
78
104
  flyTo(center: [number, number], zoom: number) {
79
- map.flyTo(center, zoom, flyToOptions);
105
+ map.flyTo(center, zoom, { duration: 2, ...flyToOptions });
80
106
  },
81
107
 
82
108
  fitBounds(bbox: [number, number, number, number], padding: number): void {
@@ -85,7 +111,7 @@ export function createLeafletMapController(
85
111
  [bbox[1], bbox[0]],
86
112
  [bbox[3], bbox[2]],
87
113
  ],
88
- { ...flyToBounds, padding: [padding, padding] }
114
+ { padding: [padding, padding], duration: 2, ...flyToBounds }
89
115
  );
90
116
  },
91
117
 
@@ -97,37 +123,110 @@ export function createLeafletMapController(
97
123
  markedFeatures: Feature[] | undefined,
98
124
  picked: Feature | undefined
99
125
  ): void {
126
+ function setData(data?: GeoJSON.GeoJSON) {
127
+ resultLayer.clearLayers();
128
+
129
+ if (data) {
130
+ resultLayer.addData(data);
131
+ }
132
+ }
133
+
100
134
  for (const marker of markers) {
101
135
  marker.remove();
102
136
  }
103
137
 
104
138
  markers.length = 0;
105
139
 
106
- for (const feature of picked
107
- ? [...(markedFeatures ?? []), picked]
108
- : markedFeatures ?? []) {
109
- let m: L.Marker;
140
+ setData();
110
141
 
111
- const pos: L.LatLngExpression = [feature.center[1], feature.center[0]];
142
+ const createMarker = (pos: L.LatLngExpression) => {
143
+ const element = document.createElement("div");
144
+
145
+ new MarkerIcon({ props: { displayIn: "leaflet" }, target: element });
146
+
147
+ return new L.Marker(pos, {
148
+ icon: new L.DivIcon({ html: element, className: "" }),
149
+ });
150
+ };
151
+
152
+ if (picked) {
153
+ let handled = false;
154
+
155
+ if (picked.geometry.type === "GeometryCollection") {
156
+ const geoms = picked.geometry.geometries.filter(
157
+ (geometry) =>
158
+ geometry.type === "Polygon" || geometry.type === "MultiPolygon"
159
+ ) as (Polygon | MultiPolygon)[];
160
+
161
+ if (geoms.length > 0) {
162
+ let geometry = geoms.pop()!;
163
+
164
+ for (const geom of geoms) {
165
+ geometry = union(geometry, geom) as unknown as
166
+ | Polygon
167
+ | MultiPolygon; // union actually returns geometry
168
+ }
169
+
170
+ setData(mask({ ...picked, geometry }));
112
171
 
113
- if (feature === picked && typeof marker === "object") {
114
- m = new L.Marker(pos, marker);
172
+ handled = true;
173
+ } else {
174
+ const geometries = picked.geometry.geometries.filter(
175
+ (geometry) =>
176
+ geometry.type === "LineString" ||
177
+ geometry.type === "MultiLineString"
178
+ ) as (LineString | MultiLineString)[];
179
+
180
+ if (geometries.length > 0) {
181
+ setData({
182
+ ...picked,
183
+ geometry: { type: "GeometryCollection", geometries },
184
+ });
185
+
186
+ handled = true;
187
+ }
188
+ }
189
+ }
190
+
191
+ if (handled) {
192
+ // nothing
193
+ } else if (
194
+ picked.geometry.type === "Polygon" ||
195
+ picked.geometry.type === "MultiPolygon"
196
+ ) {
197
+ setData(mask(picked as any));
115
198
  } else if (
116
- feature !== picked &&
117
- typeof showResultMarkers === "object"
199
+ picked.geometry.type === "LineString" ||
200
+ picked.geometry.type === "MultiLineString"
118
201
  ) {
119
- m = new L.Marker(pos, showResultMarkers);
120
- } else {
121
- const element = document.createElement("div");
202
+ setData(picked as any);
122
203
 
123
- new MarkerIcon({ props: { displayIn: "leaflet" }, target: element });
204
+ return; // no pin for (multi)linestrings
205
+ }
206
+
207
+ const pos: L.LatLngExpression = [picked.center[1], picked.center[0]];
208
+
209
+ markers.push(
210
+ (typeof marker === "object"
211
+ ? new L.Marker(pos, marker)
212
+ : createMarker(pos)
213
+ ).addTo(map)
214
+ );
215
+ }
124
216
 
125
- m = new L.Marker(pos, {
126
- icon: new L.DivIcon({ html: element, className: "" }),
127
- });
217
+ for (const feature of markedFeatures ?? []) {
218
+ if (feature === picked) {
219
+ continue;
128
220
  }
129
221
 
130
- markers.push(m.addTo(map));
222
+ const pos: L.LatLngExpression = [feature.center[1], feature.center[0]];
223
+
224
+ markers.push(
225
+ (typeof showResultMarkers === "object"
226
+ ? new L.Marker(pos, showResultMarkers)
227
+ : createMarker(pos)
228
+ ).addTo(map)
229
+ );
131
230
  }
132
231
  },
133
232
 
@@ -7,6 +7,8 @@ import type {
7
7
  Marker,
8
8
  FlyToOptions,
9
9
  GeoJSONSource,
10
+ FillLayerSpecification,
11
+ LineLayerSpecification,
10
12
  } from "maplibre-gl";
11
13
  import MarkerIcon from "./MarkerIcon.svelte";
12
14
  import type { Feature, MapController, Proximity } from "./types";
@@ -30,7 +32,29 @@ export function createMaplibreglMapController(
30
32
  marker: boolean | maplibregl.MarkerOptions = true,
31
33
  showResultMarkers: boolean | maplibregl.MarkerOptions = true,
32
34
  flyToOptions: FlyToOptions = {},
33
- fitBoundsOptions: FitBoundsOptions = {}
35
+ fitBoundsOptions: FitBoundsOptions = {},
36
+ fullGeometryStyle: {
37
+ fill: Pick<FillLayerSpecification, "layout" | "paint" | "filter">;
38
+ line: Pick<LineLayerSpecification, "layout" | "paint" | "filter">;
39
+ } = {
40
+ fill: {
41
+ layout: {},
42
+ paint: {
43
+ "fill-color": "#000",
44
+ "fill-opacity": 0.1,
45
+ },
46
+ },
47
+ line: {
48
+ layout: {
49
+ "line-cap": "square",
50
+ },
51
+ paint: {
52
+ "line-width": ["case", ["==", ["geometry-type"], "Polygon"], 2, 3],
53
+ "line-dasharray": [1, 1],
54
+ "line-color": "#3170fe",
55
+ },
56
+ },
57
+ }
34
58
  ) {
35
59
  let proximityChangeHandler: ((proximity: Proximity) => void) | undefined;
36
60
 
@@ -42,44 +66,33 @@ export function createMaplibreglMapController(
42
66
 
43
67
  let selectedMarker: maplibregl.Marker | undefined;
44
68
 
45
- function addPreviewLayer() {
46
- map.addSource("preview", {
69
+ function addFullGeometryLayer() {
70
+ map.addSource("full-geom", {
47
71
  type: "geojson",
48
72
  data: emptyGeojson,
49
73
  });
50
74
 
51
75
  map.addLayer({
52
- id: "preview-fill",
76
+ ...fullGeometryStyle.fill,
77
+ id: "full-geom-fill",
53
78
  type: "fill",
54
- source: "preview",
55
- layout: {},
56
- paint: {
57
- "fill-color": "#000",
58
- "fill-opacity": 0.1,
59
- },
79
+ source: "full-geom",
60
80
  filter: ["==", ["geometry-type"], "Polygon"],
61
81
  });
62
82
 
63
83
  map.addLayer({
64
- id: "preview-line",
84
+ ...fullGeometryStyle.line,
85
+ id: "full-geom-line",
65
86
  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
- },
87
+ source: "full-geom",
75
88
  });
76
89
  }
77
90
 
78
91
  if (map.loaded()) {
79
- addPreviewLayer();
92
+ addFullGeometryLayer();
80
93
  } else {
81
94
  map.once("load", () => {
82
- addPreviewLayer();
95
+ addFullGeometryLayer();
83
96
  });
84
97
  }
85
98
 
@@ -156,21 +169,32 @@ export function createMaplibreglMapController(
156
169
  picked: Feature | undefined
157
170
  ): void {
158
171
  function setData(data: GeoJSON.GeoJSON) {
159
- (map.getSource("preview") as GeoJSONSource)?.setData(data);
172
+ (map.getSource("full-geom") as GeoJSONSource)?.setData(data);
160
173
  }
161
174
 
162
175
  for (const marker of markers) {
163
176
  marker.remove();
164
177
  }
165
178
 
166
- setData(emptyGeojson);
167
-
168
179
  markers.length = 0;
169
180
 
181
+ setData(emptyGeojson);
182
+
170
183
  if (!maplibregl) {
171
184
  return;
172
185
  }
173
186
 
187
+ const createMarker = () => {
188
+ const element = document.createElement("div");
189
+
190
+ new MarkerIcon({
191
+ props: { displayIn: "maplibre" },
192
+ target: element,
193
+ });
194
+
195
+ return new maplibregl.Marker({ element });
196
+ };
197
+
174
198
  if (picked) {
175
199
  let handled = false;
176
200
 
@@ -210,8 +234,6 @@ export function createMaplibreglMapController(
210
234
  }
211
235
  }
212
236
 
213
- let m: Marker | undefined = undefined;
214
-
215
237
  if (handled) {
216
238
  // nothing
217
239
  } else if (
@@ -225,25 +247,17 @@ export function createMaplibreglMapController(
225
247
  ) {
226
248
  setData(picked as any);
227
249
 
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 });
250
+ return; // no pin for (multi)linestrings
242
251
  }
243
252
 
244
- if (m) {
245
- markers.push(m.setLngLat(picked.center).addTo(map));
246
- }
253
+ markers.push(
254
+ (typeof marker === "object"
255
+ ? new maplibregl.Marker(marker)
256
+ : createMarker()
257
+ )
258
+ .setLngLat(picked.center)
259
+ .addTo(map)
260
+ );
247
261
  }
248
262
 
249
263
  for (const feature of markedFeatures ?? []) {
@@ -251,19 +265,14 @@ export function createMaplibreglMapController(
251
265
  continue;
252
266
  }
253
267
 
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));
268
+ markers.push(
269
+ (typeof showResultMarkers === "object"
270
+ ? new maplibregl.Marker(showResultMarkers)
271
+ : createMarker()
272
+ )
273
+ .setLngLat(feature.center)
274
+ .addTo(map)
275
+ );
267
276
  }
268
277
  },
269
278
 
package/src/lib/types.ts CHANGED
@@ -179,11 +179,25 @@ export type ControlOptions = {
179
179
  class?: string;
180
180
 
181
181
  /**
182
- * Set to `true` to enable reverse geocoding button with title _toggle reverse geocoding_ or set the button title directly.
182
+ * Set to `true` to enable reverse geocoding button with title. Set to `"always"` to reverse geocoding be always active.
183
183
  *
184
184
  * @default false
185
185
  */
186
- enableReverse?: boolean | string;
186
+ enableReverse?: boolean | "always";
187
+
188
+ /**
189
+ * Reverse toggle button title.
190
+ *
191
+ * @default "toggle reverse geocoding"
192
+ */
193
+ reverseButtonTitle?: string;
194
+
195
+ /**
196
+ * Clear button title.
197
+ *
198
+ * @default "clear"
199
+ */
200
+ clearButtonTitle?: string;
187
201
 
188
202
  /**
189
203
  * Set to `true` to show place type.
@@ -192,6 +206,13 @@ export type ControlOptions = {
192
206
  */
193
207
  showPlaceType?: boolean;
194
208
 
209
+ /**
210
+ * Set to `true` to show full feature geometry of the chosen result. Otherwise only marker will be shown.
211
+ *
212
+ * @default true
213
+ */
214
+ showFullGeometry?: boolean;
215
+
195
216
  // TODO - missing but useful from maplibre-gl-geocoder
196
217
  // popup // If true, a Popup will be added to the map when clicking on a marker using a default set of popup options. If the value is an object, the popup will be constructed using these options. If false, no popup will be added to the map. Requires that options.maplibregl also be set. (optional, default true)
197
218
  // render // A function that specifies how the results should be rendered in the dropdown menu. This function should accepts a single Carmen GeoJSON object as input and return a string. Any HTML in the returned string will be rendered.
@@ -1,4 +0,0 @@
1
- import type MapLibreGL from "maplibre-gl";
2
- import type { FitBoundsOptions, Map, FlyToOptions } from "maplibre-gl";
3
- import type { MapController } from "./types";
4
- export declare function createMaplibreMapController(map: Map, maplibregl?: typeof MapLibreGL | undefined, marker?: boolean | maplibregl.MarkerOptions, showResultMarkers?: boolean | maplibregl.MarkerOptions, flyToOptions?: FlyToOptions, fitBoundsOptions?: FitBoundsOptions): MapController;
@@ -1,12 +0,0 @@
1
- <svg viewBox="0 0 1000 1000" width="18px" height="18px">
2
- <path
3
- d="M500 115.1c212.2 0 384.9 172.6 384.9 384.9 0 212.2-172.7 384.9-384.9 384.9S115.1 712.2 115.1 500c0-212.4 172.5-384.9 384.9-384.9M500 10C229.4 10 10 229.4 10 500s219.4 490 490 490 490-219.4 490-490c-.2-270.6-219.5-490-490-490zm0 315c96.5 0 175 78.4 175 175 0 96.5-78.4 175-175 175-96.5 0-175-78.4-175-175 0-96.5 78.4-175 175-175m0-105c-154.7 0-279.9 125.4-279.9 279.9 0 154.7 125.4 279.9 279.9 279.9 154.5 0 279.9-125.4 279.9-279.9C779.9 345.3 654.5 220 500 220zm70 280c0 38.7-31.3 70-70 70s-70-31.3-70-70 31.3-70 70-70 70 31.3 70 70z"
4
- />
5
- </svg>
6
-
7
- <style>
8
- svg {
9
- display: block;
10
- fill: var(--color-icon-button);
11
- }
12
- </style>