Обновить route.py

This commit is contained in:
2025-10-29 15:54:17 +03:00
parent 5074a37141
commit 82158fea68

913
route.py
View File

@@ -1,454 +1,461 @@
import math import math
import itertools import itertools
import requests import requests
from typing import List, Tuple, Dict, Optional, Set from typing import List, Tuple, Dict, Optional, Set
import random
class Point: class Point:
def __init__(self, coord: List[float], tag: str, visit_time: int): def __init__(self, coord: List[float], tag: str, visit_time: int):
self.coord = coord self.coord = coord
self.tag = tag self.tag = tag
self.visit_time = visit_time self.visit_time = visit_time
self.matrix_index = None # Индекс точки в матрице расстояний self.matrix_index = None # Индекс точки в матрице расстояний
self.estimated_time = None # Оценочное время (перемещение + посещение) self.estimated_time = None # Оценочное время (перемещение + посещение)
def haversine(coord1: List[float], coord2: List[float]) -> float: def haversine(coord1: List[float], coord2: List[float]) -> float:
"""Calculate the great-circle distance between two points in kilometers.""" """Calculate the great-circle distance between two points in kilometers."""
lat1, lon1 = coord1 lat1, lon1 = coord1
lat2, lon2 = coord2 lat2, lon2 = coord2
R = 6371 # Earth radius in km R = 6371 # Earth radius in km
dlat = math.radians(lat2 - lat1) dlat = math.radians(lat2 - lat1)
dlon = math.radians(lon2 - lon1) dlon = math.radians(lon2 - lon1)
a = (math.sin(dlat/2) * math.sin(dlat/2) + a = (math.sin(dlat/2) * math.sin(dlat/2) +
math.cos(math.radians(lat1)) * math.cos(math.radians(lat2)) * math.cos(math.radians(lat1)) * math.cos(math.radians(lat2)) *
math.sin(dlon/2) * math.sin(dlon/2)) math.sin(dlon/2) * math.sin(dlon/2))
return R * 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a)) return R * 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
def filter_points_by_time(start_coord: List[float], points: List[Point], total_time: int) -> List[Point]: def filter_points_by_time(start_coord: List[float], points: List[Point], total_time: int) -> List[Point]:
"""Filter points based on straight-line distance and visit time.""" """Filter points based on straight-line distance and visit time."""
filtered = [] filtered = []
for point in points: for point in points:
distance = haversine(start_coord, point.coord) distance = haversine(start_coord, point.coord)
travel_time = distance * 10 # Assume 6 km/h walking speed (10 min/km) travel_time = distance * 10 # Assume 6 km/h walking speed (10 min/km)
point.estimated_time = travel_time + point.visit_time point.estimated_time = travel_time + point.visit_time
if point.estimated_time <= total_time: if point.estimated_time <= total_time:
filtered.append(point) filtered.append(point)
return filtered return filtered
def filter_points_by_tag_proximity(points: List[Point], max_per_tag: int = 10) -> List[Point]: def filter_points_by_tag_proximity(points: List[Point], max_per_tag: int = 10) -> List[Point]:
"""For each tag, keep only the closest points (by estimated time).""" """For each tag, keep only the closest points (by estimated time)."""
# Group points by tag # Group points by tag
tag_to_points = {} tag_to_points = {}
for point in points: for point in points:
if point.tag not in tag_to_points: if point.tag not in tag_to_points:
tag_to_points[point.tag] = [] tag_to_points[point.tag] = []
tag_to_points[point.tag].append(point) tag_to_points[point.tag].append(point)
# For each tag, sort by estimated time and keep top max_per_tag # For each tag, sort by estimated time and keep top max_per_tag
filtered_points = [] filtered_points = []
for tag, tag_points in tag_to_points.items(): for tag, tag_points in tag_to_points.items():
# Sort by estimated time (ascending) # Sort by estimated time (ascending)
sorted_points = sorted(tag_points, key=lambda p: p.estimated_time) sorted_points = sorted(tag_points, key=lambda p: p.estimated_time)
# Keep at most max_per_tag points # Keep at most max_per_tag points
kept_points = sorted_points[:max_per_tag] kept_points = sorted_points[:max_per_tag]
filtered_points.extend(kept_points) filtered_points.extend(kept_points)
print(f"Tag '{tag}': kept {len(kept_points)} out of {len(tag_points)} points") print(f"Tag '{tag}': kept {len(kept_points)} out of {len(tag_points)} points")
return filtered_points return filtered_points
def get_duration_matrix(points: List[List[float]]) -> Optional[Tuple[List[List[float]], List[List[float]]]]: def get_duration_matrix(points: List[List[float]]) -> Optional[Tuple[List[List[float]], List[List[float]]]]:
"""Get duration matrix from server.""" """Get duration matrix from server."""
url = "https://ha1m-maap-pdmc.gw-1a.dockhost.net/table" url = "https://ha1m-maap-pdmc.gw-1a.dockhost.net/table"
payload = {"points": points} payload = {"points": points}
headers = {"content-type": "application/json"} headers = {"content-type": "application/json"}
try: try:
response = requests.post(url, json=payload, headers=headers, timeout=30) response = requests.post(url, json=payload, headers=headers, timeout=30)
if response.status_code == 200: if response.status_code == 200:
data = response.json() data = response.json()
return data.get("distances"), data.get("durations") return data.get("distances"), data.get("durations")
else: else:
print(f"Server error: {response.status_code}") print(f"Server error: {response.status_code}")
return None return None
except Exception as e: except Exception as e:
print(f"Error requesting duration matrix: {e}") print(f"Error requesting duration matrix: {e}")
return None return None
def group_points_by_significance(points: List[Point], tag_importance: Dict[str, int]) -> Dict[int, List[Point]]: def group_points_by_significance(points: List[Point], tag_importance: Dict[str, int]) -> Dict[int, List[Point]]:
"""Group points by their importance level.""" """Group points by their importance level."""
grouped = {} grouped = {}
for point in points: for point in points:
importance = tag_importance.get(point.tag, float('inf')) importance = tag_importance.get(point.tag, float('inf'))[0]
if importance not in grouped: if importance not in grouped:
grouped[importance] = [] grouped[importance] = []
grouped[importance].append(point) grouped[importance].append(point)
return grouped return grouped
def calculate_route_time_with_matrix(route: List[Point], start_coord: List[float], def calculate_route_time_with_matrix(route: List[Point], start_coord: List[float],
duration_matrix: List[List[float]]) -> float: duration_matrix: List[List[float]]) -> float:
"""Calculate total time for a route using the duration matrix.""" """Calculate total time for a route using the duration matrix."""
total_time = 0 total_time = 0
current_index = 0 # Start point index current_index = 0 # Start point index
for point in route: for point in route:
next_index = point.matrix_index next_index = point.matrix_index
travel_time_seconds = duration_matrix[current_index][next_index] travel_time_seconds = duration_matrix[current_index][next_index]
travel_time_minutes = travel_time_seconds / 60.0 travel_time_minutes = travel_time_seconds / 60.0
total_time += travel_time_minutes + point.visit_time total_time += travel_time_minutes + point.visit_time
current_index = next_index current_index = next_index
return total_time return total_time
def check_tags_constraint(points: List[Point]) -> bool: def check_tags_constraint(points: List[Point]) -> bool:
"""Check if there are no more than 5 unique tags.""" """Check if there are no more than 5 unique tags."""
unique_tags = set(point.tag for point in points) unique_tags = set(point.tag for point in points)
return len(unique_tags) <= 5 return len(unique_tags) <= 5
def generate_routes_exact_tags(grouped_points: Dict[int, List[Point]], def generate_routes_exact_tags(grouped_points: Dict[int, List[Point]],
all_tags: Set[str], all_tags: Set[str],
tag_importance: Dict[str, int]) -> List[List[Point]]: tag_importance: Dict[str, int]) -> List[List[Point]]:
"""Generate routes where each tag is visited exactly once using different coordinates.""" """Generate routes where each tag is visited exactly once using different coordinates."""
# Create a mapping from tag to points # Create a mapping from tag to points
tag_to_points = {} tag_to_points = {}
for points_list in grouped_points.values(): for points_list in grouped_points.values():
for point in points_list: for point in points_list:
if point.tag not in tag_to_points: if point.tag not in tag_to_points:
tag_to_points[point.tag] = [] tag_to_points[point.tag] = []
tag_to_points[point.tag].append(point) tag_to_points[point.tag].append(point)
# For each tag, we need to select exactly one point # For each tag, we need to select exactly one point
tag_selections = [] tag_selections = []
for tag in all_tags: for tag in all_tags:
tag_selections.append(tag_to_points[tag]) tag_selections.append(tag_to_points[tag])
# Generate all combinations of points (one per tag) # Generate all combinations of points (one per tag)
all_routes = [] all_routes = []
print(len(list(itertools.product(*tag_selections)))) print(len(list(itertools.product(*tag_selections))))
for point_combination in itertools.product(*tag_selections): for point_combination in itertools.product(*tag_selections):
# Check if all points have unique coordinates # Check if all points have unique coordinates
coords = [tuple(point.coord) for point in point_combination] coords = [tuple(point.coord) for point in point_combination]
if len(coords) != len(set(coords)): if len(coords) != len(set(coords)):
continue # Skip if any coordinates are duplicated continue # Skip if any coordinates are duplicated
# Group points by importance # Group points by importance
points_by_importance = {} points_by_importance = {}
for point in point_combination: for point in point_combination:
imp = tag_importance[point.tag] imp = tag_importance[point.tag][0]
if imp not in points_by_importance: if imp not in points_by_importance:
points_by_importance[imp] = [] points_by_importance[imp] = []
points_by_importance[imp].append(point) points_by_importance[imp].append(point)
# Sort by importance # Sort by importance
sorted_importances = sorted(points_by_importance.keys()) sorted_importances = sorted(points_by_importance.keys())
# Generate all permutations within each importance group # Generate all permutations within each importance group
importance_groups = [points_by_importance[imp] for imp in sorted_importances] importance_groups = [points_by_importance[imp] for imp in sorted_importances]
for ordering in itertools.product(*[itertools.permutations(group) for group in importance_groups]): for ordering in itertools.product(*[itertools.permutations(group) for group in importance_groups]):
route = [] route = []
for group in ordering: for group in ordering:
route.extend(group) route.extend(group)
all_routes.append(route) all_routes.append(route)
return all_routes return all_routes
def generate_routes_with_repeats(grouped_points: Dict[int, List[Point]], def generate_routes_with_repeats(grouped_points: Dict[int, List[Point]],
all_tags: Set[str], all_tags: Set[str],
tag_importance: Dict[str, int], tag_importance: Dict[str, int],
num_points: int) -> List[List[Point]]: num_points: int) -> List[List[Point]]:
"""Generate routes when we need to repeat tags to reach the required number of points, ensuring unique coordinates.""" """Generate routes when we need to repeat tags to reach the required number of points, ensuring unique coordinates."""
# Create a mapping from tag to points # Create a mapping from tag to points
tag_to_points = {} tag_to_points = {}
for points_list in grouped_points.values(): for points_list in grouped_points.values():
for point in points_list: for point in points_list:
if point.tag not in tag_to_points: if point.tag not in tag_to_points:
tag_to_points[point.tag] = [] tag_to_points[point.tag] = []
tag_to_points[point.tag].append(point) tag_to_points[point.tag].append(point)
all_routes = [] all_routes = []
# First, select one point for each tag (mandatory points) # First, select one point for each tag (mandatory points)
mandatory_selections = [tag_to_points[tag] for tag in all_tags] mandatory_selections = [tag_to_points[tag] for tag in all_tags]
# Generate all combinations of mandatory points (one per tag) # Generate all combinations of mandatory points (one per tag)
for mandatory_combo in itertools.product(*mandatory_selections): for mandatory_combo in itertools.product(*mandatory_selections):
mandatory_points = list(mandatory_combo) mandatory_points = list(mandatory_combo)
# Check if mandatory points have unique coordinates # Check if mandatory points have unique coordinates
mandatory_coords = [tuple(point.coord) for point in mandatory_points] mandatory_coords = [tuple(point.coord) for point in mandatory_points]
if len(mandatory_coords) != len(set(mandatory_coords)): if len(mandatory_coords) != len(set(mandatory_coords)):
continue # Skip if any coordinates are duplicated in mandatory points continue # Skip if any coordinates are duplicated in mandatory points
# We need to add (num_points - len(mandatory_points)) additional points # We need to add (num_points - len(mandatory_points)) additional points
num_additional = num_points - len(mandatory_points) num_additional = num_points - len(mandatory_points)
if num_additional == 0: if num_additional == 0:
# We have exactly the right number of points # We have exactly the right number of points
points_by_importance = {} points_by_importance = {}
for point in mandatory_points: for point in mandatory_points:
imp = tag_importance[point.tag] imp = tag_importance[point.tag][0]
if imp not in points_by_importance: if imp not in points_by_importance:
points_by_importance[imp] = [] points_by_importance[imp] = []
points_by_importance[imp].append(point) points_by_importance[imp].append(point)
sorted_importances = sorted(points_by_importance.keys()) sorted_importances = sorted(points_by_importance.keys())
importance_groups = [points_by_importance[imp] for imp in sorted_importances] importance_groups = [points_by_importance[imp] for imp in sorted_importances]
for ordering in itertools.product(*[itertools.permutations(group) for group in importance_groups]): for ordering in itertools.product(*[itertools.permutations(group) for group in importance_groups]):
route = [] route = []
for group in ordering: for group in ordering:
route.extend(group) route.extend(group)
all_routes.append(route) all_routes.append(route)
else: else:
# We need to add additional points (can be from any tag, including repeats) # We need to add additional points (can be from any tag, including repeats)
# But we must ensure all coordinates are unique # But we must ensure all coordinates are unique
# Get all available points excluding mandatory points # Get all available points excluding mandatory points
all_available_points = [] all_available_points = []
for points_list in grouped_points.values(): for key in grouped_points.keys():
all_available_points.extend(points_list) if tag_importance[key][1]:
all_available_points.extend(grouped_points[key])
# Remove mandatory points from available points
available_points = [p for p in all_available_points if p not in mandatory_points] # Remove mandatory points from available points
available_points = [p for p in all_available_points if p not in mandatory_points]
# Generate combinations of additional points
for additional_combo in itertools.combinations(available_points, num_additional): # Generate combinations of additional points
# Check if additional points have unique coordinates and don't duplicate with mandatory for additional_combo in itertools.combinations(available_points, num_additional):
additional_coords = [tuple(point.coord) for point in additional_combo] # Check if additional points have unique coordinates and don't duplicate with mandatory
if len(additional_coords) != len(set(additional_coords)): additional_coords = [tuple(point.coord) for point in additional_combo]
continue # Skip if any coordinates are duplicated in additional points if len(additional_coords) != len(set(additional_coords)):
continue # Skip if any coordinates are duplicated in additional points
# Check if additional points don't duplicate with mandatory points
all_coords = mandatory_coords + additional_coords # Check if additional points don't duplicate with mandatory points
if len(all_coords) != len(set(all_coords)): all_coords = mandatory_coords + additional_coords
continue # Skip if any coordinates are duplicated between mandatory and additional if len(all_coords) != len(set(all_coords)):
continue # Skip if any coordinates are duplicated between mandatory and additional
full_route_candidate = mandatory_points + list(additional_combo)
full_route_candidate = mandatory_points + list(additional_combo)
# Group by importance
points_by_importance = {} # Group by importance
for point in full_route_candidate: points_by_importance = {}
imp = tag_importance[point.tag] for point in full_route_candidate:
if imp not in points_by_importance: imp = tag_importance[point.tag]
points_by_importance[imp] = [] if imp not in points_by_importance:
points_by_importance[imp].append(point) points_by_importance[imp] = []
points_by_importance[imp].append(point)
sorted_importances = sorted(points_by_importance.keys())
importance_groups = [points_by_importance[imp] for imp in sorted_importances] sorted_importances = sorted(points_by_importance.keys())
importance_groups = [points_by_importance[imp] for imp in sorted_importances]
for ordering in itertools.product(*[itertools.permutations(group) for group in importance_groups]):
route = [] for ordering in itertools.product(*[itertools.permutations(group) for group in importance_groups]):
for group in ordering: route = []
route.extend(group) for group in ordering:
all_routes.append(route) route.extend(group)
all_routes.append(route)
return all_routes
return all_routes
def form_point_list(data):
point_list =[] def form_point_list(data):
for entry in data: point_list =[]
point = Point(list(map(float,entry['coordinate'].split(', '))),entry['type'],entry['time_to_visit']) for entry in data:
point_list.append(point) point = Point(list(map(float,entry['coordinate'].split(', '))),entry['type'],entry['time_to_visit'])
return point_list point_list.append(point)
return point_list
def build_route(data, mapping,start_coord,total_time,n_nodes):
# Example input data - теперь с не более чем 5 уникальными тегами def build_route(data, mapping,start_coord,total_time,n_nodes,strategy='best'):
# Example input data - теперь с не более чем 5 уникальными тегами
start_coord_test = [56.331576, 44.003277]
total_time_test = 180 # Увеличим время до 4 часов для большего выбора start_coord_test = [56.331576, 44.003277]
points = form_point_list(data) total_time_test = 180 # Увеличим время до 4 часов для большего выбора
tag_importance =mapping points = form_point_list(data)
# Используем 3 уникальных тега для демонстрации tag_importance =mapping
points_test = [ # Используем 3 уникальных тега для демонстрации
Point([56.32448, 43.983546], "Памятник", 20), points_test = [
Point([56.335607, 43.97481], "Архитектура", 20), Point([56.32448, 43.983546], "Памятник", 20),
Point([56.313472, 43.990747], "Памятник", 20), Point([56.335607, 43.97481], "Архитектура", 20),
#Point([56.324157, 44.002696], "Памятник", 20), Point([56.313472, 43.990747], "Памятник", 20),
#Point([56.316436, 43.994177], "Памятник", 20), #Point([56.324157, 44.002696], "Памятник", 20),
#Point([56.32377, 44.001879], "Памятник", 20), #Point([56.316436, 43.994177], "Памятник", 20),
#Point([56.329867, 43.99687], "Памятник", 20), #Point([56.32377, 44.001879], "Памятник", 20),
Point([56.311066, 43.94595], "Памятник", 20), #Point([56.329867, 43.99687], "Памятник", 20),
Point([56.333265, 43.972417], "Памятник", 20), Point([56.311066, 43.94595], "Памятник", 20),
# Point([56.332166, 44.012111], "Памятник", 20), Point([56.333265, 43.972417], "Памятник", 20),
#Point([56.326786, 44.006836], "Памятник", 20), # Point([56.332166, 44.012111], "Памятник", 20),
Point([56.330232, 44.010941], "Парк", 20), #Point([56.326786, 44.006836], "Памятник", 20),
Point([56.282221, 43.979263], "Парк", 20), Point([56.330232, 44.010941], "Парк", 20),
Point([56.277315, 43.921408], "Мозаика", 20), Point([56.282221, 43.979263], "Парк", 20),
Point([56.284829, 44.01893], "Парк", 20), Point([56.277315, 43.921408], "Мозаика", 20),
Point([56.308973, 43.99821], "Парк", 20), Point([56.284829, 44.01893], "Парк", 20),
Point([56.321545, 44.001921], "Парк", 20), Point([56.308973, 43.99821], "Парк", 20),
#Point([56.301798, 44.044003], "Мозаика", 20), Point([56.321545, 44.001921], "Парк", 20),
Point([56.268282, 43.919475], "Парк", 20), #Point([56.301798, 44.044003], "Мозаика", 20),
Point([56.239625, 43.854551], "Парк", 20), Point([56.268282, 43.919475], "Парк", 20),
#Point([56.311214, 43.933981], "Парк", 20), Point([56.239625, 43.854551], "Парк", 20),
Point([56.314984, 44.007347], "Парк", 20), #Point([56.311214, 43.933981], "Парк", 20),
Point([56.32509, 43.983433], "Парк", 20), Point([56.314984, 44.007347], "Парк", 20),
Point([56.27449, 43.973357], "Парк", 20), Point([56.32509, 43.983433], "Парк", 20),
Point([56.278073, 43.940886], "Парк", 20), Point([56.27449, 43.973357], "Парк", 20),
Point([56.358805, 43.825376], "Парк", 20), Point([56.278073, 43.940886], "Парк", 20),
Point([56.329995, 44.009444], "Памятник", 20), Point([56.358805, 43.825376], "Парк", 20),
Point([56.328551, 43.998718], "Памятник", 20), Point([56.329995, 44.009444], "Памятник", 20),
Point([56.330355, 43.993105], "Архитектура", 20), Point([56.328551, 43.998718], "Памятник", 20),
Point([56.321416, 43.973897], "Архитектура", 20), Point([56.330355, 43.993105], "Архитектура", 20),
# Point([56.327298, 44.005706], "Архитектура", 20), Point([56.321416, 43.973897], "Архитектура", 20),
#Point([56.328757, 43.998183], "Архитектура", 20), # Point([56.327298, 44.005706], "Архитектура", 20),
# Point([56.328908, 43.995645], "Архитектура", 20), #Point([56.328757, 43.998183], "Архитектура", 20),
Point([56.317578, 43.995805], "Архитектура", 20), # Point([56.328908, 43.995645], "Архитектура", 20),
Point([56.329433, 44.012764], "Архитектура", 20), Point([56.317578, 43.995805], "Архитектура", 20),
Point([56.3301, 44.008831], "Архитектура", 20), Point([56.329433, 44.012764], "Архитектура", 20),
#Point([56.32995, 43.999495], "Архитектура", 20), Point([56.3301, 44.008831], "Архитектура", 20),
Point([56.327454, 44.041745], "Архитектура", 20), #Point([56.32995, 43.999495], "Архитектура", 20),
#Point([56.328576, 44.004872], "Архитектура", 20), Point([56.327454, 44.041745], "Архитектура", 20),
Point([56.3275, 44.007658], "Архитектура", 20), #Point([56.328576, 44.004872], "Архитектура", 20),
Point([56.330679, 44.013874], "Архитектура", 20), Point([56.3275, 44.007658], "Архитектура", 20),
# Point([56.331541, 44.001747], "Архитектура", 20), Point([56.330679, 44.013874], "Архитектура", 20),
# Point([56.335071, 43.974627], "Архитектура", 20), # Point([56.331541, 44.001747], "Архитектура", 20),
#Point([56.317707, 43.995847], "Архитектура", 20), # Point([56.335071, 43.974627], "Архитектура", 20),
#Point([56.323851, 43.985939], "Архитектура", 20), #Point([56.317707, 43.995847], "Архитектура", 20),
Point([56.325701, 44.001527], "Архитектура", 20), #Point([56.323851, 43.985939], "Архитектура", 20),
Point([56.328754, 43.998954], "Архитектура", 20), Point([56.325701, 44.001527], "Архитектура", 20),
#Point([56.323937, 43.990728], "Музей", 20), Point([56.328754, 43.998954], "Архитектура", 20),
#Point([56.2841, 43.84621], "Музей", 20), #Point([56.323937, 43.990728], "Музей", 20),
#Point([56.328646, 44.028973], "Музей", 20), #Point([56.2841, 43.84621], "Музей", 20),
Point([56.327391, 43.857522], "Мозаика", 20), #Point([56.328646, 44.028973], "Музей", 20),
#Point([56.252239, 43.889066], "Мозаика", 20), Point([56.327391, 43.857522], "Мозаика", 20),
#Point([56.248436, 43.88106], "Мозаика", 20), #Point([56.252239, 43.889066], "Мозаика", 20),
#Point([56.321257, 43.94545], "Мозаика", 20), #Point([56.248436, 43.88106], "Мозаика", 20),
# Point([56.365284, 43.823251], "Мозаика", 20), #Point([56.321257, 43.94545], "Мозаика", 20),
Point([56.294371, 43.912625], "Мозаика", 20), # Point([56.365284, 43.823251], "Мозаика", 20),
#Point([56.241768, 43.859687], "Мозаика", 20), Point([56.294371, 43.912625], "Мозаика", 20),
#Point([56.300073, 43.938526], "Мозаика", 20), #Point([56.241768, 43.859687], "Мозаика", 20),
#Point([56.229652, 43.947973], "Мозаика", 20), #Point([56.300073, 43.938526], "Мозаика", 20),
# Point([56.269486, 43.9238], "Мозаика", 20), #Point([56.229652, 43.947973], "Мозаика", 20),
Point([56.299251, 43.985146], "Мозаика", 20), # Point([56.269486, 43.9238], "Мозаика", 20),
Point([56.293297, 44.034095], "Мозаика", 20), Point([56.299251, 43.985146], "Мозаика", 20),
Point([56.299251, 43.985146], "Мозаика", 20), Point([56.293297, 44.034095], "Мозаика", 20),
Point([56.229652, 43.947973], "Мозаика", 20), Point([56.299251, 43.985146], "Мозаика", 20),
Point([56.269486, 43.9238], "Мозаика", 20), Point([56.229652, 43.947973], "Мозаика", 20),
#Point([56.293297, 44.034095], "Мозаика", 20), Point([56.269486, 43.9238], "Мозаика", 20),
#Point([56.229652, 43.947973], "Мозаика", 20) #Point([56.293297, 44.034095], "Мозаика", 20),
] #Point([56.229652, 43.947973], "Мозаика", 20)
]
tag_importance_test = {
"Памятник": 1, tag_importance_test = {
"Парк": 1, "Памятник": 1,
"Мозаика": 1, "Парк": 1,
"Архитектура": 1, "Мозаика": 1,
#"Музей": 1 "Архитектура": 1,
} #"Музей": 1
}
# Check tags constraint
if not check_tags_constraint(points): # Check tags constraint
print("Error: More than 5 unique tags in the input data") if not check_tags_constraint(points):
return print("Error: More than 5 unique tags in the input data")
return
print("Input data validation: OK (5 or fewer unique tags)")
print("Input data validation: OK (5 or fewer unique tags)")
# Step 1: Filter points using straight-line distance and total time
filtered_by_time = filter_points_by_time(start_coord, points, total_time) # Step 1: Filter points using straight-line distance and total time
print(f"After initial time filtering: {len(filtered_by_time)} points") filtered_by_time = filter_points_by_time(start_coord, points, total_time)
print(f"After initial time filtering: {len(filtered_by_time)} points")
if len(filtered_by_time) < 3:
print("Not enough points after time filtering") if len(filtered_by_time) < 3:
return print("Not enough points after time filtering")
return
# Step 2: Filter points by tag proximity (keep max 10 closest points per tag)
filtered_points = filter_points_by_tag_proximity(filtered_by_time, max_per_tag=10) # Step 2: Filter points by tag proximity (keep max 10 closest points per tag)
print(f"After tag proximity filtering: {len(filtered_points)} points") filtered_points = filter_points_by_tag_proximity(filtered_by_time, max_per_tag=10)
print(f"After tag proximity filtering: {len(filtered_points)} points")
if len(filtered_points) < 3:
print("Not enough points after tag proximity filtering") if len(filtered_points) < 3:
return print("Not enough points after tag proximity filtering")
return
# Step 3: Prepare points for server request (start point + filtered points)
points_for_matrix = [start_coord] + [point.coord for point in filtered_points] # Step 3: Prepare points for server request (start point + filtered points)
points_for_matrix = [start_coord] + [point.coord for point in filtered_points]
print("Requesting duration matrix from server...")
# Step 4: Get duration matrix from server print("Requesting duration matrix from server...")
result = get_duration_matrix(points_for_matrix) # Step 4: Get duration matrix from server
if result is None: result = get_duration_matrix(points_for_matrix)
print("Failed to get duration matrix from server") if result is None:
return print("Failed to get duration matrix from server")
return
distances_matrix, durations_matrix = result
print("Duration matrix received successfully") distances_matrix, durations_matrix = result
print("Duration matrix received successfully")
# Assign matrix indices to points
for i, point in enumerate(filtered_points): # Assign matrix indices to points
point.matrix_index = i + 1 # +1 because index 0 is the start point for i, point in enumerate(filtered_points):
point.matrix_index = i + 1 # +1 because index 0 is the start point
# Step 5: Group by importance
grouped_points = group_points_by_significance(filtered_points, tag_importance) # Step 5: Group by importance
grouped_points = group_points_by_significance(filtered_points, tag_importance)
# Get all unique tags
all_tags = set(point.tag for point in filtered_points) # Get all unique tags
num_unique_tags = len(all_tags) all_tags = set(point.tag for point in filtered_points)
num_unique_tags = len(all_tags)
print(f"Unique tags: {all_tags} ({num_unique_tags} tags)")
print(f"Unique tags: {all_tags} ({num_unique_tags} tags)")
# Step 6: Generate possible routes
print("Generating possible routes...") # Step 6: Generate possible routes
print("Generating possible routes...")
# Determine the number of points in the route
if num_unique_tags >= n_nodes: # Determine the number of points in the route
# Each tag must be visited exactly once if num_unique_tags >= n_nodes:
print("Each tag will be visited exactly once with unique coordinates") # Each tag must be visited exactly once
possible_routes = generate_routes_exact_tags(grouped_points, all_tags, tag_importance) print("Each tag will be visited exactly once with unique coordinates")
else: possible_routes = generate_routes_exact_tags(grouped_points, all_tags, tag_importance)
# We have fewer than 3 unique tags, need to repeat some tags else:
print(f"Only {num_unique_tags} unique tags available, will repeat tags to reach 3 points with unique coordinates") # We have fewer than 3 unique tags, need to repeat some tags
possible_routes = generate_routes_with_repeats(grouped_points, all_tags, tag_importance, n_nodes) print(f"Only {num_unique_tags} unique tags available, will repeat tags to reach 3 points with unique coordinates")
possible_routes = generate_routes_with_repeats(grouped_points, all_tags, tag_importance, n_nodes)
if not possible_routes:
print("No valid routes found that cover all tags with unique coordinates") if not possible_routes:
return print("No valid routes found that cover all tags with unique coordinates")
return
# Step 7: Calculate time for each route and filter by total_time
valid_routes = [] # Step 7: Calculate time for each route and filter by total_time
for route in possible_routes: valid_routes = []
route_time = calculate_route_time_with_matrix(route, start_coord, durations_matrix) for route in possible_routes:
if route_time <= total_time: route_time = calculate_route_time_with_matrix(route, start_coord, durations_matrix)
valid_routes.append((route, route_time)) if route_time <= total_time:
valid_routes.append((route, route_time))
if not valid_routes:
print("No valid routes found within time constraint") if not valid_routes:
return print("No valid routes found within time constraint")
return
# Step 8: Find optimal route (minimum time)
optimal_route, min_time = min(valid_routes, key=lambda x: x[1]) # Step 8: Find optimal route (minimum time)
print(f"\nOptimal route (time: {min_time:.2f} min):") if strategy=='random':
for i, point in enumerate(optimal_route, 1): optimal_route, min_time = random.choice(valid_routes)
print(f"{i}. {point.tag} at {point.coord} ({point.visit_time} min)") elif strategy=='longest':
optimal_route, min_time = max(valid_routes, key=lambda x: x[1])
# Print route details with travel times else:
print("\nRoute details:") optimal_route, min_time = min(valid_routes, key=lambda x: x[1])
current_index = 0
total_route_time = 0 print(f"\nOptimal route (time: {min_time:.2f} min):")
for i, point in enumerate(optimal_route): for i, point in enumerate(optimal_route, 1):
travel_time_seconds = durations_matrix[current_index][point.matrix_index] print(f"{i}. {point.tag} at {point.coord} ({point.visit_time} min)")
travel_time_minutes = travel_time_seconds / 60.0
segment_time = travel_time_minutes + point.visit_time # Print route details with travel times
total_route_time += segment_time print("\nRoute details:")
current_index = 0
print(f"Segment {i+1}: {travel_time_minutes:.2f} min travel + {point.visit_time} min visit = {segment_time:.2f} min") total_route_time = 0
current_index = point.matrix_index for i, point in enumerate(optimal_route):
travel_time_seconds = durations_matrix[current_index][point.matrix_index]
print(f"Total route time: {total_route_time:.2f} min") travel_time_minutes = travel_time_seconds / 60.0
segment_time = travel_time_minutes + point.visit_time
# Display all tags covered by the route total_route_time += segment_time
route_tags = set(point.tag for point in optimal_route)
print(f"\nTags covered in this route: {', '.join(route_tags)}") print(f"Segment {i+1}: {travel_time_minutes:.2f} min travel + {point.visit_time} min visit = {segment_time:.2f} min")
if all_tags.issubset(route_tags): current_index = point.matrix_index
print("All tags are covered in this route!")
print(f"Total route time: {total_route_time:.2f} min")
# Verify all coordinates are unique
route_coords = [tuple(point.coord) for point in optimal_route] # Display all tags covered by the route
if len(route_coords) == len(set(route_coords)): route_tags = set(point.tag for point in optimal_route)
print("All coordinates in the route are unique!") #print(f"\nTags covered in this route: {', '.join(route_tags)}")
else: if all_tags.issubset(route_tags):
print("ERROR: Duplicate coordinates found in the route!") print("All tags are covered in this route!")
#if __name__ == "__main__": # Verify all coordinates are unique
route_coords = [tuple(point.coord) for point in optimal_route]
if len(route_coords) == len(set(route_coords)):
print("All coordinates in the route are unique!")
else:
print("ERROR: Duplicate coordinates found in the route!")
return route_coords
#if __name__ == "__main__":
# build_route() # build_route()