@maptiler/geocoding-control
Advanced tools
Comparing version 0.0.44 to 0.0.48
@@ -9,2 +9,3 @@ export type Feature = GeoJSON.Feature & { | ||
address?: string; | ||
matching_text?: string; | ||
}; | ||
@@ -15,5 +16,20 @@ export type FeatureCollection = { | ||
}; | ||
export type MapEvent = { | ||
type: "proximityChange"; | ||
proximity: [number, number] | undefined; | ||
} | { | ||
type: "mapClick"; | ||
coordinates: [number, number]; | ||
} | { | ||
type: "markerClick"; | ||
id: string; | ||
} | { | ||
type: "markerMouseEnter"; | ||
id: string; | ||
} | { | ||
type: "markerMouseLeave"; | ||
id: string; | ||
}; | ||
export type MapController = { | ||
setProximityChangeHandler(proximityChangeHandler: undefined | ((proximity: [number, number] | undefined) => void)): void; | ||
setMapClickHandler(mapClickHandler: undefined | ((coordinates: [number, number]) => void)): void; | ||
setEventHandler(handler: undefined | ((e: MapEvent) => void)): void; | ||
flyTo(center: [number, number], zoom: number): void; | ||
@@ -23,2 +39,3 @@ fitBounds(bbox: [number, number, number, number], padding: number): void; | ||
setMarkers(features: Feature[] | undefined, picked: Feature | undefined): void; | ||
setReverseMarker(coordinates?: [number, number]): void; | ||
setSelectedMarker(index: number): void; | ||
@@ -80,2 +97,8 @@ }; | ||
/** | ||
* Maximum number of results to show. | ||
* | ||
* @default 5 | ||
*/ | ||
limit?: number; | ||
/** | ||
* Specify the language to use for response text and query result weighting. | ||
@@ -86,3 +109,3 @@ * Options are IETF language tags comprised of a mandatory ISO 639-1 language code and optionally one or more IETF subtags for country or script. | ||
*/ | ||
language?: string; | ||
language?: string | string[]; | ||
/** | ||
@@ -96,2 +119,8 @@ * If `false`, indicates that search will only occur on enter key press. | ||
/** | ||
* Set to `false` to disable fuzzy search. | ||
* | ||
* @default true | ||
*/ | ||
fuzzyMatch?: boolean; | ||
/** | ||
* On geocoded result what zoom level should the map animate to when a bbox isn't found in the response. | ||
@@ -154,2 +183,14 @@ * If a bbox is found the map will fit to the bbox. | ||
showFullGeometry?: boolean; | ||
/** | ||
* Limit search to specified country(ies). | ||
* | ||
* @default undefined use all countries | ||
*/ | ||
country?: string | string[]; | ||
/** | ||
* Filter of feature types to return. | ||
* | ||
* @default undefined all available feature types are returned | ||
*/ | ||
types?: string[]; | ||
}; |
@@ -1,3 +0,3 @@ | ||
import App from "./AppMaplibregl.svelte"; | ||
import App from "./AppLeaflet.svelte"; | ||
declare const app: App; | ||
export default app; |
{ | ||
"name": "@maptiler/geocoding-control", | ||
"version": "0.0.44", | ||
"version": "0.0.48", | ||
"type": "module", | ||
@@ -46,3 +46,3 @@ "author": { | ||
"devDependencies": { | ||
"@sveltejs/vite-plugin-svelte": "^1.3.1", | ||
"@sveltejs/vite-plugin-svelte": "^2.0.2", | ||
"@tsconfig/svelte": "^3.0.0", | ||
@@ -53,14 +53,14 @@ "@turf/buffer": "^6.5.0", | ||
"@types/leaflet": "^1.9.0", | ||
"prettier": "^2.8.0", | ||
"prettier-plugin-svelte": "^2.8.1", | ||
"svelte": "^3.53.1", | ||
"svelte-check": "^2.9.2", | ||
"svelte-preprocess": "^4.10.7", | ||
"prettier": "^2.8.1", | ||
"prettier-plugin-svelte": "^2.9.0", | ||
"svelte": "^3.55.0", | ||
"svelte-check": "^2.10.2", | ||
"svelte-preprocess": "^5.0.0", | ||
"tslib": "^2.4.1", | ||
"typescript": "^4.9.3", | ||
"vite": "^3.2.4" | ||
"typescript": "^4.9.4", | ||
"vite": "^4.0.2" | ||
}, | ||
"peerDependencies": { | ||
"leaflet": "^1.9.2", | ||
"maplibre-gl": "> 1.14.0" | ||
"leaflet": "^1.9.3", | ||
"maplibre-gl": "^2.4.0" | ||
}, | ||
@@ -67,0 +67,0 @@ "peerDependenciesMeta": { |
@@ -77,3 +77,3 @@ # MapTiler Geocoding control for MapLibre GL JS and Leaflet | ||
Options: | ||
### Options | ||
@@ -90,3 +90,3 @@ - `apiKey`<sup>\*</sup>: `string` - Maptiler API key | ||
- `bbox`: `[number, number, number, number]` - A bounding box argument: this is a bounding box given as an array in the format [minX, minY, maxX, maxY]. Search results will be limited to the bounding box. | ||
- `language`: `string` - Specify the language to use for response text and query result weighting. Options are IETF language tags comprised of a mandatory ISO 639-1 language code and optionally one or more IETF subtags for country or script. More than one value can also be specified, separated by commas. Set to empty string for forcing no language preference. | ||
- `language`: `string | string[]` - Specify the language(s) to use for response text and query result weighting. Options are IETF language tags comprised of a mandatory ISO 639-1 language code and optionally one or more IETF subtags for country or script. More than one value can also be specified, separated by commas. Set to empty string for forcing no language preference. | ||
- `showResultsWhileTyping`: `boolean` - If `false`, indicates that search will only occur on enter key press. If `true`, indicates that the Geocoder will search on the input box being updated above the minLength option. Default `false`. | ||
@@ -106,4 +106,8 @@ - `marker`: `boolean | MarkerOptions` - If `true`, a [MapLibre GL Marker](https://maplibre.org/maplibre-gl-js-docs/api/markers/#marker) / [Leaflet Marker](https://leafletjs.com/reference.html#marker) will be added to the map at the location of the user-selected result using a default set of Marker options. If the value is an object, the marker will be constructed using these options. If `false`, no marker will be added to the map. Requires that `options.maplibregl` also be set. Default `true`. | ||
- `fullGeometryStyle`: `{ fill: Pick<FillLayerSpecification, "layout" | "paint" | "filter">; line: Pick<LineLayerSpecification, "layout" | "paint" | "filter">; } | (L.PathOptions | L.StyleFunction)` - style of the full feature geometry. See Mapplibre GL JS or Leaflet documentation. | ||
- `fuzzyMatch`: `boolean` - Set to `false` to disable fuzzy search. Default `true`. | ||
- `limit`: `number` - Maximum number of results to show. Default `5`. | ||
- `country`: `string | string[]` - Limit search to specified country(ies). Default `undefined` (use all countries). | ||
- `types`: `string[]` - Filter of feature types to return. Default `undefined` (all available feature types are returned). | ||
Methods: | ||
### Methods | ||
@@ -115,4 +119,6 @@ - `setQuery(value: string, submit = true): void` - set the query and optionally submit it | ||
Events: | ||
### Events | ||
Events are implemented using [EventTarget](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget) and [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent). | ||
- `select` - Fired on highlighting search result in the dropdown by hovering it or by keyboard selection. Event value will be set to the highlighted `Feature` or to `undefined` if nothing is highlighted. | ||
@@ -127,2 +133,10 @@ - `pick` - Fired on picking the result from the dropdown. Event value will be set to the picked `Feature` or to `undefined` if nothing is picked (eg. search input is cleared). | ||
Example: | ||
```javascript | ||
geocodingControl.addEventListener("optionsVisibilityChange", (e) => { | ||
console.log("Options visible:", e.detail); | ||
}); | ||
``` | ||
## Svelte component | ||
@@ -129,0 +143,0 @@ |
import * as L from "leaflet"; | ||
import MarkerIcon from "./MarkerIcon.svelte"; | ||
import type { Feature, MapController, Proximity } from "./types"; | ||
import type { Feature, MapController, MapEvent, Proximity } from "./types"; | ||
import type { | ||
@@ -38,6 +38,4 @@ Polygon, | ||
) { | ||
let proximityChangeHandler: ((proximity: Proximity) => void) | undefined; | ||
let eventHandler: ((e: MapEvent) => void) | undefined; | ||
let mapClickHandler: ((coordinates: [number, number]) => void) | undefined; | ||
let prevProximity: Proximity = undefined; | ||
@@ -49,13 +47,10 @@ | ||
let reverseMarker: L.Marker | undefined; | ||
let resultLayer = L.geoJSON(undefined, { | ||
style: fullGeometryStyle, | ||
interactive: false, | ||
}).addTo(map); | ||
const handleMoveEnd = () => { | ||
if (!proximityChangeHandler) { | ||
prevProximity = undefined; | ||
return; | ||
} | ||
let c: L.LatLng; | ||
@@ -71,3 +66,3 @@ | ||
proximityChangeHandler(proximity); | ||
eventHandler?.({ type: "proximityChange", proximity }); | ||
} | ||
@@ -77,11 +72,27 @@ }; | ||
const handleMapClick = (e: L.LeafletMouseEvent) => { | ||
mapClickHandler?.([e.latlng.lng, e.latlng.lat]); | ||
eventHandler?.({ | ||
type: "mapClick", | ||
coordinates: [e.latlng.lng, e.latlng.lat], | ||
}); | ||
}; | ||
function createMarker(pos: L.LatLngExpression, interactive = false) { | ||
const element = document.createElement("div"); | ||
new MarkerIcon({ props: { displayIn: "leaflet" }, target: element }); | ||
return new L.Marker(pos, { | ||
interactive, | ||
icon: new L.DivIcon({ | ||
html: element, | ||
className: "", | ||
iconAnchor: [12, 26], | ||
}), | ||
}); | ||
} | ||
const ctrl: MapController = { | ||
setProximityChangeHandler( | ||
_proximityChangeHandler: ((proximity: Proximity) => void) | undefined | ||
): void { | ||
if (_proximityChangeHandler) { | ||
proximityChangeHandler = _proximityChangeHandler; | ||
setEventHandler(handler: undefined | ((e: MapEvent) => void)): void { | ||
if (handler) { | ||
eventHandler = handler; | ||
@@ -91,19 +102,11 @@ map.on("moveend", handleMoveEnd); | ||
handleMoveEnd(); | ||
map.on("click", handleMapClick); | ||
} else { | ||
map.off("moveend", handleMoveEnd); | ||
proximityChangeHandler?.(undefined); | ||
eventHandler?.({ type: "proximityChange", proximity: undefined }); | ||
proximityChangeHandler = undefined; | ||
} | ||
}, | ||
eventHandler = undefined; | ||
setMapClickHandler( | ||
_mapClickHandler: ((coordinates: [number, number]) => void) | undefined | ||
): void { | ||
mapClickHandler = _mapClickHandler; | ||
if (mapClickHandler) { | ||
map.on("click", handleMapClick); | ||
} else { | ||
map.off("click", handleMapClick); | ||
@@ -114,3 +117,3 @@ } | ||
flyTo(center: [number, number], zoom: number) { | ||
map.flyTo(center, zoom, { duration: 2, ...flyToOptions }); | ||
map.flyTo([center[1], center[0]], zoom, { duration: 2, ...flyToOptions }); | ||
}, | ||
@@ -132,2 +135,29 @@ | ||
setReverseMarker(coordinates: [number, number]) { | ||
if (!marker) { | ||
return; | ||
} | ||
const latLng = | ||
coordinates && ([coordinates[1], coordinates[0]] as [number, number]); | ||
if (reverseMarker) { | ||
if (!latLng) { | ||
reverseMarker.remove(); | ||
reverseMarker = undefined; | ||
} else { | ||
reverseMarker.setLatLng(latLng); | ||
} | ||
} else if (latLng) { | ||
reverseMarker = ( | ||
typeof marker === "object" | ||
? new L.Marker(latLng, marker) | ||
: createMarker(latLng) | ||
).addTo(map); | ||
reverseMarker.getElement()?.classList.add("marker-reverse"); | ||
} | ||
}, | ||
setMarkers( | ||
@@ -137,2 +167,6 @@ markedFeatures: Feature[] | undefined, | ||
): void { | ||
if (!marker) { | ||
return; | ||
} | ||
function setData(data?: GeoJSON.GeoJSON) { | ||
@@ -154,12 +188,2 @@ resultLayer.clearLayers(); | ||
const createMarker = (pos: L.LatLngExpression) => { | ||
const element = document.createElement("div"); | ||
new MarkerIcon({ props: { displayIn: "leaflet" }, target: element }); | ||
return new L.Marker(pos, { | ||
icon: new L.DivIcon({ html: element, className: "" }), | ||
}); | ||
}; | ||
if (picked) { | ||
@@ -230,15 +254,42 @@ let handled = false; | ||
for (const feature of markedFeatures ?? []) { | ||
if (feature === picked) { | ||
continue; | ||
} | ||
if (showResultMarkers) { | ||
for (const feature of markedFeatures ?? []) { | ||
if (feature === picked) { | ||
continue; | ||
} | ||
const pos: L.LatLngExpression = [feature.center[1], feature.center[0]]; | ||
const pos: L.LatLngExpression = [ | ||
feature.center[1], | ||
feature.center[0], | ||
]; | ||
markers.push( | ||
(typeof showResultMarkers === "object" | ||
? new L.Marker(pos, showResultMarkers) | ||
: createMarker(pos) | ||
).addTo(map) | ||
); | ||
const marker = | ||
typeof showResultMarkers === "object" | ||
? new L.Marker(pos, showResultMarkers) | ||
: createMarker(pos, true); | ||
marker.addTo(map); | ||
const element = marker.getElement(); | ||
if (element) { | ||
element.addEventListener("click", (e) => { | ||
e.stopPropagation(); | ||
eventHandler?.({ type: "markerClick", id: feature.id }); | ||
}); | ||
element.addEventListener("mouseenter", () => { | ||
eventHandler?.({ type: "markerMouseEnter", id: feature.id }); | ||
}); | ||
element.addEventListener("mouseleave", () => { | ||
eventHandler?.({ type: "markerMouseLeave", id: feature.id }); | ||
}); | ||
element.classList.toggle("marker-fuzzy", !!feature.matching_text); | ||
} | ||
markers.push(marker); | ||
} | ||
} | ||
@@ -245,0 +296,0 @@ }, |
@@ -14,3 +14,3 @@ import type MapLibreGL from "maplibre-gl"; | ||
import MarkerIcon from "./MarkerIcon.svelte"; | ||
import type { Feature, MapController, Proximity } from "./types"; | ||
import type { Feature, MapController, MapEvent, Proximity } from "./types"; | ||
import union from "@turf/union"; | ||
@@ -63,6 +63,4 @@ import type { | ||
) { | ||
let proximityChangeHandler: ((proximity: Proximity) => void) | undefined; | ||
let eventHandler: ((e: MapEvent) => void) | undefined; | ||
let mapClickHandler: ((coordinates: [number, number]) => void) | undefined; | ||
let prevProximity: Proximity = undefined; | ||
@@ -74,2 +72,4 @@ | ||
let reverseMarker: maplibregl.Marker | undefined; | ||
function addFullGeometryLayer() { | ||
@@ -111,3 +111,6 @@ if (fullGeometryStyle?.fill || fullGeometryStyle?.line) { | ||
const handleMapClick = (e: MapMouseEvent) => { | ||
mapClickHandler?.([e.lngLat.lng, e.lngLat.lat]); | ||
eventHandler?.({ | ||
type: "mapClick", | ||
coordinates: [e.lngLat.lng, e.lngLat.lat], | ||
}); | ||
}; | ||
@@ -126,12 +129,29 @@ | ||
proximityChangeHandler?.(proximity); | ||
eventHandler?.({ type: "proximityChange", proximity }); | ||
} | ||
}; | ||
function createMarker(interactive = false) { | ||
if (!maplibregl) { | ||
throw new Error(); | ||
} | ||
const element = document.createElement("div"); | ||
if (interactive) { | ||
element.classList.add("marker-interactive"); | ||
} | ||
new MarkerIcon({ | ||
props: { displayIn: "maplibre" }, | ||
target: element, | ||
}); | ||
return new maplibregl.Marker({ element, offset: [1, -13] }); | ||
} | ||
const ctrl: MapController = { | ||
setProximityChangeHandler( | ||
_proximityChangeHandler: ((proximity: Proximity) => void) | undefined | ||
): void { | ||
if (_proximityChangeHandler) { | ||
proximityChangeHandler = _proximityChangeHandler; | ||
setEventHandler(handler: undefined | ((e: MapEvent) => void)): void { | ||
if (handler) { | ||
eventHandler = handler; | ||
@@ -141,19 +161,11 @@ map.on("moveend", handleMoveEnd); | ||
handleMoveEnd(); | ||
map.on("click", handleMapClick); | ||
} else { | ||
map.off("moveend", handleMoveEnd); | ||
proximityChangeHandler?.(undefined); | ||
eventHandler?.({ type: "proximityChange", proximity: undefined }); | ||
proximityChangeHandler = undefined; | ||
} | ||
}, | ||
eventHandler = undefined; | ||
setMapClickHandler( | ||
_mapClickHandler: ((coordinates: [number, number]) => void) | undefined | ||
): void { | ||
mapClickHandler = _mapClickHandler; | ||
if (mapClickHandler) { | ||
map.on("click", handleMapClick); | ||
} else { | ||
map.off("click", handleMapClick); | ||
@@ -178,5 +190,31 @@ } | ||
indicateReverse(reverse: boolean): void { | ||
map.getCanvas().style.cursor = reverse ? "crosshair" : ""; | ||
map.getCanvasContainer().style.cursor = reverse ? "crosshair" : ""; | ||
}, | ||
setReverseMarker(coordinates: [number, number]) { | ||
if (!maplibregl || !marker) { | ||
return; | ||
} | ||
if (reverseMarker) { | ||
if (!coordinates) { | ||
reverseMarker.remove(); | ||
reverseMarker = undefined; | ||
} else { | ||
reverseMarker.setLngLat(coordinates); | ||
} | ||
} else if (coordinates) { | ||
reverseMarker = ( | ||
typeof marker === "object" | ||
? new maplibregl.Marker(marker) | ||
: createMarker() | ||
) | ||
.setLngLat(coordinates) | ||
.addTo(map); | ||
reverseMarker.getElement().classList.add("marker-reverse"); | ||
} | ||
}, | ||
setMarkers( | ||
@@ -186,2 +224,6 @@ markedFeatures: Feature[] | undefined, | ||
): void { | ||
if (!marker) { | ||
return; | ||
} | ||
function setData(data: GeoJSON.GeoJSON) { | ||
@@ -203,13 +245,2 @@ (map.getSource("full-geom") as GeoJSONSource)?.setData(data); | ||
const createMarker = () => { | ||
const element = document.createElement("div"); | ||
new MarkerIcon({ | ||
props: { displayIn: "maplibre" }, | ||
target: element, | ||
}); | ||
return new maplibregl.Marker({ element }); | ||
}; | ||
if (picked) { | ||
@@ -270,3 +301,3 @@ let handled = false; | ||
if (showResultMarkers) { | ||
if (marker) { | ||
markers.push( | ||
@@ -289,10 +320,29 @@ (typeof marker === "object" | ||
markers.push( | ||
(typeof showResultMarkers === "object" | ||
const marker = ( | ||
typeof showResultMarkers === "object" | ||
? new maplibregl.Marker(showResultMarkers) | ||
: createMarker() | ||
) | ||
.setLngLat(feature.center) | ||
.addTo(map) | ||
); | ||
: createMarker(true) | ||
) | ||
.setLngLat(feature.center) | ||
.addTo(map); | ||
const element = marker.getElement(); | ||
element.addEventListener("click", (e) => { | ||
e.stopPropagation(); | ||
eventHandler?.({ type: "markerClick", id: feature.id }); | ||
}); | ||
element.addEventListener("mouseenter", () => { | ||
eventHandler?.({ type: "markerMouseEnter", id: feature.id }); | ||
}); | ||
element.addEventListener("mouseleave", () => { | ||
eventHandler?.({ type: "markerMouseLeave", id: feature.id }); | ||
}); | ||
element.classList.toggle("marker-fuzzy", !!feature.matching_text); | ||
markers.push(marker); | ||
} | ||
@@ -299,0 +349,0 @@ } |
@@ -9,2 +9,3 @@ export type Feature = GeoJSON.Feature & { | ||
address?: string; | ||
matching_text?: string; | ||
}; | ||
@@ -17,13 +18,15 @@ | ||
export type MapEvent = | ||
| { | ||
type: "proximityChange"; | ||
proximity: [number, number] | undefined; | ||
} | ||
| { type: "mapClick"; coordinates: [number, number] } | ||
| { type: "markerClick"; id: string } | ||
| { type: "markerMouseEnter"; id: string } | ||
| { type: "markerMouseLeave"; id: string }; | ||
export type MapController = { | ||
setProximityChangeHandler( | ||
proximityChangeHandler: | ||
| undefined | ||
| ((proximity: [number, number] | undefined) => void) | ||
): void; | ||
setEventHandler(handler: undefined | ((e: MapEvent) => void)): void; | ||
setMapClickHandler( | ||
mapClickHandler: undefined | ((coordinates: [number, number]) => void) | ||
): void; | ||
flyTo(center: [number, number], zoom: number): void; | ||
@@ -40,2 +43,4 @@ | ||
setReverseMarker(coordinates?: [number, number]): void; | ||
setSelectedMarker(index: number): void; | ||
@@ -107,8 +112,8 @@ }; | ||
// /** | ||
// * Maximum number of results to show. | ||
// * | ||
// * @default 5 | ||
// */ | ||
// limit?: number; | ||
/** | ||
* Maximum number of results to show. | ||
* | ||
* @default 5 | ||
*/ | ||
limit?: number; | ||
@@ -121,3 +126,3 @@ /** | ||
*/ | ||
language?: string; | ||
language?: string | string[]; | ||
@@ -132,16 +137,9 @@ /** | ||
// /** | ||
// * Set to `false` to disable autocomplete. | ||
// * | ||
// * @default true | ||
// */ | ||
// autocomplete?: boolean; | ||
/** | ||
* Set to `false` to disable fuzzy search. | ||
* | ||
* @default true | ||
*/ | ||
fuzzyMatch?: boolean; | ||
// /** | ||
// * Set to `false` to disable fuzzy search. | ||
// * | ||
// * @default true | ||
// */ | ||
// fuzzy?: boolean; | ||
/** | ||
@@ -222,2 +220,16 @@ * On geocoded result what zoom level should the map animate to when a bbox isn't found in the response. | ||
/** | ||
* Limit search to specified country(ies). | ||
* | ||
* @default undefined use all countries | ||
*/ | ||
country?: string | string[]; | ||
/** | ||
* Filter of feature types to return. | ||
* | ||
* @default undefined all available feature types are returned | ||
*/ | ||
types?: string[]; | ||
// TODO - missing but useful from maplibre-gl-geocoder | ||
@@ -224,0 +236,0 @@ // 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) |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
1986023
35592
195