flutter_map_location_marker icon indicating copy to clipboard operation
flutter_map_location_marker copied to clipboard

The orientation of marker heading is clockwise 90 deg in ios but correct in android

Open jesussmile opened this issue 10 months ago • 11 comments

So, i wrote the code for both ios and android , in android it works fine where as in ios the rotation of the marker is clockwise 90

Android : Screenshot_20240421_180209_Gallery

Ios: IMG_0100

## Decleration ##
  late final Stream<Position?> _geolocatorStream;
#### initState ####
 const factory = LocationMarkerDataStreamFactory();
   
    _geolocatorStream =
        factory.defaultPositionStreamSource().asBroadcastStream();
    _geolocatorStream.listen((position) {
      if (position != null) {
        setState(() {
          polyAirPlaneRoute.add(LatLng(position.latitude, position.longitude));
          //print(polyAirPlaneRoute);
          _altitude = ((position.altitude * 3.281).toStringAsFixed(0));
          //convert _altitude to nearest value of 10 for example if it is 4287 then it should be 4290
          double alt = ((position.altitude * 3.281).roundToDouble() / 10)
                  .roundToDouble() *
              10;
          Provider.of<ProviderService>(context, listen: false)
              .setAltitudeFactor(alt, context);
          // Start a periodic timer that increases the altitude
          // by 100 every second until it reaches 15000
          _speed = ((position.speed * 1.94384).toStringAsFixed(0));
          _accuracy = ("${position.accuracy.toStringAsFixed(0)} m");
          _heading = (position.heading.toStringAsFixed(0));
          _speedAccuracy = (" ${position.speedAccuracy.toStringAsFixed(1)}");
          var now = DateTime.now();
          var formatterTime = DateFormat('kk:mm:ss');
          _curTime = formatterTime.format(now);
          _timeStamp =
              ("${position.timestamp!.hour}:${position.timestamp!.minute}:${position.timestamp!.second}");
        });
      }
    });

###### Implementation #######

 IgnorePointer(
    child: CurrentLocationLayer(
        positionStream:
        factory.fromGeolocatorPositionStream(
            stream: _geolocatorStream,
            // headingStream: factory.defaultHeadingStreamSource(),
        ),
        style: LocationMarkerStyle(
            markerDirection: MarkerDirection.heading,
            markerSize: const Size.square(55),
                marker: AvatarGlow(
                    //showTwoGlows: true,
                    glowColor:
                    const Color.fromARGB(255, 107, 107, 195),
                        endRadius: 500,
                        child: SvgPicture.asset(
                            "assets/plane2.svg",
                            // color: const Color.fromARGB(
                            //     255, 57, 239, 78),
                            height: 45,
                        ),
                ),
        ),
    ),
),

jesussmile avatar Apr 22 '24 02:04 jesussmile

This problem should be related to flutter_compass, please report there.

tlserver avatar Apr 22 '24 12:04 tlserver

and the heading ? gives -1 degrees from the stream ?

jesussmile avatar Apr 23 '24 15:04 jesussmile

What do you mean? Are you using real device or emulator?

tlserver avatar Apr 23 '24 15:04 tlserver

ofcourse a real device, _heading = (position.heading.toStringAsFixed(0)); in the code above at times returns back the value '-1' in android its fine, the issue lies with ios.

jesussmile avatar Apr 23 '24 16:04 jesussmile

I cannot repoduce this issue. Please provide more details. And you may try to run Custom Stream Example in your devices. If the issue still occur, modify that exmaple at line 99:

                  _headingStreamController.add(
                    LocationMarkerHeading(
                      heading: -1,
                      accuracy: pi * 0.2,
                    ),
                  );

tlserver avatar Apr 24 '24 16:04 tlserver

The issue is how ios perceives heading. it is actually a course. https://github.com/Baseflow/flutter-geolocator/issues/1281

jesussmile avatar May 07 '24 06:05 jesussmile

https://github.com/tlserver/flutter_map_location_marker/assets/11044978/e95f927f-2278-4df1-8359-61d59e5bb189

How can i align the heading of my marker based on the course and not compass then ? One way is to constantly compare the current position with the last position and align the heading which i did previously but its too much unnecessary computation and can i extract gps altitude, heading , ground speed etc directly from this plugin? I end up using geolocator for that. If you check the attached video . The heading is not based on the course rather the compass

jesussmile avatar May 07 '24 06:05 jesussmile

So, I did sth like this to get the course irrespective of the compass heading so my aircraft/ vehicle is correctly aligned to the path , Obviously i can't use HeadingSector etc.. as they are based on the compass.

import 'dart:io';
import 'dart:math';

import 'package:avatar_glow/avatar_glow.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_location_marker/flutter_map_location_marker.dart';
import 'package:geolocator/geolocator.dart';
import 'package:latlong2/latlong.dart';
// import 'dart:math' as Math;

class GeoStream extends StatefulWidget {
  @override
  State<GeoStream> createState() => _GeoStreamState();
}

class _GeoStreamState extends State<GeoStream> {
  final _positionStream = Geolocator.getPositionStream(
    locationSettings: const LocationSettings(
      accuracy: LocationAccuracy.bestForNavigation,
      //distanceFilter: 0,
      //timeLimit: Duration(minutes: 1),
    ),
  );
  Position? _previousPosition;
  bool _trackMe = false;
  final MapController _mapController = MapController();
  static double getHeading(
      double startLat, double startLong, double endLat, double endLong) {
    final double dLon = endLong - startLong;
    final double y = sin(dLon) * cos(endLat);
    final double x =
        cos(startLat) * sin(endLat) - sin(startLat) * cos(endLat) * cos(dLon);
    double heading = atan2(y, x);

    heading = 360 - (heading * 180 / pi) % 360;

    if (Platform.isIOS) heading = heading * (pi / 180);

    return heading;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Geolocator stand alone'),
      ),
      body: StreamBuilder<Position>(
        stream: _positionStream,
        builder: (context, snapshot) {
          if (!snapshot.hasData) {
            return const Center(child: CircularProgressIndicator());
          }

          final position = snapshot.data!;
          if (_trackMe) {
            _mapController.move(
              LatLng(position.latitude, position.longitude),
              _mapController.camera.zoom,
            );
          }

          double? bearing;
          if (_previousPosition != null) {
            bearing = getHeading(
              _previousPosition!.latitude,
              _previousPosition!.longitude,
              position.latitude,
              position.longitude,
            );
            //print('Device course: $bearing');
          }
          _previousPosition = position;

          return FlutterMap(
            mapController: _mapController,
            options: MapOptions(
              initialCenter: LatLng(position.latitude, position.longitude),
              initialZoom: 1,
              minZoom: 0,
              maxZoom: 19,
            ),
            children: [
              TileLayer(
                urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
                userAgentPackageName:
                    'net.tlserver6y.flutter_map_location_marker.example',
                maxZoom: 19,
              ),
              // MarkerLayer(markers: markers),
              CurrentLocationLayer(
                alignPositionOnUpdate: AlignOnUpdate.always,
                alignDirectionOnUpdate: AlignOnUpdate.never,
                style: LocationMarkerStyle(
                  showHeadingSector: false,
                  marker: DefaultLocationMarker(
                    child: AvatarGlow(
                      glowColor: Colors.blue,
                      glowRadiusFactor: 0.5,
                      child: Transform.rotate(
                        angle: bearing ?? 0,
                        child: const Icon(
                          Icons.navigation,
                          color: Colors.white,
                        ),
                      ),
                    ),
                  ),
                  markerSize: const Size(40, 40),
                  // markerDirection: MarkerDirection.,
                ),
              ),
            ],
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            _trackMe = !_trackMe;
          });
        },
        child: Icon(_trackMe ? Icons.stop : Icons.play_arrow),
      ),
    );
  }
}

jesussmile avatar May 08 '24 13:05 jesussmile

No, you absolutely can use HeadingSector. Check out the positionStream, headingStream and the Custom stream example.

tlserver avatar May 08 '24 15:05 tlserver

Ofcourse! But it's based on magnetic compass. I an afraid I am not able to explain myself properly. If u don't mind let me break it down. In your plugin the heading of the navigation is based on the device orientation. So if I point my device to south and travel north the HeadingSector will show south when I am navigating towards south. Generally while navigating it is better to use heading from a location stream rather than from compass. So far this plugin is getting all relevant information about heading from the compass. It would be nice if u could get a course instead.. that is what most navigation apps do. So your device can point any direction but you heading will always be correct as now it is following the course rather than the compass.

jesussmile avatar May 08 '24 16:05 jesussmile

Generally while navigating it is better to use heading from a location stream rather than from compass.

Since geolocation always returns {heading: -1} on iOS devices, which you have also mentioned that, heading from the location stream is not reliable.

In your plugin the heading of the navigation is based on the device orientation.

It is true that the default value of heading is based on device orientation but you are NOT limited to using device orientation as heading. The default implementation receives the device's position from the geolocator package and the device's heading from the flutter_compass package, but with type conversion, streams from other sources are also supported.

It would be nice if u could get a course instead.. that is what most navigation apps do. So your device can point any direction but you heading will always be correct as now it is following the course rather than the compass.

This feature is currently supported. This plugin is highly flexible. The displaying logic and data handling logic are separated. The CurrentLocationLayer accepts a positionStream to control the marker position and a headingStream to control the rotation of the marker. Therefore, you can create your own stream to provide the data you want. Again, check out the CustomStreamExample. This example shows how to provide a position stream and a heading stream NOT from device sensors but from a UI joystick.

Also, the AnimatedLocationMarkerLayer can be used if you prefer setState over emitting data to streams. For this case, check out the NoStreamExample.

How can i align the heading of my marker based on the course and not compass then? One way is to constantly compare the current position with the last position and align the heading which i did previously but its too much unnecessary computation

I do not think this is too much unnecessary computation; simple sine and cosine calculations only require a few compute resources. If this simple calculation happen less than 10 times per second, it should be un-noticeable. But if you already have heading value from other library, you may emit it directly into the heading stream to avoid computation.

and can i extract gps altitude, heading , ground speed etc directly from this plugin?

No, you cannot. The widget is only for displaying something on the screen but not for providing data. Instead, the widget receives data or data stream from constructor, but it smartly uses the data provided from other libraries as default values, so that you can have a flexible zero-config widget. See also FAQ.

I end up using geolocator for that.

Yes, this is the recommended way to obtain geolocation data. Then, you can consider providing the geolocation data and the calculated heading to CurrentLocationLayer or AnimatedLocationMarkerLayer. See also DefaultStreamExample

tlserver avatar May 09 '24 02:05 tlserver

I also have the same problem and it means my app looks totally broken to my users. What I really need is a complete example of how to fix this.

I don't really understand how I can switch from using the magnetic heading when stationary, to the course heading when moving?

bencollinsuk avatar May 26 '24 09:05 bencollinsuk

I don't have time to give you a full example, sorry. The recommandation is using StreamController to emit the values you want into a stream and put this stream to headingStream of CurrentLocationLayer. See also DefaultStreamExample for how to get the default magnetic heading stream.

tlserver avatar May 26 '24 12:05 tlserver

This issue is stale because it has been open for 30 days with no activity. Remove stale label or comment or this will be closed in 7 days.

github-actions[bot] avatar Jun 27 '24 10:06 github-actions[bot]

This issue was closed because it has been stalled for 7 days with no activity.

github-actions[bot] avatar Jul 05 '24 10:07 github-actions[bot]