We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
A few months ago triedry to adapte an old plugging from Leaflet call Spiderfier.
This is the result.
import type { Map as M, Marker } from 'maplibre-gl'; import mapLibre from 'maplibre-gl'; interface SpiderLegParam { x: number; y: number; angle: number; legLength: number; index: number; } export interface SpiderLeg { feature: GeoJSON.Feature; maplibreMarker: Marker; param: SpiderLegParam; } export interface OptionsSpiderfier { customPin: boolean; onClick?: (e: Event, spiderLeg: SpiderLeg) => void; circleSpiralSwitchover: number; circleFootSeparation: number; spiralFootSeparation: number; spiralLengthStart: number; spiralLengthFactor: number; makerClassName: string; popUpClassName: string; } export default class Spiderfier { private map: M; private options: OptionsSpiderfier; private previousSpiderLegs: SpiderLeg[] = []; private spiderLeg: SpiderLeg | null = null; constructor(map: M, userOptions: Partial<OptionsSpiderfier>) { this.map = map; this.options = { customPin: false, circleSpiralSwitchover: 10, circleFootSeparation: 20, spiralFootSeparation: 30, spiralLengthStart: 15, spiralLengthFactor: 4, makerClassName: 'spider-marker', popUpClassName: 'spider-popup', ...userOptions }; } public spiderfy( latLng: [number, number], features: GeoJSON.Feature<GeoJSON.Geometry, GeoJSON.GeoJsonProperties>[], currentZoom: number ) { const spiderLegs = this.generateSpiderLegs(latLng, features, currentZoom); this.previousSpiderLegs = spiderLegs; } public unspiderfy() { for (const spiderLeg of this.previousSpiderLegs) { spiderLeg.maplibreMarker.remove(); } this.previousSpiderLegs = []; } private generateSpiderLegs( latLng: [number, number], features: GeoJSON.Feature<GeoJSON.Geometry, GeoJSON.GeoJsonProperties>[], zoom: number ): SpiderLeg[] { const spiderLegParams = this.generateSpiderLegParams(features.length); return features.map((feature, index) => { const spiderLegParam = spiderLegParams[index]; const maplibreMarker = this.createMaplibreMarker(latLng, spiderLegParam, zoom); const spiderLeg: SpiderLeg = { feature, maplibreMarker, param: spiderLegParam }; this.initializeSpiderLeg(spiderLeg); return spiderLeg; }); } private initializeSpiderLeg(spiderLeg: SpiderLeg) { if (this.options.onClick && typeof this.options.onClick === 'function') { spiderLeg.maplibreMarker.getElement()?.addEventListener('click', (e) => { this.options.onClick?.(e, spiderLeg); this.spiderLeg = spiderLeg; }); } } private generateSpiderLegParams(count: number): SpiderLegParam[] { return count >= this.options.circleSpiralSwitchover ? this.generateSpiralParams(count) : this.generateCircleParams(count); } private generateSpiralParams(count: number): SpiderLegParam[] { let legLength = this.options.spiralLengthStart; let angle = 0; return Array.from({ length: count }, (_, index) => { angle += this.options.spiralFootSeparation / legLength + index * 0.0005; legLength += (2 * Math.PI * this.options.spiralLengthFactor) / angle; return { x: legLength * Math.cos(angle), y: legLength * Math.sin(angle), angle, legLength, index }; }); } private generateCircleParams(count: number): SpiderLegParam[] { const circumference = this.options.circleFootSeparation * (2 + count); const legLength = circumference / (2 * Math.PI); // = radius from circumference const angleStep = (2 * Math.PI) / count; return Array.from({ length: count }, (_, index) => { const angle = index * angleStep; return { x: legLength * Math.cos(angle), y: legLength * Math.sin(angle), angle, legLength, index }; }); } private createMaplibreMarker( latLng: [number, number], spiderLegParam: SpiderLegParam, zoom: number ): Marker { const minScaleFactor = 0.00002; // min scale factor for the spider legs const maxScaleFactor = 0.0001; // max scale factor for the spider legs const minZoom = 12; // min zoom level for the spider legs const maxZoom = 15; // max zoom level for the spider legs // Ensure zoom is within the valid range const clampedZoom = Math.max(minZoom, Math.min(maxZoom, zoom)); // Calculate the scale factor using a reverse linear interpolation const scale = Math.max( minScaleFactor, maxScaleFactor - ((maxScaleFactor - minScaleFactor) * (clampedZoom - minZoom)) / (maxZoom - minZoom) ); const newLatLng = new mapLibre.LngLat( latLng[0] + spiderLegParam.x * scale, latLng[1] + spiderLegParam.y * scale ); const marker = new mapLibre.Marker({ color: '#ff0000', className: this.options.makerClassName }).setLngLat(newLatLng); marker.addTo(this.map); // Add the marker to the map return marker; } }
I know is not the best. But it works well for my use case.
Can we consider adding this kind of feature, I am not the only one to use this.
The text was updated successfully, but these errors were encountered:
No branches or pull requests
A few months ago triedry to adapte an old plugging from Leaflet call Spiderfier.
This is the result.
I know is not the best. But it works well for my use case.
Can we consider adding this kind of feature, I am not the only one to use this.
The text was updated successfully, but these errors were encountered: