Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't press ShapeSource on top of UserLocation #241

Closed
arnaudambro opened this issue Jul 12, 2019 · 16 comments
Closed

Can't press ShapeSource on top of UserLocation #241

arnaudambro opened this issue Jul 12, 2019 · 16 comments

Comments

@arnaudambro
Copy link
Contributor

Hello,

image

I have a SymbolLayer located underneath the UserLocation, and I wish the SymbolLayer to be pressable.
But it seems it can't be done: when I press the icon, it is actually the UserLocation which is pressed, and the UserLocation onPress event doesn't have any argument and doesn't have a pointerEvents prop or equivalent.

Is there a way to get to the SymbolLayer ?

Again, great repo, and greater to become, thanks all for the work done !

@arnaudambro arnaudambro changed the title Press on UserLocation and SymbolLayer on top of each other Aug 29, 2019
@arnaudambro
Copy link
Contributor Author

@mfazekas can you experience the same behaviour ?

I can see on ShapeSource.md that

Source press listener, gets called when a user presses one of the children layers only
if that layer has a higher z-index than another source layers

And I can see that UserLocation is an Annotation, and Annotation is a ShapeSource, so does it mean that we should give a low z-index to UserLocation ? If so, how can we achieve this ?

@lpfunding
Copy link

Having the same issue, was this fixed?

@arnaudambro
Copy link
Contributor Author

No fix for now

@kristfal
Copy link
Member

Workaround can be to put a transparent layer on top of the layer in the shape source with a higher index than user location.

We could accept a PR with a prop on UserLocation that disables the on press listener, but I honestly feel like this is a very narrow usecase with a functional workaround.

@arnaudambro
Copy link
Contributor Author

arnaudambro commented Oct 11, 2019

Hi @kristfal
Thanks for the tip, but I just spent a few hours on it and I don't see how to proceed.
The last step I've done has been to increase the layerIndex of my SymbolLayer while hot reloading, which is working while hot reloading until the layerIndex 196 (don't ask me why !), then at 197 it crashes. At 196, I could see that my SymbolLayer was coming on top of the UserLocation so I was quite happy, but I still couldn't click on it.
But when I reload the app with 196, it crashes directly (even more: it quits unexpectedly and I need to yarn ios again).

So I don't see how to proceed ! If you have one more clue it would be really nice...

@lpfunding
Copy link

@kristfal I don't think this is a narrow usecase... If anyone has custom markers rendered (think of the earthquake example) which are clickable, none can be clicked if they are within a 1inch by 1inch square of UserLocation...

@arnaudambro
Copy link
Contributor Author

@kristfal I need to move forward on the subject now, this is becoming critical to me... a help would be great if possible !

Workaround can be to put a transparent layer on top of the layer in the shape source with a higher index than user location.
According to this comment on #392 , the layerIndex is still a problem, how could you use it ?

We could accept a PR with a prop on UserLocation that disables the on press listener, but I honestly feel like this is a very narrow usecase with a functional workaround.
How do you disable the onPress listener and not capture the touch press so that it goes to the underlying Symbol ?

Thanks !

@arnaudambro
Copy link
Contributor Author

Alright, with a proper utilisation of belowLayerID and aboveLayerId, I got this working.
SUppose your SymbolLayer within your ShapeSource has an id of markersSymbol, here is the code I used for the UserLocation

import React from 'react';
import MapboxGL from '@react-native-mapbox-gl/maps';
import { withTheme } from 'styled-components';

const layerStyles = (circleColor) => ({
  pluse: {
    circleRadius: 15,
    circleColor,
    circleOpacity: 0.2,
    circlePitchAlignment: 'map',
  },
  background: {
    circleRadius: 9,
    circleColor: '#fff',
    circlePitchAlignment: 'map',
  },
  foreground: {
    circleRadius: 6,
    circleColor,
    circlePitchAlignment: 'map',
  },
});


const MyUserLocation = ({ theme: { color: { primary } } }) =>
  <MapboxGL.UserLocation
    visible
    animated
  >
    <MapboxGL.CircleLayer
      key="mapboxUserLocationPluseCircle"
      id="mapboxUserLocationPluseCircle"
      belowLayerID='markersSymbol'
      style={layerStyles(primary).pluse}
    />
    <MapboxGL.CircleLayer
      key="mapboxUserLocationWhiteCircle"
      id="mapboxUserLocationWhiteCircle"
      belowLayerID='markersSymbol'
      style={layerStyles(primary).background}
    />
    <MapboxGL.CircleLayer
      key="mapboxUserLocationBlueCicle"
      id="mapboxUserLocationBlueCicle"
      belowLayerID='markersSymbol'
      aboveLayerID="mapboxUserLocationWhiteCircle"
      style={layerStyles(primary).foreground}
    />
  </MapboxGL.UserLocation>

export default withTheme(MyUserLocation)
@lpfunding
Copy link

@arnaudambro I'm not entirely following your solution - how would you apply that to my code below?

      <MapboxGL.ShapeSource
        id="ShapeSource"
        shape={this.state.featureCollection}
        cluster
        clusterRadius={50}
        clusterMaxZoom={16}
      >
        <MapboxGL.SymbolLayer id="pointCount" />
        <MapboxGL.SymbolLayer
          id="clusteredPoints"
          belowLayerID="pointCount"
          filter={['has', 'point_count']}
          style={layerStyles.clusterIcon}
        />
        <MapboxGL.SymbolLayer
          id="singleIcon"
          filter={['!', ['has', 'point_count']]}
          style={layerStyles.icon}
        />
      </MapboxGL.ShapeSource>
@arnaudambro
Copy link
Contributor Author

Can you provide the whole code, markers + user-location so that I can test myself ?

@cesar3030
Copy link

@arnaudambro Thank you very much for your help! I'm getting a step closer to what i'm looking for.

With your solution, user is hidden below the symbol so it can not be seen. What I'm trying to do is to have user on the top of the symbol but the user layer not capturing onPress event that way the layer of the symbol could catch the onPress event.

For know I did set a pulse circleRadius larger than my symbol that way the user can still guess where he is located on the map by seeing a part of the pulse circle. Bad UX but better than seeing nothing 😄

@devthejo
Copy link

devthejo commented Dec 11, 2021

@lmalvasia
Copy link

Hey guys!
Do you have a fix for this?
I'm adding markers to the map when the user clicks on any place but when the user clicks near of the user location icon, the on press function from the mapview component is not called.

ajabeckett added a commit to techmatters/terraso-mobile-client that referenced this issue Oct 20, 2023
There's a known issue with rnmapbox where the blue userlocation circle prevents taps on nearby location markers. The recommended workaround is to override the default userlocation component and define a relevant belowLayerID (in our case, sitesLayer). rnmapbox/maps#241
ajabeckett added a commit to techmatters/terraso-mobile-client that referenced this issue Oct 23, 2023
There's a known issue with rnmapbox where the blue userlocation circle prevents taps on nearby location markers. The recommended workaround is to override the default userlocation component and define a relevant belowLayerID (in our case, sitesLayer). rnmapbox/maps#241
ajabeckett added a commit to techmatters/terraso-mobile-client that referenced this issue Oct 23, 2023
* feat: clear search input

#436

* feat: override mapbox userlocation with custom userlocation

There's a known issue with rnmapbox where the blue userlocation circle prevents taps on nearby location markers. The recommended workaround is to override the default userlocation component and define a relevant belowLayerID (in our case, sitesLayer). rnmapbox/maps#241

* fix: file cleanup

* feat: don't open a second popover when tapping out

* fix: allow taps through mapsearch component

* feat: allow site creation by tapping on user location

Mapbox's default UserLocation onPress handler doesn't have a defined return type, but it does return properties we can conditionally pass through to the existing SiteMap onPress handler. This allows users to open a location popover by tapping on their location if no sites already exist.
@RyanTG
Copy link

RyanTG commented Nov 5, 2023

We could accept a PR with a prop on UserLocation that disables the on press listener

That would be really cool to have a prop for this. I don't think I have the skills to implement it - but I wish.

@RyanTG
Copy link

RyanTG commented Nov 14, 2023

Actually, using renderMode={'native'} seems to resolve this issue in iOS. However, it appears that @mfazekas is going to deprecate that soon.

@Streudal
Copy link

Streudal commented Sep 12, 2024

As a workaround I've used something like this to handle my use case:

async function handleLocationPress() {
  const currentLocation = []; // you current location position (GeoJSON.Position)
  const screenCoordinates = await mapRef.current?.getPointInView(currentLocation);
  const screenPointFeature = feature(
    {
      type: 'Point',
      coordinates: currentLocation
    },
    {
      screenPointX: screenCoordinates?.[0] || 0,
      screenPointY: screenCoordinates?.[1] || 0
    }
  );

  handlePress(screenPointFeature);
}

async function handlePress(feature) {
  const { screenPointX, screenPointY } = feature.properties;
  
  // Note this only works for the following layers: SymbolLayer, CircleLayer, LineLayer, and FillLayer
  const featureCollection = await mapRef.current?.queryRenderedFeaturesAtPoint(
    [screenPointX, screenPointY],
    null,
    [
      ...layerIdsToQueryOn // string[]
    ]
  );

 return featureCollection;
}

// Use
<Mapbox.MapView
  ref={mapRef}
  style={{ flex: 1 }}
  onPress={handlePress}
>
  <UserLocation
    ref={userLocationRef}
    onUpdate={handleUserLocationUpdate}
    minDisplacement={1}
    animated
    visible={locationVisible}
    onPress={handleLocationPress}
  />
  {/* Whatever else ShapeSources and content below */}
</Mapbox.MapView>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
8 participants