@@ -75,6 +75,10 @@ let location_active = false;
75
75
76
76
// Suivi de la carte
77
77
let tracking_active = false ;
78
+
79
+ // Suivi de la carte avec boussole
80
+ let navigation_active = false ;
81
+
78
82
let watch_id ;
79
83
let currentPosition = null ;
80
84
@@ -97,6 +101,13 @@ let firstLocation;
97
101
// Est-ce que le marqueur de "Ma Position" a un écouteur d'évènement ?
98
102
let hasEventListener = false ;
99
103
104
+ // Varaibles pour la gestion d'évènements touch et zoom spécifiques à la localisation activée
105
+ let startDist = 0 ;
106
+ let startZoom = 0 ;
107
+ let startBearing = 0 ;
108
+ let startAngle = 0 ;
109
+ let rotationEnabled = false ;
110
+
100
111
/**
101
112
* Interface pour les evenements
102
113
* @example
@@ -186,6 +197,7 @@ const moveTo = (coords, zoom = Globals.map.getZoom(), panTo = true, gps = true)
186
197
Globals . myPositionMarker = new maplibregl . Marker ( {
187
198
element : ( gps ) ? positionIcon : Globals . searchResultIcon ,
188
199
anchor : ( gps ) ? "center" : "bottom" ,
200
+ pitchAlignment : "map" ,
189
201
} )
190
202
. setLngLat ( [ coords . lon , coords . lat ] )
191
203
. addTo ( Globals . map ) ;
@@ -216,11 +228,30 @@ const moveTo = (coords, zoom = Globals.map.getZoom(), panTo = true, gps = true)
216
228
217
229
if ( panTo ) {
218
230
if ( tracking_active ) {
231
+ let bearing = Globals . map . getBearing ( ) ;
232
+ let pitch = 0 ;
233
+ let padding = 0 ;
234
+ if ( navigation_active ) {
235
+ bearing = - mapBearing ;
236
+ pitch = 45 ;
237
+ padding = { top : DOM . $map . clientHeight * 0.5 } ;
238
+ }
219
239
isMapPanning = true ;
220
- Globals . map . flyTo ( { center : [ coords . lon , coords . lat ] , zoom : zoom , bearing : - mapBearing , duration : 500 } ) ;
240
+ Globals . map . easeTo ( {
241
+ center : [ coords . lon , coords . lat ] ,
242
+ zoom : zoom ,
243
+ bearing : bearing ,
244
+ pitch : pitch ,
245
+ duration : 500 ,
246
+ padding : padding ,
247
+ } ) ;
221
248
Globals . map . once ( "moveend" , ( ) => { isMapPanning = false ; } ) ;
222
249
} else {
223
- Globals . map . flyTo ( { center : [ coords . lon , coords . lat ] , zoom : zoom } ) ;
250
+ Globals . map . flyTo ( {
251
+ center : [ coords . lon , coords . lat ] ,
252
+ zoom : zoom ,
253
+ pitch : 0
254
+ } ) ;
224
255
}
225
256
}
226
257
} ;
@@ -249,8 +280,8 @@ const watchPositionCallback = (position) => {
249
280
currentPosition = position ;
250
281
localStorage . setItem ( "lastKnownPosition" , JSON . stringify ( { lat : currentPosition . coords . latitude , lng : currentPosition . coords . longitude } ) ) ;
251
282
var zoom = Globals . map . getZoom ( ) ;
252
- if ( firstLocation || tracking_active ) {
253
- zoom = Math . max ( Globals . map . getZoom ( ) , 16 ) ;
283
+ if ( firstLocation ) {
284
+ zoom = Math . max ( Globals . map . getZoom ( ) , 16.5 ) ;
254
285
}
255
286
moveTo ( {
256
287
lat : position . coords . latitude ,
@@ -262,11 +293,9 @@ const watchPositionCallback = (position) => {
262
293
var padding ;
263
294
// gestion du mode paysage / écran large
264
295
if ( window . matchMedia ( "screen and (min-aspect-ratio: 1/1) and (min-width:400px)" ) . matches ) {
265
- var paddingLeft = parseFloat ( getComputedStyle ( document . documentElement ) . getPropertyValue ( "--safe-area-inset-left" ) . slice ( 0 , - 2 ) ) +
266
- Math . min ( window . innerHeight , window . innerWidth / 2 ) + 42 ;
267
- padding = { top : 20 , right : 20 , bottom : 20 , left : paddingLeft } ;
296
+ padding = { top : 20 , right : 20 , bottom : 20 , left : 20 } ;
268
297
} else {
269
- padding = { top : 80 , right : 20 , bottom : 120 , left : 20 } ;
298
+ padding = { top : 80 , right : 20 , bottom : 40 , left : 20 } ;
270
299
}
271
300
Globals . map . fitBounds ( bbox , {
272
301
padding : padding
@@ -360,6 +389,7 @@ const enablePosition = async() => {
360
389
return ;
361
390
}
362
391
location_active = true ;
392
+ tracking_active = true ;
363
393
if ( ! currentPosition && localStorage . getItem ( "lastKnownPosition" ) ) {
364
394
const lastPosition = JSON . parse ( localStorage . getItem ( "lastKnownPosition" ) ) ;
365
395
moveTo ( {
@@ -382,11 +412,32 @@ const locationOnOff = async () => {
382
412
if ( currentPosition === null ) {
383
413
return ;
384
414
}
385
- DOM . $geolocateBtn . style . backgroundImage = "url(\"" + LocationFollowImg + "\")" ;
415
+ DOM . $geolocateBtn . style . backgroundImage = "url(\"" + LocationFixeImg + "\")" ;
386
416
tracking_active = true ;
417
+ Globals . map . setPadding ( { top : 0 , right : 0 , bottom : 0 , left : 0 } ) ;
387
418
Globals . map . setCenter ( [ currentPosition . coords . longitude , currentPosition . coords . latitude ] ) ;
388
- Globals . map . setZoom ( 16 ) ;
389
- Globals . map . setBearing ( - mapBearing ) ;
419
+ Globals . map . touchZoomRotate . disable ( ) ;
420
+ Globals . map . getCanvasContainer ( ) . addEventListener ( "touchstart" , locationOnTouchStartHandler ) ;
421
+ Globals . map . getCanvasContainer ( ) . addEventListener ( "touchmove" , locationOnTouchMoveHandler ) ;
422
+ Toast . show ( {
423
+ text : "Suivi de position activé" ,
424
+ duration : "short" ,
425
+ position : "bottom"
426
+ } ) ;
427
+ } else if ( ! navigation_active ) {
428
+ if ( currentPosition === null ) {
429
+ return ;
430
+ }
431
+ DOM . $geolocateBtn . style . backgroundImage = "url(\"" + LocationFollowImg + "\")" ;
432
+ navigation_active = true ;
433
+ Globals . map . setMaxPitch ( 45 ) ;
434
+ const padding = { top : DOM . $map . clientHeight * 0.5 } ;
435
+ Globals . map . easeTo ( {
436
+ center : [ currentPosition . coords . longitude , currentPosition . coords . latitude ] ,
437
+ bearing : - mapBearing ,
438
+ pitch : 45 ,
439
+ padding : padding ,
440
+ } ) ;
390
441
DOM . $compassBtn . classList . remove ( "d-none" ) ;
391
442
DOM . $compassBtn . style . transform = "rotate(" + mapBearing + "deg)" ;
392
443
Toast . show ( {
@@ -395,14 +446,19 @@ const locationOnOff = async () => {
395
446
position : "bottom"
396
447
} ) ;
397
448
} else {
449
+ Globals . map . flyTo ( {
450
+ pitch : 0 ,
451
+ duration : 500 ,
452
+ } ) ;
453
+ setTimeout ( ( ) => { Globals . map . setMaxPitch ( 0 ) ; } , 500 ) ;
398
454
DOM . $geolocateBtn . style . backgroundImage = "url(\"" + LocationImg + "\")" ;
399
- Geolocation . clearWatch ( { id : watch_id } ) ;
400
- clean ( ) ;
401
- currentPosition = null ;
402
- location_active = false ;
403
455
tracking_active = false ;
456
+ navigation_active = false ;
457
+ Globals . map . touchZoomRotate . enable ( ) ;
458
+ Globals . map . getCanvasContainer ( ) . removeEventListener ( "touchstart" , locationOnTouchStartHandler ) ;
459
+ Globals . map . getCanvasContainer ( ) . removeEventListener ( "touchmove" , locationOnTouchMoveHandler ) ;
404
460
Toast . show ( {
405
- text : "Navigation et suivi de position désactivés " ,
461
+ text : "Navigation désactivée " ,
406
462
duration : "short" ,
407
463
position : "bottom"
408
464
} ) ;
@@ -434,7 +490,7 @@ const getOrientation = async (event) => {
434
490
tempMapBearing -= 90 ;
435
491
}
436
492
mapBearing = tempMapBearing ;
437
- if ( tracking_active ) {
493
+ if ( navigation_active ) {
438
494
if ( ! isMapPanning ) {
439
495
Globals . map . easeTo ( { bearing : - mapBearing , duration : 100 } ) ;
440
496
}
@@ -491,13 +547,29 @@ const getLocation = async () => {
491
547
} ;
492
548
493
549
const disableTracking = ( ) => {
494
- DOM . $geolocateBtn . style . backgroundImage = "url(\"" + LocationFixeImg + "\")" ;
550
+ DOM . $geolocateBtn . style . backgroundImage = "url(\"" + LocationImg + "\")" ;
495
551
tracking_active = false ;
496
- Toast . show ( {
497
- text : "Suivi de position activé" ,
498
- duration : "short" ,
499
- position : "bottom"
552
+ if ( navigation_active ) {
553
+ Globals . map . setMaxPitch ( 0 ) ;
554
+ navigation_active = false ;
555
+ }
556
+ Globals . map . touchZoomRotate . enable ( ) ;
557
+ Globals . map . getCanvasContainer ( ) . removeEventListener ( "touchstart" , locationOnTouchStartHandler ) ;
558
+ Globals . map . getCanvasContainer ( ) . removeEventListener ( "touchmove" , locationOnTouchMoveHandler ) ;
559
+ } ;
560
+
561
+ const disableNavigation = ( bearing = Globals . map . getBearing ( ) ) => {
562
+ DOM . $geolocateBtn . style . backgroundImage = "url(\"" + LocationFixeImg + "\")" ;
563
+ navigation_active = false ;
564
+ Globals . map . flyTo ( {
565
+ pitch : 0 ,
566
+ bearing : bearing ,
567
+ duration : 500 ,
500
568
} ) ;
569
+ setTimeout ( ( ) => { Globals . map . setMaxPitch ( 0 ) ; } , 500 ) ;
570
+ if ( bearing === 0 ) {
571
+ DOM . $compassBtn . classList . add ( "d-none" ) ;
572
+ }
501
573
} ;
502
574
503
575
let listenResumeAfterLocation = false ;
@@ -577,19 +649,74 @@ const isTrackingActive = () => {
577
649
return tracking_active ;
578
650
} ;
579
651
652
+ const isNavigationActive = ( ) => {
653
+ return navigation_active ;
654
+ } ;
655
+
580
656
const getCurrentPosition = ( ) => {
581
657
return currentPosition ;
582
658
} ;
583
659
660
+ // Event handlers for rotation and zoom when tracking active
661
+ const locationOnTouchStartHandler = ( e ) => {
662
+ if ( e . touches . length === 2 ) {
663
+ startDist = getTouchDistance ( e . touches ) ;
664
+ startZoom = Globals . map . getZoom ( ) ;
665
+ startBearing = Globals . map . getBearing ( ) ;
666
+ startAngle = getTouchAngle ( e . touches ) ;
667
+ rotationEnabled = false ;
668
+ }
669
+ } ;
670
+
671
+ const locationOnTouchMoveHandler = ( e ) => {
672
+ if ( e . touches . length === 2 ) {
673
+ e . preventDefault ( ) ;
674
+
675
+ const currentDist = getTouchDistance ( e . touches ) ;
676
+ const scale = currentDist / startDist ;
677
+ const newZoom = startZoom + Math . log2 ( scale ) ;
678
+
679
+ const currentAngle = getTouchAngle ( e . touches ) ;
680
+ let angleDelta = currentAngle - startAngle ;
681
+ if ( ! rotationEnabled && Math . abs ( angleDelta ) > 8 ) {
682
+ rotationEnabled = true ;
683
+ startAngle += angleDelta ;
684
+ angleDelta = 0 ;
685
+ }
686
+ const newBearing = startBearing + angleDelta ;
687
+
688
+ Globals . map . setZoom ( newZoom , { around : Globals . map . getCenter ( ) } ) ;
689
+ if ( rotationEnabled ) {
690
+ Globals . map . setBearing ( newBearing ) ;
691
+ }
692
+ }
693
+ } ;
694
+
695
+ function getTouchDistance ( touches ) {
696
+ const [ touch1 , touch2 ] = touches ;
697
+ const dx = touch1 . clientX - touch2 . clientX ;
698
+ const dy = touch1 . clientY - touch2 . clientY ;
699
+ return Math . sqrt ( dx * dx + dy * dy ) ;
700
+ }
701
+
702
+ function getTouchAngle ( touches ) {
703
+ const [ touch1 , touch2 ] = touches ;
704
+ const dx = touch1 . clientX - touch2 . clientX ;
705
+ const dy = touch1 . clientY - touch2 . clientY ;
706
+ return Math . atan2 ( dy , dx ) * ( 180 / Math . PI ) ;
707
+ }
708
+
584
709
export default {
585
710
target,
586
711
getCurrentPosition,
587
712
isLocationActive,
588
713
isTrackingActive,
714
+ isNavigationActive,
589
715
moveTo,
590
716
enablePosition,
591
717
locationOnOff,
592
718
getOrientation,
593
719
getLocation,
594
720
disableTracking,
721
+ disableNavigation,
595
722
} ;
0 commit comments