-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #104 from Seoyoung2222/backend/feature/crew
BE: [feat] 크루 공지글 조회 #83
- Loading branch information
Showing
16 changed files
with
69,812 additions
and
14 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
import os | ||
import gpxpy | ||
import pandas as pd | ||
import numpy as np | ||
from geopy.distance import geodesic | ||
|
||
class GPXProcessor: | ||
def __init__(self, toilet_data_path, conv_data_path): | ||
self.toilet_data = pd.read_csv(toilet_data_path) | ||
self.conv_data = pd.read_csv(conv_data_path) | ||
|
||
def extract_gpx_points(self, file_path): | ||
points = [] | ||
with open(file_path, 'r', encoding='utf-8') as gpx_file: | ||
gpx = gpxpy.parse(gpx_file) | ||
for track in gpx.tracks: | ||
for segment in track.segments: | ||
for point in segment.points: | ||
points.append((point.latitude, point.longitude, point.elevation)) | ||
return points | ||
|
||
def calculate_track_length(self, points): | ||
return sum( | ||
geodesic((points[i][0], points[i][1]), (points[i+1][0], points[i+1][1])).meters | ||
for i in range(len(points) - 1) | ||
) | ||
|
||
def sample_points_by_distance(self, points, interval=500): | ||
sampled_points = [points[0]] | ||
accumulated_distance = 0 | ||
last_point = points[0] | ||
|
||
for point in points[1:]: | ||
accumulated_distance += geodesic( | ||
(last_point[0], last_point[1]), (point[0], point[1]) | ||
).meters | ||
if accumulated_distance >= interval: | ||
sampled_points.append(point) | ||
accumulated_distance = 0 | ||
last_point = point | ||
|
||
return sampled_points | ||
|
||
def count_facilities_for_gpx(self, sampled_points, radius=500): | ||
def count_nearby(facility_data): | ||
return sum( | ||
any( | ||
geodesic((point[0], point[1]), (facility['latitude'], facility['longitude'])).meters <= radius | ||
for point in sampled_points | ||
) | ||
for _, facility in facility_data.iterrows() | ||
) | ||
|
||
toilets = count_nearby(self.toilet_data) | ||
convenience_stores = count_nearby(self.conv_data) | ||
return toilets, convenience_stores | ||
|
||
def classify_facilities(self, toilets, conv_stores): | ||
total_facilities = toilets + conv_stores | ||
if total_facilities == 0: | ||
return "No_Facilities" | ||
elif total_facilities >= 23: | ||
return "Enhanced_Facilities" | ||
else: | ||
return "Essential_Facilities" | ||
|
||
def classify_track(self, points): | ||
center_proximity_threshold = 0.6 # 중심 근처 포인트 비율 | ||
nearby_distance_threshold = 90 # 중심 근처 거리 기준 (미터) | ||
endpoint_proximity_threshold = 50 # 시작/끝점 거리 기준 (미터) | ||
min_track_length = 300 # 최소 트랙 길이 (미터) | ||
|
||
lats = [p[0] for p in points] | ||
lons = [p[1] for p in points] | ||
center = (np.mean(lats), np.mean(lons)) | ||
|
||
center_proximity = sum( | ||
geodesic((p[0], p[1]), center).meters < nearby_distance_threshold | ||
for p in points | ||
) / len(points) | ||
|
||
if center_proximity < center_proximity_threshold: | ||
return "NonTrack" | ||
|
||
start_point = (points[0][0], points[0][1]) | ||
end_point = (points[-1][0], points[-1][1]) | ||
if geodesic(start_point, end_point).meters > endpoint_proximity_threshold: | ||
return "NonTrack" | ||
|
||
total_length = self.calculate_track_length(points) | ||
return "Track" if total_length >= min_track_length else "NonTrack" | ||
|
||
def classify_difficulty(self, avg_elevation_change): | ||
cluster_thresholds = { | ||
"beginner_threshold": 0.020962435991564006, | ||
"advanced_threshold": 0.05274945669390355, | ||
} | ||
if avg_elevation_change <= cluster_thresholds["beginner_threshold"]: | ||
return "Beginner" | ||
elif avg_elevation_change <= cluster_thresholds["advanced_threshold"]: | ||
return "Advanced" | ||
else: | ||
return "Expert" | ||
|
||
def process_gpx_file(self, file_path): | ||
points = self.extract_gpx_points(file_path) | ||
total_length = self.calculate_track_length(points) / 1000 # 거리(km) | ||
|
||
sampled_points = self.sample_points_by_distance(points) | ||
toilets, conv_stores = self.count_facilities_for_gpx(sampled_points) | ||
|
||
avg_elevation_change = np.mean([ | ||
abs(points[i][2] - points[i-1][2]) / | ||
geodesic((points[i-1][0], points[i-1][1]), (points[i][0], points[i][1])).meters | ||
for i in range(1, len(points)) if points[i][2] is not None and points[i-1][2] is not None | ||
]) | ||
|
||
facility_label = self.classify_facilities(toilets, conv_stores) | ||
track_label = self.classify_track(points) | ||
difficulty_label = self.classify_difficulty(avg_elevation_change) | ||
|
||
return { | ||
"file_name": os.path.basename(file_path), | ||
"difficulty": difficulty_label, | ||
"facilities": facility_label, | ||
"track_type": track_label, | ||
"distance_km": round(total_length, 1) | ||
} | ||
|
||
if __name__ == "__main__": | ||
toilet_data_path = "C:/Users/정호원/OneDrive/바탕 화면/gpx 수집/test/final_toilet.csv" | ||
conv_data_path = "C:/Users/정호원/OneDrive/바탕 화면/gpx 수집/test/final_conv.csv" | ||
sample_file = "C:/Users/정호원/OneDrive/바탕 화면/gpx 수집/test/sample2.gpx" | ||
|
||
gpx_processor = GPXProcessor(toilet_data_path, conv_data_path) | ||
result = gpx_processor.process_gpx_file(sample_file) | ||
|
||
new_file_name = f"{result['difficulty']}_{result['facilities']}_{result['track_type']}_{result['distance_km']}Km.gpx" | ||
print(f"새로운 파일명: {new_file_name}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
#################### Library install #################### | ||
#################### import Library #################### | ||
|
||
## *********** 한 번 실행하고 각주 처리를 해야함 *********** | ||
# import subprocess | ||
# import sys | ||
|
||
# def install_package(package_name): | ||
# try: | ||
# subprocess.check_call([sys.executable, "-m", "pip", "install", package_name, "--user"]) | ||
# print(f"'{package_name}' install success") | ||
# except subprocess.CalledProcessError as e: | ||
# print(f"'{package_name}' error happened: {e}") | ||
|
||
# # install list | ||
# packages = ["folium", "gpxpy", "geopy"] | ||
|
||
# for package in packages: | ||
# install_package(package) | ||
|
||
# # install check | ||
# print("all packages installed") | ||
########################################################## | ||
|
||
## gpx 파일들, 가로등, 신호등, 편의점, 화장실 데이터 load ### | ||
## *********** 한 번 실행하고 각주 처리를 해야함 *********** | ||
import os | ||
import gpxpy | ||
import pandas as pd | ||
|
||
## csv file load | ||
def load_csv(file_path): | ||
if os.path.exists(file_path): | ||
return pd.read_csv(file_path) | ||
|
||
## csv | ||
if __name__ == "__main__": | ||
toilet_csv_path = 'C:/Users/정호원/OneDrive/바탕 화면/gpx 수집/code/final_toilet.csv' ## *** your path *** | ||
conv_csv_path = 'C:/Users/정호원/OneDrive/바탕 화면/gpx 수집/code/final_conv.csv' ## *** your path *** | ||
streetlight_csv_path = 'C:/Users/정호원/OneDrive/바탕 화면/gpx 수집/code/final_streetlight.csv' ## *** your path *** | ||
trafficlight_csv_path = 'C:/Users/정호원/OneDrive/바탕 화면/gpx 수집/code/final_trafficlight.csv' ## *** your path *** | ||
|
||
## csv file load | ||
toilet_data = load_csv(toilet_csv_path) | ||
conv_data = load_csv(conv_csv_path) | ||
streetlight_data = load_csv(streetlight_csv_path) | ||
trafficlight_data = load_csv(trafficlight_csv_path) | ||
|
||
# csv file load check | ||
if toilet_data is not None: | ||
print("final_toilet loaded") | ||
if conv_data is not None: | ||
print("final_conv loaded") | ||
if streetlight_data is not None: | ||
print("final_streetlight loaded") | ||
if trafficlight_data is not None: | ||
print("final_trafficlight loaded") | ||
|
||
## gpx file load | ||
def load_gpx_files(directory_path): | ||
if os.path.exists(directory_path): | ||
gpx_files = [] | ||
for file_name in os.listdir(directory_path): | ||
if file_name.endswith(".gpx"): | ||
file_path = os.path.join(directory_path, file_name) | ||
with open(file_path, 'r', encoding='utf-8') as gpx_file : # encoding : UTF-8 | ||
try: | ||
gpx_data = gpxpy.parse(gpx_file) | ||
gpx_files.append((file_name, gpx_data)) | ||
except Exception as e: | ||
print(f"error {file_name}, happened : {e}") | ||
return gpx_files | ||
|
||
## gpx directory | ||
gpx_directory = 'C:/Users/정호원/OneDrive/바탕 화면/gpx 수집/gpx' ## *** your path *** | ||
|
||
## gpx file load | ||
gpx_files = load_gpx_files(gpx_directory) | ||
|
||
## gpx file load check | ||
print(f"{len(gpx_files)} gpx files loaded") | ||
########################################################## | ||
|
||
########## base setting 상태에서 경로 추천 ############### | ||
## filtering | ||
from geopy.distance import geodesic | ||
def filter_gpx_within_radius(gpx_files, center_coords, radius): | ||
within_radius_files = [] | ||
for file_name, gpx_data in gpx_files: | ||
for track in gpx_data.tracks: | ||
for segment in track.segments: | ||
points = segment.points | ||
if any(geodesic((point.latitude, point.longitude), center_coords).meters < radius for point in points): | ||
within_radius_files.append(file_name) | ||
break | ||
else: | ||
continue | ||
break | ||
return within_radius_files | ||
|
||
## matching mapping | ||
def filter_by_preferences(within_radius_files, elevation, convenience, track): | ||
# elevation matching | ||
elevation_mapping = { | ||
"LOW": ["Beginner"], | ||
"MEDIUM": ["Advanced"], | ||
"HIGH": ["Expert"], | ||
} | ||
elevation_categories = elevation_mapping.get(elevation.upper(), []) | ||
|
||
# convenience matching | ||
convenience_mapping = { | ||
"LOW": ["Enhanced_Facilities", "Essential_Facilities", "No_Facilities"], | ||
"MEDIUM": ["Enhanced_Facilities", "Essential_Facilities"], | ||
"HIGH": ["Essential_Facilities"], | ||
} | ||
convenience_categories = convenience_mapping.get(convenience.upper(), []) | ||
|
||
# track shaped matching | ||
track_categories = ["Track"] if track.upper() == "T" else ["NonTrack", "Track"] | ||
|
||
# filtering | ||
filtered_files = [ | ||
file | ||
for file in within_radius_files | ||
if any(elev in file for elev in elevation_categories) | ||
and any(conv in file for conv in convenience_categories) | ||
and any(trk in file for trk in track_categories) | ||
] | ||
return filtered_files | ||
|
||
|
||
######## base setting 상태에서 모든 경로 추천 ############# | ||
######## base setting 상태에서 추천 경로 추천 ############# | ||
## all running paths & preference running paths | ||
def print_all_and_filtered_files(gpx_files, center_coords, radius, elevation, convenience, track): | ||
within_radius_files = filter_gpx_within_radius(gpx_files, center_coords, radius) | ||
print(f"\nIn {radius / 1000:.2f}km all paths") | ||
if within_radius_files: | ||
for file in within_radius_files: | ||
print(file) | ||
else: | ||
print("No files found within the specified radius.") | ||
|
||
# preference filtering | ||
filtered_files = filter_by_preferences(within_radius_files, elevation, convenience, track) | ||
|
||
# print preference filtering | ||
print(f"\nIn {radius / 1000:.2f}km preference paths") | ||
if filtered_files: | ||
for file in filtered_files: | ||
print(file) | ||
else: | ||
print("No files found within the specified radius matching your preferences.") | ||
|
||
## *** My Location *** | ||
center_coords = (37.511989, 127.091) | ||
|
||
# radius | ||
radius_threshold = 2500 # 2.5km | ||
|
||
## Run | ||
if __name__ == "__main__": | ||
print("\ncheck my preference") | ||
## ***** USER Input ***** | ||
elevation_preference = input("Elevation preference (LOW, MEDIUM, HIGH): ").strip() | ||
convenience_preference = input("Convenience preference (LOW, MEDIUM, HIGH): ").strip() | ||
track_preference = input("Track preference (T for Track only, F for both): ").strip() | ||
|
||
# function Run | ||
print_all_and_filtered_files( | ||
gpx_files, center_coords, radius_threshold, elevation_preference, convenience_preference, track_preference | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.