import { IControl, Map as MapboxMap } from "mapbox-gl"; import { GeoJsonProperties } from "geojson"; export interface IMapboxInfoBoxOptions { layerId?: string, formatter?: (properties: GeoJsonProperties) => string additionalContainerClasses?: string | string[] } export class MapboxInfoBoxControl implements IControl { private static readonly DEFAULT_OPTIONS: IMapboxInfoBoxOptions = { layerId: "features", formatter: properties => properties ? `Name: ${properties['name']}` : '' } private controlContainer: HTMLElement; private formatter: (properties: GeoJsonProperties) => string; private layerId: string; private map?: MapboxMap; constructor(options: IMapboxInfoBoxOptions = MapboxInfoBoxControl.DEFAULT_OPTIONS) { this.createContainer(options); const controlOptions = Object.assign({}, MapboxInfoBoxControl.DEFAULT_OPTIONS, options); this.formatter = controlOptions.formatter!; this.layerId = controlOptions.layerId!; this.handleMouseEnter = this.handleMouseEnter.bind(this); this.handleMouseLeave = this.handleMouseLeave.bind(this); this.handleMouseMove = this.handleMouseMove.bind(this); } public getDefaultPosition(): string { return "top-left"; } public onAdd(map: MapboxMap): HTMLElement { this.map = map; this.controlContainer.style.display = "none"; map.on("mouseenter", this.layerId, this.handleMouseEnter); map.on("mouseleave", this.layerId, this.handleMouseLeave); map.on("mousemove", this.layerId, this.handleMouseMove); return this.controlContainer; } public onRemove(): void { if (!this.controlContainer || !this.controlContainer.parentNode || !this.map) { return; } this.controlContainer.parentNode.removeChild(this.controlContainer); this.map.off("mouseenter", this.layerId, this.handleMouseEnter); this.map.off("mouseleave", this.layerId, this.handleMouseLeave); this.map.off("mousemove", this.layerId, this.handleMouseMove); } private createContainer(options: IMapboxInfoBoxOptions) { this.controlContainer = document.createElement("div"); this.controlContainer.classList.add("mapboxgl-ctrl"); this.controlContainer.classList.add("mapboxgl-ctrl-group"); this.controlContainer.classList.add("mapboxgl-ctrl-icon"); this.controlContainer.classList.add("mapboxgl-info-box-ctrl"); if (options.additionalContainerClasses?.length) { const classes = Array.isArray(options.additionalContainerClasses) ? options.additionalContainerClasses : options.additionalContainerClasses.split(" "); this.controlContainer.classList.add(...classes); } } private handleMouseEnter(): void { if (!this.map) { return; } this.map.getCanvas().style.cursor = "pointer"; this.controlContainer.style.display = "block"; } private handleMouseLeave(): void { if (!this.map || !this.controlContainer) { return; } this.map.getCanvas().style.cursor = ""; this.controlContainer.style.display = "none"; } private handleMouseMove(e): void { if (!e.features || !e.features.length) { return; } const [feature] = e.features; this.controlContainer.innerHTML = this.formatter(feature.properties); } } export interface IMapboxGradientSteps { minValue: number, maxValue: number } export interface IMapboxGradientBoxOptions { formatter?: (value: number) => string, gradientSteps?: IMapboxGradientSteps, getWeight?: (properties: GeoJsonProperties) => number, layerId?: string } export class MapboxGradientBoxControl implements IControl { private static readonly DEFAULT_OPTIONS: IMapboxGradientBoxOptions = { formatter: value => String(value), layerId: "features", gradientSteps: {minValue: 0, maxValue: 100}, getWeight: (properties) => (properties ? properties.weight : 0) }; private controlContainer: HTMLElement; private leftValueElement: HTMLElement; private gradientElement: HTMLElement; private caretElement: HTMLElement; private rightValueElement: HTMLElement; private gradientSteps: IMapboxGradientSteps; private getWeight: (properties: GeoJsonProperties) => number; private layerId: string; private map?: MapboxMap; constructor( options: IMapboxGradientBoxOptions = MapboxGradientBoxControl.DEFAULT_OPTIONS) { const controlOptions = Object.assign({}, MapboxGradientBoxControl.DEFAULT_OPTIONS, options); const formatter = options.formatter || (value => String(value)); this.controlContainer = document.createElement("div"); this.controlContainer.classList.add("mapboxgl-ctrl"); this.controlContainer.classList.add("mapboxgl-ctrl-group"); this.controlContainer.classList.add("mapboxgl-gradient-box-ctrl"); this.leftValueElement = document.createElement("div"); this.leftValueElement.classList.add("left-value"); this.leftValueElement.innerText = formatter(controlOptions.gradientSteps!.minValue); this.controlContainer.appendChild(this.leftValueElement); this.gradientElement = document.createElement("div"); this.gradientElement.classList.add("gradient"); this.controlContainer.appendChild(this.gradientElement); this.caretElement = document.createElement("div"); this.caretElement.classList.add("caret"); this.caretElement.innerText = "◆"; this.gradientElement.appendChild(this.caretElement); this.rightValueElement = document.createElement("div"); this.rightValueElement.classList.add("right-value"); this.rightValueElement.innerText = formatter(controlOptions.gradientSteps!.maxValue); this.controlContainer.appendChild(this.rightValueElement); this.getWeight = controlOptions.getWeight!; this.layerId = controlOptions.layerId!; this.gradientSteps = controlOptions.gradientSteps!; this.handleMouseEnter = this.handleMouseEnter.bind(this); this.handleMouseLeave = this.handleMouseLeave.bind(this); this.handleMouseMove = this.handleMouseMove.bind(this); } public getDefaultPosition(): string { return "top-left"; } public onAdd(map: MapboxMap): HTMLElement { this.map = map; map.on("mouseenter", this.layerId, this.handleMouseEnter); map.on("mouseleave", this.layerId, this.handleMouseLeave); map.on("mousemove", this.layerId, this.handleMouseMove); return this.controlContainer; } public onRemove(): void { if (!this.controlContainer || !this.controlContainer.parentNode || !this.map) { return; } this.controlContainer.parentNode.removeChild(this.controlContainer); this.map.off("mouseenter", this.layerId, this.handleMouseEnter); this.map.off("mouseleave", this.layerId, this.handleMouseLeave); this.map.off("mousemove", this.layerId, this.handleMouseMove); } private handleMouseEnter(): void { if (!this.map) { return; } this.map.getCanvas().style.cursor = "pointer"; this.caretElement.style.display = "block"; } private handleMouseLeave(): void { if (!this.map || !this.controlContainer) { return; } this.map.getCanvas().style.cursor = ""; this.caretElement.style.display = "none"; } private handleMouseMove(e): void { if (!e.features || !e.features.length) { return; } const [feature] = e.features; const weight = this.getWeight(feature.properties); const delta = this.gradientSteps.maxValue - this.gradientSteps.minValue; let percentage = (weight - this.gradientSteps.minValue) / delta * 100; percentage = percentage > 100 ? 100 : (percentage < 0 ? 0 : percentage); this.caretElement.style.paddingLeft = `${percentage}%`; this.caretElement.style.display = "inline"; } }