"""
.. _example_usage_path_computation:

Path computation
=================

In this example, we show how to perform path computation for Coquimbo, a city in La Serena Metropolitan Area in Chile.
"""

# Imports
from uuid import uuid4
from tempfile import gettempdir
from os.path import join
from aequilibrae.utils.create_example import create_example

# We create the example project inside our temp folder
fldr = join(gettempdir(), uuid4().hex)

project = create_example(fldr, "coquimbo")

# %%
import logging
import sys

# We the project opens, we can tell the logger to direct all messages to the terminal as well
logger = project.logger
stdout_handler = logging.StreamHandler(sys.stdout)
formatter = logging.Formatter("%(asctime)s;%(levelname)s ; %(message)s")
stdout_handler.setFormatter(formatter)
logger.addHandler(stdout_handler)

#%%
# Path Computation
# ----------------

# %%
from aequilibrae.paths import PathResults

# %%
# We build all graphs
project.network.build_graphs()
# We get warnings that several fields in the project are filled with NaNs. 
# This is true, but we won't use those fields.

# %%
# We grab the graph for cars
graph = project.network.graphs["c"]

# we also see what graphs are available
# project.network.graphs.keys()

# let's say we want to minimize the distance
graph.set_graph("distance")

# And will skim time and distance while we are at it
graph.set_skimming(["travel_time", "distance"])

# And we will allow paths to be computed going through other centroids/centroid connectors
# required for the Sioux Falls network, as all nodes are centroids
# BE CAREFUL WITH THIS SETTING
graph.set_blocked_centroid_flows(False)

# %%
# Let's instantiate a path results object and prepare it to work with the graph
res = PathResults()
res.prepare(graph)

# compute a path from node 32343 to 22041, thats from near the airport to Fort Lambert, a popular location due to its views of the Coquimbo bay.
res.compute_path(32343, 22041)

# %%

# We can get the sequence of nodes we traverse
res.path_nodes

# %%

# We can get the link sequence we traverse
res.path

# %%

# We can get the mileposts for our sequence of nodes
res.milepost

# Additionally we could also provide `early_exit=True` or `a_star=True` to `compute_path` to adjust its path finding behaviour.
# Providing `early_exit=True` will allow the path finding to quit once it's discovered the destination, this means it will
# perform better for ODs that are topographically close. However, exiting early may cause subsequent calls to `update_trace`
# to recompute the tree in cases where it usually wouldn't. `a_star=True` has precedence of `early_exit=True`.
res.compute_path(32343, 22041, early_exit=True)

# If you'd prefer to find a potentially non-optimal path to the destination faster provide `a_star=True` to use A* with a
# heuristic. With this method `update_trace` will always recompute the path.
res.compute_path(32343, 22041, a_star=True)



# By default a equirectangular heuristic is used. We can view the available heuristics via
res.get_heuristics()

# If you'd like the more accurate, but slower, but more accurate haversine heuristic you can set it using
res.set_heuristic("haversine")

# or
res.compute_path(32343, 22041, a_star=True, heuristic="haversine")

# If we want to compute the path for a different destination and the same origin, we can just do this
# It is way faster when you have large networks
# Here we'll adjust our path to the University of La Serena. Our previous early exit and A* settings will persist with calls
# to `update_trace`. If you'd like to adjust them for subsequent path re-computations set the `res.early_exit` and `res.a_star` attributes.
res.a_star = False
res.update_trace(73131)

# %%

res.path_nodes

# %%
# If you want to show the path in Python
# We do NOT recommend this, though....  It is very slow for real networks
import matplotlib.pyplot as plt
from shapely.ops import linemerge

# %%
links = project.network.links

# We plot the entire network
curr = project.conn.cursor()
curr.execute("Select link_id from links;")

for lid in curr.fetchall():
    geo = links.get(lid[0]).geometry
    plt.plot(*geo.xy, color="red")

path_geometry = linemerge(links.get(lid).geometry for lid in res.path)
plt.plot(*path_geometry.xy, color="blue", linestyle="dashed", linewidth=2)
plt.show()

# %%
project.close()
