cmapper or 8xplorer like smooth map clustering library for both iOS and Android.
- install dependency
yarn add react-native-maps
yarn add supercluster
yarn add @mapbox/geo-viewport
- install library
yarn add https://github.com/yoshidan/react-native-map-cluster
Complete examples is Here
There are only keypoint in this README.
- use
withAnimatedCluster
- use
onRegionChanged
for MapView#onRegionChangeComplete
import React, {Fragment} from 'react';
import {Dimensions,Image,SafeAreaView,StatusBar,StyleSheet,Text,View } from 'react-native';
import { AnimatedMarker, withAnimatedCluster } from '@yoshidan/react-native-map-cluster';
import MapView, {MarkerAnimated} from 'react-native-maps';
import Supercluster from 'supercluster';
const {width, height} = Dimensions.get('window');
const Component = withAnimatedCluster({
moveSpeed: 600,
deltaOffset: 1.3,
width,
height,
superClusterProvider: () =>new Supercluster(),
})(class Map extends React.Component<Props, State> {
// render map
public render() {
const { initialRegion } = this.props;
// here is the property that the HoC injects.
const {animatedMarkers, onRegionChanged} = this.props;
return (
<MapView
// recreate cluster when the region changes
onRegionChangeComplete={onRegionChanged}
initialRegion={initialRegion}
style={styles.map}>
// render markers
{animatedMarkers.map(this.renderMarker)}
</MapView>
);
}
}
- Use wrapped component with required props
markers
andinitialRegion
const App = () => {
return (
<Fragment>
<StatusBar barStyle="dark-content" />
<SafeAreaView style={{flex: 1}}>
<Component
// {id , coorinates: { latitude, longitude}} is required for each markers
markers={markers}
initialRegion={{
...markers[0].coordinate,
latitudeDelta: 0,
longitudeDelta: 0,
}}
/>
</SafeAreaView>
</Fragment>
);
};
Name | Type | Default | Note |
---|---|---|---|
moveSpeed | number | 600 | the animating speed of split or synthesize cluster |
deltaOffset | number | 1.3 | the value to suppress marker spreading to the outside of the window when splitting cluster. Set smaller value if the icon is small or bigger value if the icon is big. |
width | number | null | dimension width of map |
height | number | null | dimension height of map |
superClusterProvider | () => Supercluster | null | the function to create the Supercluster |
Name | Type | Note |
---|---|---|
markers | Marker[] | Markers to display on the MapView |
initialRegion | Region | initial region of MapView |
type Marker = {
coordinate: Region;
id: number | string
}
Name | Type | Note |
---|---|---|
animatedMarkers | AnimatedMarkers[] | Converted to markers to animate. render this markers |
region | Region | Current region |
clusters | Cluster[] | Current clusters |
onRegionChanged | (region:Region) => void | function to cluster markers with current region. Set as MapView#onRegionChangeCompleted |
type AnimatedMarkers = {
coordinate: AnimatedRegion;
id: number | string;
getCluster: (clusters: Cluster[]) => Cluster | undefined;
}
type Clusters = {
properties: {
point_count: number // count of markers in this cluster
},
userExtension: {
getCenterPosition: () => Region;
}
}
getCenterPosition
is required for splitting cluster on marker pressed
renderMarker(marker: AnimatedMarker) {
const {clusters, region} = this.props;
const currentCluster = marker.getCluster(clusters);
const markersInClusterCount = currentCluster ? currentCluster.properties.point_count : 0;
return (
<MarkerAnimated
key={marker.id}
coordinate={marker.coordinate}
// split clster when markers is pressed.
onPress={() => markersInClusterCount && this.map.animateToRegion(currentCluster.userExtension.getCenterPosition()}>
<...>
</MarkerAnimated>
);
}