maplibre / maplibre-navigation-ios

MapLibre Navigation SDK for iOS
Other
36 stars 31 forks source link

User's location, UserPuckCourseView, and map camera are out of sync. #94

Open michaelkirk opened 3 months ago

michaelkirk commented 3 months ago

The puck is a tricky fellow.

We've had (and continue to have) issues with the user location puck, the map camera, and the user location not always being in sync.

Background info

I think there's basically two distinct things that drive state changes here:

  1. The user moves around in the real world, their new gps coordinates need to be reflected in the app.
  2. The app changes its frame of reference - e.g. user manually pans, or toggles overview mode.

The UserPuckCourseView reflects the user's gps location and bearing. When in "tracking mode" the map camera also reflects the user's gps location and bearing.

Corollary: When in "tracking mode" the UserPuckCourseView should appear stationary relative to the physical screen, and the map should move and turn "beneath" it. (CONFIRM: Is this truly the desired behavior?)

We have a concept of frame-by-frame view tracking, for ensuring the puck stays on the user's GPS location during tricky animations. It can be enabled for a specific duration with: enableFrameByFrameCourseViewTracking.

Issues

I want to collect all the issues I know about affecting map camera and location puck in once place. They might represent several underlying problems, but I'm hoping to avoid a wack-a-mole situation where we fix one thing while breaking something else.

Please add to this if you know of other problems with this system.

The puck distance from the bottom of the screen is not constant

e.g. look at the puck position relative to the mouse pointer in this video, and note how it's position is twitching forward and backward. This effect becomes more pronounced as you increase the speed multiplier of the SimulatedLocationManager.

I'm not sure if this is an artifact of the SimulatedLocationManager. My real world usage of the app is primarily at slow speeds (bicycling), where this effect is not obviously visible.

https://github.com/user-attachments/assets/6ee97c8e-b92a-455b-8abe-df90e1b97144

With some debugging, I suspect that this might be because we have several competing places that position the puck view (by setting puckView.center). Some of these placements are animated and some are not. Sometimes we'll set the new location without animation during a previous animation which is still taking place.

There are a handful of one-time positioning that occurs during setup, but here are the places that are repeatedly positioning the puck:

  1. In mapViewDidFinishRenderingFrameFullyRendered when frame-by-frame tracking is enabled.
  2. As the user progresses down their route and their GPS coordinates change, updateCourseTracking is called.
  3. When navigationMapView.layoutSubviews is called, it also calls updateCourseTracking. Some things I haven't figured out yet: 3.a Why is updateCourseTracking called here? 3.b Why is navigationMapView.layoutSubviews called so frequently? Not constantly, but seemingly every few seconds - maybe because some superview is changing its frame/bounds to implement map mapping?
  4. In updateCourseView which is fired in response to gestures. I haven't dug into this yet.

The puck orientation lags transitioning to/from overview

Same issue as #61 - I just wanted to document that it might be related here.

Note how the puck is rotating before/after the map, so for a while it's point the wrong direction. The map and puck should always be oriented in lock step.

https://github.com/user-attachments/assets/2cf51ad2-62dc-4844-a2df-072452b46dd6

In modal presentation, the puck animates onto the map.

Note: This screenshot is from my own app, because it involves the modal presentation of the NavigationViewController which isn't used in the demo app.

https://github.com/user-attachments/assets/72a9b66f-21c4-4e5a-bcab-0a0d3794766d

Instead of sliding the puck to the start, the puck should already be at the start when it first appears.

Related issues:

The enableFrameByFrameCourseViewTracking stopped getting called at some point due to a bug, which caused gesture panning and gesture zooming to drag the puck off the route line. This was resolved in #53.

enableFrameByFrameCourseViewTracking used to be a private method, but #59 made it public in case you are doing some external camera manipulation that required similar treatment.


92 - didn't fix anything, but it changed the default camera position to be lower.


As I understand #82, it has two goals:

  1. positioning the camera using the delegate navigationMapViewUserAnchorPoint()
  2. Fix some puck placement issues. In my testing, the above cases are all still a problem with this branch. Maybe there is yet another bug that I haven't accounted for?
Patrick-Kladek commented 3 months ago

Corollary: When in "tracking mode" the UserPuckCourseView should appear stationary relative to the physical screen, and the map should move and turn "beneath" it. (CONFIRM: Is this truly the desired behavior?)

Yes I would keep the puck stationary.

We have a concept of frame-by-frame view tracking, for ensuring the puck stays on the user's GPS location during tricky animations. It can be enabled for a specific duration with: enableFrameByFrameCourseViewTracking.

I'm not sure why frame-by-frame animation even exists. If it was added as a battery optimisation I don't understand it, as re-drawing the map even if it moved slightly needs a magnitude more energy then positioning one UIView. Based on this and the fact that the puck becomes out of sync and jitters, I would simply position the puck on every frame and remove enableFrameByFrameCourseViewTracking or basically keep it in this mode forever.

boldtrn commented 3 months ago

If it was added as a battery optimisation I don't understand it, as re-drawing the map even if it moved slightly needs a magnitude more energy then positioning one UIView.

While I don't know this for sure, I expect that battery optimisation was the reason for this. Have you actually measured the battery consumption and compared them for a real live scenario (so using a longer route and and actual map style, not just the demo style)? We have made the experience that limiting the FPS of the map save serious amounts of energy on Android.

Patrick-Kladek commented 3 months ago

I fully support throttling the map and we also had a lot of battery life gains by reducing the frame rate even more.

However throttling the positioning of the cursor but not the map makes no sense to me. It would make more sense if we attach a CADisplayLink and update the cursor from there, then it's really synced with the Display but our current solution makes no sense to me. Thats why I want to get rid of it.

boldtrn commented 3 months ago

Ah ok got it, yeah ok, sounds good to me then. You are right, it is only the arrow that behaves differently which is kind of weird.

simf20 commented 1 month ago

Hey guys, I am experiencing the same issues as described here which make the navigation barely usable. Is there any update on those issues yet?

Patrick-Kladek commented 1 month ago

@simf20 have you tried https://github.com/maplibre/maplibre-navigation-ios/pull/82?

Recently, we migrated to Ferrostar, which has a much better camera and puck movement, so sadly, I won't be contributing much to Maplibre-navigation anymore.

simf20 commented 1 month ago

@simf20 have you tried #82?

Recently, we migrated to Ferrostar, which has a much better camera and puck movement, so sadly, I won't be contributing much to Maplibre-navigation anymore.

Unfortunately, I am having the same "shaking" issue after merging #82 ...

I confirm that the problem is not only in the SimulatedLocationManager. It also happens while driving a car.