GPS och karta
I denna övning tar vi först en titt på hur vi kan använda oss av den inbyggda kartan på telefonen. Sen lägger vi till en markör för både orderns leveransadress och användarens nuvarande position.
#En start
I uppgiften i kmom05 ska vi skapa en lista med ordrar som är redo att levereras. Jag utgår i detta exemplet från att vi har skapat en list-vy precis som tidigare i kursen och att vi har tillgång en order som en del av route.params
via en Stack.Navigator
. Jag kommer alltså utgå från följande komponent och sedan bygga vidare på under övningen.
import { Text, View, StyleSheet } from "react-native";
import { Base, Typography } from "../../styles";
export default function ShipOrder({ route }) {
const {order} = route.params;
return (
<View style={Base.base}>
<Text style={Typography.header2}>Skicka order</Text>
</View>
);
};
#Kartkomponenten
Vi kommer i denna övning använda modulen react-native-maps
för att visa upp en karta i vår mobila enhet och för att rita ut markörer på denna karta. Vi installerar komponenten med kommandot expo install react-native-maps
. Innan vi fortsätter kan det vara bra att titta på modulens dokumentation.
Vi kan nu importera MapView
och rita ut en karta. Vi definierar först en MapView
och till den komponenten skickar vi med ett objekt med breddgrader och längdgrader. Vi använder ett inbyggt style
attribut fra StyleSheet
som heter ...StyleSheet.absoluteFillObject
. Det gör att vi fyller upp hela den tillgängliga ytan med kartan.
import { Text, View, StyleSheet } from "react-native";
import { Base, Typography } from "../../styles";
import MapView from 'react-native-maps';
export default function ShipOrder({ route }) {
const {order} = route.params;
return (
<View style={Base.base}>
<Text style={Typography.header2}>Skicka order</Text>
<View style={styles.container}>
<MapView
style={styles.map}
initialRegion={{
latitude: 56.1612,
longitude: 15.5869,
latitudeDelta: 0.1,
longitudeDelta: 0.1,
}} />
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "flex-end",
alignItems: "center",
},
map: {
...StyleSheet.absoluteFillObject,
},
});
#En markör
För att vi ska kunna rita ut vart ordern ska levereras vill vi använda oss av en markör (Marker
). Vi importerar först Marker
komponenten och lägger sedan till den som en barn komponent till MapView
.
<MapView
style={styles.map}
initialRegion={{
latitude: 56.1612,
longitude: 15.5869,
latitudeDelta: 0.1,
longitudeDelta: 0.1,
}}>
<Marker
coordinate={{ latitude: 56.17, longitude: 15.59 }}
title="Min första markör"
/>
</MapView>
Nu är ett bra läge att testa så allt fungerar innan vi går vidare.
#Koordinater från adress
Nu har vi inte koordinater tillgängliga för att alla ordrar så vi behöver ett sätt att översätta addresser till koordinater. Som tur är finns tjänsten nominatim som även tillhandahåller ett API. Jag har valt att skapa en modell models/nominatim.ts
som exporterar funktionen getCoordinates
. Funktionen tar en sträng som parameter och returnerar sedan en array med result.
// models/nominatim.ts
export default async function getCoordinates(address: string) {
const urlEncodedAddress = encodeURIComponent(address);
const url = "https://nominatim.openstreetmap.org/search.php?format=jsonv2&q=";
const response = await fetch(`${url}${urlEncodedAddress}`);
const result = await response.json();
return result;
};
Vi kan i vår komponent sedan importera modellen och använda den för att skapa en markör. Jag väljer att lägga marker
som en del av state och använder sedan useEffect
för att hämta koordinaterna från en adress.
För att få ut rätt i nästa steg är det viktigt att adresser från API:t existerar på riktigt. De adresser som finns på de kopierade ordrarna är hitte-på adresser.
import { useEffect, useState } from "react";
import { Text, View, StyleSheet } from "react-native";
import { Base, Typography } from "../../styles";
import MapView from 'react-native-maps';
import { Marker } from "react-native-maps";
import getCoordinates from "../../models/nominatim";
export default function ShipOrder({ route }) {
const {order} = route.params;
const [marker, setMarker] = useState(null);
useEffect(() => {
(async () => {
const results = await getCoordinates(`${order.address}, ${order.city}`);
setMarker(<Marker
coordinate={{ latitude: parseFloat(results[0].lat), longitude: parseFloat(results[0].lon) }}
title={results[0].display_name}
/>);
})();
}, []);
return (
<View style={Base.base}>
<Text style={Typography.header2}>Skicka order</Text>
<View style={styles.container}>
<MapView
style={styles.map}
initialRegion={{
latitude: 56.1612,
longitude: 15.5869,
latitudeDelta: 0.1,
longitudeDelta: 0.1,
}}>
{marker}
</MapView>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "flex-end",
alignItems: "center",
},
map: {
...StyleSheet.absoluteFillObject,
},
});
Notera att vi använder parseFloat(results[0].lat)
då Nominatim tjänsten returnerar strängar för latitude och longitude. Samt att vi ritar ut marker
som ett barn till MapView
.
#Användarens position
Sista delen av denna övningen går ut på att rita ut användarens position som en markör på kartan. Vi använder ungefär samma tillvägagångssätt som innan. Först installerar vi modulen Location
med hjälp av expo install expo-location
och importerar med import * as Location from 'expo-location';
. Vi initierar sedan locationMarker
och errorMessage
som en del av state.
Sedan använder vi useEffect
igen för att hämta användarens position från den inbyggda GPS’n i mobilen. Vi ser nedan att vi först frågar användaren om vi kan få använda GPS i telefonen. Se till att svara “Tillåt” på detta under utveckling för att det ska fungera. Sedan hämtar vi nuvarande position och utifrån den skapar och sätter vi sedan state
variabeln locationMarker
.
const [locationMarker, setLocationMarker] = useState(null);
const [errorMessage, setErrorMessage] = useState(null);
useEffect(() => {
(async () => {
const { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
setErrorMessage('Permission to access location was denied');
return;
}
const currentLocation = await Location.getCurrentPositionAsync({});
setLocationMarker(<Marker
coordinate={{
latitude: currentLocation.coords.latitude,
longitude: currentLocation.coords.longitude
}}
title="Min plats"
pinColor="blue"
/>);
})();
}, []);
Vi kan sedan i return
delen av komponenten rita ut locationMarker
på samma sätt som med marker
.
<MapView
style={styles.map}
initialRegion={{
latitude: 56.1612,
longitude: 15.5869,
latitudeDelta: 0.1,
longitudeDelta: 0.1,
}}>
{marker}
{locationMarker}
</MapView>
#Avslutningsvis
Vi har i denna artikel använd oss av kartor och att placera ut markörer på en karta på specifika platser. Vi har använt telefonens inbyggda GPS för att rita ut vår egen plats på kartan.
#Revision history
- 2018-03-02: (A, efo) Första utgåvan inför kursen webapp v3.