Calculate Route¶
Setup¶
Prerequisites¶
Run the example¶
Start a terminal/command prompt and go to the calculate_route
directory,
within the flutter examples directory
Build and run the example:
Build and run¶
Note - the gem_kit
directory containing the Maps SDK for Flutter
should be in the plugins
directory of the example, e.g.
calculate_route/plugins/gem_kit
- see the environment setup guide above.
Download project dependencies:
flutter upgrade
run the following terminal commands in the project directory,
where the pubspec.yaml
file is located:
flutter clean
flutter pub get
Run the example:
flutter run
If such a question appears, select the chrome
browser; in the above example, press 2.
First, verify that the ANDROID_SDK_ROOT
environment variable
is set to the root path of your android SDK.
In android/build.gradle
add the maven {}
block as shown,
within the allprojects {} block
, for both debug and release builds,
without the line numbers, those are for reference:
1allprojects {
2 repositories {
3 google()
4 mavenCentral()
5 maven {
6 url "${rootDir}/../plugins/gem_kit/android/build"
7 }
8 }
9}
in android/app/build.gradle
within the android {}
block, in the defaultConfig {}
block,
the android SDK version minSdkVersion
must be set as shown below.
Additionally, for release builds, in android/app/build.gradle
,
within the android {}
block, add the buildTypes {}
block as shown:
1android {
2 defaultConfig {
3 applicationId "com.magiclane.gem_kit.examples.calculate_route"
4 minSdkVersion 21
5 targetSdkVersion flutter.targetSdkVersion
6 versionCode flutterVersionCode.toInteger()
7 versionName flutterVersionName
8 }
9 buildTypes {
10 release {
11 // TODO: Add your own signing config for the release build.
12 // Signing with the debug keys for now, so `flutter run --release` works.
13 minifyEnabled false
14 shrinkResources false
15 signingConfig signingConfigs.debug
16 }
17 }
18}
Then build the apk:
flutter build apk --debug
flutter build apk --release
build/app/outputs/apk/debug
or
build/app/outputs/apk/release
subdirectory,
for debug or release build respectively,
within the current project directory, which is calculate_route
in this case.app-release.apk
or app-debug.apk
adb push app-release.apk sdcard
And then click on the apk in the file browser on the device to install and run it.
In the ios/Podfile
configuration text file, at the top, set the minimum ios
platform to 13 like this:
platform :ios, '13.0'
pod install
in the [ios folder] ./ios/
flutter build ios
to build a Runner.app.flutter run
to build and run on an attached device.<path/to>/ios/Runner.xcworkspace
project in Xcode
and execute and debug from there.How it works¶
In the calculate_route
project directory, there is a text file named
pubspec.yaml
which contains project configuration and dependencies.
The most important lines from this file are shown here:
1name: calculate_route
2version: 1.0.0+1
3
4environment:
5 sdk: '>=2.19.6 <3.0.0'
6
7dependencies:
8 flutter:
9 sdk: flutter
10 gem_kit:
11 path: plugins/gem_kit
12
13# The following section is specific to Flutter packages.
14flutter:
15 uses-material-design: true
The project must have a name and version. The dependencies list the Flutter SDK, and the gem_kit, Maps for Flutter SDK.
The source code is in calculate_route/lib/main.dart
1import 'package:gem_kit/gem_kit_basic.dart';
2import 'package:gem_kit/gem_kit_map_controller.dart';
3import 'package:gem_kit/api/gem_coordinates.dart';
4import 'package:gem_kit/api/gem_landmark.dart';
5import 'package:gem_kit/api/gem_routingpreferences.dart';
6import 'package:gem_kit/api/gem_sdksettings.dart';
7import 'package:gem_kit/api/gem_types.dart';
8import 'package:gem_kit/api/gem_routingservice.dart' as gem;
9import 'package:gem_kit/widget/gem_kit_map.dart';
10import 'package:flutter/material.dart';
11
12void main() {
13 runApp(const MyApp());
14}
15
16class MyApp extends StatelessWidget {
17 const MyApp({super.key});
18
19 // This widget is the root of your application.
20 @override
21 Widget build(BuildContext context) {
22 return const MaterialApp(
23 debugShowCheckedModeBanner: false,
24 title: 'Build route example',
25 home: MyHomePage(),
26 );
27 }
28}
29
30class MyHomePage extends StatefulWidget {
31 const MyHomePage({super.key});
32
33 @override
34 State<MyHomePage> createState() => _MyHomePageState();
35}
The dart
material package is imported, as well as the gem_kit
packages for the map controller, which enables user input such as pan and zoom,
the map package which draws the map, the routingservice which computes
a route, the landmark and coordinates packages for geolocation,
and the settings package.
The map is in a widget which is the root of the application.
1class _MyHomePageState extends State<MyHomePage> {
2 late GemMapController mapController;
3 late SdkSettings _sdkSettings;
4 late gem.RoutingService _routingService;
5 List<Coordinates> waypoints = [];
6 List<gem.Route> shownRoutes = [];
7 bool haveRoutes = false;
8
9 @override
10 void initState() {
11 super.initState();
12 waypoints.add(
13 Coordinates(latitude: 48.85682120481962, longitude: 2.343751354197309));
14 waypoints.add(Coordinates(
15 latitude: 50.846442672966944, longitude: 4.345870353765759));
16 }
17
18 Future<void> onMapCreated(GemMapController controller) async {
19 mapController = controller;
20 SdkSettings.create(mapController.mapId).then((value)
21 {
22 _sdkSettings = value;
23 _sdkSettings.setAppAuthorization("YOUR_API_KEY_TOKEN");
24 });
25 _routingService = await gem.RoutingService.create(_mapController.mapId);
26 }
The map is initialized with the map controller and the settings.
Setting the API key¶
The string |
The waypoints for the departure and destination positions are hardcoded
in the void initState()
function shown above.
A tap on the purple button at the top right causes a set of routes to be computed and rendered on the map.
1// Custom method for calling calculate route and creating
2_onPressed(List<Coordinates> waypoints, BuildContext context) async {
3// Create a landmark list
4final landmarkWaypoints =
5await gem.LandmarkList.create(_mapController.mapId);
6
7// Create landmarks from coordinates and add them to the list
8for (final wp in waypoints) {
9 var landmark = await Landmark.create(_mapController.mapId);
10 await landmark.setCoordinates(
11 Coordinates(latitude: wp.latitude, longitude: wp.longitude));
12 landmarkWaypoints.push_back(landmark);
13}
14
15final routePreferences = RoutePreferences();
16
17var result = await _routingService.calculateRoute(
18 landmarkWaypoints, routePreferences, (err, routes) async {
19 if (err != GemError.success || routes == null) {
20 return;
21 } else {
22 // Get the controller's preferences
23 final mapViewPreferences = await _mapController.preferences();
24 // Get the routes from the preferences
25 final routesMap = await mapViewPreferences.routes();
26 //Get the number of routes
27 final routesSize = await routes.size();
28
29 for (int i = 0; i < routesSize; i++) {
30 final route = await routes.at(i);
31 shownRoutes.add(route);
32
33 final timeDistance = await route.getTimeDistance();
34
35 final totalDistance = convertDistance(
36 timeDistance.unrestrictedDistanceM +
37 timeDistance.restrictedDistanceM);
38
39 final totalTime = convertDuration(
40 timeDistance.unrestrictedTimeS + timeDistance.restrictedTimeS);
41 // Add labels to the routes
42 await routesMap.add(route, i == 0,
43 label: '$totalDistance \n $totalTime');
44 }
45 // Select the first route as the main one
46 final mainRoute = await routes.at(0);
47 final width = MediaQuery.of(context).size.width - 150;
48 final height = MediaQuery.of(context).size.height - 150;
49 await _mapController.centerOnRoute(mainRoute,
50 rc: RectType(
51 x: 50, y: 50, width: width.toInt(), height: height.toInt()));
52 }
53 });
54 haveRoutes = true;
55 setState(() {});
56 return result;
57}
This is the custom method to compute a route by calling:
_routingService.calculateRoute()
,
rendering the resulting set of routes on the map,
then choosing the route at index 0 (the first one),
setting it as the mainRoute
and then
centering the main route so it fits in the viewport:
_mapController.centerOnRoute()
1_removeRoutes(List<gem.Route> routes) async {
2 final prefs = await _mapController.preferences();
3 final routesMap = await prefs.routes();
4
5 for (final route in routes) {
6 routesMap.remove(route);
7 }
8 haveRoutes = false;
9 setState(() {});
10}
Function to remove the computed routes rendered on the map.
1@override
2Widget build(BuildContext context) {
3 return Scaffold(
4 body: Center(
5 child: GemMap(
6 onMapCreated: onMapCreated,
7 ),
8 ),
9 floatingActionButtonLocation: FloatingActionButtonLocation.endTop,
10 floatingActionButton: haveRoutes
11 ? FloatingActionButton(
12 backgroundColor: Colors.red,
13 onPressed: () => _removeRoutes(shownRoutes),
14 child: const Icon(Icons.cancel),
15 )
16 : FloatingActionButton(
17 backgroundColor: Colors.deepPurple[900],
18 onPressed: () => _onPressed(waypoints, context),
19 child: const Icon(Icons.directions),
20 ),
21 resizeToAvoidBottomInset: false,
22 );
23}
Button for calling the _removeRoutes
function.
A tap on the red button at the top right causes the rendered set of routes to be removed from the map.
1String convertDistance(int meters) {
2 if (meters >= 1000) {
3 double kilometers = meters / 1000;
4 return '${kilometers.toStringAsFixed(1)} km';
5 } else {
6 return '${meters.toString()} m';
7 }
8}
9
10String convertDuration(int seconds) {
11 int hours = seconds ~/ 3600; // Number of whole hours
12 int minutes = (seconds % 3600) ~/ 60; // Number of whole minutes
13 String hoursText = (hours > 0) ? '$hours h ' : ''; // Hours text
14 String minutesText = '$minutes min'; // Minutes text
15 return hoursText + minutesText;
16}
17}
Automatic conversion of distance to meters or kilometers, respectively, depending on length, and automatic conversion of time in hours and minutes.