From ca0c86a3f65d997ae569f8c087354ed198daf494 Mon Sep 17 00:00:00 2001 From: alper-savas <alpersavas1998@gmail.com> Date: Tue, 28 Mar 2023 22:24:43 +0200 Subject: [PATCH] Implement conversion from Google maps to Flutter map. --- lib/config/config.dart | 1 + lib/main.dart | 295 ++++++++++++++++++++++---------- lib/widgets/directions.dart | 82 ++++----- lib/widgets/directionsRepo.dart | 53 +++--- 4 files changed, 268 insertions(+), 163 deletions(-) diff --git a/lib/config/config.dart b/lib/config/config.dart index a62b33a..5c7118b 100644 --- a/lib/config/config.dart +++ b/lib/config/config.dart @@ -1,4 +1,5 @@ class Config { final String GOOGLE_API = ''; final String WEATHER_API = ''; + final String OSM_API = ''; } diff --git a/lib/main.dart b/lib/main.dart index f08931d..4608290 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,17 +1,19 @@ -// ignore_for_file: prefer_const_constructors, sort_child_properties_last, import_of_legacy_library_into_null_safe, prefer_is_not_empty, unnecessary_null_comparison, deprecated_member_use, depend_on_referenced_packages, avoid_function_literals_in_foreach_calls, prefer_final_fields +// ignore_for_file: prefer_const_constructors, sort_child_properties_last, import_of_legacy_library_into_null_safe, prefer_is_not_empty, unnecessary_null_comparison, deprecated_member_use, depend_on_referenced_packages, avoid_function_literals_in_foreach_calls, prefer_final_fields, prefer_typing_uninitialized_variables import 'package:flutter/material.dart'; -import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:flutter_map/flutter_map.dart'; import 'package:intl/intl.dart'; import 'widgets/returnOrigin.dart'; import 'widgets/returnDestination.dart'; -import 'widgets/directionsRepo.dart'; -import 'widgets/directions.dart'; import 'package:geocoding/geocoding.dart'; import 'package:geolocator/geolocator.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; import 'package:weather_icons/weather_icons.dart'; import 'config/config.dart'; +import 'package:latlong/latlong.dart' as latlng; +// import 'widgets/directionsRepo.dart'; +// import 'package:google_maps_flutter/google_maps_flutter.dart'; +// import 'widgets/directions.dart'; void main() => runApp(MyApp()); @@ -35,6 +37,12 @@ class MyApp extends StatelessWidget { } } +// Helper class +class LineString { + LineString(this.lineString); + List<dynamic> lineString; +} + class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @@ -45,21 +53,24 @@ class MyHomePage extends StatefulWidget { class _MyHomePageState extends State<MyHomePage> { Config config = Config(); // Initial map coords and map controller to control camera movements. - static const _initialCameraPosition = - CameraPosition(target: LatLng(52.5163, 13.3777), zoom: 12); - late GoogleMapController _mapController; + late final MapController _mapController = MapController(); + // static const _initialCameraPosition = + // CameraPosition(target: LatLng(52.5163, 13.3777), zoom: 12); + // late GoogleMapController _mapController; // Input field texts for origin/dest points. String originText = 'Starting Point...'; String destinationText = 'Destination...'; // Set origin/dest coords, respective markers, lat/lng coords between poits and polyline corresponding to coords. - late LatLng _originCoordinates; - late LatLng _destCoordinates; + late latlng.LatLng _originCoordinates; + late latlng.LatLng _destCoordinates; int _markerCounter = 0; Set<Marker> _markers = {}; - late Directions _info; - Set<Polyline> _polyline = {}; + var _data; + List<latlng.LatLng> _polyline = <latlng.LatLng>[]; + // late Directions _info; + // Set<Polyline> _polyline = {}; String _totalDuration = ''; String _totalDistance = ''; @@ -102,17 +113,13 @@ class _MyHomePageState extends State<MyHomePage> { setState(() { originText = result; _originCoordinates = - LatLng(originLoc[0].latitude, originLoc[0].longitude); + latlng.LatLng(originLoc[0].latitude, originLoc[0].longitude); }); } // Toggle keyboard FocusManager.instance.primaryFocus?.unfocus(); // Animate camera to the starting point. - _mapController.animateCamera( - CameraUpdate.newCameraPosition( - CameraPosition(target: _originCoordinates, zoom: 14), - ), - ); + _mapController.move(_originCoordinates, 13); setState(() { _markers = {}; }); @@ -133,18 +140,15 @@ class _MyHomePageState extends State<MyHomePage> { setState( () { destinationText = result; - _destCoordinates = LatLng(destLoc[0].latitude, destLoc[0].longitude); + _destCoordinates = + latlng.LatLng(destLoc[0].latitude, destLoc[0].longitude); }, ); } // Toggle keyboard FocusManager.instance.primaryFocus?.unfocus(); - // Animate camera to the destination point. - _mapController.animateCamera( - CameraUpdate.newCameraPosition( - CameraPosition(target: _destCoordinates, zoom: 14), - ), - ); + // Animate camera to the destionation point. + _mapController.move(_destCoordinates, 13); if (_markers.length > 1) { _markers.remove(_markers.last); } @@ -160,17 +164,20 @@ class _MyHomePageState extends State<MyHomePage> { void _getShortestPath() async { _getWeather(); _resetPolyline(); - _drawPolyline(); + _getDirections(); } // Add markers for origin/dest coords. void _addOriginMarker() { _markers.add( Marker( - markerId: MarkerId('origin'), - infoWindow: InfoWindow(title: 'Origin'), - icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueGreen), - position: _originCoordinates, + width: 140, + height: 140, + point: _originCoordinates, + builder: (ctx) => Icon( + Icons.location_on, + color: Color.fromRGBO(0, 128, 0, 1), + ), ), ); setState(() { @@ -182,10 +189,13 @@ class _MyHomePageState extends State<MyHomePage> { void _addDestinationMarker() { _markers.add( Marker( - markerId: MarkerId('destination'), - infoWindow: InfoWindow(title: 'Destination'), - icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRed), - position: _destCoordinates, + width: 100, + height: 100, + point: _destCoordinates, + builder: (ctx) => Icon( + Icons.location_on, + color: Colors.red, + ), ), ); setState(() { @@ -194,47 +204,122 @@ class _MyHomePageState extends State<MyHomePage> { }); } - // Get coords between origin/dest points corresponding to shortest path and update _info. - // This part is going to be updated to take directions from backend instead of Direction API. - Future<Directions> _getDirections() async { - final directions = await DirectionsRepo().getDirections( - origin: _originCoordinates, destination: _destCoordinates); + // Get coords between origin/dest points corresponding to shortest path. + void _getDirections() async { + _resetPolyline(); + try { + var originlat = _originCoordinates.latitude; + var originlon = _originCoordinates.longitude; + var destlat = _destCoordinates.latitude; + var destlon = _destCoordinates.longitude; - setState(() { - _info = directions; - }); + // Get direction data + final response = await http.get( + Uri.parse( + 'https://api.openrouteservice.org/v2/directions/driving-car?api_key=${config.OSM_API}&start=$originlon,$originlat&end=$destlon,$destlat', + ), + ); + _data = jsonDecode(response.body); + var coordinates = _data['features'][0]['geometry']['coordinates']; + double duration = + _data['features'][0]['properties']['segments'][0]['duration']; + double distance = + _data['features'][0]['properties']['segments'][0]['distance']; - return _info; - } + // Format duration and distance + _totalDuration = getTotalDuration(duration); + _totalDistance = getTotalDistance(distance); + + // Draw polyline + drawPolyline(coordinates); + + // Find bound and move camera + final bounds = LatLngBounds.fromPoints([ + _originCoordinates, + _destCoordinates, + ]); + _mapController.fitBounds( + bounds, + options: const FitBoundsOptions( + padding: EdgeInsets.only(left: 30, right: 30), + ), + ); - // Draw Polyline between given list of coordinates and update _polyline - void _drawPolyline() { - _getDirections().then((value) { - _polyline.add(Polyline( - polylineId: PolylineId("polylineId"), - color: Theme.of(context).primaryColor, - width: 4, - points: value.polylinePoints - .map((e) => LatLng(e.latitude, e.longitude)) - .toList(), - )); - _totalDuration = value.totalDuration; - _totalDistance = value.totalDistance; setState(() { - _polyline; _totalDuration; _totalDistance; + _polyline; }); - // Animate camera to the shortest path. - _mapController.animateCamera( - CameraUpdate.newLatLngBounds(_info.bounds, 110.0), - ); - }); + } catch (e) { + print(e); + } } + void drawPolyline(coordinates) { + LineString ls = LineString(coordinates); + + // Draw polyline based on given coordinates + for (int i = 0; i < ls.lineString.length; i++) { + _polyline.add(latlng.LatLng(ls.lineString[i][1], ls.lineString[i][0])); + } + } + + String getTotalDuration(double duration) { + double durationInMins = duration / 60; + int hours = (durationInMins / 60).floor(); + int min = (durationInMins % 60).floor(); + if (hours != 0) { + return '$hours hours $min mins'; + } + return '$min mins'; + } + + String getTotalDistance(double distance) { + int distanceInKm = (distance / 1000).ceil(); + return '$distanceInKm km'; + } + + // Get coords between origin/dest points corresponding to shortest path and update _info. + // This part is going to be updated to take directions from backend instead of Direction API. + // Future<Directions> _getDirections() async { + // final directions = await DirectionsRepo().getDirections( + // origin: _originCoordinates, destination: _destCoordinates); + + // setState(() { + // _info = directions; + // }); + + // return _info; + // } + + // Draw Polyline between given list of coordinates and update _polyline + // void _drawPolyline() { + // _getDirections().then((value) { + // _polyline.add(Polyline( + // polylineId: PolylineId("polylineId"), + // color: Theme.of(context).primaryColor, + // width: 4, + // points: value.polylinePoints + // .map((e) => LatLng(e.latitude, e.longitude)) + // .toList(), + // )); + // _totalDuration = value.totalDuration; + // _totalDistance = value.totalDistance; + // setState(() { + // _polyline; + // _totalDuration; + // _totalDistance; + // }); + // // Animate camera to the shortest path. + // // _mapController.animateCamera( + // // CameraUpdate.newLatLngBounds(_info.bounds, 110.0), + // // ); + // }); + // } + // Helper function to reset polyline before calculating new polyline for another route. void _resetPolyline() { - _polyline = {}; + _polyline = []; setState(() { _polyline; }); @@ -264,14 +349,11 @@ class _MyHomePageState extends State<MyHomePage> { originText = json.decode(response.body)['results'][0]['formatted_address']; _originCoordinates = - LatLng(position.latitude, position.longitude); + latlng.LatLng(position.latitude, position.longitude); }); - _mapController.animateCamera( - CameraUpdate.newCameraPosition( - CameraPosition(target: _destCoordinates, zoom: 14), - ), - ); - _addDestinationMarker(); + _mapController.move(_originCoordinates, 13); + _resetOriginMarker(); + _addOriginMarker(); _resetPolyline(); }, ).catchError( @@ -284,15 +366,18 @@ class _MyHomePageState extends State<MyHomePage> { } // Add origin marker on touch. - void _appearOriginMarkerOnTouch(LatLng pos) async { + void _appearOriginMarkerOnTouch(latlng.LatLng pos) async { _resetPolyline(); _resetOriginMarker(); _markers.add( Marker( - markerId: MarkerId('origin'), - infoWindow: InfoWindow(title: 'Origin'), - icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueGreen), - position: pos, + width: 100, + height: 100, + point: pos, + builder: (ctx) => Icon( + Icons.location_on, + color: Color.fromRGBO(0, 128, 0, 1), + ), ), ); final url = Uri.parse( @@ -302,21 +387,26 @@ class _MyHomePageState extends State<MyHomePage> { setState(() { _markers; originText; - _originCoordinates = LatLng(pos.latitude, pos.longitude); + _originCoordinates = latlng.LatLng(pos.latitude, pos.longitude); _markerCounter = 1; }); } // Add destination marker on touch. - void _appearDestMarkerOnTouch(LatLng pos) async { + void _appearDestMarkerOnTouch(latlng.LatLng pos) async { _resetPolyline(); - + if (_markers.length > 1) { + _markers.remove(_markers.elementAt(1)); + } _markers.add( Marker( - markerId: MarkerId('dest'), - infoWindow: InfoWindow(title: 'Dest'), - icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRed), - position: pos, + width: 100, + height: 100, + point: pos, + builder: (ctx) => Icon( + Icons.location_on, + color: Colors.red, + ), ), ); final url = Uri.parse( @@ -327,7 +417,7 @@ class _MyHomePageState extends State<MyHomePage> { setState(() { _markers; destinationText; - _destCoordinates = LatLng(pos.latitude, pos.longitude); + _destCoordinates = latlng.LatLng(pos.latitude, pos.longitude); _markerCounter = 0; }); } @@ -607,16 +697,37 @@ class _MyHomePageState extends State<MyHomePage> { return Scaffold( body: Stack( children: [ - GoogleMap( - initialCameraPosition: _initialCameraPosition, - myLocationButtonEnabled: false, - zoomControlsEnabled: false, - onMapCreated: (controller) => _mapController = controller, - markers: _markers, - polylines: _polyline, - onLongPress: _markerCounter == 0 + FlutterMap( + mapController: _mapController, + options: MapOptions( + center: latlng.LatLng(52.5163, 13.3777), + zoom: 12, + onTap: _markerCounter == 0 ? _appearOriginMarkerOnTouch - : _appearDestMarkerOnTouch), + : _appearDestMarkerOnTouch, + ), + layers: [ + TileLayerOptions( + urlTemplate: + 'https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png', + subdomains: ['a', 'b', 'c'], + ), + MarkerLayerOptions( + markers: [ + ..._markers, + ], + ), + PolylineLayerOptions( + polylines: [ + Polyline( + points: _polyline, + color: Color.fromRGBO(9, 89, 95, 1), // Set the line's color + strokeWidth: 5.0, // Set the line's width + ), + ], + ) + ], + ), SafeArea( child: Column( children: [ diff --git a/lib/widgets/directions.dart b/lib/widgets/directions.dart index 6ad96b6..2e66c6f 100644 --- a/lib/widgets/directions.dart +++ b/lib/widgets/directions.dart @@ -1,48 +1,48 @@ -// ignore_for_file: import_of_legacy_library_into_null_safe +// // ignore_for_file: import_of_legacy_library_into_null_safe -import 'package:flutter_polyline_points/flutter_polyline_points.dart'; -import 'package:google_maps_flutter/google_maps_flutter.dart'; +// import 'package:flutter_polyline_points/flutter_polyline_points.dart'; +// import 'package:google_maps_flutter/google_maps_flutter.dart'; -class Directions { - final LatLngBounds bounds; - final List<PointLatLng> polylinePoints; - final String totalDistance; - final String totalDuration; +// class Directions { +// final LatLngBounds bounds; +// final List<PointLatLng> polylinePoints; +// final String totalDistance; +// final String totalDuration; - const Directions({ - required this.bounds, - required this.polylinePoints, - required this.totalDistance, - required this.totalDuration, - }); +// const Directions({ +// required this.bounds, +// required this.polylinePoints, +// required this.totalDistance, +// required this.totalDuration, +// }); - factory Directions.fromMap(Map<String, dynamic> map) { - // Get route information - final data = Map<String, dynamic>.from(map['routes'][0]); +// factory Directions.fromMap(Map<String, dynamic> map) { +// // Get route information +// final data = Map<String, dynamic>.from(map['routes'][0]); - // Bounds - final northeast = data['bounds']['northeast']; - final southwest = data['bounds']['southwest']; - final bounds = LatLngBounds( - northeast: LatLng(northeast['lat'], northeast['lng']), - southwest: LatLng(southwest['lat'], southwest['lng']), - ); +// // Bounds +// final northeast = data['bounds']['northeast']; +// final southwest = data['bounds']['southwest']; +// final bounds = LatLngBounds( +// northeast: LatLng(northeast['lat'], northeast['lng']), +// southwest: LatLng(southwest['lat'], southwest['lng']), +// ); - // Distance and Duration - String distance = ''; - String duration = ''; - if ((data['legs'] as List).isNotEmpty) { - final leg = data['legs'][0]; - distance = leg['distance']['text']; - duration = leg['duration']['text']; - } +// // Distance and Duration +// String distance = ''; +// String duration = ''; +// if ((data['legs'] as List).isNotEmpty) { +// final leg = data['legs'][0]; +// distance = leg['distance']['text']; +// duration = leg['duration']['text']; +// } - return Directions( - bounds: bounds, - polylinePoints: - PolylinePoints().decodePolyline(data['overview_polyline']['points']), - totalDistance: distance, - totalDuration: duration, - ); - } -} +// return Directions( +// bounds: bounds, +// polylinePoints: +// PolylinePoints().decodePolyline(data['overview_polyline']['points']), +// totalDistance: distance, +// totalDuration: duration, +// ); +// } +// } diff --git a/lib/widgets/directionsRepo.dart b/lib/widgets/directionsRepo.dart index b9ec6bf..feb6c53 100644 --- a/lib/widgets/directionsRepo.dart +++ b/lib/widgets/directionsRepo.dart @@ -1,35 +1,28 @@ // ignore_for_file: file_names -import 'package:dio/dio.dart'; -import 'package:google_maps_flutter/google_maps_flutter.dart'; -import './directions.dart'; -import '../config/config.dart'; +// // import 'package:google_maps_flutter/google_maps_flutter.dart'; +// import '../config/config.dart'; +// import 'package:latlong/latlong.dart' as latlng; +// import 'package:http/http.dart' as http; -class DirectionsRepo { - Config config = Config(); - static const String _baseUrl = - 'https://maps.googleapis.com/maps/api/directions/json?'; - final Dio _dio; - DirectionsRepo({Dio? dio}) : _dio = dio ?? Dio(); +// class DirectionsRepo { +// Config config = Config(); +// latlng.LatLng origin; +// latlng.LatLng destination; +// DirectionsRepo(this.origin, this.destination); - Future<Directions> getDirections({ - required LatLng origin, - required LatLng destination, - }) async { - final response = await _dio.get( - _baseUrl, - queryParameters: { - 'origin': '${origin.latitude},${origin.longitude}', - 'destination': '${destination.latitude},${destination.longitude}', - 'key': config.GOOGLE_API, - }, - ); +// Future<http.Response> getDirections() async { +// var originlat = origin.latitude; +// var originlon = origin.longitude; +// var destlat = destination.latitude; +// var destlon = destination.longitude; +// final response = await http.get( +// Uri.parse( +// 'https://api.openrouteservice.org/v2/directions/driving-car?api_key=${config.OSM_API}&start=$originlon,$originlat&end=$destlon,$destlat', +// ), +// ); - // Check if response is successful - if (response.statusCode == 200) { - return Directions.fromMap(response.data); - } else { - throw Error(); - } - } -} +// // Check if response is successful +// return response; +// } +// } -- GitLab