diff --git a/FlaskApps/__pycache__/weatherrec.cpython-312.pyc b/FlaskApps/__pycache__/weatherrec.cpython-312.pyc new file mode 100644 index 0000000..e8fc441 Binary files /dev/null and b/FlaskApps/__pycache__/weatherrec.cpython-312.pyc differ diff --git a/FlaskApps/weather.py b/FlaskApps/weather.py index 5a97aa5..2b63224 100644 --- a/FlaskApps/weather.py +++ b/FlaskApps/weather.py @@ -1,17 +1,16 @@ from flask import Flask, jsonify, request import requests import pandas as pd -import numpy as np from datetime import datetime from lxml import etree from haversine import haversine from datasets import load_dataset +from weatherrec import get_place_recommendation app = Flask(__name__) -@app.route('/weather',methods=['GET']) +@app.route('/weather', methods=['GET']) def get_weather(): - # 오늘 날짜와 시간 불러오기 what_date = datetime.now().strftime("%Y%m%d") what_time = datetime.now().strftime("%H%M") @@ -21,16 +20,15 @@ def get_weather(): kor_loc = pd.DataFrame(dataset['train']) kor_loc = kor_loc.iloc[:,:15].dropna() - mylat = request.args.get('lat', type=str) - mylong = request.args.get('long', type=str) + mylat = request.args.get('latitude') + mylong = request.args.get('longitude') if not mylat: - print("response error:not valid user lat") + print("response error: not valid user lat") mylat = '37.51490409227562' if not mylong: - print("response error:not valid user long") + print("response error: not valid user long") mylong = '126.84135103588255' - # 내 좌표 설정 my_loc = (float(mylat), float(mylong)) @@ -80,12 +78,10 @@ def get_weather(): '6': "빗방울 또는 눈날림", '7': "눈날림" } - rain = rain_mapping.get(rain, "기상 정보 없음") + rain = rain_mapping.get(rain, "강수없음") # 초단기예보데이터 url2 = 'http://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getUltraSrtFcst' - - response2 = requests.get(url2, params=params) root2 = etree.fromstring(response2.content) @@ -104,7 +100,6 @@ def get_weather(): "ny": item.findtext("ny") } for item in items] - # 데이터프레임으로 만들기 df = pd.DataFrame(data) @@ -119,86 +114,21 @@ def get_weather(): except (KeyError, IndexError): sky = "흐림" - # 날씨 정보에 따른 장소 추천 로직 - park_name, lib_name, muse_name = None, None, None - park_lat, lib_lat, muse_lat = None, None, None - park_long, lib_long, muse_long = None, None, None - park_adres, lib_adres, muse_adres = None, None, None - if (sky in ['맑음', '흐림']) and (rain == ' ') and (15 <= float(temp) <= 29): - # 공원 정보 불러오기 및 추천 로직 - park_url = 'http://openAPI.seoul.go.kr:8088/57524f76506d656e3732636a52457a/json/SearchParkInfoService/1/1000/' - park_response = requests.get(park_url) - park_data = park_response.json()['SearchParkInfoService']['row'] - park = pd.DataFrame(park_data) - park.rename(columns={'P_PARK': "NAME", 'P_ADDR': "ADRES", 'XCNTS': 'LATITUDE', 'YDNTS': "LONGITUDE"}, inplace=True) - park['LATITUDE'].replace('', np.nan, inplace=True) - park['LONGITUDE'].replace('', np.nan, inplace=True) - park = park.dropna() - park['LATITUDE'] = park['LATITUDE'].astype(float) - park['LONGITUDE'] = park['LONGITUDE'].astype(float) - # 가장 가까운 위치 찾기 - min_distance = float('inf') - for index, row in park.iterrows(): - point = (row['LATITUDE'], row['LONGITUDE']) - distance = haversine(my_loc, point) - if distance < min_distance: - min_distance = distance - park_name, park_lat, park_long, park_adres = row['NAME'], row['LATITUDE'], row['LONGITUDE'], row['ADRES'] - elif rain != '비가 오고 있지 않습니다.': - # 도서관 정보 불러오기 및 추천 로직 - lib_url = 'http://openAPI.seoul.go.kr:8088/57524f76506d656e3732636a52457a/json/SeoulLibraryTimeInfo/1/1000/' - lib_response = requests.get(lib_url) - lib_data = lib_response.json()['SeoulLibraryTimeInfo']['row'] - lib = pd.DataFrame(lib_data) - lib_url2 = 'http://openAPI.seoul.go.kr:8088/57524f76506d656e3732636a52457a/json/SeoulLibraryTimeInfo/1001/2000/' - lib_response2 = requests.get(lib_url2) - lib_data2 = lib_response2.json()['SeoulLibraryTimeInfo']['row'] - lib2 = pd.DataFrame(lib_data2) - lib = pd.concat([lib,lib2]) - lib.rename(columns={'LBRRY_NAME': "NAME", 'ADRES': "ADRES", 'XCNTS': 'LATITUDE', 'YDNTS': "LONGITUDE"}, inplace=True) - lib['LATITUDE'] = lib['LATITUDE'].astype(float) - lib['LONGITUDE'] = lib['LONGITUDE'].astype(float) - # 가장 가까운 위치 찾기 - min_distance = float('inf') - for index, row in lib.iterrows(): - point = (row['LATITUDE'], row['LONGITUDE']) - distance = haversine(my_loc, point) - if distance < min_distance: - min_distance = distance - lib_name, lib_lat, lib_long, lib_adres = row['NAME'], row['LATITUDE'], row['LONGITUDE'], row['ADRES'] - else: - # 박물관 정보 불러오기 및 추천 로직 - muse_url = 'http://openAPI.seoul.go.kr:8088/57524f76506d656e3732636a52457a/json/SeoulMuseumInfo/1/1000/' - muse_response = requests.get(muse_url) - muse_data = muse_response.json()['SeoulMuseumInfo']['row'] - muse = pd.DataFrame(muse_data) - muse.rename(columns={'MUSEUM_NAME': "NAME", 'ADDR': "ADRES", 'XCNTS': 'LATITUDE', 'YDNTS': "LONGITUDE"}, inplace=True) - muse['LATITUDE'] = muse['LATITUDE'].astype(float) - muse['LONGITUDE'] = muse['LONGITUDE'].astype(float) - # 가장 가까운 위치 찾기 - min_distance = float('inf') - for index, row in muse.iterrows(): - point = (row['LATITUDE'], row['LONGITUDE']) - distance = haversine(my_loc, point) - if distance < min_distance: - min_distance = distance - muse_name, muse_lat, muse_long, muse_adres = row['NAME'], row['LATITUDE'], row['LONGITUDE'], row['ADRES'] + # 날씨에 따라 추천 장소 얻기 + place_recommendation = get_place_recommendation(my_loc, sky, rain, temp) # 결과 출력 result = { "sky": sky, "rain": rain, "temp": temp, - "name": park_name or lib_name or muse_name, - "latitude": park_lat or lib_lat or muse_lat, - "longitude": park_long or lib_long or muse_long, - "address": park_adres or lib_adres or muse_adres, - "place_type": "공원" if park_name else "도서관" if lib_name else "박물관", - "guName" : guName, - "dongName": dongName + "guName": guName, + "dongName": dongName, + **place_recommendation } response = jsonify(result) - response.headers.add('Content-Type', 'application/json; charset=ASCII') #인코딩 문제 해결 + response.headers.add('Content-Type', 'application/json; charset=ASCII') # 인코딩 문제 해결 return response + if __name__ == '__main__': app.run(debug=True, host='0.0.0.0', port=5001) diff --git a/FlaskApps/weatherrec.py b/FlaskApps/weatherrec.py new file mode 100644 index 0000000..804fa33 --- /dev/null +++ b/FlaskApps/weatherrec.py @@ -0,0 +1,91 @@ +# weatherrec.py +import requests +import pandas as pd +import numpy as np +from haversine import haversine +from datasets import load_dataset + + +def get_place_recommendation(my_loc, sky, rain, temp): + park_name, lib_name, muse_name = None, None, None + park_lat, lib_lat, muse_lat = None, None, None + park_long, lib_long, muse_long = None, None, None + park_adres, lib_adres, muse_adres = None, None, None + + # 날씨 정보에 따른 장소 추천 로직 + park_name, lib_name, muse_name = None, None, None + park_lat, lib_lat, muse_lat = None, None, None + park_long, lib_long, muse_long = None, None, None + park_adres, lib_adres, muse_adres = None, None, None + if (sky in ['맑음', '흐림']) and (rain == '강수없음') and (15 <= float(temp) <= 29): + # 공원 정보 불러오기 및 추천 로직 + park_url = 'http://openAPI.seoul.go.kr:8088/57524f76506d656e3732636a52457a/json/SearchParkInfoService/1/1000/' + park_response = requests.get(park_url) + park_data = park_response.json()['SearchParkInfoService']['row'] + park = pd.DataFrame(park_data) + park.rename(columns={'P_PARK': "NAME", 'P_ADDR': "ADRES", 'XCNTS': 'LATITUDE', 'YDNTS': "LONGITUDE"}, inplace=True) + park['LATITUDE'].replace('', np.nan, inplace=True) + park['LONGITUDE'].replace('', np.nan, inplace=True) + park = park.dropna() + park['LATITUDE'] = park['LATITUDE'].astype(float) + park['LONGITUDE'] = park['LONGITUDE'].astype(float) + # 가장 가까운 위치 찾기 + min_distance = float('inf') + for index, row in park.iterrows(): + point = (row['LATITUDE'], row['LONGITUDE']) + distance = haversine(my_loc, point) + if distance < min_distance: + min_distance = distance + park_name, park_lat, park_long, park_adres = row['NAME'], row['LATITUDE'], row['LONGITUDE'], row['ADRES'] + elif rain != '강수없음': + # 도서관 정보 불러오기 및 추천 로직 + lib_url = 'http://openAPI.seoul.go.kr:8088/57524f76506d656e3732636a52457a/json/SeoulLibraryTimeInfo/1/1000/' + lib_response = requests.get(lib_url) + lib_data = lib_response.json()['SeoulLibraryTimeInfo']['row'] + lib = pd.DataFrame(lib_data) + lib_url2 = 'http://openAPI.seoul.go.kr:8088/57524f76506d656e3732636a52457a/json/SeoulLibraryTimeInfo/1001/2000/' + lib_response2 = requests.get(lib_url2) + lib_data2 = lib_response2.json()['SeoulLibraryTimeInfo']['row'] + lib2 = pd.DataFrame(lib_data2) + lib = pd.concat([lib,lib2]) + lib.rename(columns={'LBRRY_NAME': "NAME", 'ADRES': "ADRES", 'XCNTS': 'LATITUDE', 'YDNTS': "LONGITUDE"}, inplace=True) + lib['LATITUDE'] = lib['LATITUDE'].astype(float) + lib['LONGITUDE'] = lib['LONGITUDE'].astype(float) + # 가장 가까운 위치 찾기 + min_distance = float('inf') + for index, row in lib.iterrows(): + point = (row['LATITUDE'], row['LONGITUDE']) + distance = haversine(my_loc, point) + if distance < min_distance: + min_distance = distance + lib_name, lib_lat, lib_long, lib_adres = row['NAME'], row['LATITUDE'], row['LONGITUDE'], row['ADRES'] + else: + # 박물관 정보 불러오기 및 추천 로직 + muse = load_dataset("hscrown/seoul_museums") + muse = pd.DataFrame(muse['train']) + muse.rename(columns={'시설명': "NAME", '주소': "ADRES", '위도': 'LATITUDE', '경도': "LONGITUDE"}, inplace=True) + muse['LATITUDE'].replace('', np.nan, inplace=True) + muse['LONGITUDE'].replace('', np.nan, inplace=True) + muse['LATITUDE'] = muse['LATITUDE'].astype(float) + muse['LONGITUDE'] = muse['LONGITUDE'].astype(float) + + # 가장 가까운 위치 찾기 + min_distance = float('inf') + for index, row in muse.iterrows(): + point = (row['LATITUDE'], row['LONGITUDE']) + try: + distance = haversine(my_loc, point) + if distance < min_distance: + min_distance = distance + muse_name, muse_lat, muse_long, muse_adres = row['NAME'], row['LATITUDE'], row['LONGITUDE'], row['ADRES'] + except ValueError: + continue # 에러가 발생하면 해당 행을 건너뛰도록 설정 + + result = { + "name": park_name or lib_name or muse_name, + "latitude": park_lat or lib_lat or muse_lat, + "longitude": park_long or lib_long or muse_long, + "address": park_adres or lib_adres or muse_adres, + "place_type": "공원" if park_name else "도서관" if lib_name else "박물관" + } + return result diff --git a/app.json b/app.json index 97ffa14..f01597f 100644 --- a/app.json +++ b/app.json @@ -18,7 +18,7 @@ "ios": { "supportsTablet": true, "config": { - "googleMapsApiKey": "AIzaSyCNv0u_zbqsH3_xPIIJSJw4_cMkW8_6mlk" + "googleMapsApiKey": "GOOGLE_MAP_KEY" } }, "android": { @@ -28,7 +28,7 @@ }, "config": { "googleMaps": { - "apiKey": "AIzaSyCNv0u_zbqsH3_xPIIJSJw4_cMkW8_6mlk" + "apiKey": "GOOGLE_MAP_KEY" } } }, diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx index 188a129..a7e2aea 100644 --- a/app/(tabs)/index.tsx +++ b/app/(tabs)/index.tsx @@ -1,18 +1,17 @@ +import React, { useEffect, useState } from 'react'; import { Image, - ScrollView, StyleSheet, Text, TextInput, TouchableOpacity, View, } from 'react-native'; -import React, { useEffect, useState } from 'react'; -import { Stack } from 'expo-router'; -import { Ionicons, FontAwesome } from '@expo/vector-icons'; -import Colors from '@/constants/Colors'; +import { FontAwesome } from '@expo/vector-icons'; import { useHeaderHeight } from '@react-navigation/elements'; import { router } from "expo-router"; +import * as Location from 'expo-location'; +import Colors from '@/constants/Colors'; const handleWeather = () => { router.push("weathers"); @@ -20,20 +19,54 @@ const handleWeather = () => { const handleMaps = () => { router.push("maps"); -} +}; -const handlePlace =() => { +const handlePlace = () => { router.push("place"); -} +}; const handleBorough = () => { router.push("borough"); -} +}; const Page = () => { const headerHeight = useHeaderHeight(); const [category, setCategory] = useState('전체'); const [loading, setLoading] = useState(false); + const [location, setLocation] = useState(null); + const [errorMsg, setErrorMsg] = useState(null); + const [address, setAddress] = useState('사용자 위치 정보를 불러오고 있습니다...'); + const [latitude, setLatitude] = useState(null); + const [longitude, setLongitude] = useState(null); + + useEffect(() => { + (async () => { + let { status } = await Location.requestForegroundPermissionsAsync(); + if (status !== 'granted') { + setErrorMsg('위치 접근 권한이 거부되었습니다.'); + return; + } + + let location = await Location.getCurrentPositionAsync({}); + setLocation(location); + + if (location) { + const { latitude, longitude } = location.coords; + setLatitude(latitude); + setLongitude(longitude); + + try { + let reverseGeocode = await Location.reverseGeocodeAsync({ latitude, longitude }); + if (reverseGeocode.length > 0) { + let address = reverseGeocode[0]; + setAddress(`${address.city} ${address.street} ${address.streetNumber} (${address.postalCode})`); + } + } catch (error) { + setErrorMsg('주소를 가져오는 중 오류가 발생했습니다.'); + } + } + })(); + }, []); const onCatChanged = (category: string) => { setCategory(category); @@ -41,45 +74,47 @@ const Page = () => { return ( <> - + - - - - 구파발역.서울특별시 은평구 지하 101 - - - - - Lemon MuL - :Free to Go everywhere! - - 시작하기 - - - - - 서울시 탐색 - - - 리뷰 / 평가 - - - 날씨 기반 추천 서비스 - - - 사용자 맞춤 추천 서비스 - + + + + + {errorMsg ? errorMsg : address} + + + + + + Lemon MuL + :Free to Go everywhere! + + 시작하기 + + + + + 서울시 탐색 + + + 리뷰 / 평가 + + + 날씨 기반 추천 서비스 + + + 사용자 맞춤 추천 서비스 + + - ); }; @@ -152,7 +187,6 @@ const styles = StyleSheet.create({ alignItems: "center", marginBottom: 15, elevation: 3, - }, blueButton: { backgroundColor: "#5ea3ff", diff --git a/app/maps/fullMarkerMap.tsx b/app/maps/fullMarkerMap.tsx index 788de18..61628ec 100644 --- a/app/maps/fullMarkerMap.tsx +++ b/app/maps/fullMarkerMap.tsx @@ -68,7 +68,7 @@ const Page: React.FC = () => { }, []); const fetchParks = async () => { - const seoul_key = '57524f76506d656e3732636a52457a'; + const seoul_key = 'SEOUL_API_KEY'; const url = `http://openAPI.seoul.go.kr:8088/${seoul_key}/json/SearchParkInfoService/1/1000/`; try { const response = await axios.get(url); diff --git a/app/maps/library.tsx b/app/maps/library.tsx index dbe2bb6..b6ac299 100644 --- a/app/maps/library.tsx +++ b/app/maps/library.tsx @@ -53,10 +53,10 @@ const HomeScreen: React.FC = () => { }, []); const fetchLibraries = async () => { - const api_key = '57524f76506d656e3732636a52457a'; + const seoul_key = 'SEOUL_API_KEY'; const urls = [ - `http://openAPI.seoul.go.kr:8088/${api_key}/json/SeoulLibraryTimeInfo/1/1000/`, - `http://openAPI.seoul.go.kr:8088/${api_key}/json/SeoulLibraryTimeInfo/1001/2000/` + `http://openAPI.seoul.go.kr:8088/${seoul_key}/json/SeoulLibraryTimeInfo/1/1000/`, + `http://openAPI.seoul.go.kr:8088/${seoul_key}/json/SeoulLibraryTimeInfo/1001/2000/` ]; try { diff --git a/app/maps/park.tsx b/app/maps/park.tsx index 41a5b28..f690115 100644 --- a/app/maps/park.tsx +++ b/app/maps/park.tsx @@ -56,7 +56,7 @@ const ParkScreen: React.FC = () => { }, []); const fetchParks = async () => { - const seoul_key = '57524f76506d656e3732636a52457a'; + const seoul_key = 'SEOUL_API_KEY'; const url = `http://openAPI.seoul.go.kr:8088/${seoul_key}/json/SearchParkInfoService/1/1000/`; try { diff --git a/app/weathers/weatherCompleted.tsx b/app/weathers/weatherCompleted.tsx index 3136eab..3f0a607 100644 --- a/app/weathers/weatherCompleted.tsx +++ b/app/weathers/weatherCompleted.tsx @@ -1,5 +1,6 @@ import React, { useState, useEffect } from 'react'; -import { StyleSheet, Text, View, Image, TouchableOpacity } from 'react-native'; +import { StyleSheet, Text, View, Image, TouchableOpacity, Alert } from 'react-native'; +import * as Location from 'expo-location'; import Colors from '@/constants/Colors'; import { router } from 'expo-router'; import BackButton from '@/components/BackButton'; @@ -7,18 +8,34 @@ import FlaskConfig from '@/flask.config'; const WeatherDashboard = () => { const [weatherData, setWeatherData] = useState({}); - const lat = 37.5665; - const long = 126.978; + const [location, setLocation] = useState(null); + const [errorMsg, setErrorMsg] = useState(null); useEffect(() => { - console.log('Component mounted, starting to fetch weather data...'); - loadWeatherPlaces(lat, long); + (async () => { + let { status } = await Location.requestForegroundPermissionsAsync(); + if (status !== 'granted') { + setErrorMsg('Permission to access location was denied'); + return; + } + + let location = await Location.getCurrentPositionAsync({}); + setLocation(location); + })(); }, []); + useEffect(() => { + if (location) { + const { latitude, longitude } = location.coords; + loadWeatherPlaces(latitude, longitude); + } + }, [location]); + const loadWeatherPlaces = (lat, long) => { console.log( `Fetching weather data for latitude: ${lat}, longitude: ${long}` ); + fetch( `http://${FlaskConfig.Private_IP_Address}:${FlaskConfig.weather}/weather?latitude=${lat}&longitude=${long}` ) @@ -61,7 +78,7 @@ const WeatherDashboard = () => { /> {weatherData.name} - 오늘은 날씨가 {weatherData.sky}! + 오늘 날씨는 {weatherData.sky}! 이곳은 어떨까요?