Skip to content

Instantly share code, notes, and snippets.

@kristfal
Created June 18, 2018 11:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save kristfal/9b1af1a381c1d57fe8dab1993cb1ad62 to your computer and use it in GitHub Desktop.
Save kristfal/9b1af1a381c1d57fe8dab1993cb1ad62 to your computer and use it in GitHub Desktop.
Mapbox camera snippets
const cameraValid = camera =>
camera && camera.geometry && Array.isArray(camera.geometry.coordinates);
// Workaround for Mapbox issue
// https://github.com/mapbox/react-native-mapbox-gl/issues/1126
export const enforceIntegerPadding = padding =>
padding.map(value => parseInt(value, 10));
export const isValidBounds = camera =>
camera &&
camera.bounds &&
Array.isArray(camera.bounds) &&
Array.isArray(camera.bounds[0]) &&
Array.isArray(camera.bounds[1]) &&
isNumeric(camera.bounds[0][0]) &&
isNumeric(camera.bounds[0][1]) &&
isNumeric(camera.bounds[1][1]) &&
isNumeric(camera.bounds[1][1]);
export const isCameraChanged = (a, b) => {
if (!a || !b) return true;
if (a.isInitial) return true;
const defaultPropertiesChanged =
a.bearing !== b.bearing ||
a.center[0] !== b.center[0] ||
a.center[1] !== b.center[1] ||
a.pitch !== b.pitch ||
a.zoom !== b.zoom ||
a.userInitiated !== b.userInitiated;
if (!isValidBounds(a) || !isValidBounds(b)) return defaultPropertiesChanged;
return (
defaultPropertiesChanged ||
a.bounds[0][0] !== b.bounds[0][0] ||
a.bounds[0][1] !== b.bounds[0][1] ||
a.bounds[1][0] !== b.bounds[1][0] ||
a.bounds[1][1] !== b.bounds[1][1]
);
};
export const isInstantCameraChange = (a, b) => {
if (!a || !b) return true;
if (a.isInstant) return true;
const dZ = 7;
const dD = 5;
if (Math.abs(a.zoom - b.zoom) > dZ) return true;
if (Math.abs(a.center[0] - b.center[0]) > dD) return true;
if (Math.abs(a.center[1] - b.center[1]) > dD) return true;
return false;
};
export const isSignificantChange = (nextCamera, camera) => {
const coordDelta = SIGNIFICANT_COORDINATE_DELTA;
const zoomDelta = SINGIFICANT_ZOOM_DELTA;
if (!cameraValid(camera) || !cameraValid(nextCamera)) return true;
const deltaLat =
Math.abs(
nextCamera.geometry.coordinates[1] - camera.geometry.coordinates[1],
) > coordDelta;
const deltaLng =
Math.abs(
nextCamera.geometry.coordinates[0] - camera.geometry.coordinates[0],
) > coordDelta;
const deltaZoom =
Math.abs(nextCamera.properties.zoomLevel - camera.properties.zoomLevel) >
zoomDelta;
return deltaLat || deltaLng || deltaZoom;
};
class EmbarkMapMapbox extends PureComponent {
static propTypes = {
pitchEnabled: PropTypes.bool,
rotateEnabled: PropTypes.bool,
scrollEnabled: PropTypes.bool,
panResponderEnabled: PropTypes.bool,
zoomLevel: PropTypes.number,
onPress: PropTypes.func,
onLongPress: PropTypes.func,
onRegionDidChange: PropTypes.func,
textureMode: PropTypes.bool,
padding: PropTypes.array,
minZoomLevel: PropTypes.number,
onPanResponderMove: PropTypes.func,
onPanResponderRelease: PropTypes.func,
style: ViewPropTypes.style,
children: PropTypes.node,
camera: PropTypes.object,
initialCamera: PropTypes.object,
onDidFinishLoadingMap: PropTypes.func,
onAttribution: PropTypes.func,
testID: PropTypes.string,
};
static defaultProps = {
pitchEnabled: false,
rotateEnabled: true,
scrollEnabled: true,
padding: [15, 15, 174, 15],
hitSlop: EMBARK_MAP_HITSLOP,
};
constructor() {
super();
this.onWillStartLoadingMap = this.onWillStartLoadingMap.bind(this);
}
componentWillMount() {
MapboxGL.setAccessToken(MAPBOX_ACCESSTOKEN);
}
onWillStartLoadingMap() {
const {camera, initialCamera, padding} = this.props;
const derivedCamera = initialCamera || camera;
this.handleCameraChange(
{
...derivedCamera,
userInitiated: false,
isInstant: true,
isInitial: true,
},
padding,
);
}
componentWillReceiveProps({camera, padding}) {
this.handleCameraChange(camera, padding);
}
handleCameraChange(camera, padding) {
if (!camera) return;
const currentCamera = this.props.camera;
const changed = isCameraChanged(camera, currentCamera);
const instant = isInstantCameraChange(camera, currentCamera);
const validBounds = isValidBounds(camera);
const duration = instant ? 1 : EMBARK_MAP_ANIMATION_DURATION;
if (!changed || camera.userInitiated) return;
if (validBounds) {
this.setCameraWithBounds(camera, padding, duration);
} else {
this.setCameraWithCenter(camera, duration);
}
}
setCameraWithBounds(camera, padding, duration) {
this.map.fitBounds(
camera.bounds[0],
camera.bounds[1],
enforceIntegerPadding(padding),
duration,
);
}
setCameraWithCenter(camera, duration) {
this.map.setCamera({
centerCoordinate: camera.center,
zoom: camera.zoom,
pitch: camera.pitch,
heading: camera.bearing,
duration,
});
}
render() {
const {
pitchEnabled,
rotateEnabled,
scrollEnabled,
textureMode,
minZoomLevel,
style,
onDidFinishLoadingMap,
testID,
} = this.props;
return (
<View style={[styles.wrapper, style]}>
<MapboxGL.MapView
ref={ref => (this.map = ref)}
textureMode={textureMode}
attributionEnabled={false}
compassEnabled={false}
showUserLocation={false}
logoEnabled={false}
minZoomLevel={minZoomLevel}
maxZoomLevel={17}
zoomLevel={0}
contentInset={[90, 15, 15, 15]}
onWillStartLoadingMap={this.onWillStartLoadingMap}
style={styles.map}
styleURL={API.EMBARK_STYLE_URL}
pitchEnabled={pitchEnabled}
rotateEnabled={rotateEnabled}
scrollEnabled={scrollEnabled}
testID={testID}
>
{this.props.children}
</MapboxGL.MapView>
</View>
);
}
}
export default EmbarkMapMapbox;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment