flyToLocationWithBearing method

Future<void> flyToLocationWithBearing(
  1. RaliPosition position, {
  2. double? bearing,
  3. double zoom = 17.0,
  4. double pitch = 70.0,
  5. int duration = 100,
  6. double? speed,
})

Flies to location with bearing calculated from movement direction in OTS view

Implementation

Future<void> flyToLocationWithBearing(RaliPosition position, {
  double? bearing,
  double zoom = 17.0,
  double pitch = 70.0,
  int duration = 100, // Very short duration for smoother navigation
  double? speed,
}) async {
  try {
    // Throttle updates to prevent too many animations stacking
    final now = DateTime.now();
    if (now.difference(_lastCameraUpdate).inMilliseconds < 20) {
      // Skip this update if too soon after the last one
      return;
    }
    _lastCameraUpdate = now;

    if (DebugSettings.showCameraLogs) {
      print('[CAMERA] Updating camera: pos=${position.lat},${position.lng} bearing=${bearing ?? 0} animating=$_isCameraAnimating');
    }

    // Calculate dynamic puck position based on speed
    // Position ranges from 10% from bottom at normal speeds to 30% at crawling speeds
    final speedKmh = speed ?? 0.0;
    double puckPositionPercentage = 0.1; // Default 10% from bottom

    // At lower speeds, move puck position upward
    if (speedKmh < 5.0) {
      puckPositionPercentage = 0.3; // 30% from bottom at very low speeds
    } else if (speedKmh < 15.0) {
      // Linear interpolation between 30% and 10% based on speed
      puckPositionPercentage = 0.3 - (speedKmh - 5.0) * (0.2 / 10.0);
    }

    // Get map size to calculate actual padding
    final mapSize = await mapboxMap.getSize();
    final screenHeight = mapSize != null && mapSize.height != null
        ? mapSize.height.toDouble()
        : 500.0;

    // Calculate bottom padding in pixels
    final bottomPadding = screenHeight * puckPositionPercentage;
    debugPrint("[DEBUG] Dynamic puck position: ${(puckPositionPercentage * 100).toStringAsFixed(1)}% from bottom (${bottomPadding.toStringAsFixed(1)}px) at speed ${speedKmh.toStringAsFixed(1)} km/h");

    // Convert to Mapbox position
    final mapboxPosition = mapbox.Position(position.lng, position.lat);
    final point = mapbox.Point(coordinates: mapboxPosition);

    // Create camera options with the calculated padding
    final camera = mapbox.CameraOptions(
      center: point,
      zoom: zoom,
      bearing: bearing ?? 0.0,
      pitch: pitch,
      padding: mapbox.MbxEdgeInsets(
        top: 0,
        left: 0,
        right: 0,
        bottom: bottomPadding, // Dynamic padding based on speed
      ),
    );

    // If we're already animating, use direct camera update to prevent animation queuing
    if (_isCameraAnimating) {
      await mapboxMap.setCamera(camera);
      return;
    }

    // Mark animation starting
    _isCameraAnimating = true;

    // Use very short animation duration for smoother navigation
    await mapboxMap.flyTo(
      camera,
      mapbox.MapAnimationOptions(
        duration: duration,
      )
    );

    // Mark animation complete
    _isCameraAnimating = false;
  } catch (e) {
    print('Error moving camera with bearing: $e');
    _isCameraAnimating = false; // Ensure flag is reset on error

    // Attempt direct camera update as fallback
    try {
      await mapboxMap.setCamera(
        mapbox.CameraOptions(
          center: mapbox.Point(
            coordinates: mapbox.Position(position.lng, position.lat)
          ),
          zoom: zoom,
          bearing: bearing ?? 0.0,
          pitch: pitch
        )
      );
    } catch (e2) {
      print('Fallback direct camera update also failed: $e2');
    }
  }
}