maps icon indicating copy to clipboard operation
maps copied to clipboard

[Bug]: `LocationPuck` bearing not visible on Android

Open philiplindberg opened this issue 1 year ago • 11 comments

Mapbox Implementation

Mapbox

Mapbox Version

default

React Native Version

0.73.4

Platform

Android

@rnmapbox/maps version

10.1.15

Standalone component to reproduce

import React from 'react'
import { Camera, LocationPuck, MapView } from '@rnmapbox/maps'

export default function App() {
  return (
    <MapView style={{ flex: 1 }}>
      <Camera
        zoomLevel={14}
        followUserLocation={true}
      />
      <LocationPuck
        puckBearingEnabled={true}
      />
    </MapView>
  )
}

Observed behavior and steps to reproduce

When rendering the LocationPuck with its bearing enabled on Android, the bearing does not appear. The bearing does appear on iOS.

Android iOS
unnamed (1) IMG_69F787A3B577-1

Expected behavior

The LocationPuck's bearing should be visible on both Android and iOS if enabled.

Notes / preliminary analysis

No response

Additional links and references

No response

philiplindberg avatar Feb 23 '24 16:02 philiplindberg

You should use a custom image to show LocationPuck bearing on Android.

<MapboxGL.Images images={{ 
      "userLocationTopImage": markerUserIcon, 
      "headingArrow": headingIcon,
      "headingArrowXS": headingIconXS
      }} />

  <MapboxGL.LocationPuck
      topImage="userLocationTopImage"
      puckBearingEnabled={true} 
      puckBrearing="heading"
      bearingImage="headingArrow"
      visible={true}
      pulsing={{
          isEnabled: true,
          color: customColors.markerUserLocation,
          radius: 50.0,
      }} />

smartmedev avatar Mar 15 '24 14:03 smartmedev

Hi there! for some reason this custom image is not working for my project in android. Is there any way i could add an SVG icon here instead of the PNG? I am trying to add the SVG Image but it is giving me some errors. Here are my project details:

React Native Version 0.70.15

Platform Android

@rnmapbox/maps version 10.1.15

Code:

import React from 'react'; import { View } from 'react-native'; import MapboxGL from '@rnmapbox/maps'; import markerUserIcon from './markerUserIcon.svg'; import headingIcon from './headingIcon.svg';

const ShowMap = () => { return ( <View style={{ flex: 1 }}> <MapboxGL.MapView style={{ flex: 1 }}> <MapboxGL.Images images={{ userLocationTopImage: markerUserIcon, headingArrow: headingIcon, }} /> {/* Other MapboxGL components and configurations */} </MapboxGL.MapView> </View> ); }; export default ShowMap;

<MapboxGL.LocationPuck topImage="userLocationTopImage" puckBearingEnabled={true} puckBearing="heading" bearingImage="headingArrow" visible={true} pulsing={{ isEnabled: true, color: customColors.markerUserLocation, radius: 50.0, }} />

Error that i am encountering:

Error while updating property 'images' of a view managed by: RNMBXImages null next. value must not be null

fahadShah23 avatar Jul 24 '24 05:07 fahadShah23

@philiplindberg nope svg is not supported, sorry

mfazekas avatar Jul 24 '24 08:07 mfazekas

Just a quick question @smartmedev or may be you can answer @mfazekas. How can i import the png images (markerUserIcon, headingIcon, headingIconXS) in my file? i am trying to import them like below in my working file but they does not seems to be working at my end.

import markerUserIcon from './markerUserIcon.png'; import headingIcon from './headingIcon.png'; import headingIconXS from './headingIconXS.png';

i have placed the icons at the same path as my project file on which i want to use the image. am i doing it correct or anything i need to change here?

fahadShah23 avatar Aug 05 '24 21:08 fahadShah23

Hi @fahadShah23 , in react native you cannot import images with "import". You need require. In my project I have an "icons.js" files where I require all required images then export them. (Note that all images are on /app/assets/images/ folder) On the other side, in components and screens I can import images from icons.js file.

Here's an example:

on my /app/utils/icons.js file:

const headingIcon = require('../assets/images/heading.png');
const markerUserIcon = require('../assets/images/marker-user.png');
const headingIconXS = require('../assets/images/heading-icon-xs.png');

export {
    headingIcon,
    markerUserIcon,
    headingIconXS
}

then on my /app/screens/HomeScreen.js import icons like this:

import { headingIcon, markerUserIcon, headingIconXS } from '../../utils/icons';

<MapboxGL.Images images={{ 
      "userLocationTopImage": markerUserIcon, 
      "headingArrow": headingIcon,
      "headingArrowXS": headingIconXS
      }} />

  <MapboxGL.LocationPuck
      topImage="userLocationTopImage"
      puckBearingEnabled={true} 
      puckBrearing="heading"
      bearingImage="headingArrow"
      visible={true}
      pulsing={{
          isEnabled: true,
          color: customColors.markerUserLocation,
          radius: 50.0,
      }} />

smartmedev avatar Aug 07 '24 12:08 smartmedev

Hi @smartmedev thank you for your response. I have tried your solution but unfortunately image is still not appearing at my end. No sure what wrong i am doing here, can you please help identify. Here is my code, for instance i have placed my images on the same path/folder where my code file is. Below are the path to my implementation file ( where i need to get and show the image) and also the images path:

File Path: /home/{user}/{projectName}/libs/pattern/src/ui/features/Field/DrawField/DrawFieldFeature.native.tsx Images Path: /home/{user}/{projectName}/libs/pattern/src/ui/features/Field/heading.png /home/{user}/{projectName}/libs/pattern/src/ui/features/Field/DrawField/marker-user.png /home/{user}/{projectName}/libs/pattern/src/ui/features/Field/DrawField/heading-icon-xs.png

here is how i am getting them in my file i.e. 'DrawFieldFeature.native.tsx':

const headingIcon = require('./heading.png'); const markerUserIcon = require('./marker-user.png'); const headingIconXS = require('./heading-icon-xs.png');

Here is how i am using the above icons in Images & LocationPuck:

<Images
          images={{
            userLocationTopImage: markerUserIcon,
            headingArrow: headingIcon,
            headingArrowXS: headingIconXS,
          }}
        />
        <LocationPuck
          visible
          topImage="userLocationTopImage"
          bearingImage="headingArrow"
          puckBearingEnabled
          puckBearing="heading"
          pulsing={{ isEnabled: true, radius: 'accuracy', color: 'lightblue' }}
        />

Also just to check if the images are correctly imported in the file, i console the headingIcon, markerUserIcon and headingIconXS values and it gives my an numeric value i.e. 6. is it correct or it should give some kind or uri?

fahadShah23 avatar Aug 15 '24 09:08 fahadShah23

hi @fahadShah23 if images are in the same folder, have you tried to import like this ? Maybe absolute path could not work.

const headingIcon = require('heading.png');
const markerUserIcon = require('marker-user.png');
const headingIconXS = require('heading-icon-xs.png');

Also, use double quotes "" for the name of image in "images" attribute of <Images> element, you didn't put it in your example.

<MapboxGL.Images images={{ 
      "userLocationTopImage": markerUserIcon, 
      "headingArrow": headingIcon,
      "headingArrowXS": headingIconXS
      }} />

Let me know if it works, i think that require should give the path not numbers, but I'm not sure at all.

smartmedev avatar Aug 17 '24 20:08 smartmedev

I have tried this but I can only get the bearingImage to show if I hide the topImage. They do not seem to play nice together.

import React from 'react';
import { Images, LocationPuck } from '@rnmapbox/maps';

export default function UserLocationMarker() {
  const headingIcon = require('./heading.png');
  const userIcon = require('./user-location.png');
  return (
    <>
      <Images
        images={{
          headingArrow: headingIcon,
          userLocation: userIcon,
        }}
      />
      <LocationPuck
        // bug? topImage and bearingImage do not work well together
        // topImage={'userLocation'}
        puckBearingEnabled={true}
        puckBearing={'heading'}
        bearingImage={'headingArrow'}
        visible={true}
        pulsing={{ isEnabled: true, radius: 50.0, color: 'lightblue' }}
      />
    </>
  );
}

I'm running Mapbox version 10.1.28 as I am on react native 0.73.8 currently.

brien-crean avatar Sep 11 '24 20:09 brien-crean

@brien-crean

I had the same issue as you and came up with an alternative, hope this helps for you. I have a prop called puckBearingEnabled, if that is true and the platform is android, it shows a custom image that already has a bearing arrow in it (which i created myself). I have added the image to the comment in PNG (so you can use it yourself) or SVG (so you can edit it in figma or other tool)

  if (Platform.OS === 'android' && puckBearingEnabled) {
    const bearing = require('../../assets/icons/map/user-location-with-bearing.png');

    const images = {
      headingArrow: bearing,
    };

    locationPuck = (
      <>
        <Images images={images} />

        <Mapbox.LocationPuck
          puckBearingEnabled={puckBearingEnabled}
          puckBearing="heading"
          bearingImage={puckBearingEnabled ? "headingArrow" : null}
          visible={true}
        />
      </>
    );
  } else {
    locationPuck = (
      <Mapbox.LocationPuck
        puckBearingEnabled={puckBearingEnabled}
        puckBearing="heading"
        visible={true}
      />
    );
  }

user-location-with-bearing

user-location-with-bearing

fredro84 avatar Oct 12 '24 16:10 fredro84

if anyone is looking for a way to use the View component or SVG (react-native-svg), here is an example:

...
import {LocationPuck, Images, Image} from '@rnmapbox/maps';

const ImagesWrapper = ({children}) => {
  // fix ios issue with overlapping the map layer
  if (Platform.OS === 'ios') {
    return <View style={styles.hiddenLayer}>{children}</View>;
  }
  return <>{children}</>;
};

const CustomLocation = () => {
  return (
    <>
      <ImagesWrapper>
        <Images>
          <Image name="topImageID">
            <SomeSvgComponent />
          </Image>
          <Image name="shadowImageID">
            <SomeViewComponent />
          </Image>
        </Images>
      </ImagesWrapper>

      <LocationPuck
        shadowImage="shadowImageID"
        topImage="topImageID"
        puckBearingEnabled={true}
        puckBearing="heading"
        pulsing={{
          isEnabled: false,
        }}
      />
    </>
  );
};

const styles = StyleSheet.create({
  hiddenLayer: {
    position: 'absolute',
    top: -1000,
    left: -1000,
  },
});

yuriydrobniy avatar Oct 31 '24 10:10 yuriydrobniy

It seems to me like the puckBearingEnabled property just gets ignored inside Android? Like right here it's supposed to set the bearing image and it only pays attention to the deprecated androidRenderMode. It looks to me like that function in particular is just a poor imitation of the Android SDK's createDefault2DPuck. Which is what should be used instead I think..

Anyhow, I'll see if I have time make a PR so everybody can stop messing around with loading custom images for a bearing indicator hahaha.

RmStorm avatar Jan 17 '25 11:01 RmStorm