1import { Controller } from "@hotwired/stimulus" 2import supportsInteractionObserver from "utils/supports_interaction_observer" 3import mapboxgl from "mapbox-gl" 4 5// Connects to data-controller="numiko--what-we-do-map" 6export default class extends Controller { 7 static values = { token: String, style: String } 8 9 connect () { 10 if (supportsInteractionObserver()) { 11 this.#setupObserver(); 12 } else { 13 this.#loadMapbox(); 14 } 15 } 16 17 disconnect () { 18 if (this.intersectionObserver) { 19 this.intersectionObserver.disconnect(); 20 } 21 } 22 23 #setupObserver () { 24 this.intersectionObserver = new window.IntersectionObserver(([entry]) => { 25 if (entry.isIntersecting) { 26 this.#loadMapbox(); 27 return; 28 } 29 }, { root: null, threshold: 0.1 }) 30 31 this.intersectionObserver.observe(this.element); 32 } 33 34 #loadMapbox () { 35 /* Should make file .js.erb and interpolate via ENV key */ 36 mapboxgl.accessToken = this.tokenValue; 37 let map = new mapboxgl.Map({ 38 container: 'svMap', 39 style: this.styleValue, 40 center: [0, 10], 41 projection: { name: 'winkelTripel' }, 42 zoom: 1.2, 43 minZoom: 1.2, 44 scrollZoom: false, 45 dragRotate: false, 46 renderWorldCopies: false 47 }); 48 let nav = new mapboxgl.NavigationControl(); 49 map.addControl(nav, 'top-right'); 50 map.on('load', function() { 51 map.addSource('globalimpact', { 52 type: 'geojson', 53 data: '/data/successes.' + locale + '.geojson', 54 cluster: true, 55 clusterMaxZoom: 14, 56 clusterRadius: 50 57 }); 58 map.addLayer({ 59 id: 'clusters', 60 type: 'circle', 61 source: 'globalimpact', 62 filter: ['has', 'point_count'], 63 paint: { 64 'circle-color': ['step', ['get', 'point_count'], '#B33D26', 100, '#B33D26', 750, '#B33D26'], 65 'circle-radius': ['step', ['get', 'point_count'], 20, 10, 30, 20, 40, 30, 60] 66 } 67 }); 68 map.addLayer({ 69 id: 'cluster-count', 70 type: 'symbol', 71 source: 'globalimpact', 72 filter: ['has', 'point_count'], 73 layout: { 74 'text-field': '{point_count_abbreviated}', 75 'text-font': ['DIN Offc Pro Medium', 'Arial Unicode MS Bold'], 76 'text-size': 14 77 }, 78 paint: { 79 'text-color': '#ffffff' 80 } 81 }); 82 map.addLayer({ 83 id: 'unclustered-point', 84 type: 'circle', 85 source: 'globalimpact', 86 filter: ['!', ['has', 'point_count']], 87 paint: { 88 'circle-color': '#B33D26', 89 'circle-radius': 8, 90 'circle-stroke-width': 1, 91 'circle-stroke-color': '#fff' 92 } 93 }); 94 map.on('click', 'clusters', function(e) { 95 var clusterId, features; 96 features = map.queryRenderedFeatures(e.point, { 97 layers: ['clusters'] 98 }); 99 clusterId = features[0].properties.cluster_id; 100 map.getSource('globalimpact').getClusterExpansionZoom(clusterId, function(err, zoom) { 101 if (err) { 102 return; 103 } 104 map.easeTo({ 105 center: features[0].geometry.coordinates, 106 zoom: zoom + 1 107 }); 108 }); 109 }); 110 map.on('mouseenter', 'clusters', function() { 111 map.getCanvas().style.cursor = 'pointer'; 112 }); 113 map.on('mouseleave', 'clusters', function() { 114 map.getCanvas().style.cursor = ''; 115 }); 116 map.on('click', 'unclustered-point', function(e) { 117 var coordinates, linktext, popupHtml, properties; 118 coordinates = e.features[0].geometry.coordinates.slice(); 119 properties = e.features[0].properties; 120 popupHtml = '<h3>' + properties.name + '</h3>'; 121 popupHtml += '<h4>' + properties.year || 'None' + '</h4>'; 122 if (properties.multimedia) { 123 if (properties.multimedia === 'quotepic') { 124 popupHtml += '<img src="' + properties.source + '" />'; 125 } 126 if (properties.multimedia === 'video') { 127 popupHtml += '<div class="embed-responsive embed-responsive-16by9"><iframe class="embed-responsive-item" src="https://player.vimeo.com/video/' + properties.source + '?color=B33D26" allowfullscreen></iframe></div>'; 128 } 129 } 130 if (properties.story) { 131 popupHtml += '<p>' + properties.story + '</p>'; 132 } 133 if (properties.link) { 134 linktext = 'Find out more';
135 if (properties.linktext) { 136 linktext = properties.linktext; 137 } 138 popupHtml += '<a class="btn btn-primary" href="' + properties.link + '">' + linktext + '</a>'; 139 } 140 while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) { 141 coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360; 142 } 143 (new mapboxgl.Popup).setLngLat(coordinates).setHTML(popupHtml).addTo(map); 144 }); 145 map.on('mouseenter', 'unclustered-point', function() { 146 map.getCanvas().style.cursor = 'pointer'; 147 }); 148 map.on('mouseleave', 'unclustered-point', function() { 149 map.getCanvas().style.cursor = ''; 150 }); 151 }); 152 } 153}
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.