Skip to content

Commit

Permalink
fix: make follow props on Camera deterministic (#550)
Browse files Browse the repository at this point in the history
  • Loading branch information
KiwiKilian authored Dec 27, 2024
1 parent 3d519fd commit e9256e7
Show file tree
Hide file tree
Showing 8 changed files with 415 additions and 214 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import android.content.Context;
import android.location.Location;
import android.util.Log;

import org.maplibre.android.camera.CameraPosition;
import org.maplibre.android.camera.CameraUpdate;
Expand All @@ -11,13 +12,9 @@
import org.maplibre.android.geometry.VisibleRegion;
import org.maplibre.android.location.OnCameraTrackingChangedListener;
import org.maplibre.android.location.modes.CameraMode;
import org.maplibre.android.location.modes.RenderMode;
import org.maplibre.android.maps.MapLibreMap;
import org.maplibre.android.maps.Style;
import org.maplibre.android.location.LocationComponent;
import org.maplibre.android.location.LocationComponentOptions;
import org.maplibre.android.location.LocationComponentActivationOptions;
// import org.maplibre.android.plugins.locationlayer.LocationLayerPlugin;

import org.maplibre.reactnative.components.AbstractMapFeature;
import org.maplibre.reactnative.components.location.LocationComponentManager;
import org.maplibre.reactnative.components.mapview.MLRNMapView;
Expand All @@ -31,8 +28,6 @@
import org.maplibre.reactnative.location.UserTrackingState;
import org.maplibre.reactnative.utils.GeoJSONUtils;

import org.maplibre.reactnative.R;

import org.maplibre.reactnative.events.constants.EventTypes;

import com.facebook.react.bridge.WritableMap;
Expand Down Expand Up @@ -87,12 +82,12 @@ public class MLRNCamera extends AbstractMapFeature {
private LocationManager.OnUserLocationChange mLocationChangeListener = new LocationManager.OnUserLocationChange() {
@Override
public void onLocationChange(Location nextLocation) {
if (getMapboxMap() == null || mLocationComponentManager == null || !mLocationComponentManager.hasLocationComponent() || (!mFollowUserLocation)) {
return;
}
if (getMapboxMap() == null || mLocationComponentManager == null || !mLocationComponentManager.hasLocationComponent() || (!mFollowUserLocation)) {
return;
}

mUserLocation.setCurrentLocation(nextLocation);
sendUserLocationUpdateEvent(nextLocation);
mUserLocation.setCurrentLocation(nextLocation);
sendUserLocationUpdateEvent(nextLocation);
}
};

Expand Down Expand Up @@ -236,12 +231,7 @@ private CameraPosition getUserLocationUpdateCameraPosition(double zoomLevel) {
}
}

return new CameraPosition.Builder()
.target(center)
.bearing(getDirectionForUserLocationUpdate())
.tilt(mPitch)
.zoom(zoomLevel)
.build();
return new CameraPosition.Builder().target(center).bearing(getDirectionForUserLocationUpdate()).tilt(mPitch).zoom(zoomLevel).build();
}

private double getDirectionForUserLocationUpdate() {
Expand All @@ -260,7 +250,7 @@ private double getDirectionForUserLocationUpdate() {
}

private void sendUserLocationUpdateEvent(Location location) {
if(location == null){
if (location == null) {
return;
}
IEvent event = new MapChangeEvent(this, EventTypes.USER_LOCATION_UPDATED, makeLocationChangePayload(location));
Expand Down Expand Up @@ -376,37 +366,38 @@ private void updateLocationLayer(@NonNull Style style) {
}

mLocationComponentManager.update(style);

if (mFollowUserLocation) {
mLocationComponentManager.setCameraMode(UserTrackingMode.getCameraMode(mUserTrackingMode));
}
mLocationComponentManager.setFollowUserLocation(mFollowUserLocation);

if (mFollowUserLocation) {
mLocationComponentManager.setCameraMode(UserTrackingMode.getCameraMode(mUserTrackingMode));
mLocationComponentManager.addOnCameraTrackingChangedListener(new OnCameraTrackingChangedListener() {
@Override public void onCameraTrackingChanged(int currentMode) {
int userTrackingMode = UserTrackingMode.NONE;
switch (currentMode) {
case CameraMode.NONE:
userTrackingMode = UserTrackingMode.NONE;
break;
case CameraMode.TRACKING:
userTrackingMode = UserTrackingMode.FOLLOW;
break;
case CameraMode.TRACKING_COMPASS:
userTrackingMode = UserTrackingMode.FollowWithHeading;
break;
case CameraMode.TRACKING_GPS:
userTrackingMode = UserTrackingMode.FollowWithCourse;
break;
default:
userTrackingMode = UserTrackingMode.NONE;
}
updateUserTrackingMode(userTrackingMode);
}
@Override public void onCameraTrackingDismissed() {
@Override
public void onCameraTrackingChanged(int currentMode) {
int userTrackingMode;

switch (currentMode) {
case CameraMode.NONE:
userTrackingMode = UserTrackingMode.NONE;
break;
case CameraMode.TRACKING:
userTrackingMode = UserTrackingMode.FOLLOW;
break;
case CameraMode.TRACKING_COMPASS:
userTrackingMode = UserTrackingMode.FollowWithHeading;
break;
case CameraMode.TRACKING_GPS:
userTrackingMode = UserTrackingMode.FollowWithCourse;
break;
default:
userTrackingMode = UserTrackingMode.NONE;
}

updateUserTrackingMode(userTrackingMode);
}

@Override
public void onCameraTrackingDismissed() {
}
});
} else {
mLocationComponentManager.setCameraMode(CameraMode.NONE);
Expand All @@ -429,10 +420,7 @@ public void setZoomLevel(double zoomLevel) {
}

private CameraPosition buildCamera(CameraPosition previousPosition, boolean shouldUpdateTarget) {
CameraPosition.Builder builder = new CameraPosition.Builder(previousPosition)
.bearing(mHeading)
.tilt(mPitch)
.zoom(mZoomLevel);
CameraPosition.Builder builder = new CameraPosition.Builder(previousPosition).bearing(mHeading).tilt(mPitch).zoom(mZoomLevel);

if (shouldUpdateTarget) {
builder.target(GeoJSONUtils.toLatLng(mCenterCoordinate));
Expand Down Expand Up @@ -507,6 +495,7 @@ MapLibreMap getMapboxMap() {
/**
* Create a payload of the location data per the web api geolocation spec
* https://dev.w3.org/geo/api/spec-source.html#position
*
* @return
*/
private WritableMap makeLocationChangePayload(Location location) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ public LocationComponentManager(MLRNMapView mlrnMapView, Context context) {

private boolean mShowingUserLocation = false;

private OnCameraTrackingChangedListener mOnCameraTrackingChangedListener = null;

public void showUserLocation(boolean showUserLocation) {
mShowUserLocation = showUserLocation;
stateChanged();
Expand All @@ -68,16 +70,21 @@ public void setRenderMode(@RenderMode.Mode int renderMode) {
}

public void setPreferredFramesPerSecond(int preferredFramesPerSecond) {
if(mLocationComponent == null || preferredFramesPerSecond <= 0) {
if (mLocationComponent == null || preferredFramesPerSecond <= 0) {
return;
}
}

mLocationComponent.setMaxAnimationFps(preferredFramesPerSecond);
}


public void addOnCameraTrackingChangedListener(OnCameraTrackingChangedListener onCameraTrackingChangedListener) {
mLocationComponent.addOnCameraTrackingChangedListener(onCameraTrackingChangedListener);
if (mOnCameraTrackingChangedListener != null) {
mLocationComponent.removeOnCameraTrackingChangedListener(mOnCameraTrackingChangedListener);
}

mOnCameraTrackingChangedListener = onCameraTrackingChangedListener;

mLocationComponent.addOnCameraTrackingChangedListener(mOnCameraTrackingChangedListener);
}

@SuppressLint("MissingPermission")
Expand Down Expand Up @@ -111,7 +118,7 @@ public void update(@NonNull Style style) {
public void update(boolean displayUserLocation, @NonNull Style style) {
Integer tintColor = mMapView.getTintColor();

if (mLocationComponent == null || tintColor != null ) {
if (mLocationComponent == null || tintColor != null) {
mLocationComponent = mMap.getLocationComponent();

LocationComponentActivationOptions locationComponentActivationOptions = LocationComponentActivationOptions
Expand Down Expand Up @@ -148,10 +155,10 @@ LocationComponentOptions options(boolean displayUserLocation) {
.accuracyAlpha(0.0f);
} else if (tintColor != null) {
builder = builder
.enableStaleState(false)
.bearingTintColor(tintColor)
.foregroundTintColor(tintColor)
.accuracyColor(tintColor);
.enableStaleState(false)
.bearingTintColor(tintColor)
.foregroundTintColor(tintColor)
.accuracyColor(tintColor);
}
return builder.build();
}
Expand Down
13 changes: 6 additions & 7 deletions ios/MLRN/MLRNCamera.m
Original file line number Diff line number Diff line change
Expand Up @@ -179,11 +179,6 @@ - (void)_updateCameraFromTrackingMode
return;
}

if (_map.userTrackingMode != [self _userTrackingMode]) {
_map.showsUserLocation = [self _userTrackingMode] != MLNUserTrackingModeNone;
_map.userTrackingMode = [self _userTrackingMode];
}

MLNMapCamera *camera = _map.camera;
if (_followPitch != nil && [_followPitch floatValue] >= 0.0) {
camera.pitch = [_followPitch floatValue];
Expand All @@ -202,8 +197,12 @@ - (void)_updateCameraFromTrackingMode
if (_followZoomLevel != nil && [_followZoomLevel doubleValue] >= 0.0) {
camera.altitude = [_map altitudeFromZoom:[_followZoomLevel doubleValue]];
}

[_map setCamera:camera animated:YES];

[_map setCamera:camera animated:NO];

if (_map.userTrackingMode != [self _userTrackingMode]) {
[_map setUserTrackingMode:[self _userTrackingMode] animated:NO completionHandler:nil];
}
}

- (NSUInteger)_userTrackingMode
Expand Down
4 changes: 4 additions & 0 deletions packages/examples/src/Examples.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ const Examples = new ExampleGroup(
"Follow User Location Render Mode",
MapLibreExamples.FollowUserLocationRenderMode,
),
new ExampleItem(
"User Location for Navigation",
MapLibreExamples.UserLocationForNavigation,
),
new ExampleItem(
"User Location Updates",
MapLibreExamples.UserLocationUpdate,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import {
Camera,
MapView,
SymbolLayer,
UserLocation,
UserLocationRenderMode,
UserTrackingMode,
} from "@maplibre/maplibre-react-native";
import { useState } from "react";
import { Button } from "react-native";

import maplibreIcon from "../../assets/images/maplibre.png";
import { OSM_RASTER_STYLE } from "../../constants/OSM_RASTER_STYLE";
import { sheet } from "../../styles/sheet";

export function UserLocationForNavigation() {
const [navigationActive, setNavigationActive] = useState(false);

return (
<>
<Button
title={`Navigation is ${navigationActive ? "active" : "inactive"}`}
onPress={() => setNavigationActive((prevState) => !prevState)}
/>

<MapView
style={sheet.matchParent}
mapStyle={OSM_RASTER_STYLE}
contentInset={navigationActive ? [200, 0, 0, 0] : undefined}
pitchEnabled={navigationActive}
>
{navigationActive ? (
<UserLocation
renderMode={
navigationActive
? UserLocationRenderMode.Normal
: UserLocationRenderMode.Native
}
showsUserHeadingIndicator
>
<SymbolLayer
id="navigation-icon"
style={{
iconImage: maplibreIcon,
iconPitchAlignment: "map",
iconAllowOverlap: true,
}}
/>
</UserLocation>
) : null}

<Camera
followUserLocation={navigationActive}
followUserMode={
navigationActive
? UserTrackingMode.FollowWithHeading
: UserTrackingMode.Follow
}
followZoomLevel={19}
followPitch={60}
pitch={0}
onUserTrackingModeChange={(event) => {
console.log("js userTrackingModeChange");
console.log("js", event.type);
console.log("js", JSON.stringify(event.nativeEvent));

if (
navigationActive &&
!event.nativeEvent.payload.followUserLocation
) {
setNavigationActive(false);
}
}}
/>
</MapView>
</>
);
}
1 change: 1 addition & 0 deletions packages/examples/src/examples/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export { ShapeSourceIcon } from "./SymbolCircleLayer/ShapeSourceIcon";
// USERLOCATION
export { FollowUserLocationRenderMode } from "./UserLocation/FollowUserLocationRenderMode";
export { FollowUserLocationAlignment } from "./UserLocation/FollowUserLocationAlignment";
export { UserLocationForNavigation } from "./UserLocation/UserLocationForNavigation";
export { SetAndroidPreferredFramesPerSecond } from "./UserLocation/SetAndroidPreferredFramesPerSecond";
export { UserLocationDisplacement } from "./UserLocation/UserLocationDisplacement";
export { UserLocationUpdate } from "./UserLocation/UserLocationUpdate";
Expand Down
Loading

0 comments on commit e9256e7

Please sign in to comment.