coolfrxcrazy's picture
Update app.py
97b0716 verified
raw
history blame
10.9 kB
from flask import Flask, render_template, jsonify, request
import googlemaps
from polyline import decode
import os
from dotenv import load_dotenv
from bs4 import BeautifulSoup # To clean HTML tags
load_dotenv()
API_KEY = os.getenv('GOOGLE_MAPS_API_KEY_3')
gmaps = googlemaps.Client(key=API_KEY)
app = Flask(__name__)
# Define avoided segments globally
avoid_list = [
[(24.7970264, 46.719939), (24.7388275, 46.59441409999999)],
[(24.954535, 47.0142416), (24.7258606, 46.583506)],
[(24.796827, 46.5643251), (24.7089077, 46.6195443)],
[(24.9229714, 46.7204701), (24.796827, 46.5643251)],
[(24.796827, 46.5643251), (24.6575642, 46.5630617)],
[(24.7575596, 46.6895021), (24.70444, 46.6237931)],
]
# Fetch directions from Google Maps API
def get_directions(start, end, alternatives=False):
directions_result = gmaps.directions(start, end, mode="driving", alternatives=alternatives)
if directions_result:
return directions_result
return None
# Decode a Google Maps encoded polyline to latitude and longitude points
def decode_polyline_to_points(polyline):
return decode(polyline)
# Check if two routes intersect based on their polylines
def do_routes_intersect(route_a_steps, route_b_steps):
# Loop through steps of both routes
for step_a in route_a_steps:
for step_b in route_b_steps:
# Check if the routes share the same point (latitude and longitude)
if step_a == step_b:
road_name_a = get_road_name_from_step(step_a)
road_name_b = get_road_name_from_step(step_b)
if road_name_a == road_name_b:
return True
# No intersection found
return False
def get_road_name_from_step(step):
# The road name is usually part of 'html_instructions'
if 'html_instructions' in step:
instruction = step['html_instructions']
# Optionally: Clean up the instruction to extract only the road name (e.g., remove HTML tags)
road_name = extract_road_name(instruction)
return road_name
return None
def extract_road_name(instruction):
# Here you can implement more complex logic if needed to extract the road name from the instruction
# e.g., remove HTML tags, get the road name from the instructions
return BeautifulSoup(instruction, "html.parser").text
# Find a route that avoids all segments in avoid_list and return the avoided routes
def find_route_avoiding_segments(start, end, avoid_list):
directions_a_b = get_directions(start, end, alternatives=True)
if not directions_a_b:
return None, None
avoided_routes = []
for route in directions_a_b:
route_a_b_points = decode_polyline_to_points(route['overview_polyline']['points'])
avoid_crossing = False
for avoid_start, avoid_end in avoid_list:
directions_c_d = get_directions(avoid_start, avoid_end, alternatives=False)
if directions_c_d:
route_c_d_points = decode_polyline_to_points(directions_c_d[0]['overview_polyline']['points'])
if do_routes_intersect(route_a_b_points, route_c_d_points):
avoid_crossing = True
avoided_routes.append(directions_c_d[0]) # Save the avoided route
break # This route crosses an avoidable segment, so skip it
if not avoid_crossing:
return route, avoided_routes # Return the valid route and avoided routes
return None, None
RIYADH_BOUNDING_BOX = {
'north': 25.0885, # Northernmost latitude in Riyadh
'south': 24.3246, # Southernmost latitude in Riyadh
'west': 46.2613, # Westernmost longitude in Riyadh
'east': 47.0484 # Easternmost longitude in Riyadh
}
def is_within_bounds(lat, lng):
"""Check if a given latitude and longitude are within the Riyadh bounding box."""
return (RIYADH_BOUNDING_BOX['south'] <= lat <= RIYADH_BOUNDING_BOX['north'] and
RIYADH_BOUNDING_BOX['west'] <= lng <= RIYADH_BOUNDING_BOX['east'])
def is_route_within_bounds(route):
"""Check if the entire route stays within the Riyadh bounding box."""
# Go through each step in the route and check the start and end locations
for leg in route['legs']:
for step in leg['steps']:
start_lat = step['start_location']['lat']
start_lng = step['start_location']['lng']
end_lat = step['end_location']['lat']
end_lng = step['end_location']['lng']
if not is_within_bounds(start_lat, start_lng) or not is_within_bounds(end_lat, end_lng):
return False
return True
def find_mid_point_between(start, end):
"""Finds a mid-point between start and end, ensuring it is within Riyadh."""
start_lat, start_lng = map(float, start.split(','))
end_lat, end_lng = map(float, end.split(','))
mid_lat = (start_lat + end_lat) / 2
mid_lng = (start_lng + end_lng) / 2
# Check if the mid-point is within the Riyadh bounding box
if is_within_bounds(mid_lat, mid_lng):
return f"{mid_lat},{mid_lng}"
else:
# If the mid-point is outside bounds, return None
return None
def recursive_route_search(start, end, avoid_list, depth=0, max_depth=3):
"""
Helper function that determines whether we can find valid segments
by recursively calculating mid-points.
"""
if depth > max_depth:
return None
# Calculate the mid-point between start and end
mid_point = find_mid_point_between(start, end)
if not mid_point:
# If the mid-point is outside valid bounds, stop recursion
return None
# Check if direct route from start to mid-point is valid
first_segment = find_valid_route(start, mid_point, avoid_list)
if not first_segment:
# If we can't find a valid first segment, try smaller segments
return recursive_route_search(start, mid_point, avoid_list, depth + 1, max_depth)
# Check if direct route from mid-point to end is valid
second_segment = find_valid_route(mid_point, end, avoid_list)
if not second_segment:
# If we can't find a valid second segment, try smaller segments
return recursive_route_search(mid_point, end, avoid_list, depth + 1, max_depth)
# If both segments are valid, merge them and return
return merge_segments(first_segment, second_segment)
def merge_segments(first_segment, second_segment):
"""Merges two route segments into one."""
merged_route = {
'legs': first_segment['legs'] + second_segment['legs'],
'overview_polyline': {
'points': first_segment['overview_polyline']['points'] + second_segment['overview_polyline']['points']
}
}
return merged_route
def find_valid_route(start, end, avoid_list):
"""Attempts to find a valid route from start to end, avoiding specific segments."""
# Get routes from A to B (with alternatives)
routes = get_directions(start, end, alternatives=True)
if not routes:
return None
# Iterate through all available routes
for route in routes:
route_a_b_points = decode_polyline_to_points(route['overview_polyline']['points'])
# Ensure the entire route stays within bounds
if not is_route_within_bounds(route):
continue # Skip this route if it goes outside the bounding box
# Assume this route is valid until proven otherwise
avoid_crossing = False
# Check if it crosses any avoided routes
for avoid_start, avoid_end in avoid_list:
directions_c_d = get_directions(avoid_start, avoid_end, alternatives=False)
if directions_c_d:
route_c_d_points = decode_polyline_to_points(directions_c_d[0]['overview_polyline']['points'])
if do_routes_intersect(route_a_b_points, route_c_d_points):
avoid_crossing = True
break # Exit loop since the route crosses an avoided route
if not avoid_crossing:
# Found a valid route that doesn't cross any avoided segments
return route
return None # No valid route found
@app.route('/calculate-route', methods=['POST'])
def calculate_route():
data = request.json
start = data['start']
end = data['end']
# Try to find a direct route first
route = find_valid_route(start, end, avoid_list)
if not route:
# If no valid direct route is found, try to find an alternative recursively
route = recursive_route_search(start, end, avoid_list)
if route:
route_json = format_route_to_json(route)
return jsonify({
'status': 'success',
'route': route_json
})
else:
return jsonify({
'status': 'error',
'message': 'All routes cross an avoided street or no valid route found.'
})
# Helper function to format the route into JSON format
def format_route_to_json(route):
return {
'start_address': route['legs'][0]['start_address'],
'end_address': route['legs'][0]['end_address'],
'distance': route['legs'][0]['distance']['text'],
'duration': route['legs'][0]['duration']['text'],
'overview_polyline': route['overview_polyline']['points']
}
# Define vehicle costs as functions
def get_truck_cost():
return 500 # Example cost for truck, can be dynamic
def get_car_cost():
return 300 # Example cost for car, can be dynamic
def get_motorbike_cost():
return 100 # Example cost for motorbike, can be dynamic
# Define a function to return the total number of vehicles
def get_total_vehicles():
return 150 # Example total, can be dynamic
# Return avoided routes on map initialization
@app.route('/get-avoided-routes', methods=['GET'])
def get_avoided_routes():
avoided_routes_json = []
# Retrieve directions for each avoid list pair and add to the JSON
for avoid_start, avoid_end in avoid_list:
directions = get_directions(avoid_start, avoid_end, alternatives=False)
if directions:
avoided_routes_json.append(format_route_to_json(directions[0]))
return jsonify({
'status': 'success',
'avoided_routes': avoided_routes_json
})
@app.route('/get-vehicle-info', methods=['GET'])
def get_vehicle_info():
return jsonify({
'truck_cost': get_truck_cost(),
'car_cost': get_car_cost(),
'motorbike_cost': get_motorbike_cost(),
'total_vehicles': get_total_vehicles()
})
@app.route('/')
def index():
return render_template(
'index.html',
truck_cost=get_truck_cost(),
car_cost=get_car_cost(),
motorbike_cost=get_motorbike_cost(),
total_vehicles=get_total_vehicles()
)
if __name__ == "__main__":
app.run(host='0.0.0.0', port=5858,debug=True)