0% found this document useful (0 votes)
9 views10 pages

Map

The document is a Flutter application that implements a map widget for tracking delivery routes to pharmacies. It utilizes geolocation services to determine the user's current position, fetches nearby pharmacies, and calculates routes using the OpenRouteService API. The app features interactive markers for the user's location, pharmacies, and delivery routes, along with zoom and tracking controls.

Uploaded by

Alaise Tchindou
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
9 views10 pages

Map

The document is a Flutter application that implements a map widget for tracking delivery routes to pharmacies. It utilizes geolocation services to determine the user's current position, fetches nearby pharmacies, and calculates routes using the OpenRouteService API. The app features interactive markers for the user's location, pharmacies, and delivery routes, along with zoom and tracking controls.

Uploaded by

Alaise Tchindou
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 10

import 'dart:convert';

import 'dart:math';
import 'package:chapsante/utils/constants.dart';
import 'package:figma_squircle/figma_squircle.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:gap/gap.dart';
import 'package:geolocator/geolocator.dart';
import 'package:flutter_map/plugin_api.dart';
import 'package:latlong2/latlong.dart';
import 'package:http/http.dart' as http;

class MapWidget extends StatefulWidget {


const MapWidget({super.key});

@override
State<MapWidget> createState() => _MapWidgetState();
}

class _MapWidgetState extends State<MapWidget> {


final _controller = DraggableScrollableController();
final MapController mapController = MapController();

Position? _currentPosition;
LatLng? _deliveryPosition;
LatLng? _clientPosition;
LatLng? _midPoint;
List<LatLng> _pharmacies = [];
List<LatLng> _closestPharmacies = [];
List<LatLng> _otherPharmacies = [];
List<LatLng> _route = [];

@override
void initState() {
super.initState();
_determinePosition();
}

@override
void dispose() {
super.dispose();
}

Future<void> _determinePosition() async {


bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
return Future.error('Les services de localisation sont désactivés.');
}

LocationPermission permission = await Geolocator.checkPermission();


if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
return Future.error('Les permissions de localisation sont refusées.');
}
}

if (permission == LocationPermission.deniedForever) {
return Future.error(
'Les permissions de localisation sont refusées de façon permanente.');
}

Position position = await Geolocator.getCurrentPosition();


setState(() {
_currentPosition = position;
_clientPosition = LatLng(
position.latitude,
position
.longitude);
_deliveryPosition = generateRandomPosition(_clientPosition!, 7500);
mapController.move(
LatLng(position.latitude, position.longitude),
16.0,
);
_fetchPharmacies(); // Récupérer les pharmacies
});
}

LatLng generateRandomPosition(LatLng currentPosition, double radiusInMeters) {


final random = Random();

// Convert radius from meters to degrees


final double radiusInDegrees = radiusInMeters / 111320.0;

// Generate random distance and angle


final double u = random.nextDouble();
final double v = random.nextDouble();
final double w = radiusInDegrees * sqrt(u);
final double t = 2 * pi * v;
final double x = w * cos(t);
final double y = w * sin(t);

// Adjust the x-coordinate for the shrinking of the east-west distances


final double newX = x / cos(currentPosition.latitude * pi / 180);

final double newLatitude = currentPosition.latitude + y;


final double newLongitude = currentPosition.longitude + newX;

return LatLng(newLatitude, newLongitude);


}

Future<void> _fetchPharmacies() async {


if (_deliveryPosition == null || _clientPosition == null) return;

// Calcul du point médian entre le livreur et le client


final midPoint = LatLng(
(_deliveryPosition!.latitude + _clientPosition!.latitude) / 2,
(_deliveryPosition!.longitude + _clientPosition!.longitude) / 2,
);

// Rayon de recherche en mètres


final radius = 7500; // 7,5 km

print("\n================ around:$radius,${midPoint.latitude},$
{midPoint.longitude} ================\n");

final url =
'https://overpass-api.de/api/interpreter?
data=[out:json];node[amenity=pharmacy](around:$radius,${midPoint.latitude},$
{midPoint.longitude});out;';
final response = await http.get(Uri.parse(url));

if (response.statusCode == 200) {
final data = jsonDecode(response.body);
final List<LatLng> pharmacies = [];
for (var element in data['elements']) {
pharmacies.add(
LatLng(element['lat'], element['lon']),
);
}

if (mounted) {
if (pharmacies.isNotEmpty) {
_processPharmacies(pharmacies);
}
}
} else {
throw Exception('Failed to load pharmacies');
}
}

void _processPharmacies(List<LatLng> pharmacies) {


if (_deliveryPosition == null || _clientPosition == null) return;

final midPoint = LatLng(


(_deliveryPosition!.latitude + _clientPosition!.latitude) / 2,
(_deliveryPosition!.longitude + _clientPosition!.longitude) / 2,
);

final List<LatLng> nearbyPharmacies = pharmacies.where((pharmacy) {


final distance = Geolocator.distanceBetween(
midPoint.latitude,
midPoint.longitude,
pharmacy.latitude,
pharmacy.longitude,
);
return distance <= 7500; // 7.5 km
}).toList();

nearbyPharmacies.sort((a, b) {
final distA = Geolocator.distanceBetween(
_deliveryPosition!.latitude,
_deliveryPosition!.longitude,
a.latitude,
a.longitude,
) +
Geolocator.distanceBetween(
_clientPosition!.latitude,
_clientPosition!.longitude,
a.latitude,
a.longitude,
);

final distB = Geolocator.distanceBetween(


_deliveryPosition!.latitude,
_deliveryPosition!.longitude,
b.latitude,
b.longitude,
) +
Geolocator.distanceBetween(
_clientPosition!.latitude,
_clientPosition!.longitude,
b.latitude,
b.longitude,
);

return distA.compareTo(distB);
});

final closestPharmacies = nearbyPharmacies.take(3).toList();


final otherPharmacies = nearbyPharmacies.skip(3).take(7).toList();

setState(() {
_midPoint = midPoint;
_pharmacies = pharmacies;
_closestPharmacies = closestPharmacies;
_otherPharmacies = otherPharmacies;
if (closestPharmacies.isNotEmpty) {
_fetchRoute(closestPharmacies[0]);
}
});
}

Future<void> _fetchRoute(LatLng pharmacy) async {


if (_deliveryPosition == null || _clientPosition == null) return;

String apiKey = "5b3ce3597851110001cf6248fdaa681099a14954b53344ee91a3c59d";

// Itinéraire du livreur à la pharmacie


final route1Url =
'https://api.openrouteservice.org/v2/directions/driving-car?
api_key=$apiKey&start=${_deliveryPosition!.longitude},$
{_deliveryPosition!.latitude}&end=${pharmacy.longitude},${pharmacy.latitude}';
final route1Response = await http.get(Uri.parse(route1Url));

// Itinéraire de la pharmacie au client


final route2Url =
'https://api.openrouteservice.org/v2/directions/driving-car?
api_key=$apiKey&start=${pharmacy.longitude},${pharmacy.latitude}&end=$
{_clientPosition!.longitude},${_clientPosition!.latitude}';
final route2Response = await http.get(Uri.parse(route2Url));

print("route1Response.statusCode: ${route1Response.statusCode}");
print("route2Response.statusCode: ${route2Response.statusCode}");

if (route1Response.statusCode == 200 && route2Response.statusCode == 200) {


final route1Data = jsonDecode(route1Response.body);
final route2Data = jsonDecode(route2Response.body);

print("route1Data: $route1Data");
print("route2Data: $route2Data");

final List<LatLng> routeToPharmacy = [];


final List<LatLng> routeToClient = [];

if (route1Data['features'][0]['geometry']['coordinates'] != null &&


route1Data['features'][0]['geometry']['coordinates'].isNotEmpty) {
final coordinates =
route1Data['features'][0]['geometry']['coordinates'];
print("coordinates: $coordinates");
for (var coord in coordinates) {
routeToPharmacy.add(LatLng(coord[1], coord[0]));
}
}

if (route2Data['features'][0]['geometry']['coordinates'] != null &&


route2Data['features'][0]['geometry']['coordinates'].isNotEmpty) {
final coordinates =
route2Data['features'][0]['geometry']['coordinates'];
for (var coord in coordinates) {
routeToClient.add(LatLng(coord[1], coord[0]));
}
}

setState(() {
_route = routeToPharmacy + routeToClient;
_moveMarker();
});
} else {
throw Exception('Failed to fetch route');
}
}

List<LatLng> _newRoute = [];

Future<void> _moveMarker() async {


if (_deliveryPosition == null || _route.isEmpty) return;

const int steps = 50;


for (int i = 0; i < _route.length - 1; i++) {
final from = _route[i];
final to = _route[i + 1];
for (int j = 0; j <= steps; j++) {
final double lat =
from.latitude + (to.latitude - from.latitude) * (j / steps);
final double lng =
from.longitude + (to.longitude - from.longitude) * (j / steps);

setState(() {
_deliveryPosition = LatLng(lat, lng);
});
mapController.move(_deliveryPosition!, mapController.zoom);

// Si le livreur prend un autre chemin, recalculer l'itinéraire


if (_newRoute.isNotEmpty && !_newRoute.contains(LatLng(lat, lng))) {
_newRoute = await _fetchNewRoute();
setState(() {
_newRoute = _newRoute;
});
}

await Future.delayed(const Duration(milliseconds: 100));

}
}
}

Future<List<LatLng>> _fetchNewRoute() async {


if (_deliveryPosition == null || _clientPosition == null) return [];

String apiKey = "5b3ce3597851110001cf6248fdaa681099a14954b53344ee91a3c59d";

final url =
'https://api.openrouteservice.org/v2/directions/driving-car?
api_key=$apiKey&start=${_deliveryPosition!.longitude},$
{_deliveryPosition!.latitude}&end=${_clientPosition!.longitude},$
{_clientPosition!.latitude}';
final response = await http.get(Uri.parse(url));

if (response.statusCode == 200) {
final data = jsonDecode(response.body);
final List<LatLng> route = [];

if (data['features'][0]['geometry']['coordinates'] != null &&


data['features'][0]['geometry']['coordinates'].isNotEmpty) {
final coordinates = data['features'][0]['geometry']['coordinates'];
for (var coord in coordinates) {
route.add(LatLng(coord[1], coord[0]));
}
}

return route;
} else {
throw Exception('Failed to fetch new route');
}
}

@override
Widget build(BuildContext context) {
return SafeArea(
child: Center(
child: Stack(
children: [
_buildMap(),
_buildZoomButtons(),
_buildPharmaButton(),
_buildTrackPositionButton(),
Positioned(
top: 10,
left: 10,
child: Container(
padding: const EdgeInsets.all(8),
color: Colors.white.withOpacity(0.8),
child: Text(
_calculateDistanceAndTime(),
style: TextStyle(fontSize: 16, color: Colors.black),
),
),
),
],
),
),
);
}
Widget _buildMap() {
return FlutterMap(
mapController: mapController,
options: MapOptions(
center: _currentPosition != null
? LatLng(_currentPosition!.latitude, _currentPosition!.longitude)
: LatLng(6.1319, 1.2228),
zoom: 14.0,
minZoom: 12.0,
maxZoom: 18.0,
),
children: [
TileLayer(
urlTemplate: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
subdomains: ['a', 'b', 'c'],
),
MarkerLayer(
markers: _buildMarkers(),
),
PolylineLayer(
polylines: [
Polyline(
points: _route,
color: Colors.blue,
strokeWidth: 4.0,
),
Polyline(
points: _newRoute,
color: Colors.cyanAccent,
strokeWidth: 4.0,
isDotted: true,
),
],
),
],
);
}

List<Marker> _buildMarkers() {
final List<Marker> markers = [];

if (_currentPosition != null) {
markers.add(
Marker(
point:
LatLng(_currentPosition!.latitude, _currentPosition!.longitude),
builder: (ctx) => Icon(Icons.person_pin_circle,
color: Colors.blue, size: defaultIconSize),
),
);
}

for (var pharmacy in _pharmacies) {


markers.add(
Marker(
point: pharmacy,
builder: (ctx) => GestureDetector(
onTap: () {
_fetchRoute(pharmacy);
},
child: Icon(Icons.local_pharmacy,
color: Colors.orange, size: defaultIconSize),
),
),
);
}

if (_deliveryPosition != null) {
markers.add(
Marker(
point: _deliveryPosition!,
builder: (ctx) => Icon(Icons.delivery_dining,
color: Colors.red, size: defaultIconSize),
),
);
}

return markers;
}

String _calculateDistanceAndTime() {
if (_deliveryPosition == null || _clientPosition == null) return '';

final distance = Geolocator.distanceBetween(


_deliveryPosition!.latitude,
_deliveryPosition!.longitude,
_clientPosition!.latitude,
_clientPosition!.longitude,
);

final time = (distance / 50000).round(); // 50 km/h

return 'Distance: ${(distance / 1000).toStringAsFixed(2)} km\nTime: $time min';


}

Widget _buildZoomButtons() {
return Positioned(
bottom: 10,
right: 5,
child: Column(
children: [
_buildIconButton(
icon: Icons.add,
onPressed: () {
mapController.move(mapController.center, mapController.zoom + 1);
},
),
const Gap(defaultSpacing),
_buildIconButton(
icon: Icons.remove,
onPressed: () {
mapController.move(mapController.center, mapController.zoom - 1);
},
),
],
),
);
}

Widget _buildPharmaButton() {
return Positioned(
bottom: 10,
left: 5,
child: _buildIconButton(
icon: Icons.home_work_outlined,
onPressed: () {
mapController.move(
_currentPosition != null
? LatLng(
_currentPosition!.latitude, _currentPosition!.longitude)
: LatLng(6.1319, 1.2228),
16.0,
);
},
),
);
}

Widget _buildTrackPositionButton() {
return Positioned(
top: 10,
right: 5,
child: _buildIconButton(
icon: Icons.my_location,
onPressed: () async {
Position position = await Geolocator.getCurrentPosition();
mapController.move(
LatLng(position.latitude, position.longitude),
16.0,
);
},
),
);
}

Widget _buildIconButton({
required IconData icon,
required VoidCallback onPressed,
}) {
return Container(
decoration: ShapeDecoration(
color: Colors.white.withOpacity(0.8),
shape: SmoothRectangleBorder(
side: const BorderSide(
color: accentColor,
width: 2,
),
borderRadius: SmoothBorderRadius(
cornerRadius: defaultRadius,
cornerSmoothing: 1,
),
),
),
child: IconButton(
color: accentColor,
onPressed: onPressed,
icon: Icon(icon),
),
);
}
}

You might also like