rnmapbox / maps

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

[Bug]: HeatmapLayer component doesnt take expression as CircleLayer (same one, but in heatmaplayer wont work) #3551

Closed eybel closed 4 months ago

eybel commented 4 months ago

Mapbox Implementation

Mapbox

Mapbox Version

10.1.27

React Native Version

0.74.2

Platform

iOS

@rnmapbox/maps version

10.0.0-beta.11

Standalone component to reproduce

import React, { useState, useCallback, useEffect, useRef } from "react"; import Mapbox, { Camera, CircleLayer, HeatmapLayer, Images, Image, LocationPuck, MapView, ShapeSource, SymbolLayer, } from "@rnmapbox/maps"; import DogIcon from "@/assets/images/dog-icon.png"; import { FontAwesome6 } from "@expo/vector-icons"; import { StyleSheet, Text, View, Image as RNImage, TouchableOpacity, } from "react-native"; import { useLocation } from "@/components/hooks/location"; import { Tables } from "@/database.types"; import { useFetchLostPets } from "@/api/lost"; import { MapState } from "@rnmapbox/maps/lib/typescript/src/components/MapView"; import { OnPressEvent } from "@rnmapbox/maps/lib/typescript/src/types/OnPressEvent"; import { getThumbnailUrlFromMultimediaUrl } from "@/utils/imageWork"; import { featureCollection, point } from "@turf/helpers";

Mapbox.setAccessToken(process.env.EXPO_PUBLIC_MAPBOX_ACCESS_TOKEN || ""); Mapbox.setTelemetryEnabled(false);

const INITIAL_DEFAULT_REGION = { latitude: -32.94682, longitude: -60.63972, latitudeDelta: 0.2, longitudeDelta: 0.2, };

type PostType = | (Tables<"lost_found"> & { lost_found_multimedia?: { multimedia_url: string }[]; }) | null; type SectionType = "pets" | "medical";

const earthRadius = 6371; // Radius of the Earth in kilometers

// COMPONENT const Map = () => { const { location } = useLocation(); // Assuming this hook provides the user's location

const { data: lostPets = [], error, isLoading, } = useFetchLostPets("Rosario", true);

if (error) { return (

ERROR LOADING MARKERS....
);

} const [selectedMarker, setSelectedMarker] = useState();

const [section, setSection] = useState("pets"); const [zoomLevel, setZoomLevel] = useState(13);

const { latitude = null, longitude = null } = location.coords || {};

const myLocation = { latitude, longitude, latitudeDelta: 0.2, longitudeDelta: 0.2, } || INITIAL_DEFAULT_REGION;

const actualRegion = useRef(myLocation); const mapRef = useRef<Mapbox.MapView | null>(null); // Ref for MapView component const cameraRef = useRef(null); const POSTS_IN_MAP = section === "pets" ? lostPets.filter((pet) => pet.latitude && pet.longitude) : [];

const onMarkerSelected = (event: OnPressEvent) => {

// Check if the press event is on a cluster
const isCluster = !!event?.features[0]?.properties?.cluster;

const clusterCoordinates = event.features[0].geometry.coordinates;
const markerData = POSTS_IN_MAP.find(
  (post) => post.id === Number(event.features[0].id)
);

!isCluster && setSelectedMarker(markerData);

// Increase the multiplier to move the camera downwards more
const latitudeOffset = (myLocation.latitudeDelta / Math.pow(2, 16)) * 300; // Adjust multiplier as needed

// Move camera to cluster coordinates with new zoom level
cameraRef.current?.setCamera({
  centerCoordinate: [
    clusterCoordinates[0],
    clusterCoordinates[1] - (isCluster ? 0 : latitudeOffset),
  ],
  zoomLevel: 16,
  animationDuration: 2000,
});

};

const onVisitMarkerHandler = useCallback(() => { console.log("Visiting marker..."); }, []);

const deltaToKilometers = (delta: number) => Math.floor((delta / 360) * 40075);

const onRegionChangeComplete = (newRegion: MapState) => { setZoomLevel(newRegion.properties.zoom); };

const handleMapReady = useCallback(() => { console.log("Map is ready!"); }, []);

const onChangeCategoryHandler = useCallback( (section: SectionType) => () => { setSelectedMarker(null); setSection(section); }, [setSelectedMarker, setSection] );

const closeSelectedMarker = () => { setSelectedMarker(null); }; const markers = POSTS_IN_MAP.map((marker) => point([marker.longitude!!, marker.latitude!!], { marker }) );

console.log( "🚀 ~ file: MapBox.tsx:242 ~ onMarkerSelected ~ markers==>", markers ); console.log("FEATURE COLLECTION", featureCollection(markers).features[0]); return (

onRegionChangeComplete(newRegion)} onCameraChanged={(newRegion) => onRegionChangeComplete(newRegion)} > {myLocation.longitude && myLocation.latitude && ( <> )} onMarkerSelected(event)} > {selectedMarker && ( {selectedMarker.title} Close )} Mascotas

); };

const styles = StyleSheet.create({ markerTitle: { width: "100%", fontSize: 14, }, marker: { borderWidth: 0, }, markerImageInMap: { width: 30, height: 30, borderRadius: 15, }, selectedMarker: { borderWidth: 1, borderColor: "gray", borderRadius: 25, }, selectedMarkerImageInMap: { width: 50, height: 50, borderRadius: 25, }, areaToShownText: { color: "gray", fontWeight: "600", }, iconFiltersContainer: { flexDirection: "column", backgroundColor: "white", borderRadius: 10, position: "absolute", bottom: 10, left: 10, padding: 10, height: "auto", gap: 10, alignContent: "center", justifyContent: "space-between", }, iconFilterTextBox: { flexDirection: "row", justifyContent: "center", color: "gray", }, iconFilterText: { textAlign: "center", color: "gray", letterSpacing: 1, }, iconFiltersBox: { flexDirection: "row", justifyContent: "space-evenly", gap: 15, paddingHorizontal: 2, height: 30, }, iconFilterDefault: { color: "gray", }, iconFilterSelected: { color: "red", }, selectedMarkerDetails: { position: "absolute", bottom: 90, left: 0, right: 0, backgroundColor: "white", padding: 10, alignItems: "center", borderRadius: 10, }, selectedMarkerImage: { width: 100, height: 100, borderRadius: 10, }, selectedMarkerTitle: { fontSize: 16, fontWeight: "bold", marginTop: 10, }, closeButton: { marginTop: 10, padding: 10, backgroundColor: "red", borderRadius: 5, }, closeButtonText: { color: "white", fontWeight: "bold", }, });

export default Map;

Observed behavior and steps to reproduce

Same expression in CircleLayer (works, because it gets removed when is not a cluster) but in HeatMapLayer is not working and I wanted to use it as a cluster mark.

 This line in HeatmapLayer       **filter={["has", "point_count"]}** also appearing on each marker if its not a cluster.

<ShapeSource id="losts_founds" shape={featureCollection(markers)} cluster={true} clusterRadius={50} clusterMaxZoomLevel={15} onPress={(event) => onMarkerSelected(event)}

<SymbolLayer id="lost_found_clusters_count" filter={["has", "point_count"]} style={{ textField: ["get", "point_count"], textPitchAlignment: "viewport", textIgnorePlacement: true, textAllowOverlap: true, textFont: ["Open Sans Bold", "Arial Unicode MS Bold"], // Specify the font and weight }} /> <HeatmapLayer id="lost_found_clusters_heatmap" belowLayerID="lost_found_clusters_count" filter={["has", "point_count"]} />

      <SymbolLayer
        id="lost_found_markers"
        filter={["!", ["has", "point"]]}
        style={{
          iconSize: 0.25,
          iconAnchor: "bottom",
          iconAllowOverlap: true,
          iconImage: "dog-icon", // Use the feature's icon property if zoom level is greater than 13
        }}

Expected behavior

not to show the heatmaplayer if is not a cluster.

Notes / preliminary analysis

No response

Additional links and references

No response

github-actions[bot] commented 4 months ago

No code example found in issue body - More info