React native maps fitToCoordinates with heading?

Viewed 654

I’m using Expo and React Native Maps and I’m trying to figure out how to have 2 points, an origin and a destination, then orientate the map so that the destination is at the top and the origin is at the bottom. I figured out how to calculate the heading and set that using setCamera. But now I want the map to fit to the map to the origin and destination points, but the problem is when I call fitToCoordinates, the map resets to a heading of 0. Does anyone know how to do something like fitToCoordinates with a heading?

Here's the code:

import React, { useRef } from 'react';
import { Dimensions, StyleSheet, View } from 'react-native';
import { StatusBar } from 'expo-status-bar';
import MapView from 'react-native-maps';

// Converts from degrees to radians.
function toRadians(degrees) {
  return (degrees * Math.PI) / 180;
}

// Converts from radians to degrees.
function toDegrees(radians) {
  return (radians * 180) / Math.PI;
}

function getHeading(origin, destination) {
  const originLat = toRadians(origin.latitude);
  const originLng = toRadians(origin.longitude);
  const destLat = toRadians(destination.latitude);
  const destLng = toRadians(destination.longitude);

  const y = Math.sin(destLng - originLng) * Math.cos(destLat);
  const x =
    Math.cos(originLat) * Math.sin(destLat) -
    Math.sin(originLat) * Math.cos(destLat) * Math.cos(destLng - originLng);
  const heading = toDegrees(Math.atan2(y, x));
  return (heading + 360) % 360;
}

const origin = { latitude: 47.58991363446258, longitude: -122.24398815305646 };
const destination = {
  latitude: 47.596003585463656,
  longitude: -122.32888303102513,
};
const center = {
  latitude: (origin.latitude + destination.latitude) / 2,
  longitude: (origin.longitude + destination.longitude) / 2,
};
const latitudeDelta =
  Math.abs(origin.latitude - destination.latitude) + 0.00025;
const longitudeDelta =
  Math.abs(origin.longitude - destination.longitude) + 0.00025;
const heading = getHeading(origin, destination);

export default function App() {
  const mapRef = useRef(null);

  console.log('origin', origin);
  console.log('destination', destination);
  console.log('center', center);
  console.log('latitudeDelta', latitudeDelta);
  console.log('longitudeDelta', longitudeDelta);
  console.log('heading', heading);

  return (
    <View style={styles.container}>
      <MapView
        style={styles.mapStyle}
        ref={mapRef}
        initialRegion={{
          latitude: center.latitude,
          longitude: center.longitude,
          latitudeDelta,
          longitudeDelta,
        }}
        onMapReady={() => {
          console.log('mapReady...');

          // This orientates the map with the origin on bottom and destination on top
          // You can see that when the map first loads,
          // but the map isn't fit to the points
          mapRef.current.setCamera({ center, heading });

          // 5 seconds after the map loads,
          // this runs and does fit the map to the points,
          // but it sets the heading back to 0
          setTimeout(() => {
            console.log('fitting to coordinates');
            mapRef.current.fitToCoordinates([origin, destination], {
              animated: false,
            });
          }, 5000);
        }}
        scrollEnabled={true}
      />
      <StatusBar style='auto' />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
  mapStyle: {
    width: Dimensions.get('window').width,
    height: Dimensions.get('window').height,
  },
});
1 Answers

you need to be sure to fit both coords and later set the camera. .fit... methods dont have callbacks so, either you do the first and redender and back there you setCamera or, you setTimeout to be sure both methods are not redrawing the map at the same time.

Related