{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n\n# Public transport assignment with Optimal Strategies\n\nIn this example, perform a Spiess & Florian assignment. [Click here](https://doi.org/10.1016/0191-2615(89)90034-9)\nto check out the paper.\n\nWe use data from Coquimbo, a city in La Serena Metropolitan Area in Chile.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        ".. admonition:: References\n\n  * :doc:`../../public_transport`\n\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        ".. seealso::\n    Several functions, methods, classes and modules are used in this example:\n\n    * :func:`aequilibrae.transit.Transit`\n    * :func:`aequilibrae.transit.TransitGraphBuilder`\n    * :func:`aequilibrae.paths.TransitClass`\n    * :func:`aequilibrae.paths.TransitAssignment`\n    * :func:`aequilibrae.matrix.AequilibraeMatrix`\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "# Imports for example construction\nfrom uuid import uuid4\nfrom os.path import join\nfrom tempfile import gettempdir\n\nfrom aequilibrae.transit import Transit\nfrom aequilibrae.utils.create_example import create_example"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Let's create an empty project on an arbitrary folder.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "fldr = join(gettempdir(), uuid4().hex)\n\nproject = create_example(fldr, \"coquimbo\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Let's create our ``Transit`` object.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "data = Transit(project)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Graph building\nLet's build the transit network. We'll disable ``outer_stop_transfers`` and ``walking_edges`` \nbecause Coquimbo doesn't have any parent stations.\n\nFor the OD connections we'll use the ``overlapping_regions`` method and create some accurate line geometry later.\nCreating the graph should only take a moment. By default zoning information is pulled from the project network. \nIf you have your own zoning information add it using ``graph.add_zones(zones)`` then ``graph.create_graph()``. \n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "graph = data.create_graph(with_outer_stop_transfers=False, with_walking_edges=False, blocking_centroid_flows=False, connector_method=\"overlapping_regions\")\n\n# We drop geometry here for the sake of display.\ngraph.vertices.drop(columns=\"geometry\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "graph.edges"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "The graphs also also stored in the ``Transit.graphs`` dictionary. They are keyed by the 'period_id' they \nwere created for. A graph for a different 'period_id' can be created by providing ``period_id=`` in the \n``Transit.create_graph`` call. You can view previously created periods with the ``Periods`` object.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "periods = project.network.periods\nperiods.data"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Connector project matching\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "project.network.build_graphs()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Now we'll create the line strings for the access connectors, this step is optional but provides more accurate distance \nestimations and better looking geometry.\n\nBecause Coquimbo doesn't have many walking edges we'll match onto the ``\"c\"`` graph.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "graph.create_line_geometry(method=\"connector project match\", graph=\"c\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Saving and reloading\nLets save all graphs to the 'public_transport.sqlite' database.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "data.save_graphs()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We can reload the saved graphs with ``data.load``. \nThis will create new ``TransitGraphBuilder``\\'s based on the 'period_id' of the saved graphs.\nThe graph configuration is stored in the 'transit_graph_config' table in 'project_database.sqlite' \nas serialised JSON.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "data.load()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We can also remove the previously saved graphs.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "# data.remove_graphs()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Links and nodes are stored in a similar manner to the 'project_database.sqlite' database.\n\n## Reading back into AequilibraE\nYou can create back in a particular graph via it's 'period_id'.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "from aequilibrae.project.database_connection import database_connection\nfrom aequilibrae.transit.transit_graph_builder import TransitGraphBuilder"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "pt_con = database_connection(\"transit\")\n\ngraph_db = TransitGraphBuilder.from_db(pt_con, periods.default_period.period_id)\ngraph_db.vertices.drop(columns=\"geometry\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "graph_db.edges"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Converting to a AequilibraE graph object\nTo perform an assignment we need to convert the graph builder into a graph.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "transit_graph = graph.to_transit_graph()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Mock demand matrix\nWe'll create a mock demand matrix with demand 1 for every zone.\nWe'll also need to convert from ``zone_id``\\'s to ``node_id``\\'s.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import numpy as np\nfrom aequilibrae.matrix import AequilibraeMatrix"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "zones_in_the_model = len(transit_graph.centroids)\n\nnames_list = ['pt']\n\nmat = AequilibraeMatrix()\nmat.create_empty(zones=zones_in_the_model,\n                 matrix_names=names_list,\n                 memory_only=True)\nmat.index = transit_graph.centroids[:]\nmat.matrices[:, :, 0] = np.full((zones_in_the_model, zones_in_the_model), 1.0)\nmat.computational_view()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Hyperpath generation/assignment\nWe'll create a ``TransitAssignment`` object as well as a ``TransitClass``\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "from aequilibrae.paths import TransitAssignment, TransitClass"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Create the assignment class\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "assigclass = TransitClass(name=\"pt\", graph=transit_graph, matrix=mat)\n\nassig = TransitAssignment()\n\nassig.add_class(assigclass)\n\n# We need to tell AequilbraE where to find the appropriate fields we want to use,  \n# as well as the assignment algorithm to use.\nassig.set_time_field(\"trav_time\")\nassig.set_frequency_field(\"freq\")\n\nassig.set_algorithm(\"os\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "When there's multiple matrix cores we'll also need to set the core to use for the demand as we can only assign one\nat a time.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "assigclass.set_demand_matrix_core(\"pt\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Let's perform the assignment for the transit classes added\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "assig.execute()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "View the results\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "assig.results()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Saving results\nWe'll be saving the results to another sqlite db called 'results_database.sqlite'. \nThe 'results' table with 'project_database.sqlite' contains some metadata about each table in \n'results_database.sqlite'.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "assig.save_results(table_name='hyperpath example')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Wrapping up\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "project.close()"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.10.18"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}