rnmapbox / maps

A Mapbox react native module for creating custom maps
MIT License
2.27k stars 849 forks source link

[Bug]: iOS topImage inconsistent rendering #3446

Open lgspacil opened 7 months ago

lgspacil commented 7 months ago

Mapbox Implementation

Mapbox

Mapbox Version

default

React Native Version

0.71.8

Platform

iOS

@rnmapbox/maps version

10.1.19

Standalone component to reproduce

import React, { useRef, useEffect, useState } from 'react';
import Mapbox, { MapView, Camera, CustomLocationProvider, LocationPuck, Viewport } from '@rnmapbox/maps';
import { Icon } from 'react-native-elements';

export default function BugReportExample() {
    const [style, setStyle] = useState('mapbox://styles/mapbox/streets-v11');
    const [bearing, setBearing] = useState(0);
    const [location, setLocation] = useState([-74.00597, 40.71427]);
    const [prevLocation, setPrevLocation] = useState<number[] | null>(null);
    const viewportRef = useRef<Viewport | null>(null);

    // Bearing calculation function
    function calculateBearing(lat1, lon1, lat2, lon2) {
        const radLat1 = (lat1 * Math.PI) / 180;
        const radLat2 = (lat2 * Math.PI) / 180;
        const deltaLon = ((lon2 - lon1) * Math.PI) / 180;
        const y = Math.sin(deltaLon) * Math.cos(radLat2);
        const x = Math.cos(radLat1) * Math.sin(radLat2) - Math.sin(radLat1) * Math.cos(radLat2) * Math.cos(deltaLon);
        const bearing = (Math.atan2(y, x) * 180) / Math.PI;
        return (bearing + 360) % 360;
    }

    useEffect(() => {
        const interval = setInterval(() => {
            setLocation(location => {
                // Calculate the bearing if prevLocation exists
                if (prevLocation) {
                    const newBearing = calculateBearing(
                        prevLocation[1],
                        prevLocation[0],
                        location[1] + 0.0001,
                        location[0] + 0.0001
                    );
                    setBearing(newBearing);
                }
                const newLocation = [location[0] + 0.0001, location[1] + 0.0001];
                // Update prevLocation with the current location before it changes
                setPrevLocation(location);
                return newLocation;
            });
        }, 500);
        return () => clearInterval(interval);
    }, [prevLocation]);

    return (
        <>
            <MapView
                style={{ flex: 1 }}
                onDidFinishLoadingStyle={() => {
                    viewportRef.current?.transitionTo({ kind: 'followPuck' }, { kind: 'default' });
                }}
                styleURL={style}>
                <Mapbox.Images
                    images={{
                        userLocationTopImage: "<path to image>",
                    }}
                />
                <Camera
                    defaultSettings={{
                        centerCoordinate: [-74.00597, 40.71427],
                        zoomLevel: 14,
                    }}
                    followZoomLevel={14}
                    followUserLocation={true}
                />
                <Viewport ref={viewportRef} />
                <CustomLocationProvider coordinate={location} heading={bearing} />
                <LocationPuck
                    visible={true}
                    puckBearingEnabled={true}
                    topImage={'userLocationTopImage'}
                    scale={['interpolate', ['linear'], ['zoom'], 10.0, 1.0, 20.0, 2.0]}
                />
            </MapView>
            <Icon
                onPress={() => {
                    setStyle(
                        style === 'mapbox://styles/mapbox/streets-v11'
                            ? 'mapbox://styles/mapbox/satellite-v9'
                            : 'mapbox://styles/mapbox/streets-v11'
                    );
                }}
                containerStyle={{
                    position: 'absolute',
                    bottom: 10,
                    right: 0,
                }}
                raised
                name='layers'
                type='material'
                color={'black'}
            />
        </>
    );
}

Observed behavior and steps to reproduce

There is a bug with <LocationPuck />. It works perfectly fine on Android devices but on iOS the topImage is flipped on initial load. When I change the map style the image again flips and the scale is off.

iOS Device is on the left Android Device is on the right

https://github.com/rnmapbox/maps/assets/17690783/8429d7df-3bd4-491a-9411-aa0b4c09b418

Expected behavior

No response

Notes / preliminary analysis

No response

Additional links and references

No response

RemiHin commented 4 months ago

same issue