1+ <!DOCTYPE html>
2+ < html >
3+
4+ < head >
5+ < meta charset ="utf-8 " />
6+ < title > Condo halo on hover</ title >
7+ < meta name ="viewport " content ="initial-scale=1,maximum-scale=1,user-scalable=no " />
8+ < link href ="https://api.mapbox.com/mapbox-gl-js/v2.11.0/mapbox-gl.css " rel ="stylesheet ">
9+ < script src ="https://api.mapbox.com/mapbox-gl-js/v2.11.0/mapbox-gl.js "> </ script >
10+ < style >
11+ body {
12+ margin : 0 ;
13+ padding : 0 ;
14+ }
15+
16+ # map {
17+ position : absolute;
18+ top : 0 ;
19+ bottom : 0 ;
20+ width : 100% ;
21+ }
22+ </ style >
23+ </ head >
24+
25+ < body >
26+ < div id ="map "> </ div >
27+ < script >
28+ mapboxgl . accessToken = 'pk.eyJ1Ijoic2FyYWJyZW50IiwiYSI6ImNtMHpsb2Q4NTAwemoybHExbnB6eHVvZ2kifQ.nH0hUSv3IGzX6MkYB_cSmA' ;
29+ const map = new mapboxgl . Map ( {
30+ container : 'map' ,
31+ // Choose from Mapbox's core styles, or make your own style with Mapbox Studio
32+ style : 'mapbox://styles/mapbox/satellite-streets-v12' ,
33+ zoom : 11 ,
34+ center : [ - 72.688122 , 44.467397 ] ,
35+ projection : 'globe'
36+ } ) ;
37+
38+ let originalGeoJSON ;
39+
40+
41+
42+ map . on ( 'load' , ( ) => {
43+ map . addSource ( 'boundary' , {
44+ type : 'geojson' ,
45+ data : 'Boundary.geojson'
46+ } ) ;
47+
48+ map . addLayer ( {
49+ id : 'boundary' ,
50+ type : 'line' ,
51+ source : 'boundary' ,
52+ paint : {
53+ 'line-color' : 'white'
54+ }
55+ } ) ;
56+
57+ fetch ( 'AllPointsWithUnits.geojson' )
58+ . then ( res => res . json ( ) )
59+ . then ( data => {
60+ // Store original data for restoration later
61+ // 🔧 Step 1: Count duplicate coordinates
62+ const coordMap = { } ;
63+ data . features . forEach ( f => {
64+ const key = f . geometry . coordinates . join ( ',' ) ;
65+ coordMap [ key ] = ( coordMap [ key ] || 0 ) + 1 ;
66+ } ) ;
67+
68+ // 🔧 Step 2: Tag features with hasDuplicates
69+ data . features . forEach ( f => {
70+ const key = f . geometry . coordinates . join ( ',' ) ;
71+ f . properties . hasDuplicates = coordMap [ key ] > 1 ;
72+ } ) ;
73+
74+ // 🔧 Step 3: Now clone with tags intact
75+ originalGeoJSON = JSON . parse ( JSON . stringify ( data ) ) ;
76+
77+ map . addSource ( 'resi2' , {
78+ type : 'geojson' ,
79+ data : data
80+ } ) ;
81+
82+
83+ map . addLayer ( {
84+ id : 'ResiAll' ,
85+ type : 'circle' ,
86+ source : 'resi2' ,
87+ paint : {
88+ 'circle-color' : [
89+ 'match' ,
90+ [ 'get' , 'homestead' ] ,
91+ 'yes' , '#1E88E5' ,
92+ 'no' , '#FFC107' ,
93+ '#000000'
94+ ] ,
95+ 'circle-radius' : 6 ,
96+ 'circle-stroke-color' : '#000' ,
97+ 'circle-stroke-width' : 1
98+ }
99+ } ) ;
100+
101+ map . addLayer ( {
102+ id : 'ResiAll-halo' ,
103+ type : 'circle' ,
104+ source : 'resi2' ,
105+ filter : [ '==' , [ 'get' , 'hasDuplicates' ] , true ] ,
106+ paint : {
107+ 'circle-color' : 'white' ,
108+ 'circle-radius' : 16 ,
109+ 'circle-blur' : 0.8 ,
110+ 'circle-opacity' : 1
111+ }
112+ } , 'ResiAll' ) ;
113+
114+ map . on ( 'mousemove' , 'ResiAll' , ( e ) => {
115+ const features = map . queryRenderedFeatures ( e . point , { layers : [ 'ResiAll' ] } ) ;
116+
117+ if ( features . length <= 1 ) {
118+ map . getSource ( 'resi2' ) . setData ( originalGeoJSON ) ;
119+ return ;
120+ }
121+
122+ const fanned = JSON . parse ( JSON . stringify ( originalGeoJSON ) ) ;
123+ const centerCoord = features [ 0 ] . geometry . coordinates ;
124+ const angleStep = ( 2 * Math . PI ) / features . length ;
125+ const radius = 0.0001 ; // ~11 meters offset
126+
127+ features . forEach ( ( f , i ) => {
128+ const idx = fanned . features . findIndex ( orig => orig . id === f . id ) ;
129+ if ( idx === - 1 ) return ;
130+ const angle = i * angleStep ;
131+ const dx = radius * Math . cos ( angle ) ;
132+ const dy = radius * Math . sin ( angle ) ;
133+ fanned . features [ idx ] . geometry . coordinates = [
134+ centerCoord [ 0 ] + ( dx * 1.5 ) ,
135+ centerCoord [ 1 ] + ( dy * 1 )
136+ ] ;
137+ } ) ;
138+
139+ map . getSource ( 'resi2' ) . setData ( fanned ) ;
140+ } ) ;
141+
142+ map . on ( 'mouseleave' , 'ResiAll' , ( ) => {
143+ map . getSource ( 'resi2' ) . setData ( originalGeoJSON ) ;
144+ } ) ;
145+ } ) ;
146+ } ) ;
147+ </ script >
148+ </ body >
149+
150+ </ html >
0 commit comments