如何实现辅助用户准确地通过最短路径导航到任何地方的特定位置



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

嗨,查看以下链接上的完整代码:

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 }
);
}

最新更新