PageSourceSearch

https://app.youwindrenewables.com/assets/controllers/mapbox/cluste…/clustered_data_controller-871f499c.js

js youwindrenewables.com collected 2026-09-29 10:41:16 UTC 5,394 bytes, 176 lines download raw bytes

1import BaseMapController from 'controllers/mapbox/base/base_controller'
2import { SpiderfyManager } from 'utils/SpiderfyManager';
3import { ClusterManager } from 'utils/ClusterManager';
4import { LocationTooltipManager } from 'utils/LocationTooltipManager';
5
6/**
7 * Base controller for clustered data maps
8 *
9 * Handles maps that display multiple similar objects with:
10 * - Clustering for performance
11 * - Spiderfy functionality for overlapping points
12 * - Tooltip management
13 * - Base layer switching
14 *
15 * Used by: Wind Resource Grids, Time Series Locations, Projects overview
16 */
17export default class extends BaseMapController {
18  static targets = ["container"]
19
20  initializeData() {
21    this.sites = this.parseDataAttribute("sites", []);
22    this.iconUrl = this.loadDataAttribute("url-icon-image", '/assets/logo_blue.png');
23    this.zoomLevel = parseInt(this.loadDataAttribute("zoom-level", 4));
24    this.mapType = this.loadDataAttribute("map-type", 'generic');
25    this.defaultCenter = this.parseDataAttribute("company-center", [2.168, 41.38]);
26  }
27
28  calculateCenter() {
29    // Calculate center from valid sites
30    return this.calculateCenterFromSites(this.sites);
31  }
32
33  addLayers() {
34    // Add sites if available
35    if (this.sites && this.sites.length > 0) {
36      this.addSites(this.map, this.sites);
37    }
38  }
39
40  addSites(map, sites) {
41    // Filter valid sites using base class method
42    const validSites = this.filterValidSites(sites);
43
44    if (validSites.length === 0) {
45      console.warn('No valid sites found, skipping site addition');
46      return;
47    }
48
49    if (validSites.length < sites.length) {
50      console.warn(`Filtered out ${sites.length - validSites.length} invalid sites out of ${sites.length} total sites`);
51    }
52
53    // Add icon image
54    const icon = new Image(30, 30);
55    icon.onload = () => map.addImage(this.getIconImageName(), icon);
56    icon.src = this.iconUrl;
57
58    if (!map.getSource(this.getSitesSourceId())) {
59      this.setupClusteringAndSpiderfy(map, validSites);
60    }
61  }
62
63  setupClusteringAndSpiderfy(map, validSites) {
64    // Build location groups for spiderfy functionality
65    const locationGroups = {};
66    validSites.forEach((site, index) => {
67      const coordKey = `${site[0]},${site[1]}`;
68      if (!locationGroups[coordKey]) {
69        locationGroups[coordKey] = [];
70      }
71      locationGroups[coordKey].push(this.createSiteProperties(site, index));
72    });
73
74    // Initialize tooltip manager with map-specific configuration
75    this.tooltipManager = new LocationTooltipManager({
76      mapType: this.mapType
77    });
78
79    // Initialize SpiderfyManager
80    this.spiderfyManager = new SpiderfyManager(map, {
81      iconUrl: this.iconUrl,
82      iconImage: this.getIconImageName(),
83      sourceId: this.getSitesSourceId(),
84      clusterLayer: this.getClusterLayerId(),
85      sitesLayer: this.getSitesLayerId(),
86      clusterCountLayer: this.getClusterCountLayerId(),
87      onLocationClick: (coordinates, properties) => {
88        this.tooltipManager.showLocationPopup(map, coordinates, properties);
89      },
90      onLocationHover: (coordinates, properties, type) => {
91        this.handleSpiderfyHover(coordinates, properties, type);
92      }
93    });
94
95    // Set location groups
96    this.spiderfyManager.setLocationGroups(locationGroups);
97
98    // Create features for clustering
99    const features = validSites.map((site, index) => ({
100      type: 'Feature',
101      geometry: {
102        type: 'Point',
103        coordinates: [site[1], site[0]] // [longitude, latitude] for Mapbox
104      },
105      properties: {
106        ...this.createSiteProperties(site, index),
107        coordKey: `${site[0]},${site[1]}`
108      }
109    }));
110
111    // Initialize ClusterManager
112    this.clusterManager = new ClusterManager(map, {
113      sourceId: this.getSitesSourceId(),
114      clusterId: this.getClusterLayerId(),
115      clusterCountId: this.getClusterCountLayerId(),
116      sitesLayerId: this.getSitesLayerId(),
117      iconImage: this.getIconImageName(),
118      spiderfyManager: this.spiderfyManager,
119      tooltipOptions: {
120        mapType: this.mapType
121      },
122      onSingleLocationClick: (coordinates, properties) => {
123        this.tooltipManager.showLocationPopup(map, coordinates, properties);
124      }
125    });
126
127    // Setup clustering
128    this.clusterManager.setupClustering(features);
129  }
130
131  // ID generation methods using prefix
132  getSitesSourceId() {
133    return `${this.idPrefix}-sites-source`;
134  }
135
136  getClusterLayerId() {
137    return `${this.idPrefix}-clusters`;
138  }
139
140  getSitesLayerId() {
141    return `${this.idPrefix}-sites-layer`;
142  }
143
144  getClusterCountLayerId() {
145    return `${this.idPrefix}-cluster-count`;
146  }
147
148  getIconImageName() {
149    return `${this.idPrefix}-icon`;
150  }
151
152  createSiteProperties(site, index) {
153    // Default implementation - override in child classes for specific properties
154    return {
155      id: index,
156      name: site[2] || 'Unnamed Location',
157      latitude: parseFloat(site[0]),
158      longitude: parseFloat(site[1])
159    };
160  }
161
162  getReAddCallback() {
163    return 'addLayers';
164  }
165
166  // SpiderfyManager callback for handling spiderfied marker hover
167  handleSpiderfyHover(coordinates, properties, type) {
168    if (type === 'leave') {
169      this.tooltipManager.hideHoverTooltip();
170      return;
171    }
172
173    // Show single location info for spiderfied marker
174    this.tooltipManager.showHoverTooltip(this.map, coordinates, properties);
175  }
176}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.