From 32ff30a44a64945c1e34e1bba773b3fa714281e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kate=20Mih=C3=A1likov=C3=A1?= Date: Tue, 2 Dec 2025 13:52:37 +0100 Subject: [PATCH 1/2] RD-1292 RD-1293 Add Leaflet control (v1.5 - v2-alpha) --- .npmignore | 4 + .prettierignore | 2 + demos/05-leaflet.html | 23 + demos/index.html | 1 + demos/src/05-leaflet.ts | 95 + eslint.config.js | 2 +- package-lock.json | 78 +- package.json | 20 +- src/controls/leaflet-control.ts | 536 ++++++ src/controls/leaflet-events.ts | 38 + src/controls/leaflet-options.ts | 132 ++ src/controls/maplibregl-options.ts | 4 - src/leaflet.public.ts | 7 + src/utils/geo-utils.ts | 27 + tmp/@types/leaflet-v2/index.d.ts | 2572 ++++++++++++++++++++++++++++ tmp/@types/leaflet-v2/package.json | 45 + tmp/@types/leaflet-v2/readme.md | 1 + tsconfig.leaflet-v1.5.json | 10 + tsconfig.leaflet-v1.7.json | 10 + tsconfig.leaflet-v1.9.json | 10 + tsconfig.leaflet-v2.json | 10 + vite.config.ts | 20 +- 22 files changed, 3614 insertions(+), 33 deletions(-) create mode 100644 .prettierignore create mode 100644 demos/05-leaflet.html create mode 100644 demos/src/05-leaflet.ts create mode 100644 src/controls/leaflet-control.ts create mode 100644 src/controls/leaflet-events.ts create mode 100644 src/controls/leaflet-options.ts create mode 100644 src/leaflet.public.ts create mode 100644 tmp/@types/leaflet-v2/index.d.ts create mode 100644 tmp/@types/leaflet-v2/package.json create mode 100644 tmp/@types/leaflet-v2/readme.md create mode 100644 tsconfig.leaflet-v1.5.json create mode 100644 tsconfig.leaflet-v1.7.json create mode 100644 tsconfig.leaflet-v1.9.json create mode 100644 tsconfig.leaflet-v2.json diff --git a/.npmignore b/.npmignore index 2bdf114..180fb06 100644 --- a/.npmignore +++ b/.npmignore @@ -3,6 +3,7 @@ demos public src !dist/src +test *.tgz .github .husky @@ -10,3 +11,6 @@ src vite* .* tsconfig.json +tsconfig.*.json +eslint.config.js +tmp diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..2ebad6c --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +dist +tmp diff --git a/demos/05-leaflet.html b/demos/05-leaflet.html new file mode 100644 index 0000000..b0a55aa --- /dev/null +++ b/demos/05-leaflet.html @@ -0,0 +1,23 @@ + + + MapTiler Geocoding Control example + + + + +
+ + + diff --git a/demos/index.html b/demos/index.html index f23f088..9310be4 100644 --- a/demos/index.html +++ b/demos/index.html @@ -51,6 +51,7 @@ MapTiler SDK → MapLibre GL → React → + Leaflet → diff --git a/demos/src/05-leaflet.ts b/demos/src/05-leaflet.ts new file mode 100644 index 0000000..2711ded --- /dev/null +++ b/demos/src/05-leaflet.ts @@ -0,0 +1,95 @@ +import { control, map as LMap, Marker, Popup, tileLayer } from "leaflet"; +import "leaflet/dist/leaflet.css"; + +import { GeocodingControl } from "../../src/leaflet.public"; + +import { getApiKey } from "./demo-utils"; + +const map = LMap("map", { + center: [0, 0], + zoom: 2, + zoomControl: false, +}); + +tileLayer(`https://api.maptiler.com/maps/streets-v2/{z}/{x}/{y}.png?key=${getApiKey()}`, { + tileSize: 512, + zoomOffset: -1, + minZoom: 1, + attribution: + '\u003ca href="https://www.maptiler.com/copyright/" target="_blank"\u003e\u0026copy; MapTiler\u003c/a\u003e \u003ca href="https://www.openstreetmap.org/copyright" target="_blank"\u003e\u0026copy; OpenStreetMap contributors\u003c/a\u003e', + crossOrigin: true, +}).addTo(map); + +const geocodingControl = new GeocodingControl({ + apiKey: getApiKey(), + enableReverse: "button", + collapsed: true, + // limit: 20, + // types: ["poi"], + // fetchParameters: { credentials: "include" }, + // selectFirst: false, + iconsBaseUrl: "/assets/icons/", + proximity: [ + { type: "map-center", minZoom: 12 }, + { type: "client-geolocation", minZoom: 8 }, + { type: "server-geolocation", minZoom: 8 }, + ], + adjustUrl(url) { + // for reverse geocoding use only address type + if (/\/\d+\.?\d*%2C\d+.?\d*.json$/.test(url.pathname)) { + url.searchParams.set("types", "address"); + url.searchParams.set("limit", "5"); + } + }, + marker(map, feature) { + if (!feature) { + return; + } + + const marker = new Marker({ lng: feature.center[0], lat: feature.center[1] }) + .bindPopup(new Popup({ closeButton: false }).setContent(feature.text)) + .addTo(map) + .togglePopup(); + + const element = marker.getElement(); + + if (element) { + element.style.cursor = "pointer"; + + element.addEventListener("click", (e) => { + marker.togglePopup(); + e.stopPropagation(); + }); + } + + return marker; + }, + showResultMarkers(map, feature) { + return new Marker({ lng: feature.center[0], lat: feature.center[1] }) + .bindPopup(new Popup({ closeButton: false }).setContent(feature.text)) + .addTo(map) + .togglePopup(); + }, +}); + +map.addControl(geocodingControl); + +geocodingControl.on("featureslisted", (e) => { + console.log(e); +}); + +map.addControl( + new GeocodingControl({ + position: "topleft", + apiKey: getApiKey(), + keepListOpen: true, + enableReverse: "button", + collapsed: false, + pickedResultStyle: "full-geometry", + types: ["locality"], + fullGeometryStyle: true, + iconsBaseUrl: "/assets/icons/", + }), +); + +map.addControl(control.zoom({ position: "topright" })); diff --git a/eslint.config.js b/eslint.config.js index c9b71c6..6479c92 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -3,7 +3,7 @@ import { defineConfig, globalIgnores } from "eslint/config"; import tseslint from "typescript-eslint"; export default defineConfig( - globalIgnores(["dist/", "vite.config*.ts", "**/eslint.config.js"]), + globalIgnores(["dist/", "vite.config*.ts", "**/eslint.config.js", "tmp/"]), // https://typescript-eslint.io/getting-started/typed-linting/ tseslint.configs.strictTypeChecked, tseslint.configs.stylisticTypeChecked, diff --git a/package-lock.json b/package-lock.json index 4986cb4..91fa11e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "BSD-3-Clause", "dependencies": { "@turf/bbox": "^7.2.0", + "@turf/clone": "^7.2.0", "@turf/difference": "^7.2.0", "@turf/flatten": "^7.2.0", "@turf/helpers": "^7.2.0", @@ -22,6 +23,9 @@ "@canvas/image-data": "^1.1.0", "@maptiler/sdk": "^3.8.0", "@types/leaflet": "^1.9.21", + "@types/leaflet--v1.5": "npm:@types/leaflet@1.5", + "@types/leaflet--v1.7": "npm:@types/leaflet@1.7", + "@types/leaflet--v1.9": "npm:@types/leaflet@1.9", "@types/node": "ts5.8", "@types/react": "^19.2.6", "@types/react-dom": "^19.2.3", @@ -33,7 +37,6 @@ "happy-dom": "^20.0.10", "husky": "^9.1.7", "leaflet": "^1.9.4", - "leaflet-v2": "npm:leaflet@2.0.0-alpha.1", "ol": "^10.6.1", "prettier": "^3.6.2", "prettier-plugin-organize-imports": "^4.3.0", @@ -49,8 +52,8 @@ "vitest": "^4.0.8" }, "peerDependencies": { - "@maptiler/sdk": "1 - 3", - "leaflet": "1.7 - 2", + "@maptiler/sdk": "1 - 4", + "leaflet": "1.5 - 2", "maplibre-gl": "2 - 5", "ol": "6 - 10" }, @@ -1654,6 +1657,20 @@ "url": "https://opencollective.com/turf" } }, + "node_modules/@turf/clone": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@turf/clone/-/clone-7.3.1.tgz", + "integrity": "sha512-r7xDOfw9ohA7PhZW+8X9RMsO4szB4YqkhEROaELJyLtQ1bo8VNFtndpZdE6YHQpD7Pjlvlb6i99q8w1QLisEPg==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.1", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, "node_modules/@turf/difference": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@turf/difference/-/difference-7.2.0.tgz", @@ -1686,9 +1703,9 @@ } }, "node_modules/@turf/helpers": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-7.2.0.tgz", - "integrity": "sha512-cXo7bKNZoa7aC7ydLmUR02oB3IgDe7MxiPuRz3cCtYQHn+BJ6h1tihmamYDWWUlPHgSNF0i3ATc4WmDECZafKw==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-7.3.1.tgz", + "integrity": "sha512-zkL34JVhi5XhsuMEO0MUTIIFEJ8yiW1InMu4hu/oRqamlY4mMoZql0viEmH6Dafh/p+zOl8OYvMJ3Vm3rFshgg==", "license": "MIT", "dependencies": { "@types/geojson": "^7946.0.10", @@ -1792,6 +1809,39 @@ "@types/geojson": "*" } }, + "node_modules/@types/leaflet--v1.5": { + "name": "@types/leaflet", + "version": "1.5.23", + "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.5.23.tgz", + "integrity": "sha512-S/xpuwjZuwYMP+4ZzQ10PX0Jy+0XmwPeojtjqhbca9UXaINdoru91Qm/DUUXyh4qYm3CP6Vher06l/UcA9tUKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/leaflet--v1.7": { + "name": "@types/leaflet", + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.7.11.tgz", + "integrity": "sha512-VwAYom2pfIAf/pLj1VR5aLltd4tOtHyvfaJlNYCoejzP2nu52PrMi1ehsLRMUS+bgafmIIKBV1cMfKeS+uJ0Vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/leaflet--v1.9": { + "name": "@types/leaflet", + "version": "1.9.21", + "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.21.tgz", + "integrity": "sha512-TbAd9DaPGSnzp6QvtYngntMZgcRk+igFELwR2N99XZn7RXUdKgsXMR+28bUO0rPsWp8MIu/f47luLIQuSLYv/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, "node_modules/@types/node": { "version": "24.9.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.2.tgz", @@ -3663,14 +3713,6 @@ "dev": true, "license": "BSD-2-Clause" }, - "node_modules/leaflet-v2": { - "name": "leaflet", - "version": "2.0.0-alpha.1", - "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-2.0.0-alpha.1.tgz", - "integrity": "sha512-2EJU27z/wljOgQCyybRkfrm5Xc3uy6huKehh0UAPsrAdwnSMxaplsqCl9cXPAuDm6D7uL6PCznYMDVIsaAdSdA==", - "dev": true, - "license": "BSD-2-Clause" - }, "node_modules/lerc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lerc/-/lerc-3.0.0.tgz", @@ -5258,6 +5300,14 @@ "integrity": "sha512-w2NTI8+3l3eeltKAdK8QpiLo/flRAr2p8AGeakfMZOXBxOg9HIu4LVDxBi81sYgVhFhdJjv1OrB5ssI8uFPoLg==", "dev": true, "license": "MIT AND BSD-3-Clause" + }, + "tmp/@types/leaflet-v2": { + "name": "@types/leaflet", + "version": "2.0.9999", + "extraneous": true, + "dependencies": { + "@types/geojson": "*" + } } } } diff --git a/package.json b/package.json index f7ae8f5..fe79f3d 100644 --- a/package.json +++ b/package.json @@ -30,14 +30,21 @@ ], "scripts": { "dev": "vite -c vite.config-dev.ts", - "make": "npm run clean && npm run build", - "build": "npm run build:index && npm run build:index:umd && npm run build:maptilersdk && npm run build:maptilersdk:umd && npm run build:maplibregl && npm run build:maplibregl:umd", + "make": "npm run clean && npm run build && npm run check-version", + "build": "npm run build:index && npm run build:index:umd && npm run build:maptilersdk && npm run build:maptilersdk:umd && npm run build:maplibregl && npm run build:maplibregl:umd && npm run build:leaflet && npm run build:leaflet:umd", "build:index": "FLAVOUR=index vite build", "build:index:umd": "FLAVOUR=index MODE=umd vite build", "build:maptilersdk": "FLAVOUR=maptilersdk vite build", "build:maptilersdk:umd": "FLAVOUR=maptilersdk MODE=umd vite build", "build:maplibregl": "FLAVOUR=maplibregl vite build", "build:maplibregl:umd": "FLAVOUR=maplibregl MODE=umd vite build", + "build:leaflet": "FLAVOUR=leaflet vite build", + "build:leaflet:umd": "FLAVOUR=leaflet MODE=umd vite build", + "check-version": "npm run check-version:leaflet-v1.5 && npm run check-version:leaflet-v1.7 && npm run check-version:leaflet-v1.9 && npm run check-version:leaflet-v2", + "check-version:leaflet-v1.5": "tsc -p tsconfig.leaflet-v1.5.json", + "check-version:leaflet-v1.7": "tsc -p tsconfig.leaflet-v1.7.json", + "check-version:leaflet-v1.9": "tsc -p tsconfig.leaflet-v1.9.json", + "check-version:leaflet-v2": "tsc -p tsconfig.leaflet-v2.json", "lint": "eslint -c eslint.config.js . && prettier --check . && tsc --noEmit", "lint:fix": "eslint -c eslint.config.js --fix && prettier --write . && tsc --noEmit", "test": "vitest run -c vite.config-test.ts --dom", @@ -68,6 +75,7 @@ }, "dependencies": { "@turf/bbox": "^7.2.0", + "@turf/clone": "^7.2.0", "@turf/difference": "^7.2.0", "@turf/flatten": "^7.2.0", "@turf/helpers": "^7.2.0", @@ -80,6 +88,9 @@ "@canvas/image-data": "^1.1.0", "@maptiler/sdk": "^3.8.0", "@types/leaflet": "^1.9.21", + "@types/leaflet--v1.5": "npm:@types/leaflet@1.5", + "@types/leaflet--v1.7": "npm:@types/leaflet@1.7", + "@types/leaflet--v1.9": "npm:@types/leaflet@1.9", "@types/node": "ts5.8", "@types/react": "^19.2.6", "@types/react-dom": "^19.2.3", @@ -91,7 +102,6 @@ "happy-dom": "^20.0.10", "husky": "^9.1.7", "leaflet": "^1.9.4", - "leaflet-v2": "npm:leaflet@2.0.0-alpha.1", "ol": "^10.6.1", "prettier": "^3.6.2", "prettier-plugin-organize-imports": "^4.3.0", @@ -107,8 +117,8 @@ "vitest": "^4.0.8" }, "peerDependencies": { - "@maptiler/sdk": "1 - 3", - "leaflet": "1.7 - 2", + "@maptiler/sdk": "1 - 4", + "leaflet": "1.5 - 2", "maplibre-gl": "2 - 5", "ol": "6 - 10" }, diff --git a/src/controls/leaflet-control.ts b/src/controls/leaflet-control.ts new file mode 100644 index 0000000..628c64e --- /dev/null +++ b/src/controls/leaflet-control.ts @@ -0,0 +1,536 @@ +import bbox from "@turf/bbox"; +import { feature, featureCollection } from "@turf/helpers"; +import union from "@turf/union"; +import type { GeometryCollection, LineString, MultiLineString, MultiPolygon, Polygon } from "geojson"; +import { + Control, + type ControlOptions, + DivIcon, + DomEvent, + Evented, + GeoJSON, + type LatLng, + type LatLngLiteral, + type LeafletMouseEvent, + type Map as LMap, + Marker, + type MarkerOptions, + Popup, + type PopupOptions, +} from "leaflet"; + +import type { BBox, Feature } from "../types"; +import { shiftPolyCollection, unwrapBbox } from "../utils/geo-utils"; +import { getMask } from "../utils/mask"; + +import "../geocoder/geocoder"; +import type { MaptilerGeocoderElement } from "../geocoder/geocoder"; +import type { + FeaturesListedEvent, + MaptilerGeocoderEventName, + MaptilerGeocoderEventNameMap, + PickEvent, + QueryChangeEvent, + RequestEvent, + ResponseEvent, + ReverseToggleEvent, + SelectEvent, +} from "../geocoder/geocoder-events"; + +import "../components/marker"; + +import type { LeafletGeocodingControlEventName, LeafletGeocodingControlEventNameMap } from "./leaflet-events"; +import { DEFAULT_GEOMETRY_STYLE, type LeafletGeocodingControlOptions, ZOOM_DEFAULTS } from "./leaflet-options"; + +/* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */ +/** Base class for Control needs to extend both Control and Evented */ +abstract class EventedControl extends Control { + override options!: Options; +} +Object.assign(EventedControl.prototype, Evented.prototype); +interface EventedControl extends Control, Evented { + options: Options; +} +/* eslint-enable @typescript-eslint/no-unsafe-declaration-merging */ + +export class LeafletGeocodingControl extends EventedControl { + #map?: LMap; + #element?: MaptilerGeocoderElement; + + constructor(options: LeafletGeocodingControlOptions = {}) { + super(options); + } + + onAdd(map: LMap): HTMLElement { + this.#map = map; + this.#element = map.getContainer().ownerDocument.createElement("maptiler-geocoder"); + this.#element.classList.add("leaflet-geocoder"); + + this.#setElementOptions(); + this.#addEventListeners(); + this.#addResultLayer(); + + const div = map.getContainer().ownerDocument.createElement("div"); + div.classList.add("leaflet-ctrl-geocoder", "leaflet-bar"); + div.appendChild(this.#element as Node); + + DomEvent.disableClickPropagation(div); + DomEvent.disableScrollPropagation(div); + + return div; + } + + onRemove(): void { + this.#removeEventListeners(); + this.#removeResultLayer(); + this.#map = undefined; + this.#element = undefined; + } + + /** + * Update the control options. + * + * @param options options to update + */ + setOptions(options: LeafletGeocodingControlOptions) { + Object.assign(this.options, options); + this.#setElementOptions(); + } + + /** + * Set the content of search input box. + * + * @param value text to set + */ + setQuery(value: string) { + this.#element?.setQuery(value); + } + + /** + * Set the content of search input box and immediately submit it. + * + * @param value text to set and submit + */ + submitQuery(value: string) { + this.#element?.submitQuery(value); + } + + /** + * Clear geocoding search results from the map. + */ + clearMap() { + this.#markedFeatures = []; + this.#setFeatures(undefined, undefined); + } + + /** + * Clear search result list. + */ + clearList() { + this.#element?.clearList(); + } + + /** + * Set reverse geocoding mode. + * + * @param reverseActive reverse geocoding active + */ + setReverseMode(reverseActive: boolean) { + this.setOptions({ reverseActive }); + } + + /** + * Focus the search input box. + * + * @param options [FocusOptions](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus#options) + */ + focus(options?: FocusOptions) { + this.#element?.focus(options); + } + + /** + * Blur the search input box. + */ + blur() { + this.#element?.blur(); + } + + #markers = new Map(); + #selectedMarker: Marker | undefined; + #reverseMarker: Marker | undefined; + #markedFeatures?: Feature[]; + #prevIdToFly?: string; + #resultLayer?: GeoJSON; + + #elementEventListeners: { [EventName in MaptilerGeocoderEventName]: (e: MaptilerGeocoderEventNameMap[EventName]) => void } = { + reversetoggle: (event: ReverseToggleEvent) => { + const container = this.#map?.getContainer(); + if (container) { + container.style.cursor = event.detail.reverse ? "crosshair" : ""; + } + this.#dispatch("reversetoggle", event.detail); + }, + querychange: (event: QueryChangeEvent) => { + const coords = (event as QueryChangeEvent).detail.reverseCoords; + + this.#setReverseMarker(coords ? { lng: coords.decimalLongitude, lat: coords.decimalLatitude } : undefined); + this.#dispatch("querychange", event.detail); + }, + queryclear: () => { + this.#setReverseMarker(undefined); + this.#dispatch("queryclear"); + }, + request: (event: RequestEvent) => { + this.#dispatch("request", event.detail); + }, + response: (event: ResponseEvent) => { + this.#dispatch("response", event.detail); + }, + select: (event: SelectEvent) => { + const selected = (event as SelectEvent).detail.feature; + if (selected && this.#flyToEnabled && this.options.flyToSelected) { + this.#flyTo({ lng: selected.center[0], lat: selected.center[1] }, this.#computeZoom(selected)); + } + if (this.#markedFeatures && selected) { + this.#setSelectedMarker(selected); + } + this.#dispatch("select", event.detail); + }, + pick: (event: PickEvent) => { + const picked = (event as PickEvent).detail.feature; + if (picked && picked.id !== this.#prevIdToFly && this.#flyToEnabled) { + this.#goToPicked(picked); + this.#setFeatures(this.#markedFeatures, picked); + } + + this.#prevIdToFly = picked?.id; + + this.#dispatch("pick", event.detail); + }, + featuresshow: () => { + this.#dispatch("featuresshow"); + }, + featureshide: () => { + this.#dispatch("featureshide"); + }, + featureslisted: (event: FeaturesListedEvent) => { + const features = (event as FeaturesListedEvent).detail.features; + this.#markedFeatures = features; + this.#setFeatures(this.#markedFeatures, undefined); + this.#zoomToResults(features); + this.#dispatch("featureslisted", event.detail); + }, + featuresclear: () => { + this.#markedFeatures = undefined; + this.#setFeatures(undefined, undefined); + this.#dispatch("featuresclear"); + }, + focusin: () => { + this.#dispatch("focusin"); + }, + focusout: () => { + this.#dispatch("focusout"); + }, + }; + #mapEventListeners = { + render: () => { + const zoom = this.#map?.getZoom(); + const center = this.#map?.getCenter(); + this.#element?.handleMapChange(zoom && center ? [zoom, center.lng, center.lat] : undefined); + }, + click: (e: LeafletMouseEvent) => { + this.#element?.handleMapClick([e.latlng.lng, e.latlng.lat]); + }, + }; + + #setElementOptions() { + if (!this.#element) return; + + this.#element.setOptions(this.options); + this.#element.fetchFullGeometryOnPick = this.options.pickedResultStyle !== "marker-only"; + } + + #addEventListeners() { + if (!this.#element || !this.#map) return; + + for (const [type, listener] of Object.entries(this.#elementEventListeners)) { + this.#element.addEventListener(type as MaptilerGeocoderEventName, listener as EventListener); + } + + this.#map.on(this.#mapEventListeners); + } + + #removeEventListeners() { + if (!this.#element || !this.#map) return; + + for (const [type, listener] of Object.entries(this.#elementEventListeners)) { + this.#element.removeEventListener(type as MaptilerGeocoderEventName, listener as EventListener); + } + + this.#map.off(this.#mapEventListeners); + } + + #dispatch(type: E, detail?: LeafletGeocodingControlEventNameMap[E]): this { + return super.fire(type, detail); + } + + #goToPicked(picked: Feature) { + if (picked.bbox[0] === picked.bbox[2] && picked.bbox[1] === picked.bbox[3]) { + this.#flyTo({ lng: picked.center[0], lat: picked.center[1] }, this.#computeZoom(picked)); + } else { + this.#fitBounds(unwrapBbox(picked.bbox), 50, this.#computeZoom(picked)); + } + } + + #zoomToResults(features: Feature[] | undefined) { + if (!features || features.length === 0 || !this.#flyToEnabled) return; + + const fuzzyOnly = features.every((feature) => feature.matching_text); + const bbox = features.reduce( + (bbox, feature) => + fuzzyOnly || !feature.matching_text + ? [Math.min(bbox[0], feature.bbox[0]), Math.min(bbox[1], feature.bbox[1]), Math.max(bbox[2], feature.bbox[2]), Math.max(bbox[3], feature.bbox[3])] + : bbox, + [180, 90, -180, -90], + ); + + const allZoom = features + .map((feature) => this.#computeZoom(feature)) + .filter((zoom) => zoom !== undefined) + .reduce((allZoom, featZoom) => (allZoom === undefined ? featZoom : Math.max(allZoom, featZoom)), undefined); + + this.#fitBounds(unwrapBbox(bbox), 50, allZoom); + } + + #computeZoom(feature: Feature): number | undefined { + if (feature.bbox[0] !== feature.bbox[2] || feature.bbox[1] !== feature.bbox[3]) { + return undefined; + } + + const index = feature.id.replace(/\..*/, ""); + const zoomMap = this.options.zoom ?? ZOOM_DEFAULTS; + + return ( + (Array.isArray(feature.properties?.categories) + ? (feature.properties.categories as string[]).reduce((a, category) => { + const b = zoomMap[index + "." + category] as number | undefined; + + return a === undefined ? b : b === undefined ? a : Math.max(a, b); + }, undefined) + : undefined) ?? zoomMap[index] + ); + } + + get #flyToEnabled() { + return Boolean(this.options.flyTo) || this.options.flyTo === undefined; + } + get #flyToOptions() { + return typeof this.options.flyTo === "boolean" ? {} : this.options.flyTo; + } + get #fitBoundsOptions() { + return typeof this.options.flyTo === "boolean" ? {} : this.options.flyTo; + } + + #flyTo(center: LatLng | LatLngLiteral, zoom?: number): void { + this.#map?.flyTo(center, zoom, this.#flyToOptions ?? undefined); + } + + #fitBounds(bbox: BBox, padding: number, maxZoom?: number): void { + this.#map?.fitBounds( + [ + [bbox[1], bbox[0]], + [bbox[3], bbox[2]], + ], + { padding: [padding, padding], ...(maxZoom ? { maxZoom } : {}), ...this.#fitBoundsOptions }, + ); + } + + #setReverseMarker(coordinates?: LatLng | LatLngLiteral) { + if (this.options.marker === false || this.options.marker === null || !this.#map) { + return; + } + + if (this.#reverseMarker) { + if (!coordinates) { + this.#reverseMarker.remove(); + + this.#reverseMarker = undefined; + } else { + this.#reverseMarker.setLatLng(coordinates); + } + } else if (coordinates) { + if (this.options.marker instanceof Function) { + this.#reverseMarker = this.options.marker(this.#map) ?? undefined; + } else { + this.#reverseMarker = this.#createMarker(coordinates, this.options.marker).addTo(this.#map); + this.#reverseMarker.getElement()?.classList.add("marker-reverse"); + } + } + } + + #setFeatures(markedFeatures: Feature[] | undefined, picked: Feature | undefined): void { + if (!this.#map) { + return; + } + + for (const marker of this.#markers.values()) { + marker.remove(); + } + + this.#markers = new Map(); + + this.#resultLayer?.clearLayers(); + + const addMarker = () => { + if (!picked || !this.#map || this.options.marker === false || this.options.marker === null) return; + + const marker = + this.options.marker instanceof Function + ? this.options.marker(this.#map, picked) + : this.#createMarker({ lng: picked.center[0], lat: picked.center[1] }, this.options.marker).addTo(this.#map); + if (marker) { + this.#markers.set(picked, marker); + } + }; + + if (picked?.geometry.type === "GeometryCollection") { + const polygonGeometries = picked.geometry.geometries.filter( + (geometry): geometry is Polygon | MultiPolygon => geometry.type === "Polygon" || geometry.type === "MultiPolygon", + ); + + if (polygonGeometries.length > 0) { + const unioned = union(featureCollection(polygonGeometries.map((geom) => feature(geom)))); + + if (unioned) { + const mask = getMask({ ...picked, geometry: unioned.geometry }); + if (mask) { + // leaflet doesn't repeat features every 360 degrees along longitude so we clone it manually to the direction(s) which could be displayed when auto-zoomed on the feature + const features = [...mask.features]; + const bb = unwrapBbox(bbox(picked) as BBox); + const span = bb[2] - bb[0]; + + if (bb[0] - span / 4 < -180) { + features.push(...shiftPolyCollection(mask, -360).features); + } + + if (bb[2] + span / 4 > 180) { + features.push(...shiftPolyCollection(mask, 360).features); + } + + this.#resultLayer?.addData(featureCollection(features)); + } + } + } else { + const lineGeometries = picked.geometry.geometries.filter( + (geometry): geometry is LineString | MultiLineString => geometry.type === "LineString" || geometry.type === "MultiLineString", + ); + + if (lineGeometries.length > 0) { + this.#resultLayer?.addData({ + ...picked, + geometry: { type: "GeometryCollection", geometries: lineGeometries }, + } as Feature); + } + } + } else if (picked?.geometry.type.endsWith("Polygon")) { + const mask = getMask(picked as Feature); + if (mask) { + this.#resultLayer?.addData(mask); + } + if (this.options.pickedResultStyle === "full-geometry-including-polygon-center-marker") { + addMarker(); + } + } else if (picked?.geometry.type.endsWith("LineString")) { + this.#resultLayer?.addData(picked); + } else if (picked?.geometry.type.endsWith("Point")) { + addMarker(); + } + + if (this.options.showResultMarkers !== false && this.options.showResultMarkers !== null) { + for (const feature of markedFeatures ?? []) { + if (feature.id === picked?.id) { + continue; + } + + let marker; + + if (this.options.showResultMarkers instanceof Function) { + marker = this.options.showResultMarkers(this.#map, feature); + + if (!marker) { + continue; + } + } else { + marker = this.#createMarker({ lng: feature.center[0], lat: feature.center[1] }, this.options.showResultMarkers) + .bindPopup( + new Popup({ + offset: [0.3, -21], + closeButton: false, + closeOnMove: true, + className: "maptiler-gc-popup", + } as PopupOptions).setContent(feature.place_type[0] === "reverse" ? feature.place_name : feature.place_name.replace(/,.*/, "")), + ) + .addTo(this.#map); + + marker.getElement()?.classList.add("marker-interactive"); + } + + const element = marker.getElement(); + + element?.addEventListener("click", (e) => { + e.stopPropagation(); + this.#dispatch("markerclick", { feature, marker }); + }); + + element?.addEventListener("mouseenter", () => { + this.#dispatch("markermouseenter", { feature, marker }); + marker.togglePopup(); + }); + + element?.addEventListener("mouseleave", () => { + this.#dispatch("markermouseleave", { feature, marker }); + marker.togglePopup(); + }); + + // element.classList.toggle("marker-fuzzy", !!feature.matching_text); + + this.#markers.set(feature, marker); + } + } + } + + #setSelectedMarker(feature: Feature): void { + this.#selectedMarker?.getElement()?.classList.toggle("marker-selected", false); + + if (this.options.markerOnSelected) { + this.#selectedMarker = this.#markers.get(feature); + this.#selectedMarker?.getElement()?.classList.toggle("marker-selected", true); + } else if (this.#selectedMarker) { + this.#selectedMarker = undefined; + } + } + + #addResultLayer() { + if (this.#map) { + this.#resultLayer = new GeoJSON(undefined, { + style: + this.options.fullGeometryStyle === true ? DEFAULT_GEOMETRY_STYLE : this.options.fullGeometryStyle === false ? undefined : (this.options.fullGeometryStyle ?? undefined), + interactive: false, + }).addTo(this.#map); + } + } + + #removeResultLayer() { + if (this.#map && this.#resultLayer) { + this.#resultLayer.removeFrom(this.#map); + this.#resultLayer = undefined; + } + } + + #createMarker(center: LatLng | LatLngLiteral, options: MarkerOptions | true | undefined) { + if (typeof options !== "object") { + options = { icon: new DivIcon({ html: this.#map?.getContainer().ownerDocument.createElement("maptiler-geocode-marker"), iconAnchor: [12.3, 30], className: "" }) }; + } + return new Marker(center, options); + } +} diff --git a/src/controls/leaflet-events.ts b/src/controls/leaflet-events.ts new file mode 100644 index 0000000..2e79adc --- /dev/null +++ b/src/controls/leaflet-events.ts @@ -0,0 +1,38 @@ +import type { Marker } from "leaflet"; +import type { Feature } from "../types"; + +import type * as Geocoder from "../geocoder/geocoder-events"; + +export type ReverseToggleEvent = Geocoder.ReverseToggleEvent["detail"]; +export type QueryChangeEvent = Geocoder.QueryChangeEvent["detail"]; +export type FeaturesListedEvent = Geocoder.FeaturesListedEvent["detail"]; +export type RequestEvent = Geocoder.RequestEvent["detail"]; +export type ResponseEvent = Geocoder.ResponseEvent["detail"]; +export type SelectEvent = Geocoder.SelectEvent["detail"]; +export type PickEvent = Geocoder.PickEvent["detail"]; +export type MarkerClickEvent = { feature: Feature; marker: Marker }; +export type MarkerMouseEnterEvent = { feature: Feature; marker: Marker }; +export type MarkerMouseLeaveEvent = { feature: Feature; marker: Marker }; +export type LeafletGeocodingControlEventNameMap = { + reversetoggle: ReverseToggleEvent; + querychange: QueryChangeEvent; + queryclear: never; + request: RequestEvent; + response: ResponseEvent; + select: SelectEvent; + pick: PickEvent; + featuresshow: never; + featureshide: never; + featureslisted: FeaturesListedEvent; + featuresclear: never; + + focusin: never; + focusout: never; + + markerclick: MarkerClickEvent; + markermouseenter: MarkerMouseEnterEvent; + markermouseleave: MarkerMouseLeaveEvent; +}; + +export type LeafletGeocodingControlEvent = LeafletGeocodingControlEventNameMap[keyof LeafletGeocodingControlEventNameMap]; +export type LeafletGeocodingControlEventName = keyof LeafletGeocodingControlEventNameMap; diff --git a/src/controls/leaflet-options.ts b/src/controls/leaflet-options.ts new file mode 100644 index 0000000..6ff47af --- /dev/null +++ b/src/controls/leaflet-options.ts @@ -0,0 +1,132 @@ +import type { ControlOptions, Map as LMap, Marker, MarkerOptions, PathOptions, StyleFunction, ZoomPanOptions } from "leaflet"; + +import type { MaptilerGeocoderOptions } from "../geocoder/geocoder-options"; +import type { Feature, PickedResultStyle } from "../types"; + +export type LeafletGeocodingControlOptions = Omit & { + /** + * Marker to be added to the map at the location of the user-selected result using a default set of Marker options. + * + * - If `true` or `undefined` then a default marker will be used. + * - If the value is a [MarkerOptions](https://leafletjs.com/reference.html#marker-option) then the marker will be constructed using these options. + * - If the value is a function then it can return instance of the [Marker](https://leafletjs.com/reference.html#marker). + * Function can accept `Feature` as a parameter which is `undefined` for the reverse location marker. + * - If `false` or `null` then no marker will be added to the map. + * + * Default value is `true`. + */ + marker?: null | boolean | MarkerOptions | ((map: LMap, feature?: Feature) => undefined | null | Marker); + + /** + * Displays a marker on the selected feature from the result list. `marker` must be enabled in any way for this to display. + * + * Default: `true`. + */ + markerOnSelected?: boolean; + + /** + * Marker be added to the map at the location the geocoding results. + * + * - If `true` or `undefined` then a default marker will be used. + * - If the value is a [MarkerOptions](https://leafletjs.com/reference.html#marker-option) then the marker will be constructed using these options. + * - If the value is a function then it can return instance of the [Marker](https://leafletjs.com/reference.html#marker). + * In this case the default pop-up won't be added to the marker. + * Function can accept `Feature` as a parameter. + * - If `false` or `null` then no marker will be added to the map. + * + * Default value is `true`. + */ + showResultMarkers?: null | boolean | MarkerOptions | ((map: LMap, feature: Feature) => undefined | null | Marker); + + /** + * Animation to picked feature on the map. + * + * - If `false` or `null` then animating the map to a selected result is disabled. + * - If `true` or `undefined` then animating the map will use the default animation parameters. + * - If the value is [ZoomPanOptions](https://leafletjs.com/reference.html#zoom/pan-options) + * then it will be passed as options to the map [flyTo](https://leafletjs.com/reference.html#map-flyto) + * or [fitBounds](https://leafletjs.com/reference.html#map-fitbounds) method providing control over the animation of the transition. + * + * Default value is `true`. + */ + flyTo?: null | boolean | ZoomPanOptions; + + /** + * Specifies if selected (not picked) feature should be also animated to on the map. + * + * Default: `false`. + */ + flyToSelected?: boolean; + + /** + * Style for full feature geometry GeoJSON. + * + * - If `false` or `null` then no full geometry is drawn. + * - If `true` or `undefined` then default-styled full geometry is drawn. + * - If an T then it must represent the style and will be used to style the full geometry. + * + * Default is the default style. + */ + fullGeometryStyle?: null | boolean | PathOptions | StyleFunction; + + /** + * Style of the picked result on the map: + * - `"marker-only"`: Show only a marker at the center of the feature. + * - `"full-geometry"`: Display the full feature geometry. + * - `"full-geometry-including-polygon-center-marker"`: Display full geometry with a marker at the polygon center. + * + * Default: `"full-geometry"`. + */ + pickedResultStyle?: PickedResultStyle; + + /** + * Specifies the zoom level to animate the map to for a geocoded result when no bounding box is present or when the result is a point. + * If a bounding box is present and not a point, the map will fit to the bounding box. + * + * Values are key-value pairs where the key is a `` or `.` and the value is the zoom level. + * + * Default: `ZOOM_DEFAULTS`. + */ + zoom?: Record; +} & ControlOptions; + +export const ZOOM_DEFAULTS: Record = { + continental_marine: 4, + country: 4, + major_landform: 8, + region: 5, + subregion: 6, + county: 7, + joint_municipality: 8, + joint_submunicipality: 9, + municipality: 10, + municipal_district: 11, + locality: 12, + neighbourhood: 13, + place: 14, + postal_code: 14, + road: 16, + poi: 17, + address: 18, + "poi.peak": 15, + "poi.shop": 18, + "poi.cafe": 18, + "poi.restaurant": 18, + "poi.aerodrome": 13, + // TODO add many more +}; + +export const DEFAULT_GEOMETRY_STYLE: StyleFunction = (feature) => { + const type = feature?.geometry.type; + const isMask = (feature?.properties as { isMask?: boolean } | undefined)?.isMask; + const weight = isMask ? 0 : type === "LineString" || type === "MultiLineString" ? 3 : 2; + + return { + color: "#3170fe", + fillColor: "#000", + fillOpacity: isMask ? 0.1 : 0, + weight, + dashArray: [weight, weight], + lineCap: "butt", + }; +}; diff --git a/src/controls/maplibregl-options.ts b/src/controls/maplibregl-options.ts index d76bf49..15a9711 100644 --- a/src/controls/maplibregl-options.ts +++ b/src/controls/maplibregl-options.ts @@ -26,8 +26,6 @@ export type MaplibreglGeocodingControlOptions = Omit undefined | null | Marker); @@ -49,8 +47,6 @@ export type MaplibreglGeocodingControlOptions = Omit undefined | null | Marker); diff --git a/src/leaflet.public.ts b/src/leaflet.public.ts new file mode 100644 index 0000000..cdc8834 --- /dev/null +++ b/src/leaflet.public.ts @@ -0,0 +1,7 @@ +export * from "."; +export * from "./components/marker"; +export * from "./controls/leaflet-control"; +export { LeafletGeocodingControl as GeocodingControl } from "./controls/leaflet-control"; +export type * as GeocodingControlEvent from "./controls/leaflet-events"; +export * from "./controls/leaflet-options"; +export type { LeafletGeocodingControlOptions as GeocodingControlOptions } from "./controls/leaflet-options"; diff --git a/src/utils/geo-utils.ts b/src/utils/geo-utils.ts index 57bb076..ad46985 100644 --- a/src/utils/geo-utils.ts +++ b/src/utils/geo-utils.ts @@ -1,3 +1,6 @@ +import clone from "@turf/clone"; +import type { FeatureCollection, MultiPolygon, Polygon, Position } from "geojson"; + import type { BBox } from "../types"; // taken from Leaflet @@ -22,3 +25,27 @@ export function unwrapBbox(bbox0: BBox): BBox { return bbox; } + +export function shiftPolyCollection(featureCollection: FeatureCollection, distance: number): FeatureCollection { + const cloned = clone(featureCollection); + + for (const feature of cloned.features) { + if (feature.geometry.type == "MultiPolygon") { + for (const poly of feature.geometry.coordinates) { + shiftPolyCoords(poly, distance); + } + } else { + shiftPolyCoords(feature.geometry.coordinates, distance); + } + } + + return cloned; +} + +export function shiftPolyCoords(coordinates: Position[][], distance: number) { + for (const ring of coordinates) { + for (const position of ring) { + position[0] += distance; + } + } +} diff --git a/tmp/@types/leaflet-v2/index.d.ts b/tmp/@types/leaflet-v2/index.d.ts new file mode 100644 index 0000000..d4670ce --- /dev/null +++ b/tmp/@types/leaflet-v2/index.d.ts @@ -0,0 +1,2572 @@ +export as namespace L; + +import * as geojson from "geojson"; + +/** A constant that represents the Leaflet version in use. */ +export const version: string; + +export class Class { + static extend(props: any): { new(...args: any[]): any } & typeof Class; + static include(props: any): typeof Class; + static setDefaultOptions(options: any): typeof Class; + static mergeOptions(options: any): typeof Class; + + static addInitHook(initHookFn: () => void): typeof Class; + static addInitHook(methodName: string, ...args: any[]): typeof Class; + + initialize(...args: any[]): void; + callInitHooks(): void; +} + +export class Transformation { + /** Instantiates a Transformation object with the given coefficients. */ + constructor(a: number, b: number, c: number, d: number); + /** Expects an coefficients array of the form `[a: Number, b: Number, c: Number, d: Number]`. */ + constructor(coefficients: [number, number, number, number]); + transform(point: Point, scale?: number): Point; + untransform(point: Point, scale?: number): Point; +} + +/** + * @see https://github.com/Leaflet/Leaflet/blob/bc918d4bdc2ba189807bc207c77080fb41ecc196/src/geometry/LineUtil.js#L118 + */ +export namespace LineUtil { + function simplify(points: Point[], tolerance: number): Point[]; + function pointToSegmentDistance(p: Point, p1: Point, p2: Point): number; + function closestPointOnSegment(p: Point, p1: Point, p2: Point): Point; + function isFlat(latlngs: LatLngExpression[]): boolean; + function clipSegment( + a: Point, + b: Point, + bounds: Bounds, + useLastCode?: boolean, + round?: boolean, + ): [Point, Point] | false; + function polylineCenter(latlngs: LatLngExpression[], crs: CRS): LatLng; +} + +export namespace PolyUtil { + function clipPolygon(points: Point[], bounds: BoundsExpression, round?: boolean): Point[]; + function polygonCenter(latlngs: LatLngExpression[], crs: CRS): LatLng; + function centroid(latlngs: LatLngExpression[]): LatLng; +} + +export namespace DomUtil { + /** + * Get Element by its ID or with the given HTML-Element + */ + function get(element: string | HTMLElement): HTMLElement | null; + /** + * Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element. + * @param tagName The name of the tag to create (for example: `div` or `canvas`). + * @param className The class to set on the created element. + * @param container The container to append the created element to. + */ + function create( + tagName: T, + className?: string, + container?: HTMLElement, + ): HTMLElementTagNameMap[T]; + function create(tagName: string, className?: string, container?: HTMLElement): HTMLElement; + function toFront(el: HTMLElement): void; + function toBack(el: HTMLElement): void; + function setTransform(el: HTMLElement, offset: Point, scale?: number): void; + function setPosition(el: HTMLElement, position: Point): void; + function getPosition(el: HTMLElement): Point; + function getScale(el: HTMLElement): { x: number; y: number; boundingClientRect: DOMRect }; + function getSizedParentNode(el: HTMLElement): HTMLElement; + function disableTextSelection(): void; + function enableTextSelection(): void; + function disableImageDrag(): void; + function enableImageDrag(): void; + function preventOutline(el: HTMLElement): void; + function restoreOutline(): void; +} + +export class PosAnimation extends Evented { + run(el: HTMLElement, newPos: Point, duration?: number, easeLinearity?: number): void; + stop(): void; +} + +export class CRS { + static projection: Projection | undefined; + static transformation: Transformation; + + static latLngToPoint(latlng: LatLngExpression, zoom: number): Point; + static pointToLatLng(point: PointExpression, zoom: number): LatLng; + static project(latlng: LatLngExpression): Point; + static unproject(point: PointExpression): LatLng; + static scale(zoom: number): number; + static zoom(scale: number): number; + static getProjectedBounds(zoom: number): Bounds; + + static infinite: boolean; + static wrapLatLng(latlng: LatLngExpression): LatLng; + static wrapLatLngBounds(bounds: LatLngBoundsExpression[]): LatLngBounds; +} + +export namespace CRS { + class Earth extends CRS { + static wrapLng: [number, number]; + static R: number; + + static distance(latlng1: LatLngExpression, latlng2: LatLngExpression): number; + } + + class EPSG3395 extends Earth { + static code: string; + static projection: Projection.Mercator; + } + + class EPSG3857 extends Earth { + static code: string; + static projection: Projection.SphericalMercator; + } + + class EPSG900913 extends EPSG3857 { + } + + class EPSG4326 extends Earth { + static code: string; + static projection: Projection.LonLat; + } + + class Simple extends CRS { + static projection: Projection.LonLat; + + static distance(latlng1: LatLngExpression, latlng2: LatLngExpression): Bounds; + } +} + +export interface Projection { + project(latlng: LatLngExpression): Point; + unproject(point: PointExpression): LatLng; + + bounds: Bounds; +} + +export namespace Projection { + type LonLat = Projection; + + interface Mercator extends Projection { + R: 6378137; + R_MINOR: 6356752.314245179; + } + + interface SphericalMercator extends Projection { + R: 6378137; + MAX_LATITUDE: 85.0511287798; + } +} + +export class LatLng { + constructor(latitude: number, longitude: number, altitude?: number); + constructor(coords: LatLngExpression); + static validate(latitude: number, longitude: number, altitude?: number): boolean; + static validate(coords: LatLngExpression): boolean; + equals(otherLatLng: LatLngExpression, maxMargin?: number): boolean; + toString(precision?: number): string; + distanceTo(otherLatLng: LatLngExpression): number; + wrap(): LatLng; + toBounds(sizeInMeters: number): LatLngBounds; + clone(): LatLng; + + lat: number; + lng: number; + alt?: number; +} +export interface LatLngLiteral { + lat: number; + lng: number; + alt?: number; +} + +export type LatLngTuple = [number, number, number?]; + +export type LatLngExpression = LatLng | LatLngTuple | LatLngLiteral; + +export class LatLngBounds { + constructor(southWest: LatLngExpression, northEast: LatLngExpression); + constructor(latlngs: LatLngExpression[]); + extend(latlngOrBounds: LatLngExpression | LatLngBoundsExpression): this; + pad(bufferRatio: number): LatLngBounds; // Returns a new LatLngBounds + getCenter(): LatLng; + getSouthWest(): LatLng; + getNorthEast(): LatLng; + getNorthWest(): LatLng; + getSouthEast(): LatLng; + getWest(): number; + getSouth(): number; + getEast(): number; + getNorth(): number; + contains(otherBoundsOrLatLng: LatLngBoundsExpression | LatLngExpression): boolean; + intersects(otherBounds: LatLngBoundsExpression): boolean; + overlaps(otherBounds: LatLngBoundsExpression): boolean; + toBBoxString(): string; + equals(otherBounds: LatLngBoundsExpression, maxMargin?: number): boolean; + isValid(): boolean; + + protected _southWest: LatLng; + protected _northEast: LatLng; +} + +export type LatLngBoundsLiteral = LatLngTuple[]; // Must be [LatLngTuple, LatLngTuple], cant't change because Map.setMaxBounds + +export type LatLngBoundsExpression = LatLngBounds | LatLngBoundsLiteral; + +export class Point { + constructor(x: number, y: number, round?: boolean); + constructor(coords: PointExpression); + static validate(x: number, y: number): boolean; + static validate(coords: PointExpression): boolean; + clone(): Point; + add(otherPoint: PointExpression): Point; // non-destructive, returns a new point + subtract(otherPoint: PointExpression): Point; + divideBy(num: number): Point; + multiplyBy(num: number): Point; + scaleBy(scale: PointExpression): Point; + unscaleBy(scale: PointExpression): Point; + round(): Point; + floor(): Point; + ceil(): Point; + trunc(): Point; + distanceTo(otherPoint: PointExpression): number; + equals(otherPoint: PointExpression): boolean; + contains(otherPoint: PointExpression): boolean; + toString(): string; + x: number; + y: number; +} + +export interface Coords extends Point { + z: number; +} + +export type PointTuple = [number, number]; + +export interface PointLiteral { + x: number; + y: number; +} + +export type PointExpression = Point | PointTuple | PointLiteral; + +export class Bounds { + constructor(); + constructor(topLeft: PointExpression, bottomRight: PointExpression); + constructor(points: PointExpression[] | BoundsExpression); + + extend(pointOrBounds: BoundsExpression | PointExpression): this; + + getCenter(round?: boolean): Point; + getBottomLeft(): Point; + getBottomRight(): Point; + getTopLeft(): Point; + getTopRight(): Point; + getSize(): Point; + contains(pointOrBounds: BoundsExpression | PointExpression): boolean; + intersects(otherBounds: BoundsExpression): boolean; + overlaps(otherBounds: BoundsExpression): boolean; + isValid(): boolean; + pad(bufferRatio: number): Bounds; // Returns a new Bounds + equals(otherBounds: BoundsExpression): boolean; + + min?: Point; + max?: Point; +} + +export type BoundsLiteral = [PointTuple, PointTuple]; + +export type BoundsExpression = Bounds | BoundsLiteral; + +// Events + +export interface LeafletEvent { + type: string; + popup: any; + target: any; + sourceTarget: any; + propagatedFrom: any; +} + +export interface LeafletMouseEvent extends LeafletEvent { + latlng: LatLng; + layerPoint: Point; + containerPoint: Point; + originalEvent: MouseEvent; +} + +export interface LeafletKeyboardEvent extends LeafletEvent { + originalEvent: KeyboardEvent; +} + +export interface LocationEvent extends LeafletEvent { + latlng: LatLng; + bounds: LatLngBounds; + accuracy: number; + altitude: number; + altitudeAccuracy: number; + heading: number; + speed: number; + timestamp: number; +} + +export interface ErrorEvent extends LeafletEvent { + message: string; + code: number; +} + +export interface LayerEvent extends LeafletEvent { + layer: Layer; +} + +export interface LayersControlEvent extends LayerEvent { + name: string; +} + +export interface TileEvent extends LeafletEvent { + tile: HTMLImageElement; + coords: Coords; +} + +export interface TileErrorEvent extends TileEvent { + error: Error; +} + +export interface ResizeEvent extends LeafletEvent { + oldSize: Point; + newSize: Point; +} + +export interface GeoJSONEvent extends LeafletEvent { + layer: Layer; + properties: any; + geometryType: string; + id: string; +} + +export interface PopupEvent extends LeafletEvent { + popup: Popup; +} + +export interface TooltipEvent extends LeafletEvent { + tooltip: Tooltip; +} + +export interface DragEndEvent extends LeafletEvent { + distance: number; +} + +export interface ZoomAnimEvent extends LeafletEvent { + center: LatLng; + zoom: number; + noUpdate: boolean; +} + +// Event handler types + +export type LeafletEventHandlerFn = (event: LeafletEvent) => void; + +export type LayersControlEventHandlerFn = (event: LayersControlEvent) => void; + +export type LayerEventHandlerFn = (event: LayerEvent) => void; + +export type ResizeEventHandlerFn = (event: ResizeEvent) => void; + +export type PopupEventHandlerFn = (event: PopupEvent) => void; + +export type TooltipEventHandlerFn = (event: TooltipEvent) => void; + +export type ErrorEventHandlerFn = (event: ErrorEvent) => void; + +export type LocationEventHandlerFn = (event: LocationEvent) => void; + +export type LeafletMouseEventHandlerFn = (event: LeafletMouseEvent) => void; + +export type LeafletKeyboardEventHandlerFn = (event: LeafletKeyboardEvent) => void; + +export type ZoomAnimEventHandlerFn = (event: ZoomAnimEvent) => void; + +export type DragEndEventHandlerFn = (event: DragEndEvent) => void; + +export type TileEventHandlerFn = (event: TileEvent) => void; + +export type TileErrorEventHandlerFn = (event: TileErrorEvent) => void; + +export interface LeafletEventHandlerFnMap { + baselayerchange?: LayersControlEventHandlerFn | undefined; + overlayadd?: LayersControlEventHandlerFn | undefined; + overlayremove?: LayersControlEventHandlerFn | undefined; + + layeradd?: LayerEventHandlerFn | undefined; + layerremove?: LayerEventHandlerFn | undefined; + + zoomlevelschange?: LeafletEventHandlerFn | undefined; + unload?: LeafletEventHandlerFn | undefined; + viewreset?: LeafletEventHandlerFn | undefined; + load?: LeafletEventHandlerFn | undefined; + zoomstart?: LeafletEventHandlerFn | undefined; + movestart?: LeafletEventHandlerFn | undefined; + zoom?: LeafletEventHandlerFn | undefined; + move?: LeafletEventHandlerFn | undefined; + zoomend?: LeafletEventHandlerFn | undefined; + moveend?: LeafletEventHandlerFn | undefined; + autopanstart?: LeafletEventHandlerFn | undefined; + dragstart?: LeafletEventHandlerFn | undefined; + drag?: LeafletEventHandlerFn | undefined; + add?: LeafletEventHandlerFn | undefined; + remove?: LeafletEventHandlerFn | undefined; + loading?: LeafletEventHandlerFn | undefined; + error?: LeafletEventHandlerFn | undefined; + update?: LeafletEventHandlerFn | undefined; + down?: LeafletEventHandlerFn | undefined; + predrag?: LeafletEventHandlerFn | undefined; + + resize?: ResizeEventHandlerFn | undefined; + + popupopen?: PopupEventHandlerFn | undefined; + popupclose?: PopupEventHandlerFn | undefined; + + tooltipopen?: TooltipEventHandlerFn | undefined; + tooltipclose?: TooltipEventHandlerFn | undefined; + + locationerror?: ErrorEventHandlerFn | undefined; + + locationfound?: LocationEventHandlerFn | undefined; + + click?: LeafletMouseEventHandlerFn | undefined; + dblclick?: LeafletMouseEventHandlerFn | undefined; + mousedown?: LeafletMouseEventHandlerFn | undefined; + mouseup?: LeafletMouseEventHandlerFn | undefined; + mouseover?: LeafletMouseEventHandlerFn | undefined; + mouseout?: LeafletMouseEventHandlerFn | undefined; + mousemove?: LeafletMouseEventHandlerFn | undefined; + contextmenu?: LeafletMouseEventHandlerFn | undefined; + preclick?: LeafletMouseEventHandlerFn | undefined; + + keypress?: LeafletKeyboardEventHandlerFn | undefined; + keydown?: LeafletKeyboardEventHandlerFn | undefined; + keyup?: LeafletKeyboardEventHandlerFn | undefined; + + zoomanim?: ZoomAnimEventHandlerFn | undefined; + + dragend?: DragEndEventHandlerFn | undefined; + + tileunload?: TileEventHandlerFn | undefined; + tileloadstart?: TileEventHandlerFn | undefined; + tileload?: TileEventHandlerFn | undefined; + tileabort?: TileEventHandlerFn | undefined; + + tileerror?: TileErrorEventHandlerFn | undefined; + + // [name: string]: any; + // You are able add additional properties, but it makes this interface uncheckable. +} + +/** + * A set of methods shared between event-powered classes (like Map and Marker). + * Generally, events allow you to execute some function when something happens + * with an object (e.g. the user clicks on the map, causing the map to fire + * 'click' event). + */ +// eslint-disable-next-line @definitelytyped/strict-export-declare-modifiers +declare class Events { + /** + * Adds a listener function (fn) to a particular event type of the object. + * You can optionally specify the context of the listener (object the this + * keyword will point to). You can also pass several space-separated types + * (e.g. 'click dblclick'). + */ + // tslint:disable:unified-signatures + on(type: "baselayerchange" | "overlayadd" | "overlayremove", fn: LayersControlEventHandlerFn, context?: any): this; + on(type: "layeradd" | "layerremove", fn: LayerEventHandlerFn, context?: any): this; + on( + type: + | "zoomlevelschange" + | "unload" + | "viewreset" + | "load" + | "zoomstart" + | "movestart" + | "zoom" + | "move" + | "zoomend" + | "moveend" + | "autopanstart" + | "dragstart" + | "drag" + | "add" + | "remove" + | "loading" + | "error" + | "update" + | "down" + | "predrag", + fn: LeafletEventHandlerFn, + context?: any, + ): this; + on(type: "resize", fn: ResizeEventHandlerFn, context?: any): this; + on(type: "popupopen" | "popupclose", fn: PopupEventHandlerFn, context?: any): this; + on(type: "tooltipopen" | "tooltipclose", fn: TooltipEventHandlerFn, context?: any): this; + on(type: "locationerror", fn: ErrorEventHandlerFn, context?: any): this; + on(type: "locationfound", fn: LocationEventHandlerFn, context?: any): this; + on( + type: + | "click" + | "dblclick" + | "mousedown" + | "mouseup" + | "mouseover" + | "mouseout" + | "mousemove" + | "contextmenu" + | "preclick", + fn: LeafletMouseEventHandlerFn, + context?: any, + ): this; + on(type: "keypress" | "keydown" | "keyup", fn: LeafletKeyboardEventHandlerFn, context?: any): this; + on(type: "zoomanim", fn: ZoomAnimEventHandlerFn, context?: any): this; + on(type: "dragend", fn: DragEndEventHandlerFn, context?: any): this; + on(type: "tileunload" | "tileloadstart" | "tileload" | "tileabort", fn: TileEventHandlerFn, context?: any): this; + on(type: "tileerror", fn: TileErrorEventHandlerFn, context?: any): this; + on(type: string, fn: LeafletEventHandlerFn, context?: any): this; + + /** + * Adds a set of type/listener pairs, e.g. {click: onClick, mousemove: onMouseMove} + */ + on(eventMap: LeafletEventHandlerFnMap): this; + // tslint:enable:unified-signatures + + /** + * Removes a previously added listener function. If no function is specified, + * it will remove all the listeners of that particular event from the object. + * Note that if you passed a custom context to on, you must pass the same context + * to off in order to remove the listener. + */ + // tslint:disable:unified-signatures + off( + type: "baselayerchange" | "overlayadd" | "overlayremove", + fn?: LayersControlEventHandlerFn, + context?: any, + ): this; + off(type: "layeradd" | "layerremove", fn?: LayerEventHandlerFn, context?: any): this; + off( + type: + | "zoomlevelschange" + | "unload" + | "viewreset" + | "load" + | "zoomstart" + | "movestart" + | "zoom" + | "move" + | "zoomend" + | "moveend" + | "autopanstart" + | "dragstart" + | "drag" + | "add" + | "remove" + | "loading" + | "error" + | "update" + | "down" + | "predrag", + fn?: LeafletEventHandlerFn, + context?: any, + ): this; + off(type: "resize", fn?: ResizeEventHandlerFn, context?: any): this; + off(type: "popupopen" | "popupclose", fn?: PopupEventHandlerFn, context?: any): this; + off(type: "tooltipopen" | "tooltipclose", fn?: TooltipEventHandlerFn, context?: any): this; + off(type: "locationerror", fn?: ErrorEventHandlerFn, context?: any): this; + off(type: "locationfound", fn?: LocationEventHandlerFn, context?: any): this; + off( + type: + | "click" + | "dblclick" + | "mousedown" + | "mouseup" + | "mouseover" + | "mouseout" + | "mousemove" + | "contextmenu" + | "preclick", + fn?: LeafletMouseEventHandlerFn, + context?: any, + ): this; + off(type: "keypress" | "keydown" | "keyup", fn?: LeafletKeyboardEventHandlerFn, context?: any): this; + off(type: "zoomanim", fn?: ZoomAnimEventHandlerFn, context?: any): this; + off(type: "dragend", fn?: DragEndEventHandlerFn, context?: any): this; + off(type: "tileunload" | "tileloadstart" | "tileload" | "tileabort", fn?: TileEventHandlerFn, context?: any): this; + off(type: "tileerror", fn?: TileErrorEventHandlerFn, context?: any): this; + off(type: string, fn?: LeafletEventHandlerFn, context?: any): this; + + /** + * Removes a set of type/listener pairs. + */ + // With an eventMap there are no additional arguments allowed + off(eventMap: LeafletEventHandlerFnMap): this; + + /** + * Removes all listeners to all events on the object. + */ + off(): this; + // tslint:enable:unified-signatures + + /** + * Fires an event of the specified type. You can optionally provide a data + * object — the first argument of the listener function will contain its properties. + * The event might can optionally be propagated to event parents. + */ + fire(type: string, data?: any, propagate?: boolean): this; + + /** + * Returns true if a particular event type has any listeners attached to it. + */ + // tslint:disable:unified-signatures + listens( + type: + | "baselayerchange" + | "overlayadd" + | "overlayremove" + | "layeradd" + | "layerremove" + | "zoomlevelschange" + | "unload" + | "viewreset" + | "load" + | "zoomstart" + | "movestart" + | "zoom" + | "move" + | "zoomend" + | "moveend" + | "autopanstart" + | "dragstart" + | "drag" + | "add" + | "remove" + | "loading" + | "error" + | "update" + | "down" + | "predrag" + | "resize" + | "popupopen" + | "tooltipopen" + | "tooltipclose" + | "locationerror" + | "locationfound" + | "click" + | "dblclick" + | "mousedown" + | "mouseup" + | "mouseover" + | "mouseout" + | "mousemove" + | "contextmenu" + | "preclick" + | "keypress" + | "keydown" + | "keyup" + | "zoomanim" + | "dragend" + | "tileunload" + | "tileloadstart" + | "tileload" + | "tileabort" + | "tileerror", + propagate?: boolean, + ): boolean; + + listens( + type: "baselayerchange" | "overlayadd" | "overlayremove", + fn: LayersControlEventHandlerFn, + context?: any, + propagate?: boolean, + ): boolean; + listens(type: "layeradd" | "layerremove", fn: LayerEventHandlerFn, context?: any, propagate?: boolean): boolean; + listens( + type: + | "zoomlevelschange" + | "unload" + | "viewreset" + | "load" + | "zoomstart" + | "movestart" + | "zoom" + | "move" + | "zoomend" + | "moveend" + | "autopanstart" + | "dragstart" + | "drag" + | "add" + | "remove" + | "loading" + | "error" + | "update" + | "down" + | "predrag", + fn: LeafletEventHandlerFn, + context?: any, + propagate?: boolean, + ): boolean; + listens(type: "resize", fn: ResizeEventHandlerFn, context?: any, propagate?: boolean): boolean; + listens(type: "popupopen" | "popupclose", fn: PopupEventHandlerFn, context?: any, propagate?: boolean): boolean; + listens( + type: "tooltipopen" | "tooltipclose", + fn: TooltipEventHandlerFn, + context?: any, + propagate?: boolean, + ): boolean; + listens(type: "locationerror", fn: ErrorEventHandlerFn, context?: any, propagate?: boolean): boolean; + listens(type: "locationfound", fn: LocationEventHandlerFn, context?: any, propagate?: boolean): boolean; + listens( + type: + | "click" + | "dblclick" + | "mousedown" + | "mouseup" + | "mouseover" + | "mouseout" + | "mousemove" + | "contextmenu" + | "preclick", + fn: LeafletMouseEventHandlerFn, + context?: any, + propagate?: boolean, + ): boolean; + listens( + type: "keypress" | "keydown" | "keyup", + fn: LeafletKeyboardEventHandlerFn, + context?: any, + propagate?: boolean, + ): boolean; + listens(type: "zoomanim", fn: ZoomAnimEventHandlerFn, context?: any, propagate?: boolean): boolean; + listens(type: "dragend", fn: DragEndEventHandlerFn, context?: any, propagate?: boolean): boolean; + listens( + type: "tileunload" | "tileloadstart" | "tileload" | "tileabort", + fn: TileEventHandlerFn, + context?: any, + propagate?: boolean, + ): boolean; + listens(type: "tileerror", fn: TileEventHandlerFn, context?: any, propagate?: boolean): boolean; + listens(type: string, fn: LeafletEventHandlerFn, context?: any, propagate?: boolean): boolean; + + /** + * Behaves as on(...), except the listener will only get fired once and then removed. + */ + // tslint:disable:unified-signatures + once( + type: "baselayerchange" | "overlayadd" | "overlayremove", + fn: LayersControlEventHandlerFn, + context?: any, + ): this; + once(type: "layeradd" | "layerremove", fn: LayerEventHandlerFn, context?: any): this; + once( + type: + | "zoomlevelschange" + | "unload" + | "viewreset" + | "load" + | "zoomstart" + | "movestart" + | "zoom" + | "move" + | "zoomend" + | "moveend" + | "autopanstart" + | "dragstart" + | "drag" + | "add" + | "remove" + | "loading" + | "error" + | "update" + | "down" + | "predrag", + fn: LeafletEventHandlerFn, + context?: any, + ): this; + once(type: "resize", fn: ResizeEventHandlerFn, context?: any): this; + once(type: "popupopen" | "popupclose", fn: PopupEventHandlerFn, context?: any): this; + once(type: "tooltipopen" | "tooltipclose", fn: TooltipEventHandlerFn, context?: any): this; + once(type: "locationerror", fn: ErrorEventHandlerFn, context?: any): this; + once(type: "locationfound", fn: LocationEventHandlerFn, context?: any): this; + once( + type: + | "click" + | "dblclick" + | "mousedown" + | "mouseup" + | "mouseover" + | "mouseout" + | "mousemove" + | "contextmenu" + | "preclick", + fn: LeafletMouseEventHandlerFn, + context?: any, + ): this; + once(type: "keypress" | "keydown" | "keyup", fn: LeafletKeyboardEventHandlerFn, context?: any): this; + once(type: "zoomanim", fn: ZoomAnimEventHandlerFn, context?: any): this; + once(type: "dragend", fn: DragEndEventHandlerFn, context?: any): this; + once(type: "tileunload" | "tileloadstart" | "tileload" | "tileabort", fn: TileEventHandlerFn, context?: any): this; + once(type: "tileerror", fn: TileEventHandlerFn, context?: any): this; + once(type: string, fn: LeafletEventHandlerFn, context?: any): this; + + /** + * Behaves as on(...), except the listener will only get fired once and then removed. + */ + once(eventMap: LeafletEventHandlerFnMap): this; + // tslint:enable:unified-signatures + + /** + * Adds an event parent - an Evented that will receive propagated events + */ + addEventParent(obj: Evented): this; + + /** + * Removes an event parent, so it will stop receiving propagated events + */ + removeEventParent(obj: Evented): this; +} + +// eslint-disable-next-line @definitelytyped/strict-export-declare-modifiers +declare class MixinType { + Events: Events; +} + +export const Mixin: MixinType; + +/** + * Base class of Leaflet classes supporting events + */ +export abstract class Evented extends Class { + /** + * Adds a listener function (fn) to a particular event type of the object. + * You can optionally specify the context of the listener (object the this + * keyword will point to). You can also pass several space-separated types + * (e.g. 'click dblclick'). + */ + // tslint:disable:unified-signatures + on(type: "baselayerchange" | "overlayadd" | "overlayremove", fn: LayersControlEventHandlerFn, context?: any): this; + on(type: "layeradd" | "layerremove", fn: LayerEventHandlerFn, context?: any): this; + on( + type: + | "zoomlevelschange" + | "unload" + | "viewreset" + | "load" + | "zoomstart" + | "movestart" + | "zoom" + | "move" + | "zoomend" + | "moveend" + | "autopanstart" + | "dragstart" + | "drag" + | "add" + | "remove" + | "loading" + | "error" + | "update" + | "down" + | "predrag", + fn: LeafletEventHandlerFn, + context?: any, + ): this; + on(type: "resize", fn: ResizeEventHandlerFn, context?: any): this; + on(type: "popupopen" | "popupclose", fn: PopupEventHandlerFn, context?: any): this; + on(type: "tooltipopen" | "tooltipclose", fn: TooltipEventHandlerFn, context?: any): this; + on(type: "locationerror", fn: ErrorEventHandlerFn, context?: any): this; + on(type: "locationfound", fn: LocationEventHandlerFn, context?: any): this; + on( + type: + | "click" + | "dblclick" + | "mousedown" + | "mouseup" + | "mouseover" + | "mouseout" + | "mousemove" + | "contextmenu" + | "preclick", + fn: LeafletMouseEventHandlerFn, + context?: any, + ): this; + on(type: "keypress" | "keydown" | "keyup", fn: LeafletKeyboardEventHandlerFn, context?: any): this; + on(type: "zoomanim", fn: ZoomAnimEventHandlerFn, context?: any): this; + on(type: "dragend", fn: DragEndEventHandlerFn, context?: any): this; + on(type: "tileunload" | "tileloadstart" | "tileload" | "tileabort", fn: TileEventHandlerFn, context?: any): this; + on(type: "tileerror", fn: TileErrorEventHandlerFn, context?: any): this; + on(type: string, fn: LeafletEventHandlerFn, context?: any): this; + + /** + * Adds a set of type/listener pairs, e.g. {click: onClick, mousemove: onMouseMove} + */ + on(eventMap: LeafletEventHandlerFnMap): this; + // tslint:enable:unified-signatures + + /** + * Removes a previously added listener function. If no function is specified, + * it will remove all the listeners of that particular event from the object. + * Note that if you passed a custom context to on, you must pass the same context + * to off in order to remove the listener. + */ + // tslint:disable:unified-signatures + off( + type: "baselayerchange" | "overlayadd" | "overlayremove", + fn?: LayersControlEventHandlerFn, + context?: any, + ): this; + off(type: "layeradd" | "layerremove", fn?: LayerEventHandlerFn, context?: any): this; + off( + type: + | "zoomlevelschange" + | "unload" + | "viewreset" + | "load" + | "zoomstart" + | "movestart" + | "zoom" + | "move" + | "zoomend" + | "moveend" + | "autopanstart" + | "dragstart" + | "drag" + | "add" + | "remove" + | "loading" + | "error" + | "update" + | "down" + | "predrag", + fn?: LeafletEventHandlerFn, + context?: any, + ): this; + off(type: "resize", fn?: ResizeEventHandlerFn, context?: any): this; + off(type: "popupopen" | "popupclose", fn?: PopupEventHandlerFn, context?: any): this; + off(type: "tooltipopen" | "tooltipclose", fn?: TooltipEventHandlerFn, context?: any): this; + off(type: "locationerror", fn?: ErrorEventHandlerFn, context?: any): this; + off(type: "locationfound", fn?: LocationEventHandlerFn, context?: any): this; + off( + type: + | "click" + | "dblclick" + | "mousedown" + | "mouseup" + | "mouseover" + | "mouseout" + | "mousemove" + | "contextmenu" + | "preclick", + fn?: LeafletMouseEventHandlerFn, + context?: any, + ): this; + off(type: "keypress" | "keydown" | "keyup", fn?: LeafletKeyboardEventHandlerFn, context?: any): this; + off(type: "zoomanim", fn?: ZoomAnimEventHandlerFn, context?: any): this; + off(type: "dragend", fn?: DragEndEventHandlerFn, context?: any): this; + off(type: "tileunload" | "tileloadstart" | "tileload" | "tileabort", fn?: TileEventHandlerFn, context?: any): this; + off(type: "tileerror", fn?: TileErrorEventHandlerFn, context?: any): this; + off(type: string, fn?: LeafletEventHandlerFn, context?: any): this; + + /** + * Removes a set of type/listener pairs. + */ + // With an eventMap there are no additional arguments allowed + off(eventMap: LeafletEventHandlerFnMap): this; + + /** + * Removes all listeners to all events on the object. + */ + off(): this; + // tslint:enable:unified-signatures + + /** + * Fires an event of the specified type. You can optionally provide a data + * object — the first argument of the listener function will contain its properties. + * The event might can optionally be propagated to event parents. + */ + fire(type: string, data?: any, propagate?: boolean): this; + + /** + * Returns true if a particular event type has any listeners attached to it. + */ + // tslint:disable:unified-signatures + listens( + type: + | "baselayerchange" + | "overlayadd" + | "overlayremove" + | "layeradd" + | "layerremove" + | "zoomlevelschange" + | "unload" + | "viewreset" + | "load" + | "zoomstart" + | "movestart" + | "zoom" + | "move" + | "zoomend" + | "moveend" + | "autopanstart" + | "dragstart" + | "drag" + | "add" + | "remove" + | "loading" + | "error" + | "update" + | "down" + | "predrag" + | "resize" + | "popupopen" + | "tooltipopen" + | "tooltipclose" + | "locationerror" + | "locationfound" + | "click" + | "dblclick" + | "mousedown" + | "mouseup" + | "mouseover" + | "mouseout" + | "mousemove" + | "contextmenu" + | "preclick" + | "keypress" + | "keydown" + | "keyup" + | "zoomanim" + | "dragend" + | "tileunload" + | "tileloadstart" + | "tileload" + | "tileabort" + | "tileerror", + propagate?: boolean, + ): boolean; + + listens( + type: "baselayerchange" | "overlayadd" | "overlayremove", + fn: LayersControlEventHandlerFn, + context?: any, + propagate?: boolean, + ): boolean; + listens(type: "layeradd" | "layerremove", fn: LayerEventHandlerFn, context?: any, propagate?: boolean): boolean; + listens( + type: + | "zoomlevelschange" + | "unload" + | "viewreset" + | "load" + | "zoomstart" + | "movestart" + | "zoom" + | "move" + | "zoomend" + | "moveend" + | "autopanstart" + | "dragstart" + | "drag" + | "add" + | "remove" + | "loading" + | "error" + | "update" + | "down" + | "predrag", + fn: LeafletEventHandlerFn, + context?: any, + propagate?: boolean, + ): boolean; + listens(type: "resize", fn: ResizeEventHandlerFn, context?: any, propagate?: boolean): boolean; + listens(type: "popupopen" | "popupclose", fn: PopupEventHandlerFn, context?: any, propagate?: boolean): boolean; + listens( + type: "tooltipopen" | "tooltipclose", + fn: TooltipEventHandlerFn, + context?: any, + propagate?: boolean, + ): boolean; + listens(type: "locationerror", fn: ErrorEventHandlerFn, context?: any, propagate?: boolean): boolean; + listens(type: "locationfound", fn: LocationEventHandlerFn, context?: any, propagate?: boolean): boolean; + listens( + type: + | "click" + | "dblclick" + | "mousedown" + | "mouseup" + | "mouseover" + | "mouseout" + | "mousemove" + | "contextmenu" + | "preclick", + fn: LeafletMouseEventHandlerFn, + context?: any, + propagate?: boolean, + ): boolean; + listens( + type: "keypress" | "keydown" | "keyup", + fn: LeafletKeyboardEventHandlerFn, + context?: any, + propagate?: boolean, + ): boolean; + listens(type: "zoomanim", fn: ZoomAnimEventHandlerFn, context?: any, propagate?: boolean): boolean; + listens(type: "dragend", fn: DragEndEventHandlerFn, context?: any, propagate?: boolean): boolean; + listens( + type: "tileunload" | "tileloadstart" | "tileload" | "tileabort", + fn: TileEventHandlerFn, + context?: any, + propagate?: boolean, + ): boolean; + listens(type: "tileerror", fn: TileEventHandlerFn, context?: any, propagate?: boolean): boolean; + listens(type: string, fn: LeafletEventHandlerFn, context?: any, propagate?: boolean): boolean; + + /** + * Behaves as on(...), except the listener will only get fired once and then removed. + */ + // tslint:disable:unified-signatures + once( + type: "baselayerchange" | "overlayadd" | "overlayremove", + fn: LayersControlEventHandlerFn, + context?: any, + ): this; + once(type: "layeradd" | "layerremove", fn: LayerEventHandlerFn, context?: any): this; + once( + type: + | "zoomlevelschange" + | "unload" + | "viewreset" + | "load" + | "zoomstart" + | "movestart" + | "zoom" + | "move" + | "zoomend" + | "moveend" + | "autopanstart" + | "dragstart" + | "drag" + | "add" + | "remove" + | "loading" + | "error" + | "update" + | "down" + | "predrag", + fn: LeafletEventHandlerFn, + context?: any, + ): this; + once(type: "resize", fn: ResizeEventHandlerFn, context?: any): this; + once(type: "popupopen" | "popupclose", fn: PopupEventHandlerFn, context?: any): this; + once(type: "tooltipopen" | "tooltipclose", fn: TooltipEventHandlerFn, context?: any): this; + once(type: "locationerror", fn: ErrorEventHandlerFn, context?: any): this; + once(type: "locationfound", fn: LocationEventHandlerFn, context?: any): this; + once( + type: + | "click" + | "dblclick" + | "mousedown" + | "mouseup" + | "mouseover" + | "mouseout" + | "mousemove" + | "contextmenu" + | "preclick", + fn: LeafletMouseEventHandlerFn, + context?: any, + ): this; + once(type: "keypress" | "keydown" | "keyup", fn: LeafletKeyboardEventHandlerFn, context?: any): this; + once(type: "zoomanim", fn: ZoomAnimEventHandlerFn, context?: any): this; + once(type: "dragend", fn: DragEndEventHandlerFn, context?: any): this; + once(type: "tileunload" | "tileloadstart" | "tileload" | "tileabort", fn: TileEventHandlerFn, context?: any): this; + once(type: "tileerror", fn: TileEventHandlerFn, context?: any): this; + once(type: string, fn: LeafletEventHandlerFn, context?: any): this; + + /** + * Behaves as on(...), except the listener will only get fired once and then removed. + */ + once(eventMap: LeafletEventHandlerFnMap): this; + // tslint:enable:unified-signatures + + /** + * Adds an event parent - an Evented that will receive propagated events + */ + addEventParent(obj: Evented): this; + + /** + * Removes an event parent, so it will stop receiving propagated events + */ + removeEventParent(obj: Evented): this; +} + +export interface DraggableOptions { + /** + * The max number of pixels a user can shift the mouse pointer during a click + * for it to be considered a valid click (as opposed to a mouse drag). + */ + clickTolerance: number; +} + +/** + * A class for making DOM elements draggable (including touch support). + * Used internally for map and marker dragging. Only works for elements + * that were positioned with [`DomUtil.setPosition`](#domutil-setposition). + */ +export class Draggable extends Evented { + constructor( + element: HTMLElement, + dragStartTarget?: HTMLElement, + preventOutline?: boolean, + options?: DraggableOptions, + ); + + enable(): void; + + disable(): void; + + finishDrag(): void; +} + +export interface LayerOptions { + pane?: string | undefined; + attribution?: string | undefined; + // TODO: should it be in InteractiveLayerOptions because it will be only used if interactive is true? + // But GridLayerOptions and other options extend from LayerOptions but bubblingPointerEvents is not used by Leaflet core in the tile layers but maybe by plugins. + bubblingPointerEvents?: boolean | undefined; +} + +export interface InteractiveLayerOptions extends LayerOptions { + interactive?: boolean | undefined; +} + +export class Layer extends Evented { + // TODO: InteractiveLayerOptions? + constructor(options?: LayerOptions); + addTo(map: Map | LayerGroup): this; + remove(): this; + removeFrom(map: Map | LayerGroup): this; + getPane(name?: string): HTMLElement | undefined; + + addInteractiveTarget(targetEl: HTMLElement): this; + removeInteractiveTarget(targetEl: HTMLElement): this; + + // Popup methods + bindPopup(content: ((layer: Layer) => Content) | Content | Popup, options?: PopupOptions): this; + unbindPopup(): this; + openPopup(latlng?: LatLngExpression): this; + closePopup(): this; + togglePopup(): this; + isPopupOpen(): boolean; + setPopupContent(content: Content | ((layer: Layer) => Content)): this; + getPopup(): Popup | undefined; + + // Tooltip methods + bindTooltip(content: ((layer: Layer) => Content) | Content | Tooltip, options?: TooltipOptions): this; + unbindTooltip(): this; + openTooltip(latlng?: LatLngExpression): this; + closeTooltip(): this; + toggleTooltip(): this; + isTooltipOpen(): boolean; + setTooltipContent(content: Content | ((layer: Layer) => Content)): this; + getTooltip(): Tooltip | undefined; + + // Extension methods + onAdd(map: Map): this; + onRemove(map: Map): this; + getEvents?(): { [name: string]: LeafletEventHandlerFn }; + getAttribution?(): string | null; + beforeAdd?(map: Map): this; + + protected _map: Map; + + // TODO: InteractiveLayerOptions? + options: LayerOptions; +} + +export interface GridLayerOptions extends LayerOptions { + tileSize?: number | Point | undefined; + opacity?: number | undefined; + updateWhenIdle?: boolean | undefined; + updateWhenZooming?: boolean | undefined; + updateInterval?: number | undefined; + zIndex?: number | undefined; + bounds?: LatLngBoundsExpression | undefined; + minZoom?: number | undefined; + maxZoom?: number | undefined; + /** + * Maximum zoom number the tile source has available. If it is specified, the tiles on all zoom levels higher than + * `maxNativeZoom` will be loaded from `maxNativeZoom` level and auto-scaled. + */ + maxNativeZoom?: number | undefined; + /** + * Minimum zoom number the tile source has available. If it is specified, the tiles on all zoom levels lower than + * `minNativeZoom` will be loaded from `minNativeZoom` level and auto-scaled. + */ + minNativeZoom?: number | undefined; + noWrap?: boolean | undefined; + pane?: string | undefined; + className?: string | undefined; + keepBuffer?: number | undefined; +} + +export type DoneCallback = (error?: Error, tile?: HTMLElement) => void; + +export interface InternalTiles { + [key: string]: { + active?: boolean | undefined; + coords: Coords; + current: boolean; + el: HTMLElement; + loaded?: Date | undefined; + retain?: boolean | undefined; + }; +} + +export class GridLayer extends Layer { + constructor(options?: GridLayerOptions); + bringToFront(): this; + bringToBack(): this; + getContainer(): HTMLElement | null; + setOpacity(opacity: number): this; + setZIndex(zIndex: number): this; + isLoading(): boolean; + redraw(): this; + getTileSize(): Point; + + protected createTile(coords: Coords, done?: DoneCallback): HTMLElement; + protected _tileCoordsToKey(coords: Coords): string; + protected _wrapCoords(parameter: Coords): Coords; + + protected _tiles: InternalTiles; + protected _tileZoom?: number | undefined; + + options: GridLayerOptions; +} + +export interface TileLayerOptions extends GridLayerOptions { + id?: string | undefined; + subdomains?: string | string[] | undefined; + errorTileUrl?: string | undefined; + zoomOffset?: number | undefined; + tms?: boolean | undefined; + zoomReverse?: boolean | undefined; + detectRetina?: boolean | undefined; + crossOrigin?: CrossOrigin | boolean | undefined; + referrerPolicy?: ReferrerPolicy | boolean | undefined; + // [name: string]: any; + // You are able add additional properties, but it makes this interface uncheckable. + // See: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/15313 + // Example: + // tileLayer = new TileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png?{foo}&{bar}&{abc}', {foo: 'bar', bar: (data: any) => 'foo', abc: () => ''}); +} + +export class TileLayer extends GridLayer { + constructor(urlTemplate: string, options?: TileLayerOptions); + setUrl(url: string, noRedraw?: boolean): this; + getTileUrl(coords: Coords): string; + + // ? + protected createTile(coords: Coords, done?: DoneCallback): HTMLElement; + + protected _tileOnLoad(done: DoneCallback, tile: HTMLElement): void; + protected _tileOnError(done: DoneCallback, tile: HTMLElement, e: Error): void; + protected _abortLoading(): void; + protected _getZoomForUrl(): number; + + options: TileLayerOptions; +} + +export namespace TileLayer { + class WMS extends TileLayer { + constructor(baseUrl: string, options: WMSOptions); + setParams(params: WMSParams, noRedraw?: boolean): this; + + // TODO: is this correctly added? + static defaultWmsParams: WMSParams; + + wmsParams: WMSParams; + options: WMSOptions; + } +} + +export interface WMSOptions extends TileLayerOptions { + layers?: string | undefined; + styles?: string | undefined; + format?: string | undefined; + transparent?: boolean | undefined; + version?: string | undefined; + crs?: CRS | undefined; + uppercase?: boolean | undefined; +} + +export interface WMSParams { + format?: string | undefined; + layers: string; + request?: string | undefined; + service?: string | undefined; + styles?: string | undefined; + version?: string | undefined; + transparent?: boolean | undefined; + width?: number | undefined; + height?: number | undefined; +} + +export type CrossOrigin = "anonymous" | "use-credentials" | ""; +export type ReferrerPolicy = + | "no-referrer" + | "no-referrer-when-downgrade" + | "origin" + | "origin-when-cross-origin" + | "same-origin" + | "strict-origin" + | "strict-origin-when-cross-origin" + | "unsafe-url"; + +export interface ImageOverlayOptions extends InteractiveLayerOptions { + opacity?: number | undefined; + alt?: string | undefined; + interactive?: boolean | undefined; + crossOrigin?: CrossOrigin | boolean | undefined; + errorOverlayUrl?: string | undefined; + zIndex?: number | undefined; + className?: string | undefined; + decoding?: string | undefined; +} + +export interface ImageOverlayStyleOptions { + opacity?: number; + + // TODO: should it be removed? + [name: string]: any; +} + +export class ImageOverlay extends Layer { + constructor(imageUrl: string, bounds: LatLngBoundsExpression, options?: ImageOverlayOptions); + bringToFront(): this; + bringToBack(): this; + setUrl(url: string): this; + + /** Update the bounds that this ImageOverlay covers */ + setBounds(bounds: LatLngBoundsExpression): this; + + /** Changes the zIndex of the image overlay */ + setZIndex(value: number): this; + + /** Changes the opacity of the image element */ + setOpacity(opacity: number): this; + + /** Changes the style of the image element. As of 1.8, only the opacity is changed */ + setStyle(styleOpts: ImageOverlayStyleOptions): this; + + /** Get the bounds that this ImageOverlay covers */ + getBounds(): LatLngBounds; + + /** Get the center of the bounds this ImageOverlay covers */ + getCenter(): LatLng; + + /** Get the img element that represents the ImageOverlay on the map */ + getElement(): T | undefined; + + options: ImageOverlayOptions; +} + +export type SVGOverlayOptions = ImageOverlayOptions; +export type SVGOverlayStyleOptions = ImageOverlayStyleOptions; + +export class SVGOverlay extends ImageOverlay { + constructor(svgImage: string | SVGElement, bounds: LatLngBoundsExpression, options?: SVGOverlayOptions); + /** Changes the style of the image element. As of 1.8, only the opacity is changed */ + setStyle(styleOpts: SVGOverlayStyleOptions): this; + + options: SVGOverlayOptions; +} + +export interface VideoOverlayOptions extends ImageOverlayOptions { + /** Whether the video starts playing automatically when loaded. */ + autoplay?: boolean | undefined; + /** Whether the video will loop back to the beginning when played. */ + loop?: boolean | undefined; + /** + * Whether the video will save aspect ratio after the projection. Relevant for supported browsers. See + * [browser compatibility](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) + */ + keepAspectRatio?: boolean | undefined; + /** Whether the video starts on mute when loaded. */ + muted?: boolean | undefined; + playsInline?: boolean | undefined; + controls?: boolean | undefined; +} +export type VideoOverlayStyleOptions = ImageOverlayStyleOptions; + +export class VideoOverlay extends ImageOverlay { + constructor( + video: string | string[] | HTMLVideoElement, + bounds: LatLngBoundsExpression, + options?: VideoOverlayOptions, + ); + /** Changes the style of the image element. As of 1.8, only the opacity is changed */ + setStyle(styleOpts: VideoOverlayStyleOptions): this; + + /** Get the video element that represents the VideoOverlay on the map */ + getElement(): HTMLVideoElement | undefined; + + options: VideoOverlayOptions; +} + +export type LineCapShape = "butt" | "round" | "square" | "inherit"; + +export type LineJoinShape = "miter" | "round" | "bevel" | "inherit"; + +export type FillRule = "nonzero" | "evenodd" | "inherit"; + +export interface PathOptions extends InteractiveLayerOptions { + stroke?: boolean | undefined; + color?: string | undefined; + weight?: number | undefined; + opacity?: number | undefined; + lineCap?: LineCapShape | undefined; + lineJoin?: LineJoinShape | undefined; + dashArray?: string | number[] | undefined; + dashOffset?: string | undefined; + // TODO: should be fill moved to the correct options? PolygonOptions, CircleMarkerOptions, ... + fill?: boolean | undefined; + fillColor?: string | undefined; + fillOpacity?: number | undefined; + fillRule?: FillRule | undefined; + renderer?: Renderer | undefined; + className?: string | undefined; +} + +export abstract class Path extends Layer { + redraw(): this; + // TODO: should Partial be used? + setStyle(style: PathOptions): this; + bringToFront(): this; + bringToBack(): this; + getElement(): Element | undefined; + + options: PathOptions; +} + +export interface PolylineOptions extends PathOptions { + smoothFactor?: number | undefined; + noClip?: boolean | undefined; +} + +export class Polyline< + T extends geojson.GeometryObject = geojson.LineString | geojson.MultiLineString, + P = any, + U = LatLng[] | LatLng[][], +> extends Path { + constructor(latlngs: LatLngExpression[] | LatLngExpression[][], options?: PolylineOptions); + toGeoJSON(precision?: number | false): geojson.Feature; + getLatLngs(): U; + setLatLngs(latlngs: LatLngExpression[] | LatLngExpression[][]): this; + isEmpty(): boolean; + getCenter(): LatLng; + getBounds(): LatLngBounds; + addLatLng(latlng: LatLngExpression | LatLngExpression[], latlngs?: LatLng[]): this; + closestLayerPoint(p: PointExpression): Point; + // TODO: should Partial be used? + setStyle(options: PolylineOptions): this; + + feature?: geojson.Feature | undefined; + options: PolylineOptions; +} + +export type PolygonOptions = PolylineOptions; +export class Polygon

+ extends Polyline +{ + constructor(latlngs: LatLngExpression[] | LatLngExpression[][] | LatLngExpression[][][], options?: PolygonOptions); + setLatLngs(latlngs: LatLngExpression[] | LatLngExpression[][] | LatLngExpression[][][]): this; + + // TODO: should Partial be used? + setStyle(options: PolygonOptions): this; + options: PolygonOptions; +} + +export type RectangleOptions = PolygonOptions; +export class Rectangle

extends Polygon

{ + constructor(latLngBounds: LatLngBoundsExpression, options?: RectangleOptions); + setBounds(latLngBounds: LatLngBoundsExpression): this; + + // TODO: should Partial be used? + setStyle(options: RectangleOptions): this; + options: RectangleOptions; +} + +export interface CircleMarkerOptions extends PathOptions { + radius: number; +} + +export class CircleMarker

extends Path { + constructor(latlng: LatLngExpression, options: CircleMarkerOptions); + toGeoJSON(precision?: number | false): geojson.Feature; + setLatLng(latLng: LatLngExpression): this; + getLatLng(): LatLng; + setRadius(radius: number): this; + getRadius(): number; + setStyle(options: Partial): this; + + options: CircleMarkerOptions; + feature?: geojson.Feature | undefined; +} + +// TODO: sometimes options are "defined" as type and sometimes as interface, should it be unified? +export type CircleOptions = CircleMarkerOptions; + +export class Circle

extends CircleMarker

{ + constructor(latlng: LatLngExpression, options: CircleOptions); + getBounds(): LatLngBounds; + setRadius(radius: number): this; + getRadius(): number; + // TODO: should Partial be used? + setStyle(style: PathOptions): this; +} + +export interface BlanketOverlayOptions extends LayerOptions { + padding?: number | undefined; + continuous?: boolean | undefined; +} + +export class BlanketOverlay extends Layer { + constructor(options?: BlanketOverlayOptions); + + _initContainer(): undefined; + _destroyContainer(): undefined; + _resizeContainer(): Point; + _onZoomEnd(): undefined; + _onViewReset(): undefined; + _onSettled(): undefined; + + options: BlanketOverlayOptions; +} + +export interface RendererOptions extends BlanketOverlayOptions { + continuous: false; +} + +export class Renderer extends BlanketOverlay { + constructor(options?: RendererOptions); + + options: RendererOptions; +} + +export type SVGOptions = RendererOptions; +export class SVG extends Renderer { + constructor(options?: SVGOptions); + + options: SVGOptions; +} + +export namespace SVG { + function create(name: K): SVGElementTagNameMap[K]; + function create(name: string): SVGElement; + + function pointsToPath(rings: Point[], closed: boolean): string; +} + +export interface CanvasOptions extends RendererOptions { + tolerance?: number | undefined; +} + +export class Canvas extends Renderer { + constructor(options?: CanvasOptions); + + options: CanvasOptions; +} + +/** + * Used to group several layers and handle them as one. + * If you add it to the map, any layers added or removed from the group will be + * added/removed on the map as well. Extends Layer. + */ +export class LayerGroup

extends Layer { + constructor(layers?: Layer[], options?: InteractiveLayerOptions); + + toMultiPoint(precision?: number): geojson.Feature; + + /** + * Returns a GeoJSON representation of the layer group (as a GeoJSON GeometryCollection, GeoJSONFeatureCollection or Multipoint). + */ + toGeoJSON( + precision?: number | false, + ): + | geojson.FeatureCollection + | geojson.Feature + | geojson.GeometryCollection; + + /** + * Adds the given layer to the group. + */ + addLayer(layer: Layer): this; + + /** + * Removes the layer with the given internal ID or the given layer from the group. + */ + removeLayer(layer: number | Layer): this; + + /** + * Returns true if the the given internal ID or the given layer is currently added to the group. + */ + hasLayer(layer: number | Layer): boolean; + + /** + * Removes all the layers from the group. + */ + clearLayers(): this; + + /** + * Calls methodName on every layer contained in this group, passing any additional parameters. + * Has no effect if the layers contained do not implement methodName. + */ + invoke(methodName: string, ...params: any[]): this; + + /** + * Iterates over the layers of the group, + * optionally specifying context of the iterator function. + */ + eachLayer(fn: (layer: Layer) => void, context?: any): this; + + /** + * Returns the layer with the given internal ID. + */ + getLayer(id: number): Layer | undefined; + + /** + * Returns an array of all the layers added to the group. + */ + getLayers(): Layer[]; + + /** + * Calls setZIndex on every layer contained in this group, passing the z-index. + */ + setZIndex(zIndex: number): this; + + /** + * Returns the internal ID for a layer + */ + getLayerId(layer: Layer): number; + + feature?: + | geojson.FeatureCollection + | geojson.Feature + | geojson.GeometryCollection + | undefined; +} + +export type VectorLayerOptions = + | PathOptions + | PolylineOptions + | PolygonOptions + | RectangleOptions + | CircleMarkerOptions + | CircleOptions; + +/** + * Extended LayerGroup that also has pointer events (propagated from + * members of the group) and a shared bindPopup method. + */ +export class FeatureGroup

extends LayerGroup

{ + /** + * Sets the given path options to each layer of the group that has a setStyle method. + */ + setStyle(style: VectorLayerOptions): this; + + /** + * Brings the layer group to the top of all other layers + */ + bringToFront(): this; + + /** + * Brings the layer group to the top [sic] of all other layers + */ + bringToBack(): this; + + /** + * Returns the LatLngBounds of the Feature Group (created from + * bounds and coordinates of its children). + */ + getBounds(): LatLngBounds; +} + +export type StyleFunction

= (feature?: geojson.Feature) => VectorLayerOptions; + +export interface GeoJSONOptions

+ extends InteractiveLayerOptions +{ + /** + * A Function defining how GeoJSON points spawn Leaflet layers. + * It is internally called when data is added, passing the GeoJSON point + * feature and its LatLng. + * + * The default is to spawn a default Marker: + * + * ``` + * function(geoJsonPoint, latlng) { + * return new Marker(latlng); + * } + * ``` + */ + pointToLayer?(geoJsonPoint: geojson.Feature, latlng: LatLng): Layer; // should import GeoJSON typings + + /** + * PathOptions or a Function defining the Path options for styling GeoJSON lines and polygons, + * called internally when data is added. + * + * The default value is to not override any defaults: + * + * ``` + * function (geoJsonFeature) { + * return {} + * } + * ``` + */ + style?: VectorLayerOptions | StyleFunction

| undefined; + + /** + * A Function that will be called once for each created Feature, after it + * has been created and styled. Useful for attaching events and popups to features. + * + * The default is to do nothing with the newly created layers: + * + * ``` + * function (feature, layer) {} + * ``` + */ + onEachFeature?(feature: geojson.Feature, layer: Layer): void; + + /** + * A Function that will be used to decide whether to show a feature or not. + * + * The default is to show all features: + * + * ``` + * function (geoJsonFeature) { + * return true; + * } + * ``` + */ + filter?(geoJsonFeature: geojson.Feature): boolean; + + /** + * A Function that will be used for converting GeoJSON coordinates to LatLngs. + * The default is the coordsToLatLng static method. + */ + coordsToLatLng?(coords: [number, number] | [number, number, number]): LatLng; // check if LatLng has an altitude property + + /** Whether default Markers for "Point" type Features inherit from group options. */ + markersInheritOptions?: boolean | undefined; +} + +/** + * Represents a GeoJSON object or an array of GeoJSON objects. + * Allows you to parse GeoJSON data and display it on the map. Extends FeatureGroup. + */ +export class GeoJSON

extends FeatureGroup

{ + /** + * Convert layer into GeoJSON feature + */ + static getFeature

( + layer: Layer, + newGeometry: geojson.Feature | G, + ): geojson.Feature; + + /** + * Creates a Layer from a given GeoJSON feature. Can use a custom pointToLayer + * and/or coordsToLatLng functions if provided as options. + */ + static geometryToLayer

( + featureData: geojson.Feature, + options?: GeoJSONOptions, + ): Layer; + + /** + * Creates a LatLng object from an array of 2 numbers (longitude, latitude) or + * 3 numbers (longitude, latitude, altitude) used in GeoJSON for points. + */ + static coordsToLatLng(coords: [number, number] | [number, number, number]): LatLng; + + /** + * Creates a multidimensional array of LatLngs from a GeoJSON coordinates array. + * levelsDeep specifies the nesting level (0 is for an array of points, 1 for an array of + * arrays of points, etc., 0 by default). + * Can use a custom coordsToLatLng function. + */ + static coordsToLatLngs( + coords: any[], + levelsDeep?: number, + coordsToLatLng?: (coords: [number, number] | [number, number, number]) => LatLng, + ): any[]; // Using any[] to avoid artificially limiting valid calls + + /** + * Reverse of coordsToLatLng + */ + static latLngToCoords(latlng: LatLngExpression, precision?: number): [number, number] | [number, number, number]; + + /** + * Reverse of coordsToLatLngs closed determines whether the first point should be + * appended to the end of the array to close the feature, only used when levelsDeep is 0. + * False by default. + */ + static latLngsToCoords( + latlngs: LatLngExpression[], + levelsDeep?: number, + closed?: boolean, + precision?: number, + ): any[]; // Using any[] to avoid artificially limiting valid calls + + /** + * Normalize GeoJSON geometries/features into GeoJSON features. + */ + static asFeature

( + geojson: geojson.Feature | G, + ): geojson.Feature; + + constructor(geojson?: geojson.GeoJsonObject | null, options?: GeoJSONOptions | null); + /** + * Adds a GeoJSON object to the layer. + */ + addData(data: geojson.GeoJsonObject): this; + + /** + * Resets the given vector layer's style to the original GeoJSON style, + * useful for resetting style after hover events. + */ + resetStyle(layer?: Layer): this; + + /** + * Same as FeatureGroup's setStyle method, but style-functions are also + * allowed here to set the style according to the feature. + */ + setStyle(style: VectorLayerOptions | StyleFunction

): this; + + options: GeoJSONOptions; +} + +export type ControlPosition = "topleft" | "topright" | "bottomleft" | "bottomright"; + +export interface ControlOptions { + position?: ControlPosition | undefined; +} + +export class Control extends Class { + static extend( + props: T, + ): { new(...args: any[]): T } & typeof Control; + constructor(options?: Options); + getPosition(): ControlPosition; + setPosition(position: ControlPosition): this; + getContainer(): HTMLElement | undefined; + addTo(map: Map): this; + remove(): this; + + // Extension methods + onAdd?(map: Map): HTMLElement; + onRemove?(map: Map): void; + + options: Options; +} + +export namespace Control { + interface ZoomOptions extends ControlOptions { + zoomInText?: string | undefined; + zoomInTitle?: string | undefined; + zoomOutText?: string | undefined; + zoomOutTitle?: string | undefined; + } + + class Zoom extends Control { + constructor(options?: ZoomOptions); + disable(): this; + enable(): this; + + options: ZoomOptions; + } + + interface AttributionOptions extends ControlOptions { + prefix?: string | boolean | undefined; + } + + class Attribution extends Control { + constructor(options?: AttributionOptions); + setPrefix(prefix: string | false): this; + addAttribution(text: string): this; + removeAttribution(text: string): this; + + options: AttributionOptions; + } + + interface LayersOptions extends ControlOptions { + collapsed?: boolean | undefined; + collapseDelay?: number | undefined; + autoZIndex?: boolean | undefined; + hideSingleBase?: boolean | undefined; + /** + * Whether to sort the layers. When `false`, layers will keep the order in which they were added to the control. + */ + sortLayers?: boolean | undefined; + /** + * A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) + * that will be used for sorting the layers, when `sortLayers` is `true`. The function receives both the + * [`Layer`](https://leafletjs.com/reference.html#layer) instances and their names, as in + * `sortFunction(layerA, layerB, nameA, nameB)`. By default, it sorts layers alphabetically by their name. + */ + sortFunction?: ((layerA: Layer, layerB: Layer, nameA: string, nameB: string) => number) | undefined; + } + + interface LayersObject { + [name: string]: Layer; + } + + class Layers extends Control { + constructor(baseLayers?: LayersObject, overlays?: LayersObject, options?: LayersOptions); + addBaseLayer(layer: Layer, name: string): this; + addOverlay(layer: Layer, name: string): this; + removeLayer(layer: Layer): this; + expand(): this; + collapse(): this; + + options: LayersOptions; + } + + interface ScaleOptions extends ControlOptions { + maxWidth?: number | undefined; + metric?: boolean | undefined; + imperial?: boolean | undefined; + updateWhenIdle?: boolean | undefined; + } + + class Scale extends Control { + constructor(options?: ScaleOptions); + + options: ScaleOptions; + } +} + +export interface DivOverlayOptions { + offset?: PointExpression | undefined; + className?: string | undefined; + pane?: string | undefined; + interactive?: boolean | undefined; + content?: string | HTMLElement | ((layer: Layer) => string) | ((layer: Layer) => HTMLElement); +} + +export type Content = string | HTMLElement; + +export abstract class DivOverlay extends Layer { + constructor(latlng: LatLngExpression, options?: DivOverlayOptions); + constructor(options?: DivOverlayOptions, source?: Layer); + getLatLng(): LatLng | undefined; + setLatLng(latlng: LatLngExpression): this; + getContent(): Content | undefined; + // getContent(): Content | ((source: Layer) => Content) | undefined; + setContent(htmlContent: ((source: Layer) => Content) | Content): this; + getElement(): HTMLElement | undefined; + update(): void; + isOpen(): boolean; + bringToFront(): this; + bringToBack(): this; + openOn(map: Map): this; + toggle(layer?: Layer): this; + close(): this; + + options: DivOverlayOptions; +} + +export interface PopupOptions extends DivOverlayOptions { + maxWidth?: number | undefined; + minWidth?: number | undefined; + maxHeight?: number | undefined; + keepInView?: boolean | undefined; + closeButton?: boolean | undefined; + closeButtonLabel?: string | undefined; + autoPan?: boolean | undefined; + autoPanPaddingTopLeft?: PointExpression | undefined; + autoPanPaddingBottomRight?: PointExpression | undefined; + autoPanPadding?: PointExpression | undefined; + autoClose?: boolean | undefined; + closeOnClick?: boolean | undefined; + closeOnEscapeKey?: boolean | undefined; + trackResize?: boolean | undefined; +} + +export class Popup extends DivOverlay { + constructor(latlng: LatLngExpression, options?: PopupOptions); + constructor(options?: PopupOptions, source?: Layer); + openOn(map: Map): this; + + options: PopupOptions; +} + +export type Direction = "right" | "left" | "top" | "bottom" | "center" | "auto"; + +export interface TooltipOptions extends DivOverlayOptions { + pane?: string | undefined; + offset?: PointExpression | undefined; + direction?: Direction | undefined; + permanent?: boolean | undefined; + sticky?: boolean | undefined; + opacity?: number | undefined; +} + +export class Tooltip extends DivOverlay { + constructor(latlng: LatLngExpression, options?: TooltipOptions); + constructor(options?: TooltipOptions, source?: Layer); + setOpacity(opacity: number): void; + + options: TooltipOptions; +} + +export class Handler extends Class { + constructor(map: Map); + enable(): this; + disable(): this; + enabled(): boolean; + + // Extension methods + addHooks?(): void; + removeHooks?(): void; +} + +export class BoxZoom extends Handler { + moved(): boolean; + + boxZoom: boolean; +} + +export class DoubleClickZoom extends Handler { + doubleClickZoom: boolean; +} + +export class MapDrag extends Handler { + dragging: boolean; + inertia: boolean; + inertiaDeceleration: number; + inertiaMaxSpeed: number; + easeLinearity: number; + worldCopyJump: boolean; + maxBoundsViscosity: number; + + moved(): boolean; + moving(): boolean; +} + +export class Keyboard extends Handler { + keyboard: boolean; + keyboardPanDelta: number; +} + +export class PinchZoom extends Handler { + pinchZoom: boolean; + bounceAtZoomLimits: boolean; +} + +export class ScrollWheelZoom extends Handler { + scrollWheelZoom: boolean; + wheelDebounceTime: number; + wheelPxPerZoomLevel: number; +} + +export class TapHold extends Handler { + tapHold: boolean; + tapTolerance: number; +} + +export class MarkerDrag extends Handler { + constructor(marker: Marker); + + moved(): boolean; + + protected _draggable: Draggable; + protected _marker: Marker; +} + +export namespace DomEvent { + type EventHandlerFn = (event: Event) => void; + + type PropagableEvent = LeafletMouseEvent | LeafletKeyboardEvent | LeafletEvent | Event; + + function on(el: HTMLElement, types: string, fn: EventHandlerFn, context?: any): typeof DomEvent; + + function on(el: HTMLElement, eventMap: { [eventName: string]: EventHandlerFn }, context?: any): typeof DomEvent; + + // tslint:disable:unified-signatures + function off(el: HTMLElement): typeof DomEvent; + + function off(el: HTMLElement, types: string, fn: EventHandlerFn, context?: any): typeof DomEvent; + + function off(el: HTMLElement, eventMap: { [eventName: string]: EventHandlerFn }, context?: any): typeof DomEvent; + // tslint:enable:unified-signatures + + function stopPropagation(ev: PropagableEvent): typeof DomEvent; + + function disableScrollPropagation(el: HTMLElement): typeof DomEvent; + + function disableClickPropagation(el: HTMLElement): typeof DomEvent; + + function preventDefault(ev: Event): typeof DomEvent; + + function stop(ev: PropagableEvent): typeof DomEvent; + + function getPointerPosition(ev: MouseEvent, container?: HTMLElement): Point; + + function getWheelDelta(ev: Event): number; + + function getPropagationPath(ev: Event): HTMLElement[]; + + function getWheelPxFactor(): number; + + function isExternalTarget(el: HTMLElement, ev: Event): number; + + // Pointer detection + function enablePointerDetection(): void; + function disablePointerDetection(): void; + function getPointers(): PointerEvent[]; + function cleanupPointers(): void; +} + +export type Zoom = boolean | "center"; + +export interface MapOptions { + preferCanvas?: boolean | undefined; + + // Control options + attributionControl?: boolean | undefined; + zoomControl?: boolean | undefined; + + // Interaction options + closePopupOnClick?: boolean | undefined; + zoomSnap?: number | undefined; + zoomDelta?: number | undefined; + trackResize?: boolean | undefined; + boxZoom?: boolean | undefined; + doubleClickZoom?: Zoom | undefined; + dragging?: boolean | undefined; + + // Map state options + crs?: CRS | undefined; + center?: LatLngExpression | undefined; + zoom?: number | undefined; + minZoom?: number | undefined; + maxZoom?: number | undefined; + layers?: Layer[] | undefined; + maxBounds?: LatLngBoundsExpression | undefined; + renderer?: Renderer | undefined; + + // Animation options + fadeAnimation?: boolean | undefined; + markerZoomAnimation?: boolean | undefined; + transform3DLimit?: number | undefined; + zoomAnimation?: boolean | undefined; + zoomAnimationThreshold?: number | undefined; + + // Panning inertia options + inertia?: boolean | undefined; + inertiaDeceleration?: number | undefined; + inertiaMaxSpeed?: number | undefined; + easeLinearity?: number | undefined; + worldCopyJump?: boolean | undefined; + maxBoundsViscosity?: number | undefined; + + // Keyboard navigation options + keyboard?: boolean | undefined; + keyboardPanDelta?: number | undefined; + + // Mousewheel options + scrollWheelZoom?: Zoom | undefined; + wheelDebounceTime?: number | undefined; + wheelPxPerZoomLevel?: number | undefined; + + // Touch interaction options + tapHold?: boolean | undefined; + tapTolerance?: number | undefined; + pinchZoom?: Zoom | undefined; + bounceAtZoomLimits?: boolean | undefined; +} + +export interface ZoomOptions { + animate?: boolean | undefined; +} + +export interface PanOptions { + animate?: boolean | undefined; + duration?: number | undefined; + easeLinearity?: number | undefined; + noMoveStart?: boolean | undefined; +} + +// This is not empty, it extends two interfaces into one... +export interface ZoomPanOptions extends ZoomOptions, PanOptions {} + +export interface InvalidateSizeOptions extends ZoomPanOptions { + debounceMoveend?: boolean | undefined; + pan?: boolean | undefined; +} + +export interface PanInsideOptions extends PanOptions { + paddingTopLeft?: PointExpression | undefined; + paddingBottomRight?: PointExpression | undefined; + padding?: PointExpression | undefined; +} + +export interface FitBoundsOptions extends ZoomOptions, PanInsideOptions { + maxZoom?: number | undefined; +} + +export interface LocateOptions { + watch?: boolean | undefined; + setView?: boolean | undefined; + maxZoom?: number | undefined; + timeout?: number | undefined; + maximumAge?: number | undefined; + enableHighAccuracy?: boolean | undefined; +} + +export interface DefaultMapPanes { + mapPane: HTMLElement; + tilePane: HTMLElement; + overlayPane: HTMLElement; + shadowPane: HTMLElement; + markerPane: HTMLElement; + tooltipPane: HTMLElement; + popupPane: HTMLElement; +} + +export class Map extends Evented { + constructor(element: string | HTMLElement, options?: MapOptions); + getRenderer(layer: Path): Renderer; + + // Methods for layers and controls + addControl(control: Control): this; + removeControl(control: Control): this; + addLayer(layer: Layer): this; + removeLayer(layer: Layer): this; + hasLayer(layer: Layer): boolean; + eachLayer(fn: (layer: Layer) => void, context?: any): this; + openPopup(popup: Popup): this; + openPopup(content: Content, latlng: LatLngExpression, options?: PopupOptions): this; + closePopup(popup?: Popup): this; + openTooltip(tooltip: Tooltip): this; + openTooltip(content: Content, latlng: LatLngExpression, options?: TooltipOptions): this; + closeTooltip(tooltip?: Tooltip): this; + + // Methods for modifying map state + setView(center: LatLngExpression, zoom?: number, options?: ZoomPanOptions): this; + setZoom(zoom: number, options?: ZoomPanOptions): this; + zoomIn(delta?: number, options?: ZoomOptions): this; + zoomOut(delta?: number, options?: ZoomOptions): this; + setZoomAround(position: Point | LatLngExpression, zoom: number, options?: ZoomOptions): this; + fitBounds(bounds: LatLngBoundsExpression, options?: FitBoundsOptions): this; + fitWorld(options?: FitBoundsOptions): this; + panTo(latlng: LatLngExpression, options?: PanOptions): this; + panBy(offset: PointExpression, options?: PanOptions): this; + setMaxBounds(bounds?: LatLngBoundsExpression): this; + setMinZoom(zoom: number): this; + setMaxZoom(zoom: number): this; + panInside(latLng: LatLngExpression, options?: PanInsideOptions): this; + panInsideBounds(bounds: LatLngBoundsExpression, options?: PanOptions): this; + /** + * Boolean for animate or advanced ZoomPanOptions + */ + invalidateSize(options?: boolean | InvalidateSizeOptions): this; + stop(): this; + flyTo(latlng: LatLngExpression, zoom?: number, options?: ZoomPanOptions): this; + flyToBounds(bounds: LatLngBoundsExpression, options?: FitBoundsOptions): this; + + // Other methods + addHandler(name: string, HandlerClass: typeof Handler): this; // Alternatively, HandlerClass: new(map: Map) => Handler + remove(): this; + createPane(name: string, container?: HTMLElement): HTMLElement; + /** + * Name of the pane or the pane as HTML-Element + */ + getPane(pane: string | HTMLElement): HTMLElement | undefined; + getPanes(): { [name: string]: HTMLElement } & DefaultMapPanes; + getContainer(): HTMLElement; + whenReady(fn: (event: { target: Map }) => void, context?: any): this; + + // Methods for getting map state + getCenter(): LatLng; + getZoom(): number; + getBounds(): LatLngBounds; + getMinZoom(): number; + getMaxZoom(): number; + getBoundsZoom(bounds: LatLngBoundsExpression, inside?: boolean, padding?: PointExpression): number; + getSize(): Point; + getPixelBounds(): Bounds; + getPixelOrigin(): Point; + getPixelWorldBounds(zoom?: number): Bounds; + + // Conversion methods + getZoomScale(toZoom: number, fromZoom?: number): number; + getScaleZoom(scale: number, fromZoom?: number): number; + project(latlng: LatLngExpression, zoom?: number): Point; + unproject(point: PointExpression, zoom?: number): LatLng; + layerPointToLatLng(point: PointExpression): LatLng; + latLngToLayerPoint(latlng: LatLngExpression): Point; + wrapLatLng(latlng: LatLngExpression): LatLng; + wrapLatLngBounds(bounds: LatLngBoundsExpression): LatLngBounds; + distance(latlng1: LatLngExpression, latlng2: LatLngExpression): number; + containerPointToLayerPoint(point: PointExpression): Point; + containerPointToLatLng(point: PointExpression): LatLng; + layerPointToContainerPoint(point: PointExpression): Point; + latLngToContainerPoint(latlng: LatLngExpression): Point; + pointerEventToContainerPoint(ev: PointerEvent): Point; + pointerEventToLayerPoint(ev: PointerEvent): Point; + pointerEventToLatLng(ev: PointerEvent): LatLng; + + // Geolocation methods + locate(options?: LocateOptions): this; + stopLocate(): this; + + // Properties + attributionControl?: Control.Attribution | undefined; + zoomControl?: Control.Zoom | undefined; + + boxZoom?: BoxZoom | undefined; + doubleClickZoom?: DoubleClickZoom | undefined; + dragging?: MapDrag | undefined; + keyboard?: Keyboard | undefined; + scrollWheelZoom?: ScrollWheelZoom | undefined; + tapHold?: TapHold | undefined; + pinchZoom?: PinchZoom | undefined; + + options: MapOptions; +} + +export type LeafletMap = Map; + +export interface BaseIconOptions { + iconUrl?: string | undefined; + iconRetinaUrl?: string | undefined; + iconSize?: PointExpression | undefined; + iconAnchor?: PointExpression | undefined; + popupAnchor?: PointExpression | undefined; + tooltipAnchor?: PointExpression | undefined; + shadowUrl?: string | undefined; + shadowRetinaUrl?: string | undefined; + shadowSize?: PointExpression | undefined; + shadowAnchor?: PointExpression | undefined; + className?: string | undefined; + crossOrigin?: CrossOrigin | boolean | undefined; +} + +export interface IconOptions extends BaseIconOptions { + iconUrl: string; +} + +export class Icon extends Class { + constructor(options: T); + createIcon(oldIcon?: HTMLElement): HTMLElement; + createShadow(oldIcon?: HTMLElement): HTMLElement; + + options: T; +} + +export namespace Icon { + interface DefaultIconOptions extends BaseIconOptions { + imagePath?: string | undefined; + } + class Default extends Icon { + static imagePath?: string | undefined; + constructor(options?: DefaultIconOptions); + + options: DefaultIconOptions; + } +} + +export interface DivIconOptions extends BaseIconOptions { + html?: string | HTMLElement | false | undefined; + bgPos?: PointExpression | undefined; +} + +export class DivIcon extends Icon { + constructor(options?: DivIconOptions); + + options: DivIconOptions; +} + +export interface MarkerOptions extends InteractiveLayerOptions { + // TODO: should it be `typeof Icon`? + icon?: Icon | DivIcon | undefined; + /** Whether the marker is draggable with mouse/touch or not. */ + draggable?: boolean | undefined; + /** Whether the marker can be tabbed to with a keyboard and clicked by pressing enter. */ + keyboard?: boolean | undefined; + /** Text for the browser tooltip that appear on marker hover (no tooltip by default). */ + title?: string | undefined; + /** Text for the `alt` attribute of the icon image (useful for accessibility). */ + alt?: string | undefined; + /** Option for putting the marker on top of all others (or below). */ + zIndexOffset?: number | undefined; + /** The opacity of the marker. */ + opacity?: number | undefined; + /** If `true`, the marker will get on top of others when you hover the mouse over it. */ + riseOnHover?: boolean | undefined; + /** The z-index offset used for the `riseOnHover` feature. */ + riseOffset?: number | undefined; + /** `Map pane` where the markers shadow will be added. */ + shadowPane?: string | undefined; + /** Whether to pan the map when dragging this marker near its edge or not. */ + autoPan?: boolean | undefined; + /** Distance (in pixels to the left/right and to the top/bottom) of the map edge to start panning the map. */ + autoPanPadding?: PointExpression | undefined; + /** Number of pixels the map should pan by. */ + autoPanSpeed?: number | undefined; + autoPanOnFocus?: boolean | undefined; +} + +export class Marker

extends Layer { + constructor(latlng: LatLngExpression, options?: MarkerOptions); + toGeoJSON(precision?: number | false): geojson.Feature; + getLatLng(): LatLng; + setLatLng(latlng: LatLngExpression): this; + setZIndexOffset(offset: number): this; + // TODO: should it be `typeof Icon`? + getIcon(): Icon | DivIcon; + setIcon(icon: Icon | DivIcon): this; + setOpacity(opacity: number): this; + getElement(): HTMLElement | undefined; + update(): void; + + // Properties + options: MarkerOptions; + dragging?: MarkerDrag | undefined; + feature?: geojson.Feature | undefined; + + protected _shadow: HTMLElement | undefined; +} + +export namespace Browser { + // sorting according to https://leafletjs.com/reference.html#browser + const chrome: boolean; + const safari: boolean; + const mobile: boolean; + const pointer: boolean; + const touchNative: boolean; + const touch: boolean; + const retina: boolean; + const mac: boolean; + const linux: boolean; +} + +export namespace Util { + function stamp(obj: any): number; + function throttle(fn: () => void, time: number, context: any): () => void; + function wrapNum(num: number, range: number[], includeMax?: boolean): number; + function falseFn(): false; + function formatNum(num: number, digits?: number | false): number; + function splitWords(str: string): string[]; + function setOptions(obj: any, options: any): any; + function template(str: string, data: any): string; + + let lastId: number; + let emptyImageUrl: string; +} \ No newline at end of file diff --git a/tmp/@types/leaflet-v2/package.json b/tmp/@types/leaflet-v2/package.json new file mode 100644 index 0000000..9db80d5 --- /dev/null +++ b/tmp/@types/leaflet-v2/package.json @@ -0,0 +1,45 @@ +{ + "private": true, + "name": "@types/leaflet", + "version": "2.0.9999", + "projects": [ + "https://github.com/Leaflet/Leaflet" + ], + "dependencies": { + "@types/geojson": "*" + }, + "owners": [ + { + "name": "Alejandro Sánchez", + "githubUsername": "alejo90" + }, + { + "name": "Arne Schubert", + "githubUsername": "atd-schubert" + }, + { + "name": "Michael Auer", + "githubUsername": "mcauer" + }, + { + "name": "Roni Karilkar", + "githubUsername": "ronikar" + }, + { + "name": "Vladimir Dashukevich", + "githubUsername": "life777" + }, + { + "name": "Henry Thasler", + "githubUsername": "henrythasler" + }, + { + "name": "Colin Doig", + "githubUsername": "captain-igloo" + }, + { + "name": "Hugo Sales", + "githubUsername": "someonewithpc" + } + ] +} diff --git a/tmp/@types/leaflet-v2/readme.md b/tmp/@types/leaflet-v2/readme.md new file mode 100644 index 0000000..c2a1eb4 --- /dev/null +++ b/tmp/@types/leaflet-v2/readme.md @@ -0,0 +1 @@ +Copy of [commit `a5598ff`](https://github.com/Falke-Design/DefinitelyTyped/tree/a5598ff30adcbf620cce164aefd54e966b62363d/types/leaflet) since the [pull request](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/73485) that should merge this into DT is currently blocked. diff --git a/tsconfig.leaflet-v1.5.json b/tsconfig.leaflet-v1.5.json new file mode 100644 index 0000000..a03d9fd --- /dev/null +++ b/tsconfig.leaflet-v1.5.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "paths": { + "leaflet": ["../node_modules/@types/leaflet--v1.5/index.d.ts", "./leaflet.public.ts"] + } + }, + "exclude": ["dist", "demo"], + "include": ["src/leaflet.public.ts", "src/*.d.ts"] +} diff --git a/tsconfig.leaflet-v1.7.json b/tsconfig.leaflet-v1.7.json new file mode 100644 index 0000000..7111912 --- /dev/null +++ b/tsconfig.leaflet-v1.7.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "paths": { + "leaflet": ["../node_modules/@types/leaflet--v1.7/index.d.ts", "./leaflet.public.ts"] + } + }, + "exclude": ["dist", "demo"], + "include": ["src/leaflet.public.ts", "src/*.d.ts"] +} diff --git a/tsconfig.leaflet-v1.9.json b/tsconfig.leaflet-v1.9.json new file mode 100644 index 0000000..5b22b44 --- /dev/null +++ b/tsconfig.leaflet-v1.9.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "paths": { + "leaflet": ["../node_modules/@types/leaflet--v1.9/index.d.ts", "./leaflet.public.ts"] + } + }, + "exclude": ["dist", "demo"], + "include": ["src/leaflet.public.ts", "src/*.d.ts"] +} diff --git a/tsconfig.leaflet-v2.json b/tsconfig.leaflet-v2.json new file mode 100644 index 0000000..88f139a --- /dev/null +++ b/tsconfig.leaflet-v2.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "paths": { + "leaflet": ["../tmp/@types/leaflet-v2/index.d.ts", "./leaflet.public.ts"] + } + }, + "exclude": ["dist", "demo"], + "include": ["src/leaflet.public.ts", "src/*.d.ts"] +} diff --git a/vite.config.ts b/vite.config.ts index 0f866c6..b3cf230 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -13,14 +13,14 @@ const flavours: Record = { name: "maptilerGeocoder", globals: {}, }, - // leaflet: { - // fileName: "leaflet", - // entry: ["src/leaflet.ts"], - // name: "maptilerGeocoder", - // globals: { - // leaflet: "L", - // }, - // }, + leaflet: { + fileName: "leaflet", + entry: ["src/leaflet.public.ts"], + name: "maptilerGeocoder", + globals: { + leaflet: "L", + }, + }, maplibregl: { fileName: "maplibregl", entry: ["src/maplibregl.ts"], @@ -47,7 +47,9 @@ const flavours: Record = { }, }; -const flavour = flavours[process.env.FLAVOUR!] ?? flavours.standalone; +const flavour = flavours[process.env.FLAVOUR!]; +if (!process.env.FLAVOUR) throw new Error("No flavour specified for build!"); +if (!flavour) throw new Error(`Flavour "${process.env.FLAVOUR}" is not valid for build!`); export default defineConfig({ plugins: [externalizeDeps({ deps: !umd }), umd ? undefined : dts({ exclude: ["demos"] })], From c6c29c6bb4690b3ed2b533f23e0cfbd8290b475f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kate=20Mih=C3=A1likov=C3=A1?= Date: Thu, 4 Dec 2025 08:28:33 +0100 Subject: [PATCH 2/2] RD-1292 Fix review problems --- demos/05-leaflet.html | 1 + src/controls/leaflet-control.ts | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/demos/05-leaflet.html b/demos/05-leaflet.html index b0a55aa..e73b823 100644 --- a/demos/05-leaflet.html +++ b/demos/05-leaflet.html @@ -1,3 +1,4 @@ + MapTiler Geocoding Control example diff --git a/src/controls/leaflet-control.ts b/src/controls/leaflet-control.ts index 628c64e..e132829 100644 --- a/src/controls/leaflet-control.ts +++ b/src/controls/leaflet-control.ts @@ -155,11 +155,17 @@ export class LeafletGeocodingControl extends EventedControl(); + /** Marker representing the selected feature */ #selectedMarker: Marker | undefined; + /** Marker representing the picked feature */ #reverseMarker: Marker | undefined; + /** Features currently marked on the map */ #markedFeatures?: Feature[]; + /** Remember last feature that the map flew to as to not do it again */ #prevIdToFly?: string; + /** Layer used for showing geometry results */ #resultLayer?: GeoJSON; #elementEventListeners: { [EventName in MaptilerGeocoderEventName]: (e: MaptilerGeocoderEventNameMap[EventName]) => void } = { @@ -492,7 +498,7 @@ export class LeafletGeocodingControl extends EventedControl