novalabio/react-native-maps-super-cluster

Bad performance when Cluster Styling added

Closed this issue · 1 comments

Im loading some Markers from the Overpass API (OpenStreetMaps). When showing Markers and Clusters as default red markers the performance is really good:

import React, { Component } from 'react'
import {
  Text,
  View,
  Image,
  StyleSheet,
  SafeAreaView,
  TouchableOpacity,
} from 'react-native'
import { Marker, Callout } from 'react-native-maps'
import ClusteredMapView from 'react-native-maps-super-cluster'

export const getBoundByRegion = (region, scale = 1) => {
  /*
  * Latitude : max/min +90 to -90
  * Longitude : max/min +180 to -180
  */
  // Of course we can do it mo compact but it wait is more obvious
  const calcMinLatByOffset = (lng, offset) => {
    const factValue = lng - offset;
    if (factValue < -90) {
      return (90 + offset) * -1;
    }
    return factValue;
  };

  const calcMaxLatByOffset = (lng, offset) => {
    const factValue = lng + offset;
    if (90 < factValue) {
      return (90 - offset) * -1;
    }
    return factValue;
  };

  const calcMinLngByOffset = (lng, offset) => {
    const factValue = lng - offset;
    if (factValue < -180) {
      return (180 + offset) * -1;
    }
    return factValue;
  };

  const calcMaxLngByOffset = (lng, offset) => {
    const factValue = lng + offset;
    if (180 < factValue) {
      return (180 - offset) * -1;
    }
    return factValue;
  };

  const latOffset = region.latitudeDelta / 2 * scale;
  const lngD = (region.longitudeDelta < -180) ? 360 + region.longitudeDelta : region.longitudeDelta;
  const lngOffset = lngD / 2 * scale;

  return {
    minLng: calcMinLngByOffset(region.longitude, lngOffset), // westLng - min lng
    minLat: calcMinLatByOffset(region.latitude, latOffset), // southLat - min lat
    maxLng: calcMaxLngByOffset(region.longitude, lngOffset), // eastLng - max lng
    maxLat: calcMaxLatByOffset(region.latitude, latOffset),// northLat - max lat
  }
}

export default class MyClusteredMapView extends Component {
  constructor(props) {
    super(props);
  
    this.state = {
      data: [],
      isLoading: true,
      region: {
        latitude: 50.551085251254634,
        latitudeDelta: 0.005160252144293054,
        longitude: 9.676355849951506,
        longitudeDelta: 0.003820471465587616,
      }
    };
  }

  onRegionChange(region) {
    this.setState({
        region: region
    });
    //console.log(region)
  }

  async getNewBenches(bounds) {
    try {
     const response = await fetch("https://overpass.openstreetmap.fr/api/interpreter?data=[out:json];node[%27amenity%27=%27bench%27](" + bounds.minLat +"," + bounds.minLng + "," + bounds.maxLat + "," + bounds.maxLng + ");out%20body;");
     const json = await response.json();
     
     var markers = []
        for(var i = 0; i < json.elements.length; i++) {
          //console.log(json.elements[i])
  
          var mymarker =  {
            id: json.elements[i].id ,
            location: { latitude : json.elements[i].lat , longitude : json.elements[i].lon },
            tags:  json.elements[i].tags, 
          }
         markers.push(mymarker)
        }
        console.log(markers)
          
        this.setState({ data: markers });
  
   } catch (error) {
     console.error(error);
   } finally {
   }
  }

  renderCluster = (cluster, onPress) => {
    const pointCount = cluster.pointCount,
          coordinate = cluster.coordinate,
          clusterId = cluster.clusterId

    // use pointCount to calculate cluster size scaling
    // and apply it to "style" prop below

    // eventually get clustered points by using
    // underlying SuperCluster instance
    // Methods ref: https://github.com/mapbox/supercluster
    const clusteringEngine = this.map.getClusteringEngine(),
          clusteredPoints = clusteringEngine.getLeaves(clusterId, 100)

    return (
      <Marker coordinate={coordinate} onPress={onPress}>
        
        {
          /*
            Eventually use <Callout /> to
            show clustered point thumbs, i.e.:
            <Callout>
              <ScrollView>
                {
                  clusteredPoints.map(p => (
                    <Image source={p.image}>
                  ))
                }
              </ScrollView>
            </Callout>

            IMPORTANT: be aware that Marker's onPress event isn't really consistent when using Callout.
            */
        }
      </Marker>
    )
  }

  renderMarker = (data) => <Marker key={data.id || Math.random()} coordinate={data.location} /> 
  
  render() {
    return (
      <ClusteredMapView
        style={{flex: 1}}
        data={this.state.data}
        initialRegion={{
          latitude: 50.551085251254634,
          latitudeDelta: 0.005160252144293054,
          longitude: 9.676355849951506,
          longitudeDelta: 0.003820471465587616,
        }}
        onRegionChangeComplete={ (region) => {
            this.onRegionChange(region);
            this.getNewBenches(getBoundByRegion(region));
          }
        }
        ref={(r) => { this.map = r }}
        renderMarker={this.renderMarker}
        renderCluster={this.renderCluster} />
    )
  }
}

but when adding custom styles the performance is slow.

import React, { Component } from 'react'
import {
  Text,
  View,
  Image,
  StyleSheet,
  SafeAreaView,
  TouchableOpacity,
} from 'react-native'
import { Marker, Callout } from 'react-native-maps'
import ClusteredMapView from 'react-native-maps-super-cluster'

export const getBoundByRegion = (region, scale = 1) => {
  /*
  * Latitude : max/min +90 to -90
  * Longitude : max/min +180 to -180
  */
  // Of course we can do it mo compact but it wait is more obvious
  const calcMinLatByOffset = (lng, offset) => {
    const factValue = lng - offset;
    if (factValue < -90) {
      return (90 + offset) * -1;
    }
    return factValue;
  };

  const calcMaxLatByOffset = (lng, offset) => {
    const factValue = lng + offset;
    if (90 < factValue) {
      return (90 - offset) * -1;
    }
    return factValue;
  };

  const calcMinLngByOffset = (lng, offset) => {
    const factValue = lng - offset;
    if (factValue < -180) {
      return (180 + offset) * -1;
    }
    return factValue;
  };

  const calcMaxLngByOffset = (lng, offset) => {
    const factValue = lng + offset;
    if (180 < factValue) {
      return (180 - offset) * -1;
    }
    return factValue;
  };

  const latOffset = region.latitudeDelta / 2 * scale;
  const lngD = (region.longitudeDelta < -180) ? 360 + region.longitudeDelta : region.longitudeDelta;
  const lngOffset = lngD / 2 * scale;

  return {
    minLng: calcMinLngByOffset(region.longitude, lngOffset), // westLng - min lng
    minLat: calcMinLatByOffset(region.latitude, latOffset), // southLat - min lat
    maxLng: calcMaxLngByOffset(region.longitude, lngOffset), // eastLng - max lng
    maxLat: calcMaxLatByOffset(region.latitude, latOffset),// northLat - max lat
  }
}

export default class MyClusteredMapView extends Component {
  constructor(props) {
    super(props);
  
    this.state = {
      data: [],
      isLoading: true,
      region: {
        latitude: 50.551085251254634,
        latitudeDelta: 0.005160252144293054,
        longitude: 9.676355849951506,
        longitudeDelta: 0.003820471465587616,
      }
    };
  }

  onRegionChange(region) {
    this.setState({
        region: region
    });
    //console.log(region)
  }

  async getNewBenches(bounds) {
    try {
     const response = await fetch("https://overpass.openstreetmap.fr/api/interpreter?data=[out:json];node[%27amenity%27=%27bench%27](" + bounds.minLat +"," + bounds.minLng + "," + bounds.maxLat + "," + bounds.maxLng + ");out%20body;");
     const json = await response.json();
     
     var markers = []
        for(var i = 0; i < json.elements.length; i++) {
          //console.log(json.elements[i])
  
          var mymarker =  {
            id: json.elements[i].id ,
            location: { latitude : json.elements[i].lat , longitude : json.elements[i].lon },
            tags:  json.elements[i].tags, 
          }
         markers.push(mymarker)
        }
        console.log(markers)
        this.setState({ data: markers });
  
   } catch (error) {
     console.error(error);
   } finally {
   }
  }

  renderCluster = (cluster, onPress) => {
    const pointCount = cluster.pointCount,
          coordinate = cluster.coordinate,
          clusterId = cluster.clusterId

    // use pointCount to calculate cluster size scaling
    // and apply it to "style" prop below

    // eventually get clustered points by using
    // underlying SuperCluster instance
    // Methods ref: https://github.com/mapbox/supercluster
    const clusteringEngine = this.map.getClusteringEngine(),
          clusteredPoints = clusteringEngine.getLeaves(clusterId, 100)

    return (
      <Marker coordinate={coordinate} onPress={onPress}>
        <View style={styles.clusterContainer}>
          <Text style={styles.clusterText}>
            {pointCount}
          </Text>
        </View>
        {
          /*
            Eventually use <Callout /> to
            show clustered point thumbs, i.e.:
            <Callout>
              <ScrollView>
                {
                  clusteredPoints.map(p => (
                    <Image source={p.image}>
                  ))
                }
              </ScrollView>
            </Callout>

            IMPORTANT: be aware that Marker's onPress event isn't really consistent when using Callout.
            */
        }
      </Marker>
    )
  }

  renderMarker = (data) => <Marker key={data.id || Math.random()} coordinate={data.location} /> 
  
  render() {
    return (
      <ClusteredMapView
        style={{flex: 1}}
        data={this.state.data}
        initialRegion={{
          latitude: 50.551085251254634,
          latitudeDelta: 0.005160252144293054,
          longitude: 9.676355849951506,
          longitudeDelta: 0.003820471465587616,
        }}
        onRegionChangeComplete={ (region) => {
            this.onRegionChange(region);
            this.getNewBenches(getBoundByRegion(region));
          }
        }
        ref={(r) => { this.map = r }}
        renderMarker={this.renderMarker}
        renderCluster={this.renderCluster} />
    )
  }
}

const styles = StyleSheet.create({
  clusterContainer: {
    width: 30,
    height: 30,
    padding: 6,
    borderWidth: 1,
    borderRadius: 15,
    alignItems: 'center',
    borderColor: '#65bc46',
    justifyContent: 'center',
    backgroundColor: 'white',
  },
  clusterText: {
    fontSize: 13,
    color: '#65bc46',
    fontWeight: '500',
    textAlign: 'center',
  }
})

maybe this is a know issue and someone can provide help

this issue still exists