From df29f97397fba3defe71dfe53b42cc795c176cfc Mon Sep 17 00:00:00 2001 From: Julian Bissekkou Date: Tue, 28 Nov 2023 16:49:29 +0100 Subject: [PATCH] finish iOS location indicator impl --- .../lib/src/arcgis_location_display.dart | 42 ++++++++---- .../lib/src/arcgis_map_controller.dart | 9 ++- .../ios/Classes/ArcgisMapView.swift | 3 +- .../Classes/ManualLocationDataSource.swift | 10 +-- .../ios/Classes/Models/UserPosition.swift | 1 + .../lib/src/model_extension.dart | 1 + .../lib/src/models/user_position.dart | 4 +- .../lib/location_indicator_example_page.dart | 64 +++++++++++++++++++ 8 files changed, 114 insertions(+), 20 deletions(-) diff --git a/arcgis_map_sdk/lib/src/arcgis_location_display.dart b/arcgis_map_sdk/lib/src/arcgis_location_display.dart index 1fdc629b..de57a222 100644 --- a/arcgis_map_sdk/lib/src/arcgis_location_display.dart +++ b/arcgis_map_sdk/lib/src/arcgis_location_display.dart @@ -1,55 +1,75 @@ import 'package:arcgis_map_sdk_platform_interface/arcgis_map_sdk_platform_interface.dart'; +import 'package:flutter/cupertino.dart'; class ArcgisManualLocationDisplay extends ArcgisLocationDisplay { @override String get type => "manual"; - ArcgisManualLocationDisplay(super.mapId); + ArcgisManualLocationDisplay({super.mapId}); Future updateLocation(UserPosition position) { + _assertAttached(); return ArcgisMapPlatform.instance .updateLocationDisplaySourcePositionManually( - mapId, + _mapId!, position, ); } } class ArcgisLocationDisplay { - final int mapId; + int? _mapId; final String type = "system"; - ArcgisLocationDisplay(this.mapId); + ArcgisLocationDisplay({int? mapId}) : _mapId = mapId; + + void attachToMap(int mapId) => _mapId = mapId; + + void deattachFromMap() => _mapId = null; Future startSource() { - return ArcgisMapPlatform.instance.startLocationDisplayDataSource(mapId); + _assertAttached(); + return ArcgisMapPlatform.instance.startLocationDisplayDataSource(_mapId!); } Future stopSource() { - return ArcgisMapPlatform.instance.stopLocationDisplayDataSource(mapId); + _assertAttached(); + return ArcgisMapPlatform.instance.stopLocationDisplayDataSource(_mapId!); } Future setDataSource() { - return ArcgisMapPlatform.instance.setLocationDisplayDataSource(mapId); + _assertAttached(); + return ArcgisMapPlatform.instance.setLocationDisplayDataSource(_mapId!); } Future setDefaultSymbol(Symbol symbol) { + _assertAttached(); return ArcgisMapPlatform.instance - .setLocationDisplayDefaultSymbol(mapId, symbol); + .setLocationDisplayDefaultSymbol(_mapId!, symbol); } Future setAccuracySymbol(Symbol symbol) { + _assertAttached(); return ArcgisMapPlatform.instance - .setLocationDisplayAccuracySymbol(mapId, symbol); + .setLocationDisplayAccuracySymbol(_mapId!, symbol); } Future setPingAnimationSymbol(Symbol symbol) { + _assertAttached(); return ArcgisMapPlatform.instance - .setLocationDisplayPingAnimationSymbol(mapId, symbol); + .setLocationDisplayPingAnimationSymbol(_mapId!, symbol); } Future setUseCourseSymbolOnMovement(bool useCourseSymbol) { + _assertAttached(); return ArcgisMapPlatform.instance - .setUseCourseSymbolOnMovement(mapId, useCourseSymbol); + .setUseCourseSymbolOnMovement(_mapId!, useCourseSymbol); + } + + void _assertAttached() { + assert( + _mapId != null, + "LocationDisplay has not been attached to any map. Make sure to call ArcgisMapController.setLocationDisplay.", + ); } } diff --git a/arcgis_map_sdk/lib/src/arcgis_map_controller.dart b/arcgis_map_sdk/lib/src/arcgis_map_controller.dart index 39635463..117019fe 100644 --- a/arcgis_map_sdk/lib/src/arcgis_map_controller.dart +++ b/arcgis_map_sdk/lib/src/arcgis_map_controller.dart @@ -5,7 +5,7 @@ import 'package:flutter/services.dart'; class ArcgisMapController { ArcgisMapController._({ required this.mapId, - }) : _locationDisplay = ArcgisLocationDisplay(mapId); + }) : _locationDisplay = ArcgisLocationDisplay(mapId: mapId); final int mapId; @@ -258,6 +258,11 @@ class ArcgisMapController { Future setLocationDisplay(ArcgisLocationDisplay locationDisplay) { return ArcgisMapPlatform.instance .setLocationDisplay(mapId, locationDisplay.type) - .whenComplete(() => _locationDisplay = locationDisplay); + .whenComplete( + () { + _locationDisplay.deattachFromMap(); + _locationDisplay = locationDisplay..attachToMap(mapId); + }, + ); } } diff --git a/arcgis_map_sdk_ios/ios/Classes/ArcgisMapView.swift b/arcgis_map_sdk_ios/ios/Classes/ArcgisMapView.swift index dc53f9bb..5944fdb8 100644 --- a/arcgis_map_sdk_ios/ios/Classes/ArcgisMapView.swift +++ b/arcgis_map_sdk_ios/ios/Classes/ArcgisMapView.swift @@ -357,7 +357,8 @@ class ArcgisMapView: NSObject, FlutterPlatformView { return } - source.setNewLocation(coordinate: position.latLng, accuracy: position.accuracy, course: position.heading) + source.setNewLocation(position) + result(true) } private func onSetLocationDisplayDataSourceType(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { diff --git a/arcgis_map_sdk_ios/ios/Classes/ManualLocationDataSource.swift b/arcgis_map_sdk_ios/ios/Classes/ManualLocationDataSource.swift index 6ddbee41..0d266a53 100644 --- a/arcgis_map_sdk_ios/ios/Classes/ManualLocationDataSource.swift +++ b/arcgis_map_sdk_ios/ios/Classes/ManualLocationDataSource.swift @@ -9,12 +9,12 @@ import Foundation import ArcGIS class ManualLocationDataSource: AGSLocationDataSource { - public func setNewLocation(coordinate: LatLng, accuracy: Double?, course: Double?) { + public func setNewLocation(_ position: UserPosition) { let loc = AGSLocation( - position: coordinate.toAGSPoint(), - horizontalAccuracy: accuracy ?? 0, - velocity: 0, - course: course ?? 0, + position: position.latLng.toAGSPoint(), + horizontalAccuracy: position.accuracy ?? 0, + velocity: position.velocity ?? 0, + course: position.heading ?? 0, lastKnown: false ) didUpdate(loc) diff --git a/arcgis_map_sdk_ios/ios/Classes/Models/UserPosition.swift b/arcgis_map_sdk_ios/ios/Classes/Models/UserPosition.swift index aeac0bfd..bbf2e5bf 100644 --- a/arcgis_map_sdk_ios/ios/Classes/Models/UserPosition.swift +++ b/arcgis_map_sdk_ios/ios/Classes/Models/UserPosition.swift @@ -11,4 +11,5 @@ struct UserPosition: Codable { let latLng: LatLng let accuracy: Double? let heading: Double? + let velocity: Double? } diff --git a/arcgis_map_sdk_method_channel/lib/src/model_extension.dart b/arcgis_map_sdk_method_channel/lib/src/model_extension.dart index 8fca1914..4b98639d 100644 --- a/arcgis_map_sdk_method_channel/lib/src/model_extension.dart +++ b/arcgis_map_sdk_method_channel/lib/src/model_extension.dart @@ -25,6 +25,7 @@ extension UserPositionExtension on UserPosition { 'latLng': latLng.toMap(), 'accuracy': accuracy, 'heading': heading, + 'velocity': velocity, }; } diff --git a/arcgis_map_sdk_platform_interface/lib/src/models/user_position.dart b/arcgis_map_sdk_platform_interface/lib/src/models/user_position.dart index 4c21b88b..3d666b17 100644 --- a/arcgis_map_sdk_platform_interface/lib/src/models/user_position.dart +++ b/arcgis_map_sdk_platform_interface/lib/src/models/user_position.dart @@ -4,10 +4,12 @@ class UserPosition { final LatLng latLng; final double? accuracy; final double? heading; + final double? velocity; const UserPosition({ required this.latLng, - required this.accuracy, + this.accuracy, + this.velocity, this.heading, }); } diff --git a/example/lib/location_indicator_example_page.dart b/example/lib/location_indicator_example_page.dart index 60f4f70a..c9a0df25 100644 --- a/example/lib/location_indicator_example_page.dart +++ b/example/lib/location_indicator_example_page.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:arcgis_example/main.dart'; import 'package:arcgis_map_sdk/arcgis_map_sdk.dart'; import 'package:flutter/material.dart'; @@ -13,10 +15,21 @@ class LocationIndicatorExamplePage extends StatefulWidget { class _LocationIndicatorExamplePageState extends State { + final _mockLocations = [ + LatLng(48.1234963, 11.5910182), + LatLng(48.1239241, 11.45897063), + LatLng(48.123876, 11.590120), + LatLng(48.123876, 11.590120), + LatLng(48.123740, 11.589015), + LatLng(48.123164, 11.588585), + LatLng(48.1234963, 11.5910182), + ]; + final _snackBarKey = GlobalKey(); ArcgisMapController? _controller; bool _isStarted = false; bool _useCourseSymbolForMovement = false; + bool _isManualLocationSource = false; @override Widget build(BuildContext context) { @@ -59,6 +72,20 @@ class _LocationIndicatorExamplePageState ), ), const SizedBox(height: 16), + ElevatedButton( + onPressed: _switchLocationSource, + child: Text( + _isManualLocationSource + ? "Use auto location source" + : "Use manual location source", + ), + ), + if (_isManualLocationSource) ...[ + ElevatedButton( + onPressed: _simulateLocationChange, + child: Text("simulate location change"), + ), + ], ElevatedButton( onPressed: () => _configureLocationDisplay(Colors.green), child: Text("tint indicator green"), @@ -120,4 +147,41 @@ class _LocationIndicatorExamplePageState SimpleLineSymbol(color: color.shade800, width: 3), ); } + + Future _switchLocationSource() async { + await _controller!.locationDisplay.stopSource(); + await _controller!.setLocationDisplay( + _isManualLocationSource + ? ArcgisLocationDisplay() + : ArcgisManualLocationDisplay(), + ); + setState(() => _isManualLocationSource = !_isManualLocationSource); + + if (!_isManualLocationSource) { + final location = await Geolocator.getLastKnownPosition(); + if (location == null) return; + await _controller!.moveCamera( + point: LatLng(location.latitude, location.longitude), + ); + } + } + + Future _simulateLocationChange() async { + final display = _controller!.locationDisplay as ArcgisManualLocationDisplay; + + await _controller!.moveCamera(point: _mockLocations.first); + for (final latLng in _mockLocations) { + if (!mounted) break; + if (!_isManualLocationSource) break; + + await display.updateLocation( + UserPosition( + latLng: latLng, + accuracy: Random().nextInt(100).toDouble(), + velocity: Random().nextInt(100).toDouble(), + ), + ); + await Future.delayed(const Duration(milliseconds: 600)); + } + } }