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

Draft: 53-support-basic-current-location-indicator #54

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions arcgis_map_sdk/lib/arcgis_map_sdk.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// ignore: unnecessary_library_directive
library arcgis_map;

export 'package:arcgis_map_sdk/src/arcgis_location_display.dart';
export 'package:arcgis_map_sdk/src/arcgis_map_controller.dart';
export 'package:arcgis_map_sdk/src/arcgis_map_sdk.dart';
export 'package:arcgis_map_sdk_platform_interface/arcgis_map_sdk_platform_interface.dart';
69 changes: 69 additions & 0 deletions arcgis_map_sdk/lib/src/arcgis_location_display.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import 'package:arcgis_map_sdk_platform_interface/arcgis_map_sdk_platform_interface.dart';

class ArcgisManualLocationDisplay extends ArcgisLocationDisplay {
@override
String get type => "manual";

ArcgisManualLocationDisplay({super.mapId});

Future<void> updateLocation(UserPosition position) {
_assertAttached();
return ArcgisMapPlatform.instance
.updateLocationDisplaySourcePositionManually(
_mapId!,
position,
);
}
}

class ArcgisLocationDisplay {
int? _mapId;
final String type = "system";

ArcgisLocationDisplay({int? mapId}) : _mapId = mapId;

void attachToMap(int mapId) => _mapId = mapId;

void deattachFromMap() => _mapId = null;

Future<void> startSource() {
_assertAttached();
return ArcgisMapPlatform.instance.startLocationDisplayDataSource(_mapId!);
}

Future<void> stopSource() {
_assertAttached();
return ArcgisMapPlatform.instance.stopLocationDisplayDataSource(_mapId!);
}

Future<void> setDefaultSymbol(Symbol symbol) {
_assertAttached();
return ArcgisMapPlatform.instance
.setLocationDisplayDefaultSymbol(_mapId!, symbol);
}

Future<void> setAccuracySymbol(Symbol symbol) {
_assertAttached();
return ArcgisMapPlatform.instance
.setLocationDisplayAccuracySymbol(_mapId!, symbol);
}

Future<void> setPingAnimationSymbol(Symbol symbol) {
_assertAttached();
return ArcgisMapPlatform.instance
.setLocationDisplayPingAnimationSymbol(_mapId!, symbol);
}

Future<void> setUseCourseSymbolOnMovement(bool useCourseSymbol) {
_assertAttached();
return ArcgisMapPlatform.instance
.setUseCourseSymbolOnMovement(_mapId!, useCourseSymbol);
}

void _assertAttached() {
assert(
_mapId != null,
"LocationDisplay has not been attached to any map. Make sure to call ArcgisMapController.setLocationDisplay.",
);
}
}
18 changes: 17 additions & 1 deletion arcgis_map_sdk/lib/src/arcgis_map_controller.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import 'package:arcgis_map_sdk/src/arcgis_location_display.dart';
import 'package:arcgis_map_sdk_platform_interface/arcgis_map_sdk_platform_interface.dart';
import 'package:flutter/services.dart';

class ArcgisMapController {
ArcgisMapController._({
required this.mapId,
});
}) : _locationDisplay = ArcgisLocationDisplay(mapId: mapId);

final int mapId;

late ArcgisLocationDisplay _locationDisplay;

ArcgisLocationDisplay get locationDisplay => _locationDisplay;

static Future<ArcgisMapController> init(
int id,
) async {
Expand Down Expand Up @@ -249,4 +254,15 @@ class ArcgisMapController {
List<String> getVisibleGraphicIds() {
return ArcgisMapPlatform.instance.getVisibleGraphicIds(mapId);
}

Future<void> setLocationDisplay(ArcgisLocationDisplay locationDisplay) {
return ArcgisMapPlatform.instance
.setLocationDisplay(mapId, locationDisplay.type)
.whenComplete(
() {
_locationDisplay.deattachFromMap();
_locationDisplay = locationDisplay..attachToMap(mapId);
},
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import com.esri.arcgisruntime.geometry.GeometryEngine
import com.esri.arcgisruntime.geometry.Point
import com.esri.arcgisruntime.geometry.SpatialReferences
import com.esri.arcgisruntime.layers.ArcGISVectorTiledLayer
import com.esri.arcgisruntime.location.AndroidLocationDataSource
import com.esri.arcgisruntime.location.LocationDataSource
import com.esri.arcgisruntime.mapping.ArcGISMap
import com.esri.arcgisruntime.mapping.Basemap
import com.esri.arcgisruntime.mapping.BasemapStyle
Expand All @@ -16,10 +18,12 @@ import com.esri.arcgisruntime.mapping.view.AnimationCurve
import com.esri.arcgisruntime.mapping.view.Graphic
import com.esri.arcgisruntime.mapping.view.GraphicsOverlay
import com.esri.arcgisruntime.mapping.view.MapView
import com.esri.arcgisruntime.symbology.Symbol
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.UserPosition
import dev.fluttercommunity.arcgis_map_sdk_android.model.ViewPadding
import dev.fluttercommunity.arcgis_map_sdk_android.util.GraphicsParser
import io.flutter.plugin.common.BinaryMessenger
Expand All @@ -37,7 +41,7 @@ import kotlin.math.roundToInt
* A starting point for documentation can be found here: https://developers.arcgis.com/android/maps-2d/tutorials/display-a-map/
* */
internal class ArcgisMapView(
context: Context,
private val context: Context,
private val viewId: Int,
private val binaryMessenger: BinaryMessenger,
private val mapOptions: ArcgisMapOptions,
Expand Down Expand Up @@ -109,19 +113,172 @@ internal class ArcgisMapView(
private fun setupMethodChannel() {
methodChannel.setMethodCallHandler { call, result ->
when (call.method) {
"zoom_in" -> onZoomIn(call = call, result = result)
"zoom_out" -> onZoomOut(call = call, result = result)
"add_view_padding" -> onAddViewPadding(call = call, result = result)
"set_interaction" -> onSetInteraction(call = call, result = result)
"move_camera" -> onMoveCamera(call = call, result = result)
"add_graphic" -> onAddGraphic(call = call, result = result)
"remove_graphic" -> onRemoveGraphic(call = call, result = result)
"toggle_base_map" -> onToggleBaseMap(call = call, result = result)
"zoom_in" -> onZoomIn(call, result)
"zoom_out" -> onZoomOut(call, result)
"add_view_padding" -> onAddViewPadding(call, result)
"set_interaction" -> onSetInteraction(call, result)
"move_camera" -> onMoveCamera(call, result)
"add_graphic" -> onAddGraphic(call, result)
"remove_graphic" -> onRemoveGraphic(call, result)
"toggle_base_map" -> onToggleBaseMap(call, result)
"location_display_start_data_source" -> onStartLocationDisplayDataSource(result)
"location_display_stop_data_source" -> onStopLocationDisplayDataSource(result)
"location_display_set_default_symbol" -> onSetLocationDisplayDefaultSymbol(
call,
result
)

"location_display_set_accuracy_symbol" -> onSetLocationDisplayAccuracySymbol(
call,
result
)

"location_display_set_ping_animation_symbol" -> onSetLocationDisplayPingAnimationSymbol(
call,
result
)

"location_display_set_use_course_symbol_on_move" -> onSetLocationDisplayUseCourseSymbolOnMove(
call,
result
)

"location_display_update_display_source_position_manually" -> onUpdateLocationDisplaySourcePositionManually(
call,
result
)

"location_display_set_data_source_type" -> onSetLocationDisplayDataSourceType(
call,
result
)

else -> result.notImplemented()
}
}
}

private fun onStartLocationDisplayDataSource(result: MethodChannel.Result) {
val future = mapView.locationDisplay.locationDataSource.startAsync()
future.addDoneListener {
try {
result.success(future.get())
} catch (e: Exception) {
result.error("Error", e.message, null)
}
}
}

private fun onStopLocationDisplayDataSource(result: MethodChannel.Result) {
val future = mapView.locationDisplay.locationDataSource.stopAsync()
future.addDoneListener {
try {
result.success(future.get())
} catch (e: Exception) {
result.error("Error", e.message, null)
}
}
}

private fun onSetLocationDisplayDefaultSymbol(call: MethodCall, result: MethodChannel.Result) {
operationWithSymbol(call, result) { symbol ->
mapView.locationDisplay.defaultSymbol = symbol
}
}

private fun onSetLocationDisplayAccuracySymbol(call: MethodCall, result: MethodChannel.Result) {
operationWithSymbol(call, result) { symbol ->
mapView.locationDisplay.accuracySymbol = symbol
}
}

private fun onSetLocationDisplayPingAnimationSymbol(
call: MethodCall,
result: MethodChannel.Result
) {
operationWithSymbol(call, result) { symbol ->
mapView.locationDisplay.pingAnimationSymbol = symbol
}
}

private fun onSetLocationDisplayUseCourseSymbolOnMove(
call: MethodCall,
result: MethodChannel.Result
) {
val active = call.arguments as? Boolean
if (active == null) {
result.error("missing_data", "Invalid arguments.", null)
return
}

mapView.locationDisplay.isUseCourseSymbolOnMovement = active
result.success(true)
}

private fun onUpdateLocationDisplaySourcePositionManually(
call: MethodCall,
result: MethodChannel.Result
) {
val dataSource =
mapView.locationDisplay.locationDataSource as? ManualLocationDisplayDataSource
if (dataSource == null) {
result.error(
"invalid_state",
"Expected ManualLocationDataSource but got $dataSource",
null
)
return
}

val optionParams = call.arguments as Map<String, Any>
val position = optionParams.parseToClass<UserPosition>()

dataSource.setNewLocation(position)
result.success(true)
}

private fun onSetLocationDisplayDataSourceType(call: MethodCall, result: MethodChannel.Result) {
if (mapView.locationDisplay.locationDataSource.status == LocationDataSource.Status.STARTED) {
result.error(
"invalid_state",
"Current data source is running. Make sure to stop it before setting a new data source",
null
)
return
}

when (call.arguments as String) {
"manual" -> {
mapView.locationDisplay.locationDataSource = ManualLocationDisplayDataSource()
result.success(true)
}

"system" -> {
mapView.locationDisplay.locationDataSource = AndroidLocationDataSource(context)
result.success(true)
}

else -> result.error("invalid_data", "Unknown data source type ${call.arguments}", null)
}
}


private fun operationWithSymbol(
call: MethodCall,
result: MethodChannel.Result,
function: (Symbol) -> Unit
) {
try {
val map = call.arguments as Map<String, Any>
val symbol = GraphicsParser.parseSymbol(map)
function(symbol)
result.success(true)
} catch (e: Throwable) {
result.error("unknown_error", "Error while adding graphic. $e)", null)
return
}
}

private fun setupEventChannel() {
zoomStreamHandler = ZoomStreamHandler()
centerPositionStreamHandler = CenterPositionStreamHandler()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package dev.fluttercommunity.arcgis_map_sdk_android

import com.esri.arcgisruntime.location.LocationDataSource
import dev.fluttercommunity.arcgis_map_sdk_android.model.UserPosition
import dev.fluttercommunity.arcgis_map_sdk_android.model.toAGSPoint

class ManualLocationDisplayDataSource : LocationDataSource() {

override fun onStart() {
this.onStartCompleted(null)
}

override fun onStop() {

}

fun setNewLocation(userPosition: UserPosition) {
val loc = Location(
userPosition.latLng.toAGSPoint(),
userPosition.accuracy ?: 0.0,
userPosition.velocity ?: 0.0,
userPosition.heading ?: 0.0,
false
)
updateLocation(loc)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package dev.fluttercommunity.arcgis_map_sdk_android.model

data class UserPosition(
val latLng: LatLng,
val accuracy: Double?,
val heading: Double?,
val velocity: Double?
)
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package dev.fluttercommunity.arcgis_map_sdk_android.model

class ViewPadding(
data class ViewPadding(
val left: Double,
val top: Double,
val right: Double,
val bottom: Double,
)
)
Loading
Loading