1+ import { Feature , Map } from 'ol'
2+ import { useEffect , useRef , useState } from 'react'
3+ import VectorLayer from 'ol/layer/Vector'
4+ import VectorSource from 'ol/source/Vector'
5+ import { Point , Circle as CircleGeom } from 'ol/geom'
6+ import { fromLonLat } from 'ol/proj'
7+ import { Style , Fill , Stroke , Circle as CircleStyle } from 'ol/style'
8+ import Geolocation from 'ol/Geolocation'
9+
10+ interface CurrentLocationState {
11+ enabled : boolean
12+ tracking : boolean
13+ }
14+
15+ const LOCATION_LAYER_KEY = 'gh:current_location'
16+
17+ export default function useCurrentLocationLayer ( map : Map , locationState : CurrentLocationState ) {
18+ const geolocationRef = useRef < Geolocation | null > ( null )
19+ const [ hasPermission , setHasPermission ] = useState < boolean | null > ( null )
20+
21+ useEffect ( ( ) => {
22+ if ( ! locationState . enabled ) {
23+ removeCurrentLocationLayer ( map )
24+ if ( geolocationRef . current ) {
25+ geolocationRef . current . setTracking ( false )
26+ geolocationRef . current = null
27+ }
28+ return
29+ }
30+
31+ // Check for geolocation permission
32+ if ( 'permissions' in navigator ) {
33+ navigator . permissions . query ( { name : 'geolocation' } ) . then ( ( result ) => {
34+ setHasPermission ( result . state === 'granted' )
35+ result . addEventListener ( 'change' , ( ) => {
36+ setHasPermission ( result . state === 'granted' )
37+ } )
38+ } )
39+ }
40+
41+ // Create geolocation instance
42+ const geolocation = new Geolocation ( {
43+ trackingOptions : {
44+ enableHighAccuracy : true
45+ } ,
46+ projection : map . getView ( ) . getProjection ( )
47+ } )
48+
49+ geolocationRef . current = geolocation
50+
51+ // Create the location layer
52+ const locationLayer = createLocationLayer ( )
53+ map . addLayer ( locationLayer )
54+
55+ // Handle position updates
56+ const positionFeature = new Feature ( )
57+ const accuracyFeature = new Feature ( )
58+
59+ geolocation . on ( 'change:position' , ( ) => {
60+ const coordinates = geolocation . getPosition ( )
61+ if ( coordinates ) {
62+ positionFeature . setGeometry ( new Point ( coordinates ) )
63+
64+ // Update view if tracking is enabled
65+ if ( locationState . tracking ) {
66+ map . getView ( ) . animate ( {
67+ center : coordinates ,
68+ duration : 500
69+ } )
70+ }
71+ }
72+ } )
73+
74+ geolocation . on ( 'change:accuracyGeometry' , ( ) => {
75+ const accuracy = geolocation . getAccuracyGeometry ( )
76+ if ( accuracy ) {
77+ accuracyFeature . setGeometry ( accuracy )
78+ }
79+ } )
80+
81+ geolocation . on ( 'error' , ( error ) => {
82+ console . error ( 'Geolocation error:' , error )
83+ setHasPermission ( false )
84+ } )
85+
86+ // Add features to the layer
87+ const source = locationLayer . getSource ( )
88+ if ( source ) {
89+ source . addFeature ( accuracyFeature )
90+ source . addFeature ( positionFeature )
91+ }
92+
93+ // Start tracking
94+ geolocation . setTracking ( true )
95+
96+ return ( ) => {
97+ geolocation . setTracking ( false )
98+ removeCurrentLocationLayer ( map )
99+ }
100+ } , [ map , locationState . enabled , locationState . tracking ] )
101+
102+ return hasPermission
103+ }
104+
105+ function removeCurrentLocationLayer ( map : Map ) {
106+ map . getLayers ( )
107+ . getArray ( )
108+ . filter ( l => l . get ( LOCATION_LAYER_KEY ) )
109+ . forEach ( l => map . removeLayer ( l ) )
110+ }
111+
112+ function createLocationLayer ( ) : VectorLayer < VectorSource > {
113+ const layer = new VectorLayer ( {
114+ source : new VectorSource ( ) ,
115+ style : ( feature ) => {
116+ const geometry = feature . getGeometry ( )
117+ if ( geometry instanceof Point ) {
118+ // Blue dot style for position
119+ return [
120+ new Style ( {
121+ image : new CircleStyle ( {
122+ radius : 8 ,
123+ fill : new Fill ( {
124+ color : '#4285F4'
125+ } ) ,
126+ stroke : new Stroke ( {
127+ color : '#FFFFFF' ,
128+ width : 2
129+ } )
130+ } )
131+ } ) ,
132+ // Pulsing effect outer ring
133+ new Style ( {
134+ image : new CircleStyle ( {
135+ radius : 16 ,
136+ fill : new Fill ( {
137+ color : 'rgba(66, 133, 244, 0.2)'
138+ } )
139+ } )
140+ } )
141+ ]
142+ } else if ( geometry instanceof CircleGeom ) {
143+ // Accuracy circle style
144+ return new Style ( {
145+ fill : new Fill ( {
146+ color : 'rgba(66, 133, 244, 0.1)'
147+ } ) ,
148+ stroke : new Stroke ( {
149+ color : 'rgba(66, 133, 244, 0.3)' ,
150+ width : 1
151+ } )
152+ } )
153+ }
154+ return [ ]
155+ }
156+ } )
157+
158+ layer . set ( LOCATION_LAYER_KEY , true )
159+ layer . setZIndex ( 4 ) // Above paths and query points
160+
161+ return layer
162+ }
0 commit comments