프론트/React Native, React

[리액트 네이티브] React-Native GPS로 사용자의 위치 가져와서 이동한 거리 선긋기 (Expo)

연지양갱 2023. 7. 7. 18:21
728x90
반응형
SMALL

프로젝트로

애완동물과 산책하는 거리를 보여주는 프로젝트이다

그래서

사용자가 산책을 시작하면

서버에 데이터를 보낸다

사용자 + 산책시간 + 현재위치 (위도, 경도)

를 서버로 보낸다.

이렇게 만들것인데

일단 이 기능의 시나리오는

  1. 사용자가 산책하기 버튼을 클릭
  2. 사용자 + 날짜 + 산책시간(현재시간) + 현재위치(위도, 경도)를 보냄
  3. (서버) 산책하기 테이블에 저장 [ 번호 + 사용자 + 시작시간 + 종료시간(null) ] 을 저장
  4. 모달창으로 주의 사항 및 안내할 내용을 보여준다 확인버튼을 누르면 모달창은 사라짐
  5. 산책하는 동안 onUserLocationChange로 변화하면 연결해주는 선그어주기? (GPS)기능 필요
  6. 산책 종료 버튼 클릭
  7. 사진찍을 수 있는 모달창 띄우기
  8. 확인을 누르면 사진찍는 페이지
  9. 취소를 누르면 그냥 종료 되고 서버에 저장되지 않는다
  10. 확인을 누르면 카메라 실행, 사진을 찍으면 모달창에 사진을 보여주고 다시찍기와 확인 버튼을 보여줌
  11. 다시찍기는 9번 다시 실행
  12. (서버) 산책하기 버튼을 누르면 아마존스토레이지에 저장하여 링크 가져오기
  13. (서버) 가져온 링크를 사용자의 캘린더 테이블에 저장
  14. 확인하거나 취소 버튼을 누르면 제일 처음 페이지로 넘어감

이렇게 시나리오를 간단하게 짰는데 가능할지는 잘 모르겠다

가장 문제는 5번인데 좀 더 자료을 찾아봐야한다

https://velog.io/@flowersayo/React-NativeExpo%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-GPS-%EC%9C%84%EC%B9%98%EC%B6%94%EC%A0%81-%EB%9F%AC%EB%8B%9D-%ED%8A%B8%EB%9E%98%ED%82%B9-%EC%95%B1-%EB%A7%8C%EB%93%A4%EA%B8%B0

 

React Native(Expo)를 이용한 GPS 위치추적 - 러닝 트래킹 앱 만들기

우리팀원분중 한명이 만든 플로깅화면의 프로토타입이다. 조깅하면서 쓰레기를 줍는 활동을 장려하는 어플이기 때문에 플로깅을 흥미있어할 수 있도록 사용자의 위치를 실시간으로 추적하면

velog.io

 

 

 

이 블로그 내용을 보면서 구현해볼것이다

내용은 블로그에 잘 적혀있어서 쉽게 이해할 수 있었다

이 내용을 먼저 쭉 훑어보고 코드를 작성하는 것을 추천한다!

이동경로 지도에 표시하기를 중심으로 코드를 짰다

이블로그도 참고하여 소숫점 반올림하였다!

 

https://developer-talk.tistory.com/304

 

[JavaScript]소수점 반올림하는 방법

이번 포스팅에서는 JavaScript에서 소수점 반올림하는 방법들을 소개합니다. 목차 toFixed() 함수 Math.round() 함수 Math.floor() 함수 Math.ceil() 함수 toPrecision() 함수 toFixed() 함수 toFixed() 함수는 인수로 전달

developer-talk.tistory.com

 

 

 

여기까지 친구가 해줬던 내용에 조금만 넣어서 트래킹까지 완료했다

근데 너무 자세하게 나와서 가만히 있어도 위치가 자꾸 바뀐다...

이게 아이폰이라 그런건지,, 아무튼!

전체 코드다!

 

import axios from 'axios';
import React, { useEffect, useState } from 'react';
import MapView, { Marker, Circle, Callout, AnimatedRegion, Polyline, MarkerAnimated } from 'react-native-maps';
// npm i react-native-maps
import { Text, View, StyleSheet, Button, Alert, Modal, Pressable, Image } from 'react-native';
import * as Location from 'expo-location';
// npm i expo-location
import Checkbox from 'expo-checkbox';


//navigation사용할 때 필요
import 'react-native-gesture-handler';
import { NavigationContainer } from "@react-navigation/native";
import { createStackNavigator } from "@react-navigation/stack";
const Stack = createStackNavigator();


export default function Walk(navigation) {
  const [mapRegion, setmapRegion] = useState({ //나의 위치 usestate
    latitude: 36.7987869, //위도
    longitude: 127.0757584, //경도
    latitudeDelta: 0.005, //확대되는 범위
    longitudeDelta: 0.005, //확대되는 범위
  });
  //이동경로 표시하기
  const [gps, setGps] = React.useState([]);
  const [latit, setLatit] = React.useState();
  const [longit, setLongit] = React.useState();


  //모달
  const [modalVisible, setModalVisible] = React.useState(false);



  useEffect(() => {
    (async () => {

      //위치 수집 허용하는지 물어보기
      let { status } = await Location.requestForegroundPermissionsAsync();
      if (status !== 'granted') {
        setErrorMsg('Permission to access location was denied');
        return;
      }

      let location = await Location.getCurrentPositionAsync({});
      let address = await Location.reverseGeocodeAsync(location.coords);
      console.log(location);
      console.log(address);
      setmapRegion({ //현재 위치
        latitude: location.coords.latitude,
        longitude: location.coords.longitude
      })
    })();
  }, []);


  const startWalk = () => {
    //서버에 시간, 위치 보내기
    setModalVisible(true) //test
    const date = new Date();
    const time = `${date.getFullYear()}/${date.getMonth()}/${date.getDate()} ${date.getHours()}:${date.getMinutes()}`
    // axios.post("링크링크", null, {
    //   params: {
    //     latitude: latit,
    //     longitude: longit,
    //     id: "user",
    //     time: time, //시작시간
    //   }
    // })
    //   .then(function (res) {
    //     console.log(res)
    //     console.log(res.data)
    //     if (res.data === true) {
    //       setModalVisible(true)

    //     }
    //   })

  }

  const walkTo = () => {
    //서버에 위치 보내기
    console.log("walkTogether")


    // axios.post("링크링크" ,null, {
    //   params : {
    //     위도,
    //     경도
    //   }
    // })



  }


  return (
    <View style={styles.container}>
      <View style={styles.map}>
        <MapView
          style={{ alignSelf: 'stretch', height: '100%' }}
          // region={mapRegion}
          // initialRegion={{mapRegion}}
          initialRegion={{
            latitude: 36.7987869,
            longitude: 127.0757584,
            latitudeDelta: 0.0005,
            longitudeDelta: 0.0005,
          }}

          //사용자 위치에 맞게 마커가 표시된다.
          showsUserLocation={true}
          // userLocationUpdateInterval = 
          onUserLocationChange={(e) => {
            //사용자가 이동하면 위치를 저장함
            //console.log("onUserLocationChange", e.nativeEvent.coordinate);
            //위치 위도경도를 저장함

            // 너무 새새하게 나와서 자름! 
            // 소숫점 4자리까지만 나오게 저장하게 함
            //const lat = e.nativeEvent.coordinate.latitude.toFixed(4)
            //const long = e.nativeEvent.coordinate.longitude.toFixed(4)
            //그래도 정확도를 위해서 위도 경도 모두로 계산할거임
            const newCoordinate = {
              latitude: e.nativeEvent.coordinate.latitude,
              longitude: e.nativeEvent.coordinate.longitude
            }
            setGps(gps.concat(newCoordinate));
            console.log("gps", gps);

            // setmapRegion(gps.concat(newCoordinate));
          }}
        >
          {/* 마커표시 */}
          <Marker
            coordinate={mapRegion}
            draggable={true} //마커 드래그 가능
            onDragStart={(e) => { console.log("Drag start", e.nativeEvent.coordinate); }} //드래그 한 위도, 경도 나타냄
            onDragEnd={(e) => {
              const lat = e.nativeEvent.coordinate.latitude.toFixed(4)
              const long = e.nativeEvent.coordinate.longitude.toFixed(4)
              setmapRegion({
                latitude: lat,
                longitude: long
              });
              setLatit(lat)
              setLongit(long)
            }} //드래그 한 곳으로 마커 이동
          >
            <Callout>
              <Text>This is Callout</Text>
            </Callout>
          </Marker>

          {/* 반경 */}
          <Circle center={mapRegion} radius={500} />
          
                   <Polyline
            coordinates={gps}
            strokeColor="#4e90f7"
            // coordinates={[
            //   { latitude: 37.8025259, longitude: -122.4351431 },
            //   { latitude: 37.7896386, longitude: -122.421646 },
            //   { latitude: 37.7665248, longitude: -122.4161628 },
            //   { latitude: 37.7734153, longitude: -122.4577787 },
            //   { latitude: 37.7948605, longitude: -122.4596065 },
            //   { latitude: 37.8025259, longitude: -122.4351431 }
            // ]}
            // strokeColor="#000" // fallback for when `strokeColors` is not supported by the map-provider
            // strokeColors={[
            //   '#7F0000',
            //   '#00000000', // no color, creates a "long" gradient between the previous and next coordinate
            //   '#B24112',
            //   '#E5845C',
            //   '#238C23',
            //   '#7F0000'
            // ]}
            strokeWidth={6}
          />

        </MapView>


        <View style={styles.buttons}>
          {/* 버튼 */}
          <Pressable style={styles.button} onPress={startWalk} >
            <Text style={styles.text}>start</Text>
          </Pressable>
          <Text>              </Text>
          <Pressable style={styles.button} onPress={walkTo}>
            <Text style={styles.text}>togher</Text>
          </Pressable>


        </View>
      </View>
    </View>
  );
};
//{latitudeDelta: 0.0922, longitudeDelta: 0.0421}


const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',

  },
  map: {
    width: "100%",
    height: "90%",
  },
  buttons: {
    padding: 10,
    height: "10%",
    flexDirection: 'row',
    widh: "100%",
    backgroundColor: 'yellow',
    justifyContent: 'center'
  },
  button: {
    alignItems: 'center',
    justifyContent: 'center',
    paddingVertical: 12,
    paddingHorizontal: 32,
    borderRadius: 4,
    elevation: 3,
    backgroundColor: 'orange',
  },
  centeredView: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    marginTop: 22,
  },
  modalView: {
    margin: 20,
    backgroundColor: 'white',
    borderRadius: 20,
    padding: 35,
    alignItems: 'center',
    shadowColor: '#000',
    shadowOffset: {
      width: 0,
      height: 2,
    },
    shadowOpacity: 0.25,
    shadowRadius: 4,
    elevation: 5,
  },
  buttonOpen: {
    backgroundColor: '#F194FF',
  },
  buttonClose: {
    backgroundColor: '#2196F3',
  },
  textStyle: {
    color: 'white',
    fontWeight: 'bold',
    textAlign: 'center',
  },
  modalText: {
    marginBottom: 15,
    textAlign: 'center',
  },
});

 

 


 

상세 코드 정리

<MapView> 

: react native map 라이브러리에서 가져온 지도 

<MapView
          style={{ alignSelf: 'stretch', height: '100%' }}
          // region={mapRegion}
          // initialRegion={{mapRegion}}
          initialRegion={{
            latitude: 36.7987869,
            longitude: 127.0757584,
            latitudeDelta: 0.0005,
            longitudeDelta: 0.0005,
          }}

          //사용자 위치에 맞게 마커가 표시된다.
          showsUserLocation={true}
          // userLocationUpdateInterval = 
          onUserLocationChange={(e) => {
            //사용자가 이동하면 위치를 저장함
            //console.log("onUserLocationChange", e.nativeEvent.coordinate);
            //위치 위도경도를 저장함

            // 너무 새새하게 나와서 자름! 
            // 소숫점 4자리까지만 나오게 저장하게 함
            const lat = e.nativeEvent.coordinate.latitude.toFixed(4)
            const long = e.nativeEvent.coordinate.longitude.toFixed(4)

            const newCoordinate = {
              latitude: lat,
              longitude: long
            }
            setGps(gps.concat(newCoordinate));
            console.log("gps", gps);

            // setmapRegion(gps.concat(newCoordinate));
          }}
        >

아이폰에서는 i-map

갤럭시에서는 google-map이 연동 되어 있다

 

initialRegion : 위도 경도 초기값

showsUserLocation : 사용자의 현재 위치를 보여줌

onUserLocationChange : 사용자의 위치가 바뀔 떄 마다 event를 발생하도록 하는 프롭스

ㄴ e.nativeEvent.coordinate.latitude.toFixed(4) :: 소숫점 4자리까지 잘라서 벼눗에 저장

ㄴ setGps(gps.concat(newCoordinate)) : useState에 있는 값을 렌더링 해주는 것

렌더링이 될 때마다 사용자가 이동할 때 마다 위도 경도 값이 생성 될 건데 이를 저장하는 배열에 넣는것이다.

concat이 원래 있던 배열에 붙이기? 느낌인거지요

 

 

 

<Marker>

<Marker
            coordinate={mapRegion}
            draggable={true} //마커 드래그 가능
            onDragStart={(e) => { console.log("Drag start", e.nativeEvent.coordinate); }} //드래그 한 위도, 경도 나타냄
            onDragEnd={(e) => {
              const lat = e.nativeEvent.coordinate.latitude.toFixed(4)
              const long = e.nativeEvent.coordinate.longitude.toFixed(4)
              setmapRegion({
                latitude: lat,
                longitude: long
              });
              setLatit(lat)
              setLongit(long)
            }} //드래그 한 곳으로 마커 이동
          >

coordinate=위도 경도가 있는 정보를 넣어 위치를 알려줌

draggable = 드래그 가능함

onDrageStart , onDragEnd는 드래그한 부분에서 시작과 끝 말 그대로다..!

 

 

 

 

+++ 마커 대신 이미지 넣기

<Marker
                    coordinate={{ latitude: item.latitude, longitude: item.longitude }}
                    onPress={() => 
                      { 
                        sheetRef.current.snapTo(0); 
                        setmapRegion(mapRegion.map((item, idx) => {
                          if (index === idx) {
                            return { ...item, isClick: true };
                          } else {
                            return { ...item, isClick: false };
                          }
                        }));

                    }}
                    image={require('../../assets/path1.png')}
                  >

image 프롭스가 있다! 여기에 이미지를 삽입하면 된다.

 

 

 

 

<Callout>

<Callout>
              <Text>This is Callout</Text>
            </Callout>

Callout : 마커를 클릭했을 때 나오는 것들

View 태그를 넣어서 디자인, 코딩 하면 된다!

 

 

<Circle>

 {/* 반경 */}
          <Circle center={mapRegion} radius={500} />

radius  : 원이 만들어지는 반경

 

 

 

<Polyline>

<Polyline
            coordinates={gps}
            strokeColor="#4e90f7"
            // coordinates={[
            //   { latitude: 37.8025259, longitude: -122.4351431 },
            //   { latitude: 37.7896386, longitude: -122.421646 },
            //   { latitude: 37.7665248, longitude: -122.4161628 },
            //   { latitude: 37.7734153, longitude: -122.4577787 },
            //   { latitude: 37.7948605, longitude: -122.4596065 },
            //   { latitude: 37.8025259, longitude: -122.4351431 }
            // ]}
            // strokeColor="#000" // fallback for when `strokeColors` is not supported by the map-provider
            // strokeColors={[
            //   '#7F0000',
            //   '#00000000', // no color, creates a "long" gradient between the previous and next coordinate
            //   '#B24112',
            //   '#E5845C',
            //   '#238C23',
            //   '#7F0000'
            // ]}
            strokeWidth={6}
          />

 

위도 경도만 있어도 선을 그어주는 것임

상단에 onUserLocationChange에서 계속 concat 해줬던 부분에서 

값을 넣어서 선을 그어질 수 있다

strolkeColor = 선색

strokeColors = 여러 선색을 만들 수 있다(ios에서만 가능)

 

 

 

 


 

프로젝트를 진행하면서 사용했던 라이브러리를 정리했다

매번 느끼는 것이지만 document를 보면서 정리하면 충분히 개발 할 수 있다는 것을 다른 사람도 느낄 수 있으면 좋겠다!

어려운 것도 많겠지만, 할 수 있다. 해야한다는 생각을 가지고 있으면 좋다

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형