From f880ac10027ed76fbaceeb74af0cee13d97b1d2e Mon Sep 17 00:00:00 2001 From: Valentin CZERYBA Date: Sat, 16 Nov 2024 23:24:00 +0100 Subject: [PATCH] add direction 75% --- covas_mobile/lib/pages/ItemMenu.dart | 2 +- covas_mobile/lib/pages/MapboxPages.dart | 266 +++++++++++++++++------- 2 files changed, 190 insertions(+), 78 deletions(-) diff --git a/covas_mobile/lib/pages/ItemMenu.dart b/covas_mobile/lib/pages/ItemMenu.dart index 82e64cf..c83c204 100644 --- a/covas_mobile/lib/pages/ItemMenu.dart +++ b/covas_mobile/lib/pages/ItemMenu.dart @@ -244,7 +244,7 @@ class _ItemMenuState extends State with ShowErrorDialog { Navigator.push( context, MaterialPageRoute( - builder: (_) => Mapboxpages( + builder: (_) => MapboxPages( title: '${widget.title}', place: '${place}'))); }, diff --git a/covas_mobile/lib/pages/MapboxPages.dart b/covas_mobile/lib/pages/MapboxPages.dart index fc09080..d0f643f 100644 --- a/covas_mobile/lib/pages/MapboxPages.dart +++ b/covas_mobile/lib/pages/MapboxPages.dart @@ -1,18 +1,16 @@ import 'dart:convert'; -import 'dart:typed_data'; import 'dart:io'; -import 'package:flutter/services.dart'; // For loading assets import 'package:flutter/material.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; // For environment variables +import 'package:flutter/services.dart'; // For loading assets import 'package:http/http.dart' as http; import 'package:mapbox_gl/mapbox_gl.dart'; -import 'package:flutter_dotenv/flutter_dotenv.dart'; // Import dotenv +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:geolocator/geolocator.dart'; // For getting the user's location import '../classes/alert.dart'; // Assuming this contains your error dialog code. -import '../classes/events.dart'; // Your Event class, assuming you are using it. import '../variable/globals.dart' as globals; -import 'package:shared_preferences/shared_preferences.dart'; - void main() async { await dotenv.load(fileName: ".env"); // Load .env file runApp(const MyApp()); @@ -24,46 +22,48 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( - title: 'Flutter Demo', + title: 'Flutter Directions Example', theme: ThemeData( primarySwatch: Colors.blue, ), - home: const Mapboxpages(title: 'Event Location', place: "Flutter"), + home: const MapboxPages(title: 'Event Location', place: "Flutter"), ); } } -class Mapboxpages extends StatefulWidget { - const Mapboxpages({Key? key, required this.title, required this.place}) +class MapboxPages extends StatefulWidget { + const MapboxPages({Key? key, required this.title, required this.place}) : super(key: key); final String title; final String place; @override - State createState() => _MapboxpagesState(); + State createState() => _MapboxPagesState(); } -class _MapboxpagesState extends State with ShowErrorDialog { +class _MapboxPagesState extends State with ShowErrorDialog { + late MapboxMapController mapController; late String mapboxAccessToken; - late MapboxMapController mapController; // Mark mapController as nullable + List routeCoordinates = []; + String selectedMode = 'driving'; double longitude = 0.0; double latitude = 0.0; bool isLoading = true; + LatLng? userPosition; @override void initState() { super.initState(); _initToken(); _getEventInfo(); + _getUserLocation(); } - // Load the Mapbox access token from the .env file void _initToken() { mapboxAccessToken = dotenv.env['MAPBOX_ACCESS_TOKEN'] ?? ''; if (mapboxAccessToken.isEmpty) { showErrorDialog(context, "Mapbox Access Token is not available."); - return; } } @@ -76,7 +76,6 @@ class _MapboxpagesState extends State with ShowErrorDialog { var responseGet = await http.get(urlGet, headers: {HttpHeaders.cookieHeader: 'access_token=${accessToken}'}); - stderr.writeln('Response Get status: ${responseGet.statusCode}'); if (responseGet.statusCode == 200) { var events = jsonDecode(utf8.decode(responseGet.bodyBytes)); latitude = events["latitude"]; @@ -85,59 +84,98 @@ class _MapboxpagesState extends State with ShowErrorDialog { isLoading = false; }); } else { - var text = ""; - switch (responseGet.statusCode) { - case 400: - { - text = "RequĂȘte mal construite"; - } - break; - case 406: - { - text = "Mot de passe incorrect"; - } - break; - case 404: - { - text = "Utilisateur inconnu"; - } - break; - case 403: - { - text = "Vous n'avez pas l'autorisation de faire cette action"; - } - break; - case 410: - { - text = "Token invalide"; - } - break; - case 500: - { - text = "Probleme interne du serveur"; - } - break; - default: - { - text = "Probleme d'authentification inconnu"; - } - break; - } - showErrorDialog(context, text); + _handleErrorResponse(responseGet.statusCode); } } else { - showErrorDialog(context, "Cache invalide"); + showErrorDialog(context, "Invalid cache."); } } - // Load image from assets as Uint8List - Future _loadMarkerImage() async { - final ByteData data = await rootBundle.load('images/marker.png'); - return data.buffer.asUint8List(); + void _handleErrorResponse(int statusCode) { + String text; + switch (statusCode) { + case 400: + text = "Bad Request."; + break; + case 406: + text = "Incorrect Password."; + break; + case 404: + text = "User Not Found."; + break; + case 403: + text = "Action not permitted."; + break; + case 410: + text = "Invalid Token."; + break; + case 500: + text = "Internal Server Error."; + break; + default: + text = "Unknown error."; + } + showErrorDialog(context, text); } - void _onMapCreated(MapboxMapController controller) { - mapController = controller; + Future _getUserLocation() async { + try { + bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); + if (!serviceEnabled) { + showErrorDialog(context, "Location services are disabled."); + return; + } + + LocationPermission permission = await Geolocator.checkPermission(); + if (permission == LocationPermission.denied) { + permission = await Geolocator.requestPermission(); + if (permission == LocationPermission.denied) { + showErrorDialog(context, "Location permissions are denied."); + return; + } + } + + if (permission == LocationPermission.deniedForever) { + showErrorDialog(context, + "Location permissions are permanently denied. Enable them in settings."); + return; + } + LocationSettings locationSettings = LocationSettings( + accuracy: LocationAccuracy.high, + distanceFilter: + 10, // Optional: Minimum distance (in meters) to trigger location update + ); + + Position position = await Geolocator.getCurrentPosition( + locationSettings: locationSettings, + ); + setState(() { + userPosition = LatLng(position.latitude, position.longitude); + }); + } catch (e) { + showErrorDialog(context, "Failed to get user location: $e"); + } + } + + Future _fetchRoute( + LatLng origin, LatLng destination, String mode) async { + final url = Uri.parse( + 'https://api.mapbox.com/directions/v5/mapbox/$mode/${origin.longitude},${origin.latitude};${destination.longitude},${destination.latitude}?geometries=geojson&access_token=$mapboxAccessToken', + ); + + final response = await http.get(url); + if (response.statusCode == 200) { + final data = jsonDecode(response.body); + final geometry = data['routes'][0]['geometry']['coordinates']; + setState(() { + routeCoordinates = geometry.map((coord) { + return LatLng(coord[1], coord[0]); + }).toList(); + }); + } else { + showErrorDialog( + context, "Failed to fetch the route: ${response.statusCode}"); + } } // Called when the map is created @@ -152,15 +190,15 @@ class _MapboxpagesState extends State with ShowErrorDialog { // Ensure the coordinates are valid if (latitude != 0.0 && longitude != 0.0) { // Load marker image as Uint8List - final markerImage = await _loadMarkerImage(); + final userMarkerImage = await _loadMarkerImage('images/marker.png'); // Register the image with Mapbox - await mapController!.addImage("custom-marker", markerImage); + await mapController.addImage('user-marker', userMarkerImage); final symbolOptions = SymbolOptions( geometry: LatLng(latitude, longitude), - iconImage: "custom-marker", // Use the registered custom marker - iconSize: 0.5, // Optional: Adjust size + iconImage: "user-marker", // Use the registered custom marker + iconSize: 0.4, // Optional: Adjust size ); // Debugging symbol options @@ -181,21 +219,95 @@ class _MapboxpagesState extends State with ShowErrorDialog { } } + Future _drawRouteAndMarkers() async { + if (mapController != null && + userPosition != null && + latitude != 0.0 && + longitude != 0.0) { + final destination = LatLng(latitude, longitude); + + // Register the custom images + // Add event marker + + final userMarkerImage = await _loadMarkerImage('images/marker.png'); + + // Register the image with Mapbox + await mapController.addImage('event-marker', userMarkerImage); + await mapController.addSymbol(SymbolOptions( + geometry: destination, + iconImage: 'event-marker', // Custom icon for event + iconSize: 0.4, + )); + + // Fetch and draw route + await _fetchRoute(userPosition!, destination, selectedMode); + + if (routeCoordinates.isNotEmpty) { + await mapController.addLine( + LineOptions( + geometry: routeCoordinates, + lineColor: '#3b9ddd', + lineWidth: 5.0, + lineOpacity: 0.8, + ), + ); + } + } else { + showErrorDialog(context, "Invalid coordinates or user position."); + } + } + + // Load image from assets + Future _loadMarkerImage(String assetPath) async { + final ByteData data = await rootBundle.load(assetPath); + return data.buffer.asUint8List(); + } + @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: Text(widget.place)), - body: isLoading - ? Center(child: CircularProgressIndicator()) - : MapboxMap( - accessToken: mapboxAccessToken, // Your Mapbox API key - onMapCreated: _onMapCreated, - onStyleLoadedCallback: _onStyleLoaded, - initialCameraPosition: CameraPosition( - target: LatLng(latitude, longitude), - zoom: 14.0, - ), + appBar: AppBar( + title: Text(widget.place), + actions: [ + DropdownButton( + value: selectedMode, + items: ['walking', 'cycling', 'driving'].map((mode) { + return DropdownMenuItem(value: mode, child: Text(mode)); + }).toList(), + onChanged: (mode) { + setState(() { + selectedMode = mode!; + }); + }, + ) + ], + ), + body: Stack( + children: [ + isLoading + ? Center(child: CircularProgressIndicator()) + : MapboxMap( + accessToken: mapboxAccessToken, + onMapCreated: (controller) { + mapController = controller; + }, + onStyleLoadedCallback: _onStyleLoaded, + initialCameraPosition: CameraPosition( + target: LatLng(latitude, longitude), + zoom: 14.0, + ), + ), + Positioned( + bottom: 20, + right: 20, + child: FloatingActionButton( + onPressed: _drawRouteAndMarkers, + child: Icon(Icons.directions), + tooltip: 'Get Directions and Markers', ), + ), + ], + ), ); } }