Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce route service #6

Open
wants to merge 89 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
ec61e1b
catch exceptions while parsing and fix poliline on mobile
JulianBissekkou Nov 26, 2023
8bcefcd
add missing return
JulianBissekkou Nov 27, 2023
f1187f3
initial draft on iOS
JulianBissekkou Nov 26, 2023
94cc4a9
add symbol methods to controller
JulianBissekkou Nov 27, 2023
e66d60f
parse symbol
JulianBissekkou Nov 27, 2023
a5c6a29
implement useCourseSymbolOnMovement
JulianBissekkou Nov 27, 2023
c0a6e9b
implement new type of location display data source
JulianBissekkou Nov 27, 2023
d10fe1c
implement feature
sbergmair Nov 27, 2023
5808386
implement feature
sbergmair Nov 27, 2023
de8bb70
add example on button click
sbergmair Nov 27, 2023
f6f2fd5
add support for local asset android
sbergmair Nov 28, 2023
694d29d
add support for local asset android
sbergmair Nov 28, 2023
e2b224c
fix logic
sbergmair Nov 28, 2023
afb3f70
implement iOS
sbergmair Nov 28, 2023
91d8f1c
reformat
sbergmair Nov 28, 2023
2313fb2
review
sbergmair Nov 28, 2023
9674db1
Merge branch 'move_camera_and_zoom_to_provided_positions' into add_lo…
sbergmair Nov 28, 2023
80c13fe
update README.md and add button for example only on mobile
sbergmair Nov 28, 2023
5d8f9e5
Merge branch 'move_camera_and_zoom_to_provided_positions' into add_lo…
sbergmair Nov 28, 2023
df29f97
finish iOS location indicator impl
JulianBissekkou Nov 28, 2023
b820ef7
Merge branch 'zoom_camera_and_add_local_asset_support' into all_featu…
sbergmair Nov 29, 2023
65ac0e5
export
sbergmair Nov 29, 2023
e9d653f
Merge branch 'controller_reload_mapstatus_callback' into all_features…
sbergmair Nov 29, 2023
1c1da38
Merge branch 'controller_reload_mapstatus_callback' into all_features…
sbergmair Nov 29, 2023
eca8c59
implement android
JulianBissekkou Nov 29, 2023
7d164d9
Merge remote-tracking branch 'upstream/53-support-basic-current-locat…
sbergmair Nov 29, 2023
9fefbbf
merge indicator
sbergmair Nov 29, 2023
b75883a
fix android
sbergmair Nov 29, 2023
89fab34
try to fix wrong initial scale
sbergmair Nov 29, 2023
0ee9e3e
implement licenseKey and make apiKey optional
JulianBissekkou Nov 29, 2023
5ca9ec6
print statement
sbergmair Nov 29, 2023
3db2250
Merge remote-tracking branch 'upstream/62-add-option-to-set-license-k…
sbergmair Nov 29, 2023
1f42221
try to fix zoom
sbergmair Nov 29, 2023
cdd1562
fix zoom gets not initialized correctly
sbergmair Nov 30, 2023
9f2d4aa
remove print
sbergmair Nov 30, 2023
d872d47
fix android error parsing symbol and iOS zoom
sbergmair Nov 30, 2023
36d8a3a
fix bug for manual location source on android
JulianBissekkou Nov 30, 2023
a1aacd9
Merge remote-tracking branch 'upstream/53-support-basic-current-locat…
sbergmair Nov 30, 2023
a5cafed
revert changes
sbergmair Nov 30, 2023
4f16097
typo
sbergmair Nov 30, 2023
a8459fc
fix NaN android getMapScale
sbergmair Nov 30, 2023
4fd2dde
fix
sbergmair Nov 30, 2023
cce9a4f
correct round
sbergmair Nov 30, 2023
617c89b
remove web sdk
JulianBissekkou Dec 6, 2023
9b11dc0
Merge branch 'main' into all_features_iteration_4-5
sbergmair Feb 26, 2024
2770701
format
sbergmair Feb 26, 2024
1860923
fix merge
sbergmair Feb 26, 2024
5e87aa4
fix ios build
JulianBissekkou Feb 27, 2024
927dc6c
remove duplicate declaration
sbergmair Feb 27, 2024
77f82af
Merge remote-tracking branch 'origin/all_features_iteration_4-5' into…
sbergmair Feb 27, 2024
3e6dc57
Merge branch 'main' into all_features_iteration_4-5
sbergmair Apr 22, 2024
3836074
remove newline
sbergmair Apr 22, 2024
de35692
Add isAttributionTextVisible to hide map attribution
JeanTapped May 24, 2024
92fc24b
Add isAttributionTextVisible for iOS
JeanTapped May 27, 2024
ae8f01a
Add dependency_overrides
JeanTapped May 27, 2024
e941111
implement changing attribution text
JulianBissekkou May 30, 2024
89df531
Merge branch 'hide_map_attribution' into all_features_iteration_4-5
JulianBissekkou May 30, 2024
5050ba4
implement image-export
JulianBissekkou Jul 29, 2024
0dad33b
add layer status observation
JulianBissekkou Aug 12, 2024
77b4add
remove map status observeration
JulianBissekkou Aug 12, 2024
fb2efba
implement image export on android
JulianBissekkou Aug 12, 2024
58f038b
update readme
JulianBissekkou Aug 12, 2024
1c5b993
update docs
JulianBissekkou Aug 12, 2024
fd90f3d
Merge branch 'image-export' into all_features_iteration_4-5
JulianBissekkou Aug 12, 2024
f73dcb4
Merge branch 'refs/heads/main' into all_features_iteration_4-5
sbergmair Aug 22, 2024
aba1f5d
Merge branch 'main' into all_features_iteration_4-5
JulianBissekkou Aug 22, 2024
443417a
fix duplicate code
JulianBissekkou Aug 22, 2024
a50622b
specify android namespace
JulianBissekkou Oct 15, 2024
9f5f2c4
Merge branch 'specify-android-namespace' into all_features_iteration_4-5
JulianBissekkou Oct 15, 2024
2fce27b
Add routeServiceURL to ArcgisMapOptions
JeanTapped Dec 16, 2024
37d6a31
Use routeServiceURL within ArcgisMap
JeanTapped Dec 16, 2024
1ef296f
Add new RoutePoint definition
JeanTapped Dec 16, 2024
b7dc4ff
Add method channel for determineClosestPOI and determineRouteFromTo
JeanTapped Dec 16, 2024
87418f4
Add missing routeServiceUrl to ArcgisMapOptionsJsonExtension
JeanTapped Dec 16, 2024
e3fb94e
Commit updated pubspec.lock
JeanTapped Dec 16, 2024
fbea83d
Add routeServiceUrl to mapOptions
JeanTapped Dec 16, 2024
99b43b1
Add onDetermineClosestPOI and onDetermineRouteFromTo to ArcgisMapView
JeanTapped Dec 16, 2024
f6fb78a
Fix app crash due to onDetermineClosestPOI
JeanTapped Dec 16, 2024
dd37496
Add route task buttons
JeanTapped Dec 16, 2024
e9a0c87
Update ArcgisMapView onCalculateRoute
JeanTapped Jan 5, 2025
346c004
Update dart code to execute onCalculateRoute
JeanTapped Jan 5, 2025
1f299df
Add new route service example page
JeanTapped Jan 5, 2025
f462f9b
Remove obsolete TODO
JeanTapped Jan 5, 2025
33c490f
Improve route service example
JeanTapped Jan 5, 2025
93af460
Fix code format
JeanTapped Jan 6, 2025
8df5eb3
Introduce RouteManeuver and update route service example
JeanTapped Jan 7, 2025
84eb30d
Merge remote-tracking branch 'origin/all_features_iteration_4-5' into…
JeanTapped Jan 7, 2025
dbd03cb
Remove duplicate code in build.gradle after merge
JeanTapped Jan 7, 2025
a9b5ed3
Add TODO for renaming
JeanTapped Jan 15, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions arcgis_map_sdk/lib/src/arcgis_map_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -331,4 +331,22 @@ class ArcgisMapController {
return ArcgisMapPlatform.instance
.updateIsAttributionTextVisible(mapId, isAttributionTextVisible);
}

Future<void> determineClosestPOIs({
required LatLng from,
required List<PointOfInterest> pointsOfInterest,
}) async {
return ArcgisMapPlatform.instance.determineClosestPOIs(
mapId,
from,
pointsOfInterest,
);
}

Future<RouteResult> calculateRoute({
required LatLng from,
required LatLng to,
}) async {
return ArcgisMapPlatform.instance.calculateRoute(mapId, from, to);
}
}
14 changes: 14 additions & 0 deletions arcgis_map_sdk/lib/src/arcgis_map_sdk.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ class ArcgisMap extends StatefulWidget {
this.onMapCreated,
this.vectorTileLayerUrls,
this.isAttributionTextVisible,
this.routeServiceUrl,
this.closestFacilitiesUrl,
super.key,
}) : assert(
basemap != null ||
Expand Down Expand Up @@ -78,6 +80,14 @@ class ArcgisMap extends StatefulWidget {
/// is ignored.
final List<String>? vectorTileLayerUrls;

/// Allows the map to execute different routing tasks by providing a valid
/// ArcGIS route service URL.
final String? routeServiceUrl;

/// Allows the map to execute closest facility tasks by providing a valid
/// ArcGIS route service URL.
final String? closestFacilitiesUrl;

final void Function(ArcgisMapController controller)? onMapCreated;

@override
Expand Down Expand Up @@ -112,6 +122,8 @@ class _ArcgisMapState extends State<ArcgisMap> {
yMin: widget.yMin,
yMax: widget.yMax,
vectorTilesUrls: widget.vectorTileLayerUrls,
routeServiceUrl: widget.routeServiceUrl,
closestFacilitiesUrl: widget.closestFacilitiesUrl,
isAttributionTextVisible: widget.isAttributionTextVisible,
);

Expand Down Expand Up @@ -169,6 +181,8 @@ class _ArcgisMapState extends State<ArcgisMap> {
yMin: widget.yMin,
yMax: widget.yMax,
vectorTilesUrls: widget.vectorTileLayerUrls,
routeServiceUrl: widget.routeServiceUrl,
closestFacilitiesUrl: widget.closestFacilitiesUrl,
defaultUiList: widget.defaultUiList,
isAttributionTextVisible: widget.isAttributionTextVisible,
);
Expand Down
4 changes: 3 additions & 1 deletion arcgis_map_sdk/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ dependencies:
arcgis_map_sdk_android: ^0.8.0
arcgis_map_sdk_ios: ^0.8.0
arcgis_map_sdk_platform_interface: ^1.0.1
arcgis_map_sdk_web: ^0.8.0
# todo: this was removed to make sure we don't end up with web artifacts in our mobile build
# this is only a temporary solution.
#arcgis_map_sdk_web: ^0.8.0
flutter:
sdk: flutter

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,25 @@ import com.esri.arcgisruntime.mapping.view.Graphic
import com.esri.arcgisruntime.mapping.view.GraphicsOverlay
import com.esri.arcgisruntime.mapping.view.LocationDisplay.AutoPanMode
import com.esri.arcgisruntime.mapping.view.MapView
import com.esri.arcgisruntime.symbology.SimpleLineSymbol
import com.esri.arcgisruntime.symbology.SimpleLineSymbol.Style
import com.esri.arcgisruntime.symbology.Symbol
import com.esri.arcgisruntime.tasks.networkanalysis.ClosestFacilityTask
import com.esri.arcgisruntime.tasks.networkanalysis.Facility
import com.esri.arcgisruntime.tasks.networkanalysis.Incident
import com.esri.arcgisruntime.tasks.networkanalysis.Route
import com.esri.arcgisruntime.tasks.networkanalysis.RouteTask
import com.esri.arcgisruntime.tasks.networkanalysis.Stop
import com.google.gson.reflect.TypeToken
import dev.fluttercommunity.arcgis_map_sdk_android.model.AnimationOptions
import dev.fluttercommunity.arcgis_map_sdk_android.model.ArcgisMapOptions
import dev.fluttercommunity.arcgis_map_sdk_android.model.LatLng
import dev.fluttercommunity.arcgis_map_sdk_android.model.RouteResult
import dev.fluttercommunity.arcgis_map_sdk_android.model.UserPosition
import dev.fluttercommunity.arcgis_map_sdk_android.model.ViewPadding
import dev.fluttercommunity.arcgis_map_sdk_android.model.toAGSPoint
import dev.fluttercommunity.arcgis_map_sdk_android.util.GraphicsParser
import io.flutter.Log
import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodCall
Expand Down Expand Up @@ -101,7 +112,7 @@ internal class ArcgisMapView(
mapView.graphicsOverlays.add(defaultGraphicsOverlay)

mapView.addMapScaleChangedListener {
if(mapView.mapScale.isNaN()) return@addMapScaleChangedListener
if (mapView.mapScale.isNaN()) return@addMapScaleChangedListener

val zoomLevel = getZoomLevel(mapView)
zoomStreamHandler.addZoom(zoomLevel)
Expand All @@ -115,7 +126,7 @@ internal class ArcgisMapView(
val wgs84Center = GeometryEngine.project(center, SpatialReferences.getWgs84()) as? Point

val latLng = wgs84Center?.let {
LatLng(longitude = it.x,latitude = it.y)
LatLng(longitude = it.x, latitude = it.y)
} ?: return@addViewpointChangedListener

centerPositionStreamHandler.add(latLng)
Expand Down Expand Up @@ -201,6 +212,10 @@ internal class ArcgisMapView(

"export_image" -> onExportImage(result)

"determine_closest_POIs" -> onDetermineClosestPOIs(call = call, result = result)

"calculate_route" -> onCalculateRoute(call = call, result = result)

else -> result.notImplemented()
}
}
Expand Down Expand Up @@ -617,6 +632,198 @@ internal class ArcgisMapView(

}

private fun onDetermineClosestPOIs(call: MethodCall, result: MethodChannel.Result) {
// Ensure this is never called without a closest facilities service url.
if (mapOptions.closestFacilitiesUrl == null) {
result.finishWithError(Exception("No closest facilities url provided, check map options"))
}

try {
val arguments = call.arguments as Map<String, Any>

Log.w("- Closest POI -", "Received Arguments: $arguments")

val relevantPOIs =
(arguments["pointsOfInterest"] as ArrayList<Map<String, Any>>).map { poi ->
Facility(
Point(
poi["longitude"] as Double,
poi["latitude"] as Double,
SpatialReferences.getWebMercator(),
),
).apply {
name = poi["identifier"] as String
}
}
val from = arguments["from"] as Map<String, Any>
val currentLatLng = LatLng(
from["longitude"] as Double,
from["latitude"] as Double
)

Log.w(
"- Closest POI -",
"Extracted Arguments: $currentLatLng to ${relevantPOIs.size} POIs"
)

// Create a task to be able to determine the required information.
val closestFacilityTask = ClosestFacilityTask(context, mapOptions.routeServiceUrl)

Log.w("- Closest POI -", "Created Task: $closestFacilityTask")

val parameterFuture = closestFacilityTask.createDefaultParametersAsync()
parameterFuture.addDoneListener {
try {
if (parameterFuture.isDone) {
Log.w("- Closest POI -", "Future is done")

val closestFacilityParameters = parameterFuture.get()

Log.w("- Closest POI -", "Created Parameter: $closestFacilityParameters")

// Apply the current coordinates and POIs to the parameters.
closestFacilityParameters.apply {
setIncidents(listOf(Incident(currentLatLng.toAGSPoint())))
setFacilities(relevantPOIs)
}

Log.w("- Closest POI -", "Updated Parameters: $closestFacilityParameters")

val closestFacilitiesFuture =
closestFacilityTask.solveClosestFacilityAsync(closestFacilityParameters)
closestFacilitiesFuture.addDoneListener {
try {
Log.w("- Closest POI -", "Future is done")

val closestFacilityResult = closestFacilitiesFuture.get()

Log.w("- Closest POI -", "Solved Route: $closestFacilityResult")

val rankedFacilities =
closestFacilityResult.getRankedFacilityIndexes(0)

Log.w("- Closest POI -", "Facilities: $rankedFacilities")

// If the result contains a facility, solve the route from incident to facility.
if (rankedFacilities.isNotEmpty()) {
val closestFacilityIndex = rankedFacilities[0]
val route = closestFacilityResult.getRoute(
closestFacilityIndex,
0,
)
val routeSymbol = SimpleLineSymbol(
Style.SOLID,
0xFF0000FF.toInt(),
2.0f
)
val routeGraphic = Graphic(
route?.routeGeometry,
routeSymbol,
)

defaultGraphicsOverlay.graphics.add(routeGraphic)
Log.w("- Closest POI -", "Updated defaultGraphicsOverlay")
}
} catch (e: Exception) {
Log.w(
"- Closest POI -",
"Error solving closest facilities: ${e.message}",
)
result.finishWithError(e)
}
}
} else {
Log.w("- Closest POI -", "Failed to load route parameters")
result.finishWithError(Exception("Failed to load route parameters"))
}
} catch (e: Exception) {
Log.w("- Closest POI -", "Error creating route parameters: ${e.message}")
result.finishWithError(e)
}
}
} catch (e: Exception) {
Log.w("- Closest POI -", "Failed to determine closest POI: $e")
result.finishWithError(e)
}
}

private fun onCalculateRoute(call: MethodCall, result: MethodChannel.Result) {
// Ensure this is never called without a route service url.
if (mapOptions.routeServiceUrl == null) {
result.finishWithError(Exception("No route service url provided, check map options"))
}

try {
val arguments = call.arguments as Map<String, Any>

val from = arguments["from"] as Map<String, Any>
val fromCoordinates = from["coordinates"] as List<Double>
val fromLatLng = LatLng(fromCoordinates[0], fromCoordinates[1])

val to = arguments["to"] as Map<String, Any>
val toCoordinates = to["coordinates"] as List<Double>
val toLatLng = LatLng(toCoordinates[0], toCoordinates[1])

val routeTask = RouteTask(context, mapOptions.routeServiceUrl)

val parameterFuture = routeTask.createDefaultParametersAsync()
parameterFuture.addDoneListener {
try {
if (parameterFuture.isDone) {
val routeParams = parameterFuture.get()

val stops = arrayListOf(
Stop(
Point(
fromLatLng.longitude,
fromLatLng.latitude,
SpatialReferences.getWgs84()
)
),
Stop(
Point(
toLatLng.longitude,
toLatLng.latitude,
SpatialReferences.getWgs84()
)
)
)

routeParams.apply {
// Apply the From and To coordinates to the parameters.
setStops(stops)
// Set return directions true to return turn-by-turn
// directions in the route's directionManeuvers.
isReturnDirections = true
}

val routeFuture = routeTask.solveRouteAsync(routeParams)
routeFuture.addDoneListener {
try {
val taskResult = routeFuture.get()
val route = taskResult.routes[0] as Route

// Convert the route into a suitable route result.
val routeResult = RouteResult.fromRoute(route)

result.success(routeResult.toJson())

} catch (e: Exception) {
result.finishWithError(e)
}
}
} else {
result.finishWithError(Exception("Failed to load route parameters"))
}
} catch (e: Exception) {
result.finishWithError(e)
}
}
} catch (e: Exception) {
result.finishWithError(e)
}
}

/**
* Convert map scale to zoom level
* https://developers.arcgis.com/documentation/mapping-apis-and-services/reference/zoom-levels-and-scale/#conversion-tool
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ data class ArcgisMapOptions(
val licenseKey: String?,
val basemap: BasemapStyle?,
val vectorTilesUrls: List<String>,
val routeServiceUrl: String?,
val closestFacilitiesUrl: String?,
val initialCenter: LatLng,
val isInteractive: Boolean,
val zoom: Double,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package dev.fluttercommunity.arcgis_map_sdk_android.model

import com.esri.arcgisruntime.geometry.Point
import com.esri.arcgisruntime.geometry.Polyline
import com.esri.arcgisruntime.tasks.networkanalysis.DirectionManeuver

class RouteManeuver private constructor(
val paths: List<List<LatLng>>,
val directionMessage: String,
val durationMinutes: Double,
) {
companion object {
fun fromDirectionManeuver(maneuver: DirectionManeuver): RouteManeuver {
val directionMessage = maneuver.directionText
val estimatedTimeMinutes = maneuver.duration

return when (maneuver.geometry) {
is Point -> {
// The first and last entry are the start and end Point.
val pointGeometry = (maneuver.geometry as Point)
RouteManeuver(
paths = listOf(
listOf(LatLng(pointGeometry.x, pointGeometry.y)),
),
directionMessage = directionMessage,
durationMinutes = estimatedTimeMinutes,
)
}

is Polyline -> {
// The geometries between the start and end point.
val path = (maneuver.geometry as Polyline).parts.partsAsPoints.map {
listOf(LatLng(it.x, it.y))
}
RouteManeuver(
paths = path,
directionMessage = directionMessage,
durationMinutes = estimatedTimeMinutes,
)
}

else -> throw IllegalArgumentException(
"Unsupported geometry type: ${maneuver.geometry?.javaClass?.name}"
)
}
}
}
}
Loading