首页 > 解决方案 > 如何实施协助用户准确导航到任何地方的特定位置的最短路径

问题描述

1.需要显示用户当前位置 2.OnSelect 特定目的地,需要显示进度线 3.在用户向目的地移动时,地图标记应与用户一起移动。

标签: react-nativemapbox

解决方案


您好,请查看以下链接中的完整代码:

https://snack.expo.io/@matheus_cbrl/maps-geoloation-uber

下面是一些代码:

import React, { Component, Fragment } from "react";
import {
  Platform,
  StyleSheet,
  Text,
  View,
  Alert,
  ScrollView,
  Image,
  Dimensions,
  TouchableOpacity,
  StatusBar,
  AsyncStorage,
  BackHandler,
} from 'react-native'; 
import { getPixelSize } from '../utils';
import MapView, { Marker } from "react-native-maps";
import Geocoder from "react-native-geocoding";
import markerImage2 from '../assets/bus-stop(1).png';
import markerImage from '../assets/placeholder(2).png';
import AwesomeAlert from 'react-native-awesome-alerts';
import night from '../assets/placeholder(2).png';
import { FAB } from 'react-native-paper';
import Modal from 'react-native-modal';
import { ContainerTop, TypeDescription, ContainerTab } from './stylesInfo.js';
import Search from './Search/map-input.js';
import Directions from '../Directions';
import Details from "../Details";

import {
  Back,
  LocationBox,
  LocationText,
  LocationTimeBox,
  LocationTimeText,
  LocationTimeTextSmall
} from "./styles.js";

Geocoder.init(" ");

const { width, height } = Dimensions.get('window');
const ASPECT_RATIO = width / height;
const LATITUDE = -29.1684796;
const LONGITUDE = -51.1793861;
const LATITUDE_DELTA = 0.003;
const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO;
export default class Map extends Component {
  state = {
    focusedlocation: {
      latitude: LATITUDE,
      longitude: LONGITUDE,
      latitudeDelta: LATITUDE_DELTA,
      longitudeDelta: LONGITUDE_DELTA,
    },
    isLoading: true,
    markers: [],
    modalVisible: false,
    locationChosen: false,
    showAlert: false,
    initialized: false,
    destination: null,
    duration: null,
    location: null,
    region: null,
  };

  async componentDidMount() {
    navigator.geolocation.getCurrentPosition(
      async ({ coords: { latitude, longitude } }) => {
        const response = await Geocoder.from({ latitude, longitude });
        const address = response.results[0].formatted_address;
        const location = address.substring(0, address.indexOf(","));

        this.setState({
          location,
          region: {
            latitude,
            longitude,
            latitudeDelta: 0.0143,
            longitudeDelta: 0.0134
          }
        });
      }, //sucesso
      () => {}, //erro
      {
        enableHighAccuracy: true, // allows you to get the most accurate location
        timeout: 2000, // (milliseconds) in which the app has to wait for location before it throws an error
        maximumAge: 1000, // (milliseconds) if a previous location exists in the cache, how old for it to be considered acceptable
        distanceFilter: 20, // (meters) how many meters the user has to move before a location update is triggered
      }
    );
     this.getLocationHandler();
    //BackHandler.addEventListener(
      //'hardwareBackPress',
      //this.backpress, 
      //this.backHandler
    //);
    return fetch(
      'https://gist.githubusercontent.com/MatheusCbrl/bba7db1c0dbc68be2f26d5c7e15649b6/raw/9d10bab16612a0055fff0f14b012e68083e27033/ParadasDiurno.json'
    )
      .then(response => response.json())
      .then(responseJson => {
        // just setState here e.g.
        this.setState({
          markers: responseJson,
          isLoading: false,
        });
      })
      .catch(error => {
        console.error(error);
      });
  }

  onMapReady = () => {
    this.setState({ initialized: true });
  };
  backpress = () => {
    this.setState({ modalVisible: !this.state.modalVisible });
    return true;
  };
  backHandler = () => {
    BackHandler.exitApp();
  };

  handleLocationSelected = (data, { geometry }) => {
    const {
      location: { lat: latitude, lng: longitude }
    } = geometry;

    this.setState({
      destination: {
        latitude,
        longitude,
        title: data.structured_formatting.main_text
      }
    });
  };

  handleBack = () => {
    this.setState({ destination: null });
  };
    pickLocationHandler = event => {
    const coords = event.nativeEvent.coordinate;
    console.log('Location picker Marker', coords);
    this.mapView.animateToRegion({
      ...this.state.region,
      latitude: coords.latitude,
      longitude: coords.longitude,
      latitudeDelta: LATITUDE_DELTA,
      longitudeDelta: LONGITUDE_DELTA,
    });

    this.setState(prevState => {
      return {
        region: {
          ...prevState.region,
          latitude: coords.latitude,
          longitude: coords.longitude,
        },
        locationChosen: true,
      };
    });
  };

  getLocationHandler = () => {
    navigator.geolocation.getCurrentPosition(
      pos => {
        const coordsEvent = {
          nativeEvent: {
            coordinate: {
              latitude: pos.coords.latitude,
              longitude: pos.coords.longitude,
              latitudeDelta: LATITUDE_DELTA,
              longitudeDelta: LONGITUDE_DELTA,
            },
          },
        };

        this.pickLocationHandler(coordsEvent);
      },
      err => {console.log(err);
        //Alert.alert(' ','Ocorreu um erro ao carregar sua localização, por favor ligue o GPS!');
      },
      {
        enableHighAccuracy: true, // allows you to get the most accurate location
        timeout: 20000, // (milliseconds) in which the app has to wait for location before it throws an error
        maximumAge: 1000, // (milliseconds) if a previous location exists in the cache, how old for it to be considered acceptable
        distanceFilter: 20, // (meters) how many meters the user has to move before a location update is triggered
      }
    );
  };
  showAlert = () => {
    this.setState({
      showAlert: true,
    });
  };

  hideAlert = () => {
    this.setState({
      showAlert: false,
    });
  };
   renderMarkers() {
    return this.state.isLoading
      ? null
      : this.state.markers.map((marker, index) => {
          const coords = {
            latitude: JSON.parse(marker.latitude),
            longitude: JSON.parse(marker.longitude),
          };
          return (
          <Marker
              onPress={this.pickLocationHandler}
              ref={mark => (marker.mark = mark)}
              key={index}
              title={'Parada'}
              description={marker.hora}
              tracksViewChanges={!this.state.initialized}
              {...this.props}
              pinColor={'tomato'}
              coordinate={coords}>
              {this.props.children}
          </Marker>
      );
    });
  }
  _logout = async () => {
    await AsyncStorage.clear();
    this.props.navigation.navigate('Menu');
  };
  render() {
    const {showAlert, region, destination, duration, location } = this.state;

    return (
      <View style={styles.container}>
      <StatusBar hidden />
        <MapView
            rotateEnabled={true}
            scrollEnabled={true}
            showsMyLocationButton={true}
            zoomTapEnabled={true}
            zoomControlEnabled={true}   
            zoomEnabled={true}
            showsPointsOfInterest={true}
            showBuildings={false}
            followsUserLocation={true}
            showsUserLocation={true}
            clustering={true}
            showsTraffic={false}
            showsIndoors={false}
            loadingEnabled={true}
            apType="standard"
            provider="google"
            onMapReady={this.onMapReady}
            style={styles.map}
            //region={region}
            initialRegion={this.state.focusedlocation}
            ref={ref => (this.mapView = ref)}

        >
        {this.renderMarkers()}
          {destination && (
            <Fragment>
              <Directions 
                origin={region}
                destination={destination}
                onReady={result => {
                  this.setState({ duration: Math.floor(result.duration) });

                  this.mapView.fitToCoordinates(result.coordinates, {
                    edgePadding: {
                        right: getPixelSize(50),
                        left: getPixelSize(50),
                        top: getPixelSize(50),
                        bottom: getPixelSize(200),
                    }
                  });
                }}
              />
              <Marker
                onPress={this.pickLocationHandler}
                coordinate={destination}
                anchor={{ x: 0, y: 0 }}
              >
                <LocationBox>
                  <LocationText>{destination.title}</LocationText>
                </LocationBox>
              </Marker>

              <Marker coordinate={region} anchor={{ x: 0, y: 0 }}>
                <LocationBox>
                  <LocationTimeBox>
                    <LocationTimeText>{duration}</LocationTimeText>
                    <LocationTimeTextSmall>MIN</LocationTimeTextSmall>
                  </LocationTimeBox>
                  <LocationText>{location}</LocationText>
                </LocationBox>
              </Marker>
            </Fragment>
          )}
        </MapView>

        {destination ? (
          <Fragment>
            <Back onPress={this.handleBack}>
            <Image
                  source={require('../assets/back.png')}
                  style={{
                    marginLeft: 1,
                    marginTop: 1,
                    width: 25,
                    height: 25,
                    tintColor: '#FF3D00',
                  }}
                />
            </Back>
          </Fragment>
        ) : (
          <Search onLocationSelected={this.handleLocationSelected} />
        )}
            <AwesomeAlert
            show={showAlert}
            showProgress={false}
            title="Paradas Diurno"
            message="Veja as paradas próximas de você"
            closeOnTouchOutside={true}
            closeOnHardwareBackPress={false}
            showCancelButton={false}
            showConfirmButton={true}
            cancelText="Tudo Bem"
            confirmText="OK"
            confirmButtonColor="#FF3D00"
            onCancelPressed={() => {
              this.hideAlert();
            }}
            onConfirmPressed={() => {
              this.hideAlert();
            }}
          />
          <ContainerTop>
            <TypeDescription> </TypeDescription>
          </ContainerTop>
          <ContainerTab>
            <View style={styles.appbar}>
              <TouchableOpacity
                onPress={() => this.setState({ isVisible: true })}
                style={{ backgroundColor: 'transparent' }}>
                <Image
                  source={require('../assets/menu-button.png')}
                  style={{
                    width: 25,
                    height: 25,
                    marginLeft: 10,
                    marginTop: 5,
                    tintColor: '#FF3D00',
                  }}
                />
                <Text
                  style={{
                    marginBottom: 15,
                    marginLeft: 6,
                    fontSize: 12,
                    color: '#FF3D00',
                  }}>
                  Menu
                </Text>
              </TouchableOpacity>
              <View>
                <TouchableOpacity style={{ backgroundColor: 'transparent' }}>
                  <Image
                    source={require('../assets/information.png')}
                    style={{
                      width: 25,
                      height: 25,
                      marginLeft: 25,
                      marginTop: 5,
                      tintColor: '#FF3D00',
                    }}
                  />
                  <Text
                    style={{
                      marginBottom: 15,
                      marginLeft: 20,
                      fontSize: 12,
                      color: '#FF3D00',
                    }}>
                    Ajuda
                  </Text>
                </TouchableOpacity>
              </View>
              <View>
                <TouchableOpacity style={{ backgroundColor: 'transparent' }}>
                  <Image
                    source={require('../assets/location(1).png')}
                    style={{
                      width: 25,
                      height: 25,
                      marginLeft: 35,
                      marginTop: 5,
                      tintColor: '#FF3D00',
                    }}
                  />
                  <Text
                    style={{
                      marginBottom: 15,
                      marginLeft: 28,
                      fontSize: 12,
                      color: '#FF3D00',
                    }}>
                    Linhas
                  </Text>
                </TouchableOpacity>
              </View>
            </View>
          </ContainerTab>
          <FAB
            style={styles.fab}
            icon={night}
            theme={{ colors: { accent: '#FF3D00' } }}
          />
          <TouchableOpacity
            onPress={this.getLocationHandler}
            style={styles.fab2}>
            <Image
              source={require('../assets/gps.png')}
              style={{
                width: 35,
                height: 35,
                margin: 10,
                tintColor: '#FF3D00',
              }}
            />
          </TouchableOpacity>
          <Modal
            isVisible={this.state.isVisible}
            style={styles.modalSwipe}
            onBackButtonPress={() => this.setState({ isVisible: false })}
            onBackdropPress={() => this.setState({ isVisible: false })}
            onSwipeComplete={() => this.setState({ isVisible: false })}
            onSwipeThreshold={20}
            swipeDirection="down">
            <View style={{ flex: 1, backgroundColor: '#e5e5e5' }}>
              <ScrollView showsVerticalScrollIndicator={false}>
                <Text style={styles.paragraphSwipe}>Menu</Text>
                <TouchableOpacity>
                  <Text style={styles.buttonStyleSwipe}>
                    {' '}
                    Linhas Saída Diurno{' '}
                  </Text>
                </TouchableOpacity>
                <TouchableOpacity>
                  <Text style={styles.buttonStyleSwipe}>
                    {' '}
                    Linhas Saída Noturno{' '}
                  </Text>
                </TouchableOpacity>
                <TouchableOpacity>
                  <Text style={styles.buttonStyleSwipe}>
                    {' '}
                    Linhas Saída Terceiro e Quarto Turnos{' '}
                  </Text>
                </TouchableOpacity>
                <TouchableOpacity>
                  <Text style={styles.buttonStyleSwipe}> Tutorial </Text>
                </TouchableOpacity>
                <TouchableOpacity>
                  <Text style={styles.buttonStyleSwipe}> Sobre </Text>
                </TouchableOpacity>
                <TouchableOpacity>
                  <Text style={styles.buttonStyleSwipe}> Sair </Text>
                </TouchableOpacity>
              </ScrollView>
            </View>
          </Modal>
          <Modal
            animationType="slide"
            transparent={false}
            visible={this.state.modalVisible}>
            <View
              style={{
                flex: 1,
                justifyContent: 'center',
                alignItems: 'center',
              }}>
              <View
                style={{
                  width: 300,
                  height: 120,
                  backgroundColor: '#FFF',
                  padding: 20,
                  borderRadius: 20,
                  alignItems: 'center',
                  justifyContent: 'center',
                }}>
                <Text
                  style={{ fontSize: 20, color: 'black', alignSelf: 'center' }}>
                  Deseja sair do aplicativo?
                </Text>
                <View style={{ flexDirection: 'row' }}>
                  <TouchableOpacity
                    onPress={() => this.backpress()}
                    style={{
                      padding: 10,
                      marginHorizontal: 10,
                      backgroundColor: '#99d12a',
                      alignItems: 'center',
                      justifyContent: 'center',
                      borderRadius: 20,
                    }}>
                    <Text
                      style={{
                        color: 'white',
                        padding: 5,
                        alignSelf: 'center',
                        fontWeight: 'bold',
                      }}>
                      VOLTAR
                    </Text>
                  </TouchableOpacity>
                  <TouchableOpacity
                    onPress={() => this.backHandler()}
                    style={{
                      padding: 10,
                      marginHorizontal: 10,
                      backgroundColor: 'red',
                      alignItems: 'center',
                      justifyContent: 'center',
                      borderRadius: 20,
                    }}>
                    <Text
                      style={{ color: '#FFF', padding: 5, fontWeight: 'bold' }}>
                      SAIR
                    </Text>
                  </TouchableOpacity>
                </View>
              </View>
            </View>
          </Modal>
        </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  map: {
    width: '100%',
    height: '100%',
  },
  appbar: {
    position: 'absolute',
    left: 0,
    right: 0,
    bottom: 0,
    height: 46,
    flexDirection: 'row',
  },
  fab: {
    position: 'absolute',
    margin: 16,
    right: 0,
    bottom: 0,
  },
  fab2: {
    position: 'absolute',
    bottom: 250,
    height: 55,
    width: 55,
    borderRadius: 30,
    marginLeft: Platform.OS === 'ios' ? 300 : 292,
    backgroundColor: 'transparent',
  },
  paragraphSwipe: {
    margin: 12,
    fontSize: 14,
    fontWeight: 'bold',
    textAlign: 'center',
    color: '#34495e',
  },
  modalSwipe: {
    justifyContent: 'flex-start',
    backgroundColor: '#e5e5e5',
    marginHorizontal: 0,
    marginBottom: 0,
    marginTop: Platform.OS === 'ios' ? 300 : 300,
    borderBottomLeftRadius: 0,
    borderBottomRightRadius: 0,
    borderTopLeftRadius: 15,
    borderTopRightRadius: 15,
    overflow: 'hidden',
  },
  buttonStyleSwipe: {
    padding: 7,
    margin: 1,
    fontWeight: 'bold',
    borderRadius: 5,
    backgroundColor: '#FF3D00',
    color: 'white',
    fontSize: 16,
    alignItems: 'center',
  },
});

请注意把你的API_KEY

要跟踪用户的移动位置,请按照以下步骤操作:

https://medium.com/quick-code/react-native-location-tracking-14ab2c9e2db8

componentDidMount() {
  this.watchID = navigator.geolocation.watchPosition(
    position => {
      const { coordinate, routeCoordinates, distanceTravelled } =   this.state;
      const { latitude, longitude } = position.coords;

      const newCoordinate = {
        latitude,
        longitude
      };
      if (Platform.OS === "android") {
        if (this.marker) {
          this.marker._component.animateMarkerToCoordinate(
            newCoordinate,
            500
          );
         }
       } else {
         coordinate.timing(newCoordinate).start();
       }
       this.setState({
         latitude,
         longitude,
         routeCoordinates: routeCoordinates.concat([newCoordinate]),
         distanceTravelled:
         distanceTravelled + this.calcDistance(newCoordinate),
         prevLatLng: newCoordinate
       });
     },
     error => console.log(error),
     { enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 }
  );
}

推荐阅读