From 683dd3154794b4f18361289e2636a819d89d439c Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Fri, 16 Aug 2024 10:12:24 -0300 Subject: [PATCH 01/57] docs --- aequilibrae/parameters.py | 16 ++++++++++++---- .../assignment_workflows/plot_forecasting.py | 2 +- .../plot_subarea_analysis.py | 10 +++++----- docs/source/modeling_with_aequilibrae.rst | 7 ++++--- .../modeling_with_aequilibrae/parameter_file.rst | 13 ++++--------- .../project_database/about.rst | 12 ++++++++---- 6 files changed, 34 insertions(+), 26 deletions(-) diff --git a/aequilibrae/parameters.py b/aequilibrae/parameters.py index 4d030abc4..c23f3e07b 100644 --- a/aequilibrae/parameters.py +++ b/aequilibrae/parameters.py @@ -6,16 +6,24 @@ class Parameters: - """Global parameters module + """Global parameters module. + + Parameters are used in many procedures, and are often defined in the ``parameters.yml`` file ONLY. - Parameters are used in many procedures, and are often defined only in the parameters.yml file ONLY Parameters are organized in the following groups: * assignment * distribution + * network + * links + * modes + * nodes + * osm + * gmns + * osm * system - * report zeros - * temp directory + + Please observe that OSM information handled on network is not the same on the OSM group. .. code-block:: python diff --git a/docs/source/examples/assignment_workflows/plot_forecasting.py b/docs/source/examples/assignment_workflows/plot_forecasting.py index 6039d3ad1..2a428d0fb 100644 --- a/docs/source/examples/assignment_workflows/plot_forecasting.py +++ b/docs/source/examples/assignment_workflows/plot_forecasting.py @@ -2,7 +2,7 @@ .. _example_usage_forecasting: Forecasting -============ +=========== In this example, we present a full forecasting workflow for the Sioux Falls example model. diff --git a/docs/source/examples/assignment_workflows/plot_subarea_analysis.py b/docs/source/examples/assignment_workflows/plot_subarea_analysis.py index d742f37fa..4e02c25d1 100644 --- a/docs/source/examples/assignment_workflows/plot_subarea_analysis.py +++ b/docs/source/examples/assignment_workflows/plot_subarea_analysis.py @@ -107,7 +107,7 @@ # %% # Sub-area preparation -# ~~~~~~~~~~~~~~~~~~ +# ~~~~~~~~~~~~~~~~~~~~ # We need to define some polygon for out sub-area analysis, here we'll use a section of zones and create out polygon as # # the union of their geometry. It's best to choose a polygon that avoids any unnecessary intersections with links as # the # resource requirements of this approach grow quadratically with the number of links cut. @@ -118,7 +118,7 @@ # %% # Sub-area analysis -# ~~~~~~~~~~~~~~~~~~~~ +# ~~~~~~~~~~~~~~~~~ # From here there are two main paths to conduct a sub-area analysis, manual or automated. AequilibraE ships with a small # class that handle most of the details regarding the implementation and extract of the relevant data. It also exposes @@ -203,7 +203,7 @@ def plot_results(link_loads): # %% # Manual sub-area analysis further preparation -# ~~~~~~~~~~~~~~~~~~~~~ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # %% # We take the union of this GeoDataFrame as our polygon. poly = zones.unary_union @@ -234,7 +234,7 @@ def plot_results(link_loads): # %% # Sub-area visualisation -# ~~~~~~~~~~~~~~~~~~~~~ +# ~~~~~~~~~~~~~~~~~~~~~~ # Here we'll quickly visualise what out sub-area is looking like. We'll plot the polygon from our zoning system and the # links that it cuts. points = [(link_id, list(x.coords)) for link_id, x in zip(inner_links.link_id, inner_links.geometry)] @@ -259,7 +259,7 @@ def plot_results(link_loads): # %% # Manual sub-area analysis -# ~~~~~~~~~~~~~~~~~~~~~ +# ~~~~~~~~~~~~~~~~~~~~~~~~ # In order to perform out analysis we need to know what OD pairs have flow that enters and/or exists our polygon. To do # so we perform a select link analysis on all links and pairs of links that cross the boundary. We create them as # tuples of tuples to make represent the select link AND sets. diff --git a/docs/source/modeling_with_aequilibrae.rst b/docs/source/modeling_with_aequilibrae.rst index 537de3176..91c29fbb5 100644 --- a/docs/source/modeling_with_aequilibrae.rst +++ b/docs/source/modeling_with_aequilibrae.rst @@ -15,12 +15,13 @@ In this section you can find a deep dive into modeling with AequilibraE, from a start guide to a complete view into AequilibraE's data structure. .. toctree:: - :hidden: - :maxdepth: 1 + :numbered: 1 + :titlesonly: + :caption: A guide to AequilibraE modeling_with_aequilibrae/project - modeling_with_aequilibrae/parameter_file modeling_with_aequilibrae/project_database + modeling_with_aequilibrae/parameter_file modeling_with_aequilibrae/public_transport modeling_with_aequilibrae/static_traffic_assignment modeling_with_aequilibrae/transit_assignment diff --git a/docs/source/modeling_with_aequilibrae/parameter_file.rst b/docs/source/modeling_with_aequilibrae/parameter_file.rst index 4af121b80..a65e0543e 100644 --- a/docs/source/modeling_with_aequilibrae/parameter_file.rst +++ b/docs/source/modeling_with_aequilibrae/parameter_file.rst @@ -5,12 +5,6 @@ Parameters YAML File The parameter file holds the parameters information for a certain portion of the software. -* :ref:`parameters_assignment` -* :ref:`parameters_distribution` -* :ref:`parameters_network` -* :ref:`parameters_system` -* :ref:`parameters_osm` - .. _parameters_assignment: Assignment @@ -28,7 +22,6 @@ of iterations and target Relative Gap. Although these parameters are required to exist in the parameters file, one can override them during the assignment, as detailed in :ref:`convergence_criteria`. - .. _parameters_distribution: Distribution @@ -107,7 +100,6 @@ The example below also shows that it is possible to mix fields that will be impo `OSM `_ posted speed and number of lanes, and fields that need to be in the network but should not be imported from OSM, such as link capacities. - Node fields ~~~~~~~~~~~ @@ -116,7 +108,6 @@ that it does not make sense to have fields for one or two directions and that it yet to import any tagged values from OSM at the moment, and therefore the parameter *osm_source* would have no effect here. - Open Street Maps ~~~~~~~~~~~~~~~~ The **OSM** group of parameters has two specifications: **modes** and **all_link_types**. @@ -199,3 +190,7 @@ requests *sleeptime*. It is also possible to set a custom address for the Nominatim server, but its use by AequilibraE is so small that it is likely not necessary to do so. + +.. seealso:: + + :doc:`Parameters documentation <../../generated/aequilibrae.Parameters>` \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/project_database/about.rst b/docs/source/modeling_with_aequilibrae/project_database/about.rst index 9f8f66b37..a25f018a0 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/about.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/about.rst @@ -13,14 +13,18 @@ the new field is added, the underlying database is altered and the field will be present when the project is open during future use. This table, which can look something like the example from image below, is required -to exist in AequilibraE but it is not currently actively used by any process but -we strongly recommend not to edit the information on **projection** and +to exist in AequilibraE but it is not currently actively used by any process. +We strongly recommend not to edit the information on **projection** and **aequilibrae_version**, as these are fields that might or might not be used by the software to produce valuable information to the user with regards to opportunities for version upgrades. .. image:: ../../images/about_table_example.png - :width: 800 :alt: About table structure + :align: center -An API for editing the contents of this database is available from the API documentation. \ No newline at end of file +An API for editing the contents of this database is available from the API documentation. + +.. seealso:: + + :doc:`Refer to the documentation <../../generated/aequilibrae.project.About>` \ No newline at end of file From 556bc2c7609fd73b0530e0af8322dee857d8adaa Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Fri, 16 Aug 2024 11:25:37 -0300 Subject: [PATCH 02/57] split network file --- .../project_database/about.rst | 2 +- .../project_database/exporting_to_gmns.rst | 40 --- .../project_database/importing_from_gmns.rst | 45 --- .../project_database/importing_from_osm.rst | 77 ---- .../project_database/matrices.rst | 11 +- .../project_database/network.rst | 332 +----------------- .../project_database/network_geometry.rst | 316 +++++++++++++++++ .../network_import_and_export.rst | 168 +++++++++ 8 files changed, 498 insertions(+), 493 deletions(-) delete mode 100644 docs/source/modeling_with_aequilibrae/project_database/exporting_to_gmns.rst delete mode 100644 docs/source/modeling_with_aequilibrae/project_database/importing_from_gmns.rst delete mode 100644 docs/source/modeling_with_aequilibrae/project_database/importing_from_osm.rst create mode 100644 docs/source/modeling_with_aequilibrae/project_database/network_geometry.rst create mode 100644 docs/source/modeling_with_aequilibrae/project_database/network_import_and_export.rst diff --git a/docs/source/modeling_with_aequilibrae/project_database/about.rst b/docs/source/modeling_with_aequilibrae/project_database/about.rst index a25f018a0..d3770ce2a 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/about.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/about.rst @@ -27,4 +27,4 @@ An API for editing the contents of this database is available from the API docum .. seealso:: - :doc:`Refer to the documentation <../../generated/aequilibrae.project.About>` \ No newline at end of file + :doc:`Editing About table <../../generated/aequilibrae.project.About>` \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/project_database/exporting_to_gmns.rst b/docs/source/modeling_with_aequilibrae/project_database/exporting_to_gmns.rst deleted file mode 100644 index 2906bfccd..000000000 --- a/docs/source/modeling_with_aequilibrae/project_database/exporting_to_gmns.rst +++ /dev/null @@ -1,40 +0,0 @@ -:orphan: - -.. _exporting_to_gmns: - -Exporting AequilibraE model to GMNS format -========================================== - -After loading an existing AequilibraE project, you can export it to GMNS format. - -.. image:: ../../images/plot_export_to_gmns.png - :align: center - :alt: example - :target: _auto_examples/plot_export_to_gmns.html - -| - -As of July 2022, it is possible to export an AequilibraE network to the following -tables in GMNS format: - -* link table -* node table -* use_definition table - -This list does not include the optional use_group table, which is an optional argument -of the ``create_from_gmns()`` function, because mode groups are not used in the -AequilibraE modes table. - -In addition to all GMNS required fields for each of the three exported tables, some -other fields are also added as riminder of where the features came from when looking -back at the AequilibraE project. - -.. note:: - - **When a node is identified as a centroid in the AequilibraE nodes table, this** - **information is transmitted to the GMNS node table by means of the field** - **'node_type', which is set to 'centroid' in this case. The 'node_type' field** - **is an optinal field listed in the GMNS node table specification.** - -You can find the GMNS specification -`here `_. diff --git a/docs/source/modeling_with_aequilibrae/project_database/importing_from_gmns.rst b/docs/source/modeling_with_aequilibrae/project_database/importing_from_gmns.rst deleted file mode 100644 index 0cf9fb250..000000000 --- a/docs/source/modeling_with_aequilibrae/project_database/importing_from_gmns.rst +++ /dev/null @@ -1,45 +0,0 @@ -:orphan: - -.. _importing_from_gmns: - -Importing from files in GMNS format -=================================== - -Before importing a network from a source in GMNS format, it is imperative to know -in which spatial reference its geometries (links and nodes) were created. If the SRID -is different than 4326, it must be passed as an input using the argument 'srid'. - -.. image:: ../../images/plot_import_from_gmns.png - :align: center - :alt: example - :target: _auto_examples/plot_import_from_gmns.html - -| - -As of July 2022, it is possible to import the following files from a GMNS source: - -* link table; -* node table; -* use_group table; -* geometry table. - -You can find the specification for all these tables in the GMNS documentation, -`here `_. - -By default, the method ``create_from_gmns()`` read all required and optional fields -specified in the GMNS link and node tables specification. If you need it to read -any additional fields as well, you have to modify the AequilibraE parameters as -shown in the :ref:`example `. - -When adding a new field to be read in the parameters.yml file, it is important to -keep the "required" key set to False, since you will always be adding a non-required -field. Required fields for a specific table are only those defined in the GMNS -specification. - -.. note:: - - **In the AequilibraE nodes table, if a node is to be identified as a centroid, its** - **'is_centroid' field has to be set to 1. However, this is not part of the GMNS** - **specification. Thus, if you want a node to be identified as a centroid during the** - **import process, in the GMNS node table you have to set the field 'node_type' equals** - **to 'centroid'.** diff --git a/docs/source/modeling_with_aequilibrae/project_database/importing_from_osm.rst b/docs/source/modeling_with_aequilibrae/project_database/importing_from_osm.rst deleted file mode 100644 index 8caf8d90c..000000000 --- a/docs/source/modeling_with_aequilibrae/project_database/importing_from_osm.rst +++ /dev/null @@ -1,77 +0,0 @@ -:orphan: - -.. _importing_from_osm: - -Importing from Open Street Maps -=============================== - -Please review the information :ref:`parameters_osm` - -.. note:: - - **ALL links that cannot be imported due to errors in the SQL insert** - **statements are written to the log file with error message AND the SQL** - **statement itself, and therefore errors in import can be analyzed for** - **re-downloading or fixed by re-running the failed SQL statements after** - **manual fixing** - -.. _sqlite_python_limitations: - -Python limitations ------------------- -As it happens in other cases, Python's usual implementation of SQLite is -incomplete, and does not include R-Tree, a key extension used by Spatialite for -GIS operations. - -For this reason, AequilibraE's default option when importing a network from OSM -is to **NOT create spatial indices**, which renders the network consistency -triggers useless. - -If you are using a vanilla Python installation (your case if you are not sure), -you can import the network without creating indices, as shown below. - -.. code-block:: python - - from aequilibrae.project import Project - - p = Project() - p.new('path/to/project/new/folder') - p.network.create_from_osm(place_name='my favorite place') - p.conn.close() - -And then manually add the spatial index on QGIS by adding both links and nodes -layers to the canvas, and selecting properties and clicking on *create spatial* -*index* for each layer at a time. This action automatically saves the spatial -indices to the sqlite database. - -.. image:: ../../images/qgis_creating_spatial_indices.png - :width: 1383 - :align: center - :alt: Adding Spatial indices with QGIS - -| - -If you are an expert user and made sure your Python installation was compiled -against a complete SQLite set of extensions, then go ahead an import the network -with the option for creating such indices. - -.. code-block:: python - - from aequilibrae.project import Project - - p = Project() - p.new('path/to/project/new/folder/') - p.network.create_from_osm(place_name='my favorite place', spatial_index=True) - p.conn.close() - -If you want to learn a little more about this topic, you can access this -`blog post `_ -or check out the SQLite page on `R-Tree `_. - -If you want to take a stab at solving your SQLite/SpatiaLite problem -permanently, take a look at this -`other blog post `_. - -Please also note that the network consistency triggers will NOT work before -spatial indices have been created and/or if the editing is being done on a -platform that does not support both RTree and Spatialite. diff --git a/docs/source/modeling_with_aequilibrae/project_database/matrices.rst b/docs/source/modeling_with_aequilibrae/project_database/matrices.rst index c87eb7067..6019a1386 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/matrices.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/matrices.rst @@ -5,14 +5,21 @@ Matrices The **matrices** table in the project_database is nothing more than an index of all matrix files contained in the matrices folder inside the AequilibraE project. + This index, which looks like below, has two main columns. The first one is the **file_name**, which contains the actual file name in disk as to allow AequilibraE to find the file, and **name**, which is the name by which the user should refer to the matrix in order to access it through the API. .. image:: ../../images/matrices_table.png - :width: 1000 + :align: center :alt: Matrices table structure As AequilibraE is fully compatible with OMX, the index can have a mix of matrix -types (AEM and OMX) without prejudice to functionality. \ No newline at end of file +types (AEM and OMX) without prejudice to functionality. + +.. seealso:: + + :doc:`Editing Matrices table <../../generated/aequilibrae.project.Matrices>` + + :doc:`AequilibraE Matrix <../../generated/aequilibrae.matrix.AequilibraeMatrix>` \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/project_database/network.rst b/docs/source/modeling_with_aequilibrae/project_database/network.rst index a90a17de3..938eadabb 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/network.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/network.rst @@ -1,7 +1,7 @@ .. _network: Network -~~~~~~~ +======= The objectives of developing a network format for AequilibraE are to provide the users a seamless integration between network data and transportation modeling @@ -19,7 +19,7 @@ other changes to the layers or preventing the changes. **We cannot stress enough how impactful this set of spatial triggers was to** **the transportation modeling practice, as this is the first time a** -transportation network can be edited without specialized software that +**transportation network can be edited without specialized software that** **requires the editing to be done inside such software.** .. note:: @@ -27,330 +27,6 @@ transportation network can be edited without specialized software that implementation requires a complete overahaul of the path-building code, so that is still a long-term goal, barred specific development efforts. -Importing and exporting the network -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. include:: network_import_and_export.rst -Currently AequilibraE can import links and nodes from a network from OpenStreetMaps, -GMNS, and from link layers. AequilibraE can also export the existing network -into GMNS format. There is some valuable information on these topics in the following -pages: - -* :ref:`Importing files in GMNS format ` -* :ref:`Importing from OpenStreetMaps ` -* :ref:`Importing from link layers ` -* :ref:`Exporting AequilibraE model to GMNS format ` - -Dealing with Geometries -^^^^^^^^^^^^^^^^^^^^^^^ -Geometry is a key feature when dealing with transportation infrastructure and -actual travel. For this reason, all datasets in AequilibraE that correspond to -elements with physical GIS representation, links and nodes in particular, are -geo-enabled. - -This also means that the AequilibraE API needs to provide an interface to -manipulate each element's geometry in a convenient way. This is done using the -standard `Shapely `_, and we urge you to study -its comprehensive API before attempting to edit a feature's geometry in memory. - -As we mentioned in other sections of the documentation, the user is also welcome -to use its powerful tools to manipulate your model's geometries, although that -is not recommended, as the "training wheels are off". - -Data consistency -^^^^^^^^^^^^^^^^ - -Data consistency is not achieved as a monolithic piece, but rather through the -*treatment* of specific changes to each aspect of all the objects being -considered (i.e. nodes and links) and the expected consequence to other -tables/elements. To this effect, AequilibraE has triggers covering a -comprehensive set of possible operations for links and nodes, covering both -spatial and tabular aspects of the data. - -Although the behaviour of these trigger is expected to be mostly intuitive -to anybody used to editing transportation networks within commercial modeling -platforms, we have detailed the behaviour for all different network changes in -:ref:`net_section.1` . - -This implementation choice is not, however, free of caveats. Due to -technological limitations of SQLite, some of the desired behaviors identified in -:ref:`net_section.1` cannot be implemented, but such caveats do not impact the -usefulness of this implementation or its robustness in face of minimally careful -use of the tool. - - -.. note:: - This documentation, as well as the SQL code it referes to, comes from the - seminal work done in `TranspoNet `_ - by `Pedro `_ and - `Andrew `_. - -.. _network_triggers_behaviour: - -Network consistency behaviour -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -In order for the implementation of this standard to be successful, it is -necessary to map all the possible user-driven changes to the underlying data and -the behavior the SQLite database needs to demonstrate in order to maintain -consistency of the data. The detailed expected behavior is detailed below. -As each item in the network is edited, a series of checks and changes to other -components are necessary in order to keep the network as a whole consistent. In -this section we list all the possible physical (geometrical) changes to each -element of the network and what behavior (consequences) we expect from each one -of these changes. -Our implementation, in the form of a SQLite database, will be referred to as -network from this point on. - -Ensuring data consistency as each portion of the data is edited is a two part -problem: - -1. Knowing what to do when a certain edit is attempted by the user -2. Automatically applying the tests and consistency checks (and changes) - required on one - -.. _net_section.1: - -Change behavior -^^^^^^^^^^^^^^^ - -In this section we present the mapping of all meaningful operations that a user -can do to links and nodes, and you can use the table below to navigate between -each of the changes to see how they are treated through triggers. - -.. table:: - :align: center - - +--------------------------------------+-----------------------------------+ - | Nodes | Links | - +======================================+===================================+ - | :ref:`net_creating_nodes` | :ref:`net_deleting_link` | - +--------------------------------------+-----------------------------------+ - | :ref:`net_deleting_nodes` | :ref:`net_moving_link_extremity` | - +--------------------------------------+-----------------------------------+ - | :ref:`net_moving_node` | :ref:`net_reshaping_link` | - +--------------------------------------+-----------------------------------+ - | :ref:`net_add_node_field` | :ref:`net_deleting_reqfield_link` | - +--------------------------------------+-----------------------------------+ - | :ref:`net_deleting_node_field` | | - +--------------------------------------+-----------------------------------+ - | :ref:`net_modifying_node_data_entry` | | - +--------------------------------------+-----------------------------------+ - -.. _net_section.1.1: - -Node layer changes and expected behavior -'''''''''''''''''''''''''''''''''''''''' - -There are 6 possible changes envisioned for the network nodes layer, being 3 of -geographic nature and 3 of data-only nature. The possible variations for each -change are also discussed, and all the points where alternative behavior is -conceivable are also explored. - -.. _net_creating_nodes: - -Creating a node -``````````````` - -There are only three situations when a node is to be created: - -- Placement of a link extremity (new or moved) at a position where no node - already exists - -- Splitting a link in the middle - -- Creation of a centroid for later connection to the network - -In all cases a unique node ID needs to be generated for the new node, and all -other node fields should be empty. - -An alternative behavior would be to allow the user to create nodes with no -attached links. Although this would not result in inconsistent networks for -traffic and transit assignments, this behavior would not be considered valid. -All other edits that result in the creation of unconnected nodes or that result -in such case should result in an error that prevents such operation - -Behavior regarding the fields regarding modes and link types is discussed in -their respective table descriptions - -.. _net_deleting_nodes: - -Deleting a node -``````````````` - -Deleting a node is only allowed in two situations: - -- No link is connected to such node (in this case, the deletion of the node - should be handled automatically when no link is left connected to such node) - -- When only two links are connected to such node. In this case, those two links - will be merged, and a standard operation for computing the value of each field - will be applied. - -For simplicity, the operations are: Weighted average for all numeric fields, -copying the fields from the longest link for all non-numeric fields. Length is -to be recomputed in the native distance measure of distance for the projection -being used. - -A node can only be eliminated as a consequence of all links that terminated/ -originated at it being eliminated. If the user tries to delete a node, the -network should return an error and not perform such operation. - -Behavior regarding the fields regarding modes and link types is discussed in -their respective table descriptions - -.. _net_moving_node: - -Moving a node -````````````` - -There are two possibilities for moving a node: Moving to an empty space, and -moving on top of another node. - -- **If a node is moved to an empty space** - -All links originated/ending at that node will have its shape altered to conform -to that new node position and keep the network connected. The alteration of the -link happens only by changing the Latitude and Longitude of the link extremity -associated with that node. - -- **If a node is moved on top of another node** - -All the links that connected to the node on the bottom have their extremities -switched to the node on top -The node on the bottom gets eliminated as a consequence of the behavior listed -on :ref:`net_deleting_nodes` - -Behavior regarding the fields regarding modes and link types is discussed in -their respective table descriptions - -.. _net_add_node_field: - -Adding a data field -``````````````````` - -No consistency check is needed other than ensuring that no repeated data field -names exist - -.. _net_deleting_node_field: - -Deleting a data field -````````````````````` - -If the data field whose attempted deletion is mandatory, the network should -return an error and not perform such operation. Otherwise the operation can be -performed. - -.. _net_modifying_node_data_entry: - -Modifying a data entry -`````````````````````` - -If the field being edited is the node_id field, then all the related tables need -to be edited as well (e.g. a_b and b_node in the link layer, the node_id tagged -to turn restrictions and to transit stops) - -.. _net_section.1.2: - -Link layer changes and expected behavior -'''''''''''''''''''''''''''''''''''''''' - -Network links layer also has some possible changes of geographic and data-only nature. - -.. _net_deleting_link: - -Deleting a link -``````````````` - -In case a link is deleted, it is necessary to check for orphan nodes, and deal -with them as prescribed in :ref:`net_deleting_nodes`. In case one of the link -extremities is a centroid (i.e. field *is_centroid* =1), then the node should not -be deleted even if orphaned. - -Behavior regarding the fields regarding modes and link types is discussed in -their respective table descriptions. - -.. _net_moving_link_extremity: - -Moving a link extremity -``````````````````````` - -This change can happen in two different forms: - -- **The link extremity is moved to an empty space** - -In this case, a new node needs to be created, according to the behavior -described in :ref:`net_creating_nodes` . The information of node ID (A or B -node, depending on the extremity) needs to be updated according to the ID for -the new node created. - -- **The link extremity is moved from one node to another** - -The information of node ID (A or B node, depending on the extremity) needs to be -updated according to the ID for the node the link now terminates in. - -Behavior regarding the fields regarding modes and link types is discussed in -their respective table descriptions. - -.. _net_reshaping_link: - -Re-shaping a link -````````````````` - -When reshaping a link, the only thing other than we expect to be updated in the -link database is their length (or distance, in AequilibraE's field structure). -As of now, distance in AequilibraE is **ALWAYS** measured in meters. - -.. _net_deleting_reqfield_link: - -Deleting a required field -````````````````````````` -Unfortunately, SQLite does not have the resources to prevent a user to remove a -data field from the table. For this reason, if the user removes a required -field, they will most likely corrupt the project. - - -.. _net_section.1.3: - -Field-specific data consistency -''''''''''''''''''''''''''''''' -Some data fields are specially sensitive to user changes. - -.. _net_change_link_distance: - -Link distance -````````````` - -Link distance cannot be changed by the user, as it is automatically recalculated -using the Spatialite function *GeodesicLength*, which always returns distances -in meters. - -.. _net_change_link_direc: - -Link direction -`````````````` - -Triggers enforce link direction to be -1, 0 or 1, and any other value results in -an SQL exception. - -.. _net_change_link_modes: - -*modes* field (Links and Nodes layers) -`````````````````````````````````````` -A serious of triggers are associated with the modes field, and they are all -described in the :ref:`tables_modes`. - -.. _net_change_link_ltypes: - -*link_type* field (Links layer) & *link_types* field (Nodes layer) -`````````````````````````````````````````````````````````````````` -A serious of triggers are associated with the modes field, and they are all -described in the :ref:`tables_link_types`. - -.. _net_change_link_node_ids: - -a_node and b_node -````````````````` -The user should not change the a_node and b_node fields, as they are controlled -by the triggers that govern the consistency between links and nodes. It is not -possible to enforce that users do not change these two fields, as it is not -possible to choose the trigger application sequence in SQLite +.. .. include:: network_geometry.rst \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/project_database/network_geometry.rst b/docs/source/modeling_with_aequilibrae/project_database/network_geometry.rst new file mode 100644 index 000000000..0dec0c53c --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/project_database/network_geometry.rst @@ -0,0 +1,316 @@ +.. _network_geometries: + +Dealing with Geometries +======================= +Geometry is a key feature when dealing with transportation infrastructure and +actual travel. For this reason, all datasets in AequilibraE that correspond to +elements with physical GIS representation, links and nodes in particular, are +geo-enabled. + +This also means that the AequilibraE API needs to provide an interface to +manipulate each element's geometry in a convenient way. This is done using the +standard `Shapely `_, and we urge you to study +its comprehensive API before attempting to edit a feature's geometry in memory. + +As we mentioned in other sections of the documentation, the user is also welcome +to use its powerful tools to manipulate your model's geometries, although that +is not recommended, as the "training wheels are off". + +Data consistency +================ + +Data consistency is not achieved as a monolithic piece, but rather through the +*treatment* of specific changes to each aspect of all the objects being +considered (i.e. nodes and links) and the expected consequence to other +tables/elements. To this effect, AequilibraE has triggers covering a +comprehensive set of possible operations for links and nodes, covering both +spatial and tabular aspects of the data. + +Although the behaviour of these trigger is expected to be mostly intuitive +to anybody used to editing transportation networks within commercial modeling +platforms, we have detailed the behaviour for all different network changes in +:ref:`net_section.1` . + +This implementation choice is not, however, free of caveats. Due to +technological limitations of SQLite, some of the desired behaviors identified in +:ref:`net_section.1` cannot be implemented, but such caveats do not impact the +usefulness of this implementation or its robustness in face of minimally careful +use of the tool. + +.. note:: + This documentation, as well as the SQL code it referes to, comes from the + seminal work done in `TranspoNet `_ + by `Pedro `_ and + `Andrew `_. + +.. _network_triggers_behaviour: + +Network consistency behaviour +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In order for the implementation of this standard to be successful, it is +necessary to map all the possible user-driven changes to the underlying data and +the behavior the SQLite database needs to demonstrate in order to maintain +consistency of the data. The detailed expected behavior is detailed below. +As each item in the network is edited, a series of checks and changes to other +components are necessary in order to keep the network as a whole consistent. In +this section we list all the possible physical (geometrical) changes to each +element of the network and what behavior (consequences) we expect from each one +of these changes. + +Our implementation, in the form of a SQLite database, will be referred to as +network from this point on. + +Ensuring data consistency as each portion of the data is edited is a two part +problem: + +1. Knowing what to do when a certain edit is attempted by the user +2. Automatically applying the tests and consistency checks (and changes) + required on one + +.. _net_section.1: + +Change behavior +^^^^^^^^^^^^^^^ + +In this section we present the mapping of all meaningful operations that a user +can do to links and nodes, and you can use the table below to navigate between +each of the changes to see how they are treated through triggers. + +.. table:: + :align: center + + +--------------------------------------+-----------------------------------+ + | Nodes | Links | + +======================================+===================================+ + | :ref:`net_creating_nodes` | :ref:`net_deleting_link` | + +--------------------------------------+-----------------------------------+ + | :ref:`net_deleting_nodes` | :ref:`net_moving_link_extremity` | + +--------------------------------------+-----------------------------------+ + | :ref:`net_moving_node` | :ref:`net_reshaping_link` | + +--------------------------------------+-----------------------------------+ + | :ref:`net_add_node_field` | :ref:`net_deleting_reqfield_link` | + +--------------------------------------+-----------------------------------+ + | :ref:`net_deleting_node_field` | | + +--------------------------------------+-----------------------------------+ + | :ref:`net_modifying_node_data_entry` | | + +--------------------------------------+-----------------------------------+ + +.. _net_section.1.1: + +Node layer changes and expected behavior +'''''''''''''''''''''''''''''''''''''''' + +There are 6 possible changes envisioned for the network nodes layer, being 3 of +geographic nature and 3 of data-only nature. The possible variations for each +change are also discussed, and all the points where alternative behavior is +conceivable are also explored. + +.. _net_creating_nodes: + +Creating a node +``````````````` + +There are only three situations when a node is to be created: + +- Placement of a link extremity (new or moved) at a position where no node + already exists + +- Splitting a link in the middle + +- Creation of a centroid for later connection to the network + +In all cases a unique node ID needs to be generated for the new node, and all +other node fields should be empty. + +An alternative behavior would be to allow the user to create nodes with no +attached links. Although this would not result in inconsistent networks for +traffic and transit assignments, this behavior would not be considered valid. +All other edits that result in the creation of unconnected nodes or that result +in such case should result in an error that prevents such operation + +Behavior regarding the fields regarding modes and link types is discussed in +their respective table descriptions + +.. _net_deleting_nodes: + +Deleting a node +``````````````` + +Deleting a node is only allowed in two situations: + +- No link is connected to such node (in this case, the deletion of the node + should be handled automatically when no link is left connected to such node) + +- When only two links are connected to such node. In this case, those two links + will be merged, and a standard operation for computing the value of each field + will be applied. + +For simplicity, the operations are: Weighted average for all numeric fields, +copying the fields from the longest link for all non-numeric fields. Length is +to be recomputed in the native distance measure of distance for the projection +being used. + +A node can only be eliminated as a consequence of all links that terminated/ +originated at it being eliminated. If the user tries to delete a node, the +network should return an error and not perform such operation. + +Behavior regarding the fields regarding modes and link types is discussed in +their respective table descriptions + +.. _net_moving_node: + +Moving a node +````````````` + +There are two possibilities for moving a node: Moving to an empty space, and +moving on top of another node. + +- **If a node is moved to an empty space** + +All links originated/ending at that node will have its shape altered to conform +to that new node position and keep the network connected. The alteration of the +link happens only by changing the Latitude and Longitude of the link extremity +associated with that node. + +- **If a node is moved on top of another node** + +All the links that connected to the node on the bottom have their extremities +switched to the node on top +The node on the bottom gets eliminated as a consequence of the behavior listed +on :ref:`net_deleting_nodes` + +Behavior regarding the fields regarding modes and link types is discussed in +their respective table descriptions + +.. _net_add_node_field: + +Adding a data field +``````````````````` + +No consistency check is needed other than ensuring that no repeated data field +names exist + +.. _net_deleting_node_field: + +Deleting a data field +````````````````````` + +If the data field whose attempted deletion is mandatory, the network should +return an error and not perform such operation. Otherwise the operation can be +performed. + +.. _net_modifying_node_data_entry: + +Modifying a data entry +`````````````````````` + +If the field being edited is the node_id field, then all the related tables need +to be edited as well (e.g. a_b and b_node in the link layer, the node_id tagged +to turn restrictions and to transit stops) + +.. _net_section.1.2: + +Link layer changes and expected behavior +'''''''''''''''''''''''''''''''''''''''' + +Network links layer also has some possible changes of geographic and data-only nature. + +.. _net_deleting_link: + +Deleting a link +``````````````` + +In case a link is deleted, it is necessary to check for orphan nodes, and deal +with them as prescribed in :ref:`net_deleting_nodes`. In case one of the link +extremities is a centroid (i.e. field *is_centroid* =1), then the node should not +be deleted even if orphaned. + +Behavior regarding the fields regarding modes and link types is discussed in +their respective table descriptions. + +.. _net_moving_link_extremity: + +Moving a link extremity +``````````````````````` + +This change can happen in two different forms: + +- **The link extremity is moved to an empty space** + +In this case, a new node needs to be created, according to the behavior +described in :ref:`net_creating_nodes` . The information of node ID (A or B +node, depending on the extremity) needs to be updated according to the ID for +the new node created. + +- **The link extremity is moved from one node to another** + +The information of node ID (A or B node, depending on the extremity) needs to be +updated according to the ID for the node the link now terminates in. + +Behavior regarding the fields regarding modes and link types is discussed in +their respective table descriptions. + +.. _net_reshaping_link: + +Re-shaping a link +````````````````` + +When reshaping a link, the only thing other than we expect to be updated in the +link database is their length (or distance, in AequilibraE's field structure). +As of now, distance in AequilibraE is **ALWAYS** measured in meters. + +.. _net_deleting_reqfield_link: + +Deleting a required field +````````````````````````` +Unfortunately, SQLite does not have the resources to prevent a user to remove a +data field from the table. For this reason, if the user removes a required +field, they will most likely corrupt the project. + + +.. _net_section.1.3: + +Field-specific data consistency +''''''''''''''''''''''''''''''' +Some data fields are specially sensitive to user changes. + +.. _net_change_link_distance: + +Link distance +````````````` + +Link distance cannot be changed by the user, as it is automatically recalculated +using the Spatialite function *GeodesicLength*, which always returns distances +in meters. + +.. _net_change_link_direc: + +Link direction +`````````````` + +Triggers enforce link direction to be -1, 0 or 1, and any other value results in +an SQL exception. + +.. _net_change_link_modes: + +*modes* field (Links and Nodes layers) +`````````````````````````````````````` +A serious of triggers are associated with the modes field, and they are all +described in the :ref:`tables_modes`. + +.. _net_change_link_ltypes: + +*link_type* field (Links layer) & *link_types* field (Nodes layer) +`````````````````````````````````````````````````````````````````` +A serious of triggers are associated with the modes field, and they are all +described in the :ref:`tables_link_types`. + +.. _net_change_link_node_ids: + +a_node and b_node +````````````````` +The user should not change the a_node and b_node fields, as they are controlled +by the triggers that govern the consistency between links and nodes. It is not +possible to enforce that users do not change these two fields, as it is not +possible to choose the trigger application sequence in SQLite diff --git a/docs/source/modeling_with_aequilibrae/project_database/network_import_and_export.rst b/docs/source/modeling_with_aequilibrae/project_database/network_import_and_export.rst new file mode 100644 index 000000000..a22da11d9 --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/project_database/network_import_and_export.rst @@ -0,0 +1,168 @@ +Importing and exporting the network +=================================== + +Currently AequilibraE can import links and nodes from a network from OpenStreetMaps, +GMNS, and from link layers. AequilibraE can also export the existing network +into GMNS format. There is some valuable information on these topics in the following +sections. + +Importing from Open Street Maps +------------------------------- + +Please review the information :ref:`parameters_osm` + +.. note:: + + All links that cannot be imported due to errors in the SQL insert + statements are written to the log file with error message AND the SQL + statement itself, and therefore errors in import can be analyzed for + re-downloading or fixed by re-running the failed SQL statements after + manual fixing. + +Python limitations +`````````````````` + +As it happens in other cases, Python's usual implementation of SQLite is +incomplete, and does not include R-Tree, a key extension used by Spatialite for +GIS operations. + +For this reason, AequilibraE's default option when importing a network from OSM +is to **NOT create spatial indices**, which renders the network consistency +triggers useless. + +If you are using a vanilla Python installation (your case if you are not sure), +you can import the network without creating indices, as shown below. + +.. code-block:: python + + from aequilibrae.project import Project + + p = Project() + p.new('path/to/project/new/folder') + p.network.create_from_osm(place_name='my favorite place') + p.conn.close() + +And then manually add the spatial index on QGIS by adding both links and nodes +layers to the canvas, and selecting properties and clicking on *create spatial* +*index* for each layer at a time. This action automatically saves the spatial +indices to the sqlite database. + +.. image:: ../../images/qgis_creating_spatial_indices.png + :align: center + :alt: Adding Spatial indices with QGIS + +If you are an expert user and made sure your Python installation was compiled +against a complete SQLite set of extensions, then go ahead an import the network +with the option for creating such indices. + +.. code-block:: python + + from aequilibrae.project import Project + + p = Project() + p.new('path/to/project/new/folder/') + p.network.create_from_osm(place_name='my favorite place', spatial_index=True) + p.conn.close() + +If you want to learn a little more about this topic, you can access this +`blog post `_ +or check out the SQLite page on `R-Tree `_. +If you want to take a stab at solving your SQLite/Spatialite problem +permanently, take a look at this +`other blog post `_. + +Please also note that the network consistency triggers will NOT work before +spatial indices have been created and/or if the editing is being done on a +platform that does not support both R-Tree and Spatialite. + +.. seealso:: + + :func:`aequilibrae.project.Network.create_from_osm` + +Importing from link layer +------------------------- + + +:ref:`project_from_link_layer` + +Importing from files in GMNS format +----------------------------------- + +Before importing a network from a source in GMNS format, it is imperative to know +in which spatial reference its geometries (links and nodes) were created. If the SRID +is different than 4326, it must be passed as an input using the argument 'srid'. + +.. image:: ../../images/plot_import_from_gmns.png + :align: center + :alt: example + :target: ../../_auto_examples/plot_import_from_gmns.html + +As of July 2022, it is possible to import the following files from a GMNS source: + +* link table; +* node table; +* use_group table; +* geometry table. + +You can find the specification for all these tables in the GMNS documentation, +`here `_. + +By default, the method ``create_from_gmns()`` read all required and optional fields +specified in the GMNS link and node tables specification. If you need it to read +any additional fields as well, you have to modify the AequilibraE parameters as +shown in the :ref:`example `. + +When adding a new field to be read in the parameters.yml file, it is important to +keep the "required" key set to False, since you will always be adding a non-required +field. Required fields for a specific table are only those defined in the GMNS +specification. + +.. note:: + + In the AequilibraE nodes table, if a node is to be identified as a centroid, its + 'is_centroid' field has to be set to 1. However, this is not part of the GMNS + specification. Thus, if you want a node to be identified as a centroid during the + import process, in the GMNS node table you have to set the field 'node_type' equals + to 'centroid'. + +.. seealso:: + + :func:`aequilibrae.project.Network.create_from_gmns` + +Exporting AequilibraE model to GMNS format +------------------------------------------ + +After loading an existing AequilibraE project, you can export it to GMNS format. + +.. image:: ../../images/plot_export_to_gmns.png + :align: center + :alt: example + :target: ../../_auto_examples/plot_export_to_gmns.html + +As of July 2022, it is possible to export an AequilibraE network to the following +tables in GMNS format: + +* link table +* node table +* use_definition table + +This list does not include the optional 'use_group' table, which is an optional argument +of the GMNS function, because mode groups are not used in the AequilibraE modes table. + +In addition to all GMNS required fields for each of the three exported tables, some +other fields are also added as reminder of where the features came from when looking +back at the AequilibraE project. + +.. note:: + + When a node is identified as a centroid in the AequilibraE nodes table, this + information is transmitted to the GMNS node table by means of the field + 'node_type', which is set to 'centroid' in this case. The 'node_type' field + is an optinal field listed in the GMNS node table specification. + +You can find the GMNS specification +`here `_. + +.. seealso:: + + :func:`aequilibrae.project.Network.export_to_gmns` From 56fa11766cbb0c1815fffc558d866b56eb7d554d Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Fri, 16 Aug 2024 14:41:53 -0300 Subject: [PATCH 03/57] updates conf file --- docs/source/conf.py | 3 - .../project_database/network.rst | 2 +- .../project_database/network_geometry.rst | 170 ++++++++---------- .../network_import_and_export.rst | 9 +- 4 files changed, 76 insertions(+), 108 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index c62812031..7ac317195 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -168,9 +168,6 @@ "installation", "validation_benchmarking/ipf_performance", "validation_benchmarking/traffic_assignment", - "modeling_with_aequilibrae/project_database/importing_from_osm", - "modeling_with_aequilibrae/project_database/importing_from_gmns", - "modeling_with_aequilibrae/project_database/exporting_to_gmns", ] # -- Options for manual page output ------------------------------------------ diff --git a/docs/source/modeling_with_aequilibrae/project_database/network.rst b/docs/source/modeling_with_aequilibrae/project_database/network.rst index 938eadabb..f6e3b67fa 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/network.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/network.rst @@ -29,4 +29,4 @@ other changes to the layers or preventing the changes. .. include:: network_import_and_export.rst -.. .. include:: network_geometry.rst \ No newline at end of file +.. include:: network_geometry.rst diff --git a/docs/source/modeling_with_aequilibrae/project_database/network_geometry.rst b/docs/source/modeling_with_aequilibrae/project_database/network_geometry.rst index 0dec0c53c..07dd79177 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/network_geometry.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/network_geometry.rst @@ -1,5 +1,3 @@ -.. _network_geometries: - Dealing with Geometries ======================= Geometry is a key feature when dealing with transportation infrastructure and @@ -17,7 +15,7 @@ to use its powerful tools to manipulate your model's geometries, although that is not recommended, as the "training wheels are off". Data consistency -================ +---------------- Data consistency is not achieved as a monolithic piece, but rather through the *treatment* of specific changes to each aspect of all the objects being @@ -28,12 +26,11 @@ spatial and tabular aspects of the data. Although the behaviour of these trigger is expected to be mostly intuitive to anybody used to editing transportation networks within commercial modeling -platforms, we have detailed the behaviour for all different network changes in -:ref:`net_section.1` . +platforms, we have detailed the behaviour for all different network changes. This implementation choice is not, however, free of caveats. Due to -technological limitations of SQLite, some of the desired behaviors identified in -:ref:`net_section.1` cannot be implemented, but such caveats do not impact the +technological limitations of SQLite, some of the desired behaviors identified +cannot be implemented, but such caveats do not impact the usefulness of this implementation or its robustness in face of minimally careful use of the tool. @@ -43,10 +40,8 @@ use of the tool. by `Pedro `_ and `Andrew `_. -.. _network_triggers_behaviour: - Network consistency behaviour -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +----------------------------- In order for the implementation of this standard to be successful, it is necessary to map all the possible user-driven changes to the underlying data and @@ -68,12 +63,7 @@ problem: 2. Automatically applying the tests and consistency checks (and changes) required on one -.. _net_section.1: - -Change behavior -^^^^^^^^^^^^^^^ - -In this section we present the mapping of all meaningful operations that a user +The table below presents all meaningful operations that a user can do to links and nodes, and you can use the table below to navigate between each of the changes to see how they are treated through triggers. @@ -83,33 +73,29 @@ each of the changes to see how they are treated through triggers. +--------------------------------------+-----------------------------------+ | Nodes | Links | +======================================+===================================+ - | :ref:`net_creating_nodes` | :ref:`net_deleting_link` | + | Creating a node | Deleting a link | +--------------------------------------+-----------------------------------+ - | :ref:`net_deleting_nodes` | :ref:`net_moving_link_extremity` | + | Deleting a node | Moving a link extremity | +--------------------------------------+-----------------------------------+ - | :ref:`net_moving_node` | :ref:`net_reshaping_link` | + | Moving a node | Re-shaping a link | +--------------------------------------+-----------------------------------+ - | :ref:`net_add_node_field` | :ref:`net_deleting_reqfield_link` | + | Adding a data field | Deleting a required field | +--------------------------------------+-----------------------------------+ - | :ref:`net_deleting_node_field` | | + | Deleting a data field | | +--------------------------------------+-----------------------------------+ - | :ref:`net_modifying_node_data_entry` | | + | Modifying a data entry | | +--------------------------------------+-----------------------------------+ -.. _net_section.1.1: - Node layer changes and expected behavior -'''''''''''''''''''''''''''''''''''''''' +```````````````````````````````````````` There are 6 possible changes envisioned for the network nodes layer, being 3 of geographic nature and 3 of data-only nature. The possible variations for each change are also discussed, and all the points where alternative behavior is conceivable are also explored. -.. _net_creating_nodes: - Creating a node -``````````````` +~~~~~~~~~~~~~~~ There are only three situations when a node is to be created: @@ -132,10 +118,8 @@ in such case should result in an error that prevents such operation Behavior regarding the fields regarding modes and link types is discussed in their respective table descriptions -.. _net_deleting_nodes: - Deleting a node -``````````````` +~~~~~~~~~~~~~~~ Deleting a node is only allowed in two situations: @@ -158,158 +142,144 @@ network should return an error and not perform such operation. Behavior regarding the fields regarding modes and link types is discussed in their respective table descriptions -.. _net_moving_node: - Moving a node -````````````` +~~~~~~~~~~~~~ There are two possibilities for moving a node: Moving to an empty space, and moving on top of another node. -- **If a node is moved to an empty space** +- If a node is moved to an empty space -All links originated/ending at that node will have its shape altered to conform -to that new node position and keep the network connected. The alteration of the -link happens only by changing the Latitude and Longitude of the link extremity -associated with that node. + All links originated/ending at that node will have its shape altered to conform + to that new node position and keep the network connected. The alteration of the + link happens only by changing the Latitude and Longitude of the link extremity + associated with that node. -- **If a node is moved on top of another node** +- If a node is moved on top of another node -All the links that connected to the node on the bottom have their extremities -switched to the node on top -The node on the bottom gets eliminated as a consequence of the behavior listed -on :ref:`net_deleting_nodes` + All the links that connected to the node on the bottom have their extremities + switched to the node on top + The node on the bottom gets eliminated as a consequence of the behavior listed + on *Deleting a node* -Behavior regarding the fields regarding modes and link types is discussed in -their respective table descriptions +Behavior regarding the fields related to modes and link types is discussed in +their respective table descriptions. + +.. seealso:: -.. _net_add_node_field: + :ref:`Example - Editing network nodes ` Adding a data field -``````````````````` +~~~~~~~~~~~~~~~~~~~ No consistency check is needed other than ensuring that no repeated data field -names exist - -.. _net_deleting_node_field: +names exist. Deleting a data field -````````````````````` +~~~~~~~~~~~~~~~~~~~~~ If the data field whose attempted deletion is mandatory, the network should return an error and not perform such operation. Otherwise the operation can be performed. -.. _net_modifying_node_data_entry: - Modifying a data entry -`````````````````````` +~~~~~~~~~~~~~~~~~~~~~~ If the field being edited is the node_id field, then all the related tables need to be edited as well (e.g. a_b and b_node in the link layer, the node_id tagged -to turn restrictions and to transit stops) - -.. _net_section.1.2: +to turn restrictions and to transit stops). Link layer changes and expected behavior -'''''''''''''''''''''''''''''''''''''''' +```````````````````````````````````````` Network links layer also has some possible changes of geographic and data-only nature. -.. _net_deleting_link: - Deleting a link -``````````````` +~~~~~~~~~~~~~~~ In case a link is deleted, it is necessary to check for orphan nodes, and deal -with them as prescribed in :ref:`net_deleting_nodes`. In case one of the link -extremities is a centroid (i.e. field *is_centroid* =1), then the node should not +with them as prescribed in *Deleting a node*. In case one of the link +extremities is a centroid (i.e. field ``is_centroid=1``), then the node should not be deleted even if orphaned. Behavior regarding the fields regarding modes and link types is discussed in their respective table descriptions. -.. _net_moving_link_extremity: - Moving a link extremity -``````````````````````` +~~~~~~~~~~~~~~~~~~~~~~~ This change can happen in two different forms: -- **The link extremity is moved to an empty space** +- The link extremity is moved to an empty space -In this case, a new node needs to be created, according to the behavior -described in :ref:`net_creating_nodes` . The information of node ID (A or B -node, depending on the extremity) needs to be updated according to the ID for -the new node created. + In this case, a new node needs to be created, according to the behavior + described in *Creating a node* . The information of node ID (A or B + node, depending on the extremity) needs to be updated according to the ID for + the new node created. -- **The link extremity is moved from one node to another** +- The link extremity is moved from one node to another -The information of node ID (A or B node, depending on the extremity) needs to be -updated according to the ID for the node the link now terminates in. + The information of node ID (A or B node, depending on the extremity) needs to be + updated according to the ID for the node the link now terminates in. -Behavior regarding the fields regarding modes and link types is discussed in -their respective table descriptions. + Behavior regarding the fields regarding modes and link types is discussed in + their respective table descriptions. -.. _net_reshaping_link: +.. seealso:: + + :ref:`Example - Editing network links ` Re-shaping a link -````````````````` +~~~~~~~~~~~~~~~~~ When reshaping a link, the only thing other than we expect to be updated in the link database is their length (or distance, in AequilibraE's field structure). As of now, distance in AequilibraE is **ALWAYS** measured in meters. -.. _net_deleting_reqfield_link: +.. seealso:: + + :ref:`Example - Splitting network links ` Deleting a required field -````````````````````````` +~~~~~~~~~~~~~~~~~~~~~~~~~ + Unfortunately, SQLite does not have the resources to prevent a user to remove a data field from the table. For this reason, if the user removes a required field, they will most likely corrupt the project. - -.. _net_section.1.3: - Field-specific data consistency -''''''''''''''''''''''''''''''' -Some data fields are specially sensitive to user changes. +``````````````````````````````` -.. _net_change_link_distance: +Some data fields are specially sensitive to user changes. Link distance -````````````` +~~~~~~~~~~~~~ Link distance cannot be changed by the user, as it is automatically recalculated using the Spatialite function *GeodesicLength*, which always returns distances in meters. -.. _net_change_link_direc: - Link direction -`````````````` +~~~~~~~~~~~~~~ Triggers enforce link direction to be -1, 0 or 1, and any other value results in an SQL exception. -.. _net_change_link_modes: - *modes* field (Links and Nodes layers) -`````````````````````````````````````` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + A serious of triggers are associated with the modes field, and they are all described in the :ref:`tables_modes`. -.. _net_change_link_ltypes: - *link_type* field (Links layer) & *link_types* field (Nodes layer) -`````````````````````````````````````````````````````````````````` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + A serious of triggers are associated with the modes field, and they are all described in the :ref:`tables_link_types`. -.. _net_change_link_node_ids: - a_node and b_node -````````````````` +~~~~~~~~~~~~~~~~~ + The user should not change the a_node and b_node fields, as they are controlled by the triggers that govern the consistency between links and nodes. It is not possible to enforce that users do not change these two fields, as it is not diff --git a/docs/source/modeling_with_aequilibrae/project_database/network_import_and_export.rst b/docs/source/modeling_with_aequilibrae/project_database/network_import_and_export.rst index a22da11d9..6b89c39de 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/network_import_and_export.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/network_import_and_export.rst @@ -9,7 +9,7 @@ sections. Importing from Open Street Maps ------------------------------- -Please review the information :ref:`parameters_osm` +You can check more specifications on OSM download on the :ref:`parameters_file`. .. note:: @@ -82,8 +82,9 @@ platform that does not support both R-Tree and Spatialite. Importing from link layer ------------------------- - -:ref:`project_from_link_layer` +It is possible to create an AequilibraE project from a link layer, such as a \*.csv file that +contains geometry in WKT, for instance. You can check an example with all functions used in +:ref:`the following example `. Importing from files in GMNS format ----------------------------------- @@ -137,7 +138,7 @@ After loading an existing AequilibraE project, you can export it to GMNS format. .. image:: ../../images/plot_export_to_gmns.png :align: center :alt: example - :target: ../../_auto_examples/plot_export_to_gmns.html + :target: export_to_gmns As of July 2022, it is possible to export an AequilibraE network to the following tables in GMNS format: From 46d608aeed5261530e8e183c3e2e6ad0b5e8c06d Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Fri, 16 Aug 2024 15:56:39 -0300 Subject: [PATCH 04/57] docs --- .../parameter_file.rst | 16 ++-- .../modeling_with_aequilibrae/project.rst | 6 +- .../project_database/link_types.rst | 75 ++++++++++--------- .../project_database/matrices.rst | 8 +- .../project_database/modes.rst | 43 ++++++----- .../project_database/network_geometry.rst | 37 ++++----- .../network_import_and_export.rst | 10 +-- .../project_database/results.rst | 6 +- .../project_database/zones.rst | 8 +- .../static_traffic_assignment.rst | 8 +- .../multi_class_equilibrium.rst | 71 +++++++++--------- 11 files changed, 152 insertions(+), 136 deletions(-) diff --git a/docs/source/modeling_with_aequilibrae/parameter_file.rst b/docs/source/modeling_with_aequilibrae/parameter_file.rst index a65e0543e..d7990b447 100644 --- a/docs/source/modeling_with_aequilibrae/parameter_file.rst +++ b/docs/source/modeling_with_aequilibrae/parameter_file.rst @@ -15,7 +15,6 @@ contains only the convergence criteria for assignment in terms of the maximum nu of iterations and target Relative Gap. .. image:: ../images/parameters_assignment_example.png - :width: 487 :align: center :alt: Assignment example @@ -33,7 +32,6 @@ and maximum trip length to be applied in Iterative Proportional Fitting and synthetic gravity models, as shown below. .. image:: ../images/parameters_distribution_example.png - :width: 546 :align: center :alt: Distribution example @@ -71,12 +69,12 @@ to the use of **integer**, **numeric** and **varchar**. :align: center :alt: Link example -For the case of all non-mandatory fields, two more parameters are possible: *osm_source* and -*osm_behaviour*. Those two fields provide the necessary information for importing data from +For the case of all non-mandatory fields, two more parameters are possible: 'osm_source' and +'osm_behaviour'. Those two fields provide the necessary information for importing data from `Open Street Maps `_ in case such resource is required, and they work in the following way: -*osm_source*: The name of the tag for which data needs to be retrieved. Common tags are +'osm_source': The name of the tag for which data needs to be retrieved. Common tags are **highway**, **maxspeed** and **name**. The import result will contain a null value for all links that do not contain a value for such tag. @@ -87,12 +85,11 @@ values for both directions, and it might have only a tag value for **maxspeed**. Although for **maxspeed** (which is the value for posted speed) we might want to copy the same value for both directions, that would not be true for parameters such as **lanes**, which we might want to split in half for both directions (cases with an odd number of lanes usually have -forward/backward values tagged). For this reason, one can use the parameter *osm_behaviour* +forward/backward values tagged). For this reason, one can use the parameter 'osm_behaviour' to define what to do with numeric tag values that have not been tagged for both directions. the allowed values for this parameter are **copy** and **divide**, as shown below. .. image:: ../images/parameters_links_osm_behaviour.png - :width: 437 :align: center :alt: OSM behaviour examples @@ -110,6 +107,7 @@ would have no effect here. Open Street Maps ~~~~~~~~~~~~~~~~ + The **OSM** group of parameters has two specifications: **modes** and **all_link_types**. **modes** contains the list of key tags we will import for each mode. Description of tags can be found on @@ -154,7 +152,6 @@ and whether we should be saving information to a log file at all, as exemplified below. .. image:: ../images/parameters_system_example.png - :width: 812 :align: center :alt: System example @@ -180,7 +177,6 @@ download a substantial amount of data from an Overpass API, in which case it is recommended to deploy a local Overpass server. .. image:: ../images/parameters_osm_example.png - :width: 840 :align: center :alt: OSM example @@ -193,4 +189,4 @@ use by AequilibraE is so small that it is likely not necessary to do so. .. seealso:: - :doc:`Parameters documentation <../../generated/aequilibrae.Parameters>` \ No newline at end of file + :func:`aequilibrae.Parameters` \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/project.rst b/docs/source/modeling_with_aequilibrae/project.rst index 36d1882cf..720878b70 100644 --- a/docs/source/modeling_with_aequilibrae/project.rst +++ b/docs/source/modeling_with_aequilibrae/project.rst @@ -1,7 +1,7 @@ .. _project: The AequilibraE project ------------------------ +======================= Similarly to commercial packages, any AequilibraE project must have a certain structure and follow a certain set of guidelines in order for software to @@ -28,7 +28,7 @@ for Windows users this dependency is automatically handled under the hood, but the details are also discussed in the aforementioned dependencies section. Project structure -~~~~~~~~~~~~~~~~~ +----------------- Since version 0.7, the AequilibraE project consists of a main folder, where a series of files and sub folders exist, and the current project organization @@ -73,7 +73,7 @@ model, but there is still no support for manually or programmatically adding rou to a route system as of yet. Package components: A conceptual view -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +------------------------------------- As all the components of an AequilibraE model based on open-source software and open-data standards, modeling with AequilibraE is a little different from diff --git a/docs/source/modeling_with_aequilibrae/project_database/link_types.rst b/docs/source/modeling_with_aequilibrae/project_database/link_types.rst index 4b8b13642..39a400746 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/link_types.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/link_types.rst @@ -1,17 +1,18 @@ .. _tables_link_types: Link types table -~~~~~~~~~~~~~~~~ +================ The **link_types** table exists to list all the link types available in the model's network, and its main role is to support processes such as adding -centroids and centroid connectors and to store reference data like default +centroids and centroid connectors, and to store reference data like default lane capacity for each link type. .. _reserved_values: Reserved values -^^^^^^^^^^^^^^^ +--------------- + There are two default link types in the link_types table and that cannot be removed from the model without breaking it. @@ -21,96 +22,102 @@ removed from the model without breaking it. - **default** - This link type exists to facilitate the creation of networks when link types are irrelevant. The identifying letter for this mode is **y**. - That is right, you have from a to x to create your own link types, as well + That is right, you have from **a** to **x** to create your own link types, as well as all upper-case letters of the alphabet. .. _adding_new_link_types: Adding new link_types to a project -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +---------------------------------- Adding link types to a project can be done through the Python API or directly into -the link_types table, which could look like the following. +the 'link_types' table, which could look like the following. .. image:: ../../images/link_types_table.png - :width: 800 + :align: center :alt: Link_types table structure .. note:: - **Both link_type and link_type_id MUST be unique** + Both 'link_type' and 'link_type_id' MUST be unique .. _consistency_triggers: Consistency triggers -^^^^^^^^^^^^^^^^^^^^ -As it happens with the links and nodes tables, -(:ref:`network_triggers_behaviour`), the link_types table is kept consistent -with the links table through the use of database triggers +-------------------- + +As it happens with the links and nodes tables, the 'link_types' table is kept consistent +with the links table through the use of database triggers. .. _change_reserved_types: Changes to reserved link_types -'''''''''''''''''''''''''''''' +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -For both link types mentioned about (**y** & **z**), changes to the *link_type* -and *link_type_id* fields, as well as the removal of any of these records are +For both link types mentioned about (**y** & **z**), changes to the 'link_type' +and 'link_type_id' fields, as well as the removal of any of these records are blocked by database triggers, as to ensure that there is always one generic physical link type and one virtual link type present in the model. .. _change_link_type_for_link: -Changing the link_type for a certain link -''''''''''''''''''''''''''''''''''''''''' +Changing the *link_type* for a certain link +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Whenever we change the link_type associated to a link, we need to check whether +Whenever we change the 'link_type' associated to a link, we need to check whether that link type exists in the links_table. -This condition is ensured by specific trigger checking whether the new link_type +This condition is ensured by specific trigger checking whether the new 'link_type' exists in the link table. If if it does not, the transaction will fail. -We also need to update the **link_types** field the nodes connected to the link -with a new string of all the different **link_type_id**s connected to them. +We also need to update the 'link_types' field the nodes connected to the link +with a new string of all the different 'link_type_id's connected to them. .. _adding_new_link: Adding a new link -''''''''''''''''' +~~~~~~~~~~~~~~~~~ + The exact same behaviour as for :ref:`change_link_type_for_link` applies in this case, but it requires an specific trigger on the **creation** of the link. .. _editing_lt_on_lt_table: -Editing a link_type in the link_types table -''''''''''''''''''''''''''''''''''''''''''' -Whenever we want to edit a link_type in the link_types table, we need to check +Editing a *link_type* in the *link_types* table +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Whenever we want to edit a 'link_type' in the 'link_types' table, we need to check for two conditions: -* The new link_type_id is exactly one character long -* The old link_type is not in use on the network +* The new 'link_type_id' is exactly one character long +* The old 'link_type' is not in use on the network For each condition, a specific trigger was built, and if any of the checks fails, the transaction will fail. The requirements for uniqueness and non-absent values are guaranteed during the -construction of the link_types table by using the keys **UNIQUE** and +construction of the 'link_types' table by using the keys **UNIQUE** and **NOT NULL**. .. _adding_new_ltype: -Adding a new link_type to the link_types table -'''''''''''''''''''''''''''''''''''''''''''''' +Adding a new *link_type* to the *link_types* table +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + In this case, only the first behaviour mentioned above on -:ref:`editing_lt_on_lt_table` applies, the verification that the link_type_id is +:ref:`editing_lt_on_lt_table` applies, the verification that the 'link_type_id' is exactly one character long. Therefore only one new trigger is required. .. _deleting_ltype: -Removing a link_type from the link_types table -'''''''''''''''''''''''''''''''''''''''''''''' +Removing a *link_type* from the *link_types* table +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In counterpoint, only the second behaviour mentioned above on :ref:`editing_lt_on_lt_table` applies in this case, the verification that the old -link_type is not still in use by the network. Therefore only one new trigger is +'link_type' is not still in use by the network. Therefore only one new trigger is required. +.. seealso:: + + :func:`aequilibrae.project.network.LinkTypes` diff --git a/docs/source/modeling_with_aequilibrae/project_database/matrices.rst b/docs/source/modeling_with_aequilibrae/project_database/matrices.rst index 6019a1386..291b15748 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/matrices.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/matrices.rst @@ -1,7 +1,7 @@ .. _matrix_table: -Matrices -~~~~~~~~ +Matrices table +============== The **matrices** table in the project_database is nothing more than an index of all matrix files contained in the matrices folder inside the AequilibraE project. @@ -20,6 +20,6 @@ types (AEM and OMX) without prejudice to functionality. .. seealso:: - :doc:`Editing Matrices table <../../generated/aequilibrae.project.Matrices>` + :func:`aequilibrae.project.Matrices` - :doc:`AequilibraE Matrix <../../generated/aequilibrae.matrix.AequilibraeMatrix>` \ No newline at end of file + :func:`aequilibrae.matrix.AequilibraeMatrix` \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/project_database/modes.rst b/docs/source/modeling_with_aequilibrae/project_database/modes.rst index 70c373963..0b23eedfe 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/modes.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/modes.rst @@ -1,7 +1,7 @@ .. _tables_modes: Modes table -~~~~~~~~~~~ +=========== The **modes** table exists to list all the modes available in the model's network, and its main role is to support the creation of graphs directly from the SQLite @@ -9,28 +9,28 @@ project. .. note:: - **Modes must have a unique mode_id composed of a single letter, which is** - **case-sensitive to a total of 52 possible modes in the model.** + Modes must have a unique mode_id composed of a single letter, which is + case-sensitive to a total of 52 possible modes in the model. As described in the SQL data model, all AequilibraE models are created with 4 standard modes, which can be added to or removed by the user, and would look like the following. .. image:: ../../images/modes_table.png - :width: 500 + :align: center :alt: Modes table structure - Consistency triggers -^^^^^^^^^^^^^^^^^^^^ -As it happens with the links and nodes table (:ref:`network_triggers_behaviour`), -the modes table is kept consistent with the links table through the use of -database triggers. +-------------------- + +As it happens with the links and nodes tables, the modes table is kept consistent with the +links table through the use of database triggers. .. _changing_modes_for_link: Changing the modes allowed in a certain link -'''''''''''''''''''''''''''''''''''''''''''' +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Whenever we change the modes allowed on a link, we need to check for two conditions: @@ -47,20 +47,23 @@ to update the modes field in the nodes table for both of the link's a_node and b_node. Directly changing the modes field in the nodes table -'''''''''''''''''''''''''''''''''''''''''''''''''''' +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + A trigger guarantees that the value being inserted in the field is according to the values found in the associated links' modes field. If the user attempts to overwrite this value, it will automatically be set back to the appropriate value. Adding a new link -''''''''''''''''' +~~~~~~~~~~~~~~~~~ + The exact same behaviour as for :ref:`changing_modes_for_link` applies in this case, but it requires specific new triggers on the **creation** of the link. .. _editing_mode: Editing a mode in the modes table -''''''''''''''''''''''''''''''''' +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Whenever we want to edit a mode in the modes table, we need to check for two conditions: @@ -76,7 +79,8 @@ construction of the modes table by using the keys **UNIQUE** and **NOT NULL**. .. _adding_new_mode: Adding a new mode to the modes table -'''''''''''''''''''''''''''''''''''' +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + In this case, only the first behaviour mentioned above on :ref:`editing_mode` applies, the verification that the mode_id is exactly one character long. Therefore only one new trigger is required. @@ -84,8 +88,13 @@ exactly one character long. Therefore only one new trigger is required. .. _deleting_a_mode: Removing a mode from the modes table -'''''''''''''''''''''''''''''''''''' +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + In counterpoint, only the second behaviour mentioned above on :ref:`editing_mode` applies in this case, the verification that the old -mode_id is not still in use by the network. Therefore only one new trigger is -required. \ No newline at end of file +'mode_id' is not still in use by the network. Therefore only one new trigger is +required. + +.. seealso:: + + :func:`aequilibrae.project.network.Modes` \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/project_database/network_geometry.rst b/docs/source/modeling_with_aequilibrae/project_database/network_geometry.rst index 07dd79177..29f5b681b 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/network_geometry.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/network_geometry.rst @@ -1,5 +1,6 @@ Dealing with Geometries -======================= +----------------------- + Geometry is a key feature when dealing with transportation infrastructure and actual travel. For this reason, all datasets in AequilibraE that correspond to elements with physical GIS representation, links and nodes in particular, are @@ -15,7 +16,7 @@ to use its powerful tools to manipulate your model's geometries, although that is not recommended, as the "training wheels are off". Data consistency ----------------- +~~~~~~~~~~~~~~~~ Data consistency is not achieved as a monolithic piece, but rather through the *treatment* of specific changes to each aspect of all the objects being @@ -41,7 +42,7 @@ use of the tool. `Andrew `_. Network consistency behaviour ------------------------------ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In order for the implementation of this standard to be successful, it is necessary to map all the possible user-driven changes to the underlying data and @@ -95,7 +96,7 @@ change are also discussed, and all the points where alternative behavior is conceivable are also explored. Creating a node -~~~~~~~~~~~~~~~ ++++++++++++++++ There are only three situations when a node is to be created: @@ -119,7 +120,7 @@ Behavior regarding the fields regarding modes and link types is discussed in their respective table descriptions Deleting a node -~~~~~~~~~~~~~~~ ++++++++++++++++ Deleting a node is only allowed in two situations: @@ -143,7 +144,7 @@ Behavior regarding the fields regarding modes and link types is discussed in their respective table descriptions Moving a node -~~~~~~~~~~~~~ ++++++++++++++ There are two possibilities for moving a node: Moving to an empty space, and moving on top of another node. @@ -170,20 +171,20 @@ their respective table descriptions. :ref:`Example - Editing network nodes ` Adding a data field -~~~~~~~~~~~~~~~~~~~ ++++++++++++++++++++ No consistency check is needed other than ensuring that no repeated data field names exist. Deleting a data field -~~~~~~~~~~~~~~~~~~~~~ ++++++++++++++++++++++ If the data field whose attempted deletion is mandatory, the network should return an error and not perform such operation. Otherwise the operation can be performed. Modifying a data entry -~~~~~~~~~~~~~~~~~~~~~~ +++++++++++++++++++++++ If the field being edited is the node_id field, then all the related tables need to be edited as well (e.g. a_b and b_node in the link layer, the node_id tagged @@ -195,7 +196,7 @@ Link layer changes and expected behavior Network links layer also has some possible changes of geographic and data-only nature. Deleting a link -~~~~~~~~~~~~~~~ ++++++++++++++++ In case a link is deleted, it is necessary to check for orphan nodes, and deal with them as prescribed in *Deleting a node*. In case one of the link @@ -206,7 +207,7 @@ Behavior regarding the fields regarding modes and link types is discussed in their respective table descriptions. Moving a link extremity -~~~~~~~~~~~~~~~~~~~~~~~ ++++++++++++++++++++++++ This change can happen in two different forms: @@ -230,7 +231,7 @@ This change can happen in two different forms: :ref:`Example - Editing network links ` Re-shaping a link -~~~~~~~~~~~~~~~~~ ++++++++++++++++++ When reshaping a link, the only thing other than we expect to be updated in the link database is their length (or distance, in AequilibraE's field structure). @@ -241,7 +242,7 @@ As of now, distance in AequilibraE is **ALWAYS** measured in meters. :ref:`Example - Splitting network links ` Deleting a required field -~~~~~~~~~~~~~~~~~~~~~~~~~ ++++++++++++++++++++++++++ Unfortunately, SQLite does not have the resources to prevent a user to remove a data field from the table. For this reason, if the user removes a required @@ -253,32 +254,32 @@ Field-specific data consistency Some data fields are specially sensitive to user changes. Link distance -~~~~~~~~~~~~~ ++++++++++++++ Link distance cannot be changed by the user, as it is automatically recalculated using the Spatialite function *GeodesicLength*, which always returns distances in meters. Link direction -~~~~~~~~~~~~~~ +++++++++++++++ Triggers enforce link direction to be -1, 0 or 1, and any other value results in an SQL exception. *modes* field (Links and Nodes layers) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +++++++++++++++++++++++++++++++++++++++ A serious of triggers are associated with the modes field, and they are all described in the :ref:`tables_modes`. *link_type* field (Links layer) & *link_types* field (Nodes layer) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ A serious of triggers are associated with the modes field, and they are all described in the :ref:`tables_link_types`. a_node and b_node -~~~~~~~~~~~~~~~~~ ++++++++++++++++++ The user should not change the a_node and b_node fields, as they are controlled by the triggers that govern the consistency between links and nodes. It is not diff --git a/docs/source/modeling_with_aequilibrae/project_database/network_import_and_export.rst b/docs/source/modeling_with_aequilibrae/project_database/network_import_and_export.rst index 6b89c39de..e68b270f7 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/network_import_and_export.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/network_import_and_export.rst @@ -1,5 +1,5 @@ Importing and exporting the network -=================================== +----------------------------------- Currently AequilibraE can import links and nodes from a network from OpenStreetMaps, GMNS, and from link layers. AequilibraE can also export the existing network @@ -7,7 +7,7 @@ into GMNS format. There is some valuable information on these topics in the foll sections. Importing from Open Street Maps -------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can check more specifications on OSM download on the :ref:`parameters_file`. @@ -80,14 +80,14 @@ platform that does not support both R-Tree and Spatialite. :func:`aequilibrae.project.Network.create_from_osm` Importing from link layer -------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~ It is possible to create an AequilibraE project from a link layer, such as a \*.csv file that contains geometry in WKT, for instance. You can check an example with all functions used in :ref:`the following example `. Importing from files in GMNS format ------------------------------------ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Before importing a network from a source in GMNS format, it is imperative to know in which spatial reference its geometries (links and nodes) were created. If the SRID @@ -131,7 +131,7 @@ specification. :func:`aequilibrae.project.Network.create_from_gmns` Exporting AequilibraE model to GMNS format ------------------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ After loading an existing AequilibraE project, you can export it to GMNS format. diff --git a/docs/source/modeling_with_aequilibrae/project_database/results.rst b/docs/source/modeling_with_aequilibrae/project_database/results.rst index cb39c29ac..c57d49077 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/results.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/results.rst @@ -1,11 +1,11 @@ .. _tables_results: Results -~~~~~~~ +======= The **results** table exists to hold the metadata for the results stored in the **results_database.sqlite** in the same folder as the model database. In that, -the *table_name* field is unique and must match exactly the table name in the +the 'table_name' field is unique and must match exactly the table name in the **results_database.sqlite**. Although those results could as be stored in the model database, it is possible @@ -15,5 +15,5 @@ essentially clutter the **project_database.sqlite**. As a simple table, it looks as follows: .. image:: ../../images/results_table.png - :width: 800 + :align: center :alt: results table structure diff --git a/docs/source/modeling_with_aequilibrae/project_database/zones.rst b/docs/source/modeling_with_aequilibrae/project_database/zones.rst index 8b21c327b..d6d24e7aa 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/zones.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/zones.rst @@ -1,7 +1,7 @@ .. _tables_zones: Zones table -~~~~~~~~~~~ +=========== The default **zones** table has a **MultiPolygon** geometry type and a limited number of fields, as most of the data is expected to be in the @@ -12,3 +12,9 @@ consistent with what exists to manipulate the other fields in the database. As it happens with links and nodes, zones also have geometries associated with them, and in this case they are of the type . + +You can check :ref:`this example ` to learn how to add zones to your project. + +.. seealso:: + + :func:`aequilibrae.project.Zone` \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/static_traffic_assignment.rst b/docs/source/modeling_with_aequilibrae/static_traffic_assignment.rst index d33eeba87..1df25437f 100644 --- a/docs/source/modeling_with_aequilibrae/static_traffic_assignment.rst +++ b/docs/source/modeling_with_aequilibrae/static_traffic_assignment.rst @@ -3,9 +3,9 @@ Static Traffic Assignment ========================= -Performing static traffic assignment with AequilibraE is not dissimilar than doing so with traditional commercial packages, -as we strive to make it as easy as possible for seasoned modelers to migrate their models and -workflows to AequilibraE. +Performing static traffic assignment with AequilibraE is not dissimilar than doing so with traditional +commercial packages, as we strive to make it as easy as possible for seasoned modelers to migrate their +models and workflows to AequilibraE. Although modeling with AequilibraE should feel somewhat familiar to seasoned modelers, especially those used to programming, the mechanics of traffic assignment in AequilibraE might be foreign to @@ -19,7 +19,7 @@ we detail how these concepts are translated into the AequilibraE tools and recom .. toctree:: :maxdepth: 1 + :caption: Static Traffic Assignment static_traffic_assignment/multi_class_equilibrium static_traffic_assignment/assignment_mechanics - diff --git a/docs/source/modeling_with_aequilibrae/static_traffic_assignment/multi_class_equilibrium.rst b/docs/source/modeling_with_aequilibrae/static_traffic_assignment/multi_class_equilibrium.rst index e75b98994..a6c2b76ed 100644 --- a/docs/source/modeling_with_aequilibrae/static_traffic_assignment/multi_class_equilibrium.rst +++ b/docs/source/modeling_with_aequilibrae/static_traffic_assignment/multi_class_equilibrium.rst @@ -1,7 +1,7 @@ .. _multiclass_equilibrium: Multi-class Equilibrium assignment ----------------------------------- +================================== While single-class equilibrium traffic assignment [1]_ is mathematically simple, multi-class traffic assignment [7]_, especially when including monetary costs @@ -12,20 +12,10 @@ As it is to be expected, strict convergence of multi-class equilibrium assignmen comes at the cost of specific technical requirements and more advanced equilibration algorithms have slightly different requirements. -Cost function -~~~~~~~~~~~~~ - -AequilibraE supports class-=specific cost functions, where each class can include -the following: - -* PCE -* Link-based fixed financial cost components -* Value-of-Time (VoT) - .. _technical_requirements_multi_class: Technical requirements -~~~~~~~~~~~~~~~~~~~~~~ +---------------------- This documentation is not intended to discuss in detail the mathematical requirements of multi-class traffic assignment, which can be found discussed in @@ -33,21 +23,29 @@ detail on [4]_. A few requirements, however, need to be made clear. -* All traffic classes shall have identical free-flow travel times throughout the - network +* All traffic classes shall have identical free-flow travel times throughout the network * Each class shall have an unique Passenger Car Equivalency (PCE) factor for all links -* Volume delay functions shall be monotonically increasing. *Well behaved* - functions are always something we are after +* Volume delay functions shall be monotonically increasing. *Well behaved* functions are + always something we are after + +For the conjugate and biconjugate Frank-Wolfe algorithms it is also necessary that the VDFs +are differentiable. + +Cost function +------------- + +AequilibraE supports class-specific cost functions, where each class can include the following: -For the conjugate and Biconjugate Frank-Wolfe algorithms it is also necessary -that the VDFs are differentiable. +* PCE +* Link-based fixed financial cost components +* Value-of-Time (VoT) .. _convergence_criteria: Convergence criteria -~~~~~~~~~~~~~~~~~~~~ +-------------------- Convergence in AequilibraE is measured solely in terms of relative gap, which is a somewhat old recommendation [5]_, but it is still the most used measure in @@ -61,7 +59,7 @@ are described in detail in the :ref:`parameters_assignment` section, in the :ref:`parameters_file`. Algorithms available -~~~~~~~~~~~~~~~~~~~~ +-------------------- All algorithms have been implemented as a single software class, as the differences between them are simply the step direction and step size after each @@ -86,12 +84,12 @@ iteration of all-or-nothing assignment, as shown in the table below +-------------------------------+-----------------------+----------------------------------+ .. note:: - Our implementations of the conjudate and Biconjugate-Frank-Wolfe methods + Our implementations of the conjugate and biconjugate-Frank-Wolfe methods should be inherently proportional [6]_, but we have not yet carried the appropriate testing that would be required for an empirical proof. Method of Successive Averages (MSA) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This algorithm has been included largely for historical reasons, and we see very little reason to use it. Yet, it has been implemented with the appropriate @@ -99,7 +97,7 @@ computation of relative gap computation and supports all the analysis features available. Frank-Wolfe (FW) -^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~ The implementation of Frank-Wolfe in AequilibraE is extremely simple from an implementation point of view, as we use a generic optimizer from SciPy as an @@ -108,15 +106,15 @@ introduced by LeBlanc in 1975 [2]_. Conjugate Frank-Wolfe -^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~ The conjugate direction algorithm was introduced in 2013 [3]_, which is quite recent if you consider that the Frank-Wolfe algorithm was first applied in the early 1970's, and it was introduced at the same as its Biconjugate evolution, so it was born outdated. -Biconjugate Frank-Wolfe -^^^^^^^^^^^^^^^^^^^^^^^ +Biconjugate Frank-Wolfe (BFW) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The Biconjugate Frank-Wolfe algorithm is currently the fastest converging link- based traffic assignment algorithm used in practice, and it is the recommended @@ -125,7 +123,8 @@ it **requires more memory** during runtime, but very large networks should still fit nicely in systems with 16Gb of RAM. Implementation details & tricks -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +------------------------------- + A few implementation details and tricks are worth mentioning not because they are needed to use the software, but because they were things we grappled with during implementation, and it would be a shame not register it for those looking to @@ -145,12 +144,13 @@ their own purposes. was generalized to the conjugate and biconjugate Frank-Wolfe algorithms. Multi-threaded implementation -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +----------------------------- AequilibraE's All-or-Nothing assignment (the basis of all the other algorithms) has been parallelized in Python using the threading library, which is possible due to the work we have done with memory management to release Python's Global Interpreter Lock. + Other opportunities for parallelization, such as the computation of costs and its derivatives (required during the line-search optimization step), as well as all linear combination operations for vectors and matrices have been achieved @@ -161,11 +161,9 @@ Much of the gains of going back to Cython to parallelize these functions came from making in-place computation using previously existing arrays, as the instantiation of large NumPy arrays can be computationally expensive. -.. _traffic-assignment-references: - - Handling the network -~~~~~~~~~~~~~~~~~~~~ +-------------------- + The other important topic when dealing with multi-class assignment is to have a single consistent handling of networks, as in the end there is only physical network across all modes, regardless of access differences to each mode (e.g. truck @@ -173,10 +171,12 @@ lanes, High-Occupancy Lanes, etc.). This handling is often done with something called a **super-network**. Super-network -^^^^^^^^^^^^^ +~~~~~~~~~~~~~ + We deal with a super-network by having all classes with the same links in their sub-graphs, but assigning *b_node* identical to *a_node* for all links whenever a link is not available for a certain user class. + This approach is slightly less efficient when we are computing shortest paths, but it gets eliminated when topologically compressing the network for centroid-to-centroid path computation and it is a LOT more efficient when we are aggregating flows. @@ -186,10 +186,7 @@ ensure that all graphs will be built in a consistent manner and multi-class assignment is possible. References -~~~~~~~~~~ - -Traffic assignment and equilibrium -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +---------- .. [1] Wardrop J. G. (1952) "Some theoretical aspects of road traffic research." Proceedings of the Institution of Civil Engineers 1952, 1(3):325-362. From 24b8a72ca06d9b7bb7a301c831a2437d7fbc6730 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Thu, 29 Aug 2024 10:55:16 -0300 Subject: [PATCH 05/57] route choice model docs --- aequilibrae/matrix/aequilibrae_matrix.py | 2 +- docs/source/api.rst | 1 + .../assignment_workflows/plot_route_choice.py | 73 ++++++++++--------- .../plot_route_choice_set.py | 37 ++++------ .../plot_subarea_analysis.py | 50 ++++++------- 5 files changed, 78 insertions(+), 85 deletions(-) diff --git a/aequilibrae/matrix/aequilibrae_matrix.py b/aequilibrae/matrix/aequilibrae_matrix.py index 023bc43d6..d2386811f 100644 --- a/aequilibrae/matrix/aequilibrae_matrix.py +++ b/aequilibrae/matrix/aequilibrae_matrix.py @@ -405,7 +405,7 @@ def create_from_trip_list(self, path_to_file: str, from_column: str, to_column: **from_column** (:obj:`str`): trip list file column containing the origin zones numbers - **from_column** (:obj:`str`): trip list file column containing the destination zones numbers + **to_column** (:obj:`str`): trip list file column containing the destination zones numbers **list_cores** (:obj:`list`): list of core columns in the trip list file diff --git a/docs/source/api.rst b/docs/source/api.rst index 6f18302c2..dd5e7af1b 100644 --- a/docs/source/api.rst +++ b/docs/source/api.rst @@ -114,6 +114,7 @@ Paths TransitAssignment HyperpathGenerating OptimalStrategies + SubAreaAnalysis Transit ------- diff --git a/docs/source/examples/assignment_workflows/plot_route_choice.py b/docs/source/examples/assignment_workflows/plot_route_choice.py index a76947016..9d5d970bd 100644 --- a/docs/source/examples/assignment_workflows/plot_route_choice.py +++ b/docs/source/examples/assignment_workflows/plot_route_choice.py @@ -38,18 +38,16 @@ logger.addHandler(stdout_handler) # %% -# Route Choice -# ------------ +# Model parameters +# ---------------- # %% import numpy as np # %% -# Model parameters -# ~~~~~~~~~~~~~~~~ # We'll set the parameters for our route choice model. These are the parameters that will be used to calculate the -# utility of each path. In our example, the utility is equal to *theta* * distance -# And the path overlap factor (PSL) is equal to *beta*. +# utility of each path. In our example, the utility is equal to :math:`theta * distance`, +# and the path overlap factor (PSL) is equal to :math:`beta`. # Distance factor theta = 0.00011 @@ -95,8 +93,8 @@ # %% # Mock demand matrix -# ~~~~~~~~~~~~~~~~~~ -# We'll create a mock demand matrix with demand `1` for every zone. +# ------------------ +# We'll create a mock demand matrix with demand ``1`` for every zone. from aequilibrae.matrix import AequilibraeMatrix names_list = ["demand", "5x demand"] @@ -110,44 +108,46 @@ # %% # Route Choice class -# ~~~~~~~~~~~~~~~~~~ +# ------------------ # Here we'll construct and use the Route Choice class to generate our route sets from aequilibrae.paths import RouteChoice # %% # This object construct might take a minute depending on the size of the graph due to the construction of the compressed -# link to network link mapping that's required. This is a one time operation per graph and is cached. We need to supply -# a Graph and an AequilibraeMatrix or DataFrame via the `add_demand` method , if demand is not provided link loading +# link to network link mapping that's required. This is a one time operation per graph and is cached. We need to supply +# a Graph and an AequilibraeMatrix or DataFrame via the ``add_demand`` method, if demand is not provided link loading # cannot be preformed. rc = RouteChoice(graph) rc.add_demand(mat) # %% -# Here we'll set the parameters of our set generation. There are two algorithms available: Link penalisation, or BFSLE +# Here we'll set the parameters of our set generation. There are two algorithms available: Link penalisation, and BFSLE # based on the paper -# "Route choice sets for very high-resolution data" by Nadine Rieser-Schüssler, Michael Balmer & Kay W. Axhausen (2013). -# https://doi.org/10.1080/18128602.2012.671383 +# `"Route choice sets for very high-resolution data" `_ +# by Nadine Rieser-Schüssler, Michael Balmer & Kay W. Axhausen (2013). # -# Our BFSLE implementation is slightly different and has extended to allow applying link penalisation as well. Every -# link in all routes found at a depth are penalised with the `penalty` factor for the next depth. So at a depth of 0 no -# links are penalised nor removed. At depth 1, all links found at depth 0 are penalised, then the links marked for -# removal are removed. All links in the routes found at depth 1 are then penalised for the next depth. The penalisation -# compounds. Pass set `penalty=1.0` to disable. +# Our BFSLE implementation has been extended to allow applying link penalisation as well. Every +# link in all routes found at a depth are penalised with the `penalty` factor for the next depth. +# So at a depth of 0 no links are penalised nor removed. At depth 1, all links found at depth 0 are penalised, +# then the links marked for removal are removed. All links in the routes found at depth 1 are then penalised +# for the next depth. The penalisation compounds. Pass set ``penalty=1.0`` to disable. # -# To assist in filtering out bad results during the assignment, a `cutoff_prob` parameter can be provided to exclude -# routes from the path-sized logit model. The `cutoff_prob` is used to compute an inverse binary logit and obtain a max -# difference in utilities. If a paths total cost is greater than the minimum cost path in the route set plus the max +# To assist in filtering out bad results during the assignment, a ``cutoff_prob`` parameter can be provided to exclude +# routes from the path-sized logit model. The ``cutoff_prob`` is used to compute an inverse binary logit and obtain a +# max difference in utilities. If a paths total cost is greater than the minimum cost path in the route set plus the max # difference, the route is excluded from the PSL calculations. The route is still returned, but with a probability of -# 0.0. +# ``0.0``. # -# The `cutoff_prob` should be in the range [0, 1]. It is then rescaled internally to [0.5, 1] as probabilities below 0.5 -# produce negative differences in utilities. A higher `cutoff_prob` includes more routes. A value of `0.0` will only -# include the minimum cost route. A value of `1.0` includes all routes. +# The ``cutoff_prob`` should be in the range :math:`[0, 1]`. It is then rescaled internally to :math:`[0.5, 1]` +# as probabilities below ``0.5`` produce negative differences in utilities. A higher ``cutoff_prob`` includes +# more routes. A value of ``0.0`` will only include the minimum cost route. A value of ``1.0`` includes all +# routes. # -# It is highly recommended to set either `max_routes` or `max_depth` to prevent runaway results. +# It is highly recommended to set either ``max_routes`` or ``max_depth`` to prevent runaway results. -# %% # rc.set_choice_set_generation("link-penalisation", max_routes=5, penalty=1.02) + +# %% rc.set_choice_set_generation("bfsle", max_routes=5) # %% @@ -166,11 +166,8 @@ res = rc.get_results().to_pandas() res.head() - # %% # let's define a function to plot assignment results - - def plot_results(link_loads): import folium import geopandas as gpd @@ -204,14 +201,16 @@ def plot_results(link_loads): folium.LayerControl().add_to(map_osm) return map_osm - # %% plot_results(rc.get_load_results()["demand"]) # %% # To perform a batch operation we need to prepare the object first. We can either provide a list of tuple of the OD # pairs we'd like to use, or we can provided a 1D list and the generation will be run on all permutations. + # rc.prepare(graph.centroids[:5]) + +# %% rc.prepare() # %% @@ -224,13 +223,14 @@ def plot_results(link_loads): # Since we provided a matrix initially we can also perform link loading based on our assignment results. rc.get_load_results() -# %% we can plot these as well +# %% +# we can plot these as well plot_results(rc.get_load_results()["demand"]) # %% # Select link analysis -# ~~~~~~~~~~~~~~~~~~~~ -# We can also enable select link analysis by providing the links and the directions that we are interested in. Here we +# -------------------- +# We can also enable select link analysis by providing the links and the directions that we are interested in. Here we # set the select link to trigger when (7369, 1) and (20983, 1) is utilised in "sl1" and "sl2" when (7369, 1) is # utilised. rc.set_select_links({"sl1": [[(7369, 1), (20983, 1)]], "sl2": [[(7369, 1)]]}) @@ -244,7 +244,8 @@ def plot_results(link_loads): # %% # We can also access the OD matrices for this link loading. These matrices are sparse and can be converted to # scipy.sparse matrices for ease of use. They're stored in a dictionary where the key is the matrix name concatenated -# wit the select link set name via an underscore. These matrices are constructed during `get_select_link_loading_results`. +# with the select link set name via an underscore. +# These matrices are constructed during ``get_select_link_loading_results``. rc.get_select_link_od_matrix_results() # %% diff --git a/docs/source/examples/assignment_workflows/plot_route_choice_set.py b/docs/source/examples/assignment_workflows/plot_route_choice_set.py index 84ea08f13..0448ede93 100644 --- a/docs/source/examples/assignment_workflows/plot_route_choice_set.py +++ b/docs/source/examples/assignment_workflows/plot_route_choice_set.py @@ -6,11 +6,9 @@ In this example, we show how to generate route choice sets for estimation of route choice models, using a a city in La Serena Metropolitan Area in Chile. - """ # %% - # Imports from uuid import uuid4 from tempfile import gettempdir @@ -27,12 +25,10 @@ project = create_example(fldr, "coquimbo") # %% -# Choice set generation -# --------------------- - +# Model parameters +# ---------------- # %% - od_pairs_of_interest = [(71645, 79385), (77011, 74089)] nodes_of_interest = (71645, 74089, 77011, 79385) @@ -42,7 +38,6 @@ # 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"] @@ -56,35 +51,36 @@ graph.prepare_graph(np.array(nodes_of_interest)) # We allow flows through "centroid connectors" because our centroids are not really centroids -# If we have actual centroid connectors in the network (and more than one per centroid) , then we +# If we have actual centroid connectors in the network (and more than one per centroid), then we # should remove them from the graph graph.set_blocked_centroid_flows(False) # %% # Route Choice class -# ~~~~~~~~~~~~~~~~~~ +# ------------------ # Here we'll construct and use the Route Choice class to generate our route sets from aequilibrae.paths import RouteChoice -# %% This object construct might take a minute depending on the size of the graph due to the construction of the -# compressed link to network link mapping that's required. This is a one time operation per graph and is cached. We -# need to supply a Graph and an AequilibraeMatrix or DataFrame via the `add_demand` method , if demand is not provided +# %% +# This object construct might take a minute depending on the size of the graph due to the construction of the +# compressed link to network link mapping that's required. This is a one time operation per graph and is cached. We +# need to supply a Graph and an AequilibraeMatrix or DataFrame via the ``add_demand`` method, if demand is not provided # link loading cannot be preformed. rc = RouteChoice(graph) # %% # Here we'll set the parameters of our set generation. There are two algorithms available: Link penalisation, and BFSLE # based on the paper -# "Route choice sets for very high-resolution data" by Nadine Rieser-Schüssler, Michael Balmer & Kay W. Axhausen (2013). -# https://doi.org/10.1080/18128602.2012.671383 +# `"Route choice sets for very high-resolution data" `_ +# by Nadine Rieser-Schüssler, Michael Balmer & Kay W. Axhausen (2013). # # Our BFSLE implementation has been extended to allow applying link penalisation as well. Every -# link in all routes found at a depth are penalised with the `penalty` factor for the next depth. So at a depth of 0 no -# links are penalised nor removed. At depth 1, all links found at depth 0 are penalised, then the links marked for -# removal are removed. All links in the routes found at depth 1 are then penalised for the next depth. The penalisation -# compounds. Pass set `penalty=1.0` to disable. +# link in all routes found at a depth are penalised with the `penalty` factor for the next depth. +# So at a depth of 0 no links are penalised nor removed. At depth 1, all links found at depth 0 are penalised, +# then the links marked for removal are removed. All links in the routes found at depth 1 are then penalised +# for the next depth. The penalisation compounds. Pass set ``penalty=1.0`` to disable. # -# It is highly recommended to set either `max_routes` or `max_depth` to prevent runaway results. +# It is highly recommended to set either ``max_routes`` or ``max_depth`` to prevent runaway results. # rc.set_choice_set_generation("link-penalisation", max_routes=5, penalty=1.02) @@ -100,14 +96,12 @@ # Plotting choice sets # -------------------- - # %% # Now we will plot the paths we just created for the second OD pair import folium import geopandas as gpd # %% - # Let's create a separate for each route so we can visualize one at a time rlyr1 = folium.FeatureGroup("route 1") rlyr2 = folium.FeatureGroup("route 2") @@ -118,7 +112,6 @@ layers = [rlyr1, rlyr2, rlyr3, rlyr4, rlyr5] # %% - # We get the data we will use for the plot: Links, Nodes and the route choice set links = gpd.GeoDataFrame(project.network.links.data, crs=4326) nodes = gpd.GeoDataFrame(project.network.nodes.data, crs=4326) diff --git a/docs/source/examples/assignment_workflows/plot_subarea_analysis.py b/docs/source/examples/assignment_workflows/plot_subarea_analysis.py index 4e02c25d1..a271b9e0b 100644 --- a/docs/source/examples/assignment_workflows/plot_subarea_analysis.py +++ b/docs/source/examples/assignment_workflows/plot_subarea_analysis.py @@ -1,8 +1,8 @@ """ .. _example_usage_route_choice: -Route Choice -============ +Route Choice with sub-area analysis +=================================== In this example, we show how to perform sub-area analysis using route choice assignment, for a city in La Serena Metropolitan Area in Chile. @@ -41,16 +41,12 @@ stdout_handler.setFormatter(formatter) logger.addHandler(stdout_handler) -# %% -# Route Choice -# ------------ - # %% # Model parameters -# ~~~~~~~~~~~~~~~~ +# ---------------- # We'll set the parameters for our route choice model. These are the parameters that will be used to calculate the -# utility of each path. In our example, the utility is equal to *theta* * distance -# And the path overlap factor (PSL) is equal to *beta*. +# utility of each path. In our example, the utility is equal to :math:`theta * distance`, +# and the path overlap factor (PSL) is equal to :math:`beta`. # Distance factor theta = 0.011 @@ -86,15 +82,15 @@ # %% # We allow flows through "centroid connectors" because our centroids are not really centroids -# If we have actual centroid connectors in the network (and more than one per centroid) , then we +# If we have actual centroid connectors in the network (and more than one per centroid), then we # should remove them from the graph graph.set_blocked_centroid_flows(False) graph.graph.head() # %% # Mock demand matrix -# ~~~~~~~~~~~~~~~~~~ -# We'll create a mock demand matrix with demand `10` for every zone. +# ------------------ +# We'll create a mock demand matrix with demand ``10`` for every zone. from aequilibrae.matrix import AequilibraeMatrix names_list = ["demand"] @@ -107,10 +103,10 @@ # %% # Sub-area preparation -# ~~~~~~~~~~~~~~~~~~~~ +# -------------------- # We need to define some polygon for out sub-area analysis, here we'll use a section of zones and create out polygon as # # the union of their geometry. It's best to choose a polygon that avoids any unnecessary intersections with links as -# the # resource requirements of this approach grow quadratically with the number of links cut. +# the resource requirements of this approach grow quadratically with the number of links cut. zones_of_interest = [29, 30, 31, 32, 33, 34, 37, 38, 39, 40, 49, 50, 51, 52, 57, 58, 59, 60] zones = gpd.GeoDataFrame(project.zoning.data).set_index("zone_id") zones = zones.loc[zones_of_interest] @@ -118,8 +114,7 @@ # %% # Sub-area analysis -# ~~~~~~~~~~~~~~~~~ - +# ----------------- # From here there are two main paths to conduct a sub-area analysis, manual or automated. AequilibraE ships with a small # class that handle most of the details regarding the implementation and extract of the relevant data. It also exposes # all the tools necessary to conduct this analysis yourself if you need fine grained control. @@ -127,9 +122,8 @@ # %% # Automated sub-area analysis # ~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # We first construct out SubAreaAnalysis object from the graph, zones, and matrix we previously constructed, then -# configure the route choice assignment and execute it. From there the `post_process` method is able to use the route +# configure the route choice assignment and execute it. From there the ``post_process`` method is able to use the route # choice assignment results to construct the desired demand matrix as a DataFrame. from aequilibrae.paths import SubAreaAnalysis @@ -165,7 +159,6 @@ stroke=False, ) - def plot_results(link_loads): link_loads = link_loads[link_loads.tot > 0] max_load = link_loads["tot"].max() @@ -202,8 +195,9 @@ def plot_results(link_loads): map # %% -# Manual sub-area analysis further preparation -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Sub-area further preparation +# ```````````````````````````` + # %% # We take the union of this GeoDataFrame as our polygon. poly = zones.unary_union @@ -234,7 +228,9 @@ def plot_results(link_loads): # %% # Sub-area visualisation -# ~~~~~~~~~~~~~~~~~~~~~~ +# `````````````````````` + +# %% # Here we'll quickly visualise what out sub-area is looking like. We'll plot the polygon from our zoning system and the # links that it cuts. points = [(link_id, list(x.coords)) for link_id, x in zip(inner_links.link_id, inner_links.geometry)] @@ -260,8 +256,10 @@ def plot_results(link_loads): # %% # Manual sub-area analysis # ~~~~~~~~~~~~~~~~~~~~~~~~ + +# %% # In order to perform out analysis we need to know what OD pairs have flow that enters and/or exists our polygon. To do -# so we perform a select link analysis on all links and pairs of links that cross the boundary. We create them as +# so we perform a select link analysis on all links and pairs of links that cross the boundary. We create them as # tuples of tuples to make represent the select link AND sets. edge_pairs = {x: (x,) for x in itertools.permutations(g.index, r=2)} single_edges = {x: ((x,),) for x in g.index} @@ -282,8 +280,8 @@ def plot_results(link_loads): # %% # This object construction might take a minute depending on the size of the graph due to the construction of the -# compressed link to network link mapping that's required. This is a one time operation per graph and is cached. We -# need to supply a Graph and an AequilibraeMatrix or DataFrame via the `add_demand` method , if demand is not provided +# compressed link to network link mapping that's required. This is a one time operation per graph and is cached. We +# need to supply a Graph and an AequilibraeMatrix or DataFrame via the ``add_demand`` method, if demand is not provided # link loading cannot be preformed. rc = RouteChoice(graph) rc.add_demand(mat) @@ -294,7 +292,7 @@ def plot_results(link_loads): # %% # For the sake of demonstration we limit out demand matrix to a few OD pairs. This filter is also possible with the -# automated approach, just edit the `subarea.rc.demand.df` DataFrame, however make sure the index remains intact. +# automated approach, just edit the ``subarea.rc.demand.df`` DataFrame, however make sure the index remains intact. ods_pairs_of_interest = [ (4, 39), (92, 37), From 2d38c12cade044e66a7c3973df7d37aabb9a5f8e Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Thu, 29 Aug 2024 14:11:58 -0300 Subject: [PATCH 06/57] . --- aequilibrae/paths/route_choice.py | 46 ++++++++++--------- aequilibrae/paths/sub_area.py | 11 +++-- docs/source/api.rst | 1 + ..._choice.py => plot_route_choice_basics.py} | 3 -- .../plot_subarea_analysis.py | 2 +- 5 files changed, 34 insertions(+), 29 deletions(-) rename docs/source/examples/assignment_workflows/{plot_route_choice.py => plot_route_choice_basics.py} (99%) diff --git a/aequilibrae/paths/route_choice.py b/aequilibrae/paths/route_choice.py index 5f4fc7727..9f1b202dc 100644 --- a/aequilibrae/paths/route_choice.py +++ b/aequilibrae/paths/route_choice.py @@ -96,15 +96,15 @@ def set_choice_set_generation(self, /, algorithm: str, **kwargs) -> None: if returned with fewer than `max_routes`. It has a default value of `100`. - When using BFSLE `max_depth` corresponds to the maximum height of the graph of graphs. It's value is - largely dependent on the size of the paths within the network. For very small networks a value of 10 - is a recommended starting point. For large networks a good starting value is 5. Increase the value - until the number of desired routes is being consistently returned. If it is exceeded then the route set - if returned with fewer than `max_routes`. + largely dependent on the size of the paths within the network. For very small networks a value of 10 + is a recommended starting point. For large networks a good starting value is 5. Increase the value + until the number of desired routes is being consistently returned. If it is exceeded then the route set + if returned with fewer than `max_routes`. - When using LP, `max_depth` corresponds to the maximum number of iterations performed. While not enforced, - it should be higher than `max_routes`. It's value is dependent on the magnitude of the cost field, - specifically it's related to the log base `penalty` of the ratio of costs between two alternative routes. - If it is exceeded then the route set if returned with fewer than `max_routes`. + it should be higher than `max_routes`. It's value is dependent on the magnitude of the cost field, + specifically it's related to the log base `penalty` of the ratio of costs between two alternative routes. + If it is exceeded then the route set if returned with fewer than `max_routes`. Additionally BFSLE has the option to incorporate link penalisation. Every link in all routes found at a depth are penalised with the `penalty` factor for the next depth. So at a depth of 0 no links are penalised nor @@ -124,6 +124,7 @@ def set_choice_set_generation(self, /, algorithm: str, **kwargs) -> None: :Arguments: **algorithm** (:obj:`str`): Algorithm to be used + **kwargs** (:obj:`dict`): Dictionary with all parameters for the algorithm """ algo_dict = {i: i for i in self.all_algorithms} @@ -186,9 +187,9 @@ def add_demand(self, demand, fill: float = 0.0): :Arguments: **demand** (:obj:`Union[pd.DataFrame, AequilibraeMatrix]`): Demand to add to assignment. If the supplied - demand is a DataFrame, it should have a 2-level MultiIndex of Origin and Destination node IDs. If an - AequilibraE matrix is supplied node IDs will be inferred from the index. Demand values should be either - float32s or float64s. + demand is a DataFrame, it should have a 2-level MultiIndex of Origin and Destination node IDs. If an + AequilibraE matrix is supplied node IDs will be inferred from the index. Demand values should be either + float32s or float64s. **fill** (:obj:`float`): Value to fill any NaNs with. """ @@ -205,10 +206,10 @@ def prepare(self, nodes: Union[List[int], List[Tuple[int, int]], None] = None) - :Arguments: **nodes** (:obj:`Union[list[int], list[tuple[int, int]]]`): List of node IDs to operate on. If a 1D list is - provided, OD pairs are taken to be all pair permutations of the list. If a list of pairs is provided - OD pairs are taken as is. All node IDs must be present in the compressed graph. To make a node ID - always appear in the compressed graph add it as a centroid. Duplicates will be dropped on execution. - If *None* is provided, all OD pairs with non-zero flows will be used. + provided, OD pairs are taken to be all pair permutations of the list. If a list of pairs is provided + OD pairs are taken as is. All node IDs must be present in the compressed graph. To make a node ID + always appear in the compressed graph add it as a centroid. Duplicates will be dropped on execution. + If *None* is provided, all OD pairs with non-zero flows will be used. """ if nodes is not None and not self.demand.no_demand(): raise ValueError("provide either `nodes` or set a `demand` matrix, not both") @@ -245,11 +246,13 @@ def execute_single(self, origin: int, destination: int, demand: float = 0.0) -> :Arguments: **origin** (:obj:`int`): Origin node ID. + **destination** (:obj:`int`): Destination node ID. + **demand** (:obj:`float`): If provided an assignment will be performed with this demand. :Returns: - ***route set** (:obj:`List[Tuple[int]]`): A list of routes as tuples of link IDs. + **route set** (:obj:`List[Tuple[int]]`): A list of routes as tuples of link IDs. """ self.procedure_id = uuid4().hex self.procedure_date = str(datetime.today()) @@ -349,8 +352,7 @@ def get_load_results(self) -> pd.DataFrame: :Returns: **dataset** (:obj:`Union[Tuple[pd.DataFrame, pd.DataFrame], pd.DataFrame]`): - A tuple of link loading results as DataFrames. - Columns are the matrix name concatenated direction. + A tuple of link loading results as DataFrames. Columns are the matrix name concatenated direction. """ if self.demand.no_demand(): @@ -399,10 +401,10 @@ def set_select_links( :Arguments: **links** (:obj:`Union[None, Dict[Hashable, List[Union[Tuple[int, int], List[Tuple[int, int]]]]]]`): - Name of link set and Link IDs and directions to be used in select link analysis. + Name of link set and Link IDs and directions to be used in select link analysis. **link_loading** (:obj:`bool`): Enable select link loading. If disabled only OD matrix results are - available. + available. """ self._selected_links = {} @@ -466,7 +468,7 @@ def get_select_link_loading_results(self) -> pd.DataFrame: :Returns: **dataset** (:obj:`Tuple[pd.DataFrame, pd.DataFrame]`): Select link loading results as DataFrames. - Columns are the matrix name concatenated with the select link set and direction. + Columns are the matrix name concatenated with the select link set and direction. """ if self.demand.no_demand(): @@ -492,7 +494,7 @@ def get_select_link_od_matrix_results(self) -> Dict[str, Dict[str, scipy.sparse. :Returns: **select link OD matrix results** (:obj:`Dict[str, Dict[str, scipy.sparse.coo_matrix]]`): Returns a dict of - select link set names to a dict of demand column names to a sparse OD matrix + select link set names to a dict of demand column names to a sparse OD matrix """ if self.demand.no_demand(): @@ -532,6 +534,7 @@ def save_link_flows(self, table_name: str, project=None) -> None: :Arguments: **table_name** (:obj:`str`): Name of the table being inserted to. + **project** (:obj:`Project`, `Optional`): Project we want to save the results to. Defaults to the active project """ @@ -557,6 +560,7 @@ def save_select_link_flows(self, table_name: str, project=None) -> None: :Arguments: **table_name** (:obj:`str`): Name of the table being inserted to and the name of the OpenMatrix file used for OD matrices. + **project** (:obj:`Project`, `Optional`): Project we want to save the results to. Defaults to the active project """ diff --git a/aequilibrae/paths/sub_area.py b/aequilibrae/paths/sub_area.py index da3623459..4079c4903 100644 --- a/aequilibrae/paths/sub_area.py +++ b/aequilibrae/paths/sub_area.py @@ -28,12 +28,15 @@ def __init__( :Arguments: **graph** (:obj:`Graph`): AequilibraE graph object to use + **subarea** (:obj:`geopandas.GeoDataFrame`): A GeoPandas GeoDataFrame whose `unary_union` represents the - sub-area. + sub-area. + **demand** (:obj:`Union[pandas.DataFrame, AequilibraeMatrix]`): The demand matrix to provide to the route - choice assignment. + choice assignment. Minimal example: + .. code-block:: python >>> import tempfile @@ -89,8 +92,8 @@ def post_process(self, demand_cols=None): Apply the necessary post processing to the route choice assignment select link results. :Arguments: - **demand_cols** (:obj:Optional[list[str]]): If provided, only construct the sub-area matrix for these demand - matrices. + **demand_cols** (*Optional*: :obj:`[list[str]]`): If provided, only construct the sub-area matrix for these demand + matrices. :Returns: **sub_area_demand** (:obj:`pd.DataFrame`): A DataFrame representing the sub-area demand matrix. diff --git a/docs/source/api.rst b/docs/source/api.rst index dd5e7af1b..b81187a3c 100644 --- a/docs/source/api.rst +++ b/docs/source/api.rst @@ -114,6 +114,7 @@ Paths TransitAssignment HyperpathGenerating OptimalStrategies + RouteChoice SubAreaAnalysis Transit diff --git a/docs/source/examples/assignment_workflows/plot_route_choice.py b/docs/source/examples/assignment_workflows/plot_route_choice_basics.py similarity index 99% rename from docs/source/examples/assignment_workflows/plot_route_choice.py rename to docs/source/examples/assignment_workflows/plot_route_choice_basics.py index 9d5d970bd..7ee5b40ea 100644 --- a/docs/source/examples/assignment_workflows/plot_route_choice.py +++ b/docs/source/examples/assignment_workflows/plot_route_choice_basics.py @@ -6,11 +6,9 @@ In this example, we show how to perform route choice set generation using BFSLE and Link penalisation, for a city in La Serena Metropolitan Area in Chile. - """ # %% - # Imports from uuid import uuid4 from tempfile import gettempdir @@ -20,7 +18,6 @@ # sphinx_gallery_thumbnail_path = 'images/plot_route_choice_assignment.png' # %% - # We create the example project inside our temp folder fldr = join(gettempdir(), uuid4().hex) diff --git a/docs/source/examples/assignment_workflows/plot_subarea_analysis.py b/docs/source/examples/assignment_workflows/plot_subarea_analysis.py index a271b9e0b..136e0ba1d 100644 --- a/docs/source/examples/assignment_workflows/plot_subarea_analysis.py +++ b/docs/source/examples/assignment_workflows/plot_subarea_analysis.py @@ -1,5 +1,5 @@ """ -.. _example_usage_route_choice: +.. _example_usage_sub_area_analysis: Route Choice with sub-area analysis =================================== From c2a49c7fe1691d51619b6c4955a372d909e9969b Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Thu, 29 Aug 2024 14:20:24 -0300 Subject: [PATCH 07/57] Update route_choice.py --- aequilibrae/paths/route_choice.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/aequilibrae/paths/route_choice.py b/aequilibrae/paths/route_choice.py index 9f1b202dc..bc286d4f0 100644 --- a/aequilibrae/paths/route_choice.py +++ b/aequilibrae/paths/route_choice.py @@ -168,8 +168,10 @@ def set_save_routes(self, where: Optional[str] = None) -> None: """ Set save path for route choice results. Provide ``None`` to disable. - **warning** enabling route saving will disable in memory results. Viewing the results will read the results - from disk first. + .. warning: + + Enabling route saving will disable in memory results. Viewing the results will read the results + from disk first. :Arguments: **save_it** (:obj:`bool`): Boolean to indicate whether routes should be saved From 72a93b00b1fd8e1dc41597b0d322e87994bcac78 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Thu, 29 Aug 2024 17:30:11 -0300 Subject: [PATCH 08/57] docs --- .../images/attributes_documentation.png | Bin 0 -> 67058 bytes .../project_database/parameters_metadata.rst | 16 +- .../static_traffic_assignment.rst | 2 +- .../assignment_mechanics.rst | 151 +++++++++--------- .../multi_class_equilibrium.rst | 45 +++--- .../transit_assignment/hyperpath_routing.rst | 42 +---- .../transit_assignment/transit_graph.rst | 70 ++++---- 7 files changed, 153 insertions(+), 173 deletions(-) create mode 100644 docs/source/images/attributes_documentation.png diff --git a/docs/source/images/attributes_documentation.png b/docs/source/images/attributes_documentation.png new file mode 100644 index 0000000000000000000000000000000000000000..872948ee4530de8d20d3c6c14119c0c530f42fd5 GIT binary patch literal 67058 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. Traffic Assignment Class ^^^^^^^^^^^^^^^^^^^^^^^^ -Traffic assignment is organized within a object introduces on version 0.6.1 of the +Traffic assignment is organized within a object introduced on version 0.6.1 of AequilibraE, and includes a small list of member variables which should be populated by the user, providing a complete specification of the assignment procedure: @@ -184,42 +190,9 @@ To begin building the assignment it is easy: assig = TrafficAssignment() -Volume Delay Function -^^^^^^^^^^^^^^^^^^^^^ - -For now, the only VDF functions available in AequilibraE are the - -* BPR [3]_ - -.. math:: CongestedTime_{i} = FreeFlowTime_{i} * (1 + \alpha * (\frac{Volume_{i}}{Capacity_{i}})^\beta) - -* Spiess' conical [2]_ - -.. math:: CongestedTime_{i} = FreeFlowTime_{i} * (2 + \sqrt[2][\alpha^2*(1- \frac{Volume_{i}}{Capacity_{i}})^2 + \beta^2] - \alpha *(1-\frac{Volume_{i}}{Capacity_{i}})-\beta) - -* and French INRETS (alpha < 1) +.. seealso:: -Before capacity - -.. math:: CongestedTime_{i} = FreeFlowTime_{i} * \frac{1.1- (\alpha *\frac{Volume_{i}}{Capacity_{i}})}{1.1-\frac{Volume_{i}}{Capacity_{i}}} - -and after capacity - -.. math:: CongestedTime_{i} = FreeFlowTime_{i} * \frac{1.1- \alpha}{0.1} * (\frac{Volume_{i}}{Capacity_{i}})^2 - -More functions will be added as needed/requested/possible. - -Setting the volume delay function is one of the first things you should do after -instantiating an assignment problem in AequilibraE, and it is as simple as: - -.. code-block:: python - - assig.set_vdf('BPR') - -The implementation of the VDF functions in AequilibraE is written in Cython and -fully multi-threaded, and therefore descent methods that may evaluate such -function multiple times per iteration should not become unecessarily slow, -especially in modern multi-core systems. + :func:`aequilibrae.paths.TrafficAssignment` .. _assignment_class_object: @@ -240,16 +213,8 @@ required in the instantiation of this class: but which can have an arbitrary number of user-classes, setup as different layers of the matrix object -Example: - -.. code-block:: python - - tc = TrafficClass("car", graph_car, matrix_car) - - tc2 = TrafficClass("truck", graph_truck, matrix_truck) - * **pce** - The passenger-car equivalent is the standard way of modeling - multi-class traffic assignment equilibrium in a consistent manner (see [1]_ for + multi-class traffic assignment equilibrium in a consistent manner (see [3]_ for the technical detail), and it is set to 1 by default. If the **pce** for a certain class should be different than one, one can make a quick method call. @@ -258,28 +223,68 @@ Example: that contains that network. * **vot** - Value-of-Time (VoT) is the mechanism to bring time and monetary - costs into a consistent basis within a generalized cost function.in the event + costs into a consistent basis within a generalized cost function. In the event that fixed cost is measured in the same unit as free-flow travel time, then **vot** must be set to 1.0, and can be set to the appropriate value (1.0, value-of-timeIf the **vot** or whatever conversion factor is appropriate) with a method call. - .. code-block:: python - tc2.set_pce(2.5) - tc2.set_fixed_cost("truck_toll") - tc2.set_vot(0.35) + from aequilibrae.paths import TrafficClass -To add traffic classes to the assignment instance it is just a matter of making -a method call: + tc_car = TrafficClass("car", graph_car, matrix_car) + tc_truck = TrafficClass("truck", graph_truck, matrix_truck) -.. code-block:: python + tc_truck.set_pce(2.5) + tc_truck.set_fixed_cost("truck_toll") + tc_truck.set_vot(0.35) + + # Add traffic classes to the assignment instance + assig.set_classes([tc_car, tc_truck]) - assig.set_classes([tc, tc2]) + # To add only one class to the assignment instance + # assig.add_class(tc_truck) + +.. seealso:: + + :func:`aequilibrae.paths.TrafficClass` + +Volume Delay Function +^^^^^^^^^^^^^^^^^^^^^ + +For now, the only VDF functions available in AequilibraE are + +* BPR [1]_ + +.. math:: CongestedTime_{i} = FreeFlowTime_{i} * (1 + \alpha * (\frac{Volume_{i}}{Capacity_{i}})^\beta) + +* Spiess' conical [2]_ + +.. math:: CongestedTime_{i} = FreeFlowTime_{i} * (2 + \sqrt[2][\alpha^2*(1- \frac{Volume_{i}}{Capacity_{i}})^2 + \beta^2] - \alpha *(1-\frac{Volume_{i}}{Capacity_{i}})-\beta) + +* and French INRETS (alpha < 1) + +Before capacity + +.. math:: CongestedTime_{i} = FreeFlowTime_{i} * \frac{1.1- (\alpha *\frac{Volume_{i}}{Capacity_{i}})}{1.1-\frac{Volume_{i}}{Capacity_{i}}} + +and after capacity + +.. math:: CongestedTime_{i} = FreeFlowTime_{i} * \frac{1.1- \alpha}{0.1} * (\frac{Volume_{i}}{Capacity_{i}})^2 + +More functions will be added as needed/requested/possible. + +Setting the volume delay function is one of the first things you should do after +instantiating an assignment problem in AequilibraE, and it is as simple as: + +The implementation of the VDF functions in AequilibraE is written in Cython and +fully multi-threaded, and therefore descent methods that may evaluate such +function multiple times per iteration should not become unecessarily slow, +especially in modern multi-core systems. Setting VDF Parameters -^^^^^^^^^^^^^^^^^^^^^^ +"""""""""""""""""""""" Parameters for VDF functions can be passed as a fixed value to use for all links, or as graph fields. As it is the case for the travel time and capacity @@ -293,20 +298,20 @@ values for each link **OR** a single value for the entire network. Setting the VDF parameters should be done **AFTER** setting the VDF function of choice and adding traffic classes to the assignment, or it will **fail**. -To choose a field that exists in the graph, we just pass the parameters as -follows: - .. code-block:: python - assig.set_vdf_parameters({"alpha": "alphas", "beta": "betas"}) + assig.set_vdf('BPR') + # To set the parameters using a field that exists in the graph, just pass + # them as parameters + assig.set_vdf_parameters({"alpha": "alphas", "beta": "betas"}) -To pass global values, it is simply a matter of doing the following: + # Or to pass global values, it is simply a matter of doing the following: + assig.set_vdf_parameters({"alpha": 0.15, "beta": 4}) -.. code-block:: python - - assig.set_vdf_parameters({"alpha": 0.15, "beta": 4}) +.. seealso:: + :func:`aequilibrae.paths.VDF` Setting final parameters ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -314,9 +319,7 @@ Setting final parameters There are still three parameters missing for the assignment. * Capacity field - * Travel time field - * Equilibrium algorithm to use .. code-block:: python @@ -325,7 +328,6 @@ There are still three parameters missing for the assignment. assig.set_time_field("free_flow_time") assig.set_algorithm(algorithm) - Setting Preloads ^^^^^^^^^^^^^^^^ @@ -351,7 +353,6 @@ Next, add the preload to the assignment. assig.add_preload(preload, 'PT_vehicles') - Executing an Assignment ^^^^^^^^^^^^^^^^^^^^^^^ @@ -365,12 +366,12 @@ Finally, one can execute assignment: References ~~~~~~~~~~ -.. [1] Zill, J., Camargo, P., Veitch, T., Daisy,N. (2019) "Toll Choice and Stochastic User Equilibrium: - Ticking All the Boxes", Transportation Research Record, 2673(4):930-940. - Available in: https://doi.org/10.1177%2F0361198119837496 +.. [1] Hampton Roads Transportation Planning Organization, Regional Travel Demand Model V2 (2020). + Available in: https://www.hrtpo.org/uploads/docs/2020_HamptonRoads_Modelv2_MethodologyReport.pdf .. [2] Spiess H. (1990) "Technical Note—Conical Volume-Delay Functions."Transportation Science, 24(2): 153-158. Available in: https://doi.org/10.1287/trsc.24.2.153 -.. [3] Hampton Roads Transportation Planning Organization, Regional Travel Demand Model V2 (2020). - Available in: https://www.hrtpo.org/uploads/docs/2020_HamptonRoads_Modelv2_MethodologyReport.pdf \ No newline at end of file +.. [3] Zill, J., Camargo, P., Veitch, T., Daisy,N. (2019) "Toll Choice and Stochastic User Equilibrium: + Ticking All the Boxes", Transportation Research Record, 2673(4):930-940. + Available in: https://doi.org/10.1177%2F0361198119837496 \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/static_traffic_assignment/multi_class_equilibrium.rst b/docs/source/modeling_with_aequilibrae/static_traffic_assignment/multi_class_equilibrium.rst index 64c336d69..fb65782b7 100644 --- a/docs/source/modeling_with_aequilibrae/static_traffic_assignment/multi_class_equilibrium.rst +++ b/docs/source/modeling_with_aequilibrae/static_traffic_assignment/multi_class_equilibrium.rst @@ -4,7 +4,7 @@ Multi-class Equilibrium assignment ================================== While single-class equilibrium traffic assignment [1]_ is mathematically simple, -multi-class traffic assignment [7]_, especially when including monetary costs +multi-class traffic assignment [2]_, especially when including monetary costs (e.g. tolls) and multiple classes with different Passenger Car Equivalent (PCE) factors, requires more sophisticated mathematics. @@ -19,7 +19,7 @@ Technical requirements This documentation is not intended to discuss in detail the mathematical requirements of multi-class traffic assignment, which can be found discussed in -detail on [4]_. +detail on [3]_. A few requirements, however, need to be made clear. @@ -48,7 +48,7 @@ Convergence criteria -------------------- Convergence in AequilibraE is measured solely in terms of relative gap, which is -a somewhat old recommendation [5]_, but it is still the most used measure in +a somewhat old recommendation [4]_, but it is still the most used measure in practice, and is detailed below. .. math:: RelGap = \frac{\sum_{a}V_{a}^{*}*C_{a} - \sum_{a}V_{a}^{AoN}*C_{a}}{\sum_{a}V_{a}^{*}*C_{a}} @@ -85,7 +85,7 @@ iteration of all-or-nothing assignment, as shown in the table below .. note:: Our implementations of the conjugate and Biconjugate-Frank-Wolfe methods - should be inherently proportional [6]_, but we have not yet carried the + should be inherently proportional [5]_, but we have not yet carried the appropriate testing that would be required for an empirical proof. Method of Successive Averages (MSA) @@ -102,13 +102,12 @@ Frank-Wolfe (FW) The implementation of Frank-Wolfe in AequilibraE is extremely simple from an implementation point of view, as we use a generic optimizer from SciPy as an engine for the line search, and it is a standard implementation of the algorithm -introduced by LeBlanc in 1975 [2]_. - +introduced by LeBlanc in 1975 [6]_. Conjugate Frank-Wolfe ~~~~~~~~~~~~~~~~~~~~~ -The conjugate direction algorithm was introduced in 2013 [3]_, which is quite +The conjugate direction algorithm was introduced in 2013 [7]_, which is quite recent if you consider that the Frank-Wolfe algorithm was first applied in the early 1970's, and it was introduced at the same as its Biconjugate evolution, so it was born outdated. @@ -155,7 +154,7 @@ Other opportunities for parallelization, such as the computation of costs and its derivatives (required during the line-search optimization step), as well as all linear combination operations for vectors and matrices have been achieved through the use of OpenMP in pure Cython code. These implementations can be -cound on a file called *parallel_numpy.pyx* if you are curious to look at. +cound on a file called ``parallel_numpy.pyx`` if you are curious to look at. Much of the gains of going back to Cython to parallelize these functions came from making in-place computation using previously existing arrays, as the @@ -192,30 +191,30 @@ References Proceedings of the Institution of Civil Engineers 1952, 1(3):325-362. Available in: https://www.icevirtuallibrary.com/doi/abs/10.1680/ipeds.1952.11259 -.. [2] LeBlanc L. J., Morlok E. K. and Pierskalla W. P. (1975) - "An efficient approach to solving the road network equilibrium traffic assignment problem". - Transportation Research, 9(5):309-318. - Available in: https://doi.org/10.1016/0041-1647(75)90030-1 - -.. [3] Mitradjieva, M. and Lindberg, P.O. (2013) - "The Stiff Is Moving—Conjugate Direction Frank-Wolfe Methods with Applications to Traffic Assignment". - Transportation Science, 47(2):280-293. - Available in: https://doi.org/10.1287/trsc.1120.0409 +.. [2] Marcotte, P., Patriksson, M. (2007) + "Chapter 10 Traffic Equilibrium - Handbooks in Operations Research and Management Science, Vol 14", + Elsevier. Editors Barnhart, C., Laporte, G. https://doi.org/10.1016/S0927-0507(06)14010-4 -.. [4] Zill, J., Camargo, P., Veitch, T., Daisy,N. (2019) +.. [3] Zill, J., Camargo, P., Veitch, T., Daisy,N. (2019) "Toll Choice and Stochastic User Equilibrium: Ticking All the Boxes", Transportation Research Record, 2673(4):930-940. Available in: https://doi.org/10.1177%2F0361198119837496 -.. [5] Rose, G., Daskin, M., Koppelman, F. (1988) +.. [4] Rose, G., Daskin, M., Koppelman, F. (1988) "An examination of convergence error in equilibrium traffic assignment models", Transportation Research Part B, 22(4):261-274. Available in: https://doi.org/10.1016/0191-2615(88)90003-3 -.. [6] Florian, M., Morosan, C.D. (2014) "On uniqueness and proportionality in multi-class equilibrium assignment", +.. [5] Florian, M., Morosan, C.D. (2014) "On uniqueness and proportionality in multi-class equilibrium assignment", Transportation Research Part B, 70:261-274. Available in: https://doi.org/10.1016/j.trb.2014.06.011 -.. [7] Marcotte, P., Patriksson, M. (2007) - "Chapter 10 Traffic Equilibrium - Handbooks in Operations Research and Management Science, Vol 14", - Elsevier. Editors Barnhart, C., Laporte, G. https://doi.org/10.1016/S0927-0507(06)14010-4 +.. [6] LeBlanc L. J., Morlok E. K. and Pierskalla W. P. (1975) + "An efficient approach to solving the road network equilibrium traffic assignment problem". + Transportation Research, 9(5):309-318. + Available in: https://doi.org/10.1016/0041-1647(75)90030-1 + +.. [7] Mitradjieva, M. and Lindberg, P.O. (2013) + "The Stiff Is Moving—Conjugate Direction Frank-Wolfe Methods with Applications to Traffic Assignment". + Transportation Science, 47(2):280-293. + Available in: https://doi.org/10.1287/trsc.1120.0409 diff --git a/docs/source/modeling_with_aequilibrae/transit_assignment/hyperpath_routing.rst b/docs/source/modeling_with_aequilibrae/transit_assignment/hyperpath_routing.rst index f75cfd43a..44dbf8be2 100644 --- a/docs/source/modeling_with_aequilibrae/transit_assignment/hyperpath_routing.rst +++ b/docs/source/modeling_with_aequilibrae/transit_assignment/hyperpath_routing.rst @@ -116,8 +116,6 @@ Vertices vertices = create_vertices(n) vertices.head(3) - - +------+----------+-------+ | | x | y | +======+==========+=======+ @@ -128,7 +126,6 @@ Vertices | 2 | 0.222222 | 0.0 | +------+----------+-------+ - .. code:: python @jit @@ -164,9 +161,6 @@ Vertices edges = create_edges(n, seed=RS) edges.head(3) - - - +------+----------+-------+------------+------------+ | | tail | head | trav_time | delay_base | +======+==========+=======+============+============+ @@ -177,13 +171,12 @@ Vertices | 2 | 10 | 11 | 0.969136 | 0.854512 | +------+----------+-------+------------+------------+ - Plot the network ~~~~~~~~~~~~~~~~ -We use the `NetworkX `__ package to plot the -network. The bottom left vertex is the origin (‘o’) and the top right -vertex is the destination (‘d’) for the hyperpath computation. +We use the `NetworkX `_ package to plot the +network. The bottom left vertex is the origin (*o*) and the top right +vertex is the destination (*d*) for the hyperpath computation. .. code:: python @@ -222,14 +215,11 @@ vertex is the destination (‘d’) for the hyperpath computation. ax = plt.gca() _ = ax.set_title(f"Bell's network with $n$={n}", color="k") - - .. image:: ../../images/transit/hyperpath_bell_s_network.png :width: 600 :align: center :alt: Bell's network - We can also visualize the edge travel time: .. code:: python @@ -261,15 +251,11 @@ We can also visualize the edge travel time: "Bell's network - edge travel time : $\\textit{trav_time}$", color="k" ) - - .. image:: ../../images/transit/hyperpath_bell_edge_travel_time.png :width: 600 :align: center :alt: Bell's network - edge travel time - - And the base delay: .. code:: python @@ -299,28 +285,21 @@ And the base delay: ax = plt.gca() _ = ax.set_title("Bell's network - edge base delay : $\\textit{delay_base}$", color="k") - - .. image:: ../../images/transit/hyperpath_bell_edge_base_delay.png :width: 600 :align: center :alt: Bell's network - edge base delay - Hyperpath computation --------------------- We now introduce a function ``plot_shortest_hyperpath`` that: - creates the network, - - computes the edge frequency given an input value for :math:`\alpha`, - - compute the shortest hyperpath, - - plot the network and hyperpath with NetworkX. - .. code:: python def plot_shortest_hyperpath(n=10, alpha=10.0, figsize=FS, seed=RS): @@ -389,20 +368,15 @@ all the network. plot_shortest_hyperpath(n=10, alpha=0.0) - - .. image:: ../../images/transit/hyperpath_bell_n_10_alpha_0d0.png :width: 600 :align: center :alt: Shortest hyperpath - Bell's network alpha=0.0 - - The hyperpath that we obtain is the same as the shortest path that Dijkstra’s algorithm would have computed. We call NetworkX’s ``dijkstra_path`` method in order to compute the shortest path: - .. code:: python G = nx.from_pandas_edgelist( @@ -446,44 +420,37 @@ Dijkstra’s algorithm would have computed. We call NetworkX’s f"Shortest path - Bell's network", color="k" ) - .. image:: ../../images/transit/hyperpath_bell_n_10_shartest_path.png :width: 600 :align: center :alt: Shortest path - Bell's network - Let’s introduce some delay by increasing the value of :math:`\alpha`: .. code:: python plot_shortest_hyperpath(n=10, alpha=0.5) - .. image:: ../../images/transit/hyperpath_bell_n_10_alpha_0d5.png :width: 600 :align: center :alt: Shortest hyperpath - Bell's network alpha=0.5 - The shortest path is no longer unique and multiple routes are suggested. The link usage probability is reflected by the line width. The majority of the flow still follows the shortest path, but some of it is distributed among different alternative paths. This becomes more apparent as we further increase :math:`\alpha`: - .. code:: python plot_shortest_hyperpath(n=10, alpha=1.0) - .. image:: ../../images/transit/hyperpath_bell_n_10_alpha_1d0.png :width: 600 :align: center :alt: Shortest hyperpath - Bell's network alpha=1.0 - .. code:: python plot_shortest_hyperpath(n=10, alpha=100.0) @@ -494,6 +461,9 @@ apparent as we further increase :math:`\alpha`: :align: center :alt: Shortest hyperpath - Bell's network alpha=100.0 +.. seealso:: + + :func:`aequilibrae.paths.HyperpathGenerating` References ---------- diff --git a/docs/source/modeling_with_aequilibrae/transit_assignment/transit_graph.rst b/docs/source/modeling_with_aequilibrae/transit_assignment/transit_graph.rst index d26bedd5d..4cc6be37a 100644 --- a/docs/source/modeling_with_aequilibrae/transit_assignment/transit_graph.rst +++ b/docs/source/modeling_with_aequilibrae/transit_assignment/transit_graph.rst @@ -85,9 +85,7 @@ CBA. Various configurations are possible, such as: - a partial route at a given moment of the day: AB, - - a route with an additional stop : ABDC - - a route that does not stop at a given stop: AC .. image:: ../../images/transit/transit_graph_routes_2.png @@ -230,7 +228,6 @@ Connectors that connect *od* to *stop* nodes allow passengers to access the netw Link attributes ~~~~~~~~~~~~~~~ - The table below summarizes link characteristics and attributes based on link types: @@ -370,33 +367,30 @@ Transit graph specificities in AequilibraE The graph creation process in AequilibraE incorporates several edge types to capture the nuances of transit networks. Notable distinctions include: -**Connectors :** +**Connectors:** - *access connectors* directed from od nodes to the network - - *egress connectors* directed from the network to the od nodes -**Transfer edges :** +**Transfer edges:** - *inner transfer*: Connect lines within the same stop - - *outer transfer*: Connect lines between distinct stops within the same station -**Origin and Destination Nodes :** +**Origin and Destination Nodes:** - *origin* nodes: represent the starting point of passenger trips - - *destination* nodes: represent the end point of passenger trips Users can customize these features using boolean parameters: -- `with_walking_edges`: create walking edges between the stops of a station +- ``with_walking_edges``: create walking edges between the stops of a station -- `with_inner_stop_transfers`: create transfer edges between lines of a stop +- ``with_inner_stop_transfers``: create transfer edges between lines of a stop -- `with_outer_stop_transfers`: create transfer edges between lines of different stops of a station +- ``with_outer_stop_transfers``: create transfer edges between lines of different stops of a station -- `blocking_centroid_flow`: duplicate OD nodes into unconnected origin and destination nodes in order to block centroid flows. Flows starts from an origin node and ends at a destination node. It is not possible to use an egress connector followed by an access connector in the middle of a trip. +- ``blocking_centroid_flow``: duplicate OD nodes into unconnected origin and destination nodes in order to block centroid flows. Flows starts from an origin node and ends at a destination node. It is not possible to use an egress connector followed by an access connector in the middle of a trip. Note that during the assignment, if passengers have the choice between a transfer edge or a walking edge for a line change, they will always be assigned to the transfer edge. @@ -443,36 +437,42 @@ Here is a simple example of a station with two stops, with two lines each: :align: center :alt: both inner and outer transfer edges - As an illustrative example, if we build the graph for the city of Lyon France (GTFS files from 2022) on a given day, we get 20196 vertices and 91107 edges, with ``with_walking_edges=True``, ``with_inner_stop_transfers=True``, ``with_outer_stop_transfers=True`` and ``blocking_centroid_flow=False``. + Here is the distribution of edge types: -================ ===== -Edge type Count -================ ===== -outer_transfer 27287 -inner_transfer 10721 -walking 9140 -on-board 7590 -boarding 7590 -alighting 7590 -dwell 7231 -access_connector 6979 -egress_connector 6979 -================ ===== +.. table:: + :widths: 30 15 + + ================ ====== + Edge type Count + ================ ====== + outer_transfer 27,287 + inner_transfer 10,721 + walking 9,140 + on-board 7,590 + boarding 7,590 + alighting 7,590 + dwell 7,231 + access_connector 6,979 + egress_connector 6,979 + ================ ====== and vertex types: -=========== ===== -Vertex type Count -=========== ===== -alighting 7590 -boarding 7590 -stop 4499 -od 517 -=========== ===== +.. table:: + :widths: 30 15 + + =========== ===== + Vertex type Count + =========== ===== + alighting 7,590 + boarding 7,590 + stop 4,499 + od 517 + =========== ===== References ---------- From 2821f7994ba7d243ee1c060ea776dfa2738a33ea Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Thu, 29 Aug 2024 17:54:22 -0300 Subject: [PATCH 09/57] . --- .../route_choice/route_choice_model.rst | 2 +- .../transit_assignment/hyperpath_routing.rst | 61 +++-- .../transit_assignment/transit_graph.rst | 252 +++++++++--------- 3 files changed, 163 insertions(+), 152 deletions(-) diff --git a/docs/source/modeling_with_aequilibrae/route_choice/route_choice_model.rst b/docs/source/modeling_with_aequilibrae/route_choice/route_choice_model.rst index 194507452..2cdfe594c 100644 --- a/docs/source/modeling_with_aequilibrae/route_choice/route_choice_model.rst +++ b/docs/source/modeling_with_aequilibrae/route_choice/route_choice_model.rst @@ -12,7 +12,7 @@ to be implemented in AequilibraE Path-Size Logit (PSL) ~~~~~~~~~~~~~~~~~~~~~ - The PSL model’s utility function is defined by +The PSL model’s utility function is defined by .. math:: U_{i} = V_{i} + \beta_{PSL} \times \log{\gamma_i} + \varepsilon_{i} diff --git a/docs/source/modeling_with_aequilibrae/transit_assignment/hyperpath_routing.rst b/docs/source/modeling_with_aequilibrae/transit_assignment/hyperpath_routing.rst index 44dbf8be2..61a7262e1 100644 --- a/docs/source/modeling_with_aequilibrae/transit_assignment/hyperpath_routing.rst +++ b/docs/source/modeling_with_aequilibrae/transit_assignment/hyperpath_routing.rst @@ -116,15 +116,19 @@ Vertices vertices = create_vertices(n) vertices.head(3) -+------+----------+-------+ -| | x | y | -+======+==========+=======+ -| 0 | 0.000000 | 0.0 | -+------+----------+-------+ -| 1 | 0.111111 | 0.0 | -+------+----------+-------+ -| 2 | 0.222222 | 0.0 | -+------+----------+-------+ +.. table:: + :align: left + :width: 25% + + +------+----------+-------+ + | | x | y | + +======+==========+=======+ + | 0 | 0.000000 | 0.0 | + +------+----------+-------+ + | 1 | 0.111111 | 0.0 | + +------+----------+-------+ + | 2 | 0.222222 | 0.0 | + +------+----------+-------+ .. code:: python @@ -144,7 +148,6 @@ Vertices k += 1 return tail, head - def create_edges(n, seed=124): tail, head = create_edges_numba(n) edges = pd.DataFrame() @@ -161,15 +164,19 @@ Vertices edges = create_edges(n, seed=RS) edges.head(3) -+------+----------+-------+------------+------------+ -| | tail | head | trav_time | delay_base | -+======+==========+=======+============+============+ -| 0 | 0 | 1 | 0.785253 | 0.287917 | -+------+----------+-------+------------+------------+ -| 1 | 0 | 10 | 0.785859 | 0.970429 | -+------+----------+-------+------------+------------+ -| 2 | 10 | 11 | 0.969136 | 0.854512 | -+------+----------+-------+------------+------------+ +.. table:: + :align: left + :width: 25% + + +------+----------+-------+------------+------------+ + | | tail | head | trav_time | delay_base | + +======+==========+=======+============+============+ + | 0 | 0 | 1 | 0.785253 | 0.287917 | + +------+----------+-------+------------+------------+ + | 1 | 0 | 10 | 0.785859 | 0.970429 | + +------+----------+-------+------------+------------+ + | 2 | 10 | 11 | 0.969136 | 0.854512 | + +------+----------+-------+------------+------------+ Plot the network ~~~~~~~~~~~~~~~~ @@ -216,7 +223,7 @@ vertex is the destination (*d*) for the hyperpath computation. _ = ax.set_title(f"Bell's network with $n$={n}", color="k") .. image:: ../../images/transit/hyperpath_bell_s_network.png - :width: 600 + :scale: 80% :align: center :alt: Bell's network @@ -252,7 +259,7 @@ We can also visualize the edge travel time: ) .. image:: ../../images/transit/hyperpath_bell_edge_travel_time.png - :width: 600 + :scale: 80% :align: center :alt: Bell's network - edge travel time @@ -286,7 +293,7 @@ And the base delay: _ = ax.set_title("Bell's network - edge base delay : $\\textit{delay_base}$", color="k") .. image:: ../../images/transit/hyperpath_bell_edge_base_delay.png - :width: 600 + :scale: 80% :align: center :alt: Bell's network - edge base delay @@ -369,7 +376,7 @@ all the network. plot_shortest_hyperpath(n=10, alpha=0.0) .. image:: ../../images/transit/hyperpath_bell_n_10_alpha_0d0.png - :width: 600 + :scale: 80% :align: center :alt: Shortest hyperpath - Bell's network alpha=0.0 @@ -421,7 +428,7 @@ Dijkstra’s algorithm would have computed. We call NetworkX’s ) .. image:: ../../images/transit/hyperpath_bell_n_10_shartest_path.png - :width: 600 + :scale: 80% :align: center :alt: Shortest path - Bell's network @@ -432,7 +439,7 @@ Let’s introduce some delay by increasing the value of :math:`\alpha`: plot_shortest_hyperpath(n=10, alpha=0.5) .. image:: ../../images/transit/hyperpath_bell_n_10_alpha_0d5.png - :width: 600 + :scale: 80% :align: center :alt: Shortest hyperpath - Bell's network alpha=0.5 @@ -447,7 +454,7 @@ apparent as we further increase :math:`\alpha`: plot_shortest_hyperpath(n=10, alpha=1.0) .. image:: ../../images/transit/hyperpath_bell_n_10_alpha_1d0.png - :width: 600 + :scale: 80% :align: center :alt: Shortest hyperpath - Bell's network alpha=1.0 @@ -457,7 +464,7 @@ apparent as we further increase :math:`\alpha`: .. image:: ../../images/transit/hyperpath_bell_n_10_alpha_100d0.png - :width: 600 + :scale: 80% :align: center :alt: Shortest hyperpath - Bell's network alpha=100.0 diff --git a/docs/source/modeling_with_aequilibrae/transit_assignment/transit_graph.rst b/docs/source/modeling_with_aequilibrae/transit_assignment/transit_graph.rst index 4cc6be37a..46845516c 100644 --- a/docs/source/modeling_with_aequilibrae/transit_assignment/transit_graph.rst +++ b/docs/source/modeling_with_aequilibrae/transit_assignment/transit_graph.rst @@ -9,7 +9,7 @@ focus is the classic algorithm “Optimal strategies” by Spiess & Florian Let’s start by giving a few definitions: - *transit* definition from - `Wikipedia `__: + `Wikipedia `_: .. @@ -97,15 +97,19 @@ Lines can be decomposed into multiple sub-lines, each representing distinct routes. For the given example, we may have several sub-lines under the same commercial line (L1): -======= =============== ============= =========== -line id commercial name stop sequence headway (s) -======= =============== ============= =========== -L1_a1 L1 ABC 600 -L1_a2 L1 ABDC 3600 -L1_a3 L1 AB 3600 -L1_a4 L1 AC 3600 -L1_b1 L1 CBA 600 -======= =============== ============= =========== +.. table:: + :align: center + :width: 40% + + ======= =============== ============= =========== + line id commercial name stop sequence headway (s) + ======= =============== ============= =========== + L1_a1 L1 ABC 600 + L1_a2 L1 ABDC 3600 + L1_a3 L1 AB 3600 + L1_a4 L1 AC 3600 + L1_b1 L1 CBA 600 + ======= =============== ============= =========== Headway, associated with each sub-line, corresponds to the mean time range between consecutive vehicles—the inverse of the line frequency @@ -118,14 +122,18 @@ A line segment represents a portion of a transit line between two consecutive stops. Using the example line ``L1_a1``, we derive two distinct line segments: -+----------+---------+-------------+-------------------+--------------+ -| line id | segment | origin stop | destination stop | travel_time | -| | index | | | (s) | -+==========+=========+=============+===================+==============+ -| L1_a1 | 1 | A | B | 300 | -+----------+---------+-------------+-------------------+--------------+ -| L1_a1 | 2 | B | C | 600 | -+----------+---------+-------------+-------------------+--------------+ +.. table:: + :align: center + :width: 40% + + +----------+---------+-------------+-------------------+--------------+ + | line id | segment | origin stop | destination stop | travel_time | + | | index | | | (s) | + +==========+=========+=============+===================+==============+ + | L1_a1 | 1 | A | B | 300 | + +----------+---------+-------------+-------------------+--------------+ + | L1_a1 | 2 | B | C | 600 | + +----------+---------+-------------+-------------------+--------------+ Note that a travel time is included for each line segment, serving as another link attribute used by the assignment algorithm. @@ -170,29 +178,19 @@ categories play crucial roles in this representation. | **Link types:** - *on-board* - - *boarding* - - *alighting* - - *dwell* - - *transfer* - - *connector* - - *walking* | **Nodes types:** - *stop* - - *boarding* - - *alighting* - - *od* - - *walking* To illustrate, consider the anatomy of a simple stop: @@ -231,25 +229,29 @@ Link attributes The table below summarizes link characteristics and attributes based on link types: -+-------------+------------------+----------------+------------+------------------+ -| link type | from node type | to node type | cost | frequency | -+=============+==================+================+============+==================+ -| *on-board* | *boarding* | *alighting* | trav. time | :math:`\infty` | -+-------------+------------------+----------------+------------+------------------+ -| *boarding* | *stop* | *boarding* | const. | line freq. | -+-------------+------------------+----------------+------------+------------------+ -| *alighting* | *alighting* | *stop* | const. | :math:`\infty` | -+-------------+------------------+----------------+------------+------------------+ -| *dwell* | *alighting* | *boarding* | const. | :math:`\infty` | -+-------------+------------------+----------------+------------+------------------+ -| *transfer* | *alighting* | *boarding* | const. + | dest. line freq. | -| | | | trav. time | | -+-------------+------------------+----------------+------------+------------------+ -| *connector* | *od* or *stop* | *od* or *stop* | trav. time | :math:`\infty` | -+-------------+------------------+----------------+------------+------------------+ -| *walking* | *stop* or | *stop* or | trav. time | :math:`\infty` | -| | *walking* | *walking* | | | -+-------------+------------------+----------------+------------+------------------+ +.. table:: + :align: center + :width: 60% + + +-------------+------------------+----------------+------------+------------------+ + | link type | from node type | to node type | cost | frequency | + +=============+==================+================+============+==================+ + | *on-board* | *boarding* | *alighting* | trav. time | :math:`\infty` | + +-------------+------------------+----------------+------------+------------------+ + | *boarding* | *stop* | *boarding* | const. | line freq. | + +-------------+------------------+----------------+------------+------------------+ + | *alighting* | *alighting* | *stop* | const. | :math:`\infty` | + +-------------+------------------+----------------+------------+------------------+ + | *dwell* | *alighting* | *boarding* | const. | :math:`\infty` | + +-------------+------------------+----------------+------------+------------------+ + | *transfer* | *alighting* | *boarding* | const. + | dest. line freq. | + | | | | trav. time | | + +-------------+------------------+----------------+------------+------------------+ + | *connector* | *od* or *stop* | *od* or *stop* | trav. time | :math:`\infty` | + +-------------+------------------+----------------+------------+------------------+ + | *walking* | *stop* or | *stop* or | trav. time | :math:`\infty` | + | | *walking* | *walking* | | | + +-------------+------------------+----------------+------------+------------------+ The travel time is specific to each line segment or walking time. For example, there can be 10 minutes connection between stops in a large @@ -285,17 +287,21 @@ This illustrative example is taken from *Spiess and Florian* [1]_: Travel time are indicated on the figure. We have the following four distinct line characteristics: -+-------+------+-------------+-----------------+ -|line id| route|headway (min)| frequency (1/s) | -+=======+======+=============+=================+ -| L1 | AB | 12 | 0.001388889 | -+-------+------+-------------+-----------------+ -| L2 | AXY | 12 | 0.001388889 | -+-------+------+-------------+-----------------+ -| L3 | XYB | 30 | 0.000555556 | -+-------+------+-------------+-----------------+ -| L4 | YB | 6 | 0.002777778 | -+-------+------+-------------+-----------------+ +.. table:: + :align: center + :width: 40% + + +-------+------+-------------+-----------------+ + |line id| route|headway (min)| frequency (1/s) | + +=======+======+=============+=================+ + | L1 | AB | 12 | 0.001388889 | + +-------+------+-------------+-----------------+ + | L2 | AXY | 12 | 0.001388889 | + +-------+------+-------------+-----------------+ + | L3 | XYB | 30 | 0.000555556 | + +-------+------+-------------+-----------------+ + | L4 | YB | 6 | 0.002777778 | + +-------+------+-------------+-----------------+ Passengers aim to travel from A to B, prompting the division of the network area into two distinct zones: TAZ 1 and TAZ 2. The assignment graph associated with this network encompasses 26 links: @@ -306,61 +312,65 @@ Passengers aim to travel from A to B, prompting the division of the network area Here is a table listing all links : -+---------+-----------+---------+------+--------------+ -| link id | link type | line id | cost | frequency | -+=========+===========+=========+======+==============+ -| 1 |*connector*| | 0 |:math:`\infty`| -+---------+-----------+---------+------+--------------+ -| 2 | *boarding*| L1 | 0 | 0.001388889 | -+---------+-----------+---------+------+--------------+ -| 3 | *boarding*| L2 | 0 | 0.001388889 | -+---------+-----------+---------+------+--------------+ -| 4 | *on-board*| L1 | 1500 |:math:`\infty`| -+---------+-----------+---------+------+--------------+ -| 5 | *on-board*| L2 | 420 |:math:`\infty`| -+---------+-----------+---------+------+--------------+ -| 6 |*alighting*| L2 | 0 |:math:`\infty`| -+---------+-----------+---------+------+--------------+ -| 7 | *dwell*| L2 | 0 |:math:`\infty`| -+---------+-----------+---------+------+--------------+ -| 8 | *transfer*| | 0 | 0.000555556 | -+---------+-----------+---------+------+--------------+ -| 9 | *boarding*| L2 | 0 | 0.001388889 | -+---------+-----------+---------+------+--------------+ -| 10 | *boarding*| L3 | 0 | 0.000555556 | -+---------+-----------+---------+------+--------------+ -| 11 | *on-board*| L2 | 360 |:math:`\infty`| -+---------+-----------+---------+------+--------------+ -| 12 | *on-board*| L3 | 240 |:math:`\infty`| -+---------+-----------+---------+------+--------------+ -| 13 |*alighting*| L3 | 0 |:math:`\infty`| -+---------+-----------+---------+------+--------------+ -| 14 |*alighting*| L2 | 0 |:math:`\infty`| -+---------+-----------+---------+------+--------------+ -| 15 | *transfer*| L3 | 0 | 0.000555556 | -+---------+-----------+---------+------+--------------+ -| 16 | *transfer*| | 0 | 0.002777778 | -+---------+-----------+---------+------+--------------+ -| 17 | *dwell*| L3 | 0 |:math:`\infty`| -+---------+-----------+---------+------+--------------+ -| 18 | *transfer*| | 0 | 0.002777778 | -+---------+-----------+---------+------+--------------+ -| 19 | *boarding*| L3 | 0 | 0.000555556 | -+---------+-----------+---------+------+--------------+ -| 20 | *boarding*| L4 | 0 | 0.002777778 | -+---------+-----------+---------+------+--------------+ -| 21 | *on-board*| L3 | 240 |:math:`\infty`| -+---------+-----------+---------+------+--------------+ -| 22 | *on-board*| L4 | 600 |:math:`\infty`| -+---------+-----------+---------+------+--------------+ -| 23 |*alighting*| L4 | 0 |:math:`\infty`| -+---------+-----------+---------+------+--------------+ -| 24 |*alighting*| L3 | 0 |:math:`\infty`| -+---------+-----------+---------+------+--------------+ -| 25 |*alighting*| L1 | 0 |:math:`\infty`| -+---------+-----------+---------+------+--------------+ -| 26 |*connector*| | 0 |:math:`\infty`| -+---------+-----------+---------+------+--------------+ +.. table:: + :align: center + :width: 50% + + +---------+-----------+---------+------+--------------+ + | link id | link type | line id | cost | frequency | + +=========+===========+=========+======+==============+ + | 1 |*connector*| | 0 |:math:`\infty`| + +---------+-----------+---------+------+--------------+ + | 2 | *boarding*| L1 | 0 | 0.001388889 | + +---------+-----------+---------+------+--------------+ + | 3 | *boarding*| L2 | 0 | 0.001388889 | + +---------+-----------+---------+------+--------------+ + | 4 | *on-board*| L1 | 1500 |:math:`\infty`| + +---------+-----------+---------+------+--------------+ + | 5 | *on-board*| L2 | 420 |:math:`\infty`| + +---------+-----------+---------+------+--------------+ + | 6 |*alighting*| L2 | 0 |:math:`\infty`| + +---------+-----------+---------+------+--------------+ + | 7 | *dwell*| L2 | 0 |:math:`\infty`| + +---------+-----------+---------+------+--------------+ + | 8 | *transfer*| | 0 | 0.000555556 | + +---------+-----------+---------+------+--------------+ + | 9 | *boarding*| L2 | 0 | 0.001388889 | + +---------+-----------+---------+------+--------------+ + | 10 | *boarding*| L3 | 0 | 0.000555556 | + +---------+-----------+---------+------+--------------+ + | 11 | *on-board*| L2 | 360 |:math:`\infty`| + +---------+-----------+---------+------+--------------+ + | 12 | *on-board*| L3 | 240 |:math:`\infty`| + +---------+-----------+---------+------+--------------+ + | 13 |*alighting*| L3 | 0 |:math:`\infty`| + +---------+-----------+---------+------+--------------+ + | 14 |*alighting*| L2 | 0 |:math:`\infty`| + +---------+-----------+---------+------+--------------+ + | 15 | *transfer*| L3 | 0 | 0.000555556 | + +---------+-----------+---------+------+--------------+ + | 16 | *transfer*| | 0 | 0.002777778 | + +---------+-----------+---------+------+--------------+ + | 17 | *dwell*| L3 | 0 |:math:`\infty`| + +---------+-----------+---------+------+--------------+ + | 18 | *transfer*| | 0 | 0.002777778 | + +---------+-----------+---------+------+--------------+ + | 19 | *boarding*| L3 | 0 | 0.000555556 | + +---------+-----------+---------+------+--------------+ + | 20 | *boarding*| L4 | 0 | 0.002777778 | + +---------+-----------+---------+------+--------------+ + | 21 | *on-board*| L3 | 240 |:math:`\infty`| + +---------+-----------+---------+------+--------------+ + | 22 | *on-board*| L4 | 600 |:math:`\infty`| + +---------+-----------+---------+------+--------------+ + | 23 |*alighting*| L4 | 0 |:math:`\infty`| + +---------+-----------+---------+------+--------------+ + | 24 |*alighting*| L3 | 0 |:math:`\infty`| + +---------+-----------+---------+------+--------------+ + | 25 |*alighting*| L1 | 0 |:math:`\infty`| + +---------+-----------+---------+------+--------------+ + | 26 |*connector*| | 0 |:math:`\infty`| + +---------+-----------+---------+------+--------------+ Transit graph specificities in AequilibraE ------------------------------------------ @@ -397,21 +407,13 @@ Note that during the assignment, if passengers have the choice between a transfe This leads to these possible edge types: - on-board - - boarding - - alighting - - dwell - - access_connector - - egress_connector - - inner_transfer - - outer_transfer - - walking Here is a simple example of a station with two stops, with two lines each: @@ -443,8 +445,9 @@ As an illustrative example, if we build the graph for the city of Lyon France (G Here is the distribution of edge types: -.. table:: - :widths: 30 15 +.. table:: + :align: center + :width: 30% ================ ====== Edge type Count @@ -463,7 +466,8 @@ Here is the distribution of edge types: and vertex types: .. table:: - :widths: 30 15 + :align: center + :width: 30% =========== ===== Vertex type Count From ae6f1050b7a9c5019d37e90cc35b05c26ad02bf9 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Fri, 30 Aug 2024 16:11:47 -0300 Subject: [PATCH 10/57] . --- aequilibrae/paths/linear_approximation.py | 2 +- docs/source/development/roadmap.rst | 1 - .../development/softwaredevelopment.rst | 24 +++++++------- docs/source/history.rst | 7 ++-- docs/source/installation.rst | 32 +++++++----------- .../route_choice.rst | 33 ++++++++++--------- .../route_choice/choice_set_generation.rst | 2 -- .../static_traffic_assignment.rst | 1 - docs/source/support.rst | 10 +++--- .../ipf_performance.rst | 14 ++++---- .../traffic_assignment.rst | 32 +++++++++--------- docs/source/version_history.rst | 5 ++- 12 files changed, 76 insertions(+), 87 deletions(-) diff --git a/aequilibrae/paths/linear_approximation.py b/aequilibrae/paths/linear_approximation.py index 3c7f56f39..8611922c3 100644 --- a/aequilibrae/paths/linear_approximation.py +++ b/aequilibrae/paths/linear_approximation.py @@ -693,7 +693,7 @@ def calculate_stepsize(self): self.stepsize = tiny_step else: self.stepsize = 0.0 - # need to reset conjugate / bi-conjugate direction search + # need to reset conjugate / biconjugate direction search self.do_fw_step = True self.conjugate_failed = True self.iteration_issue.append("Found bad conjugate direction step. Performing FW search. {e.args}") diff --git a/docs/source/development/roadmap.rst b/docs/source/development/roadmap.rst index 7b660dcc6..f1c9e6155 100644 --- a/docs/source/development/roadmap.rst +++ b/docs/source/development/roadmap.rst @@ -24,7 +24,6 @@ identified and that we intend to dedicate some time to in the future. * Public Transport * Export of GTFS (enables editing of GTFS in QGIS) - * Public transit assignment (Scheduled for 3rd Quarter 2023) * Project diff --git a/docs/source/development/softwaredevelopment.rst b/docs/source/development/softwaredevelopment.rst index 7c1988a96..0b78b7675 100644 --- a/docs/source/development/softwaredevelopment.rst +++ b/docs/source/development/softwaredevelopment.rst @@ -23,10 +23,11 @@ Development Install As it goes with most Python packages, we recommend using a dedicated virtual environment to develop AequilibraE. -AequilibraE is currently tested for Python 3.7, 3.8, 3.9 & 3.11, but we recommend using Python 3.9 or 2.10 for development. +AequilibraE is currently tested for Python 3.9, 3.10, 3.11 & 3.12, but we recommend using Python 3.9 or 2.10 for +development. -We also assume you are using `PyCharm `_ or `VSCode `_ -which are awesome IDEs for Python. +We also assume you are using `PyCharm `_ or +`VSCode `_ which are awesome IDEs for Python. If you are using a different IDE, we would welcome if you could contribute with instructions to set that up. @@ -39,8 +40,7 @@ Non-Windows Windows ~~~~~~~ -Make sure to clone the AequilibraE repository and run the following from within -that cloned repo using an elevated command prompt. +Make sure to clone the AequilibraE repository and run the following from withinthat cloned repo using an elevated command prompt. Python 3.9 (or whatever version you chose) needs to be installed, and the following instructions assume you are using `Chocolatey @@ -74,15 +74,16 @@ Style * Python code should follow (mostly) the `pycodestyle style guide `_ * Python docstrings should follow the `reStructuredText Docstring Format `_ -* We are big fans of auto-code formatting. For that, we use `Black `_ +* We are big fans of auto-code formatting. + For that, we use `Black `_ * Negating some of what we have said so far, we use maximum line length of 120 characters Imports ~~~~~~~ * Imports should be one per line. -* Imports should be grouped into standard library, third-party, and intra-library imports (`ctrl+shit+o` - does it automatically on PyCharm). +* Imports should be grouped into standard library, third-party, and intra-library imports + (``ctrl+shit+o`` does it automatically on PyCharm). * Imports of NumPy and Pandas should follow the following convention: .. code-block:: python @@ -161,8 +162,8 @@ manually review the code before merging it into master (or returning for correct In some cases, test targets need to be updated to match the new results produced by the code since these are now the correct results. In order to update the test targets, first determine which tests are failing and then review the failing lines in the source files. These are easy to identify since each -test ultimately comes down to one of Python's various types of `assert` statements. Once you identify -which `assert` is failing, you can work your way back through the code that creates the test targets in +test ultimately comes down to one of Python's various types of ``assert`` statements. Once you identify +which ``assert`` is failing, you can work your way back through the code that creates the test targets in order to update it. After updating the test targets, re-run the tests to confirm the new code passes all the tests. @@ -194,8 +195,7 @@ Releases ~~~~~~~~~ AequilibraE releases are automatically uploaded to the `Python Package Index -`__ (pypi) at each new GitHub release (2 to 6 times per year). - +`_ (pypi) at each new GitHub release (2 to 6 times per year). Acknowledgement ~~~~~~~~~~~~~~~ diff --git a/docs/source/history.rst b/docs/source/history.rst index 473985d2a..8564cf2f1 100644 --- a/docs/source/history.rst +++ b/docs/source/history.rst @@ -14,10 +14,13 @@ It all started when `Pedro `_ was a student at `UCI-I needed low level access to outputs of standard algorithms used in transportation modeling (e.g. path files from traffic assignment) and had that denied by the maker of the commercial software he normally used. There, the `first scratch of a traffic assignment procedure `_ was born. + After that, there were a couple of scripts developed to implement synthetic gravity models (calibration and application) that were developed for a government think-tank in Brazil `IPEA `_. + Around the same time, another student needed a piece of code that transformed a GIS link layer into a proper graph, where each link would become the connection between two nodes. + So there were three fundamental pieces that would come to be part of AequilibraE. The first take on a release software @@ -27,7 +30,7 @@ Having all those algorithms at hand, it made sense combining them into something seemed that QGIS was the way to go, so Pedro developed the `very first version of AequilibraE `_. -It was buggy as hell and there was very little, if any, software engineering built into it, but it put Aequilibrae on +It was buggy as hell and there was very little, if any, software engineering built into it, but it put AequilibraE on the map. That was 16/December/2014. The first reasonable version @@ -37,7 +40,7 @@ The first important thing Pedro noticed after releasing AequilibraE was that the even though it would make a lot more sense doing it in an Object-Oriented fashion, which let him down the path of creating the objects (graph, assignment results, matrix) that the software still relies on and were the foundation blocks of the proper API that is in the making. That -`version was released in 2016 `_ +`version was released in 2016 `_. Evolving into proper software ----------------------------- diff --git a/docs/source/installation.rst b/docs/source/installation.rst index 30e1e5a90..298a1000d 100644 --- a/docs/source/installation.rst +++ b/docs/source/installation.rst @@ -5,23 +5,20 @@ Installation ============ -In this section we describe how to install AequilibraE. +In this section we describe how to install AequilibraE. The recommendations on this page are current +as of September 2024. .. note:: Although AequilibraE is under intense development, we try to avoid making breaking changes to the API. In any case, you should check for new features and possible API changes often. -.. note:: - The recommendations on this page are current as of December 2023. - .. index:: installation Installation ------------ -1. Install `Python 3.8, 3.9, 3.10, 3.11 or 3.12 `__. We recommend Python 3.10 or 3.11 - +1. Install `Python 3.9, 3.10, 3.11 or 3.12 `_. We recommend Python 3.10 or 3.11 2. Install AequilibraE :: @@ -31,11 +28,10 @@ Installation .. _dependencies: Dependencies -~~~~~~~~~~~~ +------------ -All of AequilibraE's dependencies are readily available from `PyPI -`_ for all currently supported Python versions and major -platforms. +All of AequilibraE's dependencies are readily available from `PyPI `_ +for all currently supported Python versions and major platforms. .. _installing_spatialite_on_windows: @@ -53,7 +49,7 @@ Windows ^^^^^^^ .. note:: - On Windows ONLY, AequilibraE automatically verifies if you have SpatiaLite + On Windows ONLY, AequilibraE automatically verifies if you have Spatialite installed in your system and downloads it to your temporary folder if you do not. @@ -61,13 +57,13 @@ Spatialite does not have great support on Python for Windows. For this reason, it is necessary to download Spatialite for Windows and inform and load it to the Python SQLite driver every time you connect to the database. -One can download the appropriate version of the latest SpatiaLite release +One can download the appropriate version of the latest Spatialite release directly from its `project page `_ , or the cached versions on AequilibraE's website for `64-Bit Python `_ -After unpacking the zip file into its own folder (say *D:/spatialite*), one can -**temporarily** add the spatialite folder to system path environment variable, +After unpacking the zip file into its own folder (say ``D:/spatialite``), one can +*temporarily* add the spatialite folder to system path environment variable, as follows: :: @@ -89,12 +85,11 @@ On Ubuntu it is possible to install Spatialite by simply using apt-get sudo apt install -y libsqlite3-mod-spatialite sudo apt install -y libspatialite-dev - MacOS ^^^^^ On MacOS one can use brew as per -`this answer on StackOverflow `_. +`this answer on Stack Overflow `_. :: @@ -104,13 +99,10 @@ Hardware requirements --------------------- AequilibraE's requirements depend heavily on the size of the model you are using -for computation. The most important -things to keep an eye on are: +for computation. The most important things to keep an eye on are: * Number of zones on your model (size of the matrices you are dealing with) - * Number of matrices (vehicles classes (and user classes) you are dealing with) - * Number of links and nodes on your network (far less likely to create trouble) Substantial testing has been done with large real-world models (up to 8,000 diff --git a/docs/source/modeling_with_aequilibrae/route_choice.rst b/docs/source/modeling_with_aequilibrae/route_choice.rst index 39c29f1d0..3bfc59009 100644 --- a/docs/source/modeling_with_aequilibrae/route_choice.rst +++ b/docs/source/modeling_with_aequilibrae/route_choice.rst @@ -3,15 +3,15 @@ Route Choice ============ -As argued in the literature [3]_, the route choice problem does not have a closed solution, and the selection -of one of the many modelling frameworks [4]_ depends on many factors. A common modelling framework in practice +As argued in the literature [1]_, the route choice problem does not have a closed solution, and the selection +of one of the many modelling frameworks [2]_ depends on many factors. A common modelling framework in practice is consists of two steps: Choice set generation and the choice selection process. AequilibraE is the first modeling package with full support for route choice, from the creation of choice sets through multiple algorithms to the assignment of trips to the network using the traditional Path-Size logit. Costs, utilities and signs -~~~~~~~~~~~~~~~~~~~~~~~~~~ +-------------------------- AequilibraE's path computation procedures require all link costs to be positive. For that reason, link utilities (or disutilities) must be positive, while its obvious minus sign is handled internally. @@ -19,7 +19,7 @@ This mechanism prevents the possibility of links with actual positive utility, b not reasonable to exist in practice. Choice set generation algorithms available -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +------------------------------------------ All algorithms have been implemented as a single software class @@ -29,7 +29,7 @@ All algorithms have been implemented as a single software class | Link-Penalisation | Classical link penalisation. | | | | +----------------------------------+----------------------------------+ -| Breadth-First Search with | As described in [4]_. | +| Breadth-First Search with | As described in [2]_. | | Link Removal | | +----------------------------------+----------------------------------+ | Breadth-First Search with | A combination of BFS-LE and LP | @@ -65,24 +65,27 @@ Full process overview --------------------- The estimation of route choice models based on vehicle GPS data can be explored on a family of papers scheduled to -be presented at the ATRF 2024 [1]_, [2]_, [3]_. +be presented at the ATRF 2024 [1]_ [3]_ [4]_. +.. [1] Zill, J. C., and P. V. de Camargo. State-Wide Route Choice Models (Submitted). + Presented at the ATRF, Melbourne, Australia, 2024. + +.. [2] Rieser-Schüssler, N., Balmer, M., & Axhausen, K. W. (2012). Route choice sets for very high-resolution data. + Transportmetrica A: Transport Science, 9(9), 825–845. + https://doi.org/10.1080/18128602.2012.671383 -.. [1] Camargo, P. V. de, and R. Imai. Map-Matching Large Streams of Vehicle GPS Data into Bespoke Networks (Submitted). +.. [3] Camargo, P. V. de, and R. Imai. Map-Matching Large Streams of Vehicle GPS Data into Bespoke Networks (Submitted). Presented at the ATRF, Melbourne, 2024. -.. [2] Moss, J., P. V. de Camargo, C. de Freitas, and R. Imai. High-Performance Route Choice Set Generation on +.. [4] Moss, J., P. V. de Camargo, C. de Freitas, and R. Imai. High-Performance Route Choice Set Generation on Large Networks (Submitted). Presented at the ATRF, Melbourne, 2024. -.. [3] Zill, J. C., and P. V. de Camargo. State-Wide Route Choice Models (Submitted). - Presented at the ATRF, Melbourne, Australia, 2024. - -.. [4] Rieser-Schüssler, N., Balmer, M., & Axhausen, K. W. (2012). Route choice sets for very high-resolution data. - Transportmetrica A: Transport Science, 9(9), 825–845. - https://doi.org/10.1080/18128602.2012.671383 +.. seealso:: + + LOREM IPSUM .. toctree:: :maxdepth: 1 + route_choice/route_choice_model.rst route_choice/choice_set_generation.rst - route_choice/route_choice_model.rst \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/route_choice/choice_set_generation.rst b/docs/source/modeling_with_aequilibrae/route_choice/choice_set_generation.rst index c5813245d..443b4308c 100644 --- a/docs/source/modeling_with_aequilibrae/route_choice/choice_set_generation.rst +++ b/docs/source/modeling_with_aequilibrae/route_choice/choice_set_generation.rst @@ -43,7 +43,6 @@ development/estimation stage. The overhead of the LP method is negligible due to AequilibraE's internal data structures that allow for easy data manipulation of the graph in memory. - BFS-LE ~~~~~~ @@ -100,7 +99,6 @@ Code example graph.prepare_graph(np.array([1, 2, 3, 50, 100, 150])) - nodes = [(1, 50), (2, 100), (3, 150)] # List of tuples with (origin, destination) nodes max_routes = 10 # Maximum number of routes to be computed for each OD pair penalty = 1.01 # Penalty to be applied to links used in paths. diff --git a/docs/source/modeling_with_aequilibrae/static_traffic_assignment.rst b/docs/source/modeling_with_aequilibrae/static_traffic_assignment.rst index d0077e01e..44b907706 100644 --- a/docs/source/modeling_with_aequilibrae/static_traffic_assignment.rst +++ b/docs/source/modeling_with_aequilibrae/static_traffic_assignment.rst @@ -16,7 +16,6 @@ Further, many AequilibraE users are new to the *craft*, so we have elected to st creating documentation on the most important topics in the transportation modeling practice, where we detail how these concepts are translated into the AequilibraE tools and recommended workflows. - .. toctree:: :maxdepth: 1 :caption: Static Traffic Assignment diff --git a/docs/source/support.rst b/docs/source/support.rst index 36f1bcb8b..05cd46fb5 100644 --- a/docs/source/support.rst +++ b/docs/source/support.rst @@ -8,8 +8,7 @@ Support & Sponsors Support ------- -AequilibraE is developed by a small but dedicated team of professionals with -limited funding. +AequilibraE is developed by a small but dedicated team of professionals with limited funding. Free support ~~~~~~~~~~~~ @@ -19,7 +18,7 @@ already asked in the past, with the first option being the most often used as of 1. Joining the `AequilibraE Google Group `_ and sending your question there. -2. Posting your question to `GIS StackOverflow `_ using the #aequilibrae tag; +2. Posting your question to `GIS StackOverflow `_ using the ``#aequilibrae`` tag; Please note that all questions and answers in both forums are public. @@ -29,7 +28,7 @@ Paid support Paid support for AequilibraE is offered by Outer Loop Consulting, an Australia-based consulting company, with support available in English, Portuguese, German & Spanish. -All support is offered in prepaid packages of a minimum of 10h of consulting by phone, email or Microsoft Teams +All support is offered in prepaid packages of a minimum of 10h of consulting by phone, e-mail or Microsoft Teams at a fixed rate of USD 150/h. To acquire a paid support package for AequilibraE, please e-mail aequilibrae@outerloop.io @@ -66,8 +65,7 @@ Maintenance sponsors 2016: -* `Instituto de Pesquisa Econômica Aplicadas `_ - +* `Instituto de Pesquisa Econômica Aplicada `_ Feature sponsors ~~~~~~~~~~~~~~~~ diff --git a/docs/source/validation_benchmarking/ipf_performance.rst b/docs/source/validation_benchmarking/ipf_performance.rst index ade08f154..12fe4cfec 100644 --- a/docs/source/validation_benchmarking/ipf_performance.rst +++ b/docs/source/validation_benchmarking/ipf_performance.rst @@ -10,17 +10,17 @@ we can take full advantage of multi-core CPUs. We have also implemented the abil of using both 32-bit and 64-bit floating-point seed matrices, which has direct impact on cache use and consequently computational performance. -In this section, we compare the -runtime of AequilibraE's current implementation of IPF, -with a general IPF algorithm written in pure Python, available `here `_. +In this section, we compare the runtime of AequilibraE's current implementation of IPF, +with a general IPF algorithm written in pure Python, available +`here `_. The figure below compares AequilibraE's IPF runtime with one core with the benchmark Python code. From the figure below, we can notice that the runtimes were practically the same for the instances with 1,000 zones or less. As the number of zones increases, AequilibraE demonstrated to be slightly faster -than the benchmark python code, while applying IPF to a 32-bit NumPy array (np.float32) was significantly faster. -It's worth mentioning that -the user can set up a threshold for AequilibraE's IPF function, as well as use more than one -core to speed up the fitting process. +than the benchmark python code, while applying IPF to a 32-bit NumPy array (``np.float32``) was significantly faster. + +It's worth mentioning that the user can set up a threshold for AequilibraE's IPF function, +as well as use more than one core to speed up the fitting process. .. image:: ../images/ipf_runtime_aequilibrae_vs_benchmark.png :align: center diff --git a/docs/source/validation_benchmarking/traffic_assignment.rst b/docs/source/validation_benchmarking/traffic_assignment.rst index 7f815c79f..fea3099a4 100644 --- a/docs/source/validation_benchmarking/traffic_assignment.rst +++ b/docs/source/validation_benchmarking/traffic_assignment.rst @@ -10,17 +10,17 @@ least one very reasonable question: Are the results right? For this reason, we have used all equilibrium traffic assignment algorithms available in AequilibraE to solve standard instances used in academia for comparing algorithm results. + Instances can be downloaded `here `_. All tests were performed with the AequilibraE version 1.1.0. Validation ------------ +---------- As shown below, the results produced by AequilibraE are within expected, although -some differences have been found, particularly for Winnipeg. -We suspect that there are issues with the reference results and welcome further investigations. - +some differences have been found, particularly for Winnipeg. We suspect that there are +issues with the reference results and welcome further investigations. .. tabs:: @@ -34,7 +34,7 @@ We suspect that there are issues with the reference results and welcome further * Nodes: 12,982 * Zones: 1,790 - .. tab:: Bi-conjugate Frank-Wolfe + .. tab:: biconjugate Frank-Wolfe .. image:: ../images/assig_validation/ChicagoRegional_bfw-1000_iter.png :align: center @@ -72,7 +72,7 @@ We suspect that there are issues with the reference results and welcome further * Nodes: 1,020 * Zones: 110 - .. tab:: Bi-conjugate Frank-Wolfe + .. tab:: biconjugate Frank-Wolfe .. image:: ../images/assig_validation/Barcelona_bfw-1000_iter.png :align: center @@ -110,7 +110,7 @@ We suspect that there are issues with the reference results and welcome further * Nodes: 416 * Zones: 38 - .. tab:: Bi-conjugate Frank-Wolfe + .. tab:: biconjugate Frank-Wolfe .. image:: ../images/assig_validation/Winnipeg_bfw-1000_iter.png :align: center @@ -148,7 +148,7 @@ We suspect that there are issues with the reference results and welcome further * Nodes: 416 * Zones: 38 - .. tab:: Bi-conjugate Frank-Wolfe + .. tab:: biconjugate Frank-Wolfe .. image:: ../images/assig_validation/Anaheim_bfw-1000_iter.png :align: center @@ -186,7 +186,7 @@ We suspect that there are issues with the reference results and welcome further * Nodes: 24 * Zones: 24 - .. tab:: Bi-conjugate Frank-Wolfe + .. tab:: biconjugate Frank-Wolfe .. image:: ../images/assig_validation/SiouxFalls_bfw-1000_iter.png :align: center @@ -218,8 +218,8 @@ Convergence Study ----------------- Besides validating the final results from the algorithms, we have also compared -how well they converge for the largest instance we have tested (Chicago -Regional), as that instance has a comparable size to real-world models. +how well they converge for the largest instance we have tested (Chicago Regional), +as that instance has a comparable size to real-world models. .. _algorithm_convergence_comparison: @@ -260,14 +260,12 @@ Regional), as that instance has a comparable size to real-world models. :width: 590 :alt: Algorithm convergence comparison - Not surprisingly, one can see that Frank-Wolfe far outperforms the Method of Successive Averages for a number of iterations larger than 25 in the case of -Chicago, and is capable of -reaching 1.0e-04 just after 800 iterations, while MSA is still at 3.5e-4 even -after 1,000 iterations for that same case. +Chicago, and is capable of reaching 1.0e-04 just after 800 iterations, while +MSA is still at 3.5e-4 even after 1,000 iterations for that same case. -The actual show, however, is left for the Bi-conjugate Frank-Wolfe +The actual show, however, is left for the biconjugate Frank-Wolfe implementation, which delivers a relative gap of under 1.0e-04 in under 200 iterations, and a relative gap of under 1.0e-05 in just over 700 iterations. @@ -282,7 +280,7 @@ All tests were run on a workstation equipped AMD Threadripper 3970X with 32 core (64 threads) @ 3.7 GHz (memory use is trivial for these instances). On this machine, AequilibraE performed 1,000 iterations of -Bi-conjugate Frank-Wolfe assignment on the Chicago Network in a little over 4 minutes, +biconjugate Frank-Wolfe assignment on the Chicago Network in a little over 4 minutes, or a little less than 0.43s per iteration. Compared with AequilibraE previous versions, we can notice a reasonable decrease diff --git a/docs/source/version_history.rst b/docs/source/version_history.rst index 755250d50..adb966787 100644 --- a/docs/source/version_history.rst +++ b/docs/source/version_history.rst @@ -5,9 +5,8 @@ Older versions ============== -AequilibraE has been evolving quite fast, so we recommend you upgrading to a -newer version as soon as you can. In the meantime, you can find the -documentation for all versions since 0.5.3. +AequilibraE has been evolving quite fast, so we recommend you upgrading to a newer version as soon as you can. +In the meantime, you can find the documentation for all versions since 0.5.3. .. grid:: From ea16285119abc583f32d66291538104fc75f1808 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Mon, 2 Sep 2024 09:55:25 -0300 Subject: [PATCH 11/57] change folder structure --- README.md | 2 +- docs/source/{ => api}/api.rst | 20 +- docs/source/conf.py | 8 +- docs/source/index.rst | 6 +- .../modeling_with_aequilibrae.rst | 16 +- .../route_choice.rst | 4 +- .../transit_assignment.rst | 2 + .../{ => useful_information}/developing.rst | 0 .../development/roadmap.rst | 0 .../development/softwaredevelopment.rst | 0 .../{ => useful_information}/history.rst | 0 .../{ => useful_information}/installation.rst | 0 .../{ => useful_information}/support.rst | 0 .../IPF_benchmark.ipynb | 0 .../validation_benchmarking/IPF_benchmark.rst | 757 ++++++++++++++++++ .../ipf_performance.rst | 8 +- .../traffic_assignment.rst | 50 +- .../validation_benchmarking}/validation.rst | 4 +- .../version_history.rst | 0 19 files changed, 818 insertions(+), 59 deletions(-) rename docs/source/{ => api}/api.rst (86%) rename docs/source/{ => modeling_with_aequilibrae}/modeling_with_aequilibrae.rst (69%) rename docs/source/{ => useful_information}/developing.rst (100%) rename docs/source/{ => useful_information}/development/roadmap.rst (100%) rename docs/source/{ => useful_information}/development/softwaredevelopment.rst (100%) rename docs/source/{ => useful_information}/history.rst (100%) rename docs/source/{ => useful_information}/installation.rst (100%) rename docs/source/{ => useful_information}/support.rst (100%) rename docs/source/{ => useful_information}/validation_benchmarking/IPF_benchmark.ipynb (100%) create mode 100644 docs/source/useful_information/validation_benchmarking/IPF_benchmark.rst rename docs/source/{ => useful_information}/validation_benchmarking/ipf_performance.rst (92%) rename docs/source/{ => useful_information}/validation_benchmarking/traffic_assignment.rst (88%) rename docs/source/{ => useful_information/validation_benchmarking}/validation.rst (81%) rename docs/source/{ => useful_information}/version_history.rst (100%) diff --git a/README.md b/README.md index cd6f09bcb..bb6f9e18c 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ readily available to other modeling activities. AequilibraE includes multi-class user-equilibrium assignment with full support for class-specific networks, value-of-time and generalized cost functions, and includes a range of equilibration algorithms, including MSA, the traditional -Frank-Wolfe as well as the state-of-the-art Bi-conjugate Frank-Wolfe. +Frank-Wolfe as well as the state-of-the-art biconjugate Frank-Wolfe. AequilibraE's support for public transport includes a GTFS importer that can map-match routes into the model network and an optimized version of the diff --git a/docs/source/api.rst b/docs/source/api/api.rst similarity index 86% rename from docs/source/api.rst rename to docs/source/api/api.rst index b81187a3c..c9d7cb891 100644 --- a/docs/source/api.rst +++ b/docs/source/api/api.rst @@ -11,7 +11,7 @@ Project .. currentmodule:: aequilibrae.project .. autosummary:: :nosignatures: - :toctree: generated/ + :toctree: api/generated/ Project @@ -20,7 +20,7 @@ Project Components .. currentmodule:: aequilibrae.project .. autosummary:: :nosignatures: - :toctree: generated/ + :toctree: api/generated/ About FieldEditor @@ -34,7 +34,7 @@ Project Objects .. currentmodule:: aequilibrae.project .. autosummary:: :nosignatures: - :toctree: generated/ + :toctree: api/generated/ Zone @@ -43,7 +43,7 @@ Network Data .. currentmodule:: aequilibrae.project.network .. autosummary:: :nosignatures: - :toctree: generated/ + :toctree: api/generated/ Modes LinkTypes @@ -56,7 +56,7 @@ Network Items .. currentmodule:: aequilibrae.project.network .. autosummary:: :nosignatures: - :toctree: generated/ + :toctree: api/generated/ Mode LinkType @@ -69,7 +69,7 @@ Parameters .. currentmodule:: aequilibrae .. autosummary:: :nosignatures: - :toctree: generated/ + :toctree: api/generated/ Parameters @@ -77,7 +77,7 @@ Distribution ------------ .. currentmodule:: aequilibrae.distribution .. autosummary:: - :toctree: generated/ + :toctree: api/generated/ Ipf GravityApplication @@ -89,7 +89,7 @@ Matrix .. currentmodule:: aequilibrae.matrix .. autosummary:: :nosignatures: - :toctree: generated/ + :toctree: api/generated/ AequilibraeData AequilibraeMatrix @@ -99,7 +99,7 @@ Paths .. currentmodule:: aequilibrae.paths .. autosummary:: :nosignatures: - :toctree: generated/ + :toctree: api/generated/ Graph TransitGraph @@ -122,7 +122,7 @@ Transit .. currentmodule:: aequilibrae.transit .. autosummary:: :nosignatures: - :toctree: generated/ + :toctree: api/generated/ Transit TransitGraphBuilder diff --git a/docs/source/conf.py b/docs/source/conf.py index 7ac317195..dfeee9176 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -86,7 +86,7 @@ "examples/other_applications", ] ), - # "plot_gallery": "False", + "plot_gallery": "False", } # Add any paths that contain templates here, relative to this directory. @@ -165,9 +165,9 @@ latex_documents = [(master_doc, "AequilibraE.tex", "AequilibraE Documentation", "Pedro Camargo", "manual")] latex_appendices = [ - "installation", - "validation_benchmarking/ipf_performance", - "validation_benchmarking/traffic_assignment", + "useful_information/installation", + "useful_information/validation_benchmarking/ipf_performance", + "useful_information/validation_benchmarking/traffic_assignment", ] # -- Options for manual page output ------------------------------------------ diff --git a/docs/source/index.rst b/docs/source/index.rst index b841054ae..734a5cd25 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -29,7 +29,7 @@ resources not easily available from other open-source packages in the Python (Nu results. .. grid-item-card:: :material-outlined:`menu_book;1.5em` Modeling with AequilibraE - :link: modeling_with_aequilibrae + :link: modeling_with_aequilibrae/modeling_with_aequilibrae :link-type: any :text-align: center @@ -56,5 +56,5 @@ resources not easily available from other open-source packages in the Python (Nu :maxdepth: 1 _auto_examples/index - modeling_with_aequilibrae - api + modeling_with_aequilibrae/modeling_with_aequilibrae + api/api diff --git a/docs/source/modeling_with_aequilibrae.rst b/docs/source/modeling_with_aequilibrae/modeling_with_aequilibrae.rst similarity index 69% rename from docs/source/modeling_with_aequilibrae.rst rename to docs/source/modeling_with_aequilibrae/modeling_with_aequilibrae.rst index 91c29fbb5..bda33cdb2 100644 --- a/docs/source/modeling_with_aequilibrae.rst +++ b/docs/source/modeling_with_aequilibrae/modeling_with_aequilibrae.rst @@ -16,13 +16,13 @@ a start guide to a complete view into AequilibraE's data structure. .. toctree:: :numbered: 1 - :titlesonly: + :maxdepth: 1 :caption: A guide to AequilibraE - modeling_with_aequilibrae/project - modeling_with_aequilibrae/project_database - modeling_with_aequilibrae/parameter_file - modeling_with_aequilibrae/public_transport - modeling_with_aequilibrae/static_traffic_assignment - modeling_with_aequilibrae/transit_assignment - modeling_with_aequilibrae/route_choice + project + project_database + parameter_file + public_transport + static_traffic_assignment + transit_assignment + route_choice diff --git a/docs/source/modeling_with_aequilibrae/route_choice.rst b/docs/source/modeling_with_aequilibrae/route_choice.rst index 3bfc59009..c6a52e0f5 100644 --- a/docs/source/modeling_with_aequilibrae/route_choice.rst +++ b/docs/source/modeling_with_aequilibrae/route_choice.rst @@ -71,8 +71,7 @@ be presented at the ATRF 2024 [1]_ [3]_ [4]_. Presented at the ATRF, Melbourne, Australia, 2024. .. [2] Rieser-Schüssler, N., Balmer, M., & Axhausen, K. W. (2012). Route choice sets for very high-resolution data. - Transportmetrica A: Transport Science, 9(9), 825–845. - https://doi.org/10.1080/18128602.2012.671383 + Transportmetrica A: Transport Science, 9(9), 825–845. DOI: https://doi.org/10.1080/18128602.2012.671383 .. [3] Camargo, P. V. de, and R. Imai. Map-Matching Large Streams of Vehicle GPS Data into Bespoke Networks (Submitted). Presented at the ATRF, Melbourne, 2024. @@ -86,6 +85,7 @@ be presented at the ATRF 2024 [1]_ [3]_ [4]_. .. toctree:: :maxdepth: 1 + :caption: Route Choice route_choice/route_choice_model.rst route_choice/choice_set_generation.rst diff --git a/docs/source/modeling_with_aequilibrae/transit_assignment.rst b/docs/source/modeling_with_aequilibrae/transit_assignment.rst index 196e9205b..ff1f05508 100644 --- a/docs/source/modeling_with_aequilibrae/transit_assignment.rst +++ b/docs/source/modeling_with_aequilibrae/transit_assignment.rst @@ -3,9 +3,11 @@ Transit assignment ================== +.. we should add here a brief introduction about transit assignment .. toctree:: :maxdepth: 1 + :caption: Transit Assignment transit_assignment/hyperpath_routing.rst transit_assignment/transit_graph.rst \ No newline at end of file diff --git a/docs/source/developing.rst b/docs/source/useful_information/developing.rst similarity index 100% rename from docs/source/developing.rst rename to docs/source/useful_information/developing.rst diff --git a/docs/source/development/roadmap.rst b/docs/source/useful_information/development/roadmap.rst similarity index 100% rename from docs/source/development/roadmap.rst rename to docs/source/useful_information/development/roadmap.rst diff --git a/docs/source/development/softwaredevelopment.rst b/docs/source/useful_information/development/softwaredevelopment.rst similarity index 100% rename from docs/source/development/softwaredevelopment.rst rename to docs/source/useful_information/development/softwaredevelopment.rst diff --git a/docs/source/history.rst b/docs/source/useful_information/history.rst similarity index 100% rename from docs/source/history.rst rename to docs/source/useful_information/history.rst diff --git a/docs/source/installation.rst b/docs/source/useful_information/installation.rst similarity index 100% rename from docs/source/installation.rst rename to docs/source/useful_information/installation.rst diff --git a/docs/source/support.rst b/docs/source/useful_information/support.rst similarity index 100% rename from docs/source/support.rst rename to docs/source/useful_information/support.rst diff --git a/docs/source/validation_benchmarking/IPF_benchmark.ipynb b/docs/source/useful_information/validation_benchmarking/IPF_benchmark.ipynb similarity index 100% rename from docs/source/validation_benchmarking/IPF_benchmark.ipynb rename to docs/source/useful_information/validation_benchmarking/IPF_benchmark.ipynb diff --git a/docs/source/useful_information/validation_benchmarking/IPF_benchmark.rst b/docs/source/useful_information/validation_benchmarking/IPF_benchmark.rst new file mode 100644 index 000000000..b2bf284e4 --- /dev/null +++ b/docs/source/useful_information/validation_benchmarking/IPF_benchmark.rst @@ -0,0 +1,757 @@ +IPF Performance +=============== + +The use of iterative proportional fitting (IPF) is quite common on +processes involving doubly-constraining matrices, such as synthetic +gravity models and fractional split models (aggregate destination-choice +models). + +As this is a commonly used algorithm, we have implemented it in Cython, +where we can take full advantage of multi-core CPUs. We have also +implemented the ability of using both 32-bit and 64-bit floating-point +seed matrices, which has direct impact on cache use and consequently +computational performance. + +In this section, we compare the runtime of AequilibraE’s current +implementation of IPF, with a general IPF algorithm written in pure +Python, available +`here `__. + +The figure below compares AequilibraE’s IPF runtime with one core with +the benchmark Python code. From the figure below, we can notice that the +runtimes were practically the same for the instances with 1,000 zones or +less. As the number of zones increases, AequilibraE demonstrated to be +slightly faster than the benchmark python code, while applying IPF to a +32-bit NumPy array (np.float32) was significantly faster. It’s worth +mentioning that the user can set up a threshold for AequilibraE’s IPF +function, as well as use more than one core to speed up the fitting +process. + +.. figure:: ../../images/ipf_runtime_aequilibrae_vs_benchmark.png + :alt: AequilibraE’s IPF runtime + + AequilibraE’s IPF runtime + +As IPF is an embarrassingly-parallel workload, it is more relevant to +look at the performance of the AequilibraE implementations, starting by +comparing the implementation performance for inputs in 32 vs 64 bits +using 32 threads. + +.. figure:: ../../images/ipf_runtime_32vs64bits.png + :alt: AequilibraE’s IPF runtime 32 vs 64 bits + + AequilibraE’s IPF runtime 32 vs 64 bits + +The difference is staggering, with the 32-bit implementation being twice +as fast as the 64-bit one for large matrices. It is also worth noting +that differences in results between the outputs between these two +versions are incredibly small (RMSE < 1.1e-10), and therefore unlikely +to be relevant in most applications. + +We can also look at performance gain across matrix sizes and number of +cores, and it becomes clear that the 32-bit version scales significantly +better than its 64-bit counterpart, showing significant performance +gains up to 16 threads, while the latter stops showing much improvement +beyond 8 threads, likely due to limitations on cache size. + +.. figure:: ../../images/ipf_runtime_vs_num_cores.png + :alt: number of cores used in IPF for 64 bit matrices + + number of cores used in IPF for 64 bit matrices + +.. figure:: ../../images/ipf_runtime_vs_num_cores32bits.png + :alt: number of cores used in IPF for 32 bit matrices + + number of cores used in IPF for 32 bit matrices + +In conclusion, AequilibraE’s IPF implementation is over 11 times faster +than its pure Python counterpart for large matrices on a workstation, +largely due to the use of Cython and multi-threading, but also due to +the use of a 32-bit version of the algorithm. + +These tests were run on a Threadripper 3970x (released in 2019) +workstation with 32 cores (64 threads) @ 3.7 GHz and 256 Gb of RAM. The +code is provided below for reference. + +Reference code +-------------- + +.. code:: ipython2 + + from copy import deepcopy + from time import perf_counter + import numpy as np + import pandas as pd + from aequilibrae.distribution.ipf_core import ipf_core + from tqdm import tqdm + +.. code:: ipython2 + + # From: + # https://github.com/joshchea/python-tdm/blob/master/scripts/CalcDistribution.py + + def CalcFratar(ProdA, AttrA, Trips1, maxIter=10): + '''Calculates fratar trip distribution + ProdA = Production target as array + AttrA = Attraction target as array + Trips1 = Seed trip table for fratar + maxIter (optional) = maximum iterations, default is 10 + Returns fratared trip table + ''' + # print('Checking production, attraction balancing:') + sumP = ProdA.sum() + sumA = AttrA.sum() + # print('Production: ', sumP) + # print('Attraction: ', sumA) + if sumP != sumA: + # print('Productions and attractions do not balance, attractions will be scaled to productions!') + AttrA = AttrA*(sumP/sumA) + else: + pass + # print('Production, attraction balancing OK.') + # Run 2D balancing ---> + for balIter in range(0, maxIter): + ComputedProductions = Trips1.sum(1) + ComputedProductions[ComputedProductions == 0] = 1 + OrigFac = (ProdA/ComputedProductions) + Trips1 = Trips1*OrigFac[:, np.newaxis] + + ComputedAttractions = Trips1.sum(0) + ComputedAttractions[ComputedAttractions == 0] = 1 + DestFac = (AttrA/ComputedAttractions) + Trips1 = Trips1*DestFac + return Trips1 + +.. code:: ipython2 + + mat_sizes = [500, 750, 1000, 1500, 2500, 5000, 7500, 10000, 15000] + +.. code:: ipython2 + + #Benchmarking + bench_data = [] + cores = 1 + repetitions = 5 + iterations = 100 + for zones in mat_sizes: + for repeat in tqdm(range(repetitions), f"Repetitions for zone size {zones}"): + mat1 = np.random.rand(zones, zones) + target_prod = np.random.rand(zones) + target_atra = np.random.rand(zones) + target_atra *= target_prod.sum()/target_atra.sum() + + aeq_mat = deepcopy(mat1) + # We use a nonsensical negative tolerance to force it to run all iterations + # and set warning for non-convergence to false, as we know it won't converge + t = perf_counter() + ipf_core(aeq_mat, target_prod, target_atra, max_iterations=iterations, tolerance=-5, cores=cores, warn=False) + aeqt = perf_counter() - t + + aeq_mat32 = np.array(mat1, np.float32) + # We now run the same thing with a seed matrix in single-precision (float 32 bits) instead of double as above (64 bits) + t = perf_counter() + ipf_core(aeq_mat32, target_prod, target_atra, max_iterations=iterations, tolerance=-5, cores=cores, warn=False) + aeqt2 = perf_counter() - t + + bc_mat = deepcopy(mat1) + t = perf_counter() + x = CalcFratar(target_prod, target_atra, bc_mat, maxIter=iterations) + + bench_data.append([zones, perf_counter() - t, aeqt, aeqt2]) + + +.. parsed-literal:: + + Repetitions for zone size 500: 100%|██████████| 5/5 [00:01<00:00, 2.60it/s] + Repetitions for zone size 750: 100%|██████████| 5/5 [00:04<00:00, 1.18it/s] + Repetitions for zone size 1000: 100%|██████████| 5/5 [00:07<00:00, 1.57s/it] + Repetitions for zone size 1500: 100%|██████████| 5/5 [00:19<00:00, 3.88s/it] + Repetitions for zone size 2500: 100%|██████████| 5/5 [00:56<00:00, 11.24s/it] + Repetitions for zone size 5000: 100%|██████████| 5/5 [03:44<00:00, 44.89s/it] + Repetitions for zone size 7500: 100%|██████████| 5/5 [08:09<00:00, 97.89s/it] + Repetitions for zone size 10000: 100%|██████████| 5/5 [14:11<00:00, 170.34s/it] + Repetitions for zone size 15000: 100%|██████████| 5/5 [32:23<00:00, 388.70s/it] + + +.. code:: ipython2 + + bench_df = pd.DataFrame(bench_data, columns=["Zones in the model", "PythonCode", "AequilibraE", "AequilibraE-32bits"]) + bench_df.groupby(["Zones in the model"]).mean().plot.bar() + + + + +.. parsed-literal:: + + + + + + +.. image:: IPF_benchmark_files/IPF_benchmark_7_1.png + + +.. code:: ipython2 + + bench_df.groupby(["Zones in the model"]).mean() + + + + +.. raw:: html + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PythonCodeAequilibraEAequilibraE-32bits
Zones in the model
5000.1031660.1400510.137266
7500.2417570.3029310.297389
10000.5139840.5297270.516339
15001.4168481.2768441.156637
25004.2535633.5816453.333664
500017.05916614.28876013.257936
750035.56868632.02156329.666000
1000059.03096757.02820453.155270
15000134.895915130.652199120.535656
+
+ + + +.. code:: ipython2 + + #Benchmarking 32 threads + bench_data_parallel = [] + cores = 32 + repetitions = 5 + iterations = 100 + for zones in mat_sizes: + for repeat in tqdm(range(repetitions), f"Repetitions for zone size {zones}"): + mat1 = np.random.rand(zones, zones) + target_prod = np.random.rand(zones) + target_atra = np.random.rand(zones) + target_atra *= target_prod.sum()/target_atra.sum() + + aeq_mat = deepcopy(mat1) + # We use a nonsensical negative tolerance to force it to run all iterations + # and set warning for non-convergence to false, as we know it won't converge + t = perf_counter() + ipf_core(aeq_mat, target_prod, target_atra, max_iterations=iterations, tolerance=-5, cores=cores, warn=False) + aeqt = perf_counter() - t + + aeq_mat32 = np.array(mat1, np.float32) + # We now run the same thing with a seed matrix in single-precision (float 32 bits) instead of double as above (64 bits) + t = perf_counter() + ipf_core(aeq_mat32, target_prod, target_atra, max_iterations=iterations, tolerance=-5, cores=cores, warn=False) + aeqt2 = perf_counter() - t + + rmse = np.sqrt(np.mean((aeq_mat-aeq_mat32)**2)) + + bench_data_parallel.append([zones, aeqt, aeqt2, rmse]) + + +.. parsed-literal:: + + Repetitions for zone size 500: 100%|██████████| 5/5 [00:01<00:00, 2.70it/s] + Repetitions for zone size 750: 100%|██████████| 5/5 [00:01<00:00, 2.64it/s] + Repetitions for zone size 1000: 100%|██████████| 5/5 [00:02<00:00, 2.37it/s] + Repetitions for zone size 1500: 100%|██████████| 5/5 [00:03<00:00, 1.61it/s] + Repetitions for zone size 2500: 100%|██████████| 5/5 [00:07<00:00, 1.41s/it] + Repetitions for zone size 5000: 100%|██████████| 5/5 [00:24<00:00, 4.91s/it] + Repetitions for zone size 7500: 100%|██████████| 5/5 [00:49<00:00, 9.96s/it] + Repetitions for zone size 10000: 100%|██████████| 5/5 [01:26<00:00, 17.29s/it] + Repetitions for zone size 15000: 100%|██████████| 5/5 [03:10<00:00, 38.02s/it] + + +.. code:: ipython2 + + bench_df_parallel = pd.DataFrame(bench_data_parallel, columns=["Zones in the model", "AequilibraE", "AequilibraE-32bits", "rmse"]) + bench_df_parallel.groupby(["Zones in the model"]).mean()[[ "AequilibraE", "AequilibraE-32bits"]].plot.bar() + + + + +.. parsed-literal:: + + + + + + +.. image:: IPF_benchmark_files/IPF_benchmark_10_1.png + + +.. code:: ipython2 + + bench_df_parallel.groupby(["Zones in the model"]).mean() + + + + +.. raw:: html + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AequilibraEAequilibraE-32bitsrmse
Zones in the model
5000.1702320.1724601.080659e-10
7500.1834740.1838577.100473e-11
10000.2112370.1919615.137420e-11
15000.3227090.2521153.358886e-11
25000.7797150.5220372.036197e-11
50002.7450041.7601009.588117e-12
75005.9004313.1700066.258958e-12
1000010.2553055.4566314.733115e-12
1500022.61866511.8604973.102268e-12
+
+ + + +.. code:: ipython2 + + cores_to_use = [1, 2, 4, 8, 16, 32] + +.. code:: ipython2 + + aeq_data = [] + repetitions = 1 + iterations = 50 + for zones in mat_sizes: + for cores in tqdm(cores_to_use,f"Zone size: {zones}"): + for repeat in range(repetitions): + mat1 = np.random.rand(zones, zones) + target_prod = np.random.rand(zones) + target_atra = np.random.rand(zones) + target_atra *= target_prod.sum()/target_atra.sum() + + aeq_mat = np.array(deepcopy(mat1), np.float32) + t = perf_counter() + ipf_core(aeq_mat, target_prod, target_atra, max_iterations=iterations, tolerance=-5, cores=cores, warn=False) + aeqt = perf_counter() - t + + aeq_data.append([zones, cores, aeqt]) + + +.. parsed-literal:: + + Zone size: 500: 100%|██████████| 6/6 [00:00<00:00, 12.14it/s] + Zone size: 750: 100%|██████████| 6/6 [00:00<00:00, 10.20it/s] + Zone size: 1000: 100%|██████████| 6/6 [00:00<00:00, 6.87it/s] + Zone size: 1500: 100%|██████████| 6/6 [00:01<00:00, 3.42it/s] + Zone size: 2500: 100%|██████████| 6/6 [00:04<00:00, 1.32it/s] + Zone size: 5000: 100%|██████████| 6/6 [00:16<00:00, 2.73s/it] + Zone size: 7500: 100%|██████████| 6/6 [00:35<00:00, 5.93s/it] + Zone size: 10000: 100%|██████████| 6/6 [01:02<00:00, 10.46s/it] + Zone size: 15000: 100%|██████████| 6/6 [02:21<00:00, 23.62s/it] + + +.. code:: ipython2 + + aeq_df = pd.DataFrame(aeq_data, columns=["zones", "cores", "time"]) + aeq_df = aeq_df[aeq_df.zones>1000] + aeq_df = aeq_df.groupby(["zones", "cores"]).mean().reset_index() + aeq_df = aeq_df.pivot_table(index="zones", columns="cores", values="time") + for cores in cores_to_use[::-1]: + aeq_df.loc[:, cores] /= aeq_df[1] + aeq_df.transpose().plot() + aeq_df + + + + +.. raw:: html + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
cores12481632
zones
15001.00.5638660.4267110.2704520.1911470.213856
25001.00.5277810.3750330.2497760.1564730.157657
50001.00.5101830.2781500.1744690.1431750.134064
75001.00.5087060.2658350.1572170.1250070.107014
100001.00.5071720.2661920.1548440.1207710.103968
150001.00.5046960.2599330.1523940.1150520.101344
+
+ + + + +.. image:: IPF_benchmark_files/IPF_benchmark_14_1.png + + +.. code:: ipython2 + + aeq_data = [] + repetitions = 1 + iterations = 50 + for zones in mat_sizes: + for cores in tqdm(cores_to_use,f"Zone size: {zones}"): + for repeat in range(repetitions): + mat1 = np.random.rand(zones, zones) + target_prod = np.random.rand(zones) + target_atra = np.random.rand(zones) + target_atra *= target_prod.sum()/target_atra.sum() + + aeq_mat = np.array(deepcopy(mat1), np.float64) + t = perf_counter() + ipf_core(aeq_mat, target_prod, target_atra, max_iterations=iterations, tolerance=-5, cores=cores, warn=False) + aeqt = perf_counter() - t + + aeq_data.append([zones, cores, aeqt]) + + +.. parsed-literal:: + + Zone size: 500: 100%|██████████| 6/6 [00:00<00:00, 12.51it/s] + Zone size: 750: 100%|██████████| 6/6 [00:00<00:00, 9.19it/s] + Zone size: 1000: 100%|██████████| 6/6 [00:00<00:00, 6.50it/s] + Zone size: 1500: 100%|██████████| 6/6 [00:01<00:00, 3.07it/s] + Zone size: 2500: 100%|██████████| 6/6 [00:05<00:00, 1.17it/s] + Zone size: 5000: 100%|██████████| 6/6 [00:18<00:00, 3.14s/it] + Zone size: 7500: 100%|██████████| 6/6 [00:42<00:00, 7.10s/it] + Zone size: 10000: 100%|██████████| 6/6 [01:15<00:00, 12.51s/it] + Zone size: 15000: 100%|██████████| 6/6 [02:47<00:00, 27.93s/it] + + +.. code:: ipython2 + + aeq_df = pd.DataFrame(aeq_data, columns=["zones", "cores", "time"]) + aeq_df = aeq_df[aeq_df.zones>1000] + aeq_df = aeq_df.groupby(["zones", "cores"]).mean().reset_index() + aeq_df = aeq_df.pivot_table(index="zones", columns="cores", values="time") + for cores in cores_to_use[::-1]: + aeq_df.loc[:, cores] /= aeq_df[1] + aeq_df.transpose().plot() + aeq_df + + + + +.. raw:: html + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
cores12481632
zones
15001.00.5591140.4081110.2502970.2163080.255603
25001.00.5264900.3594110.2554470.2039430.232535
50001.00.5176010.3110590.2160650.1957200.187223
75001.00.5280700.3033040.2210320.2008860.189343
100001.00.5209010.3014530.2096690.1814500.174836
150001.00.5207690.3074020.2088820.1801390.176024
+
+ + + + +.. image:: IPF_benchmark_files/IPF_benchmark_16_1.png + diff --git a/docs/source/validation_benchmarking/ipf_performance.rst b/docs/source/useful_information/validation_benchmarking/ipf_performance.rst similarity index 92% rename from docs/source/validation_benchmarking/ipf_performance.rst rename to docs/source/useful_information/validation_benchmarking/ipf_performance.rst index 12fe4cfec..41a483574 100644 --- a/docs/source/validation_benchmarking/ipf_performance.rst +++ b/docs/source/useful_information/validation_benchmarking/ipf_performance.rst @@ -22,7 +22,7 @@ than the benchmark python code, while applying IPF to a 32-bit NumPy array (``np It's worth mentioning that the user can set up a threshold for AequilibraE's IPF function, as well as use more than one core to speed up the fitting process. -.. image:: ../images/ipf_runtime_aequilibrae_vs_benchmark.png +.. image:: ../../images/ipf_runtime_aequilibrae_vs_benchmark.png :align: center :alt: AequilibraE's IPF runtime @@ -30,7 +30,7 @@ As IPF is an embarrassingly-parallel workload, it is more relevant to look at th AequilibraE implementations, starting by comparing the implementation performance for inputs in 32 vs 64 bits using 32 threads. -.. image:: ../images/ipf_runtime_32vs64bits.png +.. image:: ../../images/ipf_runtime_32vs64bits.png :align: center :alt: AequilibraE's IPF runtime 32 vs 64 bits @@ -43,11 +43,11 @@ that the 32-bit version scales significantly better than its 64-bit counterpart, gains up to 16 threads, while the latter stops showing much improvement beyond 8 threads, likely due to limitations on cache size. -.. image:: ../images/ipf_runtime_vs_num_cores.png +.. image:: ../../images/ipf_runtime_vs_num_cores.png :align: left :alt: number of cores used in IPF for 64 bit matrices -.. image:: ../images/ipf_runtime_vs_num_cores32bits.png +.. image:: ../../images/ipf_runtime_vs_num_cores32bits.png :align: right :alt: number of cores used in IPF for 32 bit matrices diff --git a/docs/source/validation_benchmarking/traffic_assignment.rst b/docs/source/useful_information/validation_benchmarking/traffic_assignment.rst similarity index 88% rename from docs/source/validation_benchmarking/traffic_assignment.rst rename to docs/source/useful_information/validation_benchmarking/traffic_assignment.rst index fea3099a4..ed108f763 100644 --- a/docs/source/validation_benchmarking/traffic_assignment.rst +++ b/docs/source/useful_information/validation_benchmarking/traffic_assignment.rst @@ -36,28 +36,28 @@ issues with the reference results and welcome further investigations. .. tab:: biconjugate Frank-Wolfe - .. image:: ../images/assig_validation/ChicagoRegional_bfw-1000_iter.png + .. image:: ../../images/assig_validation/ChicagoRegional_bfw-1000_iter.png :align: center :width: 590 :alt: Chicago Biconjugate Frank-Wolfe 1000 iterations .. tab:: Conjugate Frank-Wolfe - .. image:: ../images/assig_validation/ChicagoRegional_cfw-1000_iter.png + .. image:: ../../images/assig_validation/ChicagoRegional_cfw-1000_iter.png :align: center :width: 590 :alt: Chicago Conjugate Frank-Wolfe 1000 iterations .. tab:: Frank-Wolfe - .. image:: ../images/assig_validation/ChicagoRegional_fw-1000_iter.png + .. image:: ../../images/assig_validation/ChicagoRegional_fw-1000_iter.png :align: center :width: 590 :alt: Chicago Frank-Wolfe 1000 iterations .. tab:: MSA - .. image:: ../images/assig_validation/ChicagoRegional_msa-1000_iter.png + .. image:: ../../images/assig_validation/ChicagoRegional_msa-1000_iter.png :align: center :width: 590 :alt: Chicago MSA 1000 iterations @@ -74,28 +74,28 @@ issues with the reference results and welcome further investigations. .. tab:: biconjugate Frank-Wolfe - .. image:: ../images/assig_validation/Barcelona_bfw-1000_iter.png + .. image:: ../../images/assig_validation/Barcelona_bfw-1000_iter.png :align: center :width: 590 :alt: Barcelona Biconjugate Frank-Wolfe 1000 iterations .. tab:: Conjugate Frank-Wolfe - .. image:: ../images/assig_validation/Barcelona_cfw-1000_iter.png + .. image:: ../../images/assig_validation/Barcelona_cfw-1000_iter.png :align: center :width: 590 :alt: Barcelona Conjugate Frank-Wolfe 1000 iterations .. tab:: Frank-Wolfe - .. image:: ../images/assig_validation/Barcelona_fw-1000_iter.png + .. image:: ../../images/assig_validation/Barcelona_fw-1000_iter.png :align: center :width: 590 :alt: Barcelona Frank-Wolfe 1000 iterations .. tab:: MSA - .. image:: ../images/assig_validation/Barcelona_msa-1000_iter.png + .. image:: ../../images/assig_validation/Barcelona_msa-1000_iter.png :align: center :width: 590 :alt: Barcelona MSA 1000 iterations @@ -112,28 +112,28 @@ issues with the reference results and welcome further investigations. .. tab:: biconjugate Frank-Wolfe - .. image:: ../images/assig_validation/Winnipeg_bfw-1000_iter.png + .. image:: ../../images/assig_validation/Winnipeg_bfw-1000_iter.png :align: center :width: 590 :alt: Winnipeg Biconjugate Frank-Wolfe 1000 iterations .. tab:: Conjugate Frank-Wolfe - .. image:: ../images/assig_validation/Winnipeg_cfw-1000_iter.png + .. image:: ../../images/assig_validation/Winnipeg_cfw-1000_iter.png :align: center :width: 590 :alt: Winnipeg Conjugate Frank-Wolfe 1000 iterations .. tab:: Frank-Wolfe - .. image:: ../images/assig_validation/Winnipeg_fw-1000_iter.png + .. image:: ../../images/assig_validation/Winnipeg_fw-1000_iter.png :align: center :width: 590 :alt: Winnipeg Frank-Wolfe 1000 iterations .. tab:: MSA - .. image:: ../images/assig_validation/Winnipeg_msa-1000_iter.png + .. image:: ../../images/assig_validation/Winnipeg_msa-1000_iter.png :align: center :width: 590 :alt: Winnipeg MSA 1000 iterations @@ -150,28 +150,28 @@ issues with the reference results and welcome further investigations. .. tab:: biconjugate Frank-Wolfe - .. image:: ../images/assig_validation/Anaheim_bfw-1000_iter.png + .. image:: ../../images/assig_validation/Anaheim_bfw-1000_iter.png :align: center :width: 590 :alt: Anaheim Biconjugate Frank-Wolfe 1000 iterations .. tab:: Conjugate Frank-Wolfe - .. image:: ../images/assig_validation/Anaheim_cfw-1000_iter.png + .. image:: ../../images/assig_validation/Anaheim_cfw-1000_iter.png :align: center :width: 590 :alt: Anaheim Conjugate Frank-Wolfe 1000 iterations .. tab:: Frank-Wolfe - .. image:: ../images/assig_validation/Anaheim_fw-1000_iter.png + .. image:: ../../images/assig_validation/Anaheim_fw-1000_iter.png :align: center :width: 590 :alt: Anaheim Frank-Wolfe 1000 iterations .. tab:: MSA - .. image:: ../images/assig_validation/Anaheim_msa-1000_iter.png + .. image:: ../../images/assig_validation/Anaheim_msa-1000_iter.png :align: center :width: 590 :alt: Anaheim MSA 1000 iterations @@ -188,28 +188,28 @@ issues with the reference results and welcome further investigations. .. tab:: biconjugate Frank-Wolfe - .. image:: ../images/assig_validation/SiouxFalls_bfw-1000_iter.png + .. image:: ../../images/assig_validation/SiouxFalls_bfw-1000_iter.png :align: center :width: 590 :alt: Sioux Falls Biconjugate Frank-Wolfe 1000 iterations .. tab:: Conjugate Frank-Wolfe - .. image:: ../images/assig_validation/SiouxFalls_cfw-1000_iter.png + .. image:: ../../images/assig_validation/SiouxFalls_cfw-1000_iter.png :align: center :width: 590 :alt: Sioux Falls Conjugate Frank-Wolfe 1000 iterations .. tab:: Frank-Wolfe - .. image:: ../images/assig_validation/SiouxFalls_fw-1000_iter.png + .. image:: ../../images/assig_validation/SiouxFalls_fw-1000_iter.png :align: center :width: 590 :alt: Sioux Falls Frank-Wolfe 1000 iterations .. tab:: MSA - .. image:: ../images/assig_validation/SiouxFalls_msa-1000_iter.png + .. image:: ../../images/assig_validation/SiouxFalls_msa-1000_iter.png :align: center :width: 590 :alt: Sioux Falls MSA 1000 iterations @@ -227,35 +227,35 @@ as that instance has a comparable size to real-world models. .. tab:: Chicago - .. image:: ../images/assig_validation/convergence_comparison_ChicagoRegional.png + .. image:: ../../images/assig_validation/convergence_comparison_ChicagoRegional.png :align: center :width: 590 :alt: Algorithm convergence comparison .. tab:: Barcelona - .. image:: ../images/assig_validation/convergence_comparison_Barcelona.png + .. image:: ../../images/assig_validation/convergence_comparison_Barcelona.png :align: center :width: 590 :alt: Algorithm convergence comparison .. tab:: Winnipeg - .. image:: ../images/assig_validation/convergence_comparison_Winnipeg.png + .. image:: ../../images/assig_validation/convergence_comparison_Winnipeg.png :align: center :width: 590 :alt: Algorithm convergence comparison .. tab:: Anaheim - .. image:: ../images/assig_validation/convergence_comparison_Anaheim.png + .. image:: ../../images/assig_validation/convergence_comparison_Anaheim.png :align: center :width: 590 :alt: Algorithm convergence comparison .. tab:: Sioux-Falls - .. image:: ../images/assig_validation/convergence_comparison_SiouxFalls.png + .. image:: ../../images/assig_validation/convergence_comparison_SiouxFalls.png :align: center :width: 590 :alt: Algorithm convergence comparison diff --git a/docs/source/validation.rst b/docs/source/useful_information/validation_benchmarking/validation.rst similarity index 81% rename from docs/source/validation.rst rename to docs/source/useful_information/validation_benchmarking/validation.rst index fc8b32fd8..03dfc9fa5 100644 --- a/docs/source/validation.rst +++ b/docs/source/useful_information/validation_benchmarking/validation.rst @@ -13,5 +13,5 @@ of Traffic Assignment, linked below. .. toctree:: :maxdepth: 1 - validation_benchmarking/traffic_assignment - validation_benchmarking/IPF_benchmark \ No newline at end of file + traffic_assignment + IPF_benchmark \ No newline at end of file diff --git a/docs/source/version_history.rst b/docs/source/useful_information/version_history.rst similarity index 100% rename from docs/source/version_history.rst rename to docs/source/useful_information/version_history.rst From e28dbc1aa0b406bedc3653315f1f4a9a7b8839e0 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Mon, 2 Sep 2024 10:27:02 -0300 Subject: [PATCH 12/57] . --- docs/source/api/api.rst | 20 +++++++++---------- .../modeling_with_aequilibrae.rst | 5 +---- .../modeling_with_aequilibrae/project.rst | 8 ++++++++ .../project_database.rst | 10 +--------- .../project_database/about.rst | 4 +++- docs/table_documentation.py | 5 ++++- 6 files changed, 27 insertions(+), 25 deletions(-) diff --git a/docs/source/api/api.rst b/docs/source/api/api.rst index c9d7cb891..b81187a3c 100644 --- a/docs/source/api/api.rst +++ b/docs/source/api/api.rst @@ -11,7 +11,7 @@ Project .. currentmodule:: aequilibrae.project .. autosummary:: :nosignatures: - :toctree: api/generated/ + :toctree: generated/ Project @@ -20,7 +20,7 @@ Project Components .. currentmodule:: aequilibrae.project .. autosummary:: :nosignatures: - :toctree: api/generated/ + :toctree: generated/ About FieldEditor @@ -34,7 +34,7 @@ Project Objects .. currentmodule:: aequilibrae.project .. autosummary:: :nosignatures: - :toctree: api/generated/ + :toctree: generated/ Zone @@ -43,7 +43,7 @@ Network Data .. currentmodule:: aequilibrae.project.network .. autosummary:: :nosignatures: - :toctree: api/generated/ + :toctree: generated/ Modes LinkTypes @@ -56,7 +56,7 @@ Network Items .. currentmodule:: aequilibrae.project.network .. autosummary:: :nosignatures: - :toctree: api/generated/ + :toctree: generated/ Mode LinkType @@ -69,7 +69,7 @@ Parameters .. currentmodule:: aequilibrae .. autosummary:: :nosignatures: - :toctree: api/generated/ + :toctree: generated/ Parameters @@ -77,7 +77,7 @@ Distribution ------------ .. currentmodule:: aequilibrae.distribution .. autosummary:: - :toctree: api/generated/ + :toctree: generated/ Ipf GravityApplication @@ -89,7 +89,7 @@ Matrix .. currentmodule:: aequilibrae.matrix .. autosummary:: :nosignatures: - :toctree: api/generated/ + :toctree: generated/ AequilibraeData AequilibraeMatrix @@ -99,7 +99,7 @@ Paths .. currentmodule:: aequilibrae.paths .. autosummary:: :nosignatures: - :toctree: api/generated/ + :toctree: generated/ Graph TransitGraph @@ -122,7 +122,7 @@ Transit .. currentmodule:: aequilibrae.transit .. autosummary:: :nosignatures: - :toctree: api/generated/ + :toctree: generated/ Transit TransitGraphBuilder diff --git a/docs/source/modeling_with_aequilibrae/modeling_with_aequilibrae.rst b/docs/source/modeling_with_aequilibrae/modeling_with_aequilibrae.rst index bda33cdb2..cb77eed54 100644 --- a/docs/source/modeling_with_aequilibrae/modeling_with_aequilibrae.rst +++ b/docs/source/modeling_with_aequilibrae/modeling_with_aequilibrae.rst @@ -15,14 +15,11 @@ In this section you can find a deep dive into modeling with AequilibraE, from a start guide to a complete view into AequilibraE's data structure. .. toctree:: - :numbered: 1 + :numbered: 2 :maxdepth: 1 :caption: A guide to AequilibraE project - project_database - parameter_file - public_transport static_traffic_assignment transit_assignment route_choice diff --git a/docs/source/modeling_with_aequilibrae/project.rst b/docs/source/modeling_with_aequilibrae/project.rst index 720878b70..e4572f9d5 100644 --- a/docs/source/modeling_with_aequilibrae/project.rst +++ b/docs/source/modeling_with_aequilibrae/project.rst @@ -72,6 +72,14 @@ also created *on-the-fly* when the user imports a GTFS source into an Aequilibra model, but there is still no support for manually or programmatically adding routes to a route system as of yet. +.. toctree:: + :maxdepth: 1 + :caption: Project structure + + project_database + parameter_file + public_transport + Package components: A conceptual view ------------------------------------- diff --git a/docs/source/modeling_with_aequilibrae/project_database.rst b/docs/source/modeling_with_aequilibrae/project_database.rst index c33e89a37..cd3519ac7 100644 --- a/docs/source/modeling_with_aequilibrae/project_database.rst +++ b/docs/source/modeling_with_aequilibrae/project_database.rst @@ -1,6 +1,7 @@ Project database ---------------- + More details on the **project_database.sqlite** are discussed on a nearly *per-table* basis below, and we recommend understanding the role of each table before setting an AequilibraE model you intend to use in anger. @@ -16,12 +17,3 @@ an AequilibraE model you intend to use in anger. project_database/zones project_database/parameters_metadata project_database/results - - -A more technical view of the database structure, including the SQL queries used to -create each table and the indices used for each table are also available. - -.. toctree:: - :maxdepth: 1 - - project_database/data_model/datamodel.rst diff --git a/docs/source/modeling_with_aequilibrae/project_database/about.rst b/docs/source/modeling_with_aequilibrae/project_database/about.rst index d3770ce2a..3a5e9b6e2 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/about.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/about.rst @@ -27,4 +27,6 @@ An API for editing the contents of this database is available from the API docum .. seealso:: - :doc:`Editing About table <../../generated/aequilibrae.project.About>` \ No newline at end of file + :func:`aequilibrae.project.About` + +.. include:: data_model/about.rst \ No newline at end of file diff --git a/docs/table_documentation.py b/docs/table_documentation.py index 068a59aaa..904a3a09d 100644 --- a/docs/table_documentation.py +++ b/docs/table_documentation.py @@ -44,9 +44,12 @@ def create(self): descr = self.conn.execute(f"pragma table_info({table_name})").fetchall() # Title of the page - title = f'**{table_name.replace("_", " ")}** table structure' + title = "Table structure" txt = [title, "=" * len(title), ""] + intro = """A more technical view of the database structure, including the SQL queries used to create each table and the indices used are displayed below.\n""" + txt.append(intro) + docstrings = self.__get_docstrings(table_name) sql_code = self.__get_sql_code(table_name) From 70d710db71861f2c214719b8de56640e8ea7822c Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Mon, 2 Sep 2024 11:59:03 -0300 Subject: [PATCH 13/57] . --- aequilibrae/transit/transit.py | 12 +++-- .../modeling_with_aequilibrae.rst | 2 +- .../modeling_with_aequilibrae/project.rst | 53 ++++++++++--------- .../project_database.rst | 2 + .../project_database/datamodel.rst.template | 2 + .../project_database/link_types.rst | 20 +++---- .../project_database/matrices.rst | 4 +- .../project_database/modes.rst | 6 ++- .../project_database/network.rst | 13 +++-- .../project_database/parameters_metadata.rst | 2 + .../project_database/periods.rst | 11 ++++ .../project_database/results.rst | 2 + .../project_database/transit_graph.rst | 10 ++++ .../project_database/zones.rst | 4 +- .../public_transport.rst | 9 +--- .../route_choice.rst | 14 ++--- .../transit_database/datamodel.rst.template | 2 + docs/table_documentation.py | 8 +-- 18 files changed, 111 insertions(+), 65 deletions(-) create mode 100644 docs/source/modeling_with_aequilibrae/project_database/periods.rst create mode 100644 docs/source/modeling_with_aequilibrae/project_database/transit_graph.rst diff --git a/aequilibrae/transit/transit.py b/aequilibrae/transit/transit.py index 1d28eb412..dfd1ddeb6 100644 --- a/aequilibrae/transit/transit.py +++ b/aequilibrae/transit/transit.py @@ -118,16 +118,19 @@ def build_pt_preload(self, start: int, end: int, inclusion_cond: str = "start") """Builds a preload vector for the transit network over the specified time period :Arguments: - **start** (int): The start of the period for which to check pt schedules (seconds from midnight) + **start** (:obj:`int`): The start of the period for which to check pt schedules (seconds from midnight) - **end** (int): The end of the period for which to check pt schedules, (seconds from midnight) + **end** (:obj:`int`): The end of the period for which to check pt schedules, (seconds from midnight) - **inclusion_cond** (str): Specifies condition with which to include/exclude pt trips from the preload. + **inclusion_cond** (:obj:`str`): Specifies condition with which to include/exclude pt trips from the + preload. :Returns: - **preloads** (pd.DataFrame): A dataframe of preload from transit vehicles that can be directly used in an assignment + **preloads** (:obj:`pd.DataFrame`): A DataFrame of preload from transit vehicles that can be directly used + in an assignment Minimal example: + .. code-block:: python >>> from aequilibrae import Project @@ -139,6 +142,7 @@ def build_pt_preload(self, start: int, end: int, inclusion_cond: str = "start") >>> end = int(8.5 * 60 * 60) # 8.30 am >>> preload = proj.transit.build_pt_preload(start, end) + """ return pd.read_sql(self.__build_pt_preload_sql(start, end, inclusion_cond), self.pt_con) diff --git a/docs/source/modeling_with_aequilibrae/modeling_with_aequilibrae.rst b/docs/source/modeling_with_aequilibrae/modeling_with_aequilibrae.rst index cb77eed54..d729cf61c 100644 --- a/docs/source/modeling_with_aequilibrae/modeling_with_aequilibrae.rst +++ b/docs/source/modeling_with_aequilibrae/modeling_with_aequilibrae.rst @@ -15,7 +15,7 @@ In this section you can find a deep dive into modeling with AequilibraE, from a start guide to a complete view into AequilibraE's data structure. .. toctree:: - :numbered: 2 + :numbered: 3 :maxdepth: 1 :caption: A guide to AequilibraE diff --git a/docs/source/modeling_with_aequilibrae/project.rst b/docs/source/modeling_with_aequilibrae/project.rst index e4572f9d5..bfbc5964c 100644 --- a/docs/source/modeling_with_aequilibrae/project.rst +++ b/docs/source/modeling_with_aequilibrae/project.rst @@ -22,11 +22,37 @@ Impressive performance, portability, self containment and open-source character of these pieces of software, along with their large user base and wide industry support make them solid options to be AequilibraE's data backend. -Since working with Spatialite is not just a matter of a *pip install*, +Since working with Spatialite is not just a matter of a ``pip install``, please refer to :ref:`dependencies`. For QGIS users this is not a concern, while for Windows users this dependency is automatically handled under the hood, but the details are also discussed in the aforementioned dependencies section. +Package components: A conceptual view +------------------------------------- + +As all the components of an AequilibraE model based on open-source software and +open-data standards, modeling with AequilibraE is a little different from +modeling with commercial packages, as the user can read and manipulate model +components outside the software modeling environments (Python and QGIS). + +Thus, using/manipulating each one of an AequilibraE model components can be done +in different ways depending on the tool you use for such. + +It is then important to highlight that AequilibraE, as a software, is divided in +three very distinctive layers. The first, which is responsible for tables +consistent with each other (including links and nodes, modes and link_types), +are embedded in the data layer in the form of geo-spatial database triggers. The +second is the Python API, which provides all of AequilibraE's core algorithms +and data manipulation facilities. The third is the GUI implemented in QGIS, +which provides a user-friendly interface to access the model, visualize results +and run procedures. + +These software layers are *stacked* and depend on each other, which means that any +network editing done in SQLite, Python or QGIS will go through the SpatiaLite triggers, +while any procedure such as traffic assignment done in QGIS is nothing more than an +API call to the corresponding Python method. + + Project structure ----------------- @@ -79,28 +105,3 @@ to a route system as of yet. project_database parameter_file public_transport - -Package components: A conceptual view -------------------------------------- - -As all the components of an AequilibraE model based on open-source software and -open-data standards, modeling with AequilibraE is a little different from -modeling with commercial packages, as the user can read and manipulate model -components outside the software modeling environments (Python and QGIS). - -Thus, using/manipulating each one of an AequilibraE model components can be done -in different ways depending on the tool you use for such. - -It is then important to highlight that AequilibraE, as a software, is divided in -three very distinctive layers. The first, which is responsible for tables -consistent with each other (including links and nodes, modes and link_types), -are embedded in the data layer in the form of geo-spatial database triggers. The -second is the Python API, which provides all of AequilibraE's core algorithms -and data manipulation facilities. The third is the GUI implemented in QGIS, -which provides a user-friendly interface to access the model, visualize results -and run procedures. - -These software layers are *stacked* and depend on each other, which means that any -network editing done in SQLite, Python or QGIS will go through the SpatiaLite triggers, -while any procedure such as traffic assignment done in QGIS is nothing more than an -API call to the corresponding Python method. diff --git a/docs/source/modeling_with_aequilibrae/project_database.rst b/docs/source/modeling_with_aequilibrae/project_database.rst index cd3519ac7..a99d4a475 100644 --- a/docs/source/modeling_with_aequilibrae/project_database.rst +++ b/docs/source/modeling_with_aequilibrae/project_database.rst @@ -17,3 +17,5 @@ an AequilibraE model you intend to use in anger. project_database/zones project_database/parameters_metadata project_database/results + project_database/periods + project_database/transit_graph \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/project_database/datamodel.rst.template b/docs/source/modeling_with_aequilibrae/project_database/datamodel.rst.template index b3980a814..5cecdc360 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/datamodel.rst.template +++ b/docs/source/modeling_with_aequilibrae/project_database/datamodel.rst.template @@ -1,3 +1,5 @@ +:orphan: + .. _supply_data_model: SQL Data model diff --git a/docs/source/modeling_with_aequilibrae/project_database/link_types.rst b/docs/source/modeling_with_aequilibrae/project_database/link_types.rst index 39a400746..e6acb36db 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/link_types.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/link_types.rst @@ -27,7 +27,7 @@ removed from the model without breaking it. .. _adding_new_link_types: -Adding new link_types to a project +Adding new link types to a project ---------------------------------- Adding link types to a project can be done through the Python API or directly into @@ -61,8 +61,8 @@ physical link type and one virtual link type present in the model. .. _change_link_type_for_link: -Changing the *link_type* for a certain link -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Changing the link type for a certain link +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Whenever we change the 'link_type' associated to a link, we need to check whether that link type exists in the links_table. @@ -83,8 +83,8 @@ case, but it requires an specific trigger on the **creation** of the link. .. _editing_lt_on_lt_table: -Editing a *link_type* in the *link_types* table -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Editing a link type in the *link_types* table +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Whenever we want to edit a 'link_type' in the 'link_types' table, we need to check for two conditions: @@ -101,8 +101,8 @@ construction of the 'link_types' table by using the keys **UNIQUE** and .. _adding_new_ltype: -Adding a new *link_type* to the *link_types* table -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Adding a new link type to the *link_types* table +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In this case, only the first behaviour mentioned above on :ref:`editing_lt_on_lt_table` applies, the verification that the 'link_type_id' is @@ -110,8 +110,8 @@ exactly one character long. Therefore only one new trigger is required. .. _deleting_ltype: -Removing a *link_type* from the *link_types* table -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Removing a link type from the *link_types* table +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In counterpoint, only the second behaviour mentioned above on :ref:`editing_lt_on_lt_table` applies in this case, the verification that the old @@ -121,3 +121,5 @@ required. .. seealso:: :func:`aequilibrae.project.network.LinkTypes` + +.. include:: data_model/link_types.rst \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/project_database/matrices.rst b/docs/source/modeling_with_aequilibrae/project_database/matrices.rst index 291b15748..32ab3c893 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/matrices.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/matrices.rst @@ -22,4 +22,6 @@ types (AEM and OMX) without prejudice to functionality. :func:`aequilibrae.project.Matrices` - :func:`aequilibrae.matrix.AequilibraeMatrix` \ No newline at end of file + :func:`aequilibrae.matrix.AequilibraeMatrix` + +.. include:: data_model/matrices.rst \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/project_database/modes.rst b/docs/source/modeling_with_aequilibrae/project_database/modes.rst index 0b23eedfe..d5019fd59 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/modes.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/modes.rst @@ -7,7 +7,7 @@ The **modes** table exists to list all the modes available in the model's networ and its main role is to support the creation of graphs directly from the SQLite project. -.. note:: +.. important:: Modes must have a unique mode_id composed of a single letter, which is case-sensitive to a total of 52 possible modes in the model. @@ -97,4 +97,6 @@ required. .. seealso:: - :func:`aequilibrae.project.network.Modes` \ No newline at end of file + :func:`aequilibrae.project.network.Modes` + +.. include:: data_model/modes.rst \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/project_database/network.rst b/docs/source/modeling_with_aequilibrae/project_database/network.rst index f6e3b67fa..d9cc69b84 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/network.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/network.rst @@ -22,11 +22,18 @@ other changes to the layers or preventing the changes. **transportation network can be edited without specialized software that** **requires the editing to be done inside such software.** -.. note:: +.. important:: AequilibraE does not currently support turn penalties and/or bans. Their implementation requires a complete overahaul of the path-building code, so that is still a long-term goal, barred specific development efforts. -.. include:: network_import_and_export.rst +.. toctree:: + :maxdepth: 1 + :caption: Dive deep into network! -.. include:: network_geometry.rst + network_import_and_export.rst + network_geometry.rst + +.. include:: data_model/links.rst + +.. include:: data_model/nodes.rst diff --git a/docs/source/modeling_with_aequilibrae/project_database/parameters_metadata.rst b/docs/source/modeling_with_aequilibrae/project_database/parameters_metadata.rst index 8fb9327ff..653addabc 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/parameters_metadata.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/parameters_metadata.rst @@ -19,3 +19,5 @@ As a simple table, it looks as follows: .. seealso:: :func:`aequilibrae.project.FieldEditor` + +.. include:: data_model/attributes_documentation.rst \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/project_database/periods.rst b/docs/source/modeling_with_aequilibrae/project_database/periods.rst new file mode 100644 index 000000000..c29d42c7c --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/project_database/periods.rst @@ -0,0 +1,11 @@ +Periods table +============= + +Lorem ipsum + +.. seealso:: + + Lorem ipsum + +.. include:: data_model/periods.rst + diff --git a/docs/source/modeling_with_aequilibrae/project_database/results.rst b/docs/source/modeling_with_aequilibrae/project_database/results.rst index c57d49077..450bfc6d4 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/results.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/results.rst @@ -17,3 +17,5 @@ As a simple table, it looks as follows: .. image:: ../../images/results_table.png :align: center :alt: results table structure + +.. include:: data_model/results.rst \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/project_database/transit_graph.rst b/docs/source/modeling_with_aequilibrae/project_database/transit_graph.rst new file mode 100644 index 000000000..470da3ab6 --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/project_database/transit_graph.rst @@ -0,0 +1,10 @@ +Transit graph configuration +=========================== + +Lorem ipsum + +.. seealso:: + + Lorem ipsum + +.. include:: data_model/transit_graph_configs.rst \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/project_database/zones.rst b/docs/source/modeling_with_aequilibrae/project_database/zones.rst index d6d24e7aa..5849c7d01 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/zones.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/zones.rst @@ -17,4 +17,6 @@ You can check :ref:`this example ` to learn how to add zones to yo .. seealso:: - :func:`aequilibrae.project.Zone` \ No newline at end of file + :func:`aequilibrae.project.Zone` + +.. include:: data_model/zones.rst \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/public_transport.rst b/docs/source/modeling_with_aequilibrae/public_transport.rst index e42b681ba..a82d5a8ad 100644 --- a/docs/source/modeling_with_aequilibrae/public_transport.rst +++ b/docs/source/modeling_with_aequilibrae/public_transport.rst @@ -10,10 +10,5 @@ we recommend understanding the role of each table before setting an AequilibraE you intend to use. If you don't know much about GTFS, we strongly encourage you to take a look at the documentation provided by `Google `_. -A more technical view of the database structure, including the SQL queries used to create -each table and their indices are also available. - -.. toctree:: - :maxdepth: 1 - - transit_database/data_model/datamodel.rst \ No newline at end of file +The public transport database is created on the run when the ``Transit`` class is executed +for the first time. \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/route_choice.rst b/docs/source/modeling_with_aequilibrae/route_choice.rst index c6a52e0f5..ce9e3a08f 100644 --- a/docs/source/modeling_with_aequilibrae/route_choice.rst +++ b/docs/source/modeling_with_aequilibrae/route_choice.rst @@ -10,6 +10,13 @@ is consists of two steps: Choice set generation and the choice selection process AequilibraE is the first modeling package with full support for route choice, from the creation of choice sets through multiple algorithms to the assignment of trips to the network using the traditional Path-Size logit. +.. toctree:: + :maxdepth: 1 + :caption: Dive deep into route choice + + route_choice/route_choice_model.rst + route_choice/choice_set_generation.rst + Costs, utilities and signs -------------------------- @@ -82,10 +89,3 @@ be presented at the ATRF 2024 [1]_ [3]_ [4]_. .. seealso:: LOREM IPSUM - -.. toctree:: - :maxdepth: 1 - :caption: Route Choice - - route_choice/route_choice_model.rst - route_choice/choice_set_generation.rst diff --git a/docs/source/modeling_with_aequilibrae/transit_database/datamodel.rst.template b/docs/source/modeling_with_aequilibrae/transit_database/datamodel.rst.template index d732d4b0a..2ebd72b2c 100644 --- a/docs/source/modeling_with_aequilibrae/transit_database/datamodel.rst.template +++ b/docs/source/modeling_with_aequilibrae/transit_database/datamodel.rst.template @@ -1,3 +1,5 @@ +:orphan: + .. _transit_supply_data_model: SQL Data model diff --git a/docs/table_documentation.py b/docs/table_documentation.py index 904a3a09d..24791eb06 100644 --- a/docs/table_documentation.py +++ b/docs/table_documentation.py @@ -44,11 +44,11 @@ def create(self): descr = self.conn.execute(f"pragma table_info({table_name})").fetchall() # Title of the page - title = "Table structure" - txt = [title, "=" * len(title), ""] + title = f'*{table_name}* table structure' + txt = [title, "-" * len(title), ""] - intro = """A more technical view of the database structure, including the SQL queries used to create each table and the indices used are displayed below.\n""" - txt.append(intro) + # intro = """A more technical view of the database structure, including the SQL queries used to create each table and the indices used are displayed below.\n""" + # txt.append(intro) docstrings = self.__get_docstrings(table_name) sql_code = self.__get_sql_code(table_name) From abbbb64a1f25dbceb9797a35b860e1bdc22f340b Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Mon, 2 Sep 2024 14:40:03 -0300 Subject: [PATCH 14/57] . --- .github/workflows/documentation.yml | 2 +- aequilibrae/transit/__init__.py | 1 + aequilibrae/transit/transit.py | 3 --- docs/source/conf.py | 14 ++++++++------ .../validation_benchmarking/IPF_benchmark.rst | 8 ++++---- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index f14e19fc4..89ba3f90a 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -49,7 +49,7 @@ jobs: - name: Build documentation run: | - jupyter nbconvert --to rst docs/source/validation_benchmarking/IPF_benchmark.ipynb + jupyter nbconvert --to rst docs/source/useful_information/validation_benchmarking/IPF_benchmark.ipynb sphinx-build -M latexpdf docs/source docs/source/_static sphinx-build -b html docs/source docs/build python3 -m zipfile -c AequilibraE.zip docs/build diff --git a/aequilibrae/transit/__init__.py b/aequilibrae/transit/__init__.py index 2211cce04..6325bd09a 100644 --- a/aequilibrae/transit/__init__.py +++ b/aequilibrae/transit/__init__.py @@ -1,2 +1,3 @@ from .transit import Transit from .transit_graph_builder import TransitGraphBuilder +from .lib_gtfs import GTFSRouteSystemBuilder diff --git a/aequilibrae/transit/transit.py b/aequilibrae/transit/transit.py index dfd1ddeb6..0053239d9 100644 --- a/aequilibrae/transit/transit.py +++ b/aequilibrae/transit/transit.py @@ -129,8 +129,6 @@ def build_pt_preload(self, start: int, end: int, inclusion_cond: str = "start") **preloads** (:obj:`pd.DataFrame`): A DataFrame of preload from transit vehicles that can be directly used in an assignment - Minimal example: - .. code-block:: python >>> from aequilibrae import Project @@ -142,7 +140,6 @@ def build_pt_preload(self, start: int, end: int, inclusion_cond: str = "start") >>> end = int(8.5 * 60 * 60) # 8.30 am >>> preload = proj.transit.build_pt_preload(start, end) - """ return pd.read_sql(self.__build_pt_preload_sql(start, end, inclusion_cond), self.pt_con) diff --git a/docs/source/conf.py b/docs/source/conf.py index dfeee9176..241156326 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -57,6 +57,7 @@ extensions = [ "sphinx.ext.autodoc", "sphinx.ext.napoleon", + "sphinx.ext.mathjax", "sphinx.ext.viewcode", "sphinx.ext.autosummary", "sphinx_gallery.gen_gallery", @@ -180,17 +181,18 @@ autodoc_default_options = { "members": True, - "inherited-members": True, - "member-order": "bysource", - "special-members": False, - "private-members": False, - "undoc-members": True, + # "inherited-members": True, + # "member-order": "bysource", + # "special-members": False, + # "private-members": False, + # "undoc-members": True, # "exclude-members": "__weakref__", + 'autosummary': True } # autodoc_member_order = "groupwise" -autoclass_content = "class" # classes should include both the class' and the __init__ method's docstring +autoclass_content = "init" # classes should include both the class' and the __init__ method's docstring autosummary_generate = True diff --git a/docs/source/useful_information/validation_benchmarking/IPF_benchmark.rst b/docs/source/useful_information/validation_benchmarking/IPF_benchmark.rst index b2bf284e4..a759eca18 100644 --- a/docs/source/useful_information/validation_benchmarking/IPF_benchmark.rst +++ b/docs/source/useful_information/validation_benchmarking/IPF_benchmark.rst @@ -27,7 +27,7 @@ mentioning that the user can set up a threshold for AequilibraE’s IPF function, as well as use more than one core to speed up the fitting process. -.. figure:: ../../images/ipf_runtime_aequilibrae_vs_benchmark.png +.. figure:: ../images/ipf_runtime_aequilibrae_vs_benchmark.png :alt: AequilibraE’s IPF runtime AequilibraE’s IPF runtime @@ -37,7 +37,7 @@ look at the performance of the AequilibraE implementations, starting by comparing the implementation performance for inputs in 32 vs 64 bits using 32 threads. -.. figure:: ../../images/ipf_runtime_32vs64bits.png +.. figure:: ../images/ipf_runtime_32vs64bits.png :alt: AequilibraE’s IPF runtime 32 vs 64 bits AequilibraE’s IPF runtime 32 vs 64 bits @@ -54,12 +54,12 @@ better than its 64-bit counterpart, showing significant performance gains up to 16 threads, while the latter stops showing much improvement beyond 8 threads, likely due to limitations on cache size. -.. figure:: ../../images/ipf_runtime_vs_num_cores.png +.. figure:: ../images/ipf_runtime_vs_num_cores.png :alt: number of cores used in IPF for 64 bit matrices number of cores used in IPF for 64 bit matrices -.. figure:: ../../images/ipf_runtime_vs_num_cores32bits.png +.. figure:: ../images/ipf_runtime_vs_num_cores32bits.png :alt: number of cores used in IPF for 32 bit matrices number of cores used in IPF for 32 bit matrices From 3c304c8a4c65140a1a83f4a8d468ebbf64dff3dd Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Mon, 2 Sep 2024 16:13:41 -0300 Subject: [PATCH 15/57] rename folders --- aequilibrae/transit/transit.py | 4 +- docs/source/api/api.rst | 92 +++++++------- docs/source/conf.py | 21 +--- docs/source/index.rst | 4 +- .../index.rst} | 9 +- .../parameter_file.rst | 14 +-- .../project_database/about.rst | 0 .../attributes_documentation.rst} | 0 .../project_database/data_model/about.rst | 57 +++++++++ .../data_model/attributes_documentation.rst | 46 +++++++ .../project_database/data_model/datamodel.rst | 42 +++++++ .../data_model/link_types.rst | 61 ++++++++++ .../project_database/data_model/links.rst | 108 +++++++++++++++++ .../project_database/data_model/matrices.rst | 56 +++++++++ .../project_database/data_model/modes.rst | 64 ++++++++++ .../project_database/data_model/nodes.rst | 60 ++++++++++ .../project_database/data_model/periods.rst | 39 ++++++ .../project_database/data_model/results.rst | 50 ++++++++ .../data_model/transit_graph_configs.rst | 29 +++++ .../project_database/data_model/zones.rst | 46 +++++++ .../project_database/datamodel.rst.template | 0 .../project_database/index.rst | 21 ++++ .../project_database/link_types.rst | 0 .../project_database/matrices.rst | 0 .../project_database/modes.rst | 0 .../project_database/network.rst | 0 .../project_database/network_geometry.rst | 0 .../network_import_and_export.rst | 0 .../project_database/periods.rst | 0 .../project_database/transit_graph.rst | 0 .../project_database/zones.rst | 0 .../results_database.rst} | 8 +- .../transit_database/data_model/agencies.rst | 48 ++++++++ .../data_model/attributes_documentation.rst | 46 +++++++ .../transit_database/data_model/datamodel.rst | 50 ++++++++ .../data_model/fare_attributes.rst | 62 ++++++++++ .../data_model/fare_rules.rst | 51 ++++++++ .../data_model/fare_zones.rst | 37 ++++++ .../data_model/link_types.rst | 61 ++++++++++ .../transit_database/data_model/links.rst | 112 ++++++++++++++++++ .../transit_database/data_model/modes.rst | 64 ++++++++++ .../data_model/node_types.rst | 51 ++++++++ .../transit_database/data_model/nodes.rst | 87 ++++++++++++++ .../data_model/pattern_mapping.rst | 49 ++++++++ .../transit_database/data_model/results.rst | 50 ++++++++ .../data_model/route_links.rst | 58 +++++++++ .../transit_database/data_model/routes.rst | 75 ++++++++++++ .../data_model/stop_connectors.rst | 52 ++++++++ .../transit_database/data_model/stops.rst | 85 +++++++++++++ .../data_model/trigger_settings.rst | 29 +++++ .../transit_database/data_model/trips.rst | 42 +++++++ .../data_model/trips_schedule.rst | 42 +++++++ .../transit_database/datamodel.rst.template | 0 .../transit_database/index.rst | 66 +++++++++++ .../assignment_mechanics.rst | 0 .../index.rst} | 4 +- .../multi_class_equilibrium.rst | 0 .../hyperpath_routing.rst | 0 .../index.rst} | 4 +- .../transit_graph.rst | 0 .../choice_set_generation.rst | 0 .../index.rst} | 4 +- .../route_choice_model.rst | 0 ...odeling_with_aequilibrae.rst => index.rst} | 8 +- .../project_database.rst | 21 ---- .../public_transport.rst | 14 --- 66 files changed, 1972 insertions(+), 131 deletions(-) rename docs/source/modeling_with_aequilibrae/{project.rst => 1-aequilibrae_project/index.rst} (96%) rename docs/source/modeling_with_aequilibrae/{ => 1-aequilibrae_project}/parameter_file.rst (95%) rename docs/source/modeling_with_aequilibrae/{ => 1-aequilibrae_project}/project_database/about.rst (100%) rename docs/source/modeling_with_aequilibrae/{project_database/parameters_metadata.rst => 1-aequilibrae_project/project_database/attributes_documentation.rst} (100%) create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/about.rst create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/attributes_documentation.rst create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/datamodel.rst create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/link_types.rst create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/links.rst create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/matrices.rst create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/modes.rst create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/nodes.rst create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/periods.rst create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/results.rst create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/transit_graph_configs.rst create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/zones.rst rename docs/source/modeling_with_aequilibrae/{ => 1-aequilibrae_project}/project_database/datamodel.rst.template (100%) create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/index.rst rename docs/source/modeling_with_aequilibrae/{ => 1-aequilibrae_project}/project_database/link_types.rst (100%) rename docs/source/modeling_with_aequilibrae/{ => 1-aequilibrae_project}/project_database/matrices.rst (100%) rename docs/source/modeling_with_aequilibrae/{ => 1-aequilibrae_project}/project_database/modes.rst (100%) rename docs/source/modeling_with_aequilibrae/{ => 1-aequilibrae_project}/project_database/network.rst (100%) rename docs/source/modeling_with_aequilibrae/{ => 1-aequilibrae_project}/project_database/network_geometry.rst (100%) rename docs/source/modeling_with_aequilibrae/{ => 1-aequilibrae_project}/project_database/network_import_and_export.rst (100%) rename docs/source/modeling_with_aequilibrae/{ => 1-aequilibrae_project}/project_database/periods.rst (100%) rename docs/source/modeling_with_aequilibrae/{ => 1-aequilibrae_project}/project_database/transit_graph.rst (100%) rename docs/source/modeling_with_aequilibrae/{ => 1-aequilibrae_project}/project_database/zones.rst (100%) rename docs/source/modeling_with_aequilibrae/{project_database/results.rst => 1-aequilibrae_project/results_database.rst} (82%) create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/agencies.rst create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/attributes_documentation.rst create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/datamodel.rst create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/fare_attributes.rst create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/fare_rules.rst create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/fare_zones.rst create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/link_types.rst create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/links.rst create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/modes.rst create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/node_types.rst create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/nodes.rst create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/pattern_mapping.rst create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/results.rst create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/route_links.rst create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/routes.rst create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/stop_connectors.rst create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/stops.rst create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/trigger_settings.rst create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/trips.rst create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/trips_schedule.rst rename docs/source/modeling_with_aequilibrae/{ => 1-aequilibrae_project}/transit_database/datamodel.rst.template (100%) create mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/index.rst rename docs/source/modeling_with_aequilibrae/{static_traffic_assignment => 2-static_traffic_assignment}/assignment_mechanics.rst (100%) rename docs/source/modeling_with_aequilibrae/{static_traffic_assignment.rst => 2-static_traffic_assignment/index.rst} (90%) rename docs/source/modeling_with_aequilibrae/{static_traffic_assignment => 2-static_traffic_assignment}/multi_class_equilibrium.rst (100%) rename docs/source/modeling_with_aequilibrae/{transit_assignment => 3-transit_assignment}/hyperpath_routing.rst (100%) rename docs/source/modeling_with_aequilibrae/{transit_assignment.rst => 3-transit_assignment/index.rst} (69%) rename docs/source/modeling_with_aequilibrae/{transit_assignment => 3-transit_assignment}/transit_graph.rst (100%) rename docs/source/modeling_with_aequilibrae/{route_choice => 4-route_choice}/choice_set_generation.rst (100%) rename docs/source/modeling_with_aequilibrae/{route_choice.rst => 4-route_choice/index.rst} (97%) rename docs/source/modeling_with_aequilibrae/{route_choice => 4-route_choice}/route_choice_model.rst (100%) rename docs/source/modeling_with_aequilibrae/{modeling_with_aequilibrae.rst => index.rst} (86%) delete mode 100644 docs/source/modeling_with_aequilibrae/project_database.rst delete mode 100644 docs/source/modeling_with_aequilibrae/public_transport.rst diff --git a/aequilibrae/transit/transit.py b/aequilibrae/transit/transit.py index 0053239d9..cadce8f73 100644 --- a/aequilibrae/transit/transit.py +++ b/aequilibrae/transit/transit.py @@ -122,11 +122,11 @@ def build_pt_preload(self, start: int, end: int, inclusion_cond: str = "start") **end** (:obj:`int`): The end of the period for which to check pt schedules, (seconds from midnight) - **inclusion_cond** (:obj:`str`): Specifies condition with which to include/exclude pt trips from the + **inclusion_cond** (:obj:`str`): Specifies condition with which to include/exclude pt trips from the preload. :Returns: - **preloads** (:obj:`pd.DataFrame`): A DataFrame of preload from transit vehicles that can be directly used + **preloads** (:obj:`pd.DataFrame`): A DataFrame of preload from transit vehicles that can be directly used in an assignment .. code-block:: python diff --git a/docs/source/api/api.rst b/docs/source/api/api.rst index b81187a3c..69fc8da17 100644 --- a/docs/source/api/api.rst +++ b/docs/source/api/api.rst @@ -1,16 +1,12 @@ .. _api_reference: -============= API Reference ============= -.. automodule:: aequilibrae - Project ------- .. currentmodule:: aequilibrae.project .. autosummary:: - :nosignatures: :toctree: generated/ Project @@ -19,59 +15,54 @@ Project Components ~~~~~~~~~~~~~~~~~~ .. currentmodule:: aequilibrae.project .. autosummary:: - :nosignatures: :toctree: generated/ - About - FieldEditor - Log - Matrices - Network - Zoning + About + FieldEditor + Log + Matrices + Network + Zoning Project Objects ~~~~~~~~~~~~~~~ .. currentmodule:: aequilibrae.project .. autosummary:: - :nosignatures: :toctree: generated/ - Zone + Zone Network Data ------------ .. currentmodule:: aequilibrae.project.network .. autosummary:: - :nosignatures: :toctree: generated/ - Modes - LinkTypes - Links - Nodes - Periods + Modes + LinkTypes + Links + Nodes + Periods Network Items ------------- .. currentmodule:: aequilibrae.project.network .. autosummary:: - :nosignatures: :toctree: generated/ - Mode - LinkType - Link - Node - Period + Mode + LinkType + Link + Node + Period Parameters ---------- .. currentmodule:: aequilibrae .. autosummary:: - :nosignatures: :toctree: generated/ - Parameters + Parameters Distribution ------------ @@ -79,16 +70,15 @@ Distribution .. autosummary:: :toctree: generated/ - Ipf - GravityApplication - GravityCalibration - SyntheticGravityModel + Ipf + GravityCalibration + GravityApplication + SyntheticGravityModel Matrix ------ .. currentmodule:: aequilibrae.matrix .. autosummary:: - :nosignatures: :toctree: generated/ AequilibraeData @@ -98,32 +88,30 @@ Paths ----- .. currentmodule:: aequilibrae.paths .. autosummary:: - :nosignatures: :toctree: generated/ - Graph - TransitGraph - AssignmentResults - TransitAssignmentResults - SkimResults - PathResults - VDF - TrafficClass - TransitClass - TrafficAssignment - TransitAssignment - HyperpathGenerating - OptimalStrategies - RouteChoice - SubAreaAnalysis + Graph + TransitGraph + AssignmentResults + TransitAssignmentResults + SkimResults + PathResults + VDF + TrafficClass + TransitClass + TrafficAssignment + TransitAssignment + HyperpathGenerating + OptimalStrategies + RouteChoice + SubAreaAnalysis Transit ------- .. currentmodule:: aequilibrae.transit .. autosummary:: - :nosignatures: :toctree: generated/ - Transit - TransitGraphBuilder - + Transit + TransitGraphBuilder + .. GTFSRouteSystemBuilder \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py index 241156326..7b1b71c23 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -93,10 +93,7 @@ # Add any paths that contain templates here, relative to this directory. templates_path = ["_static"] -# The suffix(es) of source filenames.¶ -# You can specify multiple suffix as a list of string: -# -# source_suffix = ['.rst', '.md'] +# The suffix(es) of source filenames. source_suffix = ".rst" # The master toctree document. @@ -163,7 +160,7 @@ # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). -latex_documents = [(master_doc, "AequilibraE.tex", "AequilibraE Documentation", "Pedro Camargo", "manual")] +latex_documents = [(master_doc, "AequilibraE.tex", "AequilibraE Documentation", author, "manual")] latex_appendices = [ "useful_information/installation", @@ -181,18 +178,12 @@ autodoc_default_options = { "members": True, - # "inherited-members": True, - # "member-order": "bysource", - # "special-members": False, - # "private-members": False, - # "undoc-members": True, - # "exclude-members": "__weakref__", + "inherited-members": True, + "undoc-members": True, 'autosummary': True } -# autodoc_member_order = "groupwise" - -autoclass_content = "init" # classes should include both the class' and the __init__ method's docstring +autoclass_content = "class" # classes should include both the class' and the __init__ method's docstring autosummary_generate = True @@ -206,7 +197,7 @@ "AequilibraE Documentation", author, "AequilibraE", - "One line description of project.", + "Comprehensive Python package for transportation modeling", "Miscellaneous", ) ] diff --git a/docs/source/index.rst b/docs/source/index.rst index 734a5cd25..9c95d1cd1 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -29,7 +29,7 @@ resources not easily available from other open-source packages in the Python (Nu results. .. grid-item-card:: :material-outlined:`menu_book;1.5em` Modeling with AequilibraE - :link: modeling_with_aequilibrae/modeling_with_aequilibrae + :link: modeling_with_aequilibrae/index :link-type: any :text-align: center @@ -56,5 +56,5 @@ resources not easily available from other open-source packages in the Python (Nu :maxdepth: 1 _auto_examples/index - modeling_with_aequilibrae/modeling_with_aequilibrae + modeling_with_aequilibrae/index api/api diff --git a/docs/source/modeling_with_aequilibrae/project.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/index.rst similarity index 96% rename from docs/source/modeling_with_aequilibrae/project.rst rename to docs/source/modeling_with_aequilibrae/1-aequilibrae_project/index.rst index bfbc5964c..e275f8833 100644 --- a/docs/source/modeling_with_aequilibrae/project.rst +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/index.rst @@ -60,7 +60,7 @@ Since version 0.7, the AequilibraE project consists of a main folder, where a series of files and sub folders exist, and the current project organization is as follows: -.. image:: ../images/project_structure.png +.. image:: ../../images/project_structure.png :width: 700 :alt: AequilibraE project structure @@ -100,8 +100,9 @@ to a route system as of yet. .. toctree:: :maxdepth: 1 - :caption: Project structure + :caption: Dive deep into project structure! - project_database + project_database/index parameter_file - public_transport + results_database + public_transport_database/index diff --git a/docs/source/modeling_with_aequilibrae/parameter_file.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/parameter_file.rst similarity index 95% rename from docs/source/modeling_with_aequilibrae/parameter_file.rst rename to docs/source/modeling_with_aequilibrae/1-aequilibrae_project/parameter_file.rst index d7990b447..4978e7cbb 100644 --- a/docs/source/modeling_with_aequilibrae/parameter_file.rst +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/parameter_file.rst @@ -14,7 +14,7 @@ The assignment section of the parameter file is the smallest one, and it contains only the convergence criteria for assignment in terms of the maximum number of iterations and target Relative Gap. -.. image:: ../images/parameters_assignment_example.png +.. image:: ../../images/parameters_assignment_example.png :align: center :alt: Assignment example @@ -31,7 +31,7 @@ contains only the parameters for number of maximum iterations, convergence level and maximum trip length to be applied in Iterative Proportional Fitting and synthetic gravity models, as shown below. -.. image:: ../images/parameters_distribution_example.png +.. image:: ../../images/parameters_distribution_example.png :align: center :alt: Distribution example @@ -64,7 +64,7 @@ The data types available are those that exist within the `SQLite specification `_ . We recommend limiting yourself to the use of **integer**, **numeric** and **varchar**. -.. image:: ../images/parameters_links_example.png +.. image:: ../../images/parameters_links_example.png :width: 704 :align: center :alt: Link example @@ -89,7 +89,7 @@ forward/backward values tagged). For this reason, one can use the parameter 'osm to define what to do with numeric tag values that have not been tagged for both directions. the allowed values for this parameter are **copy** and **divide**, as shown below. -.. image:: ../images/parameters_links_osm_behaviour.png +.. image:: ../../images/parameters_links_osm_behaviour.png :align: center :alt: OSM behaviour examples @@ -129,7 +129,7 @@ GMNS The **GMNS** group of parameters has four specifications: **critical_dist**, **link**, **node**, and **use_definition**. -.. image:: ../images/parameter_yaml_files_gmns.png +.. image:: ../../images/parameter_yaml_files_gmns.png :align: center :alt: GMNS parameter group @@ -151,7 +151,7 @@ number of threads used in multi-threaded processes, logging and temp folders and whether we should be saving information to a log file at all, as exemplified below. -.. image:: ../images/parameters_system_example.png +.. image:: ../../images/parameters_system_example.png :align: center :alt: System example @@ -176,7 +176,7 @@ The OSM section of the parameter file is relevant only when one plans to download a substantial amount of data from an Overpass API, in which case it is recommended to deploy a local Overpass server. -.. image:: ../images/parameters_osm_example.png +.. image:: ../../images/parameters_osm_example.png :align: center :alt: OSM example diff --git a/docs/source/modeling_with_aequilibrae/project_database/about.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/about.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/project_database/about.rst rename to docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/about.rst diff --git a/docs/source/modeling_with_aequilibrae/project_database/parameters_metadata.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/attributes_documentation.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/project_database/parameters_metadata.rst rename to docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/attributes_documentation.rst diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/about.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/about.rst new file mode 100644 index 000000000..96b7f364e --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/about.rst @@ -0,0 +1,57 @@ +*about* table structure +----------------------- + +The *about* table holds information about the AequilibraE model +currently developed. + +The **infoname** field holds the name of information being added + +The **infovalue** field holds the information to add + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + infoname,TEXT,NO, + infovalue,TEXT,YES, + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + + CREATE TABLE if not exists about (infoname TEXT UNIQUE NOT NULL, + infovalue TEXT + ); + INSERT INTO 'about' (infoname) VALUES('model_name'); + + INSERT INTO 'about' (infoname) VALUES('region'); + + INSERT INTO 'about' (infoname) VALUES('description'); + + INSERT INTO 'about' (infoname) VALUES('author'); + + INSERT INTO 'about' (infoname) VALUES('year'); + + INSERT INTO 'about' (infoname) VALUES('scenario_description'); + + INSERT INTO 'about' (infoname) VALUES('model_version'); + + INSERT INTO 'about' (infoname) VALUES('project_id'); + + INSERT INTO 'about' (infoname) VALUES('aequilibrae_version'); + + INSERT INTO 'about' (infoname) VALUES('projection'); + + INSERT INTO 'about' (infoname) VALUES('driving_side'); + + INSERT INTO 'about' (infoname) VALUES('license'); + + INSERT INTO 'about' (infoname) VALUES('scenario_name'); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/attributes_documentation.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/attributes_documentation.rst new file mode 100644 index 000000000..8151c7b95 --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/attributes_documentation.rst @@ -0,0 +1,46 @@ +*attributes_documentation* table structure +------------------------------------------ + +The *attributes_documentation* table holds information about attributes +in the tables links, link_types, modes, nodes, and zones. + +By default, these attributes are all documented, but further +attribues can be added into the table. + +The **name_table** field holds the name of the table that has the attribute + +The **attribute** field holds the name of the attribute + +The **description** field holds the description of the attribute + +It is possible to have one attribute with the same name in two +different tables. However, one cannot have two attibutes with the +same name within the same table. + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + name_table,TEXT,NO, + attribute,TEXT,NO, + description,TEXT,YES, + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + + CREATE TABLE if not exists attributes_documentation (name_table TEXT NOT NULL, + attribute TEXT NOT NULL, + description TEXT, + UNIQUE (name_table, attribute) + ); + + CREATE INDEX idx_attributes ON attributes_documentation (name_table, attribute); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/datamodel.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/datamodel.rst new file mode 100644 index 000000000..77af94a69 --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/datamodel.rst @@ -0,0 +1,42 @@ +:orphan: + +.. _supply_data_model: + +SQL Data model +^^^^^^^^^^^^^^ + +The data model presented in this section pertains only to the structure of +AequilibraE's project_database and general information about the usefulness +of specific fields, especially on the interdependency between tables. + +Conventions +''''''''''' + +A few conventions have been adopted in the definition of the data model and some +are listed below: + +- Geometry field is always called **geometry** +- Projection is 4326 (WGS84) +- Tables are all in all lower case + + +.. Do not touch below this line unless you know EXACTLY what you are doing. +.. it will be automatically populated + +Project tables +'''''''''''''' + +.. toctree:: + :maxdepth: 1 + + about.rst + attributes_documentation.rst + link_types.rst + links.rst + matrices.rst + modes.rst + nodes.rst + periods.rst + results.rst + transit_graph_configs.rst + zones.rst diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/link_types.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/link_types.rst new file mode 100644 index 000000000..846d7c02a --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/link_types.rst @@ -0,0 +1,61 @@ +*link_types* table structure +---------------------------- + +The *link_types* table holds information about the available +link types in the network. + +The **link_type** field corresponds to the link type, and it is the +table's primary key + +The **link_type_id** field presents the identification of the link type + +The **description** field holds the description of the link type + +The **lanes** field presents the number or lanes for the link type + +The **lane_capacity** field presents the number of lanes for the link type + +The **speed** field holds information about the speed in the link type +Attributes follow + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + link_type*,VARCHAR,NO, + link_type_id,VARCHAR,NO, + description,VARCHAR,YES, + lanes,NUMERIC,YES, + lane_capacity,NUMERIC,YES, + speed,NUMERIC,YES, + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + + CREATE TABLE if not exists link_types (link_type VARCHAR UNIQUE NOT NULL PRIMARY KEY, + link_type_id VARCHAR UNIQUE NOT NULL, + description VARCHAR, + lanes NUMERIC, + lane_capacity NUMERIC, + speed NUMERIC + CHECK(LENGTH(link_type_id) == 1)); + + INSERT INTO 'link_types' (link_type, link_type_id, description, lanes, lane_capacity) VALUES('centroid_connector', 'z', 'VIRTUAL centroid connectors only', 10, 10000); + + INSERT INTO 'link_types' (link_type, link_type_id, description, lanes, lane_capacity) VALUES('default', 'y', 'Default general link type', 2, 900); + + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('link_types','link_type', 'Link type name. E.g. arterial, or connector'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('link_types','link_type_id', 'Single letter identifying the mode. E.g. a, for arterial'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('link_types','description', 'Description of the same. E.g. Arterials are streets like AequilibraE Avenue'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('link_types','lanes', 'Default number of lanes in each direction. E.g. 2'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('link_types','lane_capacity', 'Default vehicle capacity per lane. E.g. 900'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('link_types','speed', 'Free flow velocity in m/s'); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/links.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/links.rst new file mode 100644 index 000000000..0ba5d80b1 --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/links.rst @@ -0,0 +1,108 @@ +*links* table structure +----------------------- + +The links table holds all the links available in the aequilibrae network model +regardless of the modes allowed on it. + +All information on the fields a_node and b_node correspond to a entries in +the node_id field in the nodes table. They are automatically managed with +triggers as the user edits the network, but they are not protected by manual +editing, which would break the network if it were to happen. + +The **modes** field is a concatenation of all the ids (mode_id) of the models allowed +on each link, and map directly to the mode_id field in the **Modes** table. A mode +can only be added to a link if it exists in the **Modes** table. + +The **link_type** corresponds to the *link_type* field from the *link_types* table. +As it is the case for modes, a link_type can only be assigned to a link if it exists +in the **link_types** table. + +The fields **length**, **node_a** and **node_b** are automatically +updated by triggers based in the links' geometries and node positions. Link length +is always measured in **meters**. + +The table is indexed on **link_id** (its primary key), **node_a** and **node_b**. + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + ogc_fid*,INTEGER,YES, + link_id,INTEGER,NO, + a_node,INTEGER,YES, + b_node,INTEGER,YES, + direction,INTEGER,NO,0 + distance,NUMERIC,YES, + modes,TEXT,NO, + link_type,TEXT,YES, + name,TEXT,YES, + speed_ab,NUMERIC,YES, + speed_ba,NUMERIC,YES, + travel_time_ab,NUMERIC,YES, + travel_time_ba,NUMERIC,YES, + capacity_ab,NUMERIC,YES, + capacity_ba,NUMERIC,YES, + geometry,LINESTRING,NO,'' + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + + CREATE TABLE if not exists links (ogc_fid INTEGER PRIMARY KEY, + link_id INTEGER NOT NULL UNIQUE, + a_node INTEGER, + b_node INTEGER, + direction INTEGER NOT NULL DEFAULT 0, + distance NUMERIC, + modes TEXT NOT NULL, + link_type TEXT REFERENCES link_types(link_type) ON update RESTRICT ON delete RESTRICT, + 'name' TEXT, + speed_ab NUMERIC, + speed_ba NUMERIC, + travel_time_ab NUMERIC, + travel_time_ba NUMERIC, + capacity_ab NUMERIC, + capacity_ba NUMERIC + CHECK(TYPEOF(link_id) == 'integer') + CHECK(TYPEOF(a_node) == 'integer') + CHECK(TYPEOF(b_node) == 'integer') + CHECK(TYPEOF(direction) == 'integer') + CHECK(LENGTH(modes)>0) + CHECK(LENGTH(direction)==1)); + + select AddGeometryColumn( 'links', 'geometry', 4326, 'LINESTRING', 'XY', 1); + + CREATE UNIQUE INDEX idx_link ON links (link_id); + + SELECT CreateSpatialIndex( 'links' , 'geometry' ); + + CREATE INDEX idx_link_anode ON links (a_node); + + CREATE INDEX idx_link_bnode ON links (b_node); + + CREATE INDEX idx_link_modes ON links (modes); + + CREATE INDEX idx_link_link_type ON links (link_type); + + CREATE INDEX idx_links_a_node_b_node ON links (a_node, b_node); + + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','link_id', 'Unique link ID'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','a_node', 'origin node for the link'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','b_node', 'destination node for the link'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','direction', 'Flow direction allowed on the link'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','distance', 'length of the link'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','modes', 'modes allowed on the link'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','link_type', 'Link type'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','name', 'Name of the street/link'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','speed_*', 'Directional speeds (if allowed)'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','capacity_*', 'Directional link capacities (if allowed)'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','travel_time_*', 'Directional free-flow travel time (if allowed)'); + diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/matrices.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/matrices.rst new file mode 100644 index 000000000..620a46c66 --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/matrices.rst @@ -0,0 +1,56 @@ +*matrices* table structure +-------------------------- + +The *matrices* table holds infromation about all matrices that exists in the +project *matrix* folder. + +The **name** field presents the name of the table. + +The **file_name** field holds the file name. + +The **cores** field holds the information on the number of cores used. + +The **procedure** field holds the name the the procedure that generated +the result (e.g.: Traffic Assignment). + +The **procedure_id** field holds an unique alpha-numeric identifier for +this prodecure. + +The **timestamp** field holds the information when the procedure was executed. + +The **description** field holds the user-provided description of the result. + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + name*,TEXT,NO, + file_name,TEXT,NO, + cores,INTEGER,NO,1 + procedure,TEXT,YES, + procedure_id,TEXT,YES, + timestamp,DATETIME,YES,current_timestamp + description,TEXT,YES, + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + + create TABLE if not exists matrices (name TEXT NOT NULL PRIMARY KEY, + file_name TEXT NOT NULL UNIQUE, + cores INTEGER NOT NULL DEFAULT 1, + procedure TEXT, + procedure_id TEXT, + timestamp DATETIME DEFAULT current_timestamp, + description TEXT); + + + CREATE INDEX name_matrices ON matrices (name); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/modes.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/modes.rst new file mode 100644 index 000000000..939e88669 --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/modes.rst @@ -0,0 +1,64 @@ +*modes* table structure +----------------------- + +The *modes* table holds the information on all the modes available in +the model's network. + +The **mode_name** field contains the descriptive name of the field. + +The **mode_id** field contains a single letter that identifies the mode. + +The **description** field holds the description of the mode. + +The **pce** field holds information on Passenger-Car equivalent +for assignment. Defaults to **1.0**. + +The **vot** field holds information on Value-of-Time for traffic +assignment. Defaults to **0.0**. + +The **ppv** field holds information on average persons per vehicle. +Defaults to **1.0**. **ppv** can assume value 0 for non-travel uses. +Attributes follow + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + mode_name,VARCHAR,NO, + mode_id*,VARCHAR,NO, + description,VARCHAR,YES, + pce,NUMERIC,NO,1.0 + vot,NUMERIC,NO,0 + ppv,NUMERIC,NO,1.0 + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + + CREATE TABLE if not exists modes (mode_name VARCHAR UNIQUE NOT NULL, + mode_id VARCHAR UNIQUE NOT NULL PRIMARY KEY, + description VARCHAR, + pce NUMERIC NOT NULL DEFAULT 1.0, + vot NUMERIC NOT NULL DEFAULT 0, + ppv NUMERIC NOT NULL DEFAULT 1.0 + CHECK(LENGTH(mode_id)==1)); + + INSERT INTO 'modes' (mode_name, mode_id, description) VALUES('car', 'c', 'All motorized vehicles'); + INSERT INTO 'modes' (mode_name, mode_id, description) VALUES('transit', 't', 'Public transport vehicles'); + INSERT INTO 'modes' (mode_name, mode_id, description) VALUES('walk', 'w', 'Walking links'); + INSERT INTO 'modes' (mode_name, mode_id, description) VALUES('bicycle', 'b', 'Biking links'); + + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('modes','mode_name', 'The more descriptive name of the mode (e.g. Bicycle)'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('modes','mode_id', 'Single letter identifying the mode. E.g. b, for Bicycle'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('modes','description', 'Description of the same. E.g. Bicycles used to be human-powered two-wheeled vehicles'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('modes','pce', 'Passenger-Car equivalent for assignment'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('modes','vot', 'Value-of-Time for traffic assignment of class'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('modes','ppv', 'Average persons per vehicle. (0 for non-travel uses)'); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/nodes.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/nodes.rst new file mode 100644 index 000000000..59de23f95 --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/nodes.rst @@ -0,0 +1,60 @@ +*nodes* table structure +----------------------- + +The *nodes* table holds all the network nodes available in AequilibraE model. + +The **node_id** field is an identifier of the node. + +The **is_centroid** field holds information if the node is a centroid +of a network or not. Assumes values 0 or 1. Defaults to **0**. + +The **modes** field identifies all modes connected to the node. + +The **link_types** field identifies all link types connected +to the node. + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + ogc_fid*,INTEGER,YES, + node_id,INTEGER,NO, + is_centroid,INTEGER,NO,0 + modes,TEXT,YES, + link_types,TEXT,YES, + geometry,POINT,NO,'' + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + + CREATE TABLE if not exists nodes (ogc_fid INTEGER PRIMARY KEY, + node_id INTEGER UNIQUE NOT NULL, + is_centroid INTEGER NOT NULL DEFAULT 0, + modes TEXT, + link_types TEXT + CHECK(TYPEOF(node_id) == 'integer') + CHECK(TYPEOF(is_centroid) == 'integer') + CHECK(is_centroid>=0) + CHECK(is_centroid<=1)); + + SELECT AddGeometryColumn( 'nodes', 'geometry', 4326, 'POINT', 'XY', 1); + + SELECT CreateSpatialIndex( 'nodes' , 'geometry' ); + + CREATE INDEX idx_node ON nodes (node_id); + + CREATE INDEX idx_node_is_centroid ON nodes (is_centroid); + + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('nodes','node_id', 'Unique node ID'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('nodes','is_centroid', 'Flag identifying centroids'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('nodes','modes', 'Modes connected to the node'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('nodes','link_types', 'Link types connected to the node'); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/periods.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/periods.rst new file mode 100644 index 000000000..42cd0b1e6 --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/periods.rst @@ -0,0 +1,39 @@ +*periods* table structure +------------------------- + +The periods table holds the time periods and their period_id. Default entry with id 1 is the entire day. +Attributes follow + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + period_id,INTEGER,NO, + period_start,INTEGER,NO, + period_end,INTEGER,NO, + period_description,TEXT,YES, + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + CREATE TABLE if not exists periods (period_id INTEGER UNIQUE NOT NULL, + period_start INTEGER NOT NULL, + period_end INTEGER NOT NULL, + period_description TEXT + CHECK(TYPEOF(period_id) == 'integer') + CHECK(TYPEOF(period_start) == 'integer') + CHECK(TYPEOF(period_end) == 'integer')); + + INSERT INTO periods (period_id, period_start, period_end, period_description) VALUES(1, 0, 86400, 'Default time period, whole day'); + + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('periods','period_id', 'ID of the time period'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('periods','period_start', 'Start of the time period'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('periods','period_end', 'End of the time period'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('periods','period_description', 'Optional description of the time period'); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/results.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/results.rst new file mode 100644 index 000000000..504d5da51 --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/results.rst @@ -0,0 +1,50 @@ +*results* table structure +------------------------- + +The *results* table holds the metadata for results stored in +*results_database.sqlite*. + +The **table_name** field presents the actual name of the result +table in *results_database.sqlite*. + +The **procedure** field holds the name the the procedure that generated +the result (e.g.: Traffic Assignment). + +The **procedure_id** field holds an unique UUID identifier for this procedure, +which is created at runtime. + +The **procedure_report** field holds the output of the complete procedure report. + +The **timestamp** field holds the information when the procedure was executed. + +The **description** field holds the user-provided description of the result. + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + table_name*,TEXT,NO, + procedure,TEXT,NO, + procedure_id,TEXT,NO, + procedure_report,TEXT,NO, + timestamp,DATETIME,YES,current_timestamp + description,TEXT,YES, + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + + create TABLE if not exists results (table_name TEXT NOT NULL PRIMARY KEY, + procedure TEXT NOT NULL, + procedure_id TEXT NOT NULL, + procedure_report TEXT NOT NULL, + timestamp DATETIME DEFAULT current_timestamp, + description TEXT); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/transit_graph_configs.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/transit_graph_configs.rst new file mode 100644 index 000000000..44332333e --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/transit_graph_configs.rst @@ -0,0 +1,29 @@ +*transit_graph_configs* table structure +--------------------------------------- + +The *transit_graph_configs* table holds configuration parameters for a TransitGraph of a particular `period_id` +Attributes follow + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + period_id*,INTEGER,NO, + config,TEXT,YES, + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + CREATE TABLE if not exists transit_graph_configs (period_id INTEGER UNIQUE NOT NULL PRIMARY KEY REFERENCES periods(period_id), + config TEXT); + + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('transit_graph_configs','period_id', 'The period this config is associated with.'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('transit_graph_configs','mode_id', 'JSON string containing the configuration parameters.'); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/zones.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/zones.rst new file mode 100644 index 000000000..34d6d0393 --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/zones.rst @@ -0,0 +1,46 @@ +*zones* table structure +----------------------- + +The *zones* table holds information on the Traffic Analysis Zones (TAZs) +in AequilibraE's model. + +The **zone_id** field identifies the zone. + +The **area** field corresponds to the area of the zone in **km2**. +TAZs' area is automatically updated by triggers. + +The **name** fields allows one to identity the zone using a name +or any other description. + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + ogc_fid*,INTEGER,YES, + zone_id,INTEGER,NO, + area,NUMERIC,YES, + name,TEXT,YES, + geometry,MULTIPOLYGON,NO,'' + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + CREATE TABLE 'zones' (ogc_fid INTEGER PRIMARY KEY, + zone_id INTEGER UNIQUE NOT NULL, + area NUMERIC, + "name" TEXT); + + SELECT AddGeometryColumn( 'zones', 'geometry', 4326, 'MULTIPOLYGON', 'XY', 1); + CREATE UNIQUE INDEX idx_zone ON zones (zone_id); + SELECT CreateSpatialIndex( 'zones' , 'geometry' ); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('zones','zone_id', 'Unique node ID'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('zones','area', 'Area of the zone in km2'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('zones','name', 'Name of the zone, if any'); diff --git a/docs/source/modeling_with_aequilibrae/project_database/datamodel.rst.template b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/datamodel.rst.template similarity index 100% rename from docs/source/modeling_with_aequilibrae/project_database/datamodel.rst.template rename to docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/datamodel.rst.template diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/index.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/index.rst new file mode 100644 index 000000000..04ecc69e1 --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/index.rst @@ -0,0 +1,21 @@ + +Project database +---------------- + +More details on the **project_database.sqlite** are discussed on a nearly *per-table* +basis below, and we recommend understanding the role of each table before setting +an AequilibraE model you intend to use in anger. + +.. toctree:: + :maxdepth: 1 + + about + network + modes + link_types + matrices + zones + attributes_documentation + results + periods + transit_graph \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/project_database/link_types.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/link_types.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/project_database/link_types.rst rename to docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/link_types.rst diff --git a/docs/source/modeling_with_aequilibrae/project_database/matrices.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/matrices.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/project_database/matrices.rst rename to docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/matrices.rst diff --git a/docs/source/modeling_with_aequilibrae/project_database/modes.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/modes.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/project_database/modes.rst rename to docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/modes.rst diff --git a/docs/source/modeling_with_aequilibrae/project_database/network.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/network.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/project_database/network.rst rename to docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/network.rst diff --git a/docs/source/modeling_with_aequilibrae/project_database/network_geometry.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/network_geometry.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/project_database/network_geometry.rst rename to docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/network_geometry.rst diff --git a/docs/source/modeling_with_aequilibrae/project_database/network_import_and_export.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/network_import_and_export.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/project_database/network_import_and_export.rst rename to docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/network_import_and_export.rst diff --git a/docs/source/modeling_with_aequilibrae/project_database/periods.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/periods.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/project_database/periods.rst rename to docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/periods.rst diff --git a/docs/source/modeling_with_aequilibrae/project_database/transit_graph.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/transit_graph.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/project_database/transit_graph.rst rename to docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/transit_graph.rst diff --git a/docs/source/modeling_with_aequilibrae/project_database/zones.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/zones.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/project_database/zones.rst rename to docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/zones.rst diff --git a/docs/source/modeling_with_aequilibrae/project_database/results.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/results_database.rst similarity index 82% rename from docs/source/modeling_with_aequilibrae/project_database/results.rst rename to docs/source/modeling_with_aequilibrae/1-aequilibrae_project/results_database.rst index 450bfc6d4..55c6f0b9f 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/results.rst +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/results_database.rst @@ -1,7 +1,7 @@ .. _tables_results: -Results -======= +Results database +================ The **results** table exists to hold the metadata for the results stored in the **results_database.sqlite** in the same folder as the model database. In that, @@ -14,8 +14,8 @@ essentially clutter the **project_database.sqlite**. As a simple table, it looks as follows: -.. image:: ../../images/results_table.png +.. image:: ../images/results_table.png :align: center :alt: results table structure -.. include:: data_model/results.rst \ No newline at end of file +.. include:: project_database/data_model/results.rst \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/agencies.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/agencies.rst new file mode 100644 index 000000000..d36eff2a8 --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/agencies.rst @@ -0,0 +1,48 @@ +*agencies* table structure +-------------------------- + +The *agencies* table holds information about the Public Transport +agencies within the GTFS data. This table information comes from +GTFS file *agency.txt*. +You can check out more information `here `_. + +**agency_id** identifies the agency for the specified route + +**agency** contains the fuill name of the transit agency + +**feed_date** idicates the date for which the GTFS feed is being imported + +**service_date** indicates the date for the indicate route scheduling + +**description_field** provides useful description of a transit agency + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + agency_id*,INTEGER,NO, + agency,TEXT,NO, + feed_date,TEXT,YES, + service_date,TEXT,YES, + description,TEXT,YES, + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + create TABLE IF NOT EXISTS agencies ( + agency_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + agency TEXT NOT NULL, + feed_date TEXT, + service_date TEXT, + description TEXT + ); + + create UNIQUE INDEX IF NOT EXISTS transit_operators_id ON agencies (agency_id); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/attributes_documentation.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/attributes_documentation.rst new file mode 100644 index 000000000..8151c7b95 --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/attributes_documentation.rst @@ -0,0 +1,46 @@ +*attributes_documentation* table structure +------------------------------------------ + +The *attributes_documentation* table holds information about attributes +in the tables links, link_types, modes, nodes, and zones. + +By default, these attributes are all documented, but further +attribues can be added into the table. + +The **name_table** field holds the name of the table that has the attribute + +The **attribute** field holds the name of the attribute + +The **description** field holds the description of the attribute + +It is possible to have one attribute with the same name in two +different tables. However, one cannot have two attibutes with the +same name within the same table. + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + name_table,TEXT,NO, + attribute,TEXT,NO, + description,TEXT,YES, + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + + CREATE TABLE if not exists attributes_documentation (name_table TEXT NOT NULL, + attribute TEXT NOT NULL, + description TEXT, + UNIQUE (name_table, attribute) + ); + + CREATE INDEX idx_attributes ON attributes_documentation (name_table, attribute); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/datamodel.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/datamodel.rst new file mode 100644 index 000000000..0371315ec --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/datamodel.rst @@ -0,0 +1,50 @@ +:orphan: + +.. _transit_supply_data_model: + +SQL Data model +^^^^^^^^^^^^^^ + +The data model presented in this section pertains only to the structure of +AequilibraE's public_transport database and general information about the usefulness +of specific fields, especially on the interdependency between tables. + +Conventions +''''''''''' + +A few conventions have been adopted in the definition of the data model and some +are listed below: + +- Geometry field is always called **geometry** +- Projection is 4326 (WGS84) +- Tables are all in all lower case + + +.. Do not touch below this line unless you know EXACTLY what you are doing. +.. it will be automatically populated + +Project tables +'''''''''''''' + +.. toctree:: + :maxdepth: 1 + + agencies.rst + attributes_documentation.rst + fare_attributes.rst + fare_rules.rst + fare_zones.rst + link_types.rst + links.rst + modes.rst + node_types.rst + nodes.rst + pattern_mapping.rst + results.rst + route_links.rst + routes.rst + stop_connectors.rst + stops.rst + trigger_settings.rst + trips.rst + trips_schedule.rst diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/fare_attributes.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/fare_attributes.rst new file mode 100644 index 000000000..a32ca14e5 --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/fare_attributes.rst @@ -0,0 +1,62 @@ +*fare_attributes* table structure +--------------------------------- + +The *fare_attributes* table holds information about the fare values. +This table information comes from the GTFS file *fare_attributes.txt*. +Given that this file is optional in GTFS, it can be empty. +You can check out more information `here `_. + +**fare_id** identifies a fare class + +**fare** describes a fare class + +**agency_id** identifies a relevant agency for a fare. + +**price** especifies the fare price + +**currency_code** especifies the currency used to pay the fare + +**payment_method** indicates when the fare must be paid. + +**transfer** indicates the number of transfers permitted on the fare + +**transfer_duration** indicates the lenght of time in seconds before a +transfer expires. + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + fare_id*,INTEGER,NO, + fare,TEXT,NO, + agency_id,INTEGER,NO, + price,REAL,YES, + currency,TEXT,YES, + payment_method,INTEGER,YES, + transfer,INTEGER,YES, + transfer_duration,REAL,YES, + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + create TABLE IF NOT EXISTS fare_attributes ( + fare_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + fare TEXT NOT NULL, + agency_id INTEGER NOT NULL, + price REAL, + currency TEXT, + payment_method INTEGER, + transfer INTEGER, + transfer_duration REAL, + FOREIGN KEY(agency_id) REFERENCES agencies(agency_id) deferrable initially deferred + ); + + CREATE UNIQUE INDEX IF NOT EXISTS fare_transfer_uniqueness ON fare_attributes (fare_id, transfer); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/fare_rules.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/fare_rules.rst new file mode 100644 index 000000000..1074ba138 --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/fare_rules.rst @@ -0,0 +1,51 @@ +*fare_rules* table structure +---------------------------- + +The *fare_rules* table holds information about the fare values. +This table information comes from the GTFS file *fare_rules.txt*. +Given that this file is optional in GTFS, it can be empty. + +The **fare_id** identifies a fare class + +The **route_id** identifies a route associated with the fare class. + +The **origin** field identifies the origin zone + +The **destination** field identifies the destination zone + +The **contains** field identifies the zones that a rider will enter while using +a given fare class. + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + fare_id,INTEGER,NO, + route_id,INTEGER,YES, + origin,INTEGER,YES, + destination,INTEGER,YES, + contains,INTEGER,YES, + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + create TABLE IF NOT EXISTS fare_rules ( + fare_id INTEGER NOT NULL, + route_id INTEGER, + origin INTEGER, + destination INTEGER, + contains INTEGER, + FOREIGN KEY(fare_id) REFERENCES fare_attributes(fare_id) deferrable initially deferred, + FOREIGN KEY(route_id) REFERENCES routes(route_id) deferrable initially deferred, + FOREIGN KEY(destination) REFERENCES fare_zones(fare_zone_id) deferrable initially deferred, + FOREIGN KEY(origin) REFERENCES fare_zones(fare_zone_id) deferrable initially deferred, + FOREIGN KEY(contains) REFERENCES fare_zones(fare_zone_id) deferrable initially deferred + ); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/fare_zones.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/fare_zones.rst new file mode 100644 index 000000000..e6d7d32e4 --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/fare_zones.rst @@ -0,0 +1,37 @@ +*fare_zones* table structure +---------------------------- + +The *zones* tables holds information on the fare transit zones and +the TAZs they are in. + +**fare_zone_id** identifies the fare zone for a stop + +**transit_zone** identifies the TAZ for a fare zone + +**agency_id** identifies the agency fot the specified route + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + fare_zone_id*,INTEGER,YES, + transit_zone,TEXT,NO, + agency_id,INTEGER,NO, + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + CREATE TABLE IF NOT EXISTS fare_zones ( + fare_zone_id INTEGER PRIMARY KEY, + transit_zone TEXT NOT NULL, + agency_id INTEGER NOT NULL, + FOREIGN KEY(agency_id) REFERENCES agencies(agency_id) deferrable initially deferred + ); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/link_types.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/link_types.rst new file mode 100644 index 000000000..846d7c02a --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/link_types.rst @@ -0,0 +1,61 @@ +*link_types* table structure +---------------------------- + +The *link_types* table holds information about the available +link types in the network. + +The **link_type** field corresponds to the link type, and it is the +table's primary key + +The **link_type_id** field presents the identification of the link type + +The **description** field holds the description of the link type + +The **lanes** field presents the number or lanes for the link type + +The **lane_capacity** field presents the number of lanes for the link type + +The **speed** field holds information about the speed in the link type +Attributes follow + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + link_type*,VARCHAR,NO, + link_type_id,VARCHAR,NO, + description,VARCHAR,YES, + lanes,NUMERIC,YES, + lane_capacity,NUMERIC,YES, + speed,NUMERIC,YES, + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + + CREATE TABLE if not exists link_types (link_type VARCHAR UNIQUE NOT NULL PRIMARY KEY, + link_type_id VARCHAR UNIQUE NOT NULL, + description VARCHAR, + lanes NUMERIC, + lane_capacity NUMERIC, + speed NUMERIC + CHECK(LENGTH(link_type_id) == 1)); + + INSERT INTO 'link_types' (link_type, link_type_id, description, lanes, lane_capacity) VALUES('centroid_connector', 'z', 'VIRTUAL centroid connectors only', 10, 10000); + + INSERT INTO 'link_types' (link_type, link_type_id, description, lanes, lane_capacity) VALUES('default', 'y', 'Default general link type', 2, 900); + + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('link_types','link_type', 'Link type name. E.g. arterial, or connector'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('link_types','link_type_id', 'Single letter identifying the mode. E.g. a, for arterial'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('link_types','description', 'Description of the same. E.g. Arterials are streets like AequilibraE Avenue'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('link_types','lanes', 'Default number of lanes in each direction. E.g. 2'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('link_types','lane_capacity', 'Default vehicle capacity per lane. E.g. 900'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('link_types','speed', 'Free flow velocity in m/s'); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/links.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/links.rst new file mode 100644 index 000000000..ad6ca3dd4 --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/links.rst @@ -0,0 +1,112 @@ +*links* table structure +----------------------- + +The links table holds all the links available in the aequilibrae network model +regardless of the modes allowed on it. + +All information on the fields a_node and b_node correspond to a entries in +the node_id field in the nodes table. They are automatically managed with +triggers as the user edits the network, but they are not protected by manual +editing, which would break the network if it were to happen. + +The **modes** field is a concatenation of all the ids (mode_id) of the models allowed +on each link, and map directly to the mode_id field in the **Modes** table. A mode +can only be added to a link if it exists in the **Modes** table. + +The **link_type** corresponds to the *link_type* field from the *link_types* table. +As it is the case for modes, a link_type can only be assigned to a link if it exists +in the **link_types** table. + +The fields **length**, **node_a** and **node_b** are automatically +updated by triggers based in the links' geometries and node positions. Link length +is always measured in **meters**. + +The table is indexed on **link_id** (its primary key), **node_a** and **node_b**. + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + ogc_fid*,INTEGER,YES, + link_id,INTEGER,NO, + a_node,INTEGER,YES, + b_node,INTEGER,YES, + direction,INTEGER,NO,0 + distance,NUMERIC,YES, + modes,TEXT,NO, + link_type,TEXT,YES, + line_id,TEXT,YES, + stop_id,TEXT,YES, + line_seg_idx,INTEGER,YES, + trav_time,NUMERIC,NO, + freq,NUMERIC,NO, + o_line_id,TEXT,YES, + d_line_id,TEXT,YES, + transfer_id,TEXT,YES, + geometry,LINESTRING,NO,'' + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + + CREATE TABLE if not exists links (ogc_fid INTEGER PRIMARY KEY, + link_id INTEGER NOT NULL UNIQUE, + a_node INTEGER, + b_node INTEGER, + direction INTEGER NOT NULL DEFAULT 0, + distance NUMERIC, + modes TEXT NOT NULL, + link_type TEXT REFERENCES link_types(link_type) ON update RESTRICT ON delete RESTRICT, + line_id TEXT, + stop_id TEXT REFERENCES stops(stop) ON update RESTRICT ON delete RESTRICT, + line_seg_idx INTEGER, + trav_time NUMERIC NOT NULL, + freq NUMERIC NOT NULL, + o_line_id TEXT, + d_line_id TEXT, + transfer_id TEXT + CHECK(TYPEOF(link_id) == 'integer') + CHECK(TYPEOF(a_node) == 'integer') + CHECK(TYPEOF(b_node) == 'integer') + CHECK(TYPEOF(direction) == 'integer') + CHECK(LENGTH(modes)>0) + CHECK(LENGTH(direction)==1)); + + select AddGeometryColumn( 'links', 'geometry', 4326, 'LINESTRING', 'XY', 1); + + CREATE UNIQUE INDEX idx_link ON links (link_id); + + SELECT CreateSpatialIndex( 'links' , 'geometry' ); + + CREATE INDEX idx_link_anode ON links (a_node); + + CREATE INDEX idx_link_bnode ON links (b_node); + + CREATE INDEX idx_link_modes ON links (modes); + + CREATE INDEX idx_link_link_type ON links (link_type); + + CREATE INDEX idx_links_a_node_b_node ON links (a_node, b_node); + + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','link_id', 'Unique link ID'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','a_node', 'origin node for the link'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','b_node', 'destination node for the link'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','direction', 'Flow direction allowed on the link'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','distance', 'length of the link'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','modes', 'modes allowed on the link'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','link_type', 'Link type'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','line_id', 'ID of the line the link belongs to'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','stop_id', 'ID of the stop the link belongss to '); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','line_seg_idx', 'Line segment index'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','trav_time', 'Travel time'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','freq', 'Frequency of link traversal'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','*_line_id', 'Origin/Destination line ID for transfer links'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','transfer_id', 'Transfer link ID'); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/modes.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/modes.rst new file mode 100644 index 000000000..939e88669 --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/modes.rst @@ -0,0 +1,64 @@ +*modes* table structure +----------------------- + +The *modes* table holds the information on all the modes available in +the model's network. + +The **mode_name** field contains the descriptive name of the field. + +The **mode_id** field contains a single letter that identifies the mode. + +The **description** field holds the description of the mode. + +The **pce** field holds information on Passenger-Car equivalent +for assignment. Defaults to **1.0**. + +The **vot** field holds information on Value-of-Time for traffic +assignment. Defaults to **0.0**. + +The **ppv** field holds information on average persons per vehicle. +Defaults to **1.0**. **ppv** can assume value 0 for non-travel uses. +Attributes follow + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + mode_name,VARCHAR,NO, + mode_id*,VARCHAR,NO, + description,VARCHAR,YES, + pce,NUMERIC,NO,1.0 + vot,NUMERIC,NO,0 + ppv,NUMERIC,NO,1.0 + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + + CREATE TABLE if not exists modes (mode_name VARCHAR UNIQUE NOT NULL, + mode_id VARCHAR UNIQUE NOT NULL PRIMARY KEY, + description VARCHAR, + pce NUMERIC NOT NULL DEFAULT 1.0, + vot NUMERIC NOT NULL DEFAULT 0, + ppv NUMERIC NOT NULL DEFAULT 1.0 + CHECK(LENGTH(mode_id)==1)); + + INSERT INTO 'modes' (mode_name, mode_id, description) VALUES('car', 'c', 'All motorized vehicles'); + INSERT INTO 'modes' (mode_name, mode_id, description) VALUES('transit', 't', 'Public transport vehicles'); + INSERT INTO 'modes' (mode_name, mode_id, description) VALUES('walk', 'w', 'Walking links'); + INSERT INTO 'modes' (mode_name, mode_id, description) VALUES('bicycle', 'b', 'Biking links'); + + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('modes','mode_name', 'The more descriptive name of the mode (e.g. Bicycle)'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('modes','mode_id', 'Single letter identifying the mode. E.g. b, for Bicycle'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('modes','description', 'Description of the same. E.g. Bicycles used to be human-powered two-wheeled vehicles'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('modes','pce', 'Passenger-Car equivalent for assignment'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('modes','vot', 'Value-of-Time for traffic assignment of class'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('modes','ppv', 'Average persons per vehicle. (0 for non-travel uses)'); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/node_types.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/node_types.rst new file mode 100644 index 000000000..373e49faf --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/node_types.rst @@ -0,0 +1,51 @@ +*node_types* table structure +---------------------------- + +The *node_types* table holds information about the available +node types in the network. + +The **node_type** field corresponds to the node type, and it is the +table's primary key + +The **node_type_id** field presents the identification of the node type + +The **description** field holds the description of the node type + +Attributes follow + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + node_type*,VARCHAR,NO, + node_type_id,VARCHAR,NO, + description,VARCHAR,YES, + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + + CREATE TABLE if not exists node_types (node_type VARCHAR UNIQUE NOT NULL PRIMARY KEY, + node_type_id VARCHAR UNIQUE NOT NULL, + description VARCHAR); + + INSERT INTO 'node_types' (node_type, node_type_id, description) VALUES('default', 'y', 'Default general node type'); + INSERT INTO 'node_types' (node_type, node_type_id, description) VALUES('od', 'n', 'Origin/Desination node type'); + INSERT INTO 'node_types' (node_type, node_type_id, description) VALUES('origin', 'o', 'Origin node type'); + INSERT INTO 'node_types' (node_type, node_type_id, description) VALUES('destination', 'd', 'Desination node type'); + INSERT INTO 'node_types' (node_type, node_type_id, description) VALUES('stop', 's', 'Stop node type'); + INSERT INTO 'node_types' (node_type, node_type_id, description) VALUES('alighting', 'a', 'Alighting node type'); + INSERT INTO 'node_types' (node_type, node_type_id, description) VALUES('boarding', 'b', 'Boarding node type'); + INSERT INTO 'node_types' (node_type, node_type_id, description) VALUES('walking', 'w', 'Walking node type'); + + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('node_types','node_type', 'Node type name. E.g stop or boarding'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('node_types','node_type_id', 'Single letter identifying the mode. E.g. a, for alighting'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('node_types','description', 'Description of the same. E.g. Stop nodes connect ODs and walking nodes to boarding and alighting nodes via boarding and alighting links.'); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/nodes.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/nodes.rst new file mode 100644 index 000000000..6208dabbd --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/nodes.rst @@ -0,0 +1,87 @@ +*nodes* table structure +----------------------- + +The *nodes* table holds all the network nodes available in AequilibraE transit model. + +The **node_id** field is an identifier of the node. + +The **is_centroid** field holds information if the node is a centroid +of a network or not. Assumes values 0 or 1. Defaults to **0**. + +The **stop_id** field indicates which stop this node belongs too. This field +is TEXT as it might encode a street name or such. + +The **line_id** field indicates which line this node belongs too. This field +is TEXT as it might encode a street name or such. + +The **line_seg_idx** field indexes the segment of line **line_id**. Zero based. + +The **modes** field identifies all modes connected to the node. + +The **link_type** field identifies all link types connected to the node. + +The **node_type** field identifies the types of this node. + +The **taz_id** field is an identifier for the transit assignment zone this node +belongs to. + + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + ogc_fid*,INTEGER,YES, + node_id,INTEGER,NO, + is_centroid,INTEGER,NO,0 + stop_id,TEXT,YES, + line_id,TEXT,YES, + line_seg_idx,INTEGER,YES, + modes,TEXT,YES, + link_types,TEXT,YES, + node_type,TEXT,YES, + taz_id,TEXT,YES, + geometry,POINT,NO,'' + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + CREATE TABLE if not exists nodes (ogc_fid INTEGER PRIMARY KEY, + node_id INTEGER UNIQUE NOT NULL, + is_centroid INTEGER NOT NULL DEFAULT 0, + stop_id TEXT, + line_id TEXT, + line_seg_idx INTEGER, + modes TEXT, + link_types TEXT, + node_type TEXT, + taz_id TEXT + CHECK(TYPEOF(node_id) == 'integer') + CHECK(TYPEOF(is_centroid) == 'integer') + CHECK(is_centroid>=0) + CHECK(is_centroid<=1)); + + SELECT AddGeometryColumn( 'nodes', 'geometry', 4326, 'POINT', 'XY', 1); + + SELECT CreateSpatialIndex( 'nodes' , 'geometry' ); + + CREATE INDEX idx_node ON nodes (node_id); + + CREATE INDEX idx_node_is_centroid ON nodes (is_centroid); + + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('nodes','node_id', 'Unique node ID'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('nodes','is_centroid', 'Flag identifying centroids'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('nodes','stop_id', 'ID of the Stop this node belongs to'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('nodes','line_id', 'ID of the Line this node belongs to'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('nodes','line_seg_idx', 'Index of the line segement this node belongs to'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('nodes','modes', 'Modes connected to the node'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('nodes','link_types', 'Link types connected to the node'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('nodes','node_type', 'Node types of this node'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('nodes','taz_id', 'Transit assignemnt zone id'); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/pattern_mapping.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/pattern_mapping.rst new file mode 100644 index 000000000..d20cb366f --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/pattern_mapping.rst @@ -0,0 +1,49 @@ +*pattern_mapping* table structure +--------------------------------- + +The *pattern_mapping* table holds information on the stop pattern +for each route. + +**pattern_id** is an unique pattern for the route + +**seq** identifies the sequence of the stops for a trip + +**link** identifies the *link_id* in the links table that corresponds to the +pattern matching + +**dir** indicates the direction of travel for a trip + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + pattern_id*,INTEGER,NO, + seq,INTEGER,NO, + link,INTEGER,NO, + dir,INTEGER,NO, + geometry,LINESTRING,YES, + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + CREATE TABLE IF NOT EXISTS pattern_mapping ( + pattern_id INTEGER NOT NULL, + seq INTEGER NOT NULL, + link INTEGER NOT NULL, + dir INTEGER NOT NULL, + PRIMARY KEY(pattern_id, "seq"), + FOREIGN KEY(pattern_id) REFERENCES routes (pattern_id) deferrable initially deferred, + FOREIGN KEY(link) REFERENCES route_links (link) deferrable initially deferred + ); + + SELECT AddGeometryColumn( 'pattern_mapping', 'geometry', 4326, 'LINESTRING', 'XY'); + + SELECT CreateSpatialIndex( 'pattern_mapping' , 'geometry' ); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/results.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/results.rst new file mode 100644 index 000000000..504d5da51 --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/results.rst @@ -0,0 +1,50 @@ +*results* table structure +------------------------- + +The *results* table holds the metadata for results stored in +*results_database.sqlite*. + +The **table_name** field presents the actual name of the result +table in *results_database.sqlite*. + +The **procedure** field holds the name the the procedure that generated +the result (e.g.: Traffic Assignment). + +The **procedure_id** field holds an unique UUID identifier for this procedure, +which is created at runtime. + +The **procedure_report** field holds the output of the complete procedure report. + +The **timestamp** field holds the information when the procedure was executed. + +The **description** field holds the user-provided description of the result. + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + table_name*,TEXT,NO, + procedure,TEXT,NO, + procedure_id,TEXT,NO, + procedure_report,TEXT,NO, + timestamp,DATETIME,YES,current_timestamp + description,TEXT,YES, + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + + create TABLE if not exists results (table_name TEXT NOT NULL PRIMARY KEY, + procedure TEXT NOT NULL, + procedure_id TEXT NOT NULL, + procedure_report TEXT NOT NULL, + timestamp DATETIME DEFAULT current_timestamp, + description TEXT); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/route_links.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/route_links.rst new file mode 100644 index 000000000..0cc7bc538 --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/route_links.rst @@ -0,0 +1,58 @@ +*route_links* table structure +----------------------------- + +The *route_links* table holds information on the links of a route. + +**transit_link** identifies the GTFS transit links for the route + +**pattern_id** is an unique pattern for the route + +**seq** identifies the sequence of the stops for a trip + +**from_stop** identifies the stop the vehicle is departing + +**to_stop** identifies the next stop the vehicle is going to arrive + +**distance** identifies the distance (in meters) the vehicle travel +between the stops + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + transit_link,INTEGER,NO, + pattern_id,INTEGER,NO, + seq,INTEGER,NO, + from_stop,INTEGER,NO, + to_stop,INTEGER,NO, + distance,INTEGER,NO, + geometry,LINESTRING,YES, + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + CREATE TABLE IF NOT EXISTS route_links ( + transit_link INTEGER NOT NULL, + pattern_id INTEGER NOT NULL, + seq INTEGER NOT NULL, + from_stop INTEGER NOT NULL, + to_stop INTEGER NOT NULL, + distance INTEGER NOT NULL, + FOREIGN KEY(pattern_id) REFERENCES "routes"(pattern_id) deferrable initially deferred, + FOREIGN KEY(from_stop) REFERENCES "stops"(stop_id) deferrable initially deferred + FOREIGN KEY(to_stop) REFERENCES "stops"(stop_id) deferrable initially deferred + ); + + create UNIQUE INDEX IF NOT EXISTS route_links_stop_id ON route_links (pattern_id, transit_link); + + select AddGeometryColumn( 'route_links', 'geometry', 4326, 'LINESTRING', 'XY'); + + select CreateSpatialIndex( 'route_links' , 'geometry' ); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/routes.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/routes.rst new file mode 100644 index 000000000..95a3cf5f4 --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/routes.rst @@ -0,0 +1,75 @@ +*routes* table structure +------------------------ + +The *routes* table holds information on the available transit routes for a +specific day. This table information comes from the GTFS file *routes.txt*. +You can find more information about it `here `_. + +**pattern_id** is an unique pattern for the route + +**route_id** identifies a route + +**route** identifies the name of a route + +**agency_id** identifies the agency for the specified route + +**shortname** identifies the short name of a route + +**longname** identifies the long name of a route + +**description** provides useful description of a route + +**route_type** indicates the type of transportation used on a route + +**pce** indicates the passenger car equivalent for transportation used on a route + +**seated_capacity** indicates the seated capacity of a route + +**total_capacity** indicates the total capacity of a route + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + pattern_id*,INTEGER,NO, + route_id,INTEGER,NO, + route,TEXT,NO, + agency_id,INTEGER,NO, + shortname,TEXT,YES, + longname,TEXT,YES, + description,TEXT,YES, + route_type,INTEGER,NO, + pce,NUMERIC,NO,2.0 + seated_capacity,INTEGER,YES, + total_capacity,INTEGER,YES, + geometry,MULTILINESTRING,YES, + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + CREATE TABLE IF NOT EXISTS routes ( + pattern_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + route_id INTEGER NOT NULL, + route TEXT NOT NULL, + agency_id INTEGER NOT NULL, + shortname TEXT, + longname TEXT, + description TEXT, + route_type INTEGER NOT NULL, + pce NUMERIC NOT NULL DEFAULT 2.0, + seated_capacity INTEGER, + total_capacity INTEGER, + FOREIGN KEY(agency_id) REFERENCES agencies(agency_id) deferrable initially deferred + ); + + select AddGeometryColumn( 'routes', 'geometry', 4326, 'MULTILINESTRING', 'XY'); + + select CreateSpatialIndex( 'routes' , 'geometry' ); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/stop_connectors.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/stop_connectors.rst new file mode 100644 index 000000000..be9f20bc3 --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/stop_connectors.rst @@ -0,0 +1,52 @@ +*stop_connectors* table structure +--------------------------------- + +The *stops_connectors* table holds information on the connection of +the GTFS network with the real network. + +**id_from** identifies the network link the vehicle departs + +**id_to** identifies the network link th vehicle is heading to + +**conn_type** identifies the type of connection used to connect the links + +**traversal_time** represents the time spent crossing the link + +**penalty_cost** identifies the penalty in the connection + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + id_from,INTEGER,NO, + id_to,INTEGER,NO, + conn_type,INTEGER,NO, + traversal_time,INTEGER,NO, + penalty_cost,INTEGER,NO, + geometry,LINESTRING,NO,'' + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + + CREATE TABLE IF NOT EXISTS stop_connectors ( + id_from INTEGER NOT NULL, + id_to INTEGER NOT NULL, + traversal_time INTEGER NOT NULL, + penalty_cost INTEGER NOT NULL); + + SELECT AddGeometryColumn('stop_connectors', 'geometry', 4326, 'LINESTRING', 'XY', 1); + + SELECT CreateSpatialIndex('stop_connectors' , 'geometry'); + + CREATE INDEX IF NOT EXISTS stop_connectors_id_from ON stop_connectors (id_from); + + CREATE INDEX IF NOT EXISTS stop_connectors_id_to ON stop_connectors (id_to); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/stops.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/stops.rst new file mode 100644 index 000000000..aeabe3224 --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/stops.rst @@ -0,0 +1,85 @@ +*stops* table structure +----------------------- + +The *stops* table holds information on the stops where vehicles +pick up or drop off riders. This table information comes from +the GTFS file *stops.txt*. You can find more information about +it `here `_. + +**stop_id** is an unique identifier for a stop + +**stop** idenfifies a stop, statio, or station entrance + +**agency_id** identifies the agency fot the specified route + +**link** identifies the *link_id* in the links table that corresponds to the +pattern matching + +**dir** indicates the direction of travel for a trip + +**name** identifies the name of a stop + +**parent_station** defines hierarchy between different locations +defined in *stops.txt*. + +**description** provides useful description of the stop location + +**street** identifies the address of a stop + +**fare_zone_id** identifies the fare zone for a stop + +**transit_zone** identifies the TAZ for a fare zone + +**route_type** indicates the type of transporation used on a route + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + stop_id*,TEXT,YES, + stop,TEXT,NO, + agency_id,INTEGER,NO, + link,INTEGER,YES, + dir,INTEGER,YES, + name,TEXT,YES, + parent_station,TEXT,YES, + description,TEXT,YES, + street,TEXT,YES, + fare_zone_id,INTEGER,YES, + transit_zone,TEXT,YES, + route_type,INTEGER,NO,-1 + geometry,POINT,NO,'' + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + CREATE TABLE IF NOT EXISTS stops ( + stop_id TEXT PRIMARY KEY, + stop TEXT NOT NULL , + agency_id INTEGER NOT NULL, + link INTEGER, + dir INTEGER, + name TEXT, + parent_station TEXT, + description TEXT, + street TEXT, + fare_zone_id INTEGER, + transit_zone TEXT, + route_type INTEGER NOT NULL DEFAULT -1, + FOREIGN KEY(agency_id) REFERENCES agencies(agency_id), + FOREIGN KEY("fare_zone_id") REFERENCES fare_zones("fare_zone_id") + ); + + create INDEX IF NOT EXISTS stops_stop_id ON stops (stop_id); + + select AddGeometryColumn( 'stops', 'geometry', 4326, 'POINT', 'XY', 1); + + select CreateSpatialIndex( 'stops' , 'geometry' ); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/trigger_settings.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/trigger_settings.rst new file mode 100644 index 000000000..ed18ec71e --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/trigger_settings.rst @@ -0,0 +1,29 @@ +*trigger_settings* table structure +---------------------------------- + +This table intends to allow the enabled and disabling of certain triggers + + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + name*,TEXT,YES, + enabled,INTEGER,NO,TRUE + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + + CREATE TABLE if not exists trigger_settings (name TEXT PRIMARY KEY, enabled INTEGER NOT NULL DEFAULT TRUE); + INSERT INTO trigger_settings (name, enabled) VALUES('new_link_a_or_b_node', TRUE); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('trigger_settings', 'name', 'name for trigger to query against'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('trigger_settings', 'enabled', 'boolean value'); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/trips.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/trips.rst new file mode 100644 index 000000000..2ec66fe5e --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/trips.rst @@ -0,0 +1,42 @@ +*trips* table structure +----------------------- + +The *trips* table holds information on trips for each route. +This table comes from the GTFS file *trips.txt*. +You can find more information about it `here `_. + +**trip_id** identifies a trip + +**trip** identifies the trip to a rider + +**dir** indicates the direction of travel for a trip + +**pattern_id** is an unique pattern for the route + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + trip_id*,INTEGER,NO, + trip,TEXT,YES, + dir,INTEGER,NO, + pattern_id,INTEGER,NO, + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + CREATE TABLE IF NOT EXISTS trips ( + trip_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + trip TEXT, + dir INTEGER NOT NULL, + pattern_id INTEGER NOT NULL, + FOREIGN KEY(pattern_id) REFERENCES routes(pattern_id) deferrable initially deferred + ); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/trips_schedule.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/trips_schedule.rst new file mode 100644 index 000000000..0f74e23d6 --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/trips_schedule.rst @@ -0,0 +1,42 @@ +*trips_schedule* table structure +-------------------------------- + +The *trips_schedule* table holds information on the sequence of stops +of a trip. + +**trip_id** is an unique identifier of a trip + +**seq** identifies the sequence of the stops for a trip + +**arrival** identifies the arrival time at the stop + +**departure** identifies the departure time at the stop + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + trip_id*,INTEGER,NO, + seq,INTEGER,NO, + arrival,INTEGER,NO, + departure,INTEGER,NO, + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + CREATE TABLE IF NOT EXISTS trips_schedule ( + trip_id INTEGER NOT NULL, + seq INTEGER NOT NULL, + arrival INTEGER NOT NULL, + departure INTEGER NOT NULL, + PRIMARY KEY(trip_id,"seq"), + FOREIGN KEY(trip_id) REFERENCES trips(trip_id) deferrable initially deferred + ); diff --git a/docs/source/modeling_with_aequilibrae/transit_database/datamodel.rst.template b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/datamodel.rst.template similarity index 100% rename from docs/source/modeling_with_aequilibrae/transit_database/datamodel.rst.template rename to docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/datamodel.rst.template diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/index.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/index.rst new file mode 100644 index 000000000..2b6bfdc58 --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/index.rst @@ -0,0 +1,66 @@ +.. _public_transport_database: + +Public Transport database +========================= + +AequilibraE is capable of importing a General Transit Feed Specification (GTFS) feed +into its database. The Transit module has been updated in version 0.9.0. More details on +the **public_transport.sqlite** are discussed on a nearly *per-table* basis below, and +we recommend understanding the role of each table before setting an AequilibraE model +you intend to use. If you don't know much about GTFS, we strongly encourage you to take +a look at the documentation provided by `Google `_. + +The public transport database is created on the run when the ``Transit`` module is executed +for the first time. + +.. Só é possível popular essas tabelas através da execução dos módulos ``Transit`` e +.. ``TransitGraphBuilder`` (mais algum módulo pertinente). A tabela a seguir mostra +.. quais tabelas são povoadas através dos módulos + +.. seealso:: + + :func:`aequilibrae.transit.Transit` + + :func:`aequilibrae.transit.TransitGraphBuilder` + +In the following sections, we'll dive deep into the tables existing in the public transport database. +Please notice that some tables are homonyms to the ones existing in the **project_database.sqlite**, +but its contents are related to the public transport graph building and assignment processes. + +.. include:: data_model/agencies.rst + +.. include:: data_model/attributes_documentation.rst + +.. include:: data_model/fare_attributes.rst + +.. include:: data_model/fare_rules.rst + +.. include:: data_model/fare_zones.rst + +.. include:: data_model/link_types.rst + +.. include:: data_model/links.rst + +.. include:: data_model/modes.rst + +.. include:: data_model/node_types.rst + +.. include:: data_model/nodes.rst + +.. include:: data_model/pattern_mapping.rst + +.. include:: data_model/results.rst + +.. include:: data_model/route_links.rst + +.. include:: data_model/routes.rst + +.. include:: data_model/stop_connectors.rst + +.. include:: data_model/stops.rst + +.. include:: data_model/trigger_settings.rst + +.. include:: data_model/trips_schedule.rst + +.. include:: data_model/trips.rst diff --git a/docs/source/modeling_with_aequilibrae/static_traffic_assignment/assignment_mechanics.rst b/docs/source/modeling_with_aequilibrae/2-static_traffic_assignment/assignment_mechanics.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/static_traffic_assignment/assignment_mechanics.rst rename to docs/source/modeling_with_aequilibrae/2-static_traffic_assignment/assignment_mechanics.rst diff --git a/docs/source/modeling_with_aequilibrae/static_traffic_assignment.rst b/docs/source/modeling_with_aequilibrae/2-static_traffic_assignment/index.rst similarity index 90% rename from docs/source/modeling_with_aequilibrae/static_traffic_assignment.rst rename to docs/source/modeling_with_aequilibrae/2-static_traffic_assignment/index.rst index 44b907706..f9ed635a8 100644 --- a/docs/source/modeling_with_aequilibrae/static_traffic_assignment.rst +++ b/docs/source/modeling_with_aequilibrae/2-static_traffic_assignment/index.rst @@ -20,5 +20,5 @@ we detail how these concepts are translated into the AequilibraE tools and recom :maxdepth: 1 :caption: Static Traffic Assignment - static_traffic_assignment/assignment_mechanics - static_traffic_assignment/multi_class_equilibrium + assignment_mechanics + multi_class_equilibrium diff --git a/docs/source/modeling_with_aequilibrae/static_traffic_assignment/multi_class_equilibrium.rst b/docs/source/modeling_with_aequilibrae/2-static_traffic_assignment/multi_class_equilibrium.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/static_traffic_assignment/multi_class_equilibrium.rst rename to docs/source/modeling_with_aequilibrae/2-static_traffic_assignment/multi_class_equilibrium.rst diff --git a/docs/source/modeling_with_aequilibrae/transit_assignment/hyperpath_routing.rst b/docs/source/modeling_with_aequilibrae/3-transit_assignment/hyperpath_routing.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/transit_assignment/hyperpath_routing.rst rename to docs/source/modeling_with_aequilibrae/3-transit_assignment/hyperpath_routing.rst diff --git a/docs/source/modeling_with_aequilibrae/transit_assignment.rst b/docs/source/modeling_with_aequilibrae/3-transit_assignment/index.rst similarity index 69% rename from docs/source/modeling_with_aequilibrae/transit_assignment.rst rename to docs/source/modeling_with_aequilibrae/3-transit_assignment/index.rst index ff1f05508..3599e9176 100644 --- a/docs/source/modeling_with_aequilibrae/transit_assignment.rst +++ b/docs/source/modeling_with_aequilibrae/3-transit_assignment/index.rst @@ -9,5 +9,5 @@ Transit assignment :maxdepth: 1 :caption: Transit Assignment - transit_assignment/hyperpath_routing.rst - transit_assignment/transit_graph.rst \ No newline at end of file + hyperpath_routing.rst + transit_graph.rst \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/transit_assignment/transit_graph.rst b/docs/source/modeling_with_aequilibrae/3-transit_assignment/transit_graph.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/transit_assignment/transit_graph.rst rename to docs/source/modeling_with_aequilibrae/3-transit_assignment/transit_graph.rst diff --git a/docs/source/modeling_with_aequilibrae/route_choice/choice_set_generation.rst b/docs/source/modeling_with_aequilibrae/4-route_choice/choice_set_generation.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/route_choice/choice_set_generation.rst rename to docs/source/modeling_with_aequilibrae/4-route_choice/choice_set_generation.rst diff --git a/docs/source/modeling_with_aequilibrae/route_choice.rst b/docs/source/modeling_with_aequilibrae/4-route_choice/index.rst similarity index 97% rename from docs/source/modeling_with_aequilibrae/route_choice.rst rename to docs/source/modeling_with_aequilibrae/4-route_choice/index.rst index ce9e3a08f..5d96a9c9b 100644 --- a/docs/source/modeling_with_aequilibrae/route_choice.rst +++ b/docs/source/modeling_with_aequilibrae/4-route_choice/index.rst @@ -14,8 +14,8 @@ multiple algorithms to the assignment of trips to the network using the traditio :maxdepth: 1 :caption: Dive deep into route choice - route_choice/route_choice_model.rst - route_choice/choice_set_generation.rst + route_choice_model.rst + choice_set_generation.rst Costs, utilities and signs -------------------------- diff --git a/docs/source/modeling_with_aequilibrae/route_choice/route_choice_model.rst b/docs/source/modeling_with_aequilibrae/4-route_choice/route_choice_model.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/route_choice/route_choice_model.rst rename to docs/source/modeling_with_aequilibrae/4-route_choice/route_choice_model.rst diff --git a/docs/source/modeling_with_aequilibrae/modeling_with_aequilibrae.rst b/docs/source/modeling_with_aequilibrae/index.rst similarity index 86% rename from docs/source/modeling_with_aequilibrae/modeling_with_aequilibrae.rst rename to docs/source/modeling_with_aequilibrae/index.rst index d729cf61c..784b11f0f 100644 --- a/docs/source/modeling_with_aequilibrae/modeling_with_aequilibrae.rst +++ b/docs/source/modeling_with_aequilibrae/index.rst @@ -19,7 +19,7 @@ a start guide to a complete view into AequilibraE's data structure. :maxdepth: 1 :caption: A guide to AequilibraE - project - static_traffic_assignment - transit_assignment - route_choice + 1-aequilibrae_project/index + 2-static_traffic_assignment/index + 3-transit_assignment/index + 4-route_choice/index diff --git a/docs/source/modeling_with_aequilibrae/project_database.rst b/docs/source/modeling_with_aequilibrae/project_database.rst deleted file mode 100644 index a99d4a475..000000000 --- a/docs/source/modeling_with_aequilibrae/project_database.rst +++ /dev/null @@ -1,21 +0,0 @@ - -Project database ----------------- - -More details on the **project_database.sqlite** are discussed on a nearly *per-table* -basis below, and we recommend understanding the role of each table before setting -an AequilibraE model you intend to use in anger. - -.. toctree:: - :maxdepth: 1 - - project_database/about - project_database/network - project_database/modes - project_database/link_types - project_database/matrices - project_database/zones - project_database/parameters_metadata - project_database/results - project_database/periods - project_database/transit_graph \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/public_transport.rst b/docs/source/modeling_with_aequilibrae/public_transport.rst deleted file mode 100644 index a82d5a8ad..000000000 --- a/docs/source/modeling_with_aequilibrae/public_transport.rst +++ /dev/null @@ -1,14 +0,0 @@ -.. _public_transport_database: - -Public Transport database -========================= - -AequilibraE is capable of importing a General Transit Feed Specification (GTFS) feed -into its database. The Transit module has been updated in version 0.9.0. More details on -the **public_transport.sqlite** are discussed on a nearly *per-table* basis below, and -we recommend understanding the role of each table before setting an AequilibraE model -you intend to use. If you don't know much about GTFS, we strongly encourage you to take -a look at the documentation provided by `Google `_. - -The public transport database is created on the run when the ``Transit`` class is executed -for the first time. \ No newline at end of file From 0a582dabab31d17c4a29e0dde44cf5e028c24453 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Mon, 2 Sep 2024 16:49:33 -0300 Subject: [PATCH 16/57] linting --- docs/source/api/api.rst | 27 +++++++++++++++---- .../1-aequilibrae_project/index.rst | 5 +++- docs/table_documentation.py | 22 ++++++++++----- 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/docs/source/api/api.rst b/docs/source/api/api.rst index 69fc8da17..bb5ce8702 100644 --- a/docs/source/api/api.rst +++ b/docs/source/api/api.rst @@ -86,21 +86,38 @@ Matrix Paths ----- + +Graph +~~~~~ .. currentmodule:: aequilibrae.paths .. autosummary:: :toctree: generated/ Graph TransitGraph - AssignmentResults - TransitAssignmentResults - SkimResults - PathResults - VDF + +Traffic assignment +~~~~~~~~~~~~~~~~~~ +.. currentmodule:: aequilibrae.paths +.. autosummary:: + :toctree: generated/ + TrafficClass TransitClass + VDF TrafficAssignment TransitAssignment + AssignmentResults + TransitAssignmentResults + SkimResults + PathResults + +Route choice +~~~~~~~~~~~~ +.. currentmodule:: aequilibrae.paths +.. autosummary:: + :toctree: generated/ + HyperpathGenerating OptimalStrategies RouteChoice diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/index.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/index.rst index e275f8833..b57c7c851 100644 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/index.rst +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/index.rst @@ -98,6 +98,9 @@ also created *on-the-fly* when the user imports a GTFS source into an Aequilibra model, but there is still no support for manually or programmatically adding routes to a route system as of yet. +The following sections presents the structure and contents for all databases in the project. + + .. toctree:: :maxdepth: 1 :caption: Dive deep into project structure! @@ -105,4 +108,4 @@ to a route system as of yet. project_database/index parameter_file results_database - public_transport_database/index + transit_database/index diff --git a/docs/table_documentation.py b/docs/table_documentation.py index 24791eb06..ebe42dc8d 100644 --- a/docs/table_documentation.py +++ b/docs/table_documentation.py @@ -27,9 +27,16 @@ def __init__(self, component: str, tgt_fldr: str): self.stub = "data_model" # Get the appropriate data for the database we are documenting self.conn = database_connection(db_type=folder, project_path=self.proj_path) - self.path = join(*Path(realpath(__file__)).parts[:-1], - f"../aequilibrae/project/database_specification/{folder}/tables") - self.doc_path = str(Path(realpath(__file__)).parent / "source" / tgt_fldr) + self.path = join( + *Path(realpath(__file__)).parts[:-1], f"../aequilibrae/project/database_specification/{folder}/tables" + ) + self.doc_path = str( + Path(realpath(__file__)).parent + / "source" + / "modeling_with_aequilibrae" + / "1-aequilibrae_project" + / tgt_fldr + ) Path(join(self.doc_path, self.stub)).mkdir(exist_ok=True, parents=True) @@ -44,7 +51,7 @@ def create(self): descr = self.conn.execute(f"pragma table_info({table_name})").fetchall() # Title of the page - title = f'*{table_name}* table structure' + title = f"*{table_name}* table structure" txt = [title, "-" * len(title), ""] # intro = """A more technical view of the database structure, including the SQL queries used to create each table and the indices used are displayed below.\n""" @@ -126,8 +133,11 @@ def readlines(filename): with open(filename, "r") as f: return [x.strip() for x in f.readlines()] -tables = [("project_database", "modeling_with_aequilibrae/project_database"), - ("transit_database", "modeling_with_aequilibrae/transit_database")] + +tables = [ + ("project_database", "modeling_with_aequilibrae/project_database"), + ("transit_database", "modeling_with_aequilibrae/transit_database"), +] for table, pth in tables: s = CreateTablesSRC(table, pth) From 87ec3cab3fa5a993363321e7698046ac5708ce5a Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Mon, 2 Sep 2024 17:15:11 -0300 Subject: [PATCH 17/57] rename files --- .../1-aequilibrae_project/index.rst | 5 +- .../project_database/about.rst | 2 +- .../attributes_documentation.rst | 2 +- .../project_database/index.rst | 1 - .../project_database/link_types.rst | 2 +- .../project_database/matrices.rst | 2 +- .../project_database/modes.rst | 2 +- .../network_import_and_export.rst | 8 +- .../results_database.rst | 2 +- .../{developing.rst => development/index.rst} | 0 .../useful_information/installation.rst | 2 +- .../IPF_benchmark.ipynb | 9 +- .../validation_benchmarking/IPF_benchmark.rst | 757 ------------------ .../{validation.rst => index.rst} | 0 .../ipf_performance.rst | 2 + 15 files changed, 22 insertions(+), 774 deletions(-) rename docs/source/useful_information/{developing.rst => development/index.rst} (100%) delete mode 100644 docs/source/useful_information/validation_benchmarking/IPF_benchmark.rst rename docs/source/useful_information/validation_benchmarking/{validation.rst => index.rst} (100%) diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/index.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/index.rst index b57c7c851..6ad15bbc5 100644 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/index.rst +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/index.rst @@ -3,6 +3,10 @@ The AequilibraE project ======================= +.. contents:: **Contents** + :local: + :depth: 1 + Similarly to commercial packages, any AequilibraE project must have a certain structure and follow a certain set of guidelines in order for software to work correctly. @@ -100,7 +104,6 @@ to a route system as of yet. The following sections presents the structure and contents for all databases in the project. - .. toctree:: :maxdepth: 1 :caption: Dive deep into project structure! diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/about.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/about.rst index 3a5e9b6e2..5a2ee701e 100644 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/about.rst +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/about.rst @@ -19,7 +19,7 @@ We strongly recommend not to edit the information on **projection** and the software to produce valuable information to the user with regards to opportunities for version upgrades. -.. image:: ../../images/about_table_example.png +.. image:: ../../../images/about_table_example.png :alt: About table structure :align: center diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/attributes_documentation.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/attributes_documentation.rst index 653addabc..5ac56b796 100644 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/attributes_documentation.rst +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/attributes_documentation.rst @@ -12,7 +12,7 @@ itself. As a simple table, it looks as follows: -.. image:: ../../images/attributes_documentation.png +.. image:: ../../../images/attributes_documentation.png :align: center :alt: attributes documentation table diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/index.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/index.rst index 04ecc69e1..963862287 100644 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/index.rst +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/index.rst @@ -16,6 +16,5 @@ an AequilibraE model you intend to use in anger. matrices zones attributes_documentation - results periods transit_graph \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/link_types.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/link_types.rst index e6acb36db..a86292f96 100644 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/link_types.rst +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/link_types.rst @@ -33,7 +33,7 @@ Adding new link types to a project Adding link types to a project can be done through the Python API or directly into the 'link_types' table, which could look like the following. -.. image:: ../../images/link_types_table.png +.. image:: ../../../images/link_types_table.png :align: center :alt: Link_types table structure diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/matrices.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/matrices.rst index 32ab3c893..0a4b4f004 100644 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/matrices.rst +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/matrices.rst @@ -11,7 +11,7 @@ This index, which looks like below, has two main columns. The first one is the AequilibraE to find the file, and **name**, which is the name by which the user should refer to the matrix in order to access it through the API. -.. image:: ../../images/matrices_table.png +.. image:: ../../../images/matrices_table.png :align: center :alt: Matrices table structure diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/modes.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/modes.rst index d5019fd59..1441a00a1 100644 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/modes.rst +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/modes.rst @@ -16,7 +16,7 @@ As described in the SQL data model, all AequilibraE models are created with 4 standard modes, which can be added to or removed by the user, and would look like the following. -.. image:: ../../images/modes_table.png +.. image:: ../../../images/modes_table.png :align: center :alt: Modes table structure diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/network_import_and_export.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/network_import_and_export.rst index e68b270f7..7a97b3f78 100644 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/network_import_and_export.rst +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/network_import_and_export.rst @@ -47,7 +47,7 @@ layers to the canvas, and selecting properties and clicking on *create spatial* *index* for each layer at a time. This action automatically saves the spatial indices to the sqlite database. -.. image:: ../../images/qgis_creating_spatial_indices.png +.. image:: ../../../images/qgis_creating_spatial_indices.png :align: center :alt: Adding Spatial indices with QGIS @@ -93,10 +93,10 @@ Before importing a network from a source in GMNS format, it is imperative to kno in which spatial reference its geometries (links and nodes) were created. If the SRID is different than 4326, it must be passed as an input using the argument 'srid'. -.. image:: ../../images/plot_import_from_gmns.png +.. image:: ../../../images/plot_import_from_gmns.png :align: center :alt: example - :target: ../../_auto_examples/plot_import_from_gmns.html + :target: ../../../_auto_examples/plot_import_from_gmns.html As of July 2022, it is possible to import the following files from a GMNS source: @@ -135,7 +135,7 @@ Exporting AequilibraE model to GMNS format After loading an existing AequilibraE project, you can export it to GMNS format. -.. image:: ../../images/plot_export_to_gmns.png +.. image:: ../../../images/plot_export_to_gmns.png :align: center :alt: example :target: export_to_gmns diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/results_database.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/results_database.rst index 55c6f0b9f..5d70b28f3 100644 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/results_database.rst +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/results_database.rst @@ -14,7 +14,7 @@ essentially clutter the **project_database.sqlite**. As a simple table, it looks as follows: -.. image:: ../images/results_table.png +.. image:: ../../images/results_table.png :align: center :alt: results table structure diff --git a/docs/source/useful_information/developing.rst b/docs/source/useful_information/development/index.rst similarity index 100% rename from docs/source/useful_information/developing.rst rename to docs/source/useful_information/development/index.rst diff --git a/docs/source/useful_information/installation.rst b/docs/source/useful_information/installation.rst index 298a1000d..0e0f2013d 100644 --- a/docs/source/useful_information/installation.rst +++ b/docs/source/useful_information/installation.rst @@ -8,7 +8,7 @@ Installation In this section we describe how to install AequilibraE. The recommendations on this page are current as of September 2024. -.. note:: +.. important:: Although AequilibraE is under intense development, we try to avoid making breaking changes to the API. In any case, you should check for new features and possible API changes often. diff --git a/docs/source/useful_information/validation_benchmarking/IPF_benchmark.ipynb b/docs/source/useful_information/validation_benchmarking/IPF_benchmark.ipynb index 2ab875bd1..1759d4d8a 100644 --- a/docs/source/useful_information/validation_benchmarking/IPF_benchmark.ipynb +++ b/docs/source/useful_information/validation_benchmarking/IPF_benchmark.ipynb @@ -9,6 +9,7 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "id": "fc1c6f92", "metadata": {}, @@ -35,13 +36,13 @@ "the user can set up a threshold for AequilibraE's IPF function, as well as use more than one\n", "core to speed up the fitting process.\n", "\n", - "![AequilibraE's IPF runtime](../images/ipf_runtime_aequilibrae_vs_benchmark.png)\n", + "![AequilibraE's IPF runtime](../../images/ipf_runtime_aequilibrae_vs_benchmark.png)\n", "\n", "As IPF is an embarrassingly-parallel workload, it is more relevant to look at the performance of the\n", "AequilibraE implementations, starting by comparing the implementation performance for inputs in 32 vs 64\n", "bits using 32 threads.\n", "\n", - "![AequilibraE's IPF runtime 32 vs 64 bits](../images/ipf_runtime_32vs64bits.png)\n", + "![AequilibraE's IPF runtime 32 vs 64 bits](../../images/ipf_runtime_32vs64bits.png)\n", "\n", "The difference is staggering, with the 32-bit implementation being twice as fast as the 64-bit one for large matrices.\n", "It is also worth noting that differences in results between the outputs between these two versions are incredibly\n", @@ -52,9 +53,9 @@ "gains up to 16 threads, while the latter stops showing much improvement beyond 8 threads, likely due to limitations\n", "on cache size.\n", "\n", - "![number of cores used in IPF for 64 bit matrices](../images/ipf_runtime_vs_num_cores.png)\n", + "![number of cores used in IPF for 64 bit matrices](../../images/ipf_runtime_vs_num_cores.png)\n", "\n", - "![number of cores used in IPF for 32 bit matrices](../images/ipf_runtime_vs_num_cores32bits.png)\n", + "![number of cores used in IPF for 32 bit matrices](../../images/ipf_runtime_vs_num_cores32bits.png)\n", "\n", "In conclusion, AequilibraE's IPF implementation is over 11 times faster than its pure Python counterpart for\n", "large matrices on a workstation, largely due to the use of Cython and multi-threading, but also due to the use of a\n", diff --git a/docs/source/useful_information/validation_benchmarking/IPF_benchmark.rst b/docs/source/useful_information/validation_benchmarking/IPF_benchmark.rst deleted file mode 100644 index a759eca18..000000000 --- a/docs/source/useful_information/validation_benchmarking/IPF_benchmark.rst +++ /dev/null @@ -1,757 +0,0 @@ -IPF Performance -=============== - -The use of iterative proportional fitting (IPF) is quite common on -processes involving doubly-constraining matrices, such as synthetic -gravity models and fractional split models (aggregate destination-choice -models). - -As this is a commonly used algorithm, we have implemented it in Cython, -where we can take full advantage of multi-core CPUs. We have also -implemented the ability of using both 32-bit and 64-bit floating-point -seed matrices, which has direct impact on cache use and consequently -computational performance. - -In this section, we compare the runtime of AequilibraE’s current -implementation of IPF, with a general IPF algorithm written in pure -Python, available -`here `__. - -The figure below compares AequilibraE’s IPF runtime with one core with -the benchmark Python code. From the figure below, we can notice that the -runtimes were practically the same for the instances with 1,000 zones or -less. As the number of zones increases, AequilibraE demonstrated to be -slightly faster than the benchmark python code, while applying IPF to a -32-bit NumPy array (np.float32) was significantly faster. It’s worth -mentioning that the user can set up a threshold for AequilibraE’s IPF -function, as well as use more than one core to speed up the fitting -process. - -.. figure:: ../images/ipf_runtime_aequilibrae_vs_benchmark.png - :alt: AequilibraE’s IPF runtime - - AequilibraE’s IPF runtime - -As IPF is an embarrassingly-parallel workload, it is more relevant to -look at the performance of the AequilibraE implementations, starting by -comparing the implementation performance for inputs in 32 vs 64 bits -using 32 threads. - -.. figure:: ../images/ipf_runtime_32vs64bits.png - :alt: AequilibraE’s IPF runtime 32 vs 64 bits - - AequilibraE’s IPF runtime 32 vs 64 bits - -The difference is staggering, with the 32-bit implementation being twice -as fast as the 64-bit one for large matrices. It is also worth noting -that differences in results between the outputs between these two -versions are incredibly small (RMSE < 1.1e-10), and therefore unlikely -to be relevant in most applications. - -We can also look at performance gain across matrix sizes and number of -cores, and it becomes clear that the 32-bit version scales significantly -better than its 64-bit counterpart, showing significant performance -gains up to 16 threads, while the latter stops showing much improvement -beyond 8 threads, likely due to limitations on cache size. - -.. figure:: ../images/ipf_runtime_vs_num_cores.png - :alt: number of cores used in IPF for 64 bit matrices - - number of cores used in IPF for 64 bit matrices - -.. figure:: ../images/ipf_runtime_vs_num_cores32bits.png - :alt: number of cores used in IPF for 32 bit matrices - - number of cores used in IPF for 32 bit matrices - -In conclusion, AequilibraE’s IPF implementation is over 11 times faster -than its pure Python counterpart for large matrices on a workstation, -largely due to the use of Cython and multi-threading, but also due to -the use of a 32-bit version of the algorithm. - -These tests were run on a Threadripper 3970x (released in 2019) -workstation with 32 cores (64 threads) @ 3.7 GHz and 256 Gb of RAM. The -code is provided below for reference. - -Reference code --------------- - -.. code:: ipython2 - - from copy import deepcopy - from time import perf_counter - import numpy as np - import pandas as pd - from aequilibrae.distribution.ipf_core import ipf_core - from tqdm import tqdm - -.. code:: ipython2 - - # From: - # https://github.com/joshchea/python-tdm/blob/master/scripts/CalcDistribution.py - - def CalcFratar(ProdA, AttrA, Trips1, maxIter=10): - '''Calculates fratar trip distribution - ProdA = Production target as array - AttrA = Attraction target as array - Trips1 = Seed trip table for fratar - maxIter (optional) = maximum iterations, default is 10 - Returns fratared trip table - ''' - # print('Checking production, attraction balancing:') - sumP = ProdA.sum() - sumA = AttrA.sum() - # print('Production: ', sumP) - # print('Attraction: ', sumA) - if sumP != sumA: - # print('Productions and attractions do not balance, attractions will be scaled to productions!') - AttrA = AttrA*(sumP/sumA) - else: - pass - # print('Production, attraction balancing OK.') - # Run 2D balancing ---> - for balIter in range(0, maxIter): - ComputedProductions = Trips1.sum(1) - ComputedProductions[ComputedProductions == 0] = 1 - OrigFac = (ProdA/ComputedProductions) - Trips1 = Trips1*OrigFac[:, np.newaxis] - - ComputedAttractions = Trips1.sum(0) - ComputedAttractions[ComputedAttractions == 0] = 1 - DestFac = (AttrA/ComputedAttractions) - Trips1 = Trips1*DestFac - return Trips1 - -.. code:: ipython2 - - mat_sizes = [500, 750, 1000, 1500, 2500, 5000, 7500, 10000, 15000] - -.. code:: ipython2 - - #Benchmarking - bench_data = [] - cores = 1 - repetitions = 5 - iterations = 100 - for zones in mat_sizes: - for repeat in tqdm(range(repetitions), f"Repetitions for zone size {zones}"): - mat1 = np.random.rand(zones, zones) - target_prod = np.random.rand(zones) - target_atra = np.random.rand(zones) - target_atra *= target_prod.sum()/target_atra.sum() - - aeq_mat = deepcopy(mat1) - # We use a nonsensical negative tolerance to force it to run all iterations - # and set warning for non-convergence to false, as we know it won't converge - t = perf_counter() - ipf_core(aeq_mat, target_prod, target_atra, max_iterations=iterations, tolerance=-5, cores=cores, warn=False) - aeqt = perf_counter() - t - - aeq_mat32 = np.array(mat1, np.float32) - # We now run the same thing with a seed matrix in single-precision (float 32 bits) instead of double as above (64 bits) - t = perf_counter() - ipf_core(aeq_mat32, target_prod, target_atra, max_iterations=iterations, tolerance=-5, cores=cores, warn=False) - aeqt2 = perf_counter() - t - - bc_mat = deepcopy(mat1) - t = perf_counter() - x = CalcFratar(target_prod, target_atra, bc_mat, maxIter=iterations) - - bench_data.append([zones, perf_counter() - t, aeqt, aeqt2]) - - -.. parsed-literal:: - - Repetitions for zone size 500: 100%|██████████| 5/5 [00:01<00:00, 2.60it/s] - Repetitions for zone size 750: 100%|██████████| 5/5 [00:04<00:00, 1.18it/s] - Repetitions for zone size 1000: 100%|██████████| 5/5 [00:07<00:00, 1.57s/it] - Repetitions for zone size 1500: 100%|██████████| 5/5 [00:19<00:00, 3.88s/it] - Repetitions for zone size 2500: 100%|██████████| 5/5 [00:56<00:00, 11.24s/it] - Repetitions for zone size 5000: 100%|██████████| 5/5 [03:44<00:00, 44.89s/it] - Repetitions for zone size 7500: 100%|██████████| 5/5 [08:09<00:00, 97.89s/it] - Repetitions for zone size 10000: 100%|██████████| 5/5 [14:11<00:00, 170.34s/it] - Repetitions for zone size 15000: 100%|██████████| 5/5 [32:23<00:00, 388.70s/it] - - -.. code:: ipython2 - - bench_df = pd.DataFrame(bench_data, columns=["Zones in the model", "PythonCode", "AequilibraE", "AequilibraE-32bits"]) - bench_df.groupby(["Zones in the model"]).mean().plot.bar() - - - - -.. parsed-literal:: - - - - - - -.. image:: IPF_benchmark_files/IPF_benchmark_7_1.png - - -.. code:: ipython2 - - bench_df.groupby(["Zones in the model"]).mean() - - - - -.. raw:: html - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PythonCodeAequilibraEAequilibraE-32bits
Zones in the model
5000.1031660.1400510.137266
7500.2417570.3029310.297389
10000.5139840.5297270.516339
15001.4168481.2768441.156637
25004.2535633.5816453.333664
500017.05916614.28876013.257936
750035.56868632.02156329.666000
1000059.03096757.02820453.155270
15000134.895915130.652199120.535656
-
- - - -.. code:: ipython2 - - #Benchmarking 32 threads - bench_data_parallel = [] - cores = 32 - repetitions = 5 - iterations = 100 - for zones in mat_sizes: - for repeat in tqdm(range(repetitions), f"Repetitions for zone size {zones}"): - mat1 = np.random.rand(zones, zones) - target_prod = np.random.rand(zones) - target_atra = np.random.rand(zones) - target_atra *= target_prod.sum()/target_atra.sum() - - aeq_mat = deepcopy(mat1) - # We use a nonsensical negative tolerance to force it to run all iterations - # and set warning for non-convergence to false, as we know it won't converge - t = perf_counter() - ipf_core(aeq_mat, target_prod, target_atra, max_iterations=iterations, tolerance=-5, cores=cores, warn=False) - aeqt = perf_counter() - t - - aeq_mat32 = np.array(mat1, np.float32) - # We now run the same thing with a seed matrix in single-precision (float 32 bits) instead of double as above (64 bits) - t = perf_counter() - ipf_core(aeq_mat32, target_prod, target_atra, max_iterations=iterations, tolerance=-5, cores=cores, warn=False) - aeqt2 = perf_counter() - t - - rmse = np.sqrt(np.mean((aeq_mat-aeq_mat32)**2)) - - bench_data_parallel.append([zones, aeqt, aeqt2, rmse]) - - -.. parsed-literal:: - - Repetitions for zone size 500: 100%|██████████| 5/5 [00:01<00:00, 2.70it/s] - Repetitions for zone size 750: 100%|██████████| 5/5 [00:01<00:00, 2.64it/s] - Repetitions for zone size 1000: 100%|██████████| 5/5 [00:02<00:00, 2.37it/s] - Repetitions for zone size 1500: 100%|██████████| 5/5 [00:03<00:00, 1.61it/s] - Repetitions for zone size 2500: 100%|██████████| 5/5 [00:07<00:00, 1.41s/it] - Repetitions for zone size 5000: 100%|██████████| 5/5 [00:24<00:00, 4.91s/it] - Repetitions for zone size 7500: 100%|██████████| 5/5 [00:49<00:00, 9.96s/it] - Repetitions for zone size 10000: 100%|██████████| 5/5 [01:26<00:00, 17.29s/it] - Repetitions for zone size 15000: 100%|██████████| 5/5 [03:10<00:00, 38.02s/it] - - -.. code:: ipython2 - - bench_df_parallel = pd.DataFrame(bench_data_parallel, columns=["Zones in the model", "AequilibraE", "AequilibraE-32bits", "rmse"]) - bench_df_parallel.groupby(["Zones in the model"]).mean()[[ "AequilibraE", "AequilibraE-32bits"]].plot.bar() - - - - -.. parsed-literal:: - - - - - - -.. image:: IPF_benchmark_files/IPF_benchmark_10_1.png - - -.. code:: ipython2 - - bench_df_parallel.groupby(["Zones in the model"]).mean() - - - - -.. raw:: html - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
AequilibraEAequilibraE-32bitsrmse
Zones in the model
5000.1702320.1724601.080659e-10
7500.1834740.1838577.100473e-11
10000.2112370.1919615.137420e-11
15000.3227090.2521153.358886e-11
25000.7797150.5220372.036197e-11
50002.7450041.7601009.588117e-12
75005.9004313.1700066.258958e-12
1000010.2553055.4566314.733115e-12
1500022.61866511.8604973.102268e-12
-
- - - -.. code:: ipython2 - - cores_to_use = [1, 2, 4, 8, 16, 32] - -.. code:: ipython2 - - aeq_data = [] - repetitions = 1 - iterations = 50 - for zones in mat_sizes: - for cores in tqdm(cores_to_use,f"Zone size: {zones}"): - for repeat in range(repetitions): - mat1 = np.random.rand(zones, zones) - target_prod = np.random.rand(zones) - target_atra = np.random.rand(zones) - target_atra *= target_prod.sum()/target_atra.sum() - - aeq_mat = np.array(deepcopy(mat1), np.float32) - t = perf_counter() - ipf_core(aeq_mat, target_prod, target_atra, max_iterations=iterations, tolerance=-5, cores=cores, warn=False) - aeqt = perf_counter() - t - - aeq_data.append([zones, cores, aeqt]) - - -.. parsed-literal:: - - Zone size: 500: 100%|██████████| 6/6 [00:00<00:00, 12.14it/s] - Zone size: 750: 100%|██████████| 6/6 [00:00<00:00, 10.20it/s] - Zone size: 1000: 100%|██████████| 6/6 [00:00<00:00, 6.87it/s] - Zone size: 1500: 100%|██████████| 6/6 [00:01<00:00, 3.42it/s] - Zone size: 2500: 100%|██████████| 6/6 [00:04<00:00, 1.32it/s] - Zone size: 5000: 100%|██████████| 6/6 [00:16<00:00, 2.73s/it] - Zone size: 7500: 100%|██████████| 6/6 [00:35<00:00, 5.93s/it] - Zone size: 10000: 100%|██████████| 6/6 [01:02<00:00, 10.46s/it] - Zone size: 15000: 100%|██████████| 6/6 [02:21<00:00, 23.62s/it] - - -.. code:: ipython2 - - aeq_df = pd.DataFrame(aeq_data, columns=["zones", "cores", "time"]) - aeq_df = aeq_df[aeq_df.zones>1000] - aeq_df = aeq_df.groupby(["zones", "cores"]).mean().reset_index() - aeq_df = aeq_df.pivot_table(index="zones", columns="cores", values="time") - for cores in cores_to_use[::-1]: - aeq_df.loc[:, cores] /= aeq_df[1] - aeq_df.transpose().plot() - aeq_df - - - - -.. raw:: html - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
cores12481632
zones
15001.00.5638660.4267110.2704520.1911470.213856
25001.00.5277810.3750330.2497760.1564730.157657
50001.00.5101830.2781500.1744690.1431750.134064
75001.00.5087060.2658350.1572170.1250070.107014
100001.00.5071720.2661920.1548440.1207710.103968
150001.00.5046960.2599330.1523940.1150520.101344
-
- - - - -.. image:: IPF_benchmark_files/IPF_benchmark_14_1.png - - -.. code:: ipython2 - - aeq_data = [] - repetitions = 1 - iterations = 50 - for zones in mat_sizes: - for cores in tqdm(cores_to_use,f"Zone size: {zones}"): - for repeat in range(repetitions): - mat1 = np.random.rand(zones, zones) - target_prod = np.random.rand(zones) - target_atra = np.random.rand(zones) - target_atra *= target_prod.sum()/target_atra.sum() - - aeq_mat = np.array(deepcopy(mat1), np.float64) - t = perf_counter() - ipf_core(aeq_mat, target_prod, target_atra, max_iterations=iterations, tolerance=-5, cores=cores, warn=False) - aeqt = perf_counter() - t - - aeq_data.append([zones, cores, aeqt]) - - -.. parsed-literal:: - - Zone size: 500: 100%|██████████| 6/6 [00:00<00:00, 12.51it/s] - Zone size: 750: 100%|██████████| 6/6 [00:00<00:00, 9.19it/s] - Zone size: 1000: 100%|██████████| 6/6 [00:00<00:00, 6.50it/s] - Zone size: 1500: 100%|██████████| 6/6 [00:01<00:00, 3.07it/s] - Zone size: 2500: 100%|██████████| 6/6 [00:05<00:00, 1.17it/s] - Zone size: 5000: 100%|██████████| 6/6 [00:18<00:00, 3.14s/it] - Zone size: 7500: 100%|██████████| 6/6 [00:42<00:00, 7.10s/it] - Zone size: 10000: 100%|██████████| 6/6 [01:15<00:00, 12.51s/it] - Zone size: 15000: 100%|██████████| 6/6 [02:47<00:00, 27.93s/it] - - -.. code:: ipython2 - - aeq_df = pd.DataFrame(aeq_data, columns=["zones", "cores", "time"]) - aeq_df = aeq_df[aeq_df.zones>1000] - aeq_df = aeq_df.groupby(["zones", "cores"]).mean().reset_index() - aeq_df = aeq_df.pivot_table(index="zones", columns="cores", values="time") - for cores in cores_to_use[::-1]: - aeq_df.loc[:, cores] /= aeq_df[1] - aeq_df.transpose().plot() - aeq_df - - - - -.. raw:: html - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
cores12481632
zones
15001.00.5591140.4081110.2502970.2163080.255603
25001.00.5264900.3594110.2554470.2039430.232535
50001.00.5176010.3110590.2160650.1957200.187223
75001.00.5280700.3033040.2210320.2008860.189343
100001.00.5209010.3014530.2096690.1814500.174836
150001.00.5207690.3074020.2088820.1801390.176024
-
- - - - -.. image:: IPF_benchmark_files/IPF_benchmark_16_1.png - diff --git a/docs/source/useful_information/validation_benchmarking/validation.rst b/docs/source/useful_information/validation_benchmarking/index.rst similarity index 100% rename from docs/source/useful_information/validation_benchmarking/validation.rst rename to docs/source/useful_information/validation_benchmarking/index.rst diff --git a/docs/source/useful_information/validation_benchmarking/ipf_performance.rst b/docs/source/useful_information/validation_benchmarking/ipf_performance.rst index 41a483574..df44fd636 100644 --- a/docs/source/useful_information/validation_benchmarking/ipf_performance.rst +++ b/docs/source/useful_information/validation_benchmarking/ipf_performance.rst @@ -1,3 +1,5 @@ +:orphan: + IPF Performance =============== From b8710a3675d431a77d69f94deca337467ad923e7 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Mon, 2 Sep 2024 17:40:48 -0300 Subject: [PATCH 18/57] . --- docs/source/conf.py | 2 +- docs/source/useful_information/development/index.rst | 4 ++-- docs/table_documentation.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 7b1b71c23..f1581f1c0 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -87,7 +87,7 @@ "examples/other_applications", ] ), - "plot_gallery": "False", + # "plot_gallery": "False", } # Add any paths that contain templates here, relative to this directory. diff --git a/docs/source/useful_information/development/index.rst b/docs/source/useful_information/development/index.rst index bc8631b98..843218de9 100644 --- a/docs/source/useful_information/development/index.rst +++ b/docs/source/useful_information/development/index.rst @@ -8,6 +8,6 @@ Developing This section describes how to contribute to AequilibraE's development and what is our current roadmap. -.. include:: development/softwaredevelopment.rst +.. include:: softwaredevelopment.rst -.. include:: development/roadmap.rst +.. include:: roadmap.rst diff --git a/docs/table_documentation.py b/docs/table_documentation.py index ebe42dc8d..6730f8563 100644 --- a/docs/table_documentation.py +++ b/docs/table_documentation.py @@ -135,8 +135,8 @@ def readlines(filename): tables = [ - ("project_database", "modeling_with_aequilibrae/project_database"), - ("transit_database", "modeling_with_aequilibrae/transit_database"), + ("project_database", "project_database"), + ("transit_database", "transit_database"), ] for table, pth in tables: From 949c441e47c6291a03503d4cffdb3349f7f1e2e2 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Tue, 3 Sep 2024 09:39:18 -0300 Subject: [PATCH 19/57] . --- .../1-aequilibrae_project/index.rst | 1 - .../3-transit_assignment/hyperpath_routing.rst | 8 -------- .../3-transit_assignment/index.rst | 17 +++++++++++++---- .../3-transit_assignment/transit_graph.rst | 12 ++++-------- .../4-route_choice/route_choice_model.rst | 5 +---- 5 files changed, 18 insertions(+), 25 deletions(-) diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/index.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/index.rst index 6ad15bbc5..77ca24d84 100644 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/index.rst +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/index.rst @@ -65,7 +65,6 @@ series of files and sub folders exist, and the current project organization is as follows: .. image:: ../../images/project_structure.png - :width: 700 :alt: AequilibraE project structure | diff --git a/docs/source/modeling_with_aequilibrae/3-transit_assignment/hyperpath_routing.rst b/docs/source/modeling_with_aequilibrae/3-transit_assignment/hyperpath_routing.rst index 61a7262e1..30ba62b32 100644 --- a/docs/source/modeling_with_aequilibrae/3-transit_assignment/hyperpath_routing.rst +++ b/docs/source/modeling_with_aequilibrae/3-transit_assignment/hyperpath_routing.rst @@ -1,14 +1,6 @@ Hyperpath routing in the context of transit assignment ====================================================== -How do transit passengers choose their routes in a complex network of -lines and services? How can we estimate the distribution of passenger -flows and the performance of transit systems? These are some of the -questions that transit assignment models aim to answer. Transit -assignment models are mathematical tools that predict how passengers -behave and travel in a transit network, given some assumptions and -inputs. - One of the basic concepts in transit assignment models is hyperpath routing. Hyperpath routing is a way of representing the set of optimal routes that a passenger can take from an origin to a destination, based diff --git a/docs/source/modeling_with_aequilibrae/3-transit_assignment/index.rst b/docs/source/modeling_with_aequilibrae/3-transit_assignment/index.rst index 3599e9176..a9ada09db 100644 --- a/docs/source/modeling_with_aequilibrae/3-transit_assignment/index.rst +++ b/docs/source/modeling_with_aequilibrae/3-transit_assignment/index.rst @@ -3,11 +3,20 @@ Transit assignment ================== -.. we should add here a brief introduction about transit assignment +Transit assignment models are mathematical tools that predict how passengers +behave and travel in a transit network, given some assumptions and inputs. + +Transit assignment models aim to answer questions such as: + +- *How do transit passengers choose their routes in a complex network of lines and services?* +- *How can we estimate the distribution of passenger flows and the performance of transit systems?* + +In the following sections, we'll present the ``TransitGraph``, the graph structure used for a transit +network, and the hyperpath routing for transit assignment. .. toctree:: :maxdepth: 1 - :caption: Transit Assignment + :caption: Transit assignment - hyperpath_routing.rst - transit_graph.rst \ No newline at end of file + transit_graph.rst + hyperpath_routing.rst \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/3-transit_assignment/transit_graph.rst b/docs/source/modeling_with_aequilibrae/3-transit_assignment/transit_graph.rst index 46845516c..75e15ab68 100644 --- a/docs/source/modeling_with_aequilibrae/3-transit_assignment/transit_graph.rst +++ b/docs/source/modeling_with_aequilibrae/3-transit_assignment/transit_graph.rst @@ -11,12 +11,10 @@ Let’s start by giving a few definitions: - *transit* definition from `Wikipedia `_: -.. - - system of transport for passengers by group travel systems available - for use by the general public unlike private transport, typically - managed on a schedule, operated on established routes, and that - charge a posted fee for each trip. + system of transport for passengers by group travel systems available + for use by the general public unlike private transport, typically + managed on a schedule, operated on established routes, and that + charge a posted fee for each trip. - *transit network*: a set of transit lines and stops, where passengers can board, alight or change vehicles. @@ -478,8 +476,6 @@ and vertex types: od 517 =========== ===== -References ----------- .. [1] Spiess, H., Florian, M. (1989) "Optimal strategies: A new assignment model for transit networks". Transportation Research Part B: Methodological, 23(2), 83-102. diff --git a/docs/source/modeling_with_aequilibrae/4-route_choice/route_choice_model.rst b/docs/source/modeling_with_aequilibrae/4-route_choice/route_choice_model.rst index 2cdfe594c..809bc1afc 100644 --- a/docs/source/modeling_with_aequilibrae/4-route_choice/route_choice_model.rst +++ b/docs/source/modeling_with_aequilibrae/4-route_choice/route_choice_model.rst @@ -27,7 +27,7 @@ route :math:`i`, :math:`A_i` is the link set and :math:`R` is the route choice s :math:`j` suppressed for readability). The path overlap correction factor :math:`\gamma` can be theoretically derived by aggregation of alternatives under certain assumptions, see [3]_ and references therein. -.. note:: +.. important:: **AequilibraE uses cost to compute path overlaps rather than distance** @@ -38,9 +38,6 @@ A binary logit filter is available to remove unfavourable routes from the route assignment. This filters accepts a numerical parameter for the minimum demand share acceptable for any path, which is approximated by the binary logit considering the shortest path and each subsequent path. -References ----------- - .. [1] Ben-Akiva, M., and S. Lerman. Discrete Choice Analysis. The MIT Press, 1985. .. [2] Ramming, M. S. Network Knowledge and Route Choice. Massachusetts Institute of Technology, 2002. From 3236cf744141c5107d77599fbb980a02887c82bd Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Tue, 3 Sep 2024 15:52:14 -0300 Subject: [PATCH 20/57] . --- .../plot_assignment_without_model.py | 3 + ..._from_gmns.py => plot_create_from_gmns.py} | 18 ++- ...rom_layer.py => plot_create_from_layer.py} | 4 +- .../{from_osm.py => plot_create_from_osm.py} | 37 +++--- .../creating_models/plot_create_zoning.py | 35 ++++-- .../creating_models/plot_import_gtfs.py | 1 - docs/source/examples/readme.rst | 2 - .../1-aequilibrae_project/index.rst | 5 +- .../network_import_and_export.rst | 8 +- .../5-aequilibrae_matrix.rst | 113 ++++++++++++++++++ .../modeling_with_aequilibrae/index.rst | 1 + 11 files changed, 180 insertions(+), 47 deletions(-) rename docs/source/examples/creating_models/{plot_import_from_gmns.py => plot_create_from_gmns.py} (90%) rename docs/source/examples/creating_models/{plot_from_layer.py => plot_create_from_layer.py} (98%) rename docs/source/examples/creating_models/{from_osm.py => plot_create_from_osm.py} (88%) create mode 100644 docs/source/modeling_with_aequilibrae/5-aequilibrae_matrix.rst diff --git a/docs/source/examples/aequilibrae_without_a_model/plot_assignment_without_model.py b/docs/source/examples/aequilibrae_without_a_model/plot_assignment_without_model.py index 202ac1ce8..917cff965 100644 --- a/docs/source/examples/aequilibrae_without_a_model/plot_assignment_without_model.py +++ b/docs/source/examples/aequilibrae_without_a_model/plot_assignment_without_model.py @@ -134,3 +134,6 @@ # %% # And at the Assignment report print(assig.report()) + +# %% +# \ No newline at end of file diff --git a/docs/source/examples/creating_models/plot_import_from_gmns.py b/docs/source/examples/creating_models/plot_create_from_gmns.py similarity index 90% rename from docs/source/examples/creating_models/plot_import_from_gmns.py rename to docs/source/examples/creating_models/plot_create_from_gmns.py index 543327496..3af8d332f 100644 --- a/docs/source/examples/creating_models/plot_import_from_gmns.py +++ b/docs/source/examples/creating_models/plot_create_from_gmns.py @@ -1,12 +1,12 @@ """ .. _import_from_gmns: -Importing network from GMNS -=========================== +Create project from GMNS +======================== In this example, we import a simple network in GMNS format. -The source files of this network are publicly available in the GMNS GitHub repository itself. -Here's the repository: https://github.com/zephyr-data-specs/GMNS +The source files of this network are publicly available in the +`GMNS GitHub repository `_ itself. """ # %% @@ -21,12 +21,14 @@ # sphinx_gallery_thumbnail_path = 'images/plot_import_from_gmns.png' # %% + # We load the example file from the GMNS GitHub repository link_file = "https://raw.githubusercontent.com/zephyr-data-specs/GMNS/main/examples/Arlington_Signals/link.csv" node_file = "https://raw.githubusercontent.com/zephyr-data-specs/GMNS/main/examples/Arlington_Signals/node.csv" use_group_file = "https://raw.githubusercontent.com/zephyr-data-specs/GMNS/main/examples/Arlington_Signals/use_group.csv" # %% + # We create the example project inside our temp folder fldr = join(gettempdir(), uuid4().hex) @@ -109,6 +111,7 @@ long, lat = curr.fetchone() # %% + # We create the map map_gmns = folium.Map(location=[lat, long], zoom_start=17) @@ -122,3 +125,10 @@ # %% project.close() + +# %% +# .. seealso:: +# The use of the following functions, methods, classes and modules is shown in this example: +# +# * :func:`aequilibrae.project.Network.create_from_gmns` +# * :ref:`importing_from_gmns_file` \ No newline at end of file diff --git a/docs/source/examples/creating_models/plot_from_layer.py b/docs/source/examples/creating_models/plot_create_from_layer.py similarity index 98% rename from docs/source/examples/creating_models/plot_from_layer.py rename to docs/source/examples/creating_models/plot_create_from_layer.py index 949f656f7..1a5fd3dd9 100644 --- a/docs/source/examples/creating_models/plot_from_layer.py +++ b/docs/source/examples/creating_models/plot_create_from_layer.py @@ -1,8 +1,8 @@ """ .. _project_from_link_layer: -Project from a link layer -========================= +Create project from a link layer +================================ In this example, we show how to create an empty project and populate it with a network coming from a link layer we load from a text file. It can easily be diff --git a/docs/source/examples/creating_models/from_osm.py b/docs/source/examples/creating_models/plot_create_from_osm.py similarity index 88% rename from docs/source/examples/creating_models/from_osm.py rename to docs/source/examples/creating_models/plot_create_from_osm.py index 18bac5016..394c379ec 100644 --- a/docs/source/examples/creating_models/from_osm.py +++ b/docs/source/examples/creating_models/plot_create_from_osm.py @@ -1,8 +1,8 @@ """ .. _plot_from_osm: -Project from OpenStreetMap -============================= +Create project from OpenStreetMap +================================= In this example, we show how to create an empty project and populate it with a network from OpenStreetMap. @@ -19,41 +19,33 @@ import folium # sphinx_gallery_thumbnail_path = 'images/nauru.png' -#%% -# We create an empty project on an arbitrary folder - # %% +# We create an empty project on an arbitrary folder fldr = join(gettempdir(), uuid4().hex) project = Project() project.new(fldr) -#%% +# %% # Now we can download the network from any place in the world (as long as you have memory for all the download # and data wrangling that will be done). -#%% +# %% # We can create from a bounding box or a named place. # For the sake of this example, we will choose the small nation of Nauru. - -# %% project.network.create_from_osm(place_name="Nauru") -#%% -# We can also choose to create a model from a polygon (which must be in EPSG:4326) +# %% +# We can also choose to create a model from a polygon (which must be in `EPSG:4326`) # Or from a Polygon defined by a bounding box, for example. # project.network.create_from_osm(model_area=box(-112.185, 36.59, -112.179, 36.60)) -#%% -# We grab all the links data as a Pandas DataFrame so we can process it easier - # %% +# We grab all the links data as a Pandas DataFrame so we can process it easier links = project.network.links.data -#%% -# We create a Folium layer - # %% +# We create a Folium layer network_links = folium.FeatureGroup("links") # %% @@ -69,10 +61,8 @@ points, popup=f"link_id: {row.link_id}", tooltip=f"{row.link_type}", color="blue", weight=10 ).add_to(network_links) -#%% -# We get the center of the region we are working with some SQL magic - # %% +# We get the center of the region we are working with some SQL magic curr = project.conn.cursor() curr.execute("select avg(xmin), avg(ymin) from idx_links_geometry") long, lat = curr.fetchone() @@ -85,3 +75,10 @@ # %% project.close() + +# %% +# .. seealso:: +# The use of the following functions, methods, classes and modules is shown in this example: +# +# * :func:`aequilibrae.project.Network.create_from_osm` +# * :ref:`importing_from_osm` diff --git a/docs/source/examples/creating_models/plot_create_zoning.py b/docs/source/examples/creating_models/plot_create_zoning.py index 47820d28a..b9307c7b2 100644 --- a/docs/source/examples/creating_models/plot_create_zoning.py +++ b/docs/source/examples/creating_models/plot_create_zoning.py @@ -1,8 +1,8 @@ """ .. _create_zones: -Creating a zone system based on Hex Bins -======================================== +Create a zone system based on Hex Bins +====================================== In this example, we show how to create hex bin zones covering an arbitrary area. @@ -14,7 +14,6 @@ part of this notebook. We also add centroid connectors to our network to make it a pretty complete example - """ # %% @@ -30,6 +29,7 @@ # sphinx_gallery_thumbnail_path = "images/plot_create_zoning.png" # %% + # We create an empty project on an arbitrary folder fldr = join(gettempdir(), uuid4().hex) @@ -43,7 +43,7 @@ # We said we wanted 100 zones zones = 100 -#%% +# %% # Hex Bins using Spatialite # ------------------------- @@ -62,8 +62,8 @@ zone_area = geo.area / zones # %% -# Since the area of the hexagon is **3 * sqrt(3) * side^2 / 2** -# is side is equal to **sqrt(2 * sqrt(3) * A/9)** +# Since the area of the hexagon is :math:`\frac{3\sqrt{3}}{2} * side^{2}` +# the side is equal to :math:`\sqrt{\frac{2\sqrt{3} * area}{9}}` zone_side = sqrt(2 * sqrt(3) * zone_area / 9) # %% @@ -90,15 +90,14 @@ # %% # Let's re-number all nodes with IDs smaller than 300 to something bigger as to free space to our centroids to go from 1 -# to N +# to N. nodes = network.nodes for i in range(1, 301): nd = nodes.get(i) nd.renumber(i + 1300) # %% -# Now we can add them to the model -# And add centroids to them while we are at it +# Now we can add them to the model and add centroids to them while we are at it. zoning = project.zoning for i, zone_geo in enumerate(grid): zone = zoning.new(i + 1) @@ -108,8 +107,7 @@ # But we could provide a Shapely point as an alternative zone.add_centroid(None) - -#%% +# %% # Centroid connectors # ------------------- @@ -127,7 +125,12 @@ break # %% -# Let's add special generator zones +# Special generator zones +# ~~~~~~~~~~~~~~~~~~~~~~~ +# +# Let's add special generator zones! + +# %% # We also add a centroid at the airport terminal nodes = project.network.nodes @@ -145,3 +148,11 @@ # %% project.close() + +# %% +# .. seealso:: +# The use of the following functions, methods, classes and modules is shown in this example: +# +# * :func:`aequilibrae.project.Zone` +# * :func:`aequilibrae.project.network.Node` | :func:`aequilibrae.project.network.Nodes` +# * :ref:`tables_zones` \ No newline at end of file diff --git a/docs/source/examples/creating_models/plot_import_gtfs.py b/docs/source/examples/creating_models/plot_import_gtfs.py index f8b1ff679..3c5234c20 100644 --- a/docs/source/examples/creating_models/plot_import_gtfs.py +++ b/docs/source/examples/creating_models/plot_import_gtfs.py @@ -34,7 +34,6 @@ # %% # As the Coquimbo example already has a complete GTFS model, we shall remove its public transport # database for the sake of this example. - remove(join(fldr, "public_transport.sqlite")) # %% diff --git a/docs/source/examples/readme.rst b/docs/source/examples/readme.rst index 13596a042..1a830a406 100644 --- a/docs/source/examples/readme.rst +++ b/docs/source/examples/readme.rst @@ -1,5 +1,3 @@ -:orphan: - Examples ======== diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/index.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/index.rst index 77ca24d84..edf38943d 100644 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/index.rst +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/index.rst @@ -3,10 +3,6 @@ The AequilibraE project ======================= -.. contents:: **Contents** - :local: - :depth: 1 - Similarly to commercial packages, any AequilibraE project must have a certain structure and follow a certain set of guidelines in order for software to work correctly. @@ -65,6 +61,7 @@ series of files and sub folders exist, and the current project organization is as follows: .. image:: ../../images/project_structure.png + :width: 600 :alt: AequilibraE project structure | diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/network_import_and_export.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/network_import_and_export.rst index 7a97b3f78..22e372345 100644 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/network_import_and_export.rst +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/network_import_and_export.rst @@ -6,8 +6,10 @@ GMNS, and from link layers. AequilibraE can also export the existing network into GMNS format. There is some valuable information on these topics in the following sections. -Importing from Open Street Maps -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. _importing_from_osm: + +Importing from OpenStreetMap +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can check more specifications on OSM download on the :ref:`parameters_file`. @@ -86,6 +88,8 @@ It is possible to create an AequilibraE project from a link layer, such as a \*. contains geometry in WKT, for instance. You can check an example with all functions used in :ref:`the following example `. +.. _importing_from_gmns_file: + Importing from files in GMNS format ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/source/modeling_with_aequilibrae/5-aequilibrae_matrix.rst b/docs/source/modeling_with_aequilibrae/5-aequilibrae_matrix.rst new file mode 100644 index 000000000..8bebb4489 --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/5-aequilibrae_matrix.rst @@ -0,0 +1,113 @@ +AequilibraE Matrix +================== + +``aequilibrae.matrix`` presents different modules that allow you to make the most +with AequilibraE, and in the following sections, we'll cover the main points regarding them. + +``AequilibraeMatrix`` +--------------------- + +This class allows the creation of a memory instance for a matrix, that can be used to load an existing +matrix to the project, or to create a new one. This matrix can be saved into an external file later. + +There are three ways of creating an ``AequilibraeMatrix``: + +* from an OMX file; +* from a trip list, which is nothing more than a CSV file containing the origins, destinations, and trip cores; +* from an empty matrix. In this case, the data type must be one of the following NumPy data types: + ``np.int32``, ``np.int64``, ``np.float32``, ``np.float64``. + +.. code-block:: python + + from aequilibrae.matrix import AequilibraeMatrix + + file = "folder/path_to_my_matrix.aem" + num_zones = 10 + + mat = AequilibraeMatrix() + mat.create_empty(file_name=file, zones=num_zones, memory_only=False) + + # `memory_only` parameter can be changed to `True` case you want to save the matrix in disk. + + # Adds the matrix index, which are XXXXXXXXXXXX + mat.index[:] = index[:] + # Adds the matricial data stored in `mtx` to a matrix named "my_matrix" + mat.matrix["my_matrix"][:,:] = mtx[:,:] + +More than storing project data, AequilibraE matrices are objects necessary to run procedures, +such as traffic assignment. To do so, one must create a ``computational_view`` of the matrix, which +allows AequilibraE matrices to be used in parallelized algorithms. It is possible to create a +``computational_view`` for more than one matrix at a time + +.. code-block:: python + + mat.computational_view(["my_matrix"]) + +.. important:: + + File extension for AequilibraE matrices is **AEM**. + +.. seealso:: + + :func:`aequilibrae.matrix.AequilibraeMatrix` + Documentation for ``AequilibraeMatrix`` class + + :ref:`plot_assignment_without_model` + Usage example + +``AequilibraeData`` +------------------- + +The ``AequilibraeData`` class allows the creation of datasets, which are objects necessary to +run procedures such as traffic distritubion. + +.. code-block:: python + + import numpy as np + from aequilibrae.matrix import AequilibraeData + + num_entries = 10 + + args = {"file_path": "folder/path_to_my_dataset.aed", + "entries": num_entries, + "field_names": ["origins", "destinations"], + "data_types": [np.float64, np.float64], + "memory_mode": True} + + # `memory_mode` parameter can be changed to `False` case you don't want to save the matrix in disk. + + dataset = AequilibraeData() + dataset.create_empty(**args) + + # Adds the dataset indexes + dataset.index[:] = mtx.index[:] + # Adds the dataset field's data + dataset.origins[:] = example_vector_1[:] + dataset.destinations[:] = example_vector_2[:] + +An AequilibraE dataset can be exported to CSV or SQLite formats. Please notice that when setting the ``file_name``, +it must contain one of the following extensions: ``csv``, ``sqlite3``, ``sqlite`` or ``db``, and if using a SQLite +table, the ``table_name`` field is mandatory. + +.. code-block:: python + + dataset.export(file_name="folder/path_to_my_dataset_database.db", + table_name="table_name_to_be_saved") + +It is possible to reuse AequilibraE datasets that were stored in disk in an AED file. + +.. code-block:: python + + dataset.load(file_path="folder/path_to_my_dataset.aed") + +.. important:: + + File extension for AequilibraE datasets is **AED**. + +.. seealso:: + + :func:`aequilibrae.matrix.AequilibraeData` + Documentation for ``AequilibraeData`` class + + :ref:`example_usage_distribution` + Usage example diff --git a/docs/source/modeling_with_aequilibrae/index.rst b/docs/source/modeling_with_aequilibrae/index.rst index 784b11f0f..1c494e5f4 100644 --- a/docs/source/modeling_with_aequilibrae/index.rst +++ b/docs/source/modeling_with_aequilibrae/index.rst @@ -23,3 +23,4 @@ a start guide to a complete view into AequilibraE's data structure. 2-static_traffic_assignment/index 3-transit_assignment/index 4-route_choice/index + 5-aequilibrae_matrix \ No newline at end of file From 12b286c12222863543aabefa8f34431eceeb17a0 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Tue, 3 Sep 2024 17:14:15 -0300 Subject: [PATCH 21/57] adds modules references in examples --- aequilibrae/transit/lib_gtfs.py | 9 ++++++--- docs/source/api/api.rst | 16 +++++++++++++-- docs/source/conf.py | 2 ++ .../plot_assignment_without_model.py | 20 ++++++++++++++++--- .../creating_models/plot_create_from_layer.py | 8 ++++++++ .../creating_models/plot_import_gtfs.py | 6 ++++++ .../visualization/plot_delaunay_lines.py | 8 +++++++- .../assignment_mechanics.rst | 4 ++-- 8 files changed, 62 insertions(+), 11 deletions(-) diff --git a/aequilibrae/transit/lib_gtfs.py b/aequilibrae/transit/lib_gtfs.py index 8c59fc583..502e5fc54 100644 --- a/aequilibrae/transit/lib_gtfs.py +++ b/aequilibrae/transit/lib_gtfs.py @@ -30,9 +30,13 @@ def __init__( :Arguments: **local network** (:obj:`Network`): Supply model to which this GTFS will be imported + **agency_identifier** (:obj:`str`): ID for the agency this feed refers to (e.g. 'CTA') + **file_path** (:obj:`str`): Full path to the GTFS feed (e.g. 'D:/project/my_gtfs_feed.zip') + **day** (:obj:`str`, *Optional*): Service data contained in this field to be imported (e.g. '2019-10-04') + **description** (:obj:`str`, *Optional*): Description for this feed (e.g. 'CTA19 fixed by John after coffee') """ self.__network = network @@ -79,8 +83,7 @@ def set_capacities(self, capacities: dict): :Arguments: **capacities** (:obj:`dict`): Dictionary with GTFS types as keys, each with a list - of 3 items for values for capacities: seated and total - i.e. -> "{0: [150, 300],...}" + of 3 items for values for capacities: seated and total i.e. -> "{0: [150, 300],...}" """ self.gtfs_data._set_capacities(capacities) @@ -89,7 +92,7 @@ def set_pces(self, pces: dict): :Arguments: **pces** (:obj:`dict`): Dictionary with GTFS types as keys and the corresponding PCE - value i.e. -> "{0: 2.0,...}" + value i.e. -> "{0: 2.0,...}" """ self.gtfs_data._set_pces(pces) diff --git a/docs/source/api/api.rst b/docs/source/api/api.rst index bb5ce8702..fd1b8b7c3 100644 --- a/docs/source/api/api.rst +++ b/docs/source/api/api.rst @@ -81,8 +81,11 @@ Matrix .. autosummary:: :toctree: generated/ - AequilibraeData AequilibraeMatrix + AequilibraeData + Sparse + COO + GeneralisedCOODemand Paths ----- @@ -131,4 +134,13 @@ Transit Transit TransitGraphBuilder - .. GTFSRouteSystemBuilder \ No newline at end of file + lib_gtfs.GTFSRouteSystemBuilder + +Utils +----- +.. currentmodule:: aequilibrae.utils +.. autosummary:: + :toctree: generated/ + + create_delaunay_network.DelaunayAnalysis + create_example diff --git a/docs/source/conf.py b/docs/source/conf.py index f1581f1c0..8b400f2da 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -183,6 +183,8 @@ 'autosummary': True } +autodoc_member_order = "groupwise" + autoclass_content = "class" # classes should include both the class' and the __init__ method's docstring autosummary_generate = True diff --git a/docs/source/examples/aequilibrae_without_a_model/plot_assignment_without_model.py b/docs/source/examples/aequilibrae_without_a_model/plot_assignment_without_model.py index 917cff965..004a7d8be 100644 --- a/docs/source/examples/aequilibrae_without_a_model/plot_assignment_without_model.py +++ b/docs/source/examples/aequilibrae_without_a_model/plot_assignment_without_model.py @@ -7,7 +7,6 @@ In this example, we show how to perform Traffic Assignment in AequilibraE without a model. We are using `Sioux Falls data `_, from TNTP. - """ # %% @@ -87,7 +86,7 @@ geom["node_id"] = geom.index + 1 geom = geom.astype({"node_id": "int64", "lon": "float64", "lat": "float64"}).set_index("node_id") -#%% +# %% # Let's build our Graph! In case you're in doubt about AequilibraE Graph, # :ref:`click here ` to read more about it. @@ -106,6 +105,11 @@ g.network["id"] = g.network.link_id g.lonlat_index = geom.loc[g.all_nodes] +# %% +# .. admonition:: References +# +# :ref:`aequilibrae-graphs` + # %% # Let's perform our assignment. Feel free to try different algorithms, # as well as change the maximum number of iterations and the gap. @@ -136,4 +140,14 @@ print(assig.report()) # %% -# \ No newline at end of file +# .. admonition:: References +# +# :ref:`traffic_assignment_procedure` | :ref:`multiclass_equilibrium` + +# %% +# .. seealso:: +# The use of the following functions, methods, classes and modules is shown in this example: +# +# * :func:`aequilibrae.paths.Graph` +# * :func:`aequilibrae.paths.TrafficClass` | :func:`aequilibrae.paths.TrafficAssignment` +# * :func:`aequilibrae.matrix.AequilibraeMatrix` \ No newline at end of file diff --git a/docs/source/examples/creating_models/plot_create_from_layer.py b/docs/source/examples/creating_models/plot_create_from_layer.py index 1a5fd3dd9..0b1244a6c 100644 --- a/docs/source/examples/creating_models/plot_create_from_layer.py +++ b/docs/source/examples/creating_models/plot_create_from_layer.py @@ -166,3 +166,11 @@ # %% project.close() + +# %% +# .. seealso:: +# The use of the following functions, methods, classes and modules is shown in this example: +# +# * :func:`aequilibrae.project.network.Link` | :func:`aequilibrae.project.network.Links` +# * :func:`aequilibrae.project.network.Node` | :func:`aequilibrae.project.network.Nodes` +# * :func:`aequilibrae.project.network.Mode` | :func:`aequilibrae.project.network.LinkType` \ No newline at end of file diff --git a/docs/source/examples/creating_models/plot_import_gtfs.py b/docs/source/examples/creating_models/plot_import_gtfs.py index 3c5234c20..647c51799 100644 --- a/docs/source/examples/creating_models/plot_import_gtfs.py +++ b/docs/source/examples/creating_models/plot_import_gtfs.py @@ -122,3 +122,9 @@ # %% project.close() + +# %% +# .. seealso:: +# The use of the following functions, methods, classes and modules is shown in this example: +# +# * :func:`aequilibrae.transit.Transit` | :func:`aequilibrae.transit.lib_gtfs.GTFSRouteSystemBuilder` \ No newline at end of file diff --git a/docs/source/examples/visualization/plot_delaunay_lines.py b/docs/source/examples/visualization/plot_delaunay_lines.py index fc13b6cd3..29f2b35ea 100644 --- a/docs/source/examples/visualization/plot_delaunay_lines.py +++ b/docs/source/examples/visualization/plot_delaunay_lines.py @@ -6,7 +6,7 @@ In this example, we show how to create AequilibraE's famous Delaunay Lines, but in Python. -For more on this topic, the first publication is `here `_. +For more on this topic, see its `first publication `_. We use the Sioux Falls example once again. """ @@ -70,3 +70,9 @@ # %% # Close the project project.close() + +# %% +# .. seealso:: +# The use of the following functions, methods, classes and modules is shown in this example: +# +# * :func:`aequilibrae.utils.create_delaunay_network.DelaunayAnalysis` diff --git a/docs/source/modeling_with_aequilibrae/2-static_traffic_assignment/assignment_mechanics.rst b/docs/source/modeling_with_aequilibrae/2-static_traffic_assignment/assignment_mechanics.rst index c57911820..3c6d31f4a 100644 --- a/docs/source/modeling_with_aequilibrae/2-static_traffic_assignment/assignment_mechanics.rst +++ b/docs/source/modeling_with_aequilibrae/2-static_traffic_assignment/assignment_mechanics.rst @@ -130,6 +130,8 @@ not blocking flows through "centroids". :func:`aequilibrae.paths.TransitGraph` +.. _traffic_assignment_procedure: + Traffic Assignment Procedure ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -364,8 +366,6 @@ Finally, one can execute assignment: :ref:`convergence_criteria` is discussed in a different section. -References -~~~~~~~~~~ .. [1] Hampton Roads Transportation Planning Organization, Regional Travel Demand Model V2 (2020). Available in: https://www.hrtpo.org/uploads/docs/2020_HamptonRoads_Modelv2_MethodologyReport.pdf From e8bc29842a2b17ce001625f441e990c4bc327930 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Tue, 3 Sep 2024 17:44:06 -0300 Subject: [PATCH 22/57] . --- .../assignment_workflows/plot_forecasting.py | 26 ++++++++++++------- .../plot_public_transit_assignment.py | 22 ++++++++++++++-- .../plot_route_choice_basics.py | 14 ++++++++++ .../plot_route_choice_set.py | 12 +++++++++ .../plot_subarea_analysis.py | 12 +++++++++ .../hyperpath_routing.rst | 2 ++ .../3-transit_assignment/transit_graph.rst | 2 ++ 7 files changed, 79 insertions(+), 11 deletions(-) diff --git a/docs/source/examples/assignment_workflows/plot_forecasting.py b/docs/source/examples/assignment_workflows/plot_forecasting.py index 2a428d0fb..845fede11 100644 --- a/docs/source/examples/assignment_workflows/plot_forecasting.py +++ b/docs/source/examples/assignment_workflows/plot_forecasting.py @@ -18,14 +18,14 @@ import sys # %% + # We create the example project inside our temp folder fldr = join(gettempdir(), uuid4().hex) project = create_example(fldr) logger = project.logger - -#%% +# %% # Traffic assignment with skimming # -------------------------------- @@ -110,7 +110,7 @@ # And save the skims assig.save_skims("base_year_assignment_skims", which_ones="all", format="omx") -#%% +# %% # Trip distribution # ----------------- # Calibration @@ -137,7 +137,7 @@ # We can check which matrix cores were created for our skims to decide which one to use imped.names -#%% +# %% # Where ``free_flow_time_final`` is actually the congested time for the last iteration # %% @@ -185,7 +185,7 @@ for r in gc.report: otp.write(r + "\n") -#%% +# %% # Forecast # -------- # We create a set of 'future' vectors using some random growth factors. @@ -219,7 +219,7 @@ vectors.destinations[:] = destinations * (1 + np.random.rand(vectors.entries) / 10) vectors.destinations *= vectors.origins.sum() / vectors.destinations.sum() -#%% +# %% # Impedance # ~~~~~~~~~ @@ -259,7 +259,7 @@ proj_matrices.update_database() print(proj_matrices.list()) -#%% +# %% # IPF for the future vectors # ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -282,7 +282,7 @@ # %% df = proj_matrices.list() -#%% +# %% # Future traffic assignment # ------------------------- @@ -326,7 +326,7 @@ assig.max_iter = 500 assig.rgap_target = 0.00001 -#%% +# %% # Optional: Select link analysis # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # @@ -393,3 +393,11 @@ # %% # Close the project project.close() + +# %% +# .. seealso:: +# The use of the following functions, methods, classes and modules is shown in this example: +# +# * :func:`aequilibrae.paths.Graph` +# * :func:`aequilibrae.paths.TrafficClass` | :func:`aequilibrae.paths.TrafficAssignment` +# * :func:`aequilibrae.matrix.AequilibraeMatrix` \ No newline at end of file diff --git a/docs/source/examples/assignment_workflows/plot_public_transit_assignment.py b/docs/source/examples/assignment_workflows/plot_public_transit_assignment.py index df3903c35..2362cbb58 100644 --- a/docs/source/examples/assignment_workflows/plot_public_transit_assignment.py +++ b/docs/source/examples/assignment_workflows/plot_public_transit_assignment.py @@ -60,7 +60,7 @@ # Let's save this model for later use. transit.save_to_disk() -#%% +# %% # Graph building # -------------- # Let's build the transit network. We'll disable ``outer_stop_transfers`` and ``walking_edges`` @@ -131,6 +131,11 @@ # To perform an assignment we need to convert the graph builder into a graph. transit_graph = graph.to_transit_graph() +# %% +# .. admonition:: References +# +# :ref:`transit_assignment_graph` + # %% # Spiess & Florian assignment # --------------------------- @@ -155,7 +160,7 @@ mat.matrices[:, :, 0] = np.full((zones_in_the_model, zones_in_the_model), 1.0) mat.computational_view() -#%% +# %% # Hyperpath generation/assignment # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # We'll create a `TransitAssignment` object as well as a `TransitClass` @@ -200,3 +205,16 @@ # %% # Wrapping up project.close() + +# %% +# .. admonition:: References +# +# :ref:`transit_hyperpath_routing` + +# %% +# .. seealso:: +# The use of the following functions, methods, classes and modules is shown in this example: +# +# * :func:`aequilibrae.paths.TransitGraphBuilder` +# * :func:`aequilibrae.paths.TransitClass` | :func:`aequilibrae.paths.TransitAssignment` +# * :func:`aequilibrae.matrix.AequilibraeMatrix` \ No newline at end of file diff --git a/docs/source/examples/assignment_workflows/plot_route_choice_basics.py b/docs/source/examples/assignment_workflows/plot_route_choice_basics.py index 7ee5b40ea..15ff6221b 100644 --- a/docs/source/examples/assignment_workflows/plot_route_choice_basics.py +++ b/docs/source/examples/assignment_workflows/plot_route_choice_basics.py @@ -9,6 +9,7 @@ """ # %% + # Imports from uuid import uuid4 from tempfile import gettempdir @@ -18,6 +19,7 @@ # sphinx_gallery_thumbnail_path = 'images/plot_route_choice_assignment.png' # %% + # We create the example project inside our temp folder fldr = join(gettempdir(), uuid4().hex) @@ -251,3 +253,15 @@ def plot_results(link_loads): # %% project.close() + +# %% +# .. admonition:: References +# +# :ref:`route_choice` + +# %% +# .. seealso:: +# The use of the following functions, methods, classes and modules is shown in this example: +# +# * :func:`aequilibrae.paths.Graph` | :func:`aequilibrae.paths.RouteChoice` +# * :func:`aequilibrae.matrix.AequilibraeMatrix` \ No newline at end of file diff --git a/docs/source/examples/assignment_workflows/plot_route_choice_set.py b/docs/source/examples/assignment_workflows/plot_route_choice_set.py index 0448ede93..dae299ad8 100644 --- a/docs/source/examples/assignment_workflows/plot_route_choice_set.py +++ b/docs/source/examples/assignment_workflows/plot_route_choice_set.py @@ -162,3 +162,15 @@ # %% project.close() + +# %% +# .. admonition:: References +# +# :ref:`route_choice` + +# %% +# .. seealso:: +# The use of the following functions, methods, classes and modules is shown in this example: +# +# * :func:`aequilibrae.paths.Graph` | :func:`aequilibrae.paths.RouteChoice` +# * :func:`aequilibrae.matrix.AequilibraeMatrix` \ No newline at end of file diff --git a/docs/source/examples/assignment_workflows/plot_subarea_analysis.py b/docs/source/examples/assignment_workflows/plot_subarea_analysis.py index 136e0ba1d..5b7e27169 100644 --- a/docs/source/examples/assignment_workflows/plot_subarea_analysis.py +++ b/docs/source/examples/assignment_workflows/plot_subarea_analysis.py @@ -404,3 +404,15 @@ def plot_results(link_loads): map = plot_results(rc.get_load_results()["demand"]) subarea_zone.add_to(map) map + +# %% +# .. admonition:: References +# +# :ref:`route_choice` + +# %% +# .. seealso:: +# The use of the following functions, methods, classes and modules is shown in this example: +# +# * :func:`aequilibrae.paths.Graph` | :func:`aequilibrae.paths.RouteChoice` | :func:`aequilibrae.paths.SubAreaAnalysis` +# * :func:`aequilibrae.matrix.AequilibraeMatrix` \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/3-transit_assignment/hyperpath_routing.rst b/docs/source/modeling_with_aequilibrae/3-transit_assignment/hyperpath_routing.rst index 30ba62b32..61738483c 100644 --- a/docs/source/modeling_with_aequilibrae/3-transit_assignment/hyperpath_routing.rst +++ b/docs/source/modeling_with_aequilibrae/3-transit_assignment/hyperpath_routing.rst @@ -1,3 +1,5 @@ +.. _transit_hyperpath_routing: + Hyperpath routing in the context of transit assignment ====================================================== diff --git a/docs/source/modeling_with_aequilibrae/3-transit_assignment/transit_graph.rst b/docs/source/modeling_with_aequilibrae/3-transit_assignment/transit_graph.rst index 75e15ab68..dd02ec745 100644 --- a/docs/source/modeling_with_aequilibrae/3-transit_assignment/transit_graph.rst +++ b/docs/source/modeling_with_aequilibrae/3-transit_assignment/transit_graph.rst @@ -1,3 +1,5 @@ +.. _transit_assignment_graph: + The Transit assignment graph ============================ From 7ab7bfcc82fcbd69c98438db8f7765a6bc24e73d Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Thu, 5 Sep 2024 11:40:52 -0300 Subject: [PATCH 23/57] adds new sections in docs --- aequilibrae/matrix/aequilibrae_matrix.py | 23 ++-- aequilibrae/project/network/links.py | 20 ++-- aequilibrae/project/network/nodes.py | 14 +-- aequilibrae/project/network/periods.py | 2 +- aequilibrae/project/zoning.py | 6 +- docs/source/api/api.rst | 19 ++- .../images/project_components_and_items.png | Bin 0 -> 73691 bytes .../5-aequilibrae_matrix.rst | 113 ++++++++++-------- .../6-accessing-project-data.rst | 46 +++++++ .../7-project_components.rst | 6 + .../modeling_with_aequilibrae/index.rst | 4 +- 11 files changed, 161 insertions(+), 92 deletions(-) create mode 100644 docs/source/images/project_components_and_items.png create mode 100644 docs/source/modeling_with_aequilibrae/6-accessing-project-data.rst create mode 100644 docs/source/modeling_with_aequilibrae/7-project_components.rst diff --git a/aequilibrae/matrix/aequilibrae_matrix.py b/aequilibrae/matrix/aequilibrae_matrix.py index d2386811f..c7719ab28 100644 --- a/aequilibrae/matrix/aequilibrae_matrix.py +++ b/aequilibrae/matrix/aequilibrae_matrix.py @@ -93,6 +93,8 @@ def save(self, names=(), file_name=None) -> None: :Arguments: **names** (:obj:`tuple(str)`, *Optional*): New names for the matrices. Required if working with OMX files + + **file_name** (:obj:`str`, *Optional*): Local path to the matrix file """ if file_name is not None: cores = names if len(names) else self.names @@ -290,24 +292,25 @@ def create_from_omx( memory_only: bool = True, ) -> None: """ - Creates an AequilibraeMatrix from an original OpenMatrix + Creates an AequilibraeMatrix from an original Open Matrix :Arguments: - **file_path** (:obj:`str`): Path for the output AequilibraEMatrix + **file_path** (:obj:`str`): Path for the output AequilibraeMatrix **omx_path** (:obj:`str`): Path to the OMX file one wants to import - **cores** (:obj:`list`): List of matrix cores to be imported + **cores** (:obj:`list`, *Optional*): List of matrix cores to be imported - **mappings** (:obj:`list`): List of the matrix mappings (i.e. indices, centroid numbers) to be imported + **mappings** (:obj:`list`, *Optional*): List of the matrix mappings (i.e. indices, centroid numbers) + to be imported - **robust** (:obj:`bool`, *Optional*): Boolean for whether AequilibraE should try to adjust the names for cores - and indices in case they are too long. Defaults to True + **robust** (:obj:`bool`, *Optional*): Boolean for whether AequilibraE should try to adjust the names + for cores and indices in case they are too long. Defaults to ``True`` **compressed** (:obj:`bool`, *Optional*): Boolean for whether we should compress the output matrix. Not yet implemented - **memory_only** (:obj:`bool`, *Optional*): Whether you want to keep the matrix copy in memory only. Defaults to True + **memory_only** (:obj:`bool`, *Optional*): Whether you want to keep the matrix copy in memory only. Defaults to ``True`` """ @@ -840,7 +843,7 @@ def load(self, file_path: str): self.__load_aem__() def is_omx(self): - """Returns ``True`` if matrix data source is OMX, False otherwise""" + """Returns ``True`` if matrix data source is OMX, ``False`` otherwise""" return self.__omx def computational_view(self, core_list: List[str] = None): @@ -920,7 +923,7 @@ def copy( **output_name** (:obj:`str`): Name of the new matrix file. If none is provided, returns a copy in memory only - **cores** (:obj:`list`):List of the matrix cores to be copied + **cores** (:obj:`list`): List of the matrix cores to be copied **names** (:obj:`list`, *Optional*): List with the new names for the cores. Defaults to current names @@ -1044,7 +1047,7 @@ def columns(self) -> np.ndarray: def nan_to_num(self): """ - Converts all NaN values in all cores in the computational view to zeros + Converts all ``NaN`` values in all cores in the computational view to zeros .. code-block:: python diff --git a/aequilibrae/project/network/links.py b/aequilibrae/project/network/links.py index 23558743d..bf92849bb 100644 --- a/aequilibrae/project/network/links.py +++ b/aequilibrae/project/network/links.py @@ -46,12 +46,12 @@ def __init__(self, net): self.refresh_fields() def get(self, link_id: int) -> Link: - """Get a link from the network by its *link_id* + """Get a link from the network by its ``link_id`` - It raises an error if link_id does not exist + It raises an error if ``link_id`` does not exist :Arguments: - **link_id** (:obj:`int`): Id of a link to retrieve + **link_id** (:obj:`int`): ID of a link to retrieve :Returns: **link** (:obj:`Link`): Link object for requested link_id @@ -71,7 +71,7 @@ def new(self) -> Link: """Creates a new link :Returns: - **link** (:obj:`Link`): A new link object populated only with link_id (not saved in the model yet) + **link** (:obj:`Link`): A new link object populated only with ``link_id`` (not saved in the model yet) """ data = {key: None for key in self.__fields} @@ -86,13 +86,13 @@ def new(self) -> Link: def copy_link(self, link_id: int) -> Link: """Creates a copy of a link with a new id - It raises an error if link_id does not exist + It raises an error if ``link_id`` does not exist :Arguments: - **link_id** (:obj:`int`): Id of the link to copy + **link_id** (:obj:`int`): ID of the link to copy :Returns: - **link** (:obj:`Link`): Link object for requested link_id + **link** (:obj:`Link`): Link object for requested ``link_id`` """ data = self.__link_data(int(link_id)) @@ -108,10 +108,10 @@ def copy_link(self, link_id: int) -> Link: return link def delete(self, link_id: int) -> None: - """Removes the link with **link_id** from the project + """Removes the link with ``link_id`` from the project :Arguments: - **link_id** (:obj:`int`): Id of a link to delete + **link_id** (:obj:`int`): ID of a link to delete """ d = 1 link_id = int(link_id) @@ -140,7 +140,7 @@ def data(self) -> pd.DataFrame: """Returns all links data as a Pandas DataFrame :Returns: - **table** (:obj:`DataFrame`): Pandas dataframe with all the links, complete with Geometry + **table** (:obj:`DataFrame`): Pandas DataFrame with all the links, complete with Geometry """ dl = DataLoader(self.project.path_to_file, "links") return dl.load_table() diff --git a/aequilibrae/project/network/nodes.py b/aequilibrae/project/network/nodes.py index 9fcae40ec..087aeee48 100644 --- a/aequilibrae/project/network/nodes.py +++ b/aequilibrae/project/network/nodes.py @@ -12,7 +12,7 @@ class Nodes(BasicTable): """ - Access to the API resources to manipulate the links table in the network + Access to the API resources to manipulate the nodes table in the network .. code-block:: python @@ -42,15 +42,15 @@ def __init__(self, net): self.refresh_fields() def get(self, node_id: int) -> Node: - """Get a node from the network by its **node_id** + """Get a node from the network by its ``node_id`` - It raises an error if node_id does not exist + It raises an error if ``node_id`` does not exist :Arguments: - **node_id** (:obj:`int`): Id of a node to retrieve + **node_id** (:obj:`int`): ID of a node to retrieve :Returns: - **node** (:obj:`Node`): Node object for requested node_id + **node** (:obj:`Node`): Node object for requested ``node_id`` """ if node_id in self.__items: @@ -91,7 +91,7 @@ def new_centroid(self, node_id: int) -> Node: """Creates a new centroid with a given ID :Arguments: - **node_id** (:obj:`int`): Id of the centroid to be created + **node_id** (:obj:`int`): ID of the centroid to be created """ with commit_and_close(connect_spatialite(self.project.path_to_file)) as conn: @@ -122,7 +122,7 @@ def data(self) -> pd.DataFrame: @property def lonlat(self) -> pd.DataFrame: - """Returns all nodes lon/lat coords as a Pandas DataFrame + """Returns all nodes lon/lat coordinates as a Pandas DataFrame :Returns: **table** (:obj:`DataFrame`): Pandas DataFrame with all the nodes, with geometry as lon/lat diff --git a/aequilibrae/project/network/periods.py b/aequilibrae/project/network/periods.py index 9cabd9bef..bd251c51e 100644 --- a/aequilibrae/project/network/periods.py +++ b/aequilibrae/project/network/periods.py @@ -11,7 +11,7 @@ class Periods(BasicTable): - """Access to the API resources to manipulate the links table in the network + """Access to the API resources to manipulate the periods table in the network .. code-block:: python diff --git a/aequilibrae/project/zoning.py b/aequilibrae/project/zoning.py index f50167be8..948433649 100644 --- a/aequilibrae/project/zoning.py +++ b/aequilibrae/project/zoning.py @@ -59,7 +59,7 @@ def new(self, zone_id: int) -> Zone: """Creates a new zone :Returns: - **zone** (:obj:`Zone`): A new zone object populated only with zone_id (but not saved in the model yet) + **zone** (:obj:`Zone`): A new zone object populated only with ``zone_id`` (but not saved in the model yet) """ if zone_id in self.__items: @@ -94,13 +94,13 @@ def coverage(self) -> Polygon: return unary_union(polygons) def get(self, zone_id: str) -> Zone: - """Get a zone from the model by its **zone_id**""" + """Get a zone from the model by its ``zone_id``""" if zone_id not in self.__items: raise ValueError(f"Zone {zone_id} does not exist in the model") return self.__items[zone_id] def all_zones(self) -> dict: - """Returns a dictionary with all Zone objects available in the model. *zone_id* as key""" + """Returns a dictionary with all Zone objects available in the model, using ``zone_id`` as key""" return self.__items def save(self): diff --git a/docs/source/api/api.rst b/docs/source/api/api.rst index fd1b8b7c3..4c4c9d0dc 100644 --- a/docs/source/api/api.rst +++ b/docs/source/api/api.rst @@ -56,14 +56,6 @@ Network Items Node Period -Parameters ----------- -.. currentmodule:: aequilibrae -.. autosummary:: - :toctree: generated/ - - Parameters - Distribution ------------ .. currentmodule:: aequilibrae.distribution @@ -83,9 +75,6 @@ Matrix AequilibraeMatrix AequilibraeData - Sparse - COO - GeneralisedCOODemand Paths ----- @@ -144,3 +133,11 @@ Utils create_delaunay_network.DelaunayAnalysis create_example + +Parameters +---------- +.. currentmodule:: aequilibrae +.. autosummary:: + :toctree: generated/ + + Parameters diff --git a/docs/source/images/project_components_and_items.png b/docs/source/images/project_components_and_items.png new file mode 100644 index 0000000000000000000000000000000000000000..af9d70e1ee13bc4e04f6373624a2fdee60d3d60c GIT binary patch literal 73691 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 literal 0 HcmV?d00001 diff --git a/docs/source/modeling_with_aequilibrae/5-aequilibrae_matrix.rst b/docs/source/modeling_with_aequilibrae/5-aequilibrae_matrix.rst index 8bebb4489..94d888dcf 100644 --- a/docs/source/modeling_with_aequilibrae/5-aequilibrae_matrix.rst +++ b/docs/source/modeling_with_aequilibrae/5-aequilibrae_matrix.rst @@ -1,14 +1,14 @@ AequilibraE Matrix ================== -``aequilibrae.matrix`` presents different modules that allow you to make the most -with AequilibraE, and in the following sections, we'll cover the main points regarding them. +AequilibraE matrices are very useful objects that allow you to make the most with AequilibraE. +In the following sections, we'll cover the main points regarding them. ``AequilibraeMatrix`` --------------------- This class allows the creation of a memory instance for a matrix, that can be used to load an existing -matrix to the project, or to create a new one. This matrix can be saved into an external file later. +matrix to the project, or to create a new one. There are three ways of creating an ``AequilibraeMatrix``: @@ -29,85 +29,100 @@ There are three ways of creating an ``AequilibraeMatrix``: # `memory_only` parameter can be changed to `True` case you want to save the matrix in disk. - # Adds the matrix index, which are XXXXXXXXXXXX + # Adds the matrix indexes, which are going to be used for computation mat.index[:] = index[:] # Adds the matricial data stored in `mtx` to a matrix named "my_matrix" mat.matrix["my_matrix"][:,:] = mtx[:,:] -More than storing project data, AequilibraE matrices are objects necessary to run procedures, -such as traffic assignment. To do so, one must create a ``computational_view`` of the matrix, which -allows AequilibraE matrices to be used in parallelized algorithms. It is possible to create a -``computational_view`` for more than one matrix at a time +Creating a matrix from an OMX file is also straightforward. .. code-block:: python - mat.computational_view(["my_matrix"]) + mat = AequilibraeMatrix() + mat.create_from_omx(file_path, omx_path) -.. important:: +The following methods allow you to check the data in you AequilibraE matrix. - File extension for AequilibraE matrices is **AEM**. +.. code-block:: python -.. seealso:: + mat.cores # displays the number of cores in the matrix + mat.names # displays the names of the matrices + mat.index # displays the IDs of the indexes + + mat.get_matrix_name("name_of_a_matrix") # returns an array with the selected matrix data - :func:`aequilibrae.matrix.AequilibraeMatrix` - Documentation for ``AequilibraeMatrix`` class +More than storing project data, AequilibraE matrices are objects necessary to run procedures, +such as traffic assignment. To do so, one must create a computational view of the matrix, which +allows AequilibraE matrices to be used in parallelized algorithms. It is possible to create a +computational view for more than one matrix at a time. - :ref:`plot_assignment_without_model` - Usage example +Case you're using matricial data from an OMX file, this step is mandatory to load the data to memory, +otherwise the matrix is useless in other procedures. -``AequilibraeData`` -------------------- +.. code-block:: python -The ``AequilibraeData`` class allows the creation of datasets, which are objects necessary to -run procedures such as traffic distritubion. + mat.computational_view(["my_matrix"]) -.. code-block:: python +You can also export AequilibraE matrices to another file formats, such as CSV and OMX. When exporting +to a OMX file, you can choose the cores os the matrix you want to save, although this is not the case +for CSV file, in which all cores will be exported as separate columns in the output file. - import numpy as np - from aequilibrae.matrix import AequilibraeData +.. code-block:: python - num_entries = 10 + mat.export('/tmp/my_new_path.omx') + # or + mat.export('/tmp/my_new_path.csv') - args = {"file_path": "folder/path_to_my_dataset.aed", - "entries": num_entries, - "field_names": ["origins", "destinations"], - "data_types": [np.float64, np.float64], - "memory_mode": True} +The ``export`` method also allows you to change your mind and save your AequilibraE matrix into an AEM +file, if it's only in memory. - # `memory_mode` parameter can be changed to `False` case you don't want to save the matrix in disk. +.. code-block:: python - dataset = AequilibraeData() - dataset.create_empty(**args) + mat.export('/tmp/my_new_path.aem') - # Adds the dataset indexes - dataset.index[:] = mtx.index[:] - # Adds the dataset field's data - dataset.origins[:] = example_vector_1[:] - dataset.destinations[:] = example_vector_2[:] +.. is there a better name rather than error? -An AequilibraE dataset can be exported to CSV or SQLite formats. Please notice that when setting the ``file_name``, -it must contain one of the following extensions: ``csv``, ``sqlite3``, ``sqlite`` or ``db``, and if using a SQLite -table, the ``table_name`` field is mandatory. +To avoid errors, once open, the same AequilibraE matrix can only be used once at a time in different +procedures. To do so, you have to close the matrix, to remove it from memory and flush the data to disk, +or to close the OMX file, if that's the case. .. code-block:: python - dataset.export(file_name="folder/path_to_my_dataset_database.db", - table_name="table_name_to_be_saved") + mat.close() -It is possible to reuse AequilibraE datasets that were stored in disk in an AED file. +AequilibraE matrices saved in disk can be reused and loaded once again. .. code-block:: python - dataset.load(file_path="folder/path_to_my_dataset.aed") + mat = AequilibraeMatrix() + mat.load('/tmp/path_to_matrix.aem') .. important:: - File extension for AequilibraE datasets is **AED**. + File extension for AequilibraE matrices is **AEM**. .. seealso:: - :func:`aequilibrae.matrix.AequilibraeData` - Documentation for ``AequilibraeData`` class + :func:`aequilibrae.matrix.AequilibraeMatrix` + Documentation for ``AequilibraeMatrix`` class + + :ref:`plot_assignment_without_model` + Usage example + +Open Matrix (OMX) +----------------- + +AequilibraE can handle OMX files, but if you're wondering what is OMX and what does +it stand for, this section is for you. The text in this section is borrowed from +`Open Matrix Wiki page `_. + +The Open Matrix file format (or simply OMX) is a standard matrix format for storing and +transferring matrix data across different models and software packages, intended to make +the model development easier. It is a file capable of storing more than one matrices +at a time, including multiple indexes/lookups, and attributes (key/value pairs) for matrices and +indexes. - :ref:`example_usage_distribution` - Usage example +There are APIs in different programming languages that allow you to use OMX. In Python, we use +``omx-python`` library. In its project page, you can find a +`brief tutorial `_ +to OMX, and better understand how does it work. diff --git a/docs/source/modeling_with_aequilibrae/6-accessing-project-data.rst b/docs/source/modeling_with_aequilibrae/6-accessing-project-data.rst new file mode 100644 index 000000000..522cafd6f --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/6-accessing-project-data.rst @@ -0,0 +1,46 @@ +Accessing Project Data +====================== + +An AequilibraE project helds geometric information that can be accessed by the user in +three different classes: ``Links``, ``Nodes``, and ``Zoning``. In this section, we'll +cover the main points regarding them. + +``Links`` +--------- + +The ``Links`` class allows the access to the links table in the project network, if one +wants to manipulate it. + +.. code-block:: python + + from tempfile import gettempdir + from aequilibrae.utils.create_example import create_example + + # Let's use Coquimbo as example + project = create_example(gettempdir(), "coquimbo") + + links = project.network.links # access the links table + +The actions + +.. seealso:: + + * :func:`aequilibrae.network.Links` + Class documentation + + * :ref:`project_from_link_layer` + Usage example + +``Nodes`` +--------- + +.. seealso:: + + * :func:`aequilibrae.network.Nodes` + +``Zoning`` +---------- + +.. seealso:: + + * :func:`aequilibrae.network.Zoning` diff --git a/docs/source/modeling_with_aequilibrae/7-project_components.rst b/docs/source/modeling_with_aequilibrae/7-project_components.rst new file mode 100644 index 000000000..77dad1926 --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/7-project_components.rst @@ -0,0 +1,6 @@ +Project Components +================== + +.. image:: ../images/project_components_and_items.png + :align: center + :alt: basics on project components diff --git a/docs/source/modeling_with_aequilibrae/index.rst b/docs/source/modeling_with_aequilibrae/index.rst index 1c494e5f4..96acc7b5d 100644 --- a/docs/source/modeling_with_aequilibrae/index.rst +++ b/docs/source/modeling_with_aequilibrae/index.rst @@ -23,4 +23,6 @@ a start guide to a complete view into AequilibraE's data structure. 2-static_traffic_assignment/index 3-transit_assignment/index 4-route_choice/index - 5-aequilibrae_matrix \ No newline at end of file + 5-aequilibrae_matrix + 6-accessing-project-data + 7-project_components \ No newline at end of file From 118e6bc1f9adf96775c652667d16a4628c12d849 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Thu, 5 Sep 2024 14:23:00 -0300 Subject: [PATCH 24/57] docs --- aequilibrae/project/about.py | 2 +- aequilibrae/project/field_editor.py | 2 +- aequilibrae/project/zoning.py | 2 +- .../1-aequilibrae_project/index.rst | 1 + .../project_database/index.rst | 1 + .../7-project_components.rst | 121 ++++++++++++++++++ 6 files changed, 126 insertions(+), 3 deletions(-) diff --git a/aequilibrae/project/about.py b/aequilibrae/project/about.py index 8ffc8484a..6c9b1ae4e 100644 --- a/aequilibrae/project/about.py +++ b/aequilibrae/project/about.py @@ -8,7 +8,7 @@ class About: - """Provides an interface for querying and editing the **about** table of an AequilibraE project + """Provides an interface for querying and editing the 'about' table of an AequilibraE project .. code-block:: python diff --git a/aequilibrae/project/field_editor.py b/aequilibrae/project/field_editor.py index 488e36c63..81720da72 100644 --- a/aequilibrae/project/field_editor.py +++ b/aequilibrae/project/field_editor.py @@ -63,7 +63,7 @@ def add(self, field_name: str, description: str, data_type="NUMERIC") -> None: **description** (:obj:`str`): Description of the field to be inserted in the metadata - **data_type** (:obj:`str`, *Optional*): Valid SQLite Data type. Default: "NUMERIC" + **data_type** (:obj:`str`, *Optional*): Valid SQLite Data type. Default: ``'NUMERIC'`` """ if field_name.lower() in self._original_values.keys(): raise ValueError("attribute_name already exists") diff --git a/aequilibrae/project/zoning.py b/aequilibrae/project/zoning.py index 948433649..b0a4b88bb 100644 --- a/aequilibrae/project/zoning.py +++ b/aequilibrae/project/zoning.py @@ -20,7 +20,7 @@ class Zoning(BasicTable): """ - Access to the API resources to manipulate the zones table in the project + Access to the API resources to manipulate the 'zones' table in the project .. code-block:: python diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/index.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/index.rst index edf38943d..8dd99123a 100644 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/index.rst +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/index.rst @@ -52,6 +52,7 @@ network editing done in SQLite, Python or QGIS will go through the SpatiaLite tr while any procedure such as traffic assignment done in QGIS is nothing more than an API call to the corresponding Python method. +.. _aeq_project_structure: Project structure ----------------- diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/index.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/index.rst index 963862287..c1036495a 100644 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/index.rst +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/index.rst @@ -1,3 +1,4 @@ +.. _aeq_project_database_tables: Project database ---------------- diff --git a/docs/source/modeling_with_aequilibrae/7-project_components.rst b/docs/source/modeling_with_aequilibrae/7-project_components.rst index 77dad1926..4714aeaa9 100644 --- a/docs/source/modeling_with_aequilibrae/7-project_components.rst +++ b/docs/source/modeling_with_aequilibrae/7-project_components.rst @@ -1,6 +1,127 @@ Project Components ================== +In the :ref:`aeq_project_structure` section, we presented the main file components and folders that +consists an AequilibraE project. We also present in :ref:`aeq_project_database_tables` all tables +that are part of the project database, how do they look like, and what fields do they have. + +The components of an AequilibraE project are: + +* ``project.About`` +* ``project.FieldEditor`` +* ``project.Log`` +* ``project.Matrices`` +* ``project.Network`` +* ``project.Zoning`` + +Network and Zoning are the components that contain the geo-spatial information of the project, such +as links, nodes, and zones, which can also be manipulated. In the Network component, there are also +non-geometric classes related to the project network, such as Modes, LinkTypes, and Periods. + +One important thing to observe is that related to each component in Network and Zoning, there is an +object with similar name that corresponds to one object in the class. Thus the ``project.network.Links`` +enables the access to manipulate the links table, and each item in the items table is a +``project.network.Link``. + .. image:: ../images/project_components_and_items.png :align: center :alt: basics on project components + +In this section, we'll briefly discuss about the project components without geo-spatial information. + +``project.About`` +----------------- + +This class provides an interface for editing the 'about' table of a project. We can add new fields or +edit the existing ones as necessary, but everytime you add or modify a field, you have to write back +this information, otherwise it will be lost. + +.. code-block:: python + + from aequilibrae.utils.create_example import create_example + + project = create_example("/path_to_my_folder", "coquimbo") + + project.about.add_info_field("my_new_field") + project.about.my_new_field = "add some useful information about the field" + + # We can add data to an existing field + project.about.author = "Your Name" + + # And save our modifications + project.about.write_back() + +To check if ``my_new_field`` was added to the 'about' table, we can check all the characteristics stored +in the table. + +.. code-block:: python + + project.about.list_fields() # returns a list with all charactetistics in the 'about' table + +The 'about' table is created automatically when a project is created, but if you're loading a project +created with an older AequilibraE version that didn't contain it, it is possible to create one too. + +.. code-block:: python + + project.about.create() + # All AequilibraE's example already have an 'about' table, so you don't have to create it + +.. seealso:: + + :ref:`tables_about` + +``project.FieldEditor`` +----------------------- + +The ``FieldEditor`` allows the user to edit the project data tables, and it has two different purposes: + +* Managing data tables, through the addition/deletion of fields +* Editing the tables' metadata (aka the description of each field) + +This class is directly accessed from within the corresponding module one wants to edit. + +.. code-block:: python + + from aequilibrae.utils.create_example import create_example + + project = create_example("/path_to_my_folder", "coquimbo") + + link_fields = project.network.links.fields + # To add a new field to the 'links' table + link_fields.add("my_new_field", "this is an example of AequilibraE's funcionalities", "TEXT") + + # Don't forget to save these modifications + link_fields.save() + + # To edit the description of a field + link_fields.osm_id = "number of the osm link_id" + + # Or just to access the description of a field + link_fields.a_node + +One can also check all the fields in the links table. + +.. code-block:: python + + link_fields.all_fields() + +All field descriptions are kept in the table 'attributes_documentation'. + +.. seealso:: + + :ref:`parameters_metadata` + +``project.Log`` +--------------- + +``project.Matrices`` +-------------------- + +``project.network.LinkTypes`` +----------------------------- + +``project.network.Modes`` +------------------------- + +``project.network.Periods`` +--------------------------- From 795ec5f5b56dc298d780ee6d8ce93dc2dd6b3294 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Thu, 5 Sep 2024 17:02:18 -0300 Subject: [PATCH 25/57] . --- docs/source/api/api.rst | 8 +++ docs/source/conf.py | 8 +-- .../assignment_workflows/plot_forecasting.py | 9 +-- .../plot_public_transit_assignment.py | 1 + .../plot_route_choice_set.py | 1 + .../plot_subarea_analysis.py | 2 + .../creating_models/plot_create_from_gmns.py | 8 ++- .../creating_models/plot_create_from_layer.py | 19 +++--- .../creating_models/plot_create_from_osm.py | 8 ++- .../creating_models/plot_create_zoning.py | 11 +++- .../creating_models/plot_import_gtfs.py | 5 +- .../plot_moving_link_extremity.py | 15 ++++- .../editing_networks/plot_moving_nodes.py | 17 +++++- .../editing_networks/plot_splitting_link.py | 11 ++++ .../other_applications/plot_check_logging.py | 3 +- .../other_applications/plot_export_to_gmns.py | 14 ++++- .../plot_find_disconnected.py | 15 +++-- .../plot_path_computation.py | 13 ++-- .../plot_skimming.py | 16 +++-- docs/source/examples/skimming/readme.rst | 2 + .../plot_ipf_without_model.py | 22 ++++--- .../plot_trip_distribution.py | 14 ++++- .../visualization/plot_delaunay_lines.py | 1 + .../examples/visualization/plot_display.py | 1 + .../project_database/network_geometry.rst | 4 ++ .../network_import_and_export.rst | 2 + .../5-aequilibrae_matrix.rst | 2 + .../6-accessing-project-data.rst | 4 ++ .../7-project_components.rst | 61 ++++++++++++++++--- 29 files changed, 233 insertions(+), 64 deletions(-) rename docs/source/examples/{trip_distribution => skimming}/plot_path_computation.py (96%) rename docs/source/examples/{trip_distribution => skimming}/plot_skimming.py (87%) create mode 100644 docs/source/examples/skimming/readme.rst diff --git a/docs/source/api/api.rst b/docs/source/api/api.rst index 4c4c9d0dc..9af6b4ba4 100644 --- a/docs/source/api/api.rst +++ b/docs/source/api/api.rst @@ -79,6 +79,14 @@ Matrix Paths ----- +Skimming +~~~~~~~~ +.. currentmodule:: aequilibrae.paths +.. autosummary:: + :toctree: generated/ + + NetworkSkimming + Graph ~~~~~ .. currentmodule:: aequilibrae.paths diff --git a/docs/source/conf.py b/docs/source/conf.py index 8b400f2da..0e13545a9 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -79,11 +79,12 @@ "subsection_order": ExplicitOrder( [ "examples/creating_models", - "examples/editing_networks", + "examples/skimming", "examples/trip_distribution", - "examples/visualization", - "examples/aequilibrae_without_a_model", "examples/assignment_workflows", + "examples/editing_networks", + "examples/aequilibrae_without_a_model", + "examples/visualization", "examples/other_applications", ] ), @@ -184,7 +185,6 @@ } autodoc_member_order = "groupwise" - autoclass_content = "class" # classes should include both the class' and the __init__ method's docstring autosummary_generate = True diff --git a/docs/source/examples/assignment_workflows/plot_forecasting.py b/docs/source/examples/assignment_workflows/plot_forecasting.py index 845fede11..8b0786981 100644 --- a/docs/source/examples/assignment_workflows/plot_forecasting.py +++ b/docs/source/examples/assignment_workflows/plot_forecasting.py @@ -33,12 +33,14 @@ from aequilibrae.paths import TrafficAssignment, TrafficClass # %% + # 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"] @@ -91,10 +93,8 @@ assig.execute() # we then execute the assignment # %% -# Convergence report is easy to see -import pandas as pd -# %% +# Convergence report is easy to see convergence_report = assig.report() print(convergence_report.head()) @@ -370,9 +370,10 @@ # And save the skims assig.save_skims("future_year_assignment_skims", which_ones="all", format="omx") -#%% +# %% # We can also plot convergence # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + import matplotlib.pyplot as plt # %% diff --git a/docs/source/examples/assignment_workflows/plot_public_transit_assignment.py b/docs/source/examples/assignment_workflows/plot_public_transit_assignment.py index 2362cbb58..cb2166309 100644 --- a/docs/source/examples/assignment_workflows/plot_public_transit_assignment.py +++ b/docs/source/examples/assignment_workflows/plot_public_transit_assignment.py @@ -29,6 +29,7 @@ # sphinx_gallery_thumbnail_path = 'images/hyperpath_bell_n_10_alpha_100d0.png' # %% + # Let's create an empty project on an arbitrary folder. fldr = join(gettempdir(), uuid4().hex) diff --git a/docs/source/examples/assignment_workflows/plot_route_choice_set.py b/docs/source/examples/assignment_workflows/plot_route_choice_set.py index dae299ad8..f3ae75540 100644 --- a/docs/source/examples/assignment_workflows/plot_route_choice_set.py +++ b/docs/source/examples/assignment_workflows/plot_route_choice_set.py @@ -19,6 +19,7 @@ # sphinx_gallery_thumbnail_path = 'images/plot_route_choice_set.png' # %% + # We create the example project inside our temp folder fldr = join(gettempdir(), uuid4().hex) diff --git a/docs/source/examples/assignment_workflows/plot_subarea_analysis.py b/docs/source/examples/assignment_workflows/plot_subarea_analysis.py index 5b7e27169..02a875aac 100644 --- a/docs/source/examples/assignment_workflows/plot_subarea_analysis.py +++ b/docs/source/examples/assignment_workflows/plot_subarea_analysis.py @@ -9,6 +9,7 @@ """ # %% + # Imports from uuid import uuid4 from tempfile import gettempdir @@ -25,6 +26,7 @@ # sphinx_gallery_thumbnail_path = 'images/plot_subarea_analysis.png' # %% + # We create the example project inside our temp folder fldr = join(gettempdir(), uuid4().hex) diff --git a/docs/source/examples/creating_models/plot_create_from_gmns.py b/docs/source/examples/creating_models/plot_create_from_gmns.py index 3af8d332f..956585ac0 100644 --- a/docs/source/examples/creating_models/plot_create_from_gmns.py +++ b/docs/source/examples/creating_models/plot_create_from_gmns.py @@ -126,9 +126,13 @@ # %% project.close() +# %% +# .. admonition:: References +# +# * :ref:`importing_from_gmns_file` + # %% # .. seealso:: # The use of the following functions, methods, classes and modules is shown in this example: # -# * :func:`aequilibrae.project.Network.create_from_gmns` -# * :ref:`importing_from_gmns_file` \ No newline at end of file +# * :func:`aequilibrae.project.Network.create_from_gmns` \ No newline at end of file diff --git a/docs/source/examples/creating_models/plot_create_from_layer.py b/docs/source/examples/creating_models/plot_create_from_layer.py index 0b1244a6c..5c4437b33 100644 --- a/docs/source/examples/creating_models/plot_create_from_layer.py +++ b/docs/source/examples/creating_models/plot_create_from_layer.py @@ -27,8 +27,10 @@ # sphinx_gallery_thumbnail_path = 'images/plot_from_layer.png' # %% + # We create an empty project on an arbitrary folder fldr = join(gettempdir(), uuid4().hex) + project = Project() project.new(fldr) @@ -52,16 +54,16 @@ lt_dict = lt.all_types() existing_types = [ltype.link_type for ltype in lt_dict.values()] -#%% +# %% # We could also get it directly from the project database - +# # existing_types = [x[0] for x in project.conn.execute('Select link_type from link_types')] -#%% -# We add the link types that do not exist yet -# The trickier part is to choose a unique link type ID for each link type +# %% +# We add the link types that do not exist yet. +# The trickier part is to choose a unique link type ID for each link type. # You might want to tailor the link type for your use, but here we get letters -# in alphabetical order +# in alphabetical order. # %% types_to_add = [ltype for ltype in link_types if ltype not in existing_types] @@ -171,6 +173,5 @@ # .. seealso:: # The use of the following functions, methods, classes and modules is shown in this example: # -# * :func:`aequilibrae.project.network.Link` | :func:`aequilibrae.project.network.Links` -# * :func:`aequilibrae.project.network.Node` | :func:`aequilibrae.project.network.Nodes` -# * :func:`aequilibrae.project.network.Mode` | :func:`aequilibrae.project.network.LinkType` \ No newline at end of file +# * :func:`aequilibrae.project.network.Links` | :func:`aequilibrae.project.network.Nodes` +# * :func:`aequilibrae.project.network.Modes` | :func:`aequilibrae.project.network.LinkTypes` \ No newline at end of file diff --git a/docs/source/examples/creating_models/plot_create_from_osm.py b/docs/source/examples/creating_models/plot_create_from_osm.py index 394c379ec..5a5d84dae 100644 --- a/docs/source/examples/creating_models/plot_create_from_osm.py +++ b/docs/source/examples/creating_models/plot_create_from_osm.py @@ -20,8 +20,10 @@ # sphinx_gallery_thumbnail_path = 'images/nauru.png' # %% + # We create an empty project on an arbitrary folder fldr = join(gettempdir(), uuid4().hex) + project = Project() project.new(fldr) @@ -76,9 +78,13 @@ # %% project.close() +# %% +# .. admonition:: References +# +# * :ref:`importing_from_osm` + # %% # .. seealso:: # The use of the following functions, methods, classes and modules is shown in this example: # # * :func:`aequilibrae.project.Network.create_from_osm` -# * :ref:`importing_from_osm` diff --git a/docs/source/examples/creating_models/plot_create_zoning.py b/docs/source/examples/creating_models/plot_create_zoning.py index b9307c7b2..a5db4f114 100644 --- a/docs/source/examples/creating_models/plot_create_zoning.py +++ b/docs/source/examples/creating_models/plot_create_zoning.py @@ -149,10 +149,15 @@ # %% project.close() +# %% +# .. admonition:: References +# +# * :ref:`tables_zones` +# * :ref:`project_zoning` + # %% # .. seealso:: # The use of the following functions, methods, classes and modules is shown in this example: # -# * :func:`aequilibrae.project.Zone` -# * :func:`aequilibrae.project.network.Node` | :func:`aequilibrae.project.network.Nodes` -# * :ref:`tables_zones` \ No newline at end of file +# * :func:`aequilibrae.project.Zoning` +# * :func:`aequilibrae.project.network.Nodes` \ No newline at end of file diff --git a/docs/source/examples/creating_models/plot_import_gtfs.py b/docs/source/examples/creating_models/plot_import_gtfs.py index 647c51799..c7e2b537d 100644 --- a/docs/source/examples/creating_models/plot_import_gtfs.py +++ b/docs/source/examples/creating_models/plot_import_gtfs.py @@ -27,6 +27,7 @@ # sphinx_gallery_thumbnail_path = 'images/plot_import_gtfs.png' # %% + # Let's create an empty project on an arbitrary folder. fldr = join(gettempdir(), uuid4().hex) project = create_example(fldr, "coquimbo") @@ -106,10 +107,8 @@ fillOpacity=1.0, ).add_to(gtfs_stops) -# %% [markdown] -# Let's create the map! - # %% +# Let's create the map! map_osm = folium.Map(location=[-29.93, -71.29], zoom_start=13) # add all layers diff --git a/docs/source/examples/editing_networks/plot_moving_link_extremity.py b/docs/source/examples/editing_networks/plot_moving_link_extremity.py index 5124b9731..5078d6d49 100644 --- a/docs/source/examples/editing_networks/plot_moving_link_extremity.py +++ b/docs/source/examples/editing_networks/plot_moving_link_extremity.py @@ -28,7 +28,7 @@ all_nodes = project.network.nodes links = project.network.links -#%% +# %% # Let's move node one from the upper left corner of the image above, a bit to the left and to the bottom # %% @@ -47,7 +47,7 @@ links.save() links.refresh() -#%% +# %% # Because each link is unidirectional, you can no longer go from node 1 to node 2, obviously. # %% @@ -75,3 +75,14 @@ # %% project.close() + +# %% +# .. admonition:: References +# +# * :ref:`modifications_on_links_layer` + +# %% +# .. seealso:: +# The use of the following functions, methods, classes and modules is shown in this example: +# +# * :func:`aequilibrae.project.network.Links` \ No newline at end of file diff --git a/docs/source/examples/editing_networks/plot_moving_nodes.py b/docs/source/examples/editing_networks/plot_moving_nodes.py index 65c90d635..a255edf48 100644 --- a/docs/source/examples/editing_networks/plot_moving_nodes.py +++ b/docs/source/examples/editing_networks/plot_moving_nodes.py @@ -19,15 +19,15 @@ import matplotlib.pyplot as plt # %% + # We create the example project inside our temp folder. fldr = join(gettempdir(), uuid4().hex) project = create_example(fldr) -#%% +# %% # Let's move node one from the upper left corner of the image above, a bit to the left and to the bottom. -# %% # We also add the node we want to move. all_nodes = project.network.nodes links = project.network.links @@ -56,9 +56,20 @@ plt.show() -#%% +# %% # Did you notice the links are matching the node? # Look at the original network and see how it used to look like. # %% project.close() + +# %% +# .. admonition:: References +# +# * :ref:`modifications_on_nodes_layer` + +# %% +# .. seealso:: +# The use of the following functions, methods, classes and modules is shown in this example: +# +# * :func:`aequilibrae.project.network.Nodes` \ No newline at end of file diff --git a/docs/source/examples/editing_networks/plot_splitting_link.py b/docs/source/examples/editing_networks/plot_splitting_link.py index 3eb079325..90f5f73f7 100644 --- a/docs/source/examples/editing_networks/plot_splitting_link.py +++ b/docs/source/examples/editing_networks/plot_splitting_link.py @@ -85,3 +85,14 @@ # %% project.close() + +# %% +# .. admonition:: References +# +# * :ref:`modifications_on_links_layer` + +# %% +# .. seealso:: +# The use of the following functions, methods, classes and modules is shown in this example: +# +# * :func:`aequilibrae.project.network.Links` diff --git a/docs/source/examples/other_applications/plot_check_logging.py b/docs/source/examples/other_applications/plot_check_logging.py index 9ea750f93..5cecb25ca 100644 --- a/docs/source/examples/other_applications/plot_check_logging.py +++ b/docs/source/examples/other_applications/plot_check_logging.py @@ -25,6 +25,7 @@ # sphinx_gallery_thumbnail_path = 'images/logs_image.png' # %% + # We create an empty project on an arbitrary folder fldr = join(gettempdir(), uuid4().hex) project = create_example(fldr) @@ -102,4 +103,4 @@ # if we had created our project from OSM, the lines on top of the log would have been # different to display information on the queries done to the server to obtain the data. # -# Log image by https://oldschool.runescape.wiki/index.php?curid=66905# +# Log image by `OSRS Wiki `_ diff --git a/docs/source/examples/other_applications/plot_export_to_gmns.py b/docs/source/examples/other_applications/plot_export_to_gmns.py index c1b1a970c..2e50e6ab8 100644 --- a/docs/source/examples/other_applications/plot_export_to_gmns.py +++ b/docs/source/examples/other_applications/plot_export_to_gmns.py @@ -22,13 +22,14 @@ # sphinx_gallery_thumbnail_path = 'images/plot_export_to_gmns.png' # %% + # We load the example project inside a temp folder fldr = os.path.join(gettempdir(), uuid4().hex) project = create_example(fldr) # %% -# We export the network to csv files in GMNS format, that will be saved inside the project folder +# We export the network to CSV files in GMNS format, that will be saved inside the project folder output_fldr = os.path.join(gettempdir(), uuid4().hex) if not os.path.exists(output_fldr): os.mkdir(output_fldr) @@ -96,3 +97,14 @@ # %% project.close() + +# %% +# .. admonition:: References +# +# * :ref:`aequilibrae_to_gmns` + +# %% +# .. seealso:: +# The use of the following functions, methods, classes and modules is shown in this example: +# +# * :func:`aequilibrae.project.Network.export_to_gmns` \ No newline at end of file diff --git a/docs/source/examples/other_applications/plot_find_disconnected.py b/docs/source/examples/other_applications/plot_find_disconnected.py index b825f6faa..c8f38f56e 100644 --- a/docs/source/examples/other_applications/plot_find_disconnected.py +++ b/docs/source/examples/other_applications/plot_find_disconnected.py @@ -4,7 +4,7 @@ Finding disconnected links ========================== -In this example, we show how to find disconnected links in an AequilibraE network.. +In this example, we show how to find disconnected links in an AequilibraE network. We use the Nauru example to find disconnected links. """ @@ -22,6 +22,7 @@ # sphinx_gallery_thumbnail_path = 'images/disconnected_network.png' # %% + # We create an empty project on an arbitrary folder fldr = join(gettempdir(), uuid4().hex) @@ -100,9 +101,15 @@ # And save to disk alongside our model islands.to_csv(join(fldr, "island_outputs_complete.csv"), index=False) -#%% -# If you join the node_id field in the csv file generated above with the a_node or b_node fields -# in the links table, you will have the corresponding links in each disjoint island found. +# %% +# If you join the ``node_id`` field in the CSV file generated above with the ``a_node`` or ``b_node`` +# fields in the links table, you will have the corresponding links in each disjoint island found. # %% project.close() + +# %% +# .. seealso:: +# The use of the following functions, methods, classes and modules is shown in this example: +# +# * :func:`aequilibrae.paths.PathResults` diff --git a/docs/source/examples/trip_distribution/plot_path_computation.py b/docs/source/examples/skimming/plot_path_computation.py similarity index 96% rename from docs/source/examples/trip_distribution/plot_path_computation.py rename to docs/source/examples/skimming/plot_path_computation.py index f32a5c057..a6f0c7829 100644 --- a/docs/source/examples/trip_distribution/plot_path_computation.py +++ b/docs/source/examples/skimming/plot_path_computation.py @@ -15,6 +15,7 @@ from aequilibrae.utils.create_example import create_example # %% + # We create the example project inside our temp folder fldr = join(gettempdir(), uuid4().hex) @@ -32,17 +33,15 @@ 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. +# We get warnings that several fields in the project are filled with ``NaN``s. # This is true, but we won't use those fields. # %% @@ -145,3 +144,9 @@ # %% project.close() + +# %% +# .. seealso:: +# The use of the following functions, methods, classes and modules is shown in this example: +# +# * :func:`aequilibrae.paths.PathResults` \ No newline at end of file diff --git a/docs/source/examples/trip_distribution/plot_skimming.py b/docs/source/examples/skimming/plot_skimming.py similarity index 87% rename from docs/source/examples/trip_distribution/plot_skimming.py rename to docs/source/examples/skimming/plot_skimming.py index a33e4d668..201e46f0d 100644 --- a/docs/source/examples/trip_distribution/plot_skimming.py +++ b/docs/source/examples/skimming/plot_skimming.py @@ -17,6 +17,7 @@ # sphinx_gallery_thumbnail_path = 'images/skims.png' # %% + # We create the example project inside our temp folder fldr = join(gettempdir(), uuid4().hex) @@ -27,15 +28,14 @@ import sys # %% - -# We the project opens, we can tell the logger to direct all messages to the terminal as well +# When 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) -#%% +# %% # Network Skimming # ---------------- @@ -46,7 +46,7 @@ # %% # Let's build all graphs project.network.build_graphs() -# We get warnings that several fields in the project are filled with NaNs. +# We get warnings that several fields in the project are filled with ``NaN``s. # This is true, but we won't use those fields. # %% @@ -59,7 +59,7 @@ # let's say we want to minimize the distance graph.set_graph("distance") -# And will skim distance while we are at it, other fields like `free_flow_time` or `travel_time` +# And will skim distance while we are at it, other fields like ``free_flow_time`` or ``travel_time`` # can be added here as well graph.set_skimming(["distance"]) @@ -96,3 +96,9 @@ # %% project.close() + +# %% +# .. seealso:: +# The use of the following functions, methods, classes and modules is shown in this example: +# +# * :func:`aequilibrae.paths.NetworkSkimming` \ No newline at end of file diff --git a/docs/source/examples/skimming/readme.rst b/docs/source/examples/skimming/readme.rst new file mode 100644 index 000000000..497f8bc3b --- /dev/null +++ b/docs/source/examples/skimming/readme.rst @@ -0,0 +1,2 @@ +Skimming +-------- \ No newline at end of file diff --git a/docs/source/examples/trip_distribution/plot_ipf_without_model.py b/docs/source/examples/trip_distribution/plot_ipf_without_model.py index 2ddca5af5..b3661a75a 100644 --- a/docs/source/examples/trip_distribution/plot_ipf_without_model.py +++ b/docs/source/examples/trip_distribution/plot_ipf_without_model.py @@ -11,16 +11,17 @@ *How would your trip distribution matrix using IPF look like?* The data used in this example comes from Table 5.6 in Ortúzar & Willumsen (2011). +*ORTÚZAR, J.D., WILLUMSEN, L.G. (2011) Modelling Transport (4th ed.). Wiley-Blackwell.* """ # %% # Imports import numpy as np - -from aequilibrae.distribution import Ipf from os.path import join from tempfile import gettempdir + +from aequilibrae.distribution import Ipf from aequilibrae.matrix import AequilibraeMatrix, AequilibraeData # sphinx_gallery_thumbnail_path = 'images/ipf.png' @@ -76,9 +77,16 @@ # %% for line in fratar.report: print(line) - -#%% -# Reference -# ~~~~~~~~~ + +# %% +# .. admonition:: References # -# ORTÚZAR, J.D., WILLUMSEN, L.G. (2011) *Modelling Transport* (4th ed.). Wiley-Blackwell. \ No newline at end of file +# * :ref:`all_about_aeq_matrices` +# * :ref:`validation` + +# %% +# .. seealso:: +# The use of the following functions, methods, classes and modules is shown in this example: +# +# * :func:`aequilibrae.matrix.AequilibraeMatrix` | :func:`aequilibrae.matrix.AequilibraeData` +# * :func:`aequilibrae.distribution.Ipf` \ No newline at end of file diff --git a/docs/source/examples/trip_distribution/plot_trip_distribution.py b/docs/source/examples/trip_distribution/plot_trip_distribution.py index dda089c34..03ee986e6 100644 --- a/docs/source/examples/trip_distribution/plot_trip_distribution.py +++ b/docs/source/examples/trip_distribution/plot_trip_distribution.py @@ -18,13 +18,14 @@ import numpy as np # %% + # We create the example project inside our temp folder fldr = join(gettempdir(), uuid4().hex) project = create_example(fldr) # %% -# We get the demand matrix directly from the project record +# We get the demand matrix directly from the project record, # so let's inspect what we have in the project proj_matrices = project.matrices print(proj_matrices.list()) @@ -89,14 +90,13 @@ def plot_tlfd(demand, skim, name): plt = plot_tlfd(demand.matrix_view, impedance.matrix_view, join(fldr, "demand_tfld.png")) plt.show() -#%% +# %% # Forecast # -------- # We create a set of *'future'* vectors by applying some models # and apply the model for both deterrence functions # %% - from aequilibrae.distribution import Ipf, GravityApplication, SyntheticGravityModel from aequilibrae.matrix import AequilibraeData import numpy as np @@ -176,3 +176,11 @@ def plot_tlfd(demand, skim, name): # %% project.close() + +# %% +# .. seealso:: +# The use of the following functions, methods, classes and modules is shown in this example: +# +# * :func:`aequilibrae.distribution.Ipf` | :func:`aequilibrae.distribution.GravityCalibration` +# * :func:`aequilibrae.distribution.GravityApplication` | :func:`aequilibrae.distribution.SyntheticGravityModel` +# * :func:`aequilibrae.matrix.AequilibraeData` diff --git a/docs/source/examples/visualization/plot_delaunay_lines.py b/docs/source/examples/visualization/plot_delaunay_lines.py index 29f2b35ea..d05f56ec0 100644 --- a/docs/source/examples/visualization/plot_delaunay_lines.py +++ b/docs/source/examples/visualization/plot_delaunay_lines.py @@ -26,6 +26,7 @@ from aequilibrae.utils.create_delaunay_network import DelaunayAnalysis # %% + # We create an empty project on an arbitrary folder fldr = join(gettempdir(), uuid4().hex) diff --git a/docs/source/examples/visualization/plot_display.py b/docs/source/examples/visualization/plot_display.py index e21e3a42b..24892a62e 100644 --- a/docs/source/examples/visualization/plot_display.py +++ b/docs/source/examples/visualization/plot_display.py @@ -20,6 +20,7 @@ # sphinx_gallery_thumbnail_path = 'images/plot_network_image.png' # %% + # We create an empty project on an arbitrary folder fldr = join(gettempdir(), uuid4().hex) diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/network_geometry.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/network_geometry.rst index 29f5b681b..7cd7e26b8 100644 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/network_geometry.rst +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/network_geometry.rst @@ -87,6 +87,8 @@ each of the changes to see how they are treated through triggers. | Modifying a data entry | | +--------------------------------------+-----------------------------------+ +.. _modifications_on_nodes_layer: + Node layer changes and expected behavior ```````````````````````````````````````` @@ -190,6 +192,8 @@ If the field being edited is the node_id field, then all the related tables need to be edited as well (e.g. a_b and b_node in the link layer, the node_id tagged to turn restrictions and to transit stops). +.. _modifications_on_links_layer: + Link layer changes and expected behavior ```````````````````````````````````````` diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/network_import_and_export.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/network_import_and_export.rst index 22e372345..6df476cf8 100644 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/network_import_and_export.rst +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/network_import_and_export.rst @@ -134,6 +134,8 @@ specification. :func:`aequilibrae.project.Network.create_from_gmns` +.. _aequilibrae_to_gmns: + Exporting AequilibraE model to GMNS format ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/source/modeling_with_aequilibrae/5-aequilibrae_matrix.rst b/docs/source/modeling_with_aequilibrae/5-aequilibrae_matrix.rst index 94d888dcf..21fcb36ea 100644 --- a/docs/source/modeling_with_aequilibrae/5-aequilibrae_matrix.rst +++ b/docs/source/modeling_with_aequilibrae/5-aequilibrae_matrix.rst @@ -1,3 +1,5 @@ +.. _all_about_aeq_matrices: + AequilibraE Matrix ================== diff --git a/docs/source/modeling_with_aequilibrae/6-accessing-project-data.rst b/docs/source/modeling_with_aequilibrae/6-accessing-project-data.rst index 522cafd6f..37781fe43 100644 --- a/docs/source/modeling_with_aequilibrae/6-accessing-project-data.rst +++ b/docs/source/modeling_with_aequilibrae/6-accessing-project-data.rst @@ -1,3 +1,5 @@ +.. _accessing_project_data: + Accessing Project Data ====================== @@ -38,6 +40,8 @@ The actions * :func:`aequilibrae.network.Nodes` +.. _project_zoning: + ``Zoning`` ---------- diff --git a/docs/source/modeling_with_aequilibrae/7-project_components.rst b/docs/source/modeling_with_aequilibrae/7-project_components.rst index 4714aeaa9..985987513 100644 --- a/docs/source/modeling_with_aequilibrae/7-project_components.rst +++ b/docs/source/modeling_with_aequilibrae/7-project_components.rst @@ -1,3 +1,5 @@ +.. _project_components: + Project Components ================== @@ -18,7 +20,7 @@ Network and Zoning are the components that contain the geo-spatial information o as links, nodes, and zones, which can also be manipulated. In the Network component, there are also non-geometric classes related to the project network, such as Modes, LinkTypes, and Periods. -One important thing to observe is that related to each component in Network and Zoning, there is an +One important thing to observe is that related to each component in Matrices, Network, andd Zoning, there is an object with similar name that corresponds to one object in the class. Thus the ``project.network.Links`` enables the access to manipulate the links table, and each item in the items table is a ``project.network.Link``. @@ -56,7 +58,7 @@ in the table. .. code-block:: python - project.about.list_fields() # returns a list with all charactetistics in the 'about' table + project.about.list_fields() # returns a list with all characteristics in the 'about' table The 'about' table is created automatically when a project is created, but if you're loading a project created with an older AequilibraE version that didn't contain it, it is possible to create one too. @@ -68,7 +70,8 @@ created with an older AequilibraE version that didn't contain it, it is possible .. seealso:: - :ref:`tables_about` + * :ref:`tables_about` + Table documentation ``project.FieldEditor`` ----------------------- @@ -82,11 +85,9 @@ This class is directly accessed from within the corresponding module one wants t .. code-block:: python - from aequilibrae.utils.create_example import create_example - - project = create_example("/path_to_my_folder", "coquimbo") - + # We'll edit the fields in the 'links' table link_fields = project.network.links.fields + # To add a new field to the 'links' table link_fields.add("my_new_field", "this is an example of AequilibraE's funcionalities", "TEXT") @@ -109,19 +110,63 @@ All field descriptions are kept in the table 'attributes_documentation'. .. seealso:: - :ref:`parameters_metadata` + * :ref:`parameters_metadata` + Table documentation ``project.Log`` --------------- +Every AequilibraE project contains a log file that holds information on all the project procedures. +It is possible to access the log file contents, as presented in the next code block. + +.. code-block:: python + + project_log = project.log() + + project_log.contents() # returns a list with all entires in the log file + +If your project's log is getting cluttered, it is possible to clear it. This option must be used wiesly +once the deletion of data in the log file can't be undone. + +.. code-block:: python + + project_log.clear() + +.. seealso:: + + * :func:`aequilibrae.project.Log` + Class documentation + + * :ref:`useful-log-tips` + Usage example + ``project.Matrices`` -------------------- +This method ia a gateway to all the matrices available in the model, which allows us to update the +records in the 'matrices' table. + +.. seealso:: + + * :func:`aequilibrae.project.Matrices` + Class documentation + + * :ref:`matrix_table` + Table documentation + +Each item in ``project.Matrices`` is a ``MatrixRecord``. + ``project.network.LinkTypes`` ----------------------------- +Each item in ``project.network.LinkTypes`` is a ``LinkType``. + ``project.network.Modes`` ------------------------- +Each item in ``project.network.Modes`` is a ``Mode``. + ``project.network.Periods`` --------------------------- + +Each item in ``project.network.Periods`` is a ``Period``. \ No newline at end of file From a69e26a1e009cdecc5c38fd089c53a141ba7509e Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Fri, 6 Sep 2024 11:55:00 -0300 Subject: [PATCH 26/57] Update 7-project_components.rst --- .../7-project_components.rst | 81 +++++++++++++++++-- 1 file changed, 76 insertions(+), 5 deletions(-) diff --git a/docs/source/modeling_with_aequilibrae/7-project_components.rst b/docs/source/modeling_with_aequilibrae/7-project_components.rst index 985987513..1dbe27aa0 100644 --- a/docs/source/modeling_with_aequilibrae/7-project_components.rst +++ b/docs/source/modeling_with_aequilibrae/7-project_components.rst @@ -146,6 +146,27 @@ once the deletion of data in the log file can't be undone. This method ia a gateway to all the matrices available in the model, which allows us to update the records in the 'matrices' table. +.. code-block:: + + from aequilibrae.utils.create_example import create_example + + # Sioux Falls is a better example for this component + project = create_example("/path_to_my_folder") + + matrices = project.matrices + +One can also check all the project matrices. + +.. code-block:: python + + matrices.list() # returns a DataFrame of all available matrices + +Each item in ``project.Matrices`` is a ``MatrixRecord`` for which we can carry out some procedures. + +.. code-block:: python + + pass + .. seealso:: * :func:`aequilibrae.project.Matrices` @@ -154,19 +175,69 @@ records in the 'matrices' table. * :ref:`matrix_table` Table documentation -Each item in ``project.Matrices`` is a ``MatrixRecord``. - ``project.network.LinkTypes`` ----------------------------- +This method allows you to access the API resources to manipulate the 'link_types' table. + Each item in ``project.network.LinkTypes`` is a ``LinkType``. -``project.network.Modes`` +.. code-block:: python + + from aequilibrae.utils.create_example import create_example + + project = create_example("/path_to_my_folder", "coquimbo") + + link_types = project.network.link_types + + new_link_type = link_types.new("A") # Create a new LinkType with ID 'A' + + # We can add information to the LinkType we just created + new_link_type.description = "This is a description" + new_link_type.speed = 35 + new_link_type.link_type = "Arterial" + + # To save the modifications for ```new_link_type``` + new_link_type.save() + + # To create a new field in the 'link_types' table, you can call the function ``fields`` + # to return a FieldEditor instance, which can be edited + link_types.fields() + + # You can also remove a LinkType from a project using its ``link_type_id`` + link_types.delete("A") + + # And don't forget to save the modifications you did in the 'link_types' table + link_types.save() + + # To check all ``LinkTypes`` in the project + link_types.all_types() # returns a dictionary with all LinkType objects in the model. + # The dictionary's keys are the ``link_type_id``'s + + # There are two ways to get a LinkType from the network: + link_types.get("p") # using the ``link_type_id`` + # or + link_types.get_by_name("primary") # using the ``link_type`` + + +``project.network.modes`` ------------------------- -Each item in ``project.network.Modes`` is a ``Mode``. +This method allows you to access the API resources to manipulate the 'modes' table. + +Each item in ``project.network.modes`` is a ``Mode``. + +.. code-block:: python + + pass ``project.network.Periods`` --------------------------- -Each item in ``project.network.Periods`` is a ``Period``. \ No newline at end of file +This method allows you to access the API resources to manipulate the 'periods' table. + +Each item in ``project.network.Periods`` is a ``Period``. + +.. code-block:: python + + pass From 674584baa400c2b1416d5b49a4b7288e30b366b6 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Fri, 6 Sep 2024 14:39:55 -0300 Subject: [PATCH 27/57] . --- .../7-project_components.rst | 146 +++++++++++------- 1 file changed, 92 insertions(+), 54 deletions(-) diff --git a/docs/source/modeling_with_aequilibrae/7-project_components.rst b/docs/source/modeling_with_aequilibrae/7-project_components.rst index 1dbe27aa0..9fcbeabec 100644 --- a/docs/source/modeling_with_aequilibrae/7-project_components.rst +++ b/docs/source/modeling_with_aequilibrae/7-project_components.rst @@ -21,9 +21,9 @@ as links, nodes, and zones, which can also be manipulated. In the Network compon non-geometric classes related to the project network, such as Modes, LinkTypes, and Periods. One important thing to observe is that related to each component in Matrices, Network, andd Zoning, there is an -object with similar name that corresponds to one object in the class. Thus the ``project.network.Links`` -enables the access to manipulate the links table, and each item in the items table is a -``project.network.Link``. +object with similar name that corresponds to one object in the class. Thus ``project.network.links`` +enables the access to manipulate the 'links' table, and each item in the items table is a +``Link`` object. .. image:: ../images/project_components_and_items.png :align: center @@ -31,7 +31,7 @@ enables the access to manipulate the links table, and each item in the items tab In this section, we'll briefly discuss about the project components without geo-spatial information. -``project.About`` +``project.about`` ----------------- This class provides an interface for editing the 'about' table of a project. We can add new fields or @@ -53,20 +53,14 @@ this information, otherwise it will be lost. # And save our modifications project.about.write_back() -To check if ``my_new_field`` was added to the 'about' table, we can check all the characteristics stored -in the table. - -.. code-block:: python - + # To check if ``my_new_field`` was added to the 'about' table, we can check the characteristics + # stored in the table. project.about.list_fields() # returns a list with all characteristics in the 'about' table -The 'about' table is created automatically when a project is created, but if you're loading a project -created with an older AequilibraE version that didn't contain it, it is possible to create one too. - -.. code-block:: python - + # The 'about' table is created automatically when a project is created, but if you're + # loading a project created with an older AequilibraE version that didn't contain it, + # it is possible to create one too. project.about.create() - # All AequilibraE's example already have an 'about' table, so you don't have to create it .. seealso:: @@ -100,10 +94,7 @@ This class is directly accessed from within the corresponding module one wants t # Or just to access the description of a field link_fields.a_node -One can also check all the fields in the links table. - -.. code-block:: python - + # One can also check all the fields in the links table. link_fields.all_fields() All field descriptions are kept in the table 'attributes_documentation'. @@ -113,7 +104,7 @@ All field descriptions are kept in the table 'attributes_documentation'. * :ref:`parameters_metadata` Table documentation -``project.Log`` +``project.log`` --------------- Every AequilibraE project contains a log file that holds information on all the project procedures. @@ -125,11 +116,8 @@ It is possible to access the log file contents, as presented in the next code bl project_log.contents() # returns a list with all entires in the log file -If your project's log is getting cluttered, it is possible to clear it. This option must be used wiesly -once the deletion of data in the log file can't be undone. - -.. code-block:: python - + # If your project's log is getting cluttered, it is possible to clear it. + # This option must be used wiesly once the deletion of data in the log file can't be undone. project_log.clear() .. seealso:: @@ -144,9 +132,9 @@ once the deletion of data in the log file can't be undone. -------------------- This method ia a gateway to all the matrices available in the model, which allows us to update the -records in the 'matrices' table. +records in the 'matrices' table. Each item in the 'matrices' table is a ``MatrixRecord``. -.. code-block:: +.. code-block:: python from aequilibrae.utils.create_example import create_example @@ -155,17 +143,8 @@ records in the 'matrices' table. matrices = project.matrices -One can also check all the project matrices. - -.. code-block:: python - - matrices.list() # returns a DataFrame of all available matrices - -Each item in ``project.Matrices`` is a ``MatrixRecord`` for which we can carry out some procedures. - -.. code-block:: python - - pass + # One can also check all the project matrices as a Pandas' DataFrame + matrices.list() .. seealso:: @@ -175,12 +154,11 @@ Each item in ``project.Matrices`` is a ``MatrixRecord`` for which we can carry o * :ref:`matrix_table` Table documentation -``project.network.LinkTypes`` ------------------------------ +``project.network.link_types`` +------------------------------ This method allows you to access the API resources to manipulate the 'link_types' table. - -Each item in ``project.network.LinkTypes`` is a ``LinkType``. +Each item in the 'link_types' table is a ``LinkType`` object. .. code-block:: python @@ -197,7 +175,7 @@ Each item in ``project.network.LinkTypes`` is a ``LinkType``. new_link_type.speed = 35 new_link_type.link_type = "Arterial" - # To save the modifications for ```new_link_type``` + # To save the modifications for ``new_link_type`` new_link_type.save() # To create a new field in the 'link_types' table, you can call the function ``fields`` @@ -214,30 +192,90 @@ Each item in ``project.network.LinkTypes`` is a ``LinkType``. link_types.all_types() # returns a dictionary with all LinkType objects in the model. # The dictionary's keys are the ``link_type_id``'s - # There are two ways to get a LinkType from the network: - link_types.get("p") # using the ``link_type_id`` - # or - link_types.get_by_name("primary") # using the ``link_type`` + # There are two ways to get a LinkType from the 'link_types' table + # using the ``link_type_id`` + link_types.get("p") + # or using the ``link_type`` + link_types.get_by_name("primary") ``project.network.modes`` ------------------------- This method allows you to access the API resources to manipulate the 'modes' table. - -Each item in ``project.network.modes`` is a ``Mode``. +Each item in 'modes' table is a ``Mode`` object. .. code-block:: python - pass + from aequilibrae.utils.create_example import create_example + + project = create_example("/path_to_my_folder", "coquimbo") + + modes = project.network.modes + + # We create a new mode + new_mode = modes.new("k") + new_mode.mode_name = "flying_car" + + # And add it to the modes table + modes.add(new_mode) + + # When we add a new mode to the 'modes' table, it is automatically saved in the table + # But we can continue editing the modes, and save them as we modify them + new_mode.description = "Like the one in the Jetsons" + new_mode.save() -``project.network.Periods`` + # You can also remove a Mode from a project using its ``mode_id`` + modes.delete("w") + + # To check all ``LinkTypes`` in the project + modes.all_modes() # returns a dictionary with all Mode objects in the model. + # The dictionary's keys are the ``mode_id``'s + + # There are two ways to get a Mode from the 'modes' table + # using the ``mode_id`` + modes.get("c") + # or using the ``mode_name`` + modes.get_by_name("car") + +``project.network.periods`` --------------------------- This method allows you to access the API resources to manipulate the 'periods' table. - -Each item in ``project.network.Periods`` is a ``Period``. +Each item in the 'periods' table is a ``Period`` object. .. code-block:: python - pass + from aequilibrae.utils.create_example import create_example + + project = create_example("/path_to_my_folder", "coquimbo") + + periods = project.network.periods + + # Let's add a new field to our 'periods' table + periods.fields.add("my_field", "This is an example", "TEXT") + + # To save this modification, we must refresh the table + periods.refresh_fields() + + # To add a new period + new_period = periods.new_period(2, 21600, 43200, "6AM to noon") + + # It is also possible to renumber a period + new_period.renumber(9) + # And check the existing data fields for each period + new_period.data_fields() + + # Saving can be done after finishing all modifications in the table but for the sake + # of this example, we'll save the addition of a new period to our table right away + periods.save() + + # To see all periods data as a Pandas' DataFrame + periods.data + + # Let's get our default period and change its description + select_period = periods.get(1) + select_period.period_description = "We changed the period description" + + # And we save this period modification + select_period.save() From c2b1e4bd4a71ca30292f2e79bf44555949216709 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Fri, 6 Sep 2024 16:10:58 -0300 Subject: [PATCH 28/57] docs --- .gitignore | 2 + aequilibrae/project/network/link_types.py | 9 +- aequilibrae/project/network/mode.py | 2 +- aequilibrae/project/network/modes.py | 2 +- .../assignment_workflows/just_matrices.omx | Bin 0 -> 15520 bytes .../select_link_analysis.omx | Bin 0 -> 15520 bytes .../6-accessing-project-data.rst | 48 +++++--- ...omponents.rst => 6-project_components.rst} | 31 ++++- .../modeling_with_aequilibrae/index.rst | 4 +- docs/source/sg_execution_times.rst | 106 ++++++++++++++++++ 10 files changed, 179 insertions(+), 25 deletions(-) create mode 100644 docs/source/examples/assignment_workflows/just_matrices.omx create mode 100644 docs/source/examples/assignment_workflows/select_link_analysis.omx rename docs/source/modeling_with_aequilibrae/{7-project_components.rst => 6-project_components.rst} (90%) create mode 100644 docs/source/sg_execution_times.rst diff --git a/.gitignore b/.gitignore index b06200e36..a47170f96 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ _build _static _templates +docs/source/_auto_examples +docs/source/api/generated # User-specific stuff: .idea/**/workspace.xml diff --git a/aequilibrae/project/network/link_types.py b/aequilibrae/project/network/link_types.py index df3f91140..f5be08d36 100644 --- a/aequilibrae/project/network/link_types.py +++ b/aequilibrae/project/network/link_types.py @@ -88,7 +88,7 @@ def new(self, link_type_id: str) -> LinkType: return lt def delete(self, link_type_id: str) -> None: - """Removes the link_type with *link_type_id* from the project""" + """Removes the ``LinkType`` with ``link_type_id`` from the project""" try: lt = self.__items[link_type_id] # type: LinkType lt.delete() @@ -99,20 +99,21 @@ def delete(self, link_type_id: str) -> None: self.logger.warning(f"Link type {link_type_id} was successfully removed from the project database") def get(self, link_type_id: str) -> LinkType: - """Get a link_type from the network by its *link_type_id*""" + """Get a ``LinkType`` from the network by its ``link_type_id``""" if link_type_id not in self.__items: raise ValueError(f"Link type {link_type_id} does not exist in the model") return self.__items[link_type_id] def get_by_name(self, link_type: str) -> LinkType: - """Get a link_type from the network by its *link_type* (i.e. name)""" + """Get a ``LinkType`` from the network by its ``link_type`` (i.e. name)""" for lt in self.__items.values(): if lt.link_type.lower() == link_type.lower(): return lt + # TODO: fix the FieldEditor import to self.project def fields(self) -> FieldEditor: """Returns a FieldEditor class instance to edit the Link_Types table fields and their metadata""" - return FieldEditor(self.project.project_base_path, "link_types") + return FieldEditor(self.project, "link_types") def all_types(self) -> dict: """Returns a dictionary with all LinkType objects available in the model. link_type_id as key""" diff --git a/aequilibrae/project/network/mode.py b/aequilibrae/project/network/mode.py index 6da2dee9e..9610dcc94 100644 --- a/aequilibrae/project/network/mode.py +++ b/aequilibrae/project/network/mode.py @@ -5,7 +5,7 @@ class Mode: - """A mode object represents a single record in the *modes* table""" + """A mode object represents a single record in the 'modes' table""" __alowed_characters = string.ascii_letters + "_" diff --git a/aequilibrae/project/network/modes.py b/aequilibrae/project/network/modes.py index 7fd12f77d..0c5c2c497 100644 --- a/aequilibrae/project/network/modes.py +++ b/aequilibrae/project/network/modes.py @@ -8,7 +8,7 @@ class Modes: """ - Access to the API resources to manipulate the modes table in the network + Access to the API resources to manipulate the 'modes' table in the network .. code-block:: python diff --git a/docs/source/examples/assignment_workflows/just_matrices.omx b/docs/source/examples/assignment_workflows/just_matrices.omx new file mode 100644 index 0000000000000000000000000000000000000000..0b15743732461a96dbec8ca51f40fec5593466dc GIT binary patch literal 15520 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 HXWV}Qava&I literal 0 HcmV?d00001 diff --git a/docs/source/examples/assignment_workflows/select_link_analysis.omx b/docs/source/examples/assignment_workflows/select_link_analysis.omx new file mode 100644 index 0000000000000000000000000000000000000000..0b15743732461a96dbec8ca51f40fec5593466dc GIT binary patch literal 15520 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 HXWV}Qava&I literal 0 HcmV?d00001 diff --git a/docs/source/modeling_with_aequilibrae/6-accessing-project-data.rst b/docs/source/modeling_with_aequilibrae/6-accessing-project-data.rst index 37781fe43..a30a2c507 100644 --- a/docs/source/modeling_with_aequilibrae/6-accessing-project-data.rst +++ b/docs/source/modeling_with_aequilibrae/6-accessing-project-data.rst @@ -7,23 +7,19 @@ An AequilibraE project helds geometric information that can be accessed by the u three different classes: ``Links``, ``Nodes``, and ``Zoning``. In this section, we'll cover the main points regarding them. -``Links`` ---------- +``project.network.links`` +------------------------- -The ``Links`` class allows the access to the links table in the project network, if one -wants to manipulate it. +This method allows you to access the API resources to manipulate the 'links' table. +Each item in the 'links' table is a ``Link`` object. .. code-block:: python - from tempfile import gettempdir from aequilibrae.utils.create_example import create_example - # Let's use Coquimbo as example - project = create_example(gettempdir(), "coquimbo") + project = create_example("/path_to_my_folder", "coquimbo") - links = project.network.links # access the links table - -The actions + links = project.network.links .. seealso:: @@ -33,8 +29,21 @@ The actions * :ref:`project_from_link_layer` Usage example -``Nodes`` ---------- +``project.network.nodes`` +------------------------- + +This method allows you to access the API resources to manipulate the 'nodes' table. +Each item in the 'nodes' table is a ``Node`` object. + +.. code-block:: python + + from aequilibrae.utils.create_example import create_example + + project = create_example("/path_to_my_folder", "coquimbo") + + project_nodes = project.network.nodes + + # To add special generators, we can add a `new_centroid` .. seealso:: @@ -42,8 +51,19 @@ The actions .. _project_zoning: -``Zoning`` ----------- +``project.zoning`` +------------------ + +This method allows you to access the API resources to manipulate the 'zones' table. +Each item in the 'zones' table is a ``Zone`` object. + +.. code-block:: python + + from aequilibrae.utils.create_example import create_example + + project = create_example("/path_to_my_folder", "coquimbo") + + zones = project.zoning .. seealso:: diff --git a/docs/source/modeling_with_aequilibrae/7-project_components.rst b/docs/source/modeling_with_aequilibrae/6-project_components.rst similarity index 90% rename from docs/source/modeling_with_aequilibrae/7-project_components.rst rename to docs/source/modeling_with_aequilibrae/6-project_components.rst index 9fcbeabec..a16615bbc 100644 --- a/docs/source/modeling_with_aequilibrae/7-project_components.rst +++ b/docs/source/modeling_with_aequilibrae/6-project_components.rst @@ -128,17 +128,16 @@ It is possible to access the log file contents, as presented in the next code bl * :ref:`useful-log-tips` Usage example -``project.Matrices`` +``project.matrices`` -------------------- This method ia a gateway to all the matrices available in the model, which allows us to update the -records in the 'matrices' table. Each item in the 'matrices' table is a ``MatrixRecord``. +records in the 'matrices' table. Each item in the 'matrices' table is a ``MatrixRecord`` object. .. code-block:: python from aequilibrae.utils.create_example import create_example - # Sioux Falls is a better example for this component project = create_example("/path_to_my_folder") matrices = project.matrices @@ -146,6 +145,32 @@ records in the 'matrices' table. Each item in the 'matrices' table is a ``Matri # One can also check all the project matrices as a Pandas' DataFrame matrices.list() + # We can add a naw matrix + matrices.new_record() + + # To delete a matrix from the 'matrices' table, we can delete the record directly + matrices.delete_record("demand_mc") + # or + mat_record = matrices.get_record("demand_mc") + mat_record.delete() + + # If you're unsure if you have a matrix in you project, you can check if it exists + # This function will return `True` or `False` + matrices.check_exists("my_matrix") + + # If a matrix was added or deleted by an external process, you should update or clean + # your 'matrices' table to keep your project organised. + matrices.update_database() # in case of addition + + matrices.clear_database() # in case of deletion + + # To reload the existing matrices in memory once again + matrices.reload() + + # Similar to the `get_record` function, we have the `get_matrix`, which allows you to + # get an AequilibraE matrix. + matrices.get_matrix("demand_aem") + .. seealso:: * :func:`aequilibrae.project.Matrices` diff --git a/docs/source/modeling_with_aequilibrae/index.rst b/docs/source/modeling_with_aequilibrae/index.rst index 96acc7b5d..aeec32e55 100644 --- a/docs/source/modeling_with_aequilibrae/index.rst +++ b/docs/source/modeling_with_aequilibrae/index.rst @@ -24,5 +24,5 @@ a start guide to a complete view into AequilibraE's data structure. 3-transit_assignment/index 4-route_choice/index 5-aequilibrae_matrix - 6-accessing-project-data - 7-project_components \ No newline at end of file + 6-project_components + 6-accessing-project-data \ No newline at end of file diff --git a/docs/source/sg_execution_times.rst b/docs/source/sg_execution_times.rst new file mode 100644 index 000000000..555c7bac6 --- /dev/null +++ b/docs/source/sg_execution_times.rst @@ -0,0 +1,106 @@ + +:orphan: + +.. _sphx_glr_sg_execution_times: + + +Computation times +================= +**02:25.391** total execution time for 24 files **from all galleries**: + +.. container:: + + .. raw:: html + + + + + + + + .. list-table:: + :header-rows: 1 + :class: table table-striped sg-datatable + + * - Example + - Time + - Mem (MB) + * - :ref:`sphx_glr__auto_examples_skimming_plot_path_computation.py` (``examples/skimming/plot_path_computation.py``) + - 01:25.973 + - 0.0 + * - :ref:`sphx_glr__auto_examples_creating_models_plot_create_from_osm.py` (``examples/creating_models/plot_create_from_osm.py``) + - 00:19.374 + - 0.0 + * - :ref:`sphx_glr__auto_examples_assignment_workflows_plot_public_transit_assignment.py` (``examples/assignment_workflows/plot_public_transit_assignment.py``) + - 00:12.310 + - 0.0 + * - :ref:`sphx_glr__auto_examples_creating_models_plot_import_gtfs.py` (``examples/creating_models/plot_import_gtfs.py``) + - 00:08.777 + - 0.0 + * - :ref:`sphx_glr__auto_examples_creating_models_plot_create_zoning.py` (``examples/creating_models/plot_create_zoning.py``) + - 00:06.493 + - 0.0 + * - :ref:`sphx_glr__auto_examples_assignment_workflows_plot_route_choice_set.py` (``examples/assignment_workflows/plot_route_choice_set.py``) + - 00:04.572 + - 0.0 + * - :ref:`sphx_glr__auto_examples_creating_models_plot_create_from_layer.py` (``examples/creating_models/plot_create_from_layer.py``) + - 00:04.520 + - 0.0 + * - :ref:`sphx_glr__auto_examples_trip_distribution_plot_trip_distribution.py` (``examples/trip_distribution/plot_trip_distribution.py``) + - 00:02.511 + - 0.0 + * - :ref:`sphx_glr__auto_examples_skimming_plot_skimming.py` (``examples/skimming/plot_skimming.py``) + - 00:00.860 + - 0.0 + * - :ref:`sphx_glr__auto_examples_aequilibrae_without_a_model_plot_assignment_without_model.py` (``examples/aequilibrae_without_a_model/plot_assignment_without_model.py``) + - 00:00.000 + - 0.0 + * - :ref:`sphx_glr__auto_examples_assignment_workflows_plot_forecasting.py` (``examples/assignment_workflows/plot_forecasting.py``) + - 00:00.000 + - 0.0 + * - :ref:`sphx_glr__auto_examples_assignment_workflows_plot_route_choice_basics.py` (``examples/assignment_workflows/plot_route_choice_basics.py``) + - 00:00.000 + - 0.0 + * - :ref:`sphx_glr__auto_examples_assignment_workflows_plot_subarea_analysis.py` (``examples/assignment_workflows/plot_subarea_analysis.py``) + - 00:00.000 + - 0.0 + * - :ref:`sphx_glr__auto_examples_creating_models_plot_create_from_gmns.py` (``examples/creating_models/plot_create_from_gmns.py``) + - 00:00.000 + - 0.0 + * - :ref:`sphx_glr__auto_examples_editing_networks_plot_moving_link_extremity.py` (``examples/editing_networks/plot_moving_link_extremity.py``) + - 00:00.000 + - 0.0 + * - :ref:`sphx_glr__auto_examples_editing_networks_plot_moving_nodes.py` (``examples/editing_networks/plot_moving_nodes.py``) + - 00:00.000 + - 0.0 + * - :ref:`sphx_glr__auto_examples_editing_networks_plot_splitting_link.py` (``examples/editing_networks/plot_splitting_link.py``) + - 00:00.000 + - 0.0 + * - :ref:`sphx_glr__auto_examples_other_applications_plot_check_logging.py` (``examples/other_applications/plot_check_logging.py``) + - 00:00.000 + - 0.0 + * - :ref:`sphx_glr__auto_examples_other_applications_plot_export_to_gmns.py` (``examples/other_applications/plot_export_to_gmns.py``) + - 00:00.000 + - 0.0 + * - :ref:`sphx_glr__auto_examples_other_applications_plot_find_disconnected.py` (``examples/other_applications/plot_find_disconnected.py``) + - 00:00.000 + - 0.0 + * - :ref:`sphx_glr__auto_examples_other_applications_plot_logging_to_terminal.py` (``examples/other_applications/plot_logging_to_terminal.py``) + - 00:00.000 + - 0.0 + * - :ref:`sphx_glr__auto_examples_trip_distribution_plot_ipf_without_model.py` (``examples/trip_distribution/plot_ipf_without_model.py``) + - 00:00.000 + - 0.0 + * - :ref:`sphx_glr__auto_examples_visualization_plot_delaunay_lines.py` (``examples/visualization/plot_delaunay_lines.py``) + - 00:00.000 + - 0.0 + * - :ref:`sphx_glr__auto_examples_visualization_plot_display.py` (``examples/visualization/plot_display.py``) + - 00:00.000 + - 0.0 From 07279adb6886599b19dc493e4946e7962ceca425 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Mon, 9 Sep 2024 08:23:13 -0300 Subject: [PATCH 29/57] removes data_model tables --- .gitignore | 2 + .../project_database/data_model/about.rst | 57 --------- .../data_model/attributes_documentation.rst | 46 ------- .../project_database/data_model/datamodel.rst | 42 ------- .../data_model/link_types.rst | 61 ---------- .../project_database/data_model/links.rst | 108 ----------------- .../project_database/data_model/matrices.rst | 56 --------- .../project_database/data_model/modes.rst | 64 ---------- .../project_database/data_model/nodes.rst | 60 ---------- .../project_database/data_model/periods.rst | 39 ------ .../project_database/data_model/results.rst | 50 -------- .../data_model/transit_graph_configs.rst | 29 ----- .../project_database/data_model/zones.rst | 46 ------- .../transit_database/data_model/agencies.rst | 48 -------- .../data_model/attributes_documentation.rst | 46 ------- .../transit_database/data_model/datamodel.rst | 50 -------- .../data_model/fare_attributes.rst | 62 ---------- .../data_model/fare_rules.rst | 51 -------- .../data_model/fare_zones.rst | 37 ------ .../data_model/link_types.rst | 61 ---------- .../transit_database/data_model/links.rst | 112 ------------------ .../transit_database/data_model/modes.rst | 64 ---------- .../data_model/node_types.rst | 51 -------- .../transit_database/data_model/nodes.rst | 87 -------------- .../data_model/pattern_mapping.rst | 49 -------- .../transit_database/data_model/results.rst | 50 -------- .../data_model/route_links.rst | 58 --------- .../transit_database/data_model/routes.rst | 75 ------------ .../data_model/stop_connectors.rst | 52 -------- .../transit_database/data_model/stops.rst | 85 ------------- .../data_model/trigger_settings.rst | 29 ----- .../transit_database/data_model/trips.rst | 42 ------- .../data_model/trips_schedule.rst | 42 ------- 33 files changed, 2 insertions(+), 1809 deletions(-) delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/about.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/attributes_documentation.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/datamodel.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/link_types.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/links.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/matrices.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/modes.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/nodes.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/periods.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/results.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/transit_graph_configs.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/zones.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/agencies.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/attributes_documentation.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/datamodel.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/fare_attributes.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/fare_rules.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/fare_zones.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/link_types.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/links.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/modes.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/node_types.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/nodes.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/pattern_mapping.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/results.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/route_links.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/routes.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/stop_connectors.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/stops.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/trigger_settings.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/trips.rst delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/trips_schedule.rst diff --git a/.gitignore b/.gitignore index a47170f96..da3dd9c7d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ _static _templates docs/source/_auto_examples docs/source/api/generated +docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model +docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model # User-specific stuff: .idea/**/workspace.xml diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/about.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/about.rst deleted file mode 100644 index 96b7f364e..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/about.rst +++ /dev/null @@ -1,57 +0,0 @@ -*about* table structure ------------------------ - -The *about* table holds information about the AequilibraE model -currently developed. - -The **infoname** field holds the name of information being added - -The **infovalue** field holds the information to add - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - infoname,TEXT,NO, - infovalue,TEXT,YES, - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - - CREATE TABLE if not exists about (infoname TEXT UNIQUE NOT NULL, - infovalue TEXT - ); - INSERT INTO 'about' (infoname) VALUES('model_name'); - - INSERT INTO 'about' (infoname) VALUES('region'); - - INSERT INTO 'about' (infoname) VALUES('description'); - - INSERT INTO 'about' (infoname) VALUES('author'); - - INSERT INTO 'about' (infoname) VALUES('year'); - - INSERT INTO 'about' (infoname) VALUES('scenario_description'); - - INSERT INTO 'about' (infoname) VALUES('model_version'); - - INSERT INTO 'about' (infoname) VALUES('project_id'); - - INSERT INTO 'about' (infoname) VALUES('aequilibrae_version'); - - INSERT INTO 'about' (infoname) VALUES('projection'); - - INSERT INTO 'about' (infoname) VALUES('driving_side'); - - INSERT INTO 'about' (infoname) VALUES('license'); - - INSERT INTO 'about' (infoname) VALUES('scenario_name'); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/attributes_documentation.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/attributes_documentation.rst deleted file mode 100644 index 8151c7b95..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/attributes_documentation.rst +++ /dev/null @@ -1,46 +0,0 @@ -*attributes_documentation* table structure ------------------------------------------- - -The *attributes_documentation* table holds information about attributes -in the tables links, link_types, modes, nodes, and zones. - -By default, these attributes are all documented, but further -attribues can be added into the table. - -The **name_table** field holds the name of the table that has the attribute - -The **attribute** field holds the name of the attribute - -The **description** field holds the description of the attribute - -It is possible to have one attribute with the same name in two -different tables. However, one cannot have two attibutes with the -same name within the same table. - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - name_table,TEXT,NO, - attribute,TEXT,NO, - description,TEXT,YES, - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - - CREATE TABLE if not exists attributes_documentation (name_table TEXT NOT NULL, - attribute TEXT NOT NULL, - description TEXT, - UNIQUE (name_table, attribute) - ); - - CREATE INDEX idx_attributes ON attributes_documentation (name_table, attribute); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/datamodel.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/datamodel.rst deleted file mode 100644 index 77af94a69..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/datamodel.rst +++ /dev/null @@ -1,42 +0,0 @@ -:orphan: - -.. _supply_data_model: - -SQL Data model -^^^^^^^^^^^^^^ - -The data model presented in this section pertains only to the structure of -AequilibraE's project_database and general information about the usefulness -of specific fields, especially on the interdependency between tables. - -Conventions -''''''''''' - -A few conventions have been adopted in the definition of the data model and some -are listed below: - -- Geometry field is always called **geometry** -- Projection is 4326 (WGS84) -- Tables are all in all lower case - - -.. Do not touch below this line unless you know EXACTLY what you are doing. -.. it will be automatically populated - -Project tables -'''''''''''''' - -.. toctree:: - :maxdepth: 1 - - about.rst - attributes_documentation.rst - link_types.rst - links.rst - matrices.rst - modes.rst - nodes.rst - periods.rst - results.rst - transit_graph_configs.rst - zones.rst diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/link_types.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/link_types.rst deleted file mode 100644 index 846d7c02a..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/link_types.rst +++ /dev/null @@ -1,61 +0,0 @@ -*link_types* table structure ----------------------------- - -The *link_types* table holds information about the available -link types in the network. - -The **link_type** field corresponds to the link type, and it is the -table's primary key - -The **link_type_id** field presents the identification of the link type - -The **description** field holds the description of the link type - -The **lanes** field presents the number or lanes for the link type - -The **lane_capacity** field presents the number of lanes for the link type - -The **speed** field holds information about the speed in the link type -Attributes follow - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - link_type*,VARCHAR,NO, - link_type_id,VARCHAR,NO, - description,VARCHAR,YES, - lanes,NUMERIC,YES, - lane_capacity,NUMERIC,YES, - speed,NUMERIC,YES, - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - - CREATE TABLE if not exists link_types (link_type VARCHAR UNIQUE NOT NULL PRIMARY KEY, - link_type_id VARCHAR UNIQUE NOT NULL, - description VARCHAR, - lanes NUMERIC, - lane_capacity NUMERIC, - speed NUMERIC - CHECK(LENGTH(link_type_id) == 1)); - - INSERT INTO 'link_types' (link_type, link_type_id, description, lanes, lane_capacity) VALUES('centroid_connector', 'z', 'VIRTUAL centroid connectors only', 10, 10000); - - INSERT INTO 'link_types' (link_type, link_type_id, description, lanes, lane_capacity) VALUES('default', 'y', 'Default general link type', 2, 900); - - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('link_types','link_type', 'Link type name. E.g. arterial, or connector'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('link_types','link_type_id', 'Single letter identifying the mode. E.g. a, for arterial'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('link_types','description', 'Description of the same. E.g. Arterials are streets like AequilibraE Avenue'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('link_types','lanes', 'Default number of lanes in each direction. E.g. 2'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('link_types','lane_capacity', 'Default vehicle capacity per lane. E.g. 900'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('link_types','speed', 'Free flow velocity in m/s'); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/links.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/links.rst deleted file mode 100644 index 0ba5d80b1..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/links.rst +++ /dev/null @@ -1,108 +0,0 @@ -*links* table structure ------------------------ - -The links table holds all the links available in the aequilibrae network model -regardless of the modes allowed on it. - -All information on the fields a_node and b_node correspond to a entries in -the node_id field in the nodes table. They are automatically managed with -triggers as the user edits the network, but they are not protected by manual -editing, which would break the network if it were to happen. - -The **modes** field is a concatenation of all the ids (mode_id) of the models allowed -on each link, and map directly to the mode_id field in the **Modes** table. A mode -can only be added to a link if it exists in the **Modes** table. - -The **link_type** corresponds to the *link_type* field from the *link_types* table. -As it is the case for modes, a link_type can only be assigned to a link if it exists -in the **link_types** table. - -The fields **length**, **node_a** and **node_b** are automatically -updated by triggers based in the links' geometries and node positions. Link length -is always measured in **meters**. - -The table is indexed on **link_id** (its primary key), **node_a** and **node_b**. - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - ogc_fid*,INTEGER,YES, - link_id,INTEGER,NO, - a_node,INTEGER,YES, - b_node,INTEGER,YES, - direction,INTEGER,NO,0 - distance,NUMERIC,YES, - modes,TEXT,NO, - link_type,TEXT,YES, - name,TEXT,YES, - speed_ab,NUMERIC,YES, - speed_ba,NUMERIC,YES, - travel_time_ab,NUMERIC,YES, - travel_time_ba,NUMERIC,YES, - capacity_ab,NUMERIC,YES, - capacity_ba,NUMERIC,YES, - geometry,LINESTRING,NO,'' - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - - CREATE TABLE if not exists links (ogc_fid INTEGER PRIMARY KEY, - link_id INTEGER NOT NULL UNIQUE, - a_node INTEGER, - b_node INTEGER, - direction INTEGER NOT NULL DEFAULT 0, - distance NUMERIC, - modes TEXT NOT NULL, - link_type TEXT REFERENCES link_types(link_type) ON update RESTRICT ON delete RESTRICT, - 'name' TEXT, - speed_ab NUMERIC, - speed_ba NUMERIC, - travel_time_ab NUMERIC, - travel_time_ba NUMERIC, - capacity_ab NUMERIC, - capacity_ba NUMERIC - CHECK(TYPEOF(link_id) == 'integer') - CHECK(TYPEOF(a_node) == 'integer') - CHECK(TYPEOF(b_node) == 'integer') - CHECK(TYPEOF(direction) == 'integer') - CHECK(LENGTH(modes)>0) - CHECK(LENGTH(direction)==1)); - - select AddGeometryColumn( 'links', 'geometry', 4326, 'LINESTRING', 'XY', 1); - - CREATE UNIQUE INDEX idx_link ON links (link_id); - - SELECT CreateSpatialIndex( 'links' , 'geometry' ); - - CREATE INDEX idx_link_anode ON links (a_node); - - CREATE INDEX idx_link_bnode ON links (b_node); - - CREATE INDEX idx_link_modes ON links (modes); - - CREATE INDEX idx_link_link_type ON links (link_type); - - CREATE INDEX idx_links_a_node_b_node ON links (a_node, b_node); - - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','link_id', 'Unique link ID'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','a_node', 'origin node for the link'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','b_node', 'destination node for the link'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','direction', 'Flow direction allowed on the link'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','distance', 'length of the link'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','modes', 'modes allowed on the link'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','link_type', 'Link type'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','name', 'Name of the street/link'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','speed_*', 'Directional speeds (if allowed)'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','capacity_*', 'Directional link capacities (if allowed)'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','travel_time_*', 'Directional free-flow travel time (if allowed)'); - diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/matrices.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/matrices.rst deleted file mode 100644 index 620a46c66..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/matrices.rst +++ /dev/null @@ -1,56 +0,0 @@ -*matrices* table structure --------------------------- - -The *matrices* table holds infromation about all matrices that exists in the -project *matrix* folder. - -The **name** field presents the name of the table. - -The **file_name** field holds the file name. - -The **cores** field holds the information on the number of cores used. - -The **procedure** field holds the name the the procedure that generated -the result (e.g.: Traffic Assignment). - -The **procedure_id** field holds an unique alpha-numeric identifier for -this prodecure. - -The **timestamp** field holds the information when the procedure was executed. - -The **description** field holds the user-provided description of the result. - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - name*,TEXT,NO, - file_name,TEXT,NO, - cores,INTEGER,NO,1 - procedure,TEXT,YES, - procedure_id,TEXT,YES, - timestamp,DATETIME,YES,current_timestamp - description,TEXT,YES, - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - - create TABLE if not exists matrices (name TEXT NOT NULL PRIMARY KEY, - file_name TEXT NOT NULL UNIQUE, - cores INTEGER NOT NULL DEFAULT 1, - procedure TEXT, - procedure_id TEXT, - timestamp DATETIME DEFAULT current_timestamp, - description TEXT); - - - CREATE INDEX name_matrices ON matrices (name); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/modes.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/modes.rst deleted file mode 100644 index 939e88669..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/modes.rst +++ /dev/null @@ -1,64 +0,0 @@ -*modes* table structure ------------------------ - -The *modes* table holds the information on all the modes available in -the model's network. - -The **mode_name** field contains the descriptive name of the field. - -The **mode_id** field contains a single letter that identifies the mode. - -The **description** field holds the description of the mode. - -The **pce** field holds information on Passenger-Car equivalent -for assignment. Defaults to **1.0**. - -The **vot** field holds information on Value-of-Time for traffic -assignment. Defaults to **0.0**. - -The **ppv** field holds information on average persons per vehicle. -Defaults to **1.0**. **ppv** can assume value 0 for non-travel uses. -Attributes follow - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - mode_name,VARCHAR,NO, - mode_id*,VARCHAR,NO, - description,VARCHAR,YES, - pce,NUMERIC,NO,1.0 - vot,NUMERIC,NO,0 - ppv,NUMERIC,NO,1.0 - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - - CREATE TABLE if not exists modes (mode_name VARCHAR UNIQUE NOT NULL, - mode_id VARCHAR UNIQUE NOT NULL PRIMARY KEY, - description VARCHAR, - pce NUMERIC NOT NULL DEFAULT 1.0, - vot NUMERIC NOT NULL DEFAULT 0, - ppv NUMERIC NOT NULL DEFAULT 1.0 - CHECK(LENGTH(mode_id)==1)); - - INSERT INTO 'modes' (mode_name, mode_id, description) VALUES('car', 'c', 'All motorized vehicles'); - INSERT INTO 'modes' (mode_name, mode_id, description) VALUES('transit', 't', 'Public transport vehicles'); - INSERT INTO 'modes' (mode_name, mode_id, description) VALUES('walk', 'w', 'Walking links'); - INSERT INTO 'modes' (mode_name, mode_id, description) VALUES('bicycle', 'b', 'Biking links'); - - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('modes','mode_name', 'The more descriptive name of the mode (e.g. Bicycle)'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('modes','mode_id', 'Single letter identifying the mode. E.g. b, for Bicycle'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('modes','description', 'Description of the same. E.g. Bicycles used to be human-powered two-wheeled vehicles'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('modes','pce', 'Passenger-Car equivalent for assignment'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('modes','vot', 'Value-of-Time for traffic assignment of class'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('modes','ppv', 'Average persons per vehicle. (0 for non-travel uses)'); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/nodes.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/nodes.rst deleted file mode 100644 index 59de23f95..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/nodes.rst +++ /dev/null @@ -1,60 +0,0 @@ -*nodes* table structure ------------------------ - -The *nodes* table holds all the network nodes available in AequilibraE model. - -The **node_id** field is an identifier of the node. - -The **is_centroid** field holds information if the node is a centroid -of a network or not. Assumes values 0 or 1. Defaults to **0**. - -The **modes** field identifies all modes connected to the node. - -The **link_types** field identifies all link types connected -to the node. - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - ogc_fid*,INTEGER,YES, - node_id,INTEGER,NO, - is_centroid,INTEGER,NO,0 - modes,TEXT,YES, - link_types,TEXT,YES, - geometry,POINT,NO,'' - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - - CREATE TABLE if not exists nodes (ogc_fid INTEGER PRIMARY KEY, - node_id INTEGER UNIQUE NOT NULL, - is_centroid INTEGER NOT NULL DEFAULT 0, - modes TEXT, - link_types TEXT - CHECK(TYPEOF(node_id) == 'integer') - CHECK(TYPEOF(is_centroid) == 'integer') - CHECK(is_centroid>=0) - CHECK(is_centroid<=1)); - - SELECT AddGeometryColumn( 'nodes', 'geometry', 4326, 'POINT', 'XY', 1); - - SELECT CreateSpatialIndex( 'nodes' , 'geometry' ); - - CREATE INDEX idx_node ON nodes (node_id); - - CREATE INDEX idx_node_is_centroid ON nodes (is_centroid); - - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('nodes','node_id', 'Unique node ID'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('nodes','is_centroid', 'Flag identifying centroids'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('nodes','modes', 'Modes connected to the node'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('nodes','link_types', 'Link types connected to the node'); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/periods.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/periods.rst deleted file mode 100644 index 42cd0b1e6..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/periods.rst +++ /dev/null @@ -1,39 +0,0 @@ -*periods* table structure -------------------------- - -The periods table holds the time periods and their period_id. Default entry with id 1 is the entire day. -Attributes follow - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - period_id,INTEGER,NO, - period_start,INTEGER,NO, - period_end,INTEGER,NO, - period_description,TEXT,YES, - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - CREATE TABLE if not exists periods (period_id INTEGER UNIQUE NOT NULL, - period_start INTEGER NOT NULL, - period_end INTEGER NOT NULL, - period_description TEXT - CHECK(TYPEOF(period_id) == 'integer') - CHECK(TYPEOF(period_start) == 'integer') - CHECK(TYPEOF(period_end) == 'integer')); - - INSERT INTO periods (period_id, period_start, period_end, period_description) VALUES(1, 0, 86400, 'Default time period, whole day'); - - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('periods','period_id', 'ID of the time period'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('periods','period_start', 'Start of the time period'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('periods','period_end', 'End of the time period'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('periods','period_description', 'Optional description of the time period'); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/results.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/results.rst deleted file mode 100644 index 504d5da51..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/results.rst +++ /dev/null @@ -1,50 +0,0 @@ -*results* table structure -------------------------- - -The *results* table holds the metadata for results stored in -*results_database.sqlite*. - -The **table_name** field presents the actual name of the result -table in *results_database.sqlite*. - -The **procedure** field holds the name the the procedure that generated -the result (e.g.: Traffic Assignment). - -The **procedure_id** field holds an unique UUID identifier for this procedure, -which is created at runtime. - -The **procedure_report** field holds the output of the complete procedure report. - -The **timestamp** field holds the information when the procedure was executed. - -The **description** field holds the user-provided description of the result. - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - table_name*,TEXT,NO, - procedure,TEXT,NO, - procedure_id,TEXT,NO, - procedure_report,TEXT,NO, - timestamp,DATETIME,YES,current_timestamp - description,TEXT,YES, - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - - create TABLE if not exists results (table_name TEXT NOT NULL PRIMARY KEY, - procedure TEXT NOT NULL, - procedure_id TEXT NOT NULL, - procedure_report TEXT NOT NULL, - timestamp DATETIME DEFAULT current_timestamp, - description TEXT); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/transit_graph_configs.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/transit_graph_configs.rst deleted file mode 100644 index 44332333e..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/transit_graph_configs.rst +++ /dev/null @@ -1,29 +0,0 @@ -*transit_graph_configs* table structure ---------------------------------------- - -The *transit_graph_configs* table holds configuration parameters for a TransitGraph of a particular `period_id` -Attributes follow - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - period_id*,INTEGER,NO, - config,TEXT,YES, - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - CREATE TABLE if not exists transit_graph_configs (period_id INTEGER UNIQUE NOT NULL PRIMARY KEY REFERENCES periods(period_id), - config TEXT); - - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('transit_graph_configs','period_id', 'The period this config is associated with.'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('transit_graph_configs','mode_id', 'JSON string containing the configuration parameters.'); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/zones.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/zones.rst deleted file mode 100644 index 34d6d0393..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model/zones.rst +++ /dev/null @@ -1,46 +0,0 @@ -*zones* table structure ------------------------ - -The *zones* table holds information on the Traffic Analysis Zones (TAZs) -in AequilibraE's model. - -The **zone_id** field identifies the zone. - -The **area** field corresponds to the area of the zone in **km2**. -TAZs' area is automatically updated by triggers. - -The **name** fields allows one to identity the zone using a name -or any other description. - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - ogc_fid*,INTEGER,YES, - zone_id,INTEGER,NO, - area,NUMERIC,YES, - name,TEXT,YES, - geometry,MULTIPOLYGON,NO,'' - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - CREATE TABLE 'zones' (ogc_fid INTEGER PRIMARY KEY, - zone_id INTEGER UNIQUE NOT NULL, - area NUMERIC, - "name" TEXT); - - SELECT AddGeometryColumn( 'zones', 'geometry', 4326, 'MULTIPOLYGON', 'XY', 1); - CREATE UNIQUE INDEX idx_zone ON zones (zone_id); - SELECT CreateSpatialIndex( 'zones' , 'geometry' ); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('zones','zone_id', 'Unique node ID'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('zones','area', 'Area of the zone in km2'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('zones','name', 'Name of the zone, if any'); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/agencies.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/agencies.rst deleted file mode 100644 index d36eff2a8..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/agencies.rst +++ /dev/null @@ -1,48 +0,0 @@ -*agencies* table structure --------------------------- - -The *agencies* table holds information about the Public Transport -agencies within the GTFS data. This table information comes from -GTFS file *agency.txt*. -You can check out more information `here `_. - -**agency_id** identifies the agency for the specified route - -**agency** contains the fuill name of the transit agency - -**feed_date** idicates the date for which the GTFS feed is being imported - -**service_date** indicates the date for the indicate route scheduling - -**description_field** provides useful description of a transit agency - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - agency_id*,INTEGER,NO, - agency,TEXT,NO, - feed_date,TEXT,YES, - service_date,TEXT,YES, - description,TEXT,YES, - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - create TABLE IF NOT EXISTS agencies ( - agency_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, - agency TEXT NOT NULL, - feed_date TEXT, - service_date TEXT, - description TEXT - ); - - create UNIQUE INDEX IF NOT EXISTS transit_operators_id ON agencies (agency_id); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/attributes_documentation.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/attributes_documentation.rst deleted file mode 100644 index 8151c7b95..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/attributes_documentation.rst +++ /dev/null @@ -1,46 +0,0 @@ -*attributes_documentation* table structure ------------------------------------------- - -The *attributes_documentation* table holds information about attributes -in the tables links, link_types, modes, nodes, and zones. - -By default, these attributes are all documented, but further -attribues can be added into the table. - -The **name_table** field holds the name of the table that has the attribute - -The **attribute** field holds the name of the attribute - -The **description** field holds the description of the attribute - -It is possible to have one attribute with the same name in two -different tables. However, one cannot have two attibutes with the -same name within the same table. - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - name_table,TEXT,NO, - attribute,TEXT,NO, - description,TEXT,YES, - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - - CREATE TABLE if not exists attributes_documentation (name_table TEXT NOT NULL, - attribute TEXT NOT NULL, - description TEXT, - UNIQUE (name_table, attribute) - ); - - CREATE INDEX idx_attributes ON attributes_documentation (name_table, attribute); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/datamodel.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/datamodel.rst deleted file mode 100644 index 0371315ec..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/datamodel.rst +++ /dev/null @@ -1,50 +0,0 @@ -:orphan: - -.. _transit_supply_data_model: - -SQL Data model -^^^^^^^^^^^^^^ - -The data model presented in this section pertains only to the structure of -AequilibraE's public_transport database and general information about the usefulness -of specific fields, especially on the interdependency between tables. - -Conventions -''''''''''' - -A few conventions have been adopted in the definition of the data model and some -are listed below: - -- Geometry field is always called **geometry** -- Projection is 4326 (WGS84) -- Tables are all in all lower case - - -.. Do not touch below this line unless you know EXACTLY what you are doing. -.. it will be automatically populated - -Project tables -'''''''''''''' - -.. toctree:: - :maxdepth: 1 - - agencies.rst - attributes_documentation.rst - fare_attributes.rst - fare_rules.rst - fare_zones.rst - link_types.rst - links.rst - modes.rst - node_types.rst - nodes.rst - pattern_mapping.rst - results.rst - route_links.rst - routes.rst - stop_connectors.rst - stops.rst - trigger_settings.rst - trips.rst - trips_schedule.rst diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/fare_attributes.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/fare_attributes.rst deleted file mode 100644 index a32ca14e5..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/fare_attributes.rst +++ /dev/null @@ -1,62 +0,0 @@ -*fare_attributes* table structure ---------------------------------- - -The *fare_attributes* table holds information about the fare values. -This table information comes from the GTFS file *fare_attributes.txt*. -Given that this file is optional in GTFS, it can be empty. -You can check out more information `here `_. - -**fare_id** identifies a fare class - -**fare** describes a fare class - -**agency_id** identifies a relevant agency for a fare. - -**price** especifies the fare price - -**currency_code** especifies the currency used to pay the fare - -**payment_method** indicates when the fare must be paid. - -**transfer** indicates the number of transfers permitted on the fare - -**transfer_duration** indicates the lenght of time in seconds before a -transfer expires. - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - fare_id*,INTEGER,NO, - fare,TEXT,NO, - agency_id,INTEGER,NO, - price,REAL,YES, - currency,TEXT,YES, - payment_method,INTEGER,YES, - transfer,INTEGER,YES, - transfer_duration,REAL,YES, - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - create TABLE IF NOT EXISTS fare_attributes ( - fare_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, - fare TEXT NOT NULL, - agency_id INTEGER NOT NULL, - price REAL, - currency TEXT, - payment_method INTEGER, - transfer INTEGER, - transfer_duration REAL, - FOREIGN KEY(agency_id) REFERENCES agencies(agency_id) deferrable initially deferred - ); - - CREATE UNIQUE INDEX IF NOT EXISTS fare_transfer_uniqueness ON fare_attributes (fare_id, transfer); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/fare_rules.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/fare_rules.rst deleted file mode 100644 index 1074ba138..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/fare_rules.rst +++ /dev/null @@ -1,51 +0,0 @@ -*fare_rules* table structure ----------------------------- - -The *fare_rules* table holds information about the fare values. -This table information comes from the GTFS file *fare_rules.txt*. -Given that this file is optional in GTFS, it can be empty. - -The **fare_id** identifies a fare class - -The **route_id** identifies a route associated with the fare class. - -The **origin** field identifies the origin zone - -The **destination** field identifies the destination zone - -The **contains** field identifies the zones that a rider will enter while using -a given fare class. - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - fare_id,INTEGER,NO, - route_id,INTEGER,YES, - origin,INTEGER,YES, - destination,INTEGER,YES, - contains,INTEGER,YES, - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - create TABLE IF NOT EXISTS fare_rules ( - fare_id INTEGER NOT NULL, - route_id INTEGER, - origin INTEGER, - destination INTEGER, - contains INTEGER, - FOREIGN KEY(fare_id) REFERENCES fare_attributes(fare_id) deferrable initially deferred, - FOREIGN KEY(route_id) REFERENCES routes(route_id) deferrable initially deferred, - FOREIGN KEY(destination) REFERENCES fare_zones(fare_zone_id) deferrable initially deferred, - FOREIGN KEY(origin) REFERENCES fare_zones(fare_zone_id) deferrable initially deferred, - FOREIGN KEY(contains) REFERENCES fare_zones(fare_zone_id) deferrable initially deferred - ); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/fare_zones.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/fare_zones.rst deleted file mode 100644 index e6d7d32e4..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/fare_zones.rst +++ /dev/null @@ -1,37 +0,0 @@ -*fare_zones* table structure ----------------------------- - -The *zones* tables holds information on the fare transit zones and -the TAZs they are in. - -**fare_zone_id** identifies the fare zone for a stop - -**transit_zone** identifies the TAZ for a fare zone - -**agency_id** identifies the agency fot the specified route - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - fare_zone_id*,INTEGER,YES, - transit_zone,TEXT,NO, - agency_id,INTEGER,NO, - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - CREATE TABLE IF NOT EXISTS fare_zones ( - fare_zone_id INTEGER PRIMARY KEY, - transit_zone TEXT NOT NULL, - agency_id INTEGER NOT NULL, - FOREIGN KEY(agency_id) REFERENCES agencies(agency_id) deferrable initially deferred - ); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/link_types.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/link_types.rst deleted file mode 100644 index 846d7c02a..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/link_types.rst +++ /dev/null @@ -1,61 +0,0 @@ -*link_types* table structure ----------------------------- - -The *link_types* table holds information about the available -link types in the network. - -The **link_type** field corresponds to the link type, and it is the -table's primary key - -The **link_type_id** field presents the identification of the link type - -The **description** field holds the description of the link type - -The **lanes** field presents the number or lanes for the link type - -The **lane_capacity** field presents the number of lanes for the link type - -The **speed** field holds information about the speed in the link type -Attributes follow - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - link_type*,VARCHAR,NO, - link_type_id,VARCHAR,NO, - description,VARCHAR,YES, - lanes,NUMERIC,YES, - lane_capacity,NUMERIC,YES, - speed,NUMERIC,YES, - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - - CREATE TABLE if not exists link_types (link_type VARCHAR UNIQUE NOT NULL PRIMARY KEY, - link_type_id VARCHAR UNIQUE NOT NULL, - description VARCHAR, - lanes NUMERIC, - lane_capacity NUMERIC, - speed NUMERIC - CHECK(LENGTH(link_type_id) == 1)); - - INSERT INTO 'link_types' (link_type, link_type_id, description, lanes, lane_capacity) VALUES('centroid_connector', 'z', 'VIRTUAL centroid connectors only', 10, 10000); - - INSERT INTO 'link_types' (link_type, link_type_id, description, lanes, lane_capacity) VALUES('default', 'y', 'Default general link type', 2, 900); - - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('link_types','link_type', 'Link type name. E.g. arterial, or connector'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('link_types','link_type_id', 'Single letter identifying the mode. E.g. a, for arterial'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('link_types','description', 'Description of the same. E.g. Arterials are streets like AequilibraE Avenue'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('link_types','lanes', 'Default number of lanes in each direction. E.g. 2'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('link_types','lane_capacity', 'Default vehicle capacity per lane. E.g. 900'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('link_types','speed', 'Free flow velocity in m/s'); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/links.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/links.rst deleted file mode 100644 index ad6ca3dd4..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/links.rst +++ /dev/null @@ -1,112 +0,0 @@ -*links* table structure ------------------------ - -The links table holds all the links available in the aequilibrae network model -regardless of the modes allowed on it. - -All information on the fields a_node and b_node correspond to a entries in -the node_id field in the nodes table. They are automatically managed with -triggers as the user edits the network, but they are not protected by manual -editing, which would break the network if it were to happen. - -The **modes** field is a concatenation of all the ids (mode_id) of the models allowed -on each link, and map directly to the mode_id field in the **Modes** table. A mode -can only be added to a link if it exists in the **Modes** table. - -The **link_type** corresponds to the *link_type* field from the *link_types* table. -As it is the case for modes, a link_type can only be assigned to a link if it exists -in the **link_types** table. - -The fields **length**, **node_a** and **node_b** are automatically -updated by triggers based in the links' geometries and node positions. Link length -is always measured in **meters**. - -The table is indexed on **link_id** (its primary key), **node_a** and **node_b**. - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - ogc_fid*,INTEGER,YES, - link_id,INTEGER,NO, - a_node,INTEGER,YES, - b_node,INTEGER,YES, - direction,INTEGER,NO,0 - distance,NUMERIC,YES, - modes,TEXT,NO, - link_type,TEXT,YES, - line_id,TEXT,YES, - stop_id,TEXT,YES, - line_seg_idx,INTEGER,YES, - trav_time,NUMERIC,NO, - freq,NUMERIC,NO, - o_line_id,TEXT,YES, - d_line_id,TEXT,YES, - transfer_id,TEXT,YES, - geometry,LINESTRING,NO,'' - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - - CREATE TABLE if not exists links (ogc_fid INTEGER PRIMARY KEY, - link_id INTEGER NOT NULL UNIQUE, - a_node INTEGER, - b_node INTEGER, - direction INTEGER NOT NULL DEFAULT 0, - distance NUMERIC, - modes TEXT NOT NULL, - link_type TEXT REFERENCES link_types(link_type) ON update RESTRICT ON delete RESTRICT, - line_id TEXT, - stop_id TEXT REFERENCES stops(stop) ON update RESTRICT ON delete RESTRICT, - line_seg_idx INTEGER, - trav_time NUMERIC NOT NULL, - freq NUMERIC NOT NULL, - o_line_id TEXT, - d_line_id TEXT, - transfer_id TEXT - CHECK(TYPEOF(link_id) == 'integer') - CHECK(TYPEOF(a_node) == 'integer') - CHECK(TYPEOF(b_node) == 'integer') - CHECK(TYPEOF(direction) == 'integer') - CHECK(LENGTH(modes)>0) - CHECK(LENGTH(direction)==1)); - - select AddGeometryColumn( 'links', 'geometry', 4326, 'LINESTRING', 'XY', 1); - - CREATE UNIQUE INDEX idx_link ON links (link_id); - - SELECT CreateSpatialIndex( 'links' , 'geometry' ); - - CREATE INDEX idx_link_anode ON links (a_node); - - CREATE INDEX idx_link_bnode ON links (b_node); - - CREATE INDEX idx_link_modes ON links (modes); - - CREATE INDEX idx_link_link_type ON links (link_type); - - CREATE INDEX idx_links_a_node_b_node ON links (a_node, b_node); - - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','link_id', 'Unique link ID'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','a_node', 'origin node for the link'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','b_node', 'destination node for the link'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','direction', 'Flow direction allowed on the link'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','distance', 'length of the link'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','modes', 'modes allowed on the link'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','link_type', 'Link type'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','line_id', 'ID of the line the link belongs to'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','stop_id', 'ID of the stop the link belongss to '); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','line_seg_idx', 'Line segment index'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','trav_time', 'Travel time'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','freq', 'Frequency of link traversal'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','*_line_id', 'Origin/Destination line ID for transfer links'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('links','transfer_id', 'Transfer link ID'); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/modes.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/modes.rst deleted file mode 100644 index 939e88669..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/modes.rst +++ /dev/null @@ -1,64 +0,0 @@ -*modes* table structure ------------------------ - -The *modes* table holds the information on all the modes available in -the model's network. - -The **mode_name** field contains the descriptive name of the field. - -The **mode_id** field contains a single letter that identifies the mode. - -The **description** field holds the description of the mode. - -The **pce** field holds information on Passenger-Car equivalent -for assignment. Defaults to **1.0**. - -The **vot** field holds information on Value-of-Time for traffic -assignment. Defaults to **0.0**. - -The **ppv** field holds information on average persons per vehicle. -Defaults to **1.0**. **ppv** can assume value 0 for non-travel uses. -Attributes follow - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - mode_name,VARCHAR,NO, - mode_id*,VARCHAR,NO, - description,VARCHAR,YES, - pce,NUMERIC,NO,1.0 - vot,NUMERIC,NO,0 - ppv,NUMERIC,NO,1.0 - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - - CREATE TABLE if not exists modes (mode_name VARCHAR UNIQUE NOT NULL, - mode_id VARCHAR UNIQUE NOT NULL PRIMARY KEY, - description VARCHAR, - pce NUMERIC NOT NULL DEFAULT 1.0, - vot NUMERIC NOT NULL DEFAULT 0, - ppv NUMERIC NOT NULL DEFAULT 1.0 - CHECK(LENGTH(mode_id)==1)); - - INSERT INTO 'modes' (mode_name, mode_id, description) VALUES('car', 'c', 'All motorized vehicles'); - INSERT INTO 'modes' (mode_name, mode_id, description) VALUES('transit', 't', 'Public transport vehicles'); - INSERT INTO 'modes' (mode_name, mode_id, description) VALUES('walk', 'w', 'Walking links'); - INSERT INTO 'modes' (mode_name, mode_id, description) VALUES('bicycle', 'b', 'Biking links'); - - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('modes','mode_name', 'The more descriptive name of the mode (e.g. Bicycle)'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('modes','mode_id', 'Single letter identifying the mode. E.g. b, for Bicycle'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('modes','description', 'Description of the same. E.g. Bicycles used to be human-powered two-wheeled vehicles'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('modes','pce', 'Passenger-Car equivalent for assignment'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('modes','vot', 'Value-of-Time for traffic assignment of class'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('modes','ppv', 'Average persons per vehicle. (0 for non-travel uses)'); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/node_types.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/node_types.rst deleted file mode 100644 index 373e49faf..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/node_types.rst +++ /dev/null @@ -1,51 +0,0 @@ -*node_types* table structure ----------------------------- - -The *node_types* table holds information about the available -node types in the network. - -The **node_type** field corresponds to the node type, and it is the -table's primary key - -The **node_type_id** field presents the identification of the node type - -The **description** field holds the description of the node type - -Attributes follow - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - node_type*,VARCHAR,NO, - node_type_id,VARCHAR,NO, - description,VARCHAR,YES, - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - - CREATE TABLE if not exists node_types (node_type VARCHAR UNIQUE NOT NULL PRIMARY KEY, - node_type_id VARCHAR UNIQUE NOT NULL, - description VARCHAR); - - INSERT INTO 'node_types' (node_type, node_type_id, description) VALUES('default', 'y', 'Default general node type'); - INSERT INTO 'node_types' (node_type, node_type_id, description) VALUES('od', 'n', 'Origin/Desination node type'); - INSERT INTO 'node_types' (node_type, node_type_id, description) VALUES('origin', 'o', 'Origin node type'); - INSERT INTO 'node_types' (node_type, node_type_id, description) VALUES('destination', 'd', 'Desination node type'); - INSERT INTO 'node_types' (node_type, node_type_id, description) VALUES('stop', 's', 'Stop node type'); - INSERT INTO 'node_types' (node_type, node_type_id, description) VALUES('alighting', 'a', 'Alighting node type'); - INSERT INTO 'node_types' (node_type, node_type_id, description) VALUES('boarding', 'b', 'Boarding node type'); - INSERT INTO 'node_types' (node_type, node_type_id, description) VALUES('walking', 'w', 'Walking node type'); - - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('node_types','node_type', 'Node type name. E.g stop or boarding'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('node_types','node_type_id', 'Single letter identifying the mode. E.g. a, for alighting'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('node_types','description', 'Description of the same. E.g. Stop nodes connect ODs and walking nodes to boarding and alighting nodes via boarding and alighting links.'); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/nodes.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/nodes.rst deleted file mode 100644 index 6208dabbd..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/nodes.rst +++ /dev/null @@ -1,87 +0,0 @@ -*nodes* table structure ------------------------ - -The *nodes* table holds all the network nodes available in AequilibraE transit model. - -The **node_id** field is an identifier of the node. - -The **is_centroid** field holds information if the node is a centroid -of a network or not. Assumes values 0 or 1. Defaults to **0**. - -The **stop_id** field indicates which stop this node belongs too. This field -is TEXT as it might encode a street name or such. - -The **line_id** field indicates which line this node belongs too. This field -is TEXT as it might encode a street name or such. - -The **line_seg_idx** field indexes the segment of line **line_id**. Zero based. - -The **modes** field identifies all modes connected to the node. - -The **link_type** field identifies all link types connected to the node. - -The **node_type** field identifies the types of this node. - -The **taz_id** field is an identifier for the transit assignment zone this node -belongs to. - - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - ogc_fid*,INTEGER,YES, - node_id,INTEGER,NO, - is_centroid,INTEGER,NO,0 - stop_id,TEXT,YES, - line_id,TEXT,YES, - line_seg_idx,INTEGER,YES, - modes,TEXT,YES, - link_types,TEXT,YES, - node_type,TEXT,YES, - taz_id,TEXT,YES, - geometry,POINT,NO,'' - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - CREATE TABLE if not exists nodes (ogc_fid INTEGER PRIMARY KEY, - node_id INTEGER UNIQUE NOT NULL, - is_centroid INTEGER NOT NULL DEFAULT 0, - stop_id TEXT, - line_id TEXT, - line_seg_idx INTEGER, - modes TEXT, - link_types TEXT, - node_type TEXT, - taz_id TEXT - CHECK(TYPEOF(node_id) == 'integer') - CHECK(TYPEOF(is_centroid) == 'integer') - CHECK(is_centroid>=0) - CHECK(is_centroid<=1)); - - SELECT AddGeometryColumn( 'nodes', 'geometry', 4326, 'POINT', 'XY', 1); - - SELECT CreateSpatialIndex( 'nodes' , 'geometry' ); - - CREATE INDEX idx_node ON nodes (node_id); - - CREATE INDEX idx_node_is_centroid ON nodes (is_centroid); - - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('nodes','node_id', 'Unique node ID'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('nodes','is_centroid', 'Flag identifying centroids'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('nodes','stop_id', 'ID of the Stop this node belongs to'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('nodes','line_id', 'ID of the Line this node belongs to'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('nodes','line_seg_idx', 'Index of the line segement this node belongs to'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('nodes','modes', 'Modes connected to the node'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('nodes','link_types', 'Link types connected to the node'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('nodes','node_type', 'Node types of this node'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('nodes','taz_id', 'Transit assignemnt zone id'); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/pattern_mapping.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/pattern_mapping.rst deleted file mode 100644 index d20cb366f..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/pattern_mapping.rst +++ /dev/null @@ -1,49 +0,0 @@ -*pattern_mapping* table structure ---------------------------------- - -The *pattern_mapping* table holds information on the stop pattern -for each route. - -**pattern_id** is an unique pattern for the route - -**seq** identifies the sequence of the stops for a trip - -**link** identifies the *link_id* in the links table that corresponds to the -pattern matching - -**dir** indicates the direction of travel for a trip - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - pattern_id*,INTEGER,NO, - seq,INTEGER,NO, - link,INTEGER,NO, - dir,INTEGER,NO, - geometry,LINESTRING,YES, - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - CREATE TABLE IF NOT EXISTS pattern_mapping ( - pattern_id INTEGER NOT NULL, - seq INTEGER NOT NULL, - link INTEGER NOT NULL, - dir INTEGER NOT NULL, - PRIMARY KEY(pattern_id, "seq"), - FOREIGN KEY(pattern_id) REFERENCES routes (pattern_id) deferrable initially deferred, - FOREIGN KEY(link) REFERENCES route_links (link) deferrable initially deferred - ); - - SELECT AddGeometryColumn( 'pattern_mapping', 'geometry', 4326, 'LINESTRING', 'XY'); - - SELECT CreateSpatialIndex( 'pattern_mapping' , 'geometry' ); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/results.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/results.rst deleted file mode 100644 index 504d5da51..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/results.rst +++ /dev/null @@ -1,50 +0,0 @@ -*results* table structure -------------------------- - -The *results* table holds the metadata for results stored in -*results_database.sqlite*. - -The **table_name** field presents the actual name of the result -table in *results_database.sqlite*. - -The **procedure** field holds the name the the procedure that generated -the result (e.g.: Traffic Assignment). - -The **procedure_id** field holds an unique UUID identifier for this procedure, -which is created at runtime. - -The **procedure_report** field holds the output of the complete procedure report. - -The **timestamp** field holds the information when the procedure was executed. - -The **description** field holds the user-provided description of the result. - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - table_name*,TEXT,NO, - procedure,TEXT,NO, - procedure_id,TEXT,NO, - procedure_report,TEXT,NO, - timestamp,DATETIME,YES,current_timestamp - description,TEXT,YES, - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - - create TABLE if not exists results (table_name TEXT NOT NULL PRIMARY KEY, - procedure TEXT NOT NULL, - procedure_id TEXT NOT NULL, - procedure_report TEXT NOT NULL, - timestamp DATETIME DEFAULT current_timestamp, - description TEXT); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/route_links.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/route_links.rst deleted file mode 100644 index 0cc7bc538..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/route_links.rst +++ /dev/null @@ -1,58 +0,0 @@ -*route_links* table structure ------------------------------ - -The *route_links* table holds information on the links of a route. - -**transit_link** identifies the GTFS transit links for the route - -**pattern_id** is an unique pattern for the route - -**seq** identifies the sequence of the stops for a trip - -**from_stop** identifies the stop the vehicle is departing - -**to_stop** identifies the next stop the vehicle is going to arrive - -**distance** identifies the distance (in meters) the vehicle travel -between the stops - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - transit_link,INTEGER,NO, - pattern_id,INTEGER,NO, - seq,INTEGER,NO, - from_stop,INTEGER,NO, - to_stop,INTEGER,NO, - distance,INTEGER,NO, - geometry,LINESTRING,YES, - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - CREATE TABLE IF NOT EXISTS route_links ( - transit_link INTEGER NOT NULL, - pattern_id INTEGER NOT NULL, - seq INTEGER NOT NULL, - from_stop INTEGER NOT NULL, - to_stop INTEGER NOT NULL, - distance INTEGER NOT NULL, - FOREIGN KEY(pattern_id) REFERENCES "routes"(pattern_id) deferrable initially deferred, - FOREIGN KEY(from_stop) REFERENCES "stops"(stop_id) deferrable initially deferred - FOREIGN KEY(to_stop) REFERENCES "stops"(stop_id) deferrable initially deferred - ); - - create UNIQUE INDEX IF NOT EXISTS route_links_stop_id ON route_links (pattern_id, transit_link); - - select AddGeometryColumn( 'route_links', 'geometry', 4326, 'LINESTRING', 'XY'); - - select CreateSpatialIndex( 'route_links' , 'geometry' ); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/routes.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/routes.rst deleted file mode 100644 index 95a3cf5f4..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/routes.rst +++ /dev/null @@ -1,75 +0,0 @@ -*routes* table structure ------------------------- - -The *routes* table holds information on the available transit routes for a -specific day. This table information comes from the GTFS file *routes.txt*. -You can find more information about it `here `_. - -**pattern_id** is an unique pattern for the route - -**route_id** identifies a route - -**route** identifies the name of a route - -**agency_id** identifies the agency for the specified route - -**shortname** identifies the short name of a route - -**longname** identifies the long name of a route - -**description** provides useful description of a route - -**route_type** indicates the type of transportation used on a route - -**pce** indicates the passenger car equivalent for transportation used on a route - -**seated_capacity** indicates the seated capacity of a route - -**total_capacity** indicates the total capacity of a route - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - pattern_id*,INTEGER,NO, - route_id,INTEGER,NO, - route,TEXT,NO, - agency_id,INTEGER,NO, - shortname,TEXT,YES, - longname,TEXT,YES, - description,TEXT,YES, - route_type,INTEGER,NO, - pce,NUMERIC,NO,2.0 - seated_capacity,INTEGER,YES, - total_capacity,INTEGER,YES, - geometry,MULTILINESTRING,YES, - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - CREATE TABLE IF NOT EXISTS routes ( - pattern_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, - route_id INTEGER NOT NULL, - route TEXT NOT NULL, - agency_id INTEGER NOT NULL, - shortname TEXT, - longname TEXT, - description TEXT, - route_type INTEGER NOT NULL, - pce NUMERIC NOT NULL DEFAULT 2.0, - seated_capacity INTEGER, - total_capacity INTEGER, - FOREIGN KEY(agency_id) REFERENCES agencies(agency_id) deferrable initially deferred - ); - - select AddGeometryColumn( 'routes', 'geometry', 4326, 'MULTILINESTRING', 'XY'); - - select CreateSpatialIndex( 'routes' , 'geometry' ); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/stop_connectors.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/stop_connectors.rst deleted file mode 100644 index be9f20bc3..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/stop_connectors.rst +++ /dev/null @@ -1,52 +0,0 @@ -*stop_connectors* table structure ---------------------------------- - -The *stops_connectors* table holds information on the connection of -the GTFS network with the real network. - -**id_from** identifies the network link the vehicle departs - -**id_to** identifies the network link th vehicle is heading to - -**conn_type** identifies the type of connection used to connect the links - -**traversal_time** represents the time spent crossing the link - -**penalty_cost** identifies the penalty in the connection - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - id_from,INTEGER,NO, - id_to,INTEGER,NO, - conn_type,INTEGER,NO, - traversal_time,INTEGER,NO, - penalty_cost,INTEGER,NO, - geometry,LINESTRING,NO,'' - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - - CREATE TABLE IF NOT EXISTS stop_connectors ( - id_from INTEGER NOT NULL, - id_to INTEGER NOT NULL, - traversal_time INTEGER NOT NULL, - penalty_cost INTEGER NOT NULL); - - SELECT AddGeometryColumn('stop_connectors', 'geometry', 4326, 'LINESTRING', 'XY', 1); - - SELECT CreateSpatialIndex('stop_connectors' , 'geometry'); - - CREATE INDEX IF NOT EXISTS stop_connectors_id_from ON stop_connectors (id_from); - - CREATE INDEX IF NOT EXISTS stop_connectors_id_to ON stop_connectors (id_to); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/stops.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/stops.rst deleted file mode 100644 index aeabe3224..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/stops.rst +++ /dev/null @@ -1,85 +0,0 @@ -*stops* table structure ------------------------ - -The *stops* table holds information on the stops where vehicles -pick up or drop off riders. This table information comes from -the GTFS file *stops.txt*. You can find more information about -it `here `_. - -**stop_id** is an unique identifier for a stop - -**stop** idenfifies a stop, statio, or station entrance - -**agency_id** identifies the agency fot the specified route - -**link** identifies the *link_id* in the links table that corresponds to the -pattern matching - -**dir** indicates the direction of travel for a trip - -**name** identifies the name of a stop - -**parent_station** defines hierarchy between different locations -defined in *stops.txt*. - -**description** provides useful description of the stop location - -**street** identifies the address of a stop - -**fare_zone_id** identifies the fare zone for a stop - -**transit_zone** identifies the TAZ for a fare zone - -**route_type** indicates the type of transporation used on a route - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - stop_id*,TEXT,YES, - stop,TEXT,NO, - agency_id,INTEGER,NO, - link,INTEGER,YES, - dir,INTEGER,YES, - name,TEXT,YES, - parent_station,TEXT,YES, - description,TEXT,YES, - street,TEXT,YES, - fare_zone_id,INTEGER,YES, - transit_zone,TEXT,YES, - route_type,INTEGER,NO,-1 - geometry,POINT,NO,'' - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - CREATE TABLE IF NOT EXISTS stops ( - stop_id TEXT PRIMARY KEY, - stop TEXT NOT NULL , - agency_id INTEGER NOT NULL, - link INTEGER, - dir INTEGER, - name TEXT, - parent_station TEXT, - description TEXT, - street TEXT, - fare_zone_id INTEGER, - transit_zone TEXT, - route_type INTEGER NOT NULL DEFAULT -1, - FOREIGN KEY(agency_id) REFERENCES agencies(agency_id), - FOREIGN KEY("fare_zone_id") REFERENCES fare_zones("fare_zone_id") - ); - - create INDEX IF NOT EXISTS stops_stop_id ON stops (stop_id); - - select AddGeometryColumn( 'stops', 'geometry', 4326, 'POINT', 'XY', 1); - - select CreateSpatialIndex( 'stops' , 'geometry' ); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/trigger_settings.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/trigger_settings.rst deleted file mode 100644 index ed18ec71e..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/trigger_settings.rst +++ /dev/null @@ -1,29 +0,0 @@ -*trigger_settings* table structure ----------------------------------- - -This table intends to allow the enabled and disabling of certain triggers - - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - name*,TEXT,YES, - enabled,INTEGER,NO,TRUE - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - - CREATE TABLE if not exists trigger_settings (name TEXT PRIMARY KEY, enabled INTEGER NOT NULL DEFAULT TRUE); - INSERT INTO trigger_settings (name, enabled) VALUES('new_link_a_or_b_node', TRUE); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('trigger_settings', 'name', 'name for trigger to query against'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('trigger_settings', 'enabled', 'boolean value'); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/trips.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/trips.rst deleted file mode 100644 index 2ec66fe5e..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/trips.rst +++ /dev/null @@ -1,42 +0,0 @@ -*trips* table structure ------------------------ - -The *trips* table holds information on trips for each route. -This table comes from the GTFS file *trips.txt*. -You can find more information about it `here `_. - -**trip_id** identifies a trip - -**trip** identifies the trip to a rider - -**dir** indicates the direction of travel for a trip - -**pattern_id** is an unique pattern for the route - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - trip_id*,INTEGER,NO, - trip,TEXT,YES, - dir,INTEGER,NO, - pattern_id,INTEGER,NO, - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - CREATE TABLE IF NOT EXISTS trips ( - trip_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, - trip TEXT, - dir INTEGER NOT NULL, - pattern_id INTEGER NOT NULL, - FOREIGN KEY(pattern_id) REFERENCES routes(pattern_id) deferrable initially deferred - ); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/trips_schedule.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/trips_schedule.rst deleted file mode 100644 index 0f74e23d6..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model/trips_schedule.rst +++ /dev/null @@ -1,42 +0,0 @@ -*trips_schedule* table structure --------------------------------- - -The *trips_schedule* table holds information on the sequence of stops -of a trip. - -**trip_id** is an unique identifier of a trip - -**seq** identifies the sequence of the stops for a trip - -**arrival** identifies the arrival time at the stop - -**departure** identifies the departure time at the stop - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - trip_id*,INTEGER,NO, - seq,INTEGER,NO, - arrival,INTEGER,NO, - departure,INTEGER,NO, - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - CREATE TABLE IF NOT EXISTS trips_schedule ( - trip_id INTEGER NOT NULL, - seq INTEGER NOT NULL, - arrival INTEGER NOT NULL, - departure INTEGER NOT NULL, - PRIMARY KEY(trip_id,"seq"), - FOREIGN KEY(trip_id) REFERENCES trips(trip_id) deferrable initially deferred - ); From 9571da467f8681dcd0d5836f996cc8e43d21a0d2 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Mon, 9 Sep 2024 08:48:57 -0300 Subject: [PATCH 30/57] nodes docs --- .../6-accessing-project-data.rst | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/docs/source/modeling_with_aequilibrae/6-accessing-project-data.rst b/docs/source/modeling_with_aequilibrae/6-accessing-project-data.rst index a30a2c507..671ceb681 100644 --- a/docs/source/modeling_with_aequilibrae/6-accessing-project-data.rst +++ b/docs/source/modeling_with_aequilibrae/6-accessing-project-data.rst @@ -38,16 +38,50 @@ Each item in the 'nodes' table is a ``Node`` object. .. code-block:: python from aequilibrae.utils.create_example import create_example + from shapely.geometry import Point project = create_example("/path_to_my_folder", "coquimbo") project_nodes = project.network.nodes - # To add special generators, we can add a `new_centroid` + # To get one 'Node' object + node = project_nodes.get(10070) + + # We can check the existing fields for each node in the 'nodes' table + node.data_fields() + + # Let's renumber this node, and save it + node.renumber(1000) + node.save() + + # A node can also be used to add a special generator + centroid = project_nodes.new_centroid(2000) + + # Don't forget to add a geometry to your centroid if it's a new node + centroid.geometry = Point(-71.32, -29.94) + + # As this centroid is not associated with a zone, we must tell AequilibraE the initial area around + # the centroid to look for candidate nodes to which the centroid can connect. + centroid.connect_mode(area=centroid.geometry.buffer(0.01), mode_id="c", connectors=1) + + # Let's save our centroid connector + project_nodes.save() + + # And don't forget to update these changes to the nodes in memory + project_nodes.refresh() + + # Lastly but not less important, you can check your project nodes + # `project_nodes.data` returns a geopandas GeoDataFrame. + project_nodes.data + + # or if you want to check the coordinate of each node in the shape of + # a Pandas DataFrame + project_nodes.lonlat .. seealso:: * :func:`aequilibrae.network.Nodes` + Class documentation .. _project_zoning: @@ -68,3 +102,4 @@ Each item in the 'zones' table is a ``Zone`` object. .. seealso:: * :func:`aequilibrae.network.Zoning` + Class documentation From c183c6d5db638cae58908b546a98e476dad3c1c5 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Mon, 9 Sep 2024 11:31:19 -0300 Subject: [PATCH 31/57] links and zones docs --- aequilibrae/project/network/links.py | 2 +- aequilibrae/project/zoning.py | 5 - .../6-accessing-project-data.rst | 119 +++++++++++++++--- 3 files changed, 106 insertions(+), 20 deletions(-) diff --git a/aequilibrae/project/network/links.py b/aequilibrae/project/network/links.py index 73be4af5b..76e9f04d7 100644 --- a/aequilibrae/project/network/links.py +++ b/aequilibrae/project/network/links.py @@ -140,7 +140,7 @@ def data(self) -> gpd.GeoDataFrame: """Returns all links data as a Pandas DataFrame :Returns: - **table** (:obj:`GeoDataFrame`): GeoPandas GeoDataFrame with all the nodes + **table** (:obj:`gpd.GeoDataFrame`): GeoPandas GeoDataFrame with all the nodes """ dl = DataLoader(self.project.path_to_file, "links") return dl.load_table() diff --git a/aequilibrae/project/zoning.py b/aequilibrae/project/zoning.py index 1d8c63482..7cf9f4294 100644 --- a/aequilibrae/project/zoning.py +++ b/aequilibrae/project/zoning.py @@ -27,7 +27,6 @@ class Zoning(BasicTable): >>> project = Project.from_path("/tmp/test_project") - >>> zoning = project.zoning >>> zone_downtown = zoning.get(1) @@ -35,10 +34,6 @@ class Zoning(BasicTable): >>> zone_downtown.employment = 10039 >>> zone_downtown.save() - # changing the value for an existing value/field - >>> project.about.scenario_name = 'Just a better scenario name' - >>> project.about.write_back() - # We can also add one more field to the table >>> fields = zoning.fields >>> fields.add('parking_spots', 'Total licensed parking spots', 'INTEGER') diff --git a/docs/source/modeling_with_aequilibrae/6-accessing-project-data.rst b/docs/source/modeling_with_aequilibrae/6-accessing-project-data.rst index 671ceb681..b17316098 100644 --- a/docs/source/modeling_with_aequilibrae/6-accessing-project-data.rst +++ b/docs/source/modeling_with_aequilibrae/6-accessing-project-data.rst @@ -19,15 +19,49 @@ Each item in the 'links' table is a ``Link`` object. project = create_example("/path_to_my_folder", "coquimbo") - links = project.network.links + project_links = project.network.links + + # Let's add a new field to our 'links' table + project_links.fields.add("my_field", "This is an example", "TEXT") + # To save this modification, we must refresh the table + project_links.refresh_fields() + + # Let's add a new link to our project + new_link = project_links.new() + new_link.geometry = LineString([(-71.304754, -29.955233), (-71.304863, -29.954049)]) + new_link.modes = "bctw" + # To add a new link, it must be explicitly saved + new_link.save() + + # The 'links' table has three fields which cannot be empty (i.e. with `NULL` values): + # `link_id`, `direction`, and `modes`. When we create a node, `new` automatically + # creates a `link_id`, and sets the default value (0) for direction. Thus, the modes + # information should be added, otherwise, it will raise an error. + + # To delete one link from the project, you can use one of the following + other_link = project_links.get(21332) + other_link.delete() + # or + project_links.delete(21332) + + # The `copy_link` function creates a copy of a specified link + # It is very helpful case you want to split a link. + # You can check out in one of the usage examples. + link_copy = project_links.copy_link(10972) + + # Don't forget to save the modifications to the links layer + project_links.save() + + # And refresh the links in memory for usage + project_links.refresh() .. seealso:: - * :func:`aequilibrae.network.Links` + * :func:`aequilibrae.project.network.Links` Class documentation - * :ref:`project_from_link_layer` - Usage example + * :ref:`project_from_link_layer` | :ref:`editing_network_splitting_link` + Usage examples ``project.network.nodes`` ------------------------- @@ -50,27 +84,29 @@ Each item in the 'nodes' table is a ``Node`` object. # We can check the existing fields for each node in the 'nodes' table node.data_fields() - # Let's renumber this node, and save it + # Let's renumber this node and save it node.renumber(1000) node.save() # A node can also be used to add a special generator + # `new_centroid` returns a `Node` object that we can edit centroid = project_nodes.new_centroid(2000) # Don't forget to add a geometry to your centroid if it's a new node + # This centroid corresponds to the Port of Coquimbo! centroid.geometry = Point(-71.32, -29.94) # As this centroid is not associated with a zone, we must tell AequilibraE the initial area around # the centroid to look for candidate nodes to which the centroid can connect. - centroid.connect_mode(area=centroid.geometry.buffer(0.01), mode_id="c", connectors=1) - - # Let's save our centroid connector - project_nodes.save() + centroid.connect_mode(area=centroid.geometry.buffer(0.01), mode_id="c") - # And don't forget to update these changes to the nodes in memory + # Don't forget to update these changes to the nodes in memory project_nodes.refresh() - # Lastly but not less important, you can check your project nodes + # And save them into your project + project_nodes.save() + + # Last but not less important, you can check your project nodes # `project_nodes.data` returns a geopandas GeoDataFrame. project_nodes.data @@ -80,7 +116,7 @@ Each item in the 'nodes' table is a ``Node`` object. .. seealso:: - * :func:`aequilibrae.network.Nodes` + * :func:`aequilibrae.project.network.Nodes` Class documentation .. _project_zoning: @@ -94,12 +130,67 @@ Each item in the 'zones' table is a ``Zone`` object. .. code-block:: python from aequilibrae.utils.create_example import create_example + from shapely.geometry import Polygon, Point project = create_example("/path_to_my_folder", "coquimbo") - zones = project.zoning + project_zones = project.zoning + + # Let's start this example by adding a new field to the 'zones' table + project_zones.fields.add("parking_spots", "Number of public parking spots", "INTEGER") + + # We can check if the new field was indeed created + project_zones.fields.all_fields() + + # Now let's get a zone and modifiy it + zone = project_zones.get(40) + # By disconnecting the transit mode + zone.disconnect_mode("t") + # Connecting the bicycle mode + zone.connect_mode("b") + # And adding the number of public parking spots in the field we just created + zone.parking_spots = 30 + # You can save this modifications if you want + zone.save() + + # The changes connecting / disconnecting modes reflect in the zone centroids + # and can be seen in the 'nodes' table. + + # If you want to delete a zone + other_zone = project_zones.get(38) + other_zone.delete() + + # Or to add a new one + zone_extent = Polygon([(-71.3325, -29.9473), (-71.3283, -29.9473), (-71.3283, -29.9539), (-71.3325, -29.9539)]) + + new_zone = project_zones.new(38) + new_zone.geometry = zone_extent + # We can add a centroid to the zone we just created by specifying its location or + # pass `None` to use the geometric center of the zone + new_zone.add_centroid(Point(-71.33, -29.95)) + + # Let's refresh our fields + project_zones.refresh_geo_index() + + # And save the new changes in the project + project_zones.save() + + # Finally, to return a geopandas GeoDataFrame with the project zones + project_zones.data + + # To return a dictionary with all 'Zone' objects in the model + project_zones.all_zones() + + # To get a Shapely Polygon or Multipolygon with the entire zoning coverage + project_zones.all_zones() + + # And to get the nearest zone to giver geometry + project_zones.get_closest_zone(Point(-71.3336, -29.9490)) .. seealso:: - * :func:`aequilibrae.network.Zoning` + * :func:`aequilibrae.project.Zoning` Class documentation + + * :ref:`create_zones` + Usage example From 3b8dad29eaaa396485e3c881d3affb2fd4fe93b1 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Mon, 9 Sep 2024 12:01:15 -0300 Subject: [PATCH 32/57] docs --- .../transit/tables/agencies.sql | 3 ++- .../transit/tables/fare_attributes.sql | 2 +- .../database_specification/transit/tables/routes.sql | 2 +- .../database_specification/transit/tables/stops.sql | 2 +- .../creating_models/plot_create_from_layer.py | 11 +++++++++-- .../examples/creating_models/plot_create_zoning.py | 2 +- .../examples/creating_models/plot_import_gtfs.py | 3 ++- .../trip_distribution/plot_ipf_without_model.py | 3 ++- .../trip_distribution/plot_trip_distribution.py | 6 ++++-- .../1-aequilibrae_project/parameter_file.rst | 7 ++++++- .../project_database/periods.rst | 4 +--- .../multi_class_equilibrium.rst | 3 --- .../3-transit_assignment/hyperpath_routing.rst | 3 --- 13 files changed, 30 insertions(+), 21 deletions(-) diff --git a/aequilibrae/project/database_specification/transit/tables/agencies.sql b/aequilibrae/project/database_specification/transit/tables/agencies.sql index dcca9e9ea..d40f1eb25 100644 --- a/aequilibrae/project/database_specification/transit/tables/agencies.sql +++ b/aequilibrae/project/database_specification/transit/tables/agencies.sql @@ -1,7 +1,8 @@ --@ The *agencies* table holds information about the Public Transport --@ agencies within the GTFS data. This table information comes from --@ GTFS file *agency.txt*. ---@ You can check out more information `here `_. +--@ You can check out more information +--@ `on agency table here `_. --@ --@ **agency_id** identifies the agency for the specified route --@ diff --git a/aequilibrae/project/database_specification/transit/tables/fare_attributes.sql b/aequilibrae/project/database_specification/transit/tables/fare_attributes.sql index bcf9849bb..dc779b45d 100644 --- a/aequilibrae/project/database_specification/transit/tables/fare_attributes.sql +++ b/aequilibrae/project/database_specification/transit/tables/fare_attributes.sql @@ -1,7 +1,7 @@ --@ The *fare_attributes* table holds information about the fare values. --@ This table information comes from the GTFS file *fare_attributes.txt*. --@ Given that this file is optional in GTFS, it can be empty. ---@ You can check out more information `here `_. +--@ You can check out more information `on fare attributes here `_. --@ --@ **fare_id** identifies a fare class --@ diff --git a/aequilibrae/project/database_specification/transit/tables/routes.sql b/aequilibrae/project/database_specification/transit/tables/routes.sql index a7f353d10..2ff34d67d 100644 --- a/aequilibrae/project/database_specification/transit/tables/routes.sql +++ b/aequilibrae/project/database_specification/transit/tables/routes.sql @@ -1,6 +1,6 @@ --@ The *routes* table holds information on the available transit routes for a --@ specific day. This table information comes from the GTFS file *routes.txt*. ---@ You can find more information about it `here `_. +--@ You can find more information about `the routes table here `_. --@ --@ **pattern_id** is an unique pattern for the route --@ diff --git a/aequilibrae/project/database_specification/transit/tables/stops.sql b/aequilibrae/project/database_specification/transit/tables/stops.sql index 3620292ea..d47bcfb12 100644 --- a/aequilibrae/project/database_specification/transit/tables/stops.sql +++ b/aequilibrae/project/database_specification/transit/tables/stops.sql @@ -1,7 +1,7 @@ --@ The *stops* table holds information on the stops where vehicles --@ pick up or drop off riders. This table information comes from --@ the GTFS file *stops.txt*. You can find more information about ---@ it `here `_. +--@ `the stops table here `_. --@ --@ **stop_id** is an unique identifier for a stop --@ diff --git a/docs/source/examples/creating_models/plot_create_from_layer.py b/docs/source/examples/creating_models/plot_create_from_layer.py index 5c4437b33..97ddbe2de 100644 --- a/docs/source/examples/creating_models/plot_create_from_layer.py +++ b/docs/source/examples/creating_models/plot_create_from_layer.py @@ -169,9 +169,16 @@ # %% project.close() +# %% +# .. admonition:: References +# +# * :ref:`accessing_project_data` + # %% # .. seealso:: # The use of the following functions, methods, classes and modules is shown in this example: # -# * :func:`aequilibrae.project.network.Links` | :func:`aequilibrae.project.network.Nodes` -# * :func:`aequilibrae.project.network.Modes` | :func:`aequilibrae.project.network.LinkTypes` \ No newline at end of file +# * :func:`aequilibrae.project.network.Links` +# * :func:`aequilibrae.project.network.Nodes` +# * :func:`aequilibrae.project.network.Modes` +# * :func:`aequilibrae.project.network.LinkTypes` \ No newline at end of file diff --git a/docs/source/examples/creating_models/plot_create_zoning.py b/docs/source/examples/creating_models/plot_create_zoning.py index a5db4f114..ca056e487 100644 --- a/docs/source/examples/creating_models/plot_create_zoning.py +++ b/docs/source/examples/creating_models/plot_create_zoning.py @@ -153,7 +153,7 @@ # .. admonition:: References # # * :ref:`tables_zones` -# * :ref:`project_zoning` +# * :ref:`Accessing project zones ` # %% # .. seealso:: diff --git a/docs/source/examples/creating_models/plot_import_gtfs.py b/docs/source/examples/creating_models/plot_import_gtfs.py index c7e2b537d..daa510aed 100644 --- a/docs/source/examples/creating_models/plot_import_gtfs.py +++ b/docs/source/examples/creating_models/plot_import_gtfs.py @@ -126,4 +126,5 @@ # .. seealso:: # The use of the following functions, methods, classes and modules is shown in this example: # -# * :func:`aequilibrae.transit.Transit` | :func:`aequilibrae.transit.lib_gtfs.GTFSRouteSystemBuilder` \ No newline at end of file +# * :func:`aequilibrae.transit.Transit` +# * :func:`aequilibrae.transit.lib_gtfs.GTFSRouteSystemBuilder` \ No newline at end of file diff --git a/docs/source/examples/trip_distribution/plot_ipf_without_model.py b/docs/source/examples/trip_distribution/plot_ipf_without_model.py index b3661a75a..d3738fef4 100644 --- a/docs/source/examples/trip_distribution/plot_ipf_without_model.py +++ b/docs/source/examples/trip_distribution/plot_ipf_without_model.py @@ -88,5 +88,6 @@ # .. seealso:: # The use of the following functions, methods, classes and modules is shown in this example: # -# * :func:`aequilibrae.matrix.AequilibraeMatrix` | :func:`aequilibrae.matrix.AequilibraeData` +# * :func:`aequilibrae.matrix.AequilibraeMatrix` +# * :func:`aequilibrae.matrix.AequilibraeData` # * :func:`aequilibrae.distribution.Ipf` \ No newline at end of file diff --git a/docs/source/examples/trip_distribution/plot_trip_distribution.py b/docs/source/examples/trip_distribution/plot_trip_distribution.py index 03ee986e6..e3a20ccab 100644 --- a/docs/source/examples/trip_distribution/plot_trip_distribution.py +++ b/docs/source/examples/trip_distribution/plot_trip_distribution.py @@ -181,6 +181,8 @@ def plot_tlfd(demand, skim, name): # .. seealso:: # The use of the following functions, methods, classes and modules is shown in this example: # -# * :func:`aequilibrae.distribution.Ipf` | :func:`aequilibrae.distribution.GravityCalibration` -# * :func:`aequilibrae.distribution.GravityApplication` | :func:`aequilibrae.distribution.SyntheticGravityModel` +# * :func:`aequilibrae.distribution.Ipf` +# * :func:`aequilibrae.distribution.GravityCalibration` +# * :func:`aequilibrae.distribution.GravityApplication` +# * :func:`aequilibrae.distribution.SyntheticGravityModel` # * :func:`aequilibrae.matrix.AequilibraeData` diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/parameter_file.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/parameter_file.rst index 4978e7cbb..9f8789432 100644 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/parameter_file.rst +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/parameter_file.rst @@ -16,6 +16,7 @@ of iterations and target Relative Gap. .. image:: ../../images/parameters_assignment_example.png :align: center + :scale: 80 % :alt: Assignment example Although these parameters are required to exist in the parameters file, one can @@ -33,6 +34,7 @@ synthetic gravity models, as shown below. .. image:: ../../images/parameters_distribution_example.png :align: center + :scale: 80 % :alt: Distribution example .. _parameters_network: @@ -65,8 +67,8 @@ The data types available are those that exist within the to the use of **integer**, **numeric** and **varchar**. .. image:: ../../images/parameters_links_example.png - :width: 704 :align: center + :scale: 80 % :alt: Link example For the case of all non-mandatory fields, two more parameters are possible: 'osm_source' and @@ -91,6 +93,7 @@ the allowed values for this parameter are **copy** and **divide**, as shown belo .. image:: ../../images/parameters_links_osm_behaviour.png :align: center + :scale: 80 % :alt: OSM behaviour examples The example below also shows that it is possible to mix fields that will be imported from @@ -153,6 +156,7 @@ below. .. image:: ../../images/parameters_system_example.png :align: center + :scale: 80 % :alt: System example The number of CPUs have a special behaviour defined, as follows: @@ -178,6 +182,7 @@ recommended to deploy a local Overpass server. .. image:: ../../images/parameters_osm_example.png :align: center + :scale: 80 % :alt: OSM example The user is also welcome to change the maximum area for a single query to the diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/periods.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/periods.rst index c29d42c7c..e1b8aba38 100644 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/periods.rst +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/periods.rst @@ -1,11 +1,9 @@ Periods table ============= -Lorem ipsum - .. seealso:: - Lorem ipsum + * :func:`aequilibrae.project.network.Periods` .. include:: data_model/periods.rst diff --git a/docs/source/modeling_with_aequilibrae/2-static_traffic_assignment/multi_class_equilibrium.rst b/docs/source/modeling_with_aequilibrae/2-static_traffic_assignment/multi_class_equilibrium.rst index fb65782b7..528a55314 100644 --- a/docs/source/modeling_with_aequilibrae/2-static_traffic_assignment/multi_class_equilibrium.rst +++ b/docs/source/modeling_with_aequilibrae/2-static_traffic_assignment/multi_class_equilibrium.rst @@ -184,9 +184,6 @@ The use of the AequilibraE project and its built-in methods to build graphs ensure that all graphs will be built in a consistent manner and multi-class assignment is possible. -References ----------- - .. [1] Wardrop J. G. (1952) "Some theoretical aspects of road traffic research." Proceedings of the Institution of Civil Engineers 1952, 1(3):325-362. Available in: https://www.icevirtuallibrary.com/doi/abs/10.1680/ipeds.1952.11259 diff --git a/docs/source/modeling_with_aequilibrae/3-transit_assignment/hyperpath_routing.rst b/docs/source/modeling_with_aequilibrae/3-transit_assignment/hyperpath_routing.rst index 61738483c..a9e2a0c27 100644 --- a/docs/source/modeling_with_aequilibrae/3-transit_assignment/hyperpath_routing.rst +++ b/docs/source/modeling_with_aequilibrae/3-transit_assignment/hyperpath_routing.rst @@ -466,9 +466,6 @@ apparent as we further increase :math:`\alpha`: :func:`aequilibrae.paths.HyperpathGenerating` -References ----------- - .. [1] Spiess, H., Florian, M. (1989) "Optimal strategies: A new assignment model for transit networks". Transportation Research Part B: Methodological, 23(2), 83-102. Available in: https://doi.org/10.1016/0191-2615(89)90034-9 From 95c7084c1eb449e923b3feebe59b4d063637d59d Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Mon, 9 Sep 2024 14:13:24 -0300 Subject: [PATCH 33/57] . --- .gitignore | 1 + aequilibrae/transit/transit_graph_builder.py | 9 ++-- .../plot_assignment_without_model.py | 12 +++--- .../assignment_workflows/plot_forecasting.py | 18 ++++---- .../plot_public_transit_assignment.py | 43 +++++++++---------- .../plot_route_choice_basics.py | 3 +- .../plot_route_choice_set.py | 14 +++--- .../plot_subarea_analysis.py | 4 +- .../plot_trip_distribution.py | 2 +- .../project_database/transit_graph.rst | 5 ++- .../4-route_choice/index.rst | 18 +++----- 11 files changed, 65 insertions(+), 64 deletions(-) diff --git a/.gitignore b/.gitignore index da3dd9c7d..baa4ef4e6 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ _build _static _templates +docs/source/sg_execution_times.rst docs/source/_auto_examples docs/source/api/generated docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model diff --git a/aequilibrae/transit/transit_graph_builder.py b/aequilibrae/transit/transit_graph_builder.py index d0159f301..309d75c08 100644 --- a/aequilibrae/transit/transit_graph_builder.py +++ b/aequilibrae/transit/transit_graph_builder.py @@ -1189,7 +1189,8 @@ def create_line_geometry(self, method="direct", graph="w"): Project graphs must be built for the "connector project match" method. :Arguments: - **method** (:obj:`str`): Must be either "direct" or "connector project match". If method is "direct", ``graph`` argument is ignored. + **method** (:obj:`str`): Must be either ``"direct"`` or ``"connector project match"``. + If method is ``"direct"``, ``graph`` argument is ignored. **graph** (:obj:`str`): Must be a key within ``project.network.graphs``. """ @@ -1381,7 +1382,7 @@ def save_edges(self, recreate_line_geometry=False): """ Save the contents of self.edges to the public transport database. - If no geometry for the edges is present or `recreate_line_geometry` is True, direct lines will be created. + If no geometry for the edges is present or ``recreate_line_geometry`` is ``True``, direct lines will be created. :Arguments: **recreate_line_geometry** (:obj:`bool`): Whether to recreate the line strings for the edges as direct lines. Defaults to ``False``. @@ -1424,7 +1425,7 @@ def save(self, robust=True): self.save_config() def to_transit_graph(self) -> TransitGraph: - """Create an AequilibraE (:obj:`TransitGraph`) object from an SF graph builder.""" + """Create an AequilibraE ``TransitGraph`` object from an SF graph builder.""" # TODO: Better required link type detections # link_type_diff = set(self.edges.link_type.unique()) ^ {'access_connector', 'alighting', 'boarding', 'dwell', 'egress_connector', 'inner_transfer', 'on-board'} @@ -1463,7 +1464,7 @@ def from_db(cls, public_transport_conn, period_id: int, **kwargs): All arguments are forwarded to the constructor. :Arguments: - **public_transport_conn** (:obj:`sqlite3.Connection`): Connection to the ``public_transport.sqlite`` database. + **public_transport_conn** (:obj:`sqlite3.Connection`): Connection to the 'public_transport.sqlite' database. """ project_conn = database_connection("project_database") config = json.loads( diff --git a/docs/source/examples/aequilibrae_without_a_model/plot_assignment_without_model.py b/docs/source/examples/aequilibrae_without_a_model/plot_assignment_without_model.py index 004a7d8be..87590db65 100644 --- a/docs/source/examples/aequilibrae_without_a_model/plot_assignment_without_model.py +++ b/docs/source/examples/aequilibrae_without_a_model/plot_assignment_without_model.py @@ -105,11 +105,6 @@ g.network["id"] = g.network.link_id g.lonlat_index = geom.loc[g.all_nodes] -# %% -# .. admonition:: References -# -# :ref:`aequilibrae-graphs` - # %% # Let's perform our assignment. Feel free to try different algorithms, # as well as change the maximum number of iterations and the gap. @@ -142,12 +137,15 @@ # %% # .. admonition:: References # -# :ref:`traffic_assignment_procedure` | :ref:`multiclass_equilibrium` +# :ref:`aequilibrae-graphs` +# :ref:`traffic_assignment_procedure` +# :ref:`multiclass_equilibrium` # %% # .. seealso:: # The use of the following functions, methods, classes and modules is shown in this example: # # * :func:`aequilibrae.paths.Graph` -# * :func:`aequilibrae.paths.TrafficClass` | :func:`aequilibrae.paths.TrafficAssignment` +# * :func:`aequilibrae.paths.TrafficClass` +# * :func:`aequilibrae.paths.TrafficAssignment` # * :func:`aequilibrae.matrix.AequilibraeMatrix` \ No newline at end of file diff --git a/docs/source/examples/assignment_workflows/plot_forecasting.py b/docs/source/examples/assignment_workflows/plot_forecasting.py index 8b0786981..c1655963c 100644 --- a/docs/source/examples/assignment_workflows/plot_forecasting.py +++ b/docs/source/examples/assignment_workflows/plot_forecasting.py @@ -286,9 +286,6 @@ # Future traffic assignment # ------------------------- -# %% -from aequilibrae.paths import TrafficAssignment, TrafficClass - # %% logger.info("\n\n\n TRAFFIC ASSIGNMENT FOR FUTURE YEAR") @@ -334,7 +331,7 @@ # The format of the input select links is a ``dictionary (str: list[tuple])``. # Each entry represents a separate set of selected links to compute. The str name will name the set of links. # The list[tuple] is the list of links being selected, of the form (link_id, direction), as it occurs in the Graph. -# Direction can be 0, 1, -1. 0 denotes bi-directionality +# Direction can be 0, 1, -1. 0 denotes bi-directionality. # For example, let's use Select Link on two sets of links: # %% @@ -350,7 +347,7 @@ assig.execute() # we then execute the assignment # %% -# Now let us save our select link results, all we need to do is provide it with a name +# Now let us save our select link results, all we need to do is provide it with a name. # In addition to exporting the select link flows, it also exports the Select Link matrices in OMX format. assig.save_select_link_results("select_link_analysis") @@ -363,7 +360,7 @@ # Internally, the save_select_link_results calls both of these methods at once. # %% -# We could export it to CSV or AequilibraE data, but let's put it directly into the results database +# We could export it to CSV or AequilibraE Data, but let's put it directly into the results database assig.save_results("future_year_assignment") # %% @@ -400,5 +397,10 @@ # The use of the following functions, methods, classes and modules is shown in this example: # # * :func:`aequilibrae.paths.Graph` -# * :func:`aequilibrae.paths.TrafficClass` | :func:`aequilibrae.paths.TrafficAssignment` -# * :func:`aequilibrae.matrix.AequilibraeMatrix` \ No newline at end of file +# * :func:`aequilibrae.paths.TrafficClass` +# * :func:`aequilibrae.paths.TrafficAssignment` +# * :func:`aequilibrae.distribution.Ipf` +# * :func:`aequilibrae.distribution.GravityCalibration` +# * :func:`aequilibrae.distribution.GravityApplication` +# * :func:`aequilibrae.distribution.SyntheticGravityModel` +# * :func:`aequilibrae.matrix.AequilibraeData` diff --git a/docs/source/examples/assignment_workflows/plot_public_transit_assignment.py b/docs/source/examples/assignment_workflows/plot_public_transit_assignment.py index cb2166309..cff1a49f0 100644 --- a/docs/source/examples/assignment_workflows/plot_public_transit_assignment.py +++ b/docs/source/examples/assignment_workflows/plot_public_transit_assignment.py @@ -26,7 +26,7 @@ # Imports for SF transit graph construction from aequilibrae.project.database_connection import database_connection from aequilibrae.transit.transit_graph_builder import TransitGraphBuilder -# sphinx_gallery_thumbnail_path = 'images/hyperpath_bell_n_10_alpha_100d0.png' +# sphinx_gallery_thumbnail_path = 'images/transit/hyperpath_bell_n_10_alpha_100d0.png' # %% @@ -66,6 +66,7 @@ # -------------- # Let's build the transit network. We'll disable ``outer_stop_transfers`` and ``walking_edges`` # because Coquimbo doesn't have any parent stations. +# # For the OD connections we'll use the ``overlapping_regions`` method and create some accurate line geometry later. # Creating the graph should only take a moment. By default zoning information is pulled from the project network. # If you have your own zoning information add it using ``graph.add_zones(zones)`` then ``graph.create_graph()``. @@ -102,23 +103,23 @@ # %% # Saving and reloading # ~~~~~~~~~~~~~~~~~~~~ -# Lets save all graphs to the ``public_transport.sqlite`` database. +# Lets save all graphs to the 'public_transport.sqlite' database. data.save_graphs() # %% # We can reload the saved graphs with ``data.load``. -# This will create new `TransitGraphBuilder`\'s based on the `period_id` of the saved graphs. -# The graph configuration is stored in the `transit_graph_config` table in ``project_database.sqlite`` +# This will create new ``TransitGraphBuilder``\'s based on the 'period_id' of the saved graphs. +# The graph configuration is stored in the 'transit_graph_config' table in 'project_database.sqlite' # as serialised JSON. data.load() # %% -# Links and nodes are stored in a similar manner to the ``project_database.sqlite`` database. +# Links and nodes are stored in a similar manner to the 'project_database.sqlite' database. # %% # Reading back into AequilibraE # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# You can create back in a particular graph via it's `period_id`. +# You can create back in a particular graph via it's 'period_id'. pt_con = database_connection("transit") graph_db = TransitGraphBuilder.from_db(pt_con, periods.default_period.period_id) graph_db.vertices.drop(columns="geometry") @@ -132,11 +133,6 @@ # To perform an assignment we need to convert the graph builder into a graph. transit_graph = graph.to_transit_graph() -# %% -# .. admonition:: References -# -# :ref:`transit_assignment_graph` - # %% # Spiess & Florian assignment # --------------------------- @@ -144,8 +140,8 @@ # %% # Mock demand matrix # ~~~~~~~~~~~~~~~~~~ -# We'll create a mock demand matrix with demand `1` for every zone. -# We'll also need to convert from `zone_id`\'s to `node_id`\'s. +# We'll create a mock demand matrix with demand ``1`` for every zone. +# We'll also need to convert from ``zone_id``\'s to ``node_id``\'s. from aequilibrae.matrix import AequilibraeMatrix # %% @@ -164,7 +160,7 @@ # %% # Hyperpath generation/assignment # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# We'll create a `TransitAssignment` object as well as a `TransitClass` +# We'll create a ``TransitAssignment`` object as well as a ``TransitClass`` # %% assig = TransitAssignment() @@ -184,7 +180,7 @@ assigclass.set_demand_matrix_core("pt") # %% -# Let's perform the assignment with the mock demand matrx for all `TransitClass`\'s added. +# Let's perform the assignment with the mock demand matrx for all ``TransitClass``\'s added. assig.execute() # %% @@ -192,15 +188,15 @@ assig.results() # %% -# We can also access the `TransitAssignmentResults` object from the `TransitClass` +# We can also access the ``TransitAssignmentResults`` object from the ``TransitClass`` assigclass.results # %% # Saving results # ~~~~~~~~~~~~~~ -# We'll be saving the results to another sqlite db called ``results_database.sqlite``. -# The `results` table with ``project_database.sqlite`` contains some metadata about each table in -# ``results_database.sqlite``. +# We'll be saving the results to another sqlite db called 'results_database.sqlite'. +# The 'results' table with 'project_database.sqlite' contains some metadata about each table in +# 'results_database.sqlite'. assig.save_results(table_name='hyperpath example') # %% @@ -210,12 +206,15 @@ # %% # .. admonition:: References # -# :ref:`transit_hyperpath_routing` +# * :ref:`transit_assignment_graph` +# * :ref:`transit_hyperpath_routing` # %% # .. seealso:: # The use of the following functions, methods, classes and modules is shown in this example: # -# * :func:`aequilibrae.paths.TransitGraphBuilder` -# * :func:`aequilibrae.paths.TransitClass` | :func:`aequilibrae.paths.TransitAssignment` +# * :func:`aequilibrae.transit.Transit` +# * :func:`aequilibrae.transit.TransitGraphBuilder` +# * :func:`aequilibrae.paths.TransitClass` +# * :func:`aequilibrae.paths.TransitAssignment` # * :func:`aequilibrae.matrix.AequilibraeMatrix` \ No newline at end of file diff --git a/docs/source/examples/assignment_workflows/plot_route_choice_basics.py b/docs/source/examples/assignment_workflows/plot_route_choice_basics.py index 15ff6221b..f1b4e3eab 100644 --- a/docs/source/examples/assignment_workflows/plot_route_choice_basics.py +++ b/docs/source/examples/assignment_workflows/plot_route_choice_basics.py @@ -263,5 +263,6 @@ def plot_results(link_loads): # .. seealso:: # The use of the following functions, methods, classes and modules is shown in this example: # -# * :func:`aequilibrae.paths.Graph` | :func:`aequilibrae.paths.RouteChoice` +# * :func:`aequilibrae.paths.Graph` +# * :func:`aequilibrae.paths.RouteChoice` # * :func:`aequilibrae.matrix.AequilibraeMatrix` \ No newline at end of file diff --git a/docs/source/examples/assignment_workflows/plot_route_choice_set.py b/docs/source/examples/assignment_workflows/plot_route_choice_set.py index f3ae75540..98ff23c07 100644 --- a/docs/source/examples/assignment_workflows/plot_route_choice_set.py +++ b/docs/source/examples/assignment_workflows/plot_route_choice_set.py @@ -9,6 +9,7 @@ """ # %% + # Imports from uuid import uuid4 from tempfile import gettempdir @@ -51,7 +52,7 @@ # We set the nodes of interest as centroids to make sure they are not simplified away when we create the network graph.prepare_graph(np.array(nodes_of_interest)) -# We allow flows through "centroid connectors" because our centroids are not really centroids +# We allow flows through "centroid connectors" because our centroids are not really centroids. # If we have actual centroid connectors in the network (and more than one per centroid), then we # should remove them from the graph graph.set_blocked_centroid_flows(False) @@ -65,18 +66,18 @@ # %% # This object construct might take a minute depending on the size of the graph due to the construction of the # compressed link to network link mapping that's required. This is a one time operation per graph and is cached. We -# need to supply a Graph and an AequilibraeMatrix or DataFrame via the ``add_demand`` method, if demand is not provided -# link loading cannot be preformed. +# need to supply a ``Graph`` and an ``AequilibraeMatrix`` or Panda's DataFrame via the ``add_demand`` method, +# if demand is not provided link loading cannot be preformed. rc = RouteChoice(graph) # %% -# Here we'll set the parameters of our set generation. There are two algorithms available: Link penalisation, and BFSLE +# Here we'll set the parameters of our set generation. There are two algorithms available: Link penalisation and BFSLE, # based on the paper # `"Route choice sets for very high-resolution data" `_ # by Nadine Rieser-Schüssler, Michael Balmer & Kay W. Axhausen (2013). # # Our BFSLE implementation has been extended to allow applying link penalisation as well. Every -# link in all routes found at a depth are penalised with the `penalty` factor for the next depth. +# link in all routes found at a depth are penalised with the 'penalty' factor for the next depth. # So at a depth of 0 no links are penalised nor removed. At depth 1, all links found at depth 0 are penalised, # then the links marked for removal are removed. All links in the routes found at depth 1 are then penalised # for the next depth. The penalisation compounds. Pass set ``penalty=1.0`` to disable. @@ -173,5 +174,4 @@ # .. seealso:: # The use of the following functions, methods, classes and modules is shown in this example: # -# * :func:`aequilibrae.paths.Graph` | :func:`aequilibrae.paths.RouteChoice` -# * :func:`aequilibrae.matrix.AequilibraeMatrix` \ No newline at end of file +# * :func:`aequilibrae.paths.RouteChoice` \ No newline at end of file diff --git a/docs/source/examples/assignment_workflows/plot_subarea_analysis.py b/docs/source/examples/assignment_workflows/plot_subarea_analysis.py index 02a875aac..e92397fbe 100644 --- a/docs/source/examples/assignment_workflows/plot_subarea_analysis.py +++ b/docs/source/examples/assignment_workflows/plot_subarea_analysis.py @@ -416,5 +416,7 @@ def plot_results(link_loads): # .. seealso:: # The use of the following functions, methods, classes and modules is shown in this example: # -# * :func:`aequilibrae.paths.Graph` | :func:`aequilibrae.paths.RouteChoice` | :func:`aequilibrae.paths.SubAreaAnalysis` +# * :func:`aequilibrae.paths.Graph` +# * :func:`aequilibrae.paths.RouteChoice` +# * :func:`aequilibrae.paths.SubAreaAnalysis` # * :func:`aequilibrae.matrix.AequilibraeMatrix` \ No newline at end of file diff --git a/docs/source/examples/trip_distribution/plot_trip_distribution.py b/docs/source/examples/trip_distribution/plot_trip_distribution.py index e3a20ccab..277253df0 100644 --- a/docs/source/examples/trip_distribution/plot_trip_distribution.py +++ b/docs/source/examples/trip_distribution/plot_trip_distribution.py @@ -93,7 +93,7 @@ def plot_tlfd(demand, skim, name): # %% # Forecast # -------- -# We create a set of *'future'* vectors by applying some models +# We create a set of 'future' vectors by applying some models # and apply the model for both deterrence functions # %% diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/transit_graph.rst b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/transit_graph.rst index 470da3ab6..ea87d5248 100644 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/transit_graph.rst +++ b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/transit_graph.rst @@ -1,10 +1,11 @@ Transit graph configuration =========================== -Lorem ipsum +The 'transit_graph_configs' table holds information on the configuration parameters for a +``TransitGraph`` of a particular 'period_id'. .. seealso:: - Lorem ipsum + * :func:`aequilibtae.transit.TransitGraphBuilder` .. include:: data_model/transit_graph_configs.rst \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/4-route_choice/index.rst b/docs/source/modeling_with_aequilibrae/4-route_choice/index.rst index 5d96a9c9b..4eee3487b 100644 --- a/docs/source/modeling_with_aequilibrae/4-route_choice/index.rst +++ b/docs/source/modeling_with_aequilibrae/4-route_choice/index.rst @@ -10,13 +10,6 @@ is consists of two steps: Choice set generation and the choice selection process AequilibraE is the first modeling package with full support for route choice, from the creation of choice sets through multiple algorithms to the assignment of trips to the network using the traditional Path-Size logit. -.. toctree:: - :maxdepth: 1 - :caption: Dive deep into route choice - - route_choice_model.rst - choice_set_generation.rst - Costs, utilities and signs -------------------------- @@ -74,6 +67,13 @@ Full process overview The estimation of route choice models based on vehicle GPS data can be explored on a family of papers scheduled to be presented at the ATRF 2024 [1]_ [3]_ [4]_. +.. toctree:: + :maxdepth: 1 + :caption: Dive deep into route choice + + route_choice_model.rst + choice_set_generation.rst + .. [1] Zill, J. C., and P. V. de Camargo. State-Wide Route Choice Models (Submitted). Presented at the ATRF, Melbourne, Australia, 2024. @@ -85,7 +85,3 @@ be presented at the ATRF 2024 [1]_ [3]_ [4]_. .. [4] Moss, J., P. V. de Camargo, C. de Freitas, and R. Imai. High-Performance Route Choice Set Generation on Large Networks (Submitted). Presented at the ATRF, Melbourne, 2024. - -.. seealso:: - - LOREM IPSUM From 5f64a8777a25a8c5e53c9f2e049167db686d5981 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Mon, 9 Sep 2024 14:58:09 -0300 Subject: [PATCH 34/57] rename folder --- aequilibrae/project/network/network.py | 2 +- .../transit_database/datamodel.rst.template | 32 ------------- ...ct-data.rst => accessing_project_data.rst} | 0 ...brae_matrix.rst => aequilibrae_matrix.rst} | 8 ++-- .../index.rst | 17 +++---- .../parameter_file.rst | 8 ++-- .../project_database/about.rst | 0 .../attributes_documentation.rst | 0 .../data_model/transit_graph_configs.rst | 29 ++++++++++++ .../project_database/data_model/zones.rst | 46 +++++++++++++++++++ .../project_database/datamodel.rst.template | 0 .../project_database/index.rst | 0 .../project_database/link_types.rst | 0 .../project_database/matrices.rst | 0 .../project_database/modes.rst | 0 .../project_database/network.rst | 0 .../project_database/network_geometry.rst | 2 +- .../network_import_and_export.rst | 6 +-- .../project_database/periods.rst | 0 .../project_database/transit_graph.rst | 0 .../project_database/zones.rst | 0 .../results_database.rst | 0 .../transit_database/index.rst | 0 .../modeling_with_aequilibrae/index.rst | 12 ++--- ..._components.rst => project_components.rst} | 0 .../choice_set_generation.rst | 0 .../index.rst | 0 .../route_choice_model.rst | 0 .../assignment_mechanics.rst | 0 .../index.rst | 0 .../multi_class_equilibrium.rst | 0 .../hyperpath_routing.rst | 0 .../index.rst | 0 .../transit_graph.rst | 0 .../useful_information/installation.rst | 20 ++++---- 35 files changed, 111 insertions(+), 71 deletions(-) delete mode 100644 docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/datamodel.rst.template rename docs/source/modeling_with_aequilibrae/{6-accessing-project-data.rst => accessing_project_data.rst} (100%) rename docs/source/modeling_with_aequilibrae/{5-aequilibrae_matrix.rst => aequilibrae_matrix.rst} (95%) rename docs/source/modeling_with_aequilibrae/{1-aequilibrae_project => aequilibrae_project}/index.rst (91%) rename docs/source/modeling_with_aequilibrae/{1-aequilibrae_project => aequilibrae_project}/parameter_file.rst (97%) rename docs/source/modeling_with_aequilibrae/{1-aequilibrae_project => aequilibrae_project}/project_database/about.rst (100%) rename docs/source/modeling_with_aequilibrae/{1-aequilibrae_project => aequilibrae_project}/project_database/attributes_documentation.rst (100%) create mode 100644 docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/data_model/transit_graph_configs.rst create mode 100644 docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/data_model/zones.rst rename docs/source/modeling_with_aequilibrae/{1-aequilibrae_project => aequilibrae_project}/project_database/datamodel.rst.template (100%) rename docs/source/modeling_with_aequilibrae/{1-aequilibrae_project => aequilibrae_project}/project_database/index.rst (100%) rename docs/source/modeling_with_aequilibrae/{1-aequilibrae_project => aequilibrae_project}/project_database/link_types.rst (100%) rename docs/source/modeling_with_aequilibrae/{1-aequilibrae_project => aequilibrae_project}/project_database/matrices.rst (100%) rename docs/source/modeling_with_aequilibrae/{1-aequilibrae_project => aequilibrae_project}/project_database/modes.rst (100%) rename docs/source/modeling_with_aequilibrae/{1-aequilibrae_project => aequilibrae_project}/project_database/network.rst (100%) rename docs/source/modeling_with_aequilibrae/{1-aequilibrae_project => aequilibrae_project}/project_database/network_geometry.rst (99%) rename docs/source/modeling_with_aequilibrae/{1-aequilibrae_project => aequilibrae_project}/project_database/network_import_and_export.rst (97%) rename docs/source/modeling_with_aequilibrae/{1-aequilibrae_project => aequilibrae_project}/project_database/periods.rst (100%) rename docs/source/modeling_with_aequilibrae/{1-aequilibrae_project => aequilibrae_project}/project_database/transit_graph.rst (100%) rename docs/source/modeling_with_aequilibrae/{1-aequilibrae_project => aequilibrae_project}/project_database/zones.rst (100%) rename docs/source/modeling_with_aequilibrae/{1-aequilibrae_project => aequilibrae_project}/results_database.rst (100%) rename docs/source/modeling_with_aequilibrae/{1-aequilibrae_project => aequilibrae_project}/transit_database/index.rst (100%) rename docs/source/modeling_with_aequilibrae/{6-project_components.rst => project_components.rst} (100%) rename docs/source/modeling_with_aequilibrae/{4-route_choice => route_choice}/choice_set_generation.rst (100%) rename docs/source/modeling_with_aequilibrae/{4-route_choice => route_choice}/index.rst (100%) rename docs/source/modeling_with_aequilibrae/{4-route_choice => route_choice}/route_choice_model.rst (100%) rename docs/source/modeling_with_aequilibrae/{2-static_traffic_assignment => static_traffic_assignment}/assignment_mechanics.rst (100%) rename docs/source/modeling_with_aequilibrae/{2-static_traffic_assignment => static_traffic_assignment}/index.rst (100%) rename docs/source/modeling_with_aequilibrae/{2-static_traffic_assignment => static_traffic_assignment}/multi_class_equilibrium.rst (100%) rename docs/source/modeling_with_aequilibrae/{3-transit_assignment => transit_assignment}/hyperpath_routing.rst (100%) rename docs/source/modeling_with_aequilibrae/{3-transit_assignment => transit_assignment}/index.rst (100%) rename docs/source/modeling_with_aequilibrae/{3-transit_assignment => transit_assignment}/transit_graph.rst (100%) diff --git a/aequilibrae/project/network/network.py b/aequilibrae/project/network/network.py index fa229f68a..affc9f656 100644 --- a/aequilibrae/project/network/network.py +++ b/aequilibrae/project/network/network.py @@ -121,7 +121,7 @@ def create_from_osm( clean=True, ) -> None: """ - Downloads the network from Open-Street Maps + Downloads the network from OpenStreetMap (OSM) :Arguments: **area** (:obj:`Polygon`, *Optional*): Polygon for which the network will be downloaded. If not provided, diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/datamodel.rst.template b/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/datamodel.rst.template deleted file mode 100644 index 2ebd72b2c..000000000 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/datamodel.rst.template +++ /dev/null @@ -1,32 +0,0 @@ -:orphan: - -.. _transit_supply_data_model: - -SQL Data model -^^^^^^^^^^^^^^ - -The data model presented in this section pertains only to the structure of -AequilibraE's public_transport database and general information about the usefulness -of specific fields, especially on the interdependency between tables. - -Conventions -''''''''''' - -A few conventions have been adopted in the definition of the data model and some -are listed below: - -- Geometry field is always called **geometry** -- Projection is 4326 (WGS84) -- Tables are all in all lower case - - -.. Do not touch below this line unless you know EXACTLY what you are doing. -.. it will be automatically populated - -Project tables -'''''''''''''' - -.. toctree:: - :maxdepth: 1 - - LIST_OF_TABLES \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/6-accessing-project-data.rst b/docs/source/modeling_with_aequilibrae/accessing_project_data.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/6-accessing-project-data.rst rename to docs/source/modeling_with_aequilibrae/accessing_project_data.rst diff --git a/docs/source/modeling_with_aequilibrae/5-aequilibrae_matrix.rst b/docs/source/modeling_with_aequilibrae/aequilibrae_matrix.rst similarity index 95% rename from docs/source/modeling_with_aequilibrae/5-aequilibrae_matrix.rst rename to docs/source/modeling_with_aequilibrae/aequilibrae_matrix.rst index 21fcb36ea..f7d8777ab 100644 --- a/docs/source/modeling_with_aequilibrae/5-aequilibrae_matrix.rst +++ b/docs/source/modeling_with_aequilibrae/aequilibrae_matrix.rst @@ -111,14 +111,14 @@ AequilibraE matrices saved in disk can be reused and loaded once again. :ref:`plot_assignment_without_model` Usage example -Open Matrix (OMX) ------------------ +OpenMatrix (OMX) +---------------- AequilibraE can handle OMX files, but if you're wondering what is OMX and what does it stand for, this section is for you. The text in this section is borrowed from -`Open Matrix Wiki page `_. +`OpenMatrix Wiki page `_. -The Open Matrix file format (or simply OMX) is a standard matrix format for storing and +The OpenMatrix file format (or simply OMX) is a standard matrix format for storing and transferring matrix data across different models and software packages, intended to make the model development easier. It is a file capable of storing more than one matrices at a time, including multiple indexes/lookups, and attributes (key/value pairs) for matrices and diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/index.rst b/docs/source/modeling_with_aequilibrae/aequilibrae_project/index.rst similarity index 91% rename from docs/source/modeling_with_aequilibrae/1-aequilibrae_project/index.rst rename to docs/source/modeling_with_aequilibrae/aequilibrae_project/index.rst index 8dd99123a..51201808f 100644 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/index.rst +++ b/docs/source/modeling_with_aequilibrae/aequilibrae_project/index.rst @@ -16,13 +16,13 @@ AequilibraE is built on the shoulder of much older and more established projects, such as `SQLite `_, `SpatiaLite `_ and `NumPy `_, as well as reasonably new industry standards such as the -`Open-Matrix format `_. +`OpenMatrix format `_. Impressive performance, portability, self containment and open-source character of these pieces of software, along with their large user base and wide industry support make them solid options to be AequilibraE's data backend. -Since working with Spatialite is not just a matter of a ``pip install``, +Since working with SpatiaLite is not just a matter of a ``pip install``, please refer to :ref:`dependencies`. For QGIS users this is not a concern, while for Windows users this dependency is automatically handled under the hood, but the details are also discussed in the aforementioned dependencies section. @@ -30,7 +30,7 @@ the details are also discussed in the aforementioned dependencies section. Package components: A conceptual view ------------------------------------- -As all the components of an AequilibraE model based on open-source software and +As all the components of an AequilibraE model are based on open-source software and open-data standards, modeling with AequilibraE is a little different from modeling with commercial packages, as the user can read and manipulate model components outside the software modeling environments (Python and QGIS). @@ -62,7 +62,8 @@ series of files and sub folders exist, and the current project organization is as follows: .. image:: ../../images/project_structure.png - :width: 600 + :scale: 25 % + :align: center :alt: AequilibraE project structure | @@ -75,10 +76,10 @@ folders and databases. The second key component of any model is the **parameters.yaml** file, which holds the default values for a number of procedures (e.g. assignment convergence), as well as the specification for networks imported from -Open-Street Maps and other general *import-export* parameters. +OpenStreetMap and other general *import-export* parameters. -The third and last required component of an AequilibraE model is the Matrices -folder, where all the matrices in binary format (in AequilibraE's native AEM or +The third and last required component of an AequilibraE model is the **Matrices +folder**, where all the matrices in binary format (in AequilibraE's native AEM or OMX formats) should be placed. This folder can be empty, however, as no particular matrix is required to exist in an AequilibraE model. @@ -103,7 +104,7 @@ The following sections presents the structure and contents for all databases in .. toctree:: :maxdepth: 1 - :caption: Dive deep into project structure! + :caption: Or dive deep into project structure! project_database/index parameter_file diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/parameter_file.rst b/docs/source/modeling_with_aequilibrae/aequilibrae_project/parameter_file.rst similarity index 97% rename from docs/source/modeling_with_aequilibrae/1-aequilibrae_project/parameter_file.rst rename to docs/source/modeling_with_aequilibrae/aequilibrae_project/parameter_file.rst index 9f8789432..82bd3b8e2 100644 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/parameter_file.rst +++ b/docs/source/modeling_with_aequilibrae/aequilibrae_project/parameter_file.rst @@ -73,7 +73,7 @@ to the use of **integer**, **numeric** and **varchar**. For the case of all non-mandatory fields, two more parameters are possible: 'osm_source' and 'osm_behaviour'. Those two fields provide the necessary information for importing data from -`Open Street Maps `_ in case such resource is required, and +`OpenStreetMap `_ in case such resource is required, and they work in the following way: 'osm_source': The name of the tag for which data needs to be retrieved. Common tags are @@ -108,13 +108,13 @@ that it does not make sense to have fields for one or two directions and that it yet to import any tagged values from OSM at the moment, and therefore the parameter *osm_source* would have no effect here. -Open Street Maps -~~~~~~~~~~~~~~~~ +OpenStreetMap +~~~~~~~~~~~~~ The **OSM** group of parameters has two specifications: **modes** and **all_link_types**. **modes** contains the list of key tags we will import for each mode. Description of tags can be found on -`Open-Street Maps `_, and we recommend +`OpenStreetMap Wiki `_, and we recommend not changing the standard parameters unless you are exactly sure of what you are doing. For each mode to be imported there is also a mode filter to control for non-default diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/about.rst b/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/about.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/about.rst rename to docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/about.rst diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/attributes_documentation.rst b/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/attributes_documentation.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/attributes_documentation.rst rename to docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/attributes_documentation.rst diff --git a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/data_model/transit_graph_configs.rst b/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/data_model/transit_graph_configs.rst new file mode 100644 index 000000000..44332333e --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/data_model/transit_graph_configs.rst @@ -0,0 +1,29 @@ +*transit_graph_configs* table structure +--------------------------------------- + +The *transit_graph_configs* table holds configuration parameters for a TransitGraph of a particular `period_id` +Attributes follow + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + period_id*,INTEGER,NO, + config,TEXT,YES, + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + CREATE TABLE if not exists transit_graph_configs (period_id INTEGER UNIQUE NOT NULL PRIMARY KEY REFERENCES periods(period_id), + config TEXT); + + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('transit_graph_configs','period_id', 'The period this config is associated with.'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('transit_graph_configs','mode_id', 'JSON string containing the configuration parameters.'); diff --git a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/data_model/zones.rst b/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/data_model/zones.rst new file mode 100644 index 000000000..34d6d0393 --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/data_model/zones.rst @@ -0,0 +1,46 @@ +*zones* table structure +----------------------- + +The *zones* table holds information on the Traffic Analysis Zones (TAZs) +in AequilibraE's model. + +The **zone_id** field identifies the zone. + +The **area** field corresponds to the area of the zone in **km2**. +TAZs' area is automatically updated by triggers. + +The **name** fields allows one to identity the zone using a name +or any other description. + +.. csv-table:: + :header: "Field", "Type", "NULL allowed", "Default Value" + :widths: 30, 20, 20, 20 + + ogc_fid*,INTEGER,YES, + zone_id,INTEGER,NO, + area,NUMERIC,YES, + name,TEXT,YES, + geometry,MULTIPOLYGON,NO,'' + + +(* - Primary key) + + + +The SQL statement for table and index creation is below. + + +:: + + + CREATE TABLE 'zones' (ogc_fid INTEGER PRIMARY KEY, + zone_id INTEGER UNIQUE NOT NULL, + area NUMERIC, + "name" TEXT); + + SELECT AddGeometryColumn( 'zones', 'geometry', 4326, 'MULTIPOLYGON', 'XY', 1); + CREATE UNIQUE INDEX idx_zone ON zones (zone_id); + SELECT CreateSpatialIndex( 'zones' , 'geometry' ); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('zones','zone_id', 'Unique node ID'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('zones','area', 'Area of the zone in km2'); + INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('zones','name', 'Name of the zone, if any'); diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/datamodel.rst.template b/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/datamodel.rst.template similarity index 100% rename from docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/datamodel.rst.template rename to docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/datamodel.rst.template diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/index.rst b/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/index.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/index.rst rename to docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/index.rst diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/link_types.rst b/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/link_types.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/link_types.rst rename to docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/link_types.rst diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/matrices.rst b/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/matrices.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/matrices.rst rename to docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/matrices.rst diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/modes.rst b/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/modes.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/modes.rst rename to docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/modes.rst diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/network.rst b/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/network.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/network.rst rename to docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/network.rst diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/network_geometry.rst b/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/network_geometry.rst similarity index 99% rename from docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/network_geometry.rst rename to docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/network_geometry.rst index 7cd7e26b8..8fe8f80fc 100644 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/network_geometry.rst +++ b/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/network_geometry.rst @@ -261,7 +261,7 @@ Link distance +++++++++++++ Link distance cannot be changed by the user, as it is automatically recalculated -using the Spatialite function *GeodesicLength*, which always returns distances +using the SpatiaLite function ``GeodesicLength``, which always returns distances in meters. Link direction diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/network_import_and_export.rst b/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/network_import_and_export.rst similarity index 97% rename from docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/network_import_and_export.rst rename to docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/network_import_and_export.rst index 6df476cf8..e393151b9 100644 --- a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/network_import_and_export.rst +++ b/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/network_import_and_export.rst @@ -25,7 +25,7 @@ Python limitations `````````````````` As it happens in other cases, Python's usual implementation of SQLite is -incomplete, and does not include R-Tree, a key extension used by Spatialite for +incomplete, and does not include R-Tree, a key extension used by SpatiaLite for GIS operations. For this reason, AequilibraE's default option when importing a network from OSM @@ -69,13 +69,13 @@ with the option for creating such indices. If you want to learn a little more about this topic, you can access this `blog post `_ or check out the SQLite page on `R-Tree `_. -If you want to take a stab at solving your SQLite/Spatialite problem +If you want to take a stab at solving your SQLite/SpatiaLite problem permanently, take a look at this `other blog post `_. Please also note that the network consistency triggers will NOT work before spatial indices have been created and/or if the editing is being done on a -platform that does not support both R-Tree and Spatialite. +platform that does not support both R-Tree and SpatiaLite. .. seealso:: diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/periods.rst b/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/periods.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/periods.rst rename to docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/periods.rst diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/transit_graph.rst b/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/transit_graph.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/transit_graph.rst rename to docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/transit_graph.rst diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/zones.rst b/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/zones.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/zones.rst rename to docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/zones.rst diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/results_database.rst b/docs/source/modeling_with_aequilibrae/aequilibrae_project/results_database.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/1-aequilibrae_project/results_database.rst rename to docs/source/modeling_with_aequilibrae/aequilibrae_project/results_database.rst diff --git a/docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/index.rst b/docs/source/modeling_with_aequilibrae/aequilibrae_project/transit_database/index.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/index.rst rename to docs/source/modeling_with_aequilibrae/aequilibrae_project/transit_database/index.rst diff --git a/docs/source/modeling_with_aequilibrae/index.rst b/docs/source/modeling_with_aequilibrae/index.rst index aeec32e55..2b21d2a4d 100644 --- a/docs/source/modeling_with_aequilibrae/index.rst +++ b/docs/source/modeling_with_aequilibrae/index.rst @@ -15,14 +15,10 @@ In this section you can find a deep dive into modeling with AequilibraE, from a start guide to a complete view into AequilibraE's data structure. .. toctree:: - :numbered: 3 :maxdepth: 1 :caption: A guide to AequilibraE - 1-aequilibrae_project/index - 2-static_traffic_assignment/index - 3-transit_assignment/index - 4-route_choice/index - 5-aequilibrae_matrix - 6-project_components - 6-accessing-project-data \ No newline at end of file + aequilibrae_project/index + static_traffic_assignment/index + transit_assignment/index + route_choice/index \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/6-project_components.rst b/docs/source/modeling_with_aequilibrae/project_components.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/6-project_components.rst rename to docs/source/modeling_with_aequilibrae/project_components.rst diff --git a/docs/source/modeling_with_aequilibrae/4-route_choice/choice_set_generation.rst b/docs/source/modeling_with_aequilibrae/route_choice/choice_set_generation.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/4-route_choice/choice_set_generation.rst rename to docs/source/modeling_with_aequilibrae/route_choice/choice_set_generation.rst diff --git a/docs/source/modeling_with_aequilibrae/4-route_choice/index.rst b/docs/source/modeling_with_aequilibrae/route_choice/index.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/4-route_choice/index.rst rename to docs/source/modeling_with_aequilibrae/route_choice/index.rst diff --git a/docs/source/modeling_with_aequilibrae/4-route_choice/route_choice_model.rst b/docs/source/modeling_with_aequilibrae/route_choice/route_choice_model.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/4-route_choice/route_choice_model.rst rename to docs/source/modeling_with_aequilibrae/route_choice/route_choice_model.rst diff --git a/docs/source/modeling_with_aequilibrae/2-static_traffic_assignment/assignment_mechanics.rst b/docs/source/modeling_with_aequilibrae/static_traffic_assignment/assignment_mechanics.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/2-static_traffic_assignment/assignment_mechanics.rst rename to docs/source/modeling_with_aequilibrae/static_traffic_assignment/assignment_mechanics.rst diff --git a/docs/source/modeling_with_aequilibrae/2-static_traffic_assignment/index.rst b/docs/source/modeling_with_aequilibrae/static_traffic_assignment/index.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/2-static_traffic_assignment/index.rst rename to docs/source/modeling_with_aequilibrae/static_traffic_assignment/index.rst diff --git a/docs/source/modeling_with_aequilibrae/2-static_traffic_assignment/multi_class_equilibrium.rst b/docs/source/modeling_with_aequilibrae/static_traffic_assignment/multi_class_equilibrium.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/2-static_traffic_assignment/multi_class_equilibrium.rst rename to docs/source/modeling_with_aequilibrae/static_traffic_assignment/multi_class_equilibrium.rst diff --git a/docs/source/modeling_with_aequilibrae/3-transit_assignment/hyperpath_routing.rst b/docs/source/modeling_with_aequilibrae/transit_assignment/hyperpath_routing.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/3-transit_assignment/hyperpath_routing.rst rename to docs/source/modeling_with_aequilibrae/transit_assignment/hyperpath_routing.rst diff --git a/docs/source/modeling_with_aequilibrae/3-transit_assignment/index.rst b/docs/source/modeling_with_aequilibrae/transit_assignment/index.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/3-transit_assignment/index.rst rename to docs/source/modeling_with_aequilibrae/transit_assignment/index.rst diff --git a/docs/source/modeling_with_aequilibrae/3-transit_assignment/transit_graph.rst b/docs/source/modeling_with_aequilibrae/transit_assignment/transit_graph.rst similarity index 100% rename from docs/source/modeling_with_aequilibrae/3-transit_assignment/transit_graph.rst rename to docs/source/modeling_with_aequilibrae/transit_assignment/transit_graph.rst diff --git a/docs/source/useful_information/installation.rst b/docs/source/useful_information/installation.rst index 0e0f2013d..e4f641cd6 100644 --- a/docs/source/useful_information/installation.rst +++ b/docs/source/useful_information/installation.rst @@ -35,35 +35,35 @@ for all currently supported Python versions and major platforms. .. _installing_spatialite_on_windows: -Spatialite +SpatiaLite ++++++++++ -Although the presence of Spatialite is rather ubiquitous in the GIS ecosystem, +Although the presence of SpatiaLite is rather ubiquitous in the GIS ecosystem, it has to be installed separately from Python or AequilibraE in any platform. This `blog post `_ has a more -comprehensive explanation of what is the setup you need to get Spatialite working, +comprehensive explanation of what is the setup you need to get SpatiaLite working, but that is superfluous if all you want is to get it working. Windows ^^^^^^^ .. note:: - On Windows ONLY, AequilibraE automatically verifies if you have Spatialite + On Windows ONLY, AequilibraE automatically verifies if you have SpatiaLite installed in your system and downloads it to your temporary folder if you do not. -Spatialite does not have great support on Python for Windows. For this reason, -it is necessary to download Spatialite for Windows and inform and load it +SpatiaLite does not have great support on Python for Windows. For this reason, +it is necessary to download SpatiaLite for Windows and inform and load it to the Python SQLite driver every time you connect to the database. -One can download the appropriate version of the latest Spatialite release +One can download the appropriate version of the latest SpatiaLite release directly from its `project page `_ , or the cached versions on AequilibraE's website for `64-Bit Python `_ After unpacking the zip file into its own folder (say ``D:/spatialite``), one can -*temporarily* add the spatialite folder to system path environment variable, +*temporarily* add the SpatiaLite folder to system path environment variable, as follows: :: @@ -71,13 +71,13 @@ as follows: import os os.environ['PATH'] = 'D:/spatialite' + ';' + os.environ['PATH'] -For a permanent recording of the Spatialite location on your system, please refer +For a permanent recording of the SpatiaLite location on your system, please refer to the blog post referenced above or Windows-specific documentation. Ubuntu Linux ^^^^^^^^^^^^ -On Ubuntu it is possible to install Spatialite by simply using apt-get +On Ubuntu it is possible to install SpatiaLite by simply using apt-get :: From 2271aba7e083f98c760022f3c2c808b855d168da Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Mon, 9 Sep 2024 15:39:08 -0300 Subject: [PATCH 35/57] . --- .gitignore | 4 +- .../accessing_project_data.rst | 27 +++++-- .../aequilibrae_project/index.rst | 8 ++ .../project_database/periods.rst | 2 + .../project_components.rst | 75 +++++++++++++++---- 5 files changed, 91 insertions(+), 25 deletions(-) diff --git a/.gitignore b/.gitignore index baa4ef4e6..7421c20e0 100644 --- a/.gitignore +++ b/.gitignore @@ -5,8 +5,8 @@ _templates docs/source/sg_execution_times.rst docs/source/_auto_examples docs/source/api/generated -docs/source/modeling_with_aequilibrae/1-aequilibrae_project/project_database/data_model -docs/source/modeling_with_aequilibrae/1-aequilibrae_project/transit_database/data_model +docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/data_model +docs/source/modeling_with_aequilibrae/aequilibrae_project/transit_database/data_model # User-specific stuff: .idea/**/workspace.xml diff --git a/docs/source/modeling_with_aequilibrae/accessing_project_data.rst b/docs/source/modeling_with_aequilibrae/accessing_project_data.rst index b17316098..5b1811134 100644 --- a/docs/source/modeling_with_aequilibrae/accessing_project_data.rst +++ b/docs/source/modeling_with_aequilibrae/accessing_project_data.rst @@ -55,14 +55,18 @@ Each item in the 'links' table is a ``Link`` object. # And refresh the links in memory for usage project_links.refresh() -.. seealso:: - - * :func:`aequilibrae.project.network.Links` - Class documentation +.. admonition:: References + + * :ref:`modifications_on_links_layer` * :ref:`project_from_link_layer` | :ref:`editing_network_splitting_link` Usage examples +.. seealso:: + + * :func:`aequilibrae.project.network.Links` + Class documentation + ``project.network.nodes`` ------------------------- @@ -114,6 +118,13 @@ Each item in the 'nodes' table is a ``Node`` object. # a Pandas DataFrame project_nodes.lonlat +.. admonition:: References + + * :ref:`modifications_on_nodes_layer` + + * :ref:`editing_network_nodes` + Usage example + .. seealso:: * :func:`aequilibrae.project.network.Nodes` @@ -187,10 +198,12 @@ Each item in the 'zones' table is a ``Zone`` object. # And to get the nearest zone to giver geometry project_zones.get_closest_zone(Point(-71.3336, -29.9490)) +.. admonition:: References + + * :ref:`create_zones` + Usage example + .. seealso:: * :func:`aequilibrae.project.Zoning` Class documentation - - * :ref:`create_zones` - Usage example diff --git a/docs/source/modeling_with_aequilibrae/aequilibrae_project/index.rst b/docs/source/modeling_with_aequilibrae/aequilibrae_project/index.rst index 51201808f..8d700aedc 100644 --- a/docs/source/modeling_with_aequilibrae/aequilibrae_project/index.rst +++ b/docs/source/modeling_with_aequilibrae/aequilibrae_project/index.rst @@ -102,6 +102,14 @@ to a route system as of yet. The following sections presents the structure and contents for all databases in the project. +.. toctree:: + :maxdepth: 1 + :caption: Get to know the project components + + ../project_components + ../accessing_project_data + ../aequilibrae_matrix + .. toctree:: :maxdepth: 1 :caption: Or dive deep into project structure! diff --git a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/periods.rst b/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/periods.rst index e1b8aba38..082ae8d8d 100644 --- a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/periods.rst +++ b/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/periods.rst @@ -1,3 +1,5 @@ +.. _tables_period: + Periods table ============= diff --git a/docs/source/modeling_with_aequilibrae/project_components.rst b/docs/source/modeling_with_aequilibrae/project_components.rst index a16615bbc..c061a546e 100644 --- a/docs/source/modeling_with_aequilibrae/project_components.rst +++ b/docs/source/modeling_with_aequilibrae/project_components.rst @@ -3,7 +3,7 @@ Project Components ================== -In the :ref:`aeq_project_structure` section, we presented the main file components and folders that +In the :ref:`aeq_project_structure` section, we present the main file components and folders that consists an AequilibraE project. We also present in :ref:`aeq_project_database_tables` all tables that are part of the project database, how do they look like, and what fields do they have. @@ -62,11 +62,16 @@ this information, otherwise it will be lost. # it is possible to create one too. project.about.create() -.. seealso:: +.. admonition:: References * :ref:`tables_about` Table documentation +.. seealso:: + + * :func:`aequilibrae.procedure.About` + Class documentation + ``project.FieldEditor`` ----------------------- @@ -99,11 +104,16 @@ This class is directly accessed from within the corresponding module one wants t All field descriptions are kept in the table 'attributes_documentation'. -.. seealso:: +.. admonition:: References * :ref:`parameters_metadata` Table documentation +.. seealso:: + + * :func:`aequilibrae.project.FieldEditor` + Class documentation + ``project.log`` --------------- @@ -120,13 +130,15 @@ It is possible to access the log file contents, as presented in the next code bl # This option must be used wiesly once the deletion of data in the log file can't be undone. project_log.clear() +.. admonition:: References + + * :ref:`useful-log-tips` + Usage example + .. seealso:: * :func:`aequilibrae.project.Log` Class documentation - - * :ref:`useful-log-tips` - Usage example ``project.matrices`` -------------------- @@ -171,14 +183,16 @@ records in the 'matrices' table. Each item in the 'matrices' table is a ``Matri # get an AequilibraE matrix. matrices.get_matrix("demand_aem") +.. admonition:: References + + * :ref:`matrix_table` + Table documentation + .. seealso:: * :func:`aequilibrae.project.Matrices` Class documentation - * :ref:`matrix_table` - Table documentation - ``project.network.link_types`` ------------------------------ @@ -200,29 +214,38 @@ Each item in the 'link_types' table is a ``LinkType`` object. new_link_type.speed = 35 new_link_type.link_type = "Arterial" - # To save the modifications for ``new_link_type`` + # To save the modifications for `new_link_type` new_link_type.save() - # To create a new field in the 'link_types' table, you can call the function ``fields`` + # To create a new field in the 'link_types' table, you can call the function `fields` # to return a FieldEditor instance, which can be edited link_types.fields() - # You can also remove a LinkType from a project using its ``link_type_id`` + # You can also remove a LinkType from a project using its `link_type_id` link_types.delete("A") # And don't forget to save the modifications you did in the 'link_types' table link_types.save() - # To check all ``LinkTypes`` in the project + # To check all `LinkTypes` in the project link_types.all_types() # returns a dictionary with all LinkType objects in the model. - # The dictionary's keys are the ``link_type_id``'s + # The dictionary's keys are the `link_type_id`'s # There are two ways to get a LinkType from the 'link_types' table - # using the ``link_type_id`` + # using the `link_type_id` link_types.get("p") - # or using the ``link_type`` + # or using the `link_type` link_types.get_by_name("primary") +.. admonition:: References + + * :ref:`tables_link_types` + Table documentation + +.. seealso:: + + * :func:`aequilibrae.project.LinkTypes` + Class documentation ``project.network.modes`` ------------------------- @@ -263,6 +286,16 @@ Each item in 'modes' table is a ``Mode`` object. # or using the ``mode_name`` modes.get_by_name("car") +.. admonition:: References + + * :ref:`tables_modes` + Table documentation + +.. seealso:: + + * :func:`aequilibrae.project.Modes` + Class documentation + ``project.network.periods`` --------------------------- @@ -304,3 +337,13 @@ Each item in the 'periods' table is a ``Period`` object. # And we save this period modification select_period.save() + +.. admonition:: References + + * :ref:`tables_period` + Table documentation + +.. seealso:: + + * :func:`aequilibrae.project.Periods` + Class documentation From fb8ebe8750103799eab02792f6c70b7fcb8d2d3b Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Mon, 9 Sep 2024 15:57:41 -0300 Subject: [PATCH 36/57] Update table_documentation.py --- docs/table_documentation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/table_documentation.py b/docs/table_documentation.py index 6730f8563..6d23c8310 100644 --- a/docs/table_documentation.py +++ b/docs/table_documentation.py @@ -34,7 +34,7 @@ def __init__(self, component: str, tgt_fldr: str): Path(realpath(__file__)).parent / "source" / "modeling_with_aequilibrae" - / "1-aequilibrae_project" + / "aequilibrae_project" / tgt_fldr ) From 6e0c349c4dc1caf0bb31253ee2e196acc168db6d Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Mon, 9 Sep 2024 16:08:54 -0300 Subject: [PATCH 37/57] Create datamodel.rst.template --- .../transit_database/datamodel.rst.template | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 docs/source/modeling_with_aequilibrae/aequilibrae_project/transit_database/datamodel.rst.template diff --git a/docs/source/modeling_with_aequilibrae/aequilibrae_project/transit_database/datamodel.rst.template b/docs/source/modeling_with_aequilibrae/aequilibrae_project/transit_database/datamodel.rst.template new file mode 100644 index 000000000..2ebd72b2c --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/aequilibrae_project/transit_database/datamodel.rst.template @@ -0,0 +1,32 @@ +:orphan: + +.. _transit_supply_data_model: + +SQL Data model +^^^^^^^^^^^^^^ + +The data model presented in this section pertains only to the structure of +AequilibraE's public_transport database and general information about the usefulness +of specific fields, especially on the interdependency between tables. + +Conventions +''''''''''' + +A few conventions have been adopted in the definition of the data model and some +are listed below: + +- Geometry field is always called **geometry** +- Projection is 4326 (WGS84) +- Tables are all in all lower case + + +.. Do not touch below this line unless you know EXACTLY what you are doing. +.. it will be automatically populated + +Project tables +'''''''''''''' + +.. toctree:: + :maxdepth: 1 + + LIST_OF_TABLES \ No newline at end of file From 64b1aedea4cca137c61bb59c82100dd3f3e0cbd2 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Tue, 10 Sep 2024 11:13:31 -0300 Subject: [PATCH 38/57] removes geopandas from some examples --- .../plot_route_choice_basics.py | 3 +- .../plot_route_choice_set.py | 5 ++- .../plot_subarea_analysis.py | 11 +++---- .../creating_models/plot_create_from_layer.py | 31 ++--------------- .../creating_models/plot_create_from_osm.py | 33 +++---------------- .../examples/visualization/plot_display.py | 2 +- 6 files changed, 17 insertions(+), 68 deletions(-) diff --git a/docs/source/examples/assignment_workflows/plot_route_choice_basics.py b/docs/source/examples/assignment_workflows/plot_route_choice_basics.py index f1b4e3eab..49eeee784 100644 --- a/docs/source/examples/assignment_workflows/plot_route_choice_basics.py +++ b/docs/source/examples/assignment_workflows/plot_route_choice_basics.py @@ -169,11 +169,10 @@ # let's define a function to plot assignment results def plot_results(link_loads): import folium - import geopandas as gpd link_loads = link_loads[link_loads.tot > 0] max_load = link_loads["tot"].max() - links = gpd.GeoDataFrame(project.network.links.data, crs=4326) + links = project.network.links.data loaded_links = links.merge(link_loads, on="link_id", how="inner") loads_lyr = folium.FeatureGroup("link_loads") diff --git a/docs/source/examples/assignment_workflows/plot_route_choice_set.py b/docs/source/examples/assignment_workflows/plot_route_choice_set.py index 98ff23c07..1a7f06235 100644 --- a/docs/source/examples/assignment_workflows/plot_route_choice_set.py +++ b/docs/source/examples/assignment_workflows/plot_route_choice_set.py @@ -101,7 +101,6 @@ # %% # Now we will plot the paths we just created for the second OD pair import folium -import geopandas as gpd # %% # Let's create a separate for each route so we can visualize one at a time @@ -115,8 +114,8 @@ # %% # We get the data we will use for the plot: Links, Nodes and the route choice set -links = gpd.GeoDataFrame(project.network.links.data, crs=4326) -nodes = gpd.GeoDataFrame(project.network.nodes.data, crs=4326) +links = project.network.links.data +nodes = project.network.nodes.data plot_routes = choice_set[(choice_set["origin id"] == 77011)]["route set"].values diff --git a/docs/source/examples/assignment_workflows/plot_subarea_analysis.py b/docs/source/examples/assignment_workflows/plot_subarea_analysis.py index e92397fbe..579319960 100644 --- a/docs/source/examples/assignment_workflows/plot_subarea_analysis.py +++ b/docs/source/examples/assignment_workflows/plot_subarea_analysis.py @@ -17,7 +17,6 @@ import itertools import pandas as pd -import geopandas as gpd import numpy as np import folium @@ -110,7 +109,7 @@ # # the union of their geometry. It's best to choose a polygon that avoids any unnecessary intersections with links as # the resource requirements of this approach grow quadratically with the number of links cut. zones_of_interest = [29, 30, 31, 32, 33, 34, 37, 38, 39, 40, 49, 50, 51, 52, 57, 58, 59, 60] -zones = gpd.GeoDataFrame(project.zoning.data).set_index("zone_id") +zones = project.zoning.data.set_index("zone_id") zones = zones.loc[zones_of_interest] zones.head() @@ -164,7 +163,7 @@ def plot_results(link_loads): link_loads = link_loads[link_loads.tot > 0] max_load = link_loads["tot"].max() - links = gpd.GeoDataFrame(project.network.links.data, crs=4326) + links = project.network.links.data loaded_links = links.merge(link_loads, on="link_id", how="inner") loads_lyr = folium.FeatureGroup("link_loads") @@ -202,18 +201,18 @@ def plot_results(link_loads): # %% # We take the union of this GeoDataFrame as our polygon. -poly = zones.unary_union +poly = zones.union_all() poly # %% # It's useful later on to know which links from the network cross our polygon. -links = gpd.GeoDataFrame(project.network.links.data) +links = project.network.links.data inner_links = links[links.crosses(poly.boundary)].sort_index() inner_links.head() # %% # As well as which nodes are interior. -nodes = gpd.GeoDataFrame(project.network.nodes.data).set_index("node_id") +nodes = project.network.nodes.data.set_index("node_id") inside_nodes = nodes.sjoin(zones, how="inner").sort_index() inside_nodes.head() diff --git a/docs/source/examples/creating_models/plot_create_from_layer.py b/docs/source/examples/creating_models/plot_create_from_layer.py index 97ddbe2de..c0abe4bc0 100644 --- a/docs/source/examples/creating_models/plot_create_from_layer.py +++ b/docs/source/examples/creating_models/plot_create_from_layer.py @@ -132,37 +132,12 @@ new_link.save() # %% -# We grab all the links data as a Pandas DataFrame so we can process it easier +# We grab all the links data as a geopandas GeoDataFrame so we can process it easier links = project.network.links.data # %% -# We create a Folium layer -network_links = folium.FeatureGroup("links") - -#%% -# We do some Python magic to transform this dataset into the format required by Folium. -# We are only getting `link_id` and `link_type` into the map, but we could get other pieces of info as well - -# %% -for i, row in links.iterrows(): - points = row.geometry.wkt.replace("LINESTRING ", "").replace("(", "").replace(")", "").split(", ") - points = "[[" + "],[".join([p.replace(" ", ", ") for p in points]) + "]]" - # We need to take from x/y to lat/long - points = [[x[1], x[0]] for x in eval(points)] - - line = folium.vector_layers.PolyLine( - points, popup=f"link_id: {row.link_id}", tooltip=f"{row.link_type}", color="blue", weight=10 - ).add_to(network_links) - -# %% -# We get the center of the region we are working with some SQL magic -curr = project.conn.cursor() -curr.execute("select avg(xmin), avg(ymin) from idx_links_geometry") -long, lat = curr.fetchone() - -# %% -map_osm = folium.Map(location=[lat, long], zoom_start=15) -network_links.add_to(map_osm) +# Let's plot our network! +map_osm = links.explore(color="blue", weight=10, tooltip="link_type", popup="link_id", name="links") folium.LayerControl().add_to(map_osm) map_osm diff --git a/docs/source/examples/creating_models/plot_create_from_osm.py b/docs/source/examples/creating_models/plot_create_from_osm.py index 5a5d84dae..162cf5058 100644 --- a/docs/source/examples/creating_models/plot_create_from_osm.py +++ b/docs/source/examples/creating_models/plot_create_from_osm.py @@ -37,41 +37,18 @@ project.network.create_from_osm(place_name="Nauru") # %% -# We can also choose to create a model from a polygon (which must be in `EPSG:4326`) -# Or from a Polygon defined by a bounding box, for example. +# We can also choose to create a model from a polygon (which must be in ``EPSG:4326``) +# or from a Polygon defined by a bounding box, for example. # project.network.create_from_osm(model_area=box(-112.185, 36.59, -112.179, 36.60)) # %% -# We grab all the links data as a Pandas DataFrame so we can process it easier +# We grab all the links data as a geopandas GeoDataFrame so we can process it easier links = project.network.links.data # %% -# We create a Folium layer -network_links = folium.FeatureGroup("links") - -# %% -# We do some Python magic to transform this dataset into the format required by Folium. -# We are only getting link_id and link_type into the map, but we could get other pieces of info as well. -for i, row in links.iterrows(): - points = row.geometry.wkt.replace("LINESTRING ", "").replace("(", "").replace(")", "").split(", ") - points = "[[" + "],[".join([p.replace(" ", ", ") for p in points]) + "]]" - # we need to take from x/y to lat/long - points = [[x[1], x[0]] for x in eval(points)] - - line = folium.vector_layers.PolyLine( - points, popup=f"link_id: {row.link_id}", tooltip=f"{row.link_type}", color="blue", weight=10 - ).add_to(network_links) - -# %% -# We get the center of the region we are working with some SQL magic -curr = project.conn.cursor() -curr.execute("select avg(xmin), avg(ymin) from idx_links_geometry") -long, lat = curr.fetchone() - -# %% -map_osm = folium.Map(location=[lat, long], zoom_start=14) -network_links.add_to(map_osm) +# Let's plot our network! +map_osm = links.explore(color="blue", weight=10, tooltip="link_type", popup="link_id", name="links") folium.LayerControl().add_to(map_osm) map_osm diff --git a/docs/source/examples/visualization/plot_display.py b/docs/source/examples/visualization/plot_display.py index 24892a62e..a070ef9cf 100644 --- a/docs/source/examples/visualization/plot_display.py +++ b/docs/source/examples/visualization/plot_display.py @@ -29,7 +29,7 @@ project = create_example(fldr, "nauru") # %% -# We grab all the links data as a Pandas dataframe so we can process it easier +# We grab all the links data as a geopandas GeoDataFrame so we can process it easier links = project.network.links.data nodes = project.network.nodes.data From c7106b4e01914bd4489807b61cc453967d6002d6 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Tue, 10 Sep 2024 12:53:43 -0300 Subject: [PATCH 39/57] fixes imports --- README.md | 2 +- aequilibrae/matrix/aequilibrae_matrix.py | 2 +- aequilibrae/paths/linear_approximation.py | 2 +- aequilibrae/project/about.py | 2 +- aequilibrae/project/field_editor.py | 2 +- aequilibrae/project/network/link_types.py | 9 +- aequilibrae/project/network/links.py | 20 ++-- aequilibrae/project/network/mode.py | 2 +- aequilibrae/project/network/modes.py | 2 +- aequilibrae/project/network/nodes.py | 2 +- aequilibrae/transit/__init__.py | 1 - docs/requirements-docs.txt | 3 +- .../assignment_workflows/just_matrices.omx | Bin 15520 -> 0 bytes .../select_link_analysis.omx | Bin 15520 -> 0 bytes .../transit_database/index.rst | 4 - docs/source/sg_execution_times.rst | 106 ------------------ 16 files changed, 23 insertions(+), 136 deletions(-) delete mode 100644 docs/source/examples/assignment_workflows/just_matrices.omx delete mode 100644 docs/source/examples/assignment_workflows/select_link_analysis.omx delete mode 100644 docs/source/sg_execution_times.rst diff --git a/README.md b/README.md index bb6f9e18c..cd6f09bcb 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ readily available to other modeling activities. AequilibraE includes multi-class user-equilibrium assignment with full support for class-specific networks, value-of-time and generalized cost functions, and includes a range of equilibration algorithms, including MSA, the traditional -Frank-Wolfe as well as the state-of-the-art biconjugate Frank-Wolfe. +Frank-Wolfe as well as the state-of-the-art Bi-conjugate Frank-Wolfe. AequilibraE's support for public transport includes a GTFS importer that can map-match routes into the model network and an optimized version of the diff --git a/aequilibrae/matrix/aequilibrae_matrix.py b/aequilibrae/matrix/aequilibrae_matrix.py index c7719ab28..9b7740381 100644 --- a/aequilibrae/matrix/aequilibrae_matrix.py +++ b/aequilibrae/matrix/aequilibrae_matrix.py @@ -292,7 +292,7 @@ def create_from_omx( memory_only: bool = True, ) -> None: """ - Creates an AequilibraeMatrix from an original Open Matrix + Creates an AequilibraeMatrix from an original OpenMatrix :Arguments: **file_path** (:obj:`str`): Path for the output AequilibraeMatrix diff --git a/aequilibrae/paths/linear_approximation.py b/aequilibrae/paths/linear_approximation.py index 8611922c3..3c7f56f39 100644 --- a/aequilibrae/paths/linear_approximation.py +++ b/aequilibrae/paths/linear_approximation.py @@ -693,7 +693,7 @@ def calculate_stepsize(self): self.stepsize = tiny_step else: self.stepsize = 0.0 - # need to reset conjugate / biconjugate direction search + # need to reset conjugate / bi-conjugate direction search self.do_fw_step = True self.conjugate_failed = True self.iteration_issue.append("Found bad conjugate direction step. Performing FW search. {e.args}") diff --git a/aequilibrae/project/about.py b/aequilibrae/project/about.py index 6c9b1ae4e..8ffc8484a 100644 --- a/aequilibrae/project/about.py +++ b/aequilibrae/project/about.py @@ -8,7 +8,7 @@ class About: - """Provides an interface for querying and editing the 'about' table of an AequilibraE project + """Provides an interface for querying and editing the **about** table of an AequilibraE project .. code-block:: python diff --git a/aequilibrae/project/field_editor.py b/aequilibrae/project/field_editor.py index 81720da72..488e36c63 100644 --- a/aequilibrae/project/field_editor.py +++ b/aequilibrae/project/field_editor.py @@ -63,7 +63,7 @@ def add(self, field_name: str, description: str, data_type="NUMERIC") -> None: **description** (:obj:`str`): Description of the field to be inserted in the metadata - **data_type** (:obj:`str`, *Optional*): Valid SQLite Data type. Default: ``'NUMERIC'`` + **data_type** (:obj:`str`, *Optional*): Valid SQLite Data type. Default: "NUMERIC" """ if field_name.lower() in self._original_values.keys(): raise ValueError("attribute_name already exists") diff --git a/aequilibrae/project/network/link_types.py b/aequilibrae/project/network/link_types.py index f5be08d36..df3f91140 100644 --- a/aequilibrae/project/network/link_types.py +++ b/aequilibrae/project/network/link_types.py @@ -88,7 +88,7 @@ def new(self, link_type_id: str) -> LinkType: return lt def delete(self, link_type_id: str) -> None: - """Removes the ``LinkType`` with ``link_type_id`` from the project""" + """Removes the link_type with *link_type_id* from the project""" try: lt = self.__items[link_type_id] # type: LinkType lt.delete() @@ -99,21 +99,20 @@ def delete(self, link_type_id: str) -> None: self.logger.warning(f"Link type {link_type_id} was successfully removed from the project database") def get(self, link_type_id: str) -> LinkType: - """Get a ``LinkType`` from the network by its ``link_type_id``""" + """Get a link_type from the network by its *link_type_id*""" if link_type_id not in self.__items: raise ValueError(f"Link type {link_type_id} does not exist in the model") return self.__items[link_type_id] def get_by_name(self, link_type: str) -> LinkType: - """Get a ``LinkType`` from the network by its ``link_type`` (i.e. name)""" + """Get a link_type from the network by its *link_type* (i.e. name)""" for lt in self.__items.values(): if lt.link_type.lower() == link_type.lower(): return lt - # TODO: fix the FieldEditor import to self.project def fields(self) -> FieldEditor: """Returns a FieldEditor class instance to edit the Link_Types table fields and their metadata""" - return FieldEditor(self.project, "link_types") + return FieldEditor(self.project.project_base_path, "link_types") def all_types(self) -> dict: """Returns a dictionary with all LinkType objects available in the model. link_type_id as key""" diff --git a/aequilibrae/project/network/links.py b/aequilibrae/project/network/links.py index 76e9f04d7..6dd0f4974 100644 --- a/aequilibrae/project/network/links.py +++ b/aequilibrae/project/network/links.py @@ -46,12 +46,12 @@ def __init__(self, net): self.refresh_fields() def get(self, link_id: int) -> Link: - """Get a link from the network by its ``link_id`` + """Get a link from the network by its *link_id* - It raises an error if ``link_id`` does not exist + It raises an error if link_id does not exist :Arguments: - **link_id** (:obj:`int`): ID of a link to retrieve + **link_id** (:obj:`int`): Id of a link to retrieve :Returns: **link** (:obj:`Link`): Link object for requested link_id @@ -71,7 +71,7 @@ def new(self) -> Link: """Creates a new link :Returns: - **link** (:obj:`Link`): A new link object populated only with ``link_id`` (not saved in the model yet) + **link** (:obj:`Link`): A new link object populated only with link_id (not saved in the model yet) """ data = {key: None for key in self.__fields} @@ -86,13 +86,13 @@ def new(self) -> Link: def copy_link(self, link_id: int) -> Link: """Creates a copy of a link with a new id - It raises an error if ``link_id`` does not exist + It raises an error if link_id does not exist :Arguments: - **link_id** (:obj:`int`): ID of the link to copy + **link_id** (:obj:`int`): Id of the link to copy :Returns: - **link** (:obj:`Link`): Link object for requested ``link_id`` + **link** (:obj:`Link`): Link object for requested link_id """ data = self.__link_data(int(link_id)) @@ -108,10 +108,10 @@ def copy_link(self, link_id: int) -> Link: return link def delete(self, link_id: int) -> None: - """Removes the link with ``link_id`` from the project + """Removes the link with link_id from the project :Arguments: - **link_id** (:obj:`int`): ID of a link to delete + **link_id** (:obj:`int`): Id of a link to delete """ d = 1 link_id = int(link_id) @@ -140,7 +140,7 @@ def data(self) -> gpd.GeoDataFrame: """Returns all links data as a Pandas DataFrame :Returns: - **table** (:obj:`gpd.GeoDataFrame`): GeoPandas GeoDataFrame with all the nodes + **table** (:obj:`GeoDataFrame`): GeoPandas GeoDataFrame with all the nodes """ dl = DataLoader(self.project.path_to_file, "links") return dl.load_table() diff --git a/aequilibrae/project/network/mode.py b/aequilibrae/project/network/mode.py index 9610dcc94..6da2dee9e 100644 --- a/aequilibrae/project/network/mode.py +++ b/aequilibrae/project/network/mode.py @@ -5,7 +5,7 @@ class Mode: - """A mode object represents a single record in the 'modes' table""" + """A mode object represents a single record in the *modes* table""" __alowed_characters = string.ascii_letters + "_" diff --git a/aequilibrae/project/network/modes.py b/aequilibrae/project/network/modes.py index 0c5c2c497..7fd12f77d 100644 --- a/aequilibrae/project/network/modes.py +++ b/aequilibrae/project/network/modes.py @@ -8,7 +8,7 @@ class Modes: """ - Access to the API resources to manipulate the 'modes' table in the network + Access to the API resources to manipulate the modes table in the network .. code-block:: python diff --git a/aequilibrae/project/network/nodes.py b/aequilibrae/project/network/nodes.py index ea6d1dbc0..7e045b2fe 100644 --- a/aequilibrae/project/network/nodes.py +++ b/aequilibrae/project/network/nodes.py @@ -123,7 +123,7 @@ def data(self) -> gpd.GeoDataFrame: @property def lonlat(self) -> pd.DataFrame: - """Returns all nodes lon/lat coordinates as a Pandas DataFrame + """Returns all nodes lon/lat coords as a Pandas DataFrame :Returns: **table** (:obj:`DataFrame`): Pandas DataFrame with all the nodes, with geometry as lon/lat diff --git a/aequilibrae/transit/__init__.py b/aequilibrae/transit/__init__.py index 6325bd09a..2211cce04 100644 --- a/aequilibrae/transit/__init__.py +++ b/aequilibrae/transit/__init__.py @@ -1,3 +1,2 @@ from .transit import Transit from .transit_graph_builder import TransitGraphBuilder -from .lib_gtfs import GTFSRouteSystemBuilder diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index 2d44139ef..b819fe967 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -2,10 +2,9 @@ enum34 Sphinx pydata-sphinx-theme==0.13.3 sphinx_autodoc_annotation -pillow matplotlib folium -keplergl +mapclassify sphinx-gallery sphinx-copybutton sphinx-design diff --git a/docs/source/examples/assignment_workflows/just_matrices.omx b/docs/source/examples/assignment_workflows/just_matrices.omx deleted file mode 100644 index 0b15743732461a96dbec8ca51f40fec5593466dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15520 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 HXWV}Qava&I diff --git a/docs/source/examples/assignment_workflows/select_link_analysis.omx b/docs/source/examples/assignment_workflows/select_link_analysis.omx deleted file mode 100644 index 0b15743732461a96dbec8ca51f40fec5593466dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15520 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 HXWV}Qava&I diff --git a/docs/source/modeling_with_aequilibrae/aequilibrae_project/transit_database/index.rst b/docs/source/modeling_with_aequilibrae/aequilibrae_project/transit_database/index.rst index 2b6bfdc58..1b73cfaa4 100644 --- a/docs/source/modeling_with_aequilibrae/aequilibrae_project/transit_database/index.rst +++ b/docs/source/modeling_with_aequilibrae/aequilibrae_project/transit_database/index.rst @@ -13,10 +13,6 @@ a look at the documentation provided by `Google - - - - - - - - - .. list-table:: - :header-rows: 1 - :class: table table-striped sg-datatable - - * - Example - - Time - - Mem (MB) - * - :ref:`sphx_glr__auto_examples_skimming_plot_path_computation.py` (``examples/skimming/plot_path_computation.py``) - - 01:25.973 - - 0.0 - * - :ref:`sphx_glr__auto_examples_creating_models_plot_create_from_osm.py` (``examples/creating_models/plot_create_from_osm.py``) - - 00:19.374 - - 0.0 - * - :ref:`sphx_glr__auto_examples_assignment_workflows_plot_public_transit_assignment.py` (``examples/assignment_workflows/plot_public_transit_assignment.py``) - - 00:12.310 - - 0.0 - * - :ref:`sphx_glr__auto_examples_creating_models_plot_import_gtfs.py` (``examples/creating_models/plot_import_gtfs.py``) - - 00:08.777 - - 0.0 - * - :ref:`sphx_glr__auto_examples_creating_models_plot_create_zoning.py` (``examples/creating_models/plot_create_zoning.py``) - - 00:06.493 - - 0.0 - * - :ref:`sphx_glr__auto_examples_assignment_workflows_plot_route_choice_set.py` (``examples/assignment_workflows/plot_route_choice_set.py``) - - 00:04.572 - - 0.0 - * - :ref:`sphx_glr__auto_examples_creating_models_plot_create_from_layer.py` (``examples/creating_models/plot_create_from_layer.py``) - - 00:04.520 - - 0.0 - * - :ref:`sphx_glr__auto_examples_trip_distribution_plot_trip_distribution.py` (``examples/trip_distribution/plot_trip_distribution.py``) - - 00:02.511 - - 0.0 - * - :ref:`sphx_glr__auto_examples_skimming_plot_skimming.py` (``examples/skimming/plot_skimming.py``) - - 00:00.860 - - 0.0 - * - :ref:`sphx_glr__auto_examples_aequilibrae_without_a_model_plot_assignment_without_model.py` (``examples/aequilibrae_without_a_model/plot_assignment_without_model.py``) - - 00:00.000 - - 0.0 - * - :ref:`sphx_glr__auto_examples_assignment_workflows_plot_forecasting.py` (``examples/assignment_workflows/plot_forecasting.py``) - - 00:00.000 - - 0.0 - * - :ref:`sphx_glr__auto_examples_assignment_workflows_plot_route_choice_basics.py` (``examples/assignment_workflows/plot_route_choice_basics.py``) - - 00:00.000 - - 0.0 - * - :ref:`sphx_glr__auto_examples_assignment_workflows_plot_subarea_analysis.py` (``examples/assignment_workflows/plot_subarea_analysis.py``) - - 00:00.000 - - 0.0 - * - :ref:`sphx_glr__auto_examples_creating_models_plot_create_from_gmns.py` (``examples/creating_models/plot_create_from_gmns.py``) - - 00:00.000 - - 0.0 - * - :ref:`sphx_glr__auto_examples_editing_networks_plot_moving_link_extremity.py` (``examples/editing_networks/plot_moving_link_extremity.py``) - - 00:00.000 - - 0.0 - * - :ref:`sphx_glr__auto_examples_editing_networks_plot_moving_nodes.py` (``examples/editing_networks/plot_moving_nodes.py``) - - 00:00.000 - - 0.0 - * - :ref:`sphx_glr__auto_examples_editing_networks_plot_splitting_link.py` (``examples/editing_networks/plot_splitting_link.py``) - - 00:00.000 - - 0.0 - * - :ref:`sphx_glr__auto_examples_other_applications_plot_check_logging.py` (``examples/other_applications/plot_check_logging.py``) - - 00:00.000 - - 0.0 - * - :ref:`sphx_glr__auto_examples_other_applications_plot_export_to_gmns.py` (``examples/other_applications/plot_export_to_gmns.py``) - - 00:00.000 - - 0.0 - * - :ref:`sphx_glr__auto_examples_other_applications_plot_find_disconnected.py` (``examples/other_applications/plot_find_disconnected.py``) - - 00:00.000 - - 0.0 - * - :ref:`sphx_glr__auto_examples_other_applications_plot_logging_to_terminal.py` (``examples/other_applications/plot_logging_to_terminal.py``) - - 00:00.000 - - 0.0 - * - :ref:`sphx_glr__auto_examples_trip_distribution_plot_ipf_without_model.py` (``examples/trip_distribution/plot_ipf_without_model.py``) - - 00:00.000 - - 0.0 - * - :ref:`sphx_glr__auto_examples_visualization_plot_delaunay_lines.py` (``examples/visualization/plot_delaunay_lines.py``) - - 00:00.000 - - 0.0 - * - :ref:`sphx_glr__auto_examples_visualization_plot_display.py` (``examples/visualization/plot_display.py``) - - 00:00.000 - - 0.0 From d382665e6c9d2563b597767a4760ab749cd5ea56 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Thu, 12 Sep 2024 12:04:15 -0300 Subject: [PATCH 40/57] update docs --- .../plot_ipf_without_model.py | 0 .../assignment_workflows/plot_forecasting.py | 265 ++++++++++-------- .../plot_trip_distribution.py | 188 ------------- .../examples/trip_distribution/readme.rst | 2 - 4 files changed, 149 insertions(+), 306 deletions(-) rename docs/source/examples/{trip_distribution => aequilibrae_without_a_model}/plot_ipf_without_model.py (100%) delete mode 100644 docs/source/examples/trip_distribution/plot_trip_distribution.py delete mode 100644 docs/source/examples/trip_distribution/readme.rst diff --git a/docs/source/examples/trip_distribution/plot_ipf_without_model.py b/docs/source/examples/aequilibrae_without_a_model/plot_ipf_without_model.py similarity index 100% rename from docs/source/examples/trip_distribution/plot_ipf_without_model.py rename to docs/source/examples/aequilibrae_without_a_model/plot_ipf_without_model.py diff --git a/docs/source/examples/assignment_workflows/plot_forecasting.py b/docs/source/examples/assignment_workflows/plot_forecasting.py index c1655963c..3c004b3a8 100644 --- a/docs/source/examples/assignment_workflows/plot_forecasting.py +++ b/docs/source/examples/assignment_workflows/plot_forecasting.py @@ -4,19 +4,21 @@ Forecasting =========== -In this example, we present a full forecasting workflow for the Sioux Falls -example model. +In this example, we present a full forecasting workflow for the Sioux Falls example model. + +We start creating the skim matrices, running the assignment for the base-year, and then +distributing these trips into the network. Later, we estimate a set of future demand vectors +which are going to be the input of a future year assignnment with select link analysis. """ # %% # Imports from uuid import uuid4 -from tempfile import gettempdir from os.path import join -from aequilibrae.utils.create_example import create_example -import logging -import sys +from tempfile import gettempdir +from aequilibrae.utils.create_example import create_example +# sphinx_gallery_thumbnail_number = 3 # %% # We create the example project inside our temp folder @@ -28,8 +30,9 @@ # %% # Traffic assignment with skimming # -------------------------------- +# In this step, we'll set the skims for the variable ``free_flow_time``, and execute the +# traffic assignment for the base-year. -# %% from aequilibrae.paths import TrafficAssignment, TrafficClass # %% @@ -37,9 +40,7 @@ # 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 - -# %% +# This is true, but we won't use those fields. # We grab the graph for cars graph = project.network.graphs["c"] @@ -55,55 +56,58 @@ graph.set_blocked_centroid_flows(False) # %% -# We get the demand matrix directly from the project record. -# So let's inspect what we have in the project +# Let's get the demand matrix directly from the project record, and inspect what matrices we have in the project. proj_matrices = project.matrices -print(proj_matrices.list()) +proj_matrices.list() # %% -# Let's get it in this better way +# We get the demand matrix, and prepare it for computation demand = proj_matrices.get_matrix("demand_omx") demand.computational_view(["matrix"]) # %% -assig = TrafficAssignment() +# Let's perform the traffic assignment # Create the assignment class assigclass = TrafficClass(name="car", graph=graph, matrix=demand) -# The first thing to do is to add at list of traffic classes to be assigned +assig = TrafficAssignment() + +# We start by adding the list of traffic classes to be assigned assig.add_class(assigclass) -# We set these parameters only after adding one class to the assignment +# Then we set these parameters, which an only be configured after adding one class to the assignment assig.set_vdf("BPR") # This is not case-sensitive -# Then we set the volume delay function -assig.set_vdf_parameters({"alpha": "b", "beta": "power"}) # And its parameters +# Then we set the volume delay function and its parameters +assig.set_vdf_parameters({"alpha": "b", "beta": "power"}) -assig.set_capacity_field("capacity") # The capacity and free flow travel times as they exist in the graph +# The capacity and free flow travel times as they exist in the graph +assig.set_capacity_field("capacity") assig.set_time_field("free_flow_time") # And the algorithm we want to use to assign assig.set_algorithm("bfw") -# Since I haven't checked the parameters file, let's make sure convergence criteria is good +# Since we haven't checked the parameters file, let's make sure convergence criteria is good assig.max_iter = 1000 assig.rgap_target = 0.001 -assig.execute() # we then execute the assignment +# we then execute the assignment +assig.execute() # %% - -# Convergence report is easy to see +# After finishing the assignment, we can easily see the convergence report. convergence_report = assig.report() -print(convergence_report.head()) +convergence_report.head() # %% -volumes = assig.results() -print(volumes.head()) +# And we can also see the results of the assignment +results = assig.results() +results.head() # %% -# We could export it to CSV or AequilibraE data, but let's put it directly into the results database +# We can export our results to CSV or AequilibraE Data, but let's put it directly into the results database assig.save_results("base_year_assignment") # %% @@ -113,34 +117,51 @@ # %% # Trip distribution # ----------------- +# First, let's have a function to plot the Trip Length Frequency Distribution. +from math import log10, floor +import matplotlib.pyplot as plt + +# %% +def plot_tlfd(demand, skim, name): + plt.clf() + b = floor(log10(skim.shape[0]) * 10) + n, bins, patches = plt.hist( + np.nan_to_num(skim.flatten(), 0), + bins=b, + weights=np.nan_to_num(demand.flatten()), + density=False, + facecolor="g", + alpha=0.75, + ) + + plt.xlabel("Trip length") + plt.ylabel("Probability") + plt.title(f"Trip-length frequency distribution for {name}") + return plt + +# %% # Calibration # ~~~~~~~~~~~ -# We will calibrate synthetic gravity models using the skims for TIME that we just generated +# We will calibrate synthetic gravity models using the skims for ``free_flow_time`` that we just generated -# %% import numpy as np from aequilibrae.distribution import GravityCalibration # %% -# Let's take another look at what we have in terms of matrices in the model -print(proj_matrices.list()) - -# %% -# We need the demand +# We need the demand matrix and to prepare it for computation demand = proj_matrices.get_matrix("demand_aem") +demand.computational_view(["matrix"]) # %% -# And the skims +# We also need the skims we just saved into our project imped = proj_matrices.get_matrix("base_year_assignment_skims_car") -# %% # We can check which matrix cores were created for our skims to decide which one to use imped.names # %% # Where ``free_flow_time_final`` is actually the congested time for the last iteration - -# %% +# # But before using the data, let's get some impedance for the intrazonals. # Let's assume it is 75% of the closest zone. imped_core = "free_flow_time_final" @@ -158,20 +179,16 @@ np.fill_diagonal(imped.matrix_view, intrazonals) # %% -# Since we are working with an OMX file, we cannot overwrite a matrix on disk -# So we give a new name to save it +# Since we are working with an OMX file, we cannot overwrite a matrix on disk. +# So let's give it a new name to save. imped.save(names=["final_time_with_intrazonals"]) # %% # This also updates these new matrices as those being used for computation -# as one can verify below imped.view_names # %% -# We set the matrices for being used in computation -demand.computational_view(["matrix"]) - -# %% +# Let's calibrate our Gravity Model for function in ["power", "expo"]: gc = GravityCalibration(matrix=demand, impedance=imped, function=function, nan_as_zero=True) gc.calibrate() @@ -179,12 +196,19 @@ # We save the model model.save(join(fldr, f"{function}_model.mod")) + _ = plot_tlfd(gc.result_matrix.matrix_view, imped.matrix_view, f"{function} model") + # We can save the result of applying the model as well # We can also save the calibration report with open(join(fldr, f"{function}_convergence.log"), "w") as otp: for r in gc.report: otp.write(r + "\n") +# %% +# And let's plot a trip length frequency distribution for the demand itself +plt = plot_tlfd(demand.matrix_view, imped.matrix_view, "demand") +plt.show() + # %% # Forecast # -------- @@ -197,7 +221,10 @@ from aequilibrae.matrix import AequilibraeData # %% -# We compute the vectors from our matrix +# Compute future vectors +# ~~~~~~~~~~~~~~~~~~~~~~ +# +# First thing to do is to compute the future vectors from our matrix. origins = np.sum(demand.matrix_view, axis=1) destinations = np.sum(demand.matrix_view, axis=0) @@ -219,11 +246,34 @@ vectors.destinations[:] = destinations * (1 + np.random.rand(vectors.entries) / 10) vectors.destinations *= vectors.origins.sum() / vectors.destinations.sum() +# %% +# IPF for the future vectors +# ~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Let's balance the future vectors. The output of this step is going to be used later +# in the traffic assignment for future year. + +args = { + "matrix": demand, + "rows": vectors, + "columns": vectors, + "column_field": "destinations", + "row_field": "origins", + "nan_as_zero": True, +} + +ipf = Ipf(**args) +ipf.fit() + +# %% +# When saving our vector into the project, we'll get an output that it was recored +ipf.save_to_project(name="demand_ipfd", file_name="demand_ipfd.aem") +ipf.save_to_project(name="demand_ipfd_omx", file_name="demand_ipfd.omx") + # %% # Impedance # ~~~~~~~~~ - -# %% +# +# Let's get the base-year assignment skim for car we created before and prepare it for computation imped = proj_matrices.get_matrix("base_year_assignment_skims_car") imped.computational_view(["final_time_with_intrazonals"]) @@ -233,6 +283,7 @@ # np.fill_diagonal(imped.matrix_view, np.nan) # %% +# Now we apply the Synthetic Gravity model for function in ["power", "expo"]: model = SyntheticGravityModel() model.load(join(fldr, f"{function}_model.mod")) @@ -257,110 +308,95 @@ # %% # We update the matrices table/records and verify that the new matrices are indeed there proj_matrices.update_database() -print(proj_matrices.list()) - -# %% -# IPF for the future vectors -# ~~~~~~~~~~~~~~~~~~~~~~~~~~ - -# %% -args = { - "matrix": demand, - "rows": vectors, - "columns": vectors, - "column_field": "destinations", - "row_field": "origins", - "nan_as_zero": True, -} - -ipf = Ipf(**args) -ipf.fit() - -ipf.save_to_project(name="demand_ipfd", file_name="demand_ipfd.aem") -ipf.save_to_project(name="demand_ipfd_omx", file_name="demand_ipfd.omx") - -# %% -df = proj_matrices.list() +proj_matrices.list() # %% -# Future traffic assignment -# ------------------------- - -# %% -logger.info("\n\n\n TRAFFIC ASSIGNMENT FOR FUTURE YEAR") +# Traffic assignment with Select Link Analysis +# -------------------------------------------- +# We'll perform traffic assignment for the future year. +logger.info("\n\n\n TRAFFIC ASSIGNMENT FOR FUTURE YEAR WITH SELECT LINK ANALYSIS") # %% +# Let's get our future demand matrix, which corresponds to the IPF result we just saved, +# and see what is the core we ended up getting. It should be ``matrix``. demand = proj_matrices.get_matrix("demand_ipfd") - -# Let's see what is the core we ended up getting. It should be 'gravity' demand.names # %% -# Let's use the IPF matrix +# Let's prepare our data for computation demand.computational_view("matrix") # %% -assig = TrafficAssignment() +# The future year assignment is quite similar to the one we did for the base-year. -# Creates the assignment class +# So, let's create the assignment class assigclass = TrafficClass(name="car", graph=graph, matrix=demand) -# The first thing to do is to add at a list of traffic classes to be assigned +assig = TrafficAssignment() + +# Add at a list of traffic classes to be assigned assig.add_class(assigclass) -assig.set_vdf("BPR") # This is not case-sensitive +assig.set_vdf("BPR") -# Then we set the volume delay function -assig.set_vdf_parameters({"alpha": "b", "beta": "power"}) # And its parameters +# Set the volume delay function and its parameters +assig.set_vdf_parameters({"alpha": "b", "beta": "power"}) -assig.set_capacity_field("capacity") # The capacity and free flow travel times as they exist in the graph +# Set the capacity and free flow travel times as they exist in the graph +assig.set_capacity_field("capacity") assig.set_time_field("free_flow_time") # And the algorithm we want to use to assign assig.set_algorithm("bfw") -# Since I haven't checked the parameters file, let's make sure convergence criteria is good +# Once again we haven't checked the parameters file, so let's make sure convergence criteria is good assig.max_iter = 500 assig.rgap_target = 0.00001 # %% -# Optional: Select link analysis -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# If we want to execute select link analysis on a particular TrafficClass, we set the links we are analyzing. -# The format of the input select links is a ``dictionary (str: list[tuple])``. -# Each entry represents a separate set of selected links to compute. The str name will name the set of links. -# The list[tuple] is the list of links being selected, of the form (link_id, direction), as it occurs in the Graph. -# Direction can be 0, 1, -1. 0 denotes bi-directionality. -# For example, let's use Select Link on two sets of links: - -# %% +# Now we select two sets of links to execute select link analysis. select_links = { "Leaving node 1": [(1, 1), (2, 1)], "Random nodes": [(3, 1), (5, 1)], } # %% +# .. note:: +# +# As we are executing the select link analysis on a particular ``TrafficClass``, we should set the +# links we want to analyze. The input is a dictionary with string as keys and a list of tuples as +# values, so that each entry represents a separate set of selected links to compute. +# +# ``select_link_dict = {"set_name": [(link_id1, direction1), ..., (link_id, direction)]}`` +# +# The string name will name the set of links, and the list of tuples is the list of selected links +# in the form ``(link_id, direction)``, as it occurs in the :ref:`Graph `. +# Direction can be one of ``0``, ``1``, ``-1``, where ``0`` denotes bi-directionality. + +# %% + # We call this command on the class we are analyzing with our dictionary of values assigclass.set_select_links(select_links) -assig.execute() # we then execute the assignment +# we then execute the assignment +assig.execute() # %% -# Now let us save our select link results, all we need to do is provide it with a name. +# To save our select link results, all we need to do is provide it with a name. # In addition to exporting the select link flows, it also exports the Select Link matrices in OMX format. assig.save_select_link_results("select_link_analysis") # %% -# Say we just want to save our select link flows, we can call: -assig.save_select_link_flows("just_flows") - -# Or if we just want the SL matrices: -assig.save_select_link_matrices("just_matrices") -# Internally, the save_select_link_results calls both of these methods at once. +# .. note:: +# +# Say we just want to save our select link flows, we can call: ``assig.save_select_link_flows("just_flows")`` +# +# Or if we just want the select link matrices: ``assig.save_select_link_matrices("just_matrices")`` +# +# Internally, the ``save_select_link_results`` calls both of these methods at once. # %% -# We could export it to CSV or AequilibraE Data, but let's put it directly into the results database +# We can export the results to CSV or AequilibraE Data, but let's put it directly into the results database assig.save_results("future_year_assignment") # %% @@ -368,12 +404,9 @@ assig.save_skims("future_year_assignment_skims", which_ones="all", format="omx") # %% -# We can also plot convergence -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Run convergence study +# ~~~~~~~~~~~~~~~~~~~~~ -import matplotlib.pyplot as plt - -# %% df = assig.report() x = df.iteration.values y = df.rgap.values @@ -384,8 +417,8 @@ plt.plot(x, y, "k--") plt.yscale("log") plt.grid(True, which="both") -plt.xlabel(r"Iterations") -plt.ylabel(r"Relative Gap") +plt.xlabel("Iterations") +plt.ylabel("Relative Gap") plt.show() # %% diff --git a/docs/source/examples/trip_distribution/plot_trip_distribution.py b/docs/source/examples/trip_distribution/plot_trip_distribution.py deleted file mode 100644 index 277253df0..000000000 --- a/docs/source/examples/trip_distribution/plot_trip_distribution.py +++ /dev/null @@ -1,188 +0,0 @@ -""" -.. _example_usage_distribution: - -Trip Distribution -================= - -In this example, we calibrate a Synthetic Gravity Model that same model plus IPF (Fratar/Furness). -""" - -# %% - -# Imports -from uuid import uuid4 -from tempfile import gettempdir -from os.path import join -from aequilibrae.utils.create_example import create_example -import pandas as pd -import numpy as np - -# %% - -# We create the example project inside our temp folder -fldr = join(gettempdir(), uuid4().hex) - -project = create_example(fldr) - -# %% -# We get the demand matrix directly from the project record, -# so let's inspect what we have in the project -proj_matrices = project.matrices -print(proj_matrices.list()) - -# %% -# We get the demand matrix -demand = proj_matrices.get_matrix("demand_omx") -demand.computational_view(["matrix"]) - -# %% -# And the impedance -impedance = proj_matrices.get_matrix("skims") -impedance.computational_view(["time_final"]) - -# %% -# Let's have a function to plot the Trip Length Frequency Distribution -from math import log10, floor -import matplotlib.pyplot as plt - -# %% -def plot_tlfd(demand, skim, name): - plt.clf() - b = floor(log10(skim.shape[0]) * 10) - n, bins, patches = plt.hist( - np.nan_to_num(skim.flatten(), 0), - bins=b, - weights=np.nan_to_num(demand.flatten()), - density=False, - facecolor="g", - alpha=0.75, - ) - - plt.xlabel("Trip length") - plt.ylabel("Probability") - plt.title("Trip-length frequency distribution") - plt.savefig(name, format="png") - return plt - - -# %% -from aequilibrae.distribution import GravityCalibration - -# %% -for function in ["power", "expo"]: - gc = GravityCalibration(matrix=demand, impedance=impedance, function=function, nan_as_zero=True) - gc.calibrate() - model = gc.model - # We save the model - model.save(join(fldr, f"{function}_model.mod")) - - # We can save an image for the resulting model - _ = plot_tlfd(gc.result_matrix.matrix_view, impedance.matrix_view, join(fldr, f"{function}_tfld.png")) - - # We can save the result of applying the model as well - # We can also save the calibration report - with open(join(fldr, f"{function}_convergence.log"), "w") as otp: - for r in gc.report: - otp.write(r + "\n") - -# %% -# We save a trip length frequency distribution for the demand itself -plt = plot_tlfd(demand.matrix_view, impedance.matrix_view, join(fldr, "demand_tfld.png")) -plt.show() - -# %% -# Forecast -# -------- -# We create a set of 'future' vectors by applying some models -# and apply the model for both deterrence functions - -# %% -from aequilibrae.distribution import Ipf, GravityApplication, SyntheticGravityModel -from aequilibrae.matrix import AequilibraeData -import numpy as np - -# %% -zonal_data = pd.read_sql("Select zone_id, population, employment from zones order by zone_id", project.conn) - -# %% -# We compute the vectors from our matrix -args = { - "file_path": join(fldr, "synthetic_future_vector.aed"), - "entries": demand.zones, - "field_names": ["origins", "destinations"], - "data_types": [np.float64, np.float64], - "memory_mode": True, -} - -vectors = AequilibraeData() -vectors.create_empty(**args) - -vectors.index[:] = zonal_data.zone_id[:] - -# We apply a trivial regression-based model and balance the vectors -vectors.origins[:] = zonal_data.population[:] * 2.32 -vectors.destinations[:] = zonal_data.employment[:] * 1.87 -vectors.destinations *= vectors.origins.sum() / vectors.destinations.sum() - -# %% -# We simply apply the models to the same impedance matrix now -for function in ["power", "expo"]: - model = SyntheticGravityModel() - model.load(join(fldr, f"{function}_model.mod")) - - outmatrix = join(proj_matrices.fldr, f"demand_{function}_model.aem") - args = { - "impedance": impedance, - "rows": vectors, - "row_field": "origins", - "model": model, - "columns": vectors, - "column_field": "destinations", - "nan_as_zero": True, - } - - gravity = GravityApplication(**args) - gravity.apply() - - # We get the output matrix and save it to OMX too, - gravity.save_to_project(name=f"demand_{function}_model_omx", file_name=f"demand_{function}_model.omx") - -# %% -# We update the matrices table/records and verify that the new matrices are indeed there -proj_matrices.update_database() -print(proj_matrices.list()) - -# %% -# We now run IPF for the future vectors - -# %% -args = { - "matrix": demand, - "rows": vectors, - "columns": vectors, - "column_field": "destinations", - "row_field": "origins", - "nan_as_zero": True, -} - -ipf = Ipf(**args) -ipf.fit() - -ipf.save_to_project(name="demand_ipf", file_name="demand_ipf.aem") -ipf.save_to_project(name="demand_ipf_omx", file_name="demand_ipf.omx") - -# %% -print(proj_matrices.list()) - -# %% -project.close() - -# %% -# .. seealso:: -# The use of the following functions, methods, classes and modules is shown in this example: -# -# * :func:`aequilibrae.distribution.Ipf` -# * :func:`aequilibrae.distribution.GravityCalibration` -# * :func:`aequilibrae.distribution.GravityApplication` -# * :func:`aequilibrae.distribution.SyntheticGravityModel` -# * :func:`aequilibrae.matrix.AequilibraeData` diff --git a/docs/source/examples/trip_distribution/readme.rst b/docs/source/examples/trip_distribution/readme.rst deleted file mode 100644 index 2f88cb888..000000000 --- a/docs/source/examples/trip_distribution/readme.rst +++ /dev/null @@ -1,2 +0,0 @@ -Trip Distribution ------------------ From c7e9fbc98afe5322ce91725d3086f27e636fcb56 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Thu, 12 Sep 2024 15:16:33 -0300 Subject: [PATCH 41/57] update docs --- aequilibrae/paths/route_choice.py | 97 ++++++++++--------- aequilibrae/paths/sub_area.py | 4 +- .../plot_assignment_without_model.py | 18 ++-- .../plot_ipf_without_model.py | 2 +- .../assignment_workflows/plot_forecasting.py | 1 + .../plot_route_choice_basics.py | 53 +++------- .../plot_route_choice_set.py | 32 ++---- .../plot_subarea_analysis.py | 7 +- .../creating_models/plot_create_zoning.py | 33 +++---- .../route_choice/choice_set_generation.rst | 10 +- 10 files changed, 112 insertions(+), 145 deletions(-) diff --git a/aequilibrae/paths/route_choice.py b/aequilibrae/paths/route_choice.py index 3a1f8899c..39969809b 100644 --- a/aequilibrae/paths/route_choice.py +++ b/aequilibrae/paths/route_choice.py @@ -81,46 +81,49 @@ def set_choice_set_generation(self, /, algorithm: str, **kwargs) -> None: Options for algorithm are, 'bfsle' for breadth first search with link removal, or 'link-penalisation'/'link-penalization'. - BFSLE implementation based on "Route choice sets for very high-resolution data" by Nadine Rieser-Schüssler, - Michael Balmer & Kay W. Axhausen (2013). - https://doi.org/10.1080/18128602.2012.671383 + BFSLE implementation based on "Route choice sets for very high-resolution data" by + Nadine Rieser-Schüssler, Michael Balmer & Kay W. Axhausen (2013). + `DOI: 10.1080/18128602.2012.671383 `_. 'lp' is also accepted as an alternative to 'link-penalisation' Setting the parameters for the route choice: - `seed` is a BFSLE specific parameters. + ``seed`` is a BFSLE specific parameters. - Setting `max_depth` or `max_misses`, while not required, is strongly recommended to prevent runaway algorithms. - `max_misses` is the maximum amount of duplicate routes found per OD pair. If it is exceeded then the route set - if returned with fewer than `max_routes`. It has a default value of `100`. + Setting ``max_depth`` or ``max_misses``, while not required, is strongly recommended to prevent runaway + algorithms. - - When using BFSLE `max_depth` corresponds to the maximum height of the graph of graphs. It's value is - largely dependent on the size of the paths within the network. For very small networks a value of 10 - is a recommended starting point. For large networks a good starting value is 5. Increase the value + ``max_misses`` is the maximum amount of duplicate routes found per OD pair. If it is exceeded then the route set + if returned with fewer than ``max_routes``. It has a default value of ``100``. + + - When using **BFSLE** ``max_depth`` corresponds to the maximum height of the graph of graphs. It's value is + largely dependent on the size of the paths within the network. For very small networks a value of ``10`` + is a recommended starting point. For large networks a good starting value is ``5``. Increase the value until the number of desired routes is being consistently returned. If it is exceeded then the route set - if returned with fewer than `max_routes`. + if returned with fewer than ``max_routes``. - - When using LP, `max_depth` corresponds to the maximum number of iterations performed. While not enforced, - it should be higher than `max_routes`. It's value is dependent on the magnitude of the cost field, - specifically it's related to the log base `penalty` of the ratio of costs between two alternative routes. - If it is exceeded then the route set if returned with fewer than `max_routes`. + - When using **LP**, ``max_depth`` corresponds to the maximum number of iterations performed. While not + enforced, + it should be higher than ``max_routes``. It's value is dependent on the magnitude of the cost field, + specifically it's related to the log base ``penalty`` of the ratio of costs between two alternative routes. + If it is exceeded then the route set if returned with fewer than ``max_routes``. Additionally BFSLE has the option to incorporate link penalisation. Every link in all routes found at a depth - are penalised with the `penalty` factor for the next depth. So at a depth of 0 no links are penalised nor - removed. At depth 1, all links found at depth 0 are penalised, then the links marked for removal are removed. - All links in the routes found at depth 1 are then penalised for the next depth. The penalisation compounds. - Pass set `penalty=1.0` to disable. + are penalised with the ``penalty`` factor for the next depth. So at a depth of ``0`` no links are penalised nor + removed. At depth ``1``, all links found at depth 0 are penalised, then the links marked for removal are removed. + All links in the routes found at depth ``1`` are then penalised for the next depth. The penalisation compounds. + Pass set ``penalty=1.0`` to disable. - When performing an assignment, `cutoff_prob` can be provided to exclude routes from the path-sized logit model. - The `cutoff_prob` is used to compute an inverse binary logit and obtain a max difference in utilities. If a + When performing an assignment, ``cutoff_prob`` can be provided to exclude routes from the path-sized logit model. + The ``cutoff_prob`` is used to compute an inverse binary logit and obtain a max difference in utilities. If a paths total cost is greater than the minimum cost path in the route set plus the max difference, the route is excluded from the PSL calculations. The route is still returned, but with a probability of 0.0. - The `cutoff_prob` should be in the range [0, 1]. It is then rescaled internally to [0.5, 1] as probabilities - below 0.5 produce negative differences in utilities because the choice is between two routes only, one of - which is the shortest path. A higher `cutoff_prob` includes less routes. A value of `1.0` will only include - the minimum cost route. A value of `0.0` includes all routes. + The ``cutoff_prob`` should be in the range :math:`[0, 1]`. It is then rescaled internally to :math:`[0.5, 1]` as probabilities + below ``0.5`` produce negative differences in utilities because the choice is between two routes only, one of + which is the shortest path. A higher ``cutoff_prob`` includes less routes. A value of ``1.0`` will only include + the minimum cost route. A value of ``0.0`` includes all routes. :Arguments: **algorithm** (:obj:`str`): Algorithm to be used @@ -190,10 +193,10 @@ def add_demand(self, demand, fill: float = 0.0): :Arguments: **demand** (:obj:`Union[pd.DataFrame, AequilibraeMatrix]`): Demand to add to assignment. If the supplied demand is a DataFrame, it should have a 2-level MultiIndex of Origin and Destination node IDs. If an - AequilibraE matrix is supplied node IDs will be inferred from the index. Demand values should be either - float32s or float64s. + AequilibraE Matrix is supplied node IDs will be inferred from the index. Demand values should be either + ``float32``s or ``float64``s. - **fill** (:obj:`float`): Value to fill any NaNs with. + **fill** (:obj:`float`): Value to fill any ``NaN``s with. """ if isinstance(demand, pd.DataFrame): self.demand.add_df(demand, fill=fill) @@ -211,7 +214,7 @@ def prepare(self, nodes: Union[List[int], List[Tuple[int, int]], None] = None) - provided, OD pairs are taken to be all pair permutations of the list. If a list of pairs is provided OD pairs are taken as is. All node IDs must be present in the compressed graph. To make a node ID always appear in the compressed graph add it as a centroid. Duplicates will be dropped on execution. - If *None* is provided, all OD pairs with non-zero flows will be used. + If ``None`` is provided, all OD pairs with non-zero flows will be used. """ if nodes is not None and not self.demand.no_demand(): raise ValueError("provide either `nodes` or set a `demand` matrix, not both") @@ -276,10 +279,10 @@ def execute(self, perform_assignment: bool = True) -> None: """ Generate route choice sets between the previously supplied nodes, potentially performing an assignment. - To access results see `RouteChoice.get_results()`. + To access results see ``RouteChoice.get_results()``. :Arguments: - **perform_assignment** (:obj:`bool`): Whether or not to perform an assignment. Default `False`. + **perform_assignment** (:obj:`bool`): Whether or not to perform an assignment. Defaults to ``False``. """ if self.demand.df.index.empty: logging.warning("There is no demand or pairs of OD pairs to compute Route choice for.") @@ -301,11 +304,11 @@ def execute(self, perform_assignment: bool = True) -> None: def info(self) -> dict: """Returns information for the transit assignment procedure - Dictionary contains keys 'Algorithm', 'Matrix totals', 'Computer name', 'Procedure ID', 'Parameters', and + Dictionary contains keys 'Algorithm', 'Matrix totals', 'Computer name', 'Procedure ID', 'Parameters', and 'Select links'. The classes key is also a dictionary with all the user classes per transit class and their respective - matrix totals + matrix totals. :Returns: **info** (:obj:`dict`): Dictionary with summary information @@ -333,7 +336,7 @@ def get_results(self) -> Union[pa.Table, pa.dataset.Dataset]: Returns a table of OD pairs to lists of link IDs for each OD pair provided (as columns). Represents paths from ``origin`` to ``destination``. - If `save_routes` was specified then a Pyarrow dataset is returned. The caller is responsible for reading this + If ``save_routes`` was specified then a Pyarrow dataset is returned. The caller is responsible for reading this dataset. :Returns: @@ -351,8 +354,8 @@ def get_load_results(self) -> pd.DataFrame: Translates the link loading results from the graph format into the network format. :Returns: - **dataset** (:obj:`Union[Tuple[pd.DataFrame, pd.DataFrame], pd.DataFrame]`): - A tuple of link loading results as DataFrames. Columns are the matrix name concatenated direction. + **dataset** (:obj:`Union[Tuple[pd.DataFrame, pd.DataFrame], pd.DataFrame]`): A tuple of link loading + results as DataFrames. Columns are the matrix name concatenated direction. """ if self.demand.no_demand(): @@ -390,19 +393,25 @@ def set_select_links( self, links: Dict[Hashable, List[Union[Tuple[int, int], List[Tuple[int, int]]]]], link_loading=True ): """ - Set the selected links. Checks if the links and directions are valid. Supports OR and AND sets of links. + Set the selected links. Checks if the links and directions are valid. Supports **OR** and **AND** sets of links. + + Dictionary values should be a list of either a single ``(link_id, direction)`` tuple or a list of + ``(link_id, dirirection)``. - Dictionary values should be a list of either (link_id, dir) or a list of (link_id, dir). + The elements of the first list represent the **AND** sets, together they are OR'ed. If any of these sets is + satisfied the link are loaded as appropriate. + + The **AND** sets are comprised of either a single ``(link_id, direction)`` tuple or a list of + ``(link_id, direction)``. The single tuple represents an **AND** set with a single element. + + All links and directions in an **AND** set must appear in any order within a route for it to be considered + satisfied. - The elements of the first list represent the AND sets, together they are OR'ed. If any of these sets is - satisfied the link are loaded as appropriate. The AND sets are comprised of either a single (link_id, dir) tuple - or a list of (link_id, dir). The single tuple represents an AND set with a single element. All links and - directions in an AND set must appear in any order within a route for it to be considered satisfied. - Supply `links=None` to disable select link analysis. + Supply ``links=None`` to disable select link analysis. :Arguments: **links** (:obj:`Union[None, Dict[Hashable, List[Union[Tuple[int, int], List[Tuple[int, int]]]]]]`): - Name of link set and Link IDs and directions to be used in select link analysis. + Name of link set and link IDs and directions to be used in select link analysis. **link_loading** (:obj:`bool`): Enable select link loading. If disabled only OD matrix results are available. diff --git a/aequilibrae/paths/sub_area.py b/aequilibrae/paths/sub_area.py index 3956e7c10..84ac6efb4 100644 --- a/aequilibrae/paths/sub_area.py +++ b/aequilibrae/paths/sub_area.py @@ -28,10 +28,10 @@ def __init__( :Arguments: **graph** (:obj:`Graph`): AequilibraE graph object to use - + **subarea** (:obj:`gpd.GeoDataFrame`): A GeoPandas GeoDataFrame whose geometry union represents the sub-area. - + **demand** (:obj:`Union[pandas.DataFrame, AequilibraeMatrix]`): The demand matrix to provide to the route choice assignment. diff --git a/docs/source/examples/aequilibrae_without_a_model/plot_assignment_without_model.py b/docs/source/examples/aequilibrae_without_a_model/plot_assignment_without_model.py index 87590db65..5863578a0 100644 --- a/docs/source/examples/aequilibrae_without_a_model/plot_assignment_without_model.py +++ b/docs/source/examples/aequilibrae_without_a_model/plot_assignment_without_model.py @@ -14,6 +14,7 @@ import os import pandas as pd import numpy as np +from uuid import uuid4 from tempfile import gettempdir from aequilibrae.matrix import AequilibraeMatrix @@ -32,7 +33,7 @@ # %% # Let's use a temporary folder to store our data -folder = gettempdir() +folder = os.path.join(gettempdir(), uuid4().hex) # %% # First we load our demand file. This file has three columns: O, D, and Ton. @@ -55,8 +56,7 @@ aem = AequilibraeMatrix() kwargs = {'file_name': aemfile, 'zones': zones, - 'matrix_names': ['matrix'], - "memory_only": False} # We'll save it to disk so we can use it later + 'matrix_names': ['matrix']} aem.create_empty(**kwargs) aem.matrix['matrix'][:,:] = mtx[:,:] @@ -106,12 +106,12 @@ g.lonlat_index = geom.loc[g.all_nodes] # %% -# Let's perform our assignment. Feel free to try different algorithms, -# as well as change the maximum number of iterations and the gap. -aem = AequilibraeMatrix() -aem.load(aemfile) +# Let's prepare our matrix for computation aem.computational_view(["matrix"]) +# %% +# Let's perform our assignment. Feel free to try different algorithms, +# as well as change the maximum number of iterations and the gap assigclass = TrafficClass("car", g, aem) assig = TrafficAssignment() @@ -128,11 +128,11 @@ # %% # Now let's take a look at the Assignment results -print(assig.results()) +assig.results() # %% # And at the Assignment report -print(assig.report()) +assig.report() # %% # .. admonition:: References diff --git a/docs/source/examples/aequilibrae_without_a_model/plot_ipf_without_model.py b/docs/source/examples/aequilibrae_without_a_model/plot_ipf_without_model.py index d3738fef4..468bcd94e 100644 --- a/docs/source/examples/aequilibrae_without_a_model/plot_ipf_without_model.py +++ b/docs/source/examples/aequilibrae_without_a_model/plot_ipf_without_model.py @@ -5,7 +5,7 @@ ======================================== In this example, we show you how to use AequilibraE's IPF function without a model. -This is a compliment to the application in :ref:`Trip Distribution `. +This is a complement to the application in :ref:`example_usage_forecasting`. Let's consider that you have an OD-matrix, the future production and future attraction values. *How would your trip distribution matrix using IPF look like?* diff --git a/docs/source/examples/assignment_workflows/plot_forecasting.py b/docs/source/examples/assignment_workflows/plot_forecasting.py index 3c004b3a8..d2c079f3f 100644 --- a/docs/source/examples/assignment_workflows/plot_forecasting.py +++ b/docs/source/examples/assignment_workflows/plot_forecasting.py @@ -371,6 +371,7 @@ def plot_tlfd(demand, skim, name): # # The string name will name the set of links, and the list of tuples is the list of selected links # in the form ``(link_id, direction)``, as it occurs in the :ref:`Graph `. +# # Direction can be one of ``0``, ``1``, ``-1``, where ``0`` denotes bi-directionality. # %% diff --git a/docs/source/examples/assignment_workflows/plot_route_choice_basics.py b/docs/source/examples/assignment_workflows/plot_route_choice_basics.py index 49eeee784..295f6ea11 100644 --- a/docs/source/examples/assignment_workflows/plot_route_choice_basics.py +++ b/docs/source/examples/assignment_workflows/plot_route_choice_basics.py @@ -29,7 +29,9 @@ import logging import sys -# We the project opens, we can tell the logger to direct all messages to the terminal as well +# %% + +# When 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") @@ -40,7 +42,6 @@ # Model parameters # ---------------- -# %% import numpy as np # %% @@ -72,21 +73,20 @@ nodes_of_interest = (71645, 74089, 77011, 79385) # %% -# let's say that utility is just a function of distance -# So we build our *utility* field as the distance times theta +# Let's say that utility is just a function of distance, so we build our *utility* field as +# :math:`distance * theta` graph.network = graph.network.assign(utility=graph.network.distance * theta) # %% + # Prepare the graph with all nodes of interest as centroids graph.prepare_graph(np.array(nodes_of_interest)) -# %% # And set the cost of the graph the as the utility field just created graph.set_graph("utility") -# %% # We allow flows through "centroid connectors" because our centroids are not really centroids -# If we have actual centroid connectors in the network (and more than one per centroid) , then we +# If we have actual centroid connectors in the network (and more than one per centroid), then we # should remove them from the graph graph.set_blocked_centroid_flows(False) @@ -113,40 +113,12 @@ # %% # This object construct might take a minute depending on the size of the graph due to the construction of the compressed -# link to network link mapping that's required. This is a one time operation per graph and is cached. We need to supply -# a Graph and an AequilibraeMatrix or DataFrame via the ``add_demand`` method, if demand is not provided link loading -# cannot be preformed. +# link to network link mapping that's required. This is a one time operation per graph and is cached. rc = RouteChoice(graph) rc.add_demand(mat) # %% -# Here we'll set the parameters of our set generation. There are two algorithms available: Link penalisation, and BFSLE -# based on the paper -# `"Route choice sets for very high-resolution data" `_ -# by Nadine Rieser-Schüssler, Michael Balmer & Kay W. Axhausen (2013). -# -# Our BFSLE implementation has been extended to allow applying link penalisation as well. Every -# link in all routes found at a depth are penalised with the `penalty` factor for the next depth. -# So at a depth of 0 no links are penalised nor removed. At depth 1, all links found at depth 0 are penalised, -# then the links marked for removal are removed. All links in the routes found at depth 1 are then penalised -# for the next depth. The penalisation compounds. Pass set ``penalty=1.0`` to disable. -# -# To assist in filtering out bad results during the assignment, a ``cutoff_prob`` parameter can be provided to exclude -# routes from the path-sized logit model. The ``cutoff_prob`` is used to compute an inverse binary logit and obtain a -# max difference in utilities. If a paths total cost is greater than the minimum cost path in the route set plus the max -# difference, the route is excluded from the PSL calculations. The route is still returned, but with a probability of -# ``0.0``. -# -# The ``cutoff_prob`` should be in the range :math:`[0, 1]`. It is then rescaled internally to :math:`[0.5, 1]` -# as probabilities below ``0.5`` produce negative differences in utilities. A higher ``cutoff_prob`` includes -# more routes. A value of ``0.0`` will only include the minimum cost route. A value of ``1.0`` includes all -# routes. -# # It is highly recommended to set either ``max_routes`` or ``max_depth`` to prevent runaway results. - -# rc.set_choice_set_generation("link-penalisation", max_routes=5, penalty=1.02) - -# %% rc.set_choice_set_generation("bfsle", max_routes=5) # %% @@ -160,13 +132,13 @@ print(results[0]) # %% -# Because we asked it to also perform an assignment we can access the various results from that +# Because we asked it to also perform an assignment we can access the various results from that. # The default return is a Pyarrow Table but Pandas is nicer for viewing. res = rc.get_results().to_pandas() res.head() # %% -# let's define a function to plot assignment results +# Let's define a function to plot assignment results def plot_results(link_loads): import folium @@ -222,7 +194,7 @@ def plot_results(link_loads): rc.get_load_results() # %% -# we can plot these as well +# We can plot these as well plot_results(rc.get_load_results()["demand"]) # %% @@ -242,7 +214,8 @@ def plot_results(link_loads): # %% # We can also access the OD matrices for this link loading. These matrices are sparse and can be converted to # scipy.sparse matrices for ease of use. They're stored in a dictionary where the key is the matrix name concatenated -# with the select link set name via an underscore. +# with the select link set name via an underscore. +# # These matrices are constructed during ``get_select_link_loading_results``. rc.get_select_link_od_matrix_results() diff --git a/docs/source/examples/assignment_workflows/plot_route_choice_set.py b/docs/source/examples/assignment_workflows/plot_route_choice_set.py index 1a7f06235..fb3fcc91c 100644 --- a/docs/source/examples/assignment_workflows/plot_route_choice_set.py +++ b/docs/source/examples/assignment_workflows/plot_route_choice_set.py @@ -29,8 +29,7 @@ # %% # Model parameters # ---------------- - -# %% +# Let's select a set of nodes of interest od_pairs_of_interest = [(71645, 79385), (77011, 74089)] nodes_of_interest = (71645, 74089, 77011, 79385) @@ -54,7 +53,7 @@ # We allow flows through "centroid connectors" because our centroids are not really centroids. # If we have actual centroid connectors in the network (and more than one per centroid), then we -# should remove them from the graph +# should remove them from the graph. graph.set_blocked_centroid_flows(False) # %% @@ -65,33 +64,18 @@ # %% # This object construct might take a minute depending on the size of the graph due to the construction of the -# compressed link to network link mapping that's required. This is a one time operation per graph and is cached. We -# need to supply a ``Graph`` and an ``AequilibraeMatrix`` or Panda's DataFrame via the ``add_demand`` method, -# if demand is not provided link loading cannot be preformed. +# compressed link to network link mapping that's required. This is a one time operation per graph and is cached. rc = RouteChoice(graph) # %% -# Here we'll set the parameters of our set generation. There are two algorithms available: Link penalisation and BFSLE, -# based on the paper -# `"Route choice sets for very high-resolution data" `_ -# by Nadine Rieser-Schüssler, Michael Balmer & Kay W. Axhausen (2013). -# -# Our BFSLE implementation has been extended to allow applying link penalisation as well. Every -# link in all routes found at a depth are penalised with the 'penalty' factor for the next depth. -# So at a depth of 0 no links are penalised nor removed. At depth 1, all links found at depth 0 are penalised, -# then the links marked for removal are removed. All links in the routes found at depth 1 are then penalised -# for the next depth. The penalisation compounds. Pass set ``penalty=1.0`` to disable. -# # It is highly recommended to set either ``max_routes`` or ``max_depth`` to prevent runaway results. - -# rc.set_choice_set_generation("link-penalisation", max_routes=5, penalty=1.02) - -# %% -# The 5% penalty (1.05) is likely a little too large, but it create routes that are distinct enough to make this simple -# example more interesting +# +# We'll also set a 5% penalty (``penalty=1.05``), which is likely a little too large, but it creates routes that are +# distinct enough to make this simple example more interesting. rc.set_choice_set_generation("bfsle", max_routes=5, penalty=1.05) rc.prepare(od_pairs_of_interest) rc.execute(perform_assignment=True) + choice_set = rc.get_results().to_pandas() # %% @@ -113,7 +97,7 @@ layers = [rlyr1, rlyr2, rlyr3, rlyr4, rlyr5] # %% -# We get the data we will use for the plot: Links, Nodes and the route choice set +# We get the data we will use for the plot: links, nodes and the route choice set links = project.network.links.data nodes = project.network.nodes.data diff --git a/docs/source/examples/assignment_workflows/plot_subarea_analysis.py b/docs/source/examples/assignment_workflows/plot_subarea_analysis.py index 579319960..5b00d6cc6 100644 --- a/docs/source/examples/assignment_workflows/plot_subarea_analysis.py +++ b/docs/source/examples/assignment_workflows/plot_subarea_analysis.py @@ -35,6 +35,7 @@ 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) @@ -69,7 +70,7 @@ # We also see what graphs are available project.network.graphs.keys() # %% -# let's say that utility is just a function of distance +# Let's say that utility is just a function of distance. # So we build our *utility* field as the distance times theta graph.network = graph.network.assign(utility=graph.network.distance * theta) @@ -82,9 +83,9 @@ graph.set_graph("utility") # %% -# We allow flows through "centroid connectors" because our centroids are not really centroids +# We allow flows through "centroid connectors" because our centroids are not really centroids. # If we have actual centroid connectors in the network (and more than one per centroid), then we -# should remove them from the graph +# should remove them from the graph. graph.set_blocked_centroid_flows(False) graph.graph.head() diff --git a/docs/source/examples/creating_models/plot_create_zoning.py b/docs/source/examples/creating_models/plot_create_zoning.py index ca056e487..1a909a4fc 100644 --- a/docs/source/examples/creating_models/plot_create_zoning.py +++ b/docs/source/examples/creating_models/plot_create_zoning.py @@ -6,14 +6,15 @@ In this example, we show how to create hex bin zones covering an arbitrary area. +We also add centroid connectors and a special generator zone to our network to make +it a pretty complete example. + We use the Nauru example to create roughly 100 zones covering the whole modeling -area as delimited by the entire network +area as delimited by the entire network. You are obviously welcome to create whatever zone system you would like, as long as you have the geometries for them. In that case, you can just skip the hex bin computation part of this notebook. - -We also add centroid connectors to our network to make it a pretty complete example """ # %% @@ -28,14 +29,15 @@ from aequilibrae.utils.create_example import create_example, list_examples # sphinx_gallery_thumbnail_path = "images/plot_create_zoning.png" +# %% +# Let's print the list of examples that ship with AequilibraE +print(list_examples()) + # %% # We create an empty project on an arbitrary folder fldr = join(gettempdir(), uuid4().hex) -# We can print the list of examples that ship with AequilibraE -print(list_examples()) - # Let's use the Nauru example project for display project = create_example(fldr, "nauru") @@ -69,8 +71,8 @@ # %% # Now we can run an SQL query to compute the hexagonal grid. # There are many ways to create hex bins (including with a GUI on QGIS), but we find that -# using SpatiaLite is a pretty neat solution. -# For which we will use the entire network bounding box to make sure we cover everything +# using SpatiaLite is a pretty neat solution, +# for which we will use the entire network bounding box to make sure we cover everything. extent = network.extent() # %% @@ -84,13 +86,13 @@ grid = shapely.wkb.loads(grid) # %% -# Since we used the bounding box, we have WAY more zones than we wanted, so we clean them +# Since we used the bounding box, we have way more zones than we wanted, so we clean them # by only keeping those that intersect the network convex hull. grid = [p for p in grid.geoms if p.intersects(geo)] # %% -# Let's re-number all nodes with IDs smaller than 300 to something bigger as to free space to our centroids to go from 1 -# to N. +# Let's re-number all nodes with IDs smaller than 300 to something bigger as to free space to our +# centroids to go from 1 to N. nodes = network.nodes for i in range(1, 301): nd = nodes.get(i) @@ -110,6 +112,7 @@ # %% # Centroid connectors # ------------------- +# Let's connect our zone centroids to the network. # %% for zone_id, zone in zoning.all_zones().items(): @@ -126,13 +129,9 @@ # %% # Special generator zones -# ~~~~~~~~~~~~~~~~~~~~~~~ +# ----------------------- # -# Let's add special generator zones! - -# %% -# We also add a centroid at the airport terminal -nodes = project.network.nodes +# Let's add a special generator zone by adding a centroid at the airport terminal. # %% # Let's use some silly number for its ID, like 10,000, just so we can easily differentiate it diff --git a/docs/source/modeling_with_aequilibrae/route_choice/choice_set_generation.rst b/docs/source/modeling_with_aequilibrae/route_choice/choice_set_generation.rst index 443b4308c..0ad7088f9 100644 --- a/docs/source/modeling_with_aequilibrae/route_choice/choice_set_generation.rst +++ b/docs/source/modeling_with_aequilibrae/route_choice/choice_set_generation.rst @@ -53,11 +53,11 @@ breadth-first search (BFS) order. To efficiently store and determine the uniqueness of a new route or removed link sets, we used modified hash functions with properties that allowed us to store and nest them within standard C++ data structures. We used a commutative hash -function for the removed link sets to allow for amortised O(1) order-independent uniqueness testing. While the removed -link sets are always constructed incrementally, we did not opt for an incremental hash function as we did not deem this -a worthwhile optimisation. The removed link sets rarely grew larger than double digits, even on a network with over -600,000 directed links. This may be an area worth exploring for networks with a significantly larger number of desired -routes than links between ODs. +function for the removed link sets to allow for amortised :math:`O(1)` order-independent uniqueness testing. While the +removed link sets are always constructed incrementally, we did not opt for an incremental hash function as we did not +deem this a worthwhile optimisation. The removed link sets rarely grew larger than double digits, even on a network +with over 600,000 directed links. This may be an area worth exploring for networks with a significantly larger number +of desired routes than links between ODs. For uniqueness testing of discovered routes, AequilibraE implements a traditional, non-commutative hash function. Since cryptographic security was not a requirement for our purposes, we use a fast general-purpose integer hash function. From bd4d36ddd7b16d8c2cce5c73bad8c140e81f3d76 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Thu, 12 Sep 2024 15:17:05 -0300 Subject: [PATCH 42/57] Update route_choice.py --- aequilibrae/paths/route_choice.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/aequilibrae/paths/route_choice.py b/aequilibrae/paths/route_choice.py index 39969809b..6ad425090 100644 --- a/aequilibrae/paths/route_choice.py +++ b/aequilibrae/paths/route_choice.py @@ -81,8 +81,8 @@ def set_choice_set_generation(self, /, algorithm: str, **kwargs) -> None: Options for algorithm are, 'bfsle' for breadth first search with link removal, or 'link-penalisation'/'link-penalization'. - BFSLE implementation based on "Route choice sets for very high-resolution data" by - Nadine Rieser-Schüssler, Michael Balmer & Kay W. Axhausen (2013). + BFSLE implementation based on "Route choice sets for very high-resolution data" by + Nadine Rieser-Schüssler, Michael Balmer & Kay W. Axhausen (2013). `DOI: 10.1080/18128602.2012.671383 `_. 'lp' is also accepted as an alternative to 'link-penalisation' @@ -91,7 +91,7 @@ def set_choice_set_generation(self, /, algorithm: str, **kwargs) -> None: ``seed`` is a BFSLE specific parameters. - Setting ``max_depth`` or ``max_misses``, while not required, is strongly recommended to prevent runaway + Setting ``max_depth`` or ``max_misses``, while not required, is strongly recommended to prevent runaway algorithms. ``max_misses`` is the maximum amount of duplicate routes found per OD pair. If it is exceeded then the route set @@ -103,7 +103,7 @@ def set_choice_set_generation(self, /, algorithm: str, **kwargs) -> None: until the number of desired routes is being consistently returned. If it is exceeded then the route set if returned with fewer than ``max_routes``. - - When using **LP**, ``max_depth`` corresponds to the maximum number of iterations performed. While not + - When using **LP**, ``max_depth`` corresponds to the maximum number of iterations performed. While not enforced, it should be higher than ``max_routes``. It's value is dependent on the magnitude of the cost field, specifically it's related to the log base ``penalty`` of the ratio of costs between two alternative routes. @@ -395,15 +395,15 @@ def set_select_links( """ Set the selected links. Checks if the links and directions are valid. Supports **OR** and **AND** sets of links. - Dictionary values should be a list of either a single ``(link_id, direction)`` tuple or a list of + Dictionary values should be a list of either a single ``(link_id, direction)`` tuple or a list of ``(link_id, dirirection)``. The elements of the first list represent the **AND** sets, together they are OR'ed. If any of these sets is - satisfied the link are loaded as appropriate. - - The **AND** sets are comprised of either a single ``(link_id, direction)`` tuple or a list of - ``(link_id, direction)``. The single tuple represents an **AND** set with a single element. - + satisfied the link are loaded as appropriate. + + The **AND** sets are comprised of either a single ``(link_id, direction)`` tuple or a list of + ``(link_id, direction)``. The single tuple represents an **AND** set with a single element. + All links and directions in an **AND** set must appear in any order within a route for it to be considered satisfied. From f3a6f669d90096fb4223e62d0a061ce52e5f8544 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Thu, 12 Sep 2024 16:40:51 -0300 Subject: [PATCH 43/57] . --- docs/source/conf.py | 3 +- .../plot_ipf_without_model.py | 6 +- .../plot_public_transit_assignment.py | 94 ++++++---------- .../plot_route_choice_basics.py | 105 +++++++++--------- .../plot_subarea_analysis.py | 9 +- .../plot_moving_link_extremity.py | 10 +- .../editing_networks/plot_moving_nodes.py | 6 +- .../editing_networks/plot_splitting_link.py | 4 + .../plot_logging_to_terminal.py | 1 + 9 files changed, 107 insertions(+), 131 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 0e13545a9..deb842c91 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -79,10 +79,9 @@ "subsection_order": ExplicitOrder( [ "examples/creating_models", + "examples/editing_networks", "examples/skimming", - "examples/trip_distribution", "examples/assignment_workflows", - "examples/editing_networks", "examples/aequilibrae_without_a_model", "examples/visualization", "examples/other_applications", diff --git a/docs/source/examples/aequilibrae_without_a_model/plot_ipf_without_model.py b/docs/source/examples/aequilibrae_without_a_model/plot_ipf_without_model.py index 468bcd94e..56f117d72 100644 --- a/docs/source/examples/aequilibrae_without_a_model/plot_ipf_without_model.py +++ b/docs/source/examples/aequilibrae_without_a_model/plot_ipf_without_model.py @@ -8,10 +8,12 @@ This is a complement to the application in :ref:`example_usage_forecasting`. Let's consider that you have an OD-matrix, the future production and future attraction values. + *How would your trip distribution matrix using IPF look like?* -The data used in this example comes from Table 5.6 in Ortúzar & Willumsen (2011). -*ORTÚZAR, J.D., WILLUMSEN, L.G. (2011) Modelling Transport (4th ed.). Wiley-Blackwell.* +The data used in this example comes from Table 5.6 in +`Ortúzar & Willumsen (2011) `_. + """ # %% diff --git a/docs/source/examples/assignment_workflows/plot_public_transit_assignment.py b/docs/source/examples/assignment_workflows/plot_public_transit_assignment.py index cff1a49f0..1459f47a2 100644 --- a/docs/source/examples/assignment_workflows/plot_public_transit_assignment.py +++ b/docs/source/examples/assignment_workflows/plot_public_transit_assignment.py @@ -4,7 +4,8 @@ Public transport assignment with Optimal Strategies =================================================== -In this example, we import a GTFS feed to our model, create a public transport network, create project match connectors, and perform a Spiess & Florian assignment. +In this example, we import a GTFS feed to our model, create a public transport network, create project match connectors, and perform a Spiess & Florian assignment. `Click here `_ +to check out the article. We use data from Coquimbo, a city in La Serena Metropolitan Area in Chile. """ @@ -12,20 +13,11 @@ # Imports for example construction from uuid import uuid4 -from os import remove from os.path import join from tempfile import gettempdir -from aequilibrae.paths import TransitAssignment, TransitClass -from aequilibrae.utils.create_example import create_example -import numpy as np - -# Imports for GTFS import from aequilibrae.transit import Transit - -# Imports for SF transit graph construction -from aequilibrae.project.database_connection import database_connection -from aequilibrae.transit.transit_graph_builder import TransitGraphBuilder +from aequilibrae.utils.create_example import create_example # sphinx_gallery_thumbnail_path = 'images/transit/hyperpath_bell_n_10_alpha_100d0.png' # %% @@ -36,31 +28,9 @@ project = create_example(fldr, "coquimbo") # %% -# As the Coquimbo example already has a complete GTFS model, we shall remove its public transport -# database for the sake of this example. -remove(join(fldr, "public_transport.sqlite")) - -# %% -# Let's import the GTFS feed. -dest_path = join(fldr, "gtfs_coquimbo.zip") - -# %% -# Now we create our Transit object and import the GTFS feed into our model. -# This will automatically create a new public transport database. +# Let's create our ``Transit`` object. data = Transit(project) -transit = data.new_gtfs_builder(agency="LISANCO", file_path=dest_path) - -# %% -# To load the data, we must choose one date. We're going to continue with 2016-04-13 but feel free -# to experiment with any other available dates. Transit class has a function allowing you to check -# dates for the GTFS feed. It should take approximately 2 minutes to load the data. -transit.load_date("2016-04-13") - -# %% -# Let's save this model for later use. -transit.save_to_disk() - # %% # Graph building # -------------- @@ -70,39 +40,38 @@ # For the OD connections we'll use the ``overlapping_regions`` method and create some accurate line geometry later. # Creating the graph should only take a moment. By default zoning information is pulled from the project network. # If you have your own zoning information add it using ``graph.add_zones(zones)`` then ``graph.create_graph()``. -# We drop gemoetry here for the sake of display. # %% graph = data.create_graph(with_outer_stop_transfers=False, with_walking_edges=False, blocking_centroid_flows=False, connector_method="overlapping_regions") -# %% +# We drop geometry here for the sake of display. graph.vertices.drop(columns="geometry") # %% graph.edges # %% -# The graphs also also stored in the ``Transit.graphs`` dictionary. -# They are keyed by the `period_id` they were created for. -# A graph for a different `period_id` can be created by providing ``period_id=`` in the ``Transit.create_graph`` -# call. You can view previously created periods with the ``Periods`` object. +# The graphs also also stored in the ``Transit.graphs`` dictionary. They are keyed by the 'period_id' they +# were created for. A graph for a different 'period_id' can be created by providing ``period_id=`` in the +# ``Transit.create_graph`` call. You can view previously created periods with the ``Periods`` object. periods = project.network.periods periods.data # %% # Connector project matching -# ~~~~~~~~~~~~~~~~~~~~~~~~~~ +# -------------------------- project.network.build_graphs() # %% # Now we'll create the line strings for the access connectors, this step is optinal but provides more accurate distance -# estimations and better looking geometry. Because Coquimbo doesn't have many walking edges we'll match onto the -# `"c"` graph. +# estimations and better looking geometry. +# +# Because Coquimbo doesn't have many walking edges we'll match onto the ``"c"`` graph. graph.create_line_geometry(method="connector project match", graph="c") # %% # Saving and reloading -# ~~~~~~~~~~~~~~~~~~~~ +# -------------------- # Lets save all graphs to the 'public_transport.sqlite' database. data.save_graphs() @@ -115,12 +84,16 @@ # %% # Links and nodes are stored in a similar manner to the 'project_database.sqlite' database. - -# %% +# # Reading back into AequilibraE -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ----------------------------- # You can create back in a particular graph via it's 'period_id'. +from aequilibrae.project.database_connection import database_connection +from aequilibrae.transit.transit_graph_builder import TransitGraphBuilder + +# %% pt_con = database_connection("transit") + graph_db = TransitGraphBuilder.from_db(pt_con, periods.default_period.period_id) graph_db.vertices.drop(columns="geometry") @@ -129,19 +102,16 @@ # %% # Converting to a AequilibraE graph object -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ---------------------------------------- # To perform an assignment we need to convert the graph builder into a graph. transit_graph = graph.to_transit_graph() -# %% -# Spiess & Florian assignment -# --------------------------- - # %% # Mock demand matrix -# ~~~~~~~~~~~~~~~~~~ -# We'll create a mock demand matrix with demand ``1`` for every zone. +# ------------------ +# We'll create a mock demand matrix with demand 1 for every zone. # We'll also need to convert from ``zone_id``\'s to ``node_id``\'s. +import numpy as np from aequilibrae.matrix import AequilibraeMatrix # %% @@ -159,14 +129,17 @@ # %% # Hyperpath generation/assignment -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ------------------------------- # We'll create a ``TransitAssignment`` object as well as a ``TransitClass`` +from aequilibrae.paths import TransitAssignment, TransitClass # %% -assig = TransitAssignment() # Create the assignment class assigclass = TransitClass(name="pt", graph=transit_graph, matrix=mat) + +assig = TransitAssignment() + assig.add_class(assigclass) # We need to tell AequilbraE where to find the appropriate fields we want to use, @@ -179,21 +152,16 @@ # When there's multiple matrix cores we'll also need to set the core to use for the demand. assigclass.set_demand_matrix_core("pt") -# %% -# Let's perform the assignment with the mock demand matrx for all ``TransitClass``\'s added. +# Let's perform the assignment for the transit classes added assig.execute() # %% # View the results assig.results() -# %% -# We can also access the ``TransitAssignmentResults`` object from the ``TransitClass`` -assigclass.results - # %% # Saving results -# ~~~~~~~~~~~~~~ +# -------------- # We'll be saving the results to another sqlite db called 'results_database.sqlite'. # The 'results' table with 'project_database.sqlite' contains some metadata about each table in # 'results_database.sqlite'. diff --git a/docs/source/examples/assignment_workflows/plot_route_choice_basics.py b/docs/source/examples/assignment_workflows/plot_route_choice_basics.py index 295f6ea11..3e41d0eaa 100644 --- a/docs/source/examples/assignment_workflows/plot_route_choice_basics.py +++ b/docs/source/examples/assignment_workflows/plot_route_choice_basics.py @@ -46,7 +46,7 @@ # %% # We'll set the parameters for our route choice model. These are the parameters that will be used to calculate the -# utility of each path. In our example, the utility is equal to :math:`theta * distance`, +# utility of each path. In our example, the utility is equal to :math:`distance * theta`, # and the path overlap factor (PSL) is equal to :math:`beta`. # Distance factor @@ -55,29 +55,26 @@ # PSL parameter beta = 1.1 +# %% +# Let's select a set of nodes of interest +nodes_of_interest = (71645, 74089, 77011, 79385) + # %% # Let's 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() -od_pairs_of_interest = [(71645, 79385), (77011, 74089)] -nodes_of_interest = (71645, 74089, 77011, 79385) - # %% -# Let's say that utility is just a function of distance, so we build our *utility* field as -# :math:`distance * theta` -graph.network = graph.network.assign(utility=graph.network.distance * theta) +# We grab the graph for cars +graph = project.network.graphs["c"] -# %% +# Let's say that utility is just a function of distance, so we build our 'utility' field as distance * theta +graph.network = graph.network.assign(utility=graph.network.distance * theta) # Prepare the graph with all nodes of interest as centroids graph.prepare_graph(np.array(nodes_of_interest)) @@ -93,7 +90,7 @@ # %% # Mock demand matrix # ------------------ -# We'll create a mock demand matrix with demand ``1`` for every zone. +# We'll create a mock demand matrix with demand 1 for every zone and prepare it for computation. from aequilibrae.matrix import AequilibraeMatrix names_list = ["demand", "5x demand"] @@ -106,41 +103,13 @@ mat.computational_view() # %% -# Route Choice class -# ------------------ -# Here we'll construct and use the Route Choice class to generate our route sets -from aequilibrae.paths import RouteChoice - -# %% -# This object construct might take a minute depending on the size of the graph due to the construction of the compressed -# link to network link mapping that's required. This is a one time operation per graph and is cached. -rc = RouteChoice(graph) -rc.add_demand(mat) - -# %% -# It is highly recommended to set either ``max_routes`` or ``max_depth`` to prevent runaway results. -rc.set_choice_set_generation("bfsle", max_routes=5) - -# %% -# All parameters are optional, the defaults are: -print(rc.default_parameters) - -# %% -# We can now perform a computation for single OD pair if we'd like. Here we do one between the first and last centroid -# as well an an assignment. -results = rc.execute_single(77011, 74089, demand=1.0) -print(results[0]) - -# %% -# Because we asked it to also perform an assignment we can access the various results from that. -# The default return is a Pyarrow Table but Pandas is nicer for viewing. -res = rc.get_results().to_pandas() -res.head() +# Create plot function +# -------------------- +# Before dive into the Route Choice class, let's define a function to plot assignment results. +import folium # %% -# Let's define a function to plot assignment results def plot_results(link_loads): - import folium link_loads = link_loads[link_loads.tot > 0] max_load = link_loads["tot"].max() @@ -172,15 +141,47 @@ def plot_results(link_loads): return map_osm # %% -plot_results(rc.get_load_results()["demand"]) +# Route Choice class +# ------------------ +# Here we'll construct and use the Route Choice class to generate our route sets +from aequilibrae.paths import RouteChoice # %% -# To perform a batch operation we need to prepare the object first. We can either provide a list of tuple of the OD -# pairs we'd like to use, or we can provided a 1D list and the generation will be run on all permutations. +# This object construct might take a minute depending on the size of the graph due to the construction of the compressed +# link to network link mapping that's required. This is a one time operation per graph and is cached. +rc = RouteChoice(graph) + +# Let's check the default parameters for the Route Choice class +print(rc.default_parameters) -# rc.prepare(graph.centroids[:5]) +# %% +# Let's add the demand. If it's not provided, link loading cannot be preformed. +rc.add_demand(mat) # %% +# It is highly recommended to set either ``max_routes`` or ``max_depth`` to prevent runaway results. +rc.set_choice_set_generation("bfsle", max_routes=5) + +# %% +# We can now perform a computation for single OD pair if we'd like. Here we do one between the first and last centroid +# as well as an assignment. +results = rc.execute_single(77011, 74089, demand=1.0) +print(results[0]) + +# %% +# Because we asked it to also perform an assignment we can access the various results from that. +# The default return is a Pyarrow Table but Pandas is nicer for viewing. +res = rc.get_results().to_pandas() +res.head() + +# %% +plot_results(rc.get_load_results()["demand"]) + +# %% +# Batch operations +# ---------------- +# To perform a batch operation we need to prepare the object first. We can either provide a list of tuple of the OD +# pairs we'd like to use, or we can provided a 1D list and the generation will be run on all permutations. rc.prepare() # %% @@ -207,16 +208,14 @@ def plot_results(link_loads): rc.execute(perform_assignment=True) # %% -# We can get then the results in a Pandas data frame for both the network. +# We can get then the results in a Pandas DataFrame for both the network. sl = rc.get_select_link_loading_results() sl # %% # We can also access the OD matrices for this link loading. These matrices are sparse and can be converted to -# scipy.sparse matrices for ease of use. They're stored in a dictionary where the key is the matrix name concatenated +# SciPy sparse matrices for ease of use. They're stored in a dictionary where the key is the matrix name concatenated # with the select link set name via an underscore. -# -# These matrices are constructed during ``get_select_link_loading_results``. rc.get_select_link_od_matrix_results() # %% diff --git a/docs/source/examples/assignment_workflows/plot_subarea_analysis.py b/docs/source/examples/assignment_workflows/plot_subarea_analysis.py index 5b00d6cc6..161c30021 100644 --- a/docs/source/examples/assignment_workflows/plot_subarea_analysis.py +++ b/docs/source/examples/assignment_workflows/plot_subarea_analysis.py @@ -36,6 +36,7 @@ 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) @@ -47,7 +48,7 @@ # Model parameters # ---------------- # We'll set the parameters for our route choice model. These are the parameters that will be used to calculate the -# utility of each path. In our example, the utility is equal to :math:`theta * distance`, +# utility of each path. In our example, the utility is equal to :math:`distance * theta`, # and the path overlap factor (PSL) is equal to :math:`beta`. # Distance factor @@ -71,7 +72,7 @@ project.network.graphs.keys() # %% # Let's say that utility is just a function of distance. -# So we build our *utility* field as the distance times theta +# So we build our *utility* field as the :math:`distance * theta`. graph.network = graph.network.assign(utility=graph.network.distance * theta) # %% @@ -92,7 +93,7 @@ # %% # Mock demand matrix # ------------------ -# We'll create a mock demand matrix with demand ``10`` for every zone. +# We'll create a mock demand matrix with demand ``10`` for every zone and prepare it for computation. from aequilibrae.matrix import AequilibraeMatrix names_list = ["demand"] @@ -107,7 +108,7 @@ # Sub-area preparation # -------------------- # We need to define some polygon for out sub-area analysis, here we'll use a section of zones and create out polygon as -# # the union of their geometry. It's best to choose a polygon that avoids any unnecessary intersections with links as +# the union of their geometry. It's best to choose a polygon that avoids any unnecessary intersections with links as # the resource requirements of this approach grow quadratically with the number of links cut. zones_of_interest = [29, 30, 31, 32, 33, 34, 37, 38, 39, 40, 49, 50, 51, 52, 57, 58, 59, 60] zones = project.zoning.data.set_index("zone_id") diff --git a/docs/source/examples/editing_networks/plot_moving_link_extremity.py b/docs/source/examples/editing_networks/plot_moving_link_extremity.py index 5078d6d49..62b7f9d2f 100644 --- a/docs/source/examples/editing_networks/plot_moving_link_extremity.py +++ b/docs/source/examples/editing_networks/plot_moving_link_extremity.py @@ -19,6 +19,7 @@ import matplotlib.pyplot as plt # %% + # We create the example project inside our temp folder fldr = join(gettempdir(), uuid4().hex) @@ -31,8 +32,6 @@ # %% # Let's move node one from the upper left corner of the image above, a bit to the left and to the bottom -# %% - # We edit the link that goes from node 1 to node 2 link = links.get(1) node = all_nodes.get(1) @@ -44,15 +43,15 @@ node2 = all_nodes.get(2) link.geometry = LineString([new_extremity, node2.geometry]) +# We save the changes and refresh the links in memory for usage links.save() links.refresh() # %% # Because each link is unidirectional, you can no longer go from node 1 to node 2, obviously. - -# %% +# # We do NOT recommend this, though.... It is very slow for real networks. -# + # We plot the entire network. curr = project.conn.cursor() curr.execute("Select link_id from links;") @@ -71,6 +70,7 @@ plt.show() +# %% # Now look at the network and how it used to be. # %% diff --git a/docs/source/examples/editing_networks/plot_moving_nodes.py b/docs/source/examples/editing_networks/plot_moving_nodes.py index a255edf48..11402d43f 100644 --- a/docs/source/examples/editing_networks/plot_moving_nodes.py +++ b/docs/source/examples/editing_networks/plot_moving_nodes.py @@ -42,12 +42,14 @@ # If you want to show the path in Python. # # We do NOT recommend this, though.... It is very slow for real networks. -# -# We plot the entire network. + +# Let's refresh the links in memory for usage links.refresh() + curr = project.conn.cursor() curr.execute("Select link_id from links;") +# We plot the entire network. for lid in curr.fetchall(): geo = links.get(lid[0]).geometry plt.plot(*geo.xy, color="blue") diff --git a/docs/source/examples/editing_networks/plot_splitting_link.py b/docs/source/examples/editing_networks/plot_splitting_link.py index 90f5f73f7..18341ce0b 100644 --- a/docs/source/examples/editing_networks/plot_splitting_link.py +++ b/docs/source/examples/editing_networks/plot_splitting_link.py @@ -18,6 +18,7 @@ import matplotlib.pyplot as plt # %% + # We create the example project inside our temp folder fldr = join(gettempdir(), uuid4().hex) @@ -48,11 +49,13 @@ # %% # The link objects in memory still don't have their ID fields updated, so we refresh them. links.refresh() + link = links.get(37) new_link = links.get(new_link.link_id) print(link.distance, new_link.distance) # %% + # We can plot the two links only plt.clf() plt.plot(*link.geometry.xy, color="blue") @@ -64,6 +67,7 @@ plt.show() # %% + # Or we plot the entire network plt.clf() curr = project.conn.cursor() diff --git a/docs/source/examples/other_applications/plot_logging_to_terminal.py b/docs/source/examples/other_applications/plot_logging_to_terminal.py index ce32f460f..451f67d94 100644 --- a/docs/source/examples/other_applications/plot_logging_to_terminal.py +++ b/docs/source/examples/other_applications/plot_logging_to_terminal.py @@ -19,6 +19,7 @@ # sphinx_gallery_thumbnail_path = 'images/plot_logging_to_terminal_image.png' # %% + # We create the example project inside our temp folder fldr = join(gettempdir(), uuid4().hex) project = create_example(fldr) From 646de22cf4e62da28fc95b8d84e0fbddbaa26c38 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Fri, 13 Sep 2024 10:34:49 -0300 Subject: [PATCH 44/57] add doctest strings --- .../project_components.rst | 212 +++++++++++------- 1 file changed, 127 insertions(+), 85 deletions(-) diff --git a/docs/source/modeling_with_aequilibrae/project_components.rst b/docs/source/modeling_with_aequilibrae/project_components.rst index c061a546e..7d2195d0d 100644 --- a/docs/source/modeling_with_aequilibrae/project_components.rst +++ b/docs/source/modeling_with_aequilibrae/project_components.rst @@ -40,27 +40,31 @@ this information, otherwise it will be lost. .. code-block:: python - from aequilibrae.utils.create_example import create_example + >>> from uuid import uuid4 + >>> from aequilibrae.utils.create_example import create_example - project = create_example("/path_to_my_folder", "coquimbo") + >>> project = create_example(f"/tmp/{uuid4().hex}") - project.about.add_info_field("my_new_field") - project.about.my_new_field = "add some useful information about the field" + >>> project.about.add_info_field("my_new_field") + >>> project.about.my_new_field = "add some useful information about the field" # We can add data to an existing field - project.about.author = "Your Name" + >>> project.about.author = "Your Name" # And save our modifications - project.about.write_back() + >>> project.about.write_back() - # To check if ``my_new_field`` was added to the 'about' table, we can check the characteristics - # stored in the table. - project.about.list_fields() # returns a list with all characteristics in the 'about' table + # To assert if 'my_new_field' was added to the 'about' table, we can check the characteristics + # stored in the table by returning a list with all characteristics in the 'about' table + >>> project.about.list_fields() # doctest: +ELLIPSIS + ['model_name', ..., 'my_new_field'] # The 'about' table is created automatically when a project is created, but if you're # loading a project created with an older AequilibraE version that didn't contain it, # it is possible to create one too. - project.about.create() + >>> project.about.create() + + >>> project.close() .. admonition:: References @@ -84,23 +88,32 @@ This class is directly accessed from within the corresponding module one wants t .. code-block:: python - # We'll edit the fields in the 'links' table - link_fields = project.network.links.fields + >>> from uuid import uuid4 + >>> from aequilibrae.utils.create_example import create_example + + >>> project = create_example(f"/tmp/{uuid4().hex}", "nauru") + + # We'll edit the fields in the 'nodes' table + >>> node_fields = project.network.nodes.fields - # To add a new field to the 'links' table - link_fields.add("my_new_field", "this is an example of AequilibraE's funcionalities", "TEXT") + # To add a new field to the 'nodes' table + >>> node_fields.add("my_new_field", "this is an example of AequilibraE's funcionalities", "TEXT") # Don't forget to save these modifications - link_fields.save() + >>> node_fields.save() # To edit the description of a field - link_fields.osm_id = "number of the osm link_id" + >>> node_fields.osm_id = "number of the osm node_id" # Or just to access the description of a field - link_fields.a_node + >>> node_fields.modes + 'Modes connected to the node' - # One can also check all the fields in the links table. - link_fields.all_fields() + # One can also check all the fields in the 'nodes' table. + >>> node_fields.all_fields() # doctest: +ELLIPSIS + ['is_centroid', ..., 'my_new_field'] + + >>> project.close() All field descriptions are kept in the table 'attributes_documentation'. @@ -122,13 +135,24 @@ It is possible to access the log file contents, as presented in the next code bl .. code-block:: python - project_log = project.log() + >>> from uuid import uuid4 + >>> from aequilibrae.utils.create_example import create_example + + >>> project = create_example(f"/tmp/{uuid4().hex}", "nauru") + + >>> project_log = project.log() - project_log.contents() # returns a list with all entires in the log file + # Returns a list with all entires in the log file. + >>> project_log.contents() # doctest: +ELLIPSIS + ['2021-01-01 15:52:03,945;aequilibrae;INFO ; Created project on D:/release/Sample models/nauru', + ..., + '2021-01-01 15:52:37,843;aequilibrae;INFO ; Network built successfully'] # If your project's log is getting cluttered, it is possible to clear it. - # This option must be used wiesly once the deletion of data in the log file can't be undone. - project_log.clear() + # Use this option wiesly once the deletion of data in the log file can't be undone. + >>> project_log.clear() + + >>> project.close() .. admonition:: References @@ -148,40 +172,45 @@ records in the 'matrices' table. Each item in the 'matrices' table is a ``Matri .. code-block:: python - from aequilibrae.utils.create_example import create_example + >>> from uuid import uuid4 + >>> from aequilibrae.utils.create_example import create_example - project = create_example("/path_to_my_folder") + >>> project = create_example(f"/tmp/{uuid4().hex}") - matrices = project.matrices + >>> matrices = project.matrices # One can also check all the project matrices as a Pandas' DataFrame - matrices.list() + >>> matrices.list() # doctest: +SKIP # We can add a naw matrix - matrices.new_record() + >>> matrices.new_record() # doctest: +SKIP # To delete a matrix from the 'matrices' table, we can delete the record directly - matrices.delete_record("demand_mc") - # or - mat_record = matrices.get_record("demand_mc") - mat_record.delete() + >>> matrices.delete_record("demand_mc") + + # or by selecting the matrix and deleting it + >>> mat_record = matrices.get_record("demand_omx") + >>> mat_record.delete() # If you're unsure if you have a matrix in you project, you can check if it exists # This function will return `True` or `False` - matrices.check_exists("my_matrix") + >>> matrices.check_exists("my_matrix") + False # If a matrix was added or deleted by an external process, you should update or clean # your 'matrices' table to keep your project organised. - matrices.update_database() # in case of addition + >>> matrices.update_database() # in case of addition - matrices.clear_database() # in case of deletion + >>> matrices.clear_database() # in case of deletion # To reload the existing matrices in memory once again - matrices.reload() + >>> matrices.reload() # Similar to the `get_record` function, we have the `get_matrix`, which allows you to # get an AequilibraE matrix. - matrices.get_matrix("demand_aem") + >>> matrices.get_matrix("demand_aem") # doctest: +SKIP + + >>> project.close() .. admonition:: References @@ -201,41 +230,45 @@ Each item in the 'link_types' table is a ``LinkType`` object. .. code-block:: python - from aequilibrae.utils.create_example import create_example + >>> from uuid import uuid4 + >>> from aequilibrae.utils.create_example import create_example - project = create_example("/path_to_my_folder", "coquimbo") + >>> project = create_example(f"/tmp/{uuid4().hex}", "coquimbo") - link_types = project.network.link_types + >>> link_types = project.network.link_types - new_link_type = link_types.new("A") # Create a new LinkType with ID 'A' + >>> new_link_type = link_types.new("A") # Create a new LinkType with ID 'A' # We can add information to the LinkType we just created - new_link_type.description = "This is a description" - new_link_type.speed = 35 - new_link_type.link_type = "Arterial" + >>> new_link_type.description = "This is a description" + >>> new_link_type.speed = 35 + >>> new_link_type.link_type = "Arterial" # To save the modifications for `new_link_type` - new_link_type.save() + >>> new_link_type.save() # To create a new field in the 'link_types' table, you can call the function `fields` # to return a FieldEditor instance, which can be edited - link_types.fields() + >>> link_types.fields.add("my_new_field", "this is an example of AequilibraE's funcionalities", "TEXT") # You can also remove a LinkType from a project using its `link_type_id` - link_types.delete("A") + >>> link_types.delete("A") # And don't forget to save the modifications you did in the 'link_types' table - link_types.save() + >>> link_types.save() - # To check all `LinkTypes` in the project - link_types.all_types() # returns a dictionary with all LinkType objects in the model. - # The dictionary's keys are the `link_type_id`'s + # To check all `LinkTypes` in the project as a dictionary whose keys are the `link_type_id`'s + >>> link_types.all_types() # doctest: +ELLIPSIS + {'z': ...} # There are two ways to get a LinkType from the 'link_types' table # using the `link_type_id` - link_types.get("p") + >>> get_link = link_types.get("p") + # or using the `link_type` - link_types.get_by_name("primary") + >>> get_link = link_types.get_by_name("primary") + + >>> project.close() .. admonition:: References @@ -255,36 +288,40 @@ Each item in 'modes' table is a ``Mode`` object. .. code-block:: python - from aequilibrae.utils.create_example import create_example + >>> from uuid import uuid4 + >>> from aequilibrae.utils.create_example import create_example - project = create_example("/path_to_my_folder", "coquimbo") + >>> project = create_example(f"/tmp/{uuid4().hex}", "coquimbo") - modes = project.network.modes + >>> modes = project.network.modes # We create a new mode - new_mode = modes.new("k") - new_mode.mode_name = "flying_car" + >>> new_mode = modes.new("k") + >>> new_mode.mode_name = "flying_car" # And add it to the modes table - modes.add(new_mode) + >>> modes.add(new_mode) # When we add a new mode to the 'modes' table, it is automatically saved in the table # But we can continue editing the modes, and save them as we modify them - new_mode.description = "Like the one in the Jetsons" - new_mode.save() + >>> new_mode.description = "Like the one in the cartoons" + >>> new_mode.save() # You can also remove a Mode from a project using its ``mode_id`` - modes.delete("w") + >>> modes.delete("k") - # To check all ``LinkTypes`` in the project - modes.all_modes() # returns a dictionary with all Mode objects in the model. - # The dictionary's keys are the ``mode_id``'s + # To check all `Modes` in the project as a dictionary whose keys are the `mode_id`'s + >>> modes.all_modes() + {'b': ..., 'c': ..., 't': ..., 'w': ...} # There are two ways to get a Mode from the 'modes' table # using the ``mode_id`` - modes.get("c") + >>> get_mode = modes.get("c") + # or using the ``mode_name`` - modes.get_by_name("car") + >>> get_mode = modes.get_by_name("car") + + >>> project.close() .. admonition:: References @@ -304,39 +341,44 @@ Each item in the 'periods' table is a ``Period`` object. .. code-block:: python - from aequilibrae.utils.create_example import create_example + >>> from uuid import uuid4 + >>> from aequilibrae.utils.create_example import create_example - project = create_example("/path_to_my_folder", "coquimbo") + >>> project = create_example(f"/tmp/{uuid4().hex}", "coquimbo") - periods = project.network.periods + >>> periods = project.network.periods # Let's add a new field to our 'periods' table - periods.fields.add("my_field", "This is an example", "TEXT") + >>> periods.fields.add("my_field", "This is field description", "TEXT") # To save this modification, we must refresh the table - periods.refresh_fields() + >>> periods.refresh_fields() + + # Let's get our default period and change the description for our new field + >>> select_period = periods.get(1) + >>> select_period.my_field = "hello world" + + # And we save this period modification + >>> select_period.save() + + # To see all periods data as a Pandas' DataFrame + >>> periods.data # doctest: +SKIP # To add a new period - new_period = periods.new_period(2, 21600, 43200, "6AM to noon") + >>> new_period = periods.new_period(2, 21600, 43200, "6AM to noon") # It is also possible to renumber a period - new_period.renumber(9) + >>> new_period.renumber(9) + # And check the existing data fields for each period - new_period.data_fields() + >>> new_period.data_fields() + ['period_id', 'period_start', 'period_end', 'period_description', 'my_field'] # Saving can be done after finishing all modifications in the table but for the sake # of this example, we'll save the addition of a new period to our table right away - periods.save() - - # To see all periods data as a Pandas' DataFrame - periods.data + >>> periods.save() - # Let's get our default period and change its description - select_period = periods.get(1) - select_period.period_description = "We changed the period description" - - # And we save this period modification - select_period.save() + >>> project.close() .. admonition:: References From e7cc21ae8f00142b87486788b6b65d0b00631373 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Fri, 13 Sep 2024 11:15:07 -0300 Subject: [PATCH 45/57] . --- .github/workflows/documentation.yml | 2 + .../accessing_project_data.rst | 135 ++++++++++-------- 2 files changed, 77 insertions(+), 60 deletions(-) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 54f1a7399..fe9c62805 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -46,6 +46,8 @@ jobs: - name: Test docstrings run: | python -m pytest --doctest-modules aequilibrae/log.py aequilibrae/parameters.py aequilibrae/paths/vdf.py + python -m pytest docs/source/modeling_with_aequilibrae/project_components.rst --doctest-glob=*.rst + python -m pytest docs/source/modeling_with_aequilibrae/accessing_project_data.rst --doctest-glob=*.rst - name: Build documentation run: | diff --git a/docs/source/modeling_with_aequilibrae/accessing_project_data.rst b/docs/source/modeling_with_aequilibrae/accessing_project_data.rst index 5b1811134..227743918 100644 --- a/docs/source/modeling_with_aequilibrae/accessing_project_data.rst +++ b/docs/source/modeling_with_aequilibrae/accessing_project_data.rst @@ -15,23 +15,27 @@ Each item in the 'links' table is a ``Link`` object. .. code-block:: python - from aequilibrae.utils.create_example import create_example + >>> from shapely.geometry import LineString + >>> from uuid import uuid4 + >>> from aequilibrae.utils.create_example import create_example - project = create_example("/path_to_my_folder", "coquimbo") + >>> project = create_example(f"/tmp/{uuid4().hex}", "coquimbo") - project_links = project.network.links + >>> project_links = project.network.links # Let's add a new field to our 'links' table - project_links.fields.add("my_field", "This is an example", "TEXT") + >>> project_links.fields.add("my_field", "This is an example", "TEXT") + # To save this modification, we must refresh the table - project_links.refresh_fields() + >>> project_links.refresh_fields() # Let's add a new link to our project - new_link = project_links.new() - new_link.geometry = LineString([(-71.304754, -29.955233), (-71.304863, -29.954049)]) - new_link.modes = "bctw" + >>> new_link = project_links.new() + >>> new_link.geometry = LineString([(-71.304754, -29.955233), (-71.304863, -29.954049)]) + >>> new_link.modes = "bctw" + # To add a new link, it must be explicitly saved - new_link.save() + >>> new_link.save() # The 'links' table has three fields which cannot be empty (i.e. with `NULL` values): # `link_id`, `direction`, and `modes`. When we create a node, `new` automatically @@ -39,21 +43,22 @@ Each item in the 'links' table is a ``Link`` object. # information should be added, otherwise, it will raise an error. # To delete one link from the project, you can use one of the following - other_link = project_links.get(21332) - other_link.delete() + >>> other_link = project_links.get(21332) + >>> other_link.delete() + # or - project_links.delete(21332) + >>> project_links.delete(21337) # The `copy_link` function creates a copy of a specified link # It is very helpful case you want to split a link. # You can check out in one of the usage examples. - link_copy = project_links.copy_link(10972) + >>> link_copy = project_links.copy_link(10972) # Don't forget to save the modifications to the links layer - project_links.save() + >>> project_links.save() # And refresh the links in memory for usage - project_links.refresh() + >>> project_links.refresh() .. admonition:: References @@ -75,48 +80,51 @@ Each item in the 'nodes' table is a ``Node`` object. .. code-block:: python - from aequilibrae.utils.create_example import create_example - from shapely.geometry import Point - - project = create_example("/path_to_my_folder", "coquimbo") + >>> from shapely.geometry import Point - project_nodes = project.network.nodes + >>> project_nodes = project.network.nodes # To get one 'Node' object - node = project_nodes.get(10070) + >>> node = project_nodes.get(10070) # We can check the existing fields for each node in the 'nodes' table - node.data_fields() + >>> node.data_fields() + ['node_id', 'is_centroid', 'modes', 'link_types', 'geometry', 'osm_id'] # Let's renumber this node and save it - node.renumber(1000) - node.save() + >>> node.renumber(1000) + >>> node.save() # A node can also be used to add a special generator # `new_centroid` returns a `Node` object that we can edit - centroid = project_nodes.new_centroid(2000) + >>> centroid = project_nodes.new_centroid(2000) # Don't forget to add a geometry to your centroid if it's a new node # This centroid corresponds to the Port of Coquimbo! - centroid.geometry = Point(-71.32, -29.94) + >>> centroid.geometry = Point(-71.32, -29.94) # As this centroid is not associated with a zone, we must tell AequilibraE the initial area around # the centroid to look for candidate nodes to which the centroid can connect. - centroid.connect_mode(area=centroid.geometry.buffer(0.01), mode_id="c") + >>> centroid.connect_mode(area=centroid.geometry.buffer(0.01), mode_id="c") # Don't forget to update these changes to the nodes in memory - project_nodes.refresh() + >>> project_nodes.refresh() # And save them into your project - project_nodes.save() + >>> project_nodes.save() # Last but not less important, you can check your project nodes # `project_nodes.data` returns a geopandas GeoDataFrame. - project_nodes.data + >>> nodes_data = project_nodes.data - # or if you want to check the coordinate of each node in the shape of - # a Pandas DataFrame - project_nodes.lonlat + >>> # or if you want to check the coordinate of each node in the shape of + >>> # a Pandas DataFrame + >>> coords = project_nodes.lonlat + >>> coords.head(3) + node_id lon lat + 0 10037 -71.315117 -29.996804 + 1 10064 -71.336604 -29.949050 + 2 10065 -71.336517 -29.949062 .. admonition:: References @@ -140,63 +148,70 @@ Each item in the 'zones' table is a ``Zone`` object. .. code-block:: python - from aequilibrae.utils.create_example import create_example - from shapely.geometry import Polygon, Point + >>> from shapely.geometry import Polygon - project = create_example("/path_to_my_folder", "coquimbo") - - project_zones = project.zoning + >>> project_zones = project.zoning # Let's start this example by adding a new field to the 'zones' table - project_zones.fields.add("parking_spots", "Number of public parking spots", "INTEGER") + >>> project_zones.fields.add("parking_spots", "Number of public parking spots", "INTEGER") # We can check if the new field was indeed created - project_zones.fields.all_fields() + >>> project_zones.fields.all_fields() # doctest: +ELLIPSIS + ['area', 'employment', 'geometry', 'name', 'parking_spots', 'population', 'zone_id'] # Now let's get a zone and modifiy it - zone = project_zones.get(40) + >>> zone = project_zones.get(40) + # By disconnecting the transit mode - zone.disconnect_mode("t") + >>> zone.disconnect_mode("t") + # Connecting the bicycle mode - zone.connect_mode("b") + >>> zone.connect_mode("b") + # And adding the number of public parking spots in the field we just created - zone.parking_spots = 30 - # You can save this modifications if you want - zone.save() + >>> zone.parking_spots = 30 + + # You can save this changes if you want + >>> zone.save() # The changes connecting / disconnecting modes reflect in the zone centroids # and can be seen in the 'nodes' table. + # To return a dictionary with all 'Zone' objects in the model + >>> project_zones.all_zones() # doctest: +ELLIPSIS + {1: ..., ..., 133: ...} + # If you want to delete a zone - other_zone = project_zones.get(38) - other_zone.delete() + >>> other_zone = project_zones.get(38) + >>> other_zone.delete() # Or to add a new one - zone_extent = Polygon([(-71.3325, -29.9473), (-71.3283, -29.9473), (-71.3283, -29.9539), (-71.3325, -29.9539)]) + >>> zone_extent = Polygon([(-71.3325, -29.9473), (-71.3283, -29.9473), (-71.3283, -29.9539), (-71.3325, -29.9539)]) + + >>> new_zone = project_zones.new(38) + >>> new_zone.geometry = zone_extent - new_zone = project_zones.new(38) - new_zone.geometry = zone_extent # We can add a centroid to the zone we just created by specifying its location or # pass `None` to use the geometric center of the zone - new_zone.add_centroid(Point(-71.33, -29.95)) + >>> new_zone.add_centroid(Point(-71.33, -29.95)) # Let's refresh our fields - project_zones.refresh_geo_index() + >>> project_zones.refresh_geo_index() # And save the new changes in the project - project_zones.save() + >>> project_zones.save() # Finally, to return a geopandas GeoDataFrame with the project zones - project_zones.data - - # To return a dictionary with all 'Zone' objects in the model - project_zones.all_zones() + >>> zones = project_zones.data # To get a Shapely Polygon or Multipolygon with the entire zoning coverage - project_zones.all_zones() + >>> project_zones.coverage() # doctest: +SKIP # And to get the nearest zone to giver geometry - project_zones.get_closest_zone(Point(-71.3336, -29.9490)) + >>> project_zones.get_closest_zone(Point(-71.3336, -29.9490)) + 57 + + >>> project.close() .. admonition:: References From 11950dea2f71bc2ec5a35f6970980d29db296f0c Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Fri, 13 Sep 2024 11:54:42 -0300 Subject: [PATCH 46/57] . --- .github/workflows/documentation.yml | 1 + .../aequilibrae_matrix.rst | 57 ++++++++++++------- 2 files changed, 39 insertions(+), 19 deletions(-) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index fe9c62805..3a9b3c5ab 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -48,6 +48,7 @@ jobs: python -m pytest --doctest-modules aequilibrae/log.py aequilibrae/parameters.py aequilibrae/paths/vdf.py python -m pytest docs/source/modeling_with_aequilibrae/project_components.rst --doctest-glob=*.rst python -m pytest docs/source/modeling_with_aequilibrae/accessing_project_data.rst --doctest-glob=*.rst + python -m pytest docs/source/modeling_with_aequilibrae/aequilibrae_matrix.rst --doctest-glob=*.rst - name: Build documentation run: | diff --git a/docs/source/modeling_with_aequilibrae/aequilibrae_matrix.rst b/docs/source/modeling_with_aequilibrae/aequilibrae_matrix.rst index f7d8777ab..27e7efffb 100644 --- a/docs/source/modeling_with_aequilibrae/aequilibrae_matrix.rst +++ b/docs/source/modeling_with_aequilibrae/aequilibrae_matrix.rst @@ -21,37 +21,49 @@ There are three ways of creating an ``AequilibraeMatrix``: .. code-block:: python - from aequilibrae.matrix import AequilibraeMatrix + >>> import numpy as np + >>> from uuid import uuid4 + >>> from os.path import join + >>> from aequilibrae.matrix import AequilibraeMatrix - file = "folder/path_to_my_matrix.aem" - num_zones = 10 + >>> folder = f"/tmp/{uuid4().hex}" + + >>> file = join(folder, "path_to_my_matrix.aem") + >>> num_zones = 5 + >>> index = np.arange(1, 6, dtype=np.int32) + >>> mtx = np.ones((5, 5), dtype=np.float32) + >>> names = ["only_ones"] - mat = AequilibraeMatrix() - mat.create_empty(file_name=file, zones=num_zones, memory_only=False) + >>> mat = AequilibraeMatrix() + >>> mat.create_empty(file_name=file, zones=num_zones, matrix_names=names) # `memory_only` parameter can be changed to `True` case you want to save the matrix in disk. # Adds the matrix indexes, which are going to be used for computation - mat.index[:] = index[:] - # Adds the matricial data stored in `mtx` to a matrix named "my_matrix" - mat.matrix["my_matrix"][:,:] = mtx[:,:] + >>> mat.index[:] = index[:] -Creating a matrix from an OMX file is also straightforward. - -.. code-block:: python - - mat = AequilibraeMatrix() - mat.create_from_omx(file_path, omx_path) + # Adds the matricial data stored in `mtx` to a matrix named "only_ones" + >>> mat.matrix["only_ones"][:,:] = mtx[:,:] The following methods allow you to check the data in you AequilibraE matrix. .. code-block:: python - mat.cores # displays the number of cores in the matrix - mat.names # displays the names of the matrices - mat.index # displays the IDs of the indexes + >>> mat.cores # displays the number of cores in the matrix + 1 + + >>> mat.names # displays the names of the matrices + ['only_ones'] - mat.get_matrix_name("name_of_a_matrix") # returns an array with the selected matrix data + >>> mat.index # displays the IDs of the indexes + array([1, 2, 3, 4, 5]) + + >>> mat.get_matrix("only_ones") # returns an array with the selected matrix data + array([[1., 1., 1., 1., 1.], + [1., 1., 1., 1., 1.], + [1., 1., 1., 1., 1.], + [1., 1., 1., 1., 1.], + [1., 1., 1., 1., 1.]]) More than storing project data, AequilibraE matrices are objects necessary to run procedures, such as traffic assignment. To do so, one must create a computational view of the matrix, which @@ -63,7 +75,7 @@ otherwise the matrix is useless in other procedures. .. code-block:: python - mat.computational_view(["my_matrix"]) + >>> mat.computational_view(["only_ones"]) You can also export AequilibraE matrices to another file formats, such as CSV and OMX. When exporting to a OMX file, you can choose the cores os the matrix you want to save, although this is not the case @@ -92,6 +104,13 @@ or to close the OMX file, if that's the case. mat.close() +Creating a matrix from an OMX file is also straightforward. + +.. code-block:: python + + mat = AequilibraeMatrix() + mat.create_from_omx(file_path, omx_path) + AequilibraE matrices saved in disk can be reused and loaded once again. .. code-block:: python From c90d6d4c38bd6cefb28167144451a9cbfa0e3a4b Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Fri, 13 Sep 2024 15:10:05 -0300 Subject: [PATCH 47/57] . --- docs/source/conf.py | 1 + .../accessing_project_data.rst | 6 +- .../aequilibrae_matrix.rst | 51 ++++++++++------ .../project_components.rst | 58 +++++-------------- 4 files changed, 54 insertions(+), 62 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index deb842c91..797084f0c 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -60,6 +60,7 @@ "sphinx.ext.mathjax", "sphinx.ext.viewcode", "sphinx.ext.autosummary", + "sphinx.ext.doctest", "sphinx_gallery.gen_gallery", "sphinx_design", "sphinx_copybutton", diff --git a/docs/source/modeling_with_aequilibrae/accessing_project_data.rst b/docs/source/modeling_with_aequilibrae/accessing_project_data.rst index 227743918..6e9948b4a 100644 --- a/docs/source/modeling_with_aequilibrae/accessing_project_data.rst +++ b/docs/source/modeling_with_aequilibrae/accessing_project_data.rst @@ -16,10 +16,10 @@ Each item in the 'links' table is a ``Link`` object. .. code-block:: python >>> from shapely.geometry import LineString - >>> from uuid import uuid4 >>> from aequilibrae.utils.create_example import create_example - >>> project = create_example(f"/tmp/{uuid4().hex}", "coquimbo") + >>> folder = getfixture("create_path") + >>> project = create_example(folder, "coquimbo") >>> project_links = project.network.links @@ -120,7 +120,7 @@ Each item in the 'nodes' table is a ``Node`` object. >>> # or if you want to check the coordinate of each node in the shape of >>> # a Pandas DataFrame >>> coords = project_nodes.lonlat - >>> coords.head(3) + >>> coords.head(3) # doctest: +NORMALIZE_WHITESPACE node_id lon lat 0 10037 -71.315117 -29.996804 1 10064 -71.336604 -29.949050 diff --git a/docs/source/modeling_with_aequilibrae/aequilibrae_matrix.rst b/docs/source/modeling_with_aequilibrae/aequilibrae_matrix.rst index 27e7efffb..3b57665de 100644 --- a/docs/source/modeling_with_aequilibrae/aequilibrae_matrix.rst +++ b/docs/source/modeling_with_aequilibrae/aequilibrae_matrix.rst @@ -22,11 +22,10 @@ There are three ways of creating an ``AequilibraeMatrix``: .. code-block:: python >>> import numpy as np - >>> from uuid import uuid4 >>> from os.path import join >>> from aequilibrae.matrix import AequilibraeMatrix - >>> folder = f"/tmp/{uuid4().hex}" + >>> folder = getfixture("tmp_path") >>> file = join(folder, "path_to_my_matrix.aem") >>> num_zones = 5 @@ -58,7 +57,8 @@ The following methods allow you to check the data in you AequilibraE matrix. >>> mat.index # displays the IDs of the indexes array([1, 2, 3, 4, 5]) - >>> mat.get_matrix("only_ones") # returns an array with the selected matrix data + # To return an array with the selected matrix data + >>> mat.get_matrix("only_ones") # doctest: +NORMALIZE_WHITESPACE array([[1., 1., 1., 1., 1.], [1., 1., 1., 1., 1.], [1., 1., 1., 1., 1.], @@ -83,16 +83,16 @@ for CSV file, in which all cores will be exported as separate columns in the out .. code-block:: python - mat.export('/tmp/my_new_path.omx') - # or - mat.export('/tmp/my_new_path.csv') + >>> mat.export(join(folder, 'my_new_omx_file.omx')) + + >>> mat.export(join(folder, 'my_new_csv_file.csv')) The ``export`` method also allows you to change your mind and save your AequilibraE matrix into an AEM file, if it's only in memory. .. code-block:: python - mat.export('/tmp/my_new_path.aem') + >>> mat.export(join(folder, 'my_new_aem_file.aem')) .. is there a better name rather than error? @@ -102,21 +102,21 @@ or to close the OMX file, if that's the case. .. code-block:: python - mat.close() + >>> mat.close() -Creating a matrix from an OMX file is also straightforward. +AequilibraE matrices in disk can be reused and loaded once again. .. code-block:: python - mat = AequilibraeMatrix() - mat.create_from_omx(file_path, omx_path) - -AequilibraE matrices saved in disk can be reused and loaded once again. - -.. code-block:: python + >>> mat = AequilibraeMatrix() + >>> mat.load(join(folder, 'my_new_aem_file.aem')) - mat = AequilibraeMatrix() - mat.load('/tmp/path_to_matrix.aem') + >>> mat.get_matrix("only_ones") # doctest: +NORMALIZE_WHITESPACE + memmap([[1., 1., 1., 1., 1.], + [1., 1., 1., 1., 1.], + [1., 1., 1., 1., 1.], + [1., 1., 1., 1., 1.], + [1., 1., 1., 1., 1.]]) .. important:: @@ -147,3 +147,20 @@ There are APIs in different programming languages that allow you to use OMX. In ``omx-python`` library. In its project page, you can find a `brief tutorial `_ to OMX, and better understand how does it work. + +Creating an AequilibraE matrix from an OMX file is pretty straightforward. + +.. code-block:: python + + >>> file_path = join(folder, "path_to_new_matrix.aem") + >>> omx_path = join(folder, 'my_new_omx_file.omx') + + >>> omx_mat = AequilibraeMatrix() + >>> omx_mat.create_from_omx(file_path, omx_path) + + >>> mat.get_matrix("only_ones") # doctest: +NORMALIZE_WHITESPACE + memmap([[1., 1., 1., 1., 1.], + [1., 1., 1., 1., 1.], + [1., 1., 1., 1., 1.], + [1., 1., 1., 1., 1.], + [1., 1., 1., 1., 1.]]) diff --git a/docs/source/modeling_with_aequilibrae/project_components.rst b/docs/source/modeling_with_aequilibrae/project_components.rst index 7d2195d0d..4abf47f88 100644 --- a/docs/source/modeling_with_aequilibrae/project_components.rst +++ b/docs/source/modeling_with_aequilibrae/project_components.rst @@ -31,6 +31,15 @@ enables the access to manipulate the 'links' table, and each item in the items t In this section, we'll briefly discuss about the project components without geo-spatial information. +.. testsetup:: * + + >>> from uuid import uuid4 + >>> from aequilibrae.utils.create_example import create_example + +.. testcleanup:: * + + >> project.close() + ``project.about`` ----------------- @@ -38,10 +47,7 @@ This class provides an interface for editing the 'about' table of a project. We edit the existing ones as necessary, but everytime you add or modify a field, you have to write back this information, otherwise it will be lost. -.. code-block:: python - - >>> from uuid import uuid4 - >>> from aequilibrae.utils.create_example import create_example +.. doctest:: >>> project = create_example(f"/tmp/{uuid4().hex}") @@ -64,8 +70,6 @@ this information, otherwise it will be lost. # it is possible to create one too. >>> project.about.create() - >>> project.close() - .. admonition:: References * :ref:`tables_about` @@ -86,10 +90,7 @@ The ``FieldEditor`` allows the user to edit the project data tables, and it has This class is directly accessed from within the corresponding module one wants to edit. -.. code-block:: python - - >>> from uuid import uuid4 - >>> from aequilibrae.utils.create_example import create_example +.. doctest:: >>> project = create_example(f"/tmp/{uuid4().hex}", "nauru") @@ -113,8 +114,6 @@ This class is directly accessed from within the corresponding module one wants t >>> node_fields.all_fields() # doctest: +ELLIPSIS ['is_centroid', ..., 'my_new_field'] - >>> project.close() - All field descriptions are kept in the table 'attributes_documentation'. .. admonition:: References @@ -133,10 +132,7 @@ All field descriptions are kept in the table 'attributes_documentation'. Every AequilibraE project contains a log file that holds information on all the project procedures. It is possible to access the log file contents, as presented in the next code block. -.. code-block:: python - - >>> from uuid import uuid4 - >>> from aequilibrae.utils.create_example import create_example +.. doctest:: >>> project = create_example(f"/tmp/{uuid4().hex}", "nauru") @@ -152,8 +148,6 @@ It is possible to access the log file contents, as presented in the next code bl # Use this option wiesly once the deletion of data in the log file can't be undone. >>> project_log.clear() - >>> project.close() - .. admonition:: References * :ref:`useful-log-tips` @@ -170,10 +164,7 @@ It is possible to access the log file contents, as presented in the next code bl This method ia a gateway to all the matrices available in the model, which allows us to update the records in the 'matrices' table. Each item in the 'matrices' table is a ``MatrixRecord`` object. -.. code-block:: python - - >>> from uuid import uuid4 - >>> from aequilibrae.utils.create_example import create_example +.. doctest:: >>> project = create_example(f"/tmp/{uuid4().hex}") @@ -210,8 +201,6 @@ records in the 'matrices' table. Each item in the 'matrices' table is a ``Matri # get an AequilibraE matrix. >>> matrices.get_matrix("demand_aem") # doctest: +SKIP - >>> project.close() - .. admonition:: References * :ref:`matrix_table` @@ -228,10 +217,7 @@ records in the 'matrices' table. Each item in the 'matrices' table is a ``Matri This method allows you to access the API resources to manipulate the 'link_types' table. Each item in the 'link_types' table is a ``LinkType`` object. -.. code-block:: python - - >>> from uuid import uuid4 - >>> from aequilibrae.utils.create_example import create_example +.. doctest:: >>> project = create_example(f"/tmp/{uuid4().hex}", "coquimbo") @@ -268,8 +254,6 @@ Each item in the 'link_types' table is a ``LinkType`` object. # or using the `link_type` >>> get_link = link_types.get_by_name("primary") - >>> project.close() - .. admonition:: References * :ref:`tables_link_types` @@ -286,10 +270,7 @@ Each item in the 'link_types' table is a ``LinkType`` object. This method allows you to access the API resources to manipulate the 'modes' table. Each item in 'modes' table is a ``Mode`` object. -.. code-block:: python - - >>> from uuid import uuid4 - >>> from aequilibrae.utils.create_example import create_example +.. doctest:: >>> project = create_example(f"/tmp/{uuid4().hex}", "coquimbo") @@ -321,8 +302,6 @@ Each item in 'modes' table is a ``Mode`` object. # or using the ``mode_name`` >>> get_mode = modes.get_by_name("car") - >>> project.close() - .. admonition:: References * :ref:`tables_modes` @@ -339,10 +318,7 @@ Each item in 'modes' table is a ``Mode`` object. This method allows you to access the API resources to manipulate the 'periods' table. Each item in the 'periods' table is a ``Period`` object. -.. code-block:: python - - >>> from uuid import uuid4 - >>> from aequilibrae.utils.create_example import create_example +.. doctest:: >>> project = create_example(f"/tmp/{uuid4().hex}", "coquimbo") @@ -378,8 +354,6 @@ Each item in the 'periods' table is a ``Period`` object. # of this example, we'll save the addition of a new period to our table right away >>> periods.save() - >>> project.close() - .. admonition:: References * :ref:`tables_period` From a2cd9ea040c79772e2822236a10c7c9cd355eaaf Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Fri, 13 Sep 2024 15:25:40 -0300 Subject: [PATCH 48/57] Update project_components.rst --- docs/source/modeling_with_aequilibrae/project_components.rst | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/source/modeling_with_aequilibrae/project_components.rst b/docs/source/modeling_with_aequilibrae/project_components.rst index 4abf47f88..2abe01ed6 100644 --- a/docs/source/modeling_with_aequilibrae/project_components.rst +++ b/docs/source/modeling_with_aequilibrae/project_components.rst @@ -139,10 +139,7 @@ It is possible to access the log file contents, as presented in the next code bl >>> project_log = project.log() # Returns a list with all entires in the log file. - >>> project_log.contents() # doctest: +ELLIPSIS - ['2021-01-01 15:52:03,945;aequilibrae;INFO ; Created project on D:/release/Sample models/nauru', - ..., - '2021-01-01 15:52:37,843;aequilibrae;INFO ; Network built successfully'] + >>> project_log.contents() # doctest: +SKIP # If your project's log is getting cluttered, it is possible to clear it. # Use this option wiesly once the deletion of data in the log file can't be undone. From fc8bbb7e103ef2883edad849dae3e1f50f815f19 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Fri, 13 Sep 2024 15:45:07 -0300 Subject: [PATCH 49/57] modifies doctest ellipsis --- .../source/modeling_with_aequilibrae/aequilibrae_matrix.rst | 6 +++--- .../source/modeling_with_aequilibrae/project_components.rst | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/source/modeling_with_aequilibrae/aequilibrae_matrix.rst b/docs/source/modeling_with_aequilibrae/aequilibrae_matrix.rst index 3b57665de..703e89a1d 100644 --- a/docs/source/modeling_with_aequilibrae/aequilibrae_matrix.rst +++ b/docs/source/modeling_with_aequilibrae/aequilibrae_matrix.rst @@ -58,7 +58,7 @@ The following methods allow you to check the data in you AequilibraE matrix. array([1, 2, 3, 4, 5]) # To return an array with the selected matrix data - >>> mat.get_matrix("only_ones") # doctest: +NORMALIZE_WHITESPACE + >>> mat.get_matrix("only_ones") # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE array([[1., 1., 1., 1., 1.], [1., 1., 1., 1., 1.], [1., 1., 1., 1., 1.], @@ -111,7 +111,7 @@ AequilibraE matrices in disk can be reused and loaded once again. >>> mat = AequilibraeMatrix() >>> mat.load(join(folder, 'my_new_aem_file.aem')) - >>> mat.get_matrix("only_ones") # doctest: +NORMALIZE_WHITESPACE + >>> mat.get_matrix("only_ones") # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE memmap([[1., 1., 1., 1., 1.], [1., 1., 1., 1., 1.], [1., 1., 1., 1., 1.], @@ -158,7 +158,7 @@ Creating an AequilibraE matrix from an OMX file is pretty straightforward. >>> omx_mat = AequilibraeMatrix() >>> omx_mat.create_from_omx(file_path, omx_path) - >>> mat.get_matrix("only_ones") # doctest: +NORMALIZE_WHITESPACE + >>> mat.get_matrix("only_ones") # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE memmap([[1., 1., 1., 1., 1.], [1., 1., 1., 1., 1.], [1., 1., 1., 1., 1.], diff --git a/docs/source/modeling_with_aequilibrae/project_components.rst b/docs/source/modeling_with_aequilibrae/project_components.rst index 2abe01ed6..882af834c 100644 --- a/docs/source/modeling_with_aequilibrae/project_components.rst +++ b/docs/source/modeling_with_aequilibrae/project_components.rst @@ -241,7 +241,7 @@ Each item in the 'link_types' table is a ``LinkType`` object. >>> link_types.save() # To check all `LinkTypes` in the project as a dictionary whose keys are the `link_type_id`'s - >>> link_types.all_types() # doctest: +ELLIPSIS + >>> link_types.all_types() # doctest: +SKIP {'z': ...} # There are two ways to get a LinkType from the 'link_types' table @@ -289,8 +289,7 @@ Each item in 'modes' table is a ``Mode`` object. >>> modes.delete("k") # To check all `Modes` in the project as a dictionary whose keys are the `mode_id`'s - >>> modes.all_modes() - {'b': ..., 'c': ..., 't': ..., 'w': ...} + >>> modes.all_modes() # doctest: +SKIP # There are two ways to get a Mode from the 'modes' table # using the ``mode_id`` From 6a6b17a75954790c89752409cd874c61f5aa3ca9 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Mon, 16 Sep 2024 10:41:17 -0300 Subject: [PATCH 50/57] print test string --- docs/source/modeling_with_aequilibrae/project_components.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/source/modeling_with_aequilibrae/project_components.rst b/docs/source/modeling_with_aequilibrae/project_components.rst index 882af834c..5051c8347 100644 --- a/docs/source/modeling_with_aequilibrae/project_components.rst +++ b/docs/source/modeling_with_aequilibrae/project_components.rst @@ -139,7 +139,8 @@ It is possible to access the log file contents, as presented in the next code bl >>> project_log = project.log() # Returns a list with all entires in the log file. - >>> project_log.contents() # doctest: +SKIP + >>> print(project_log.contents()) # doctest: +ELLIPSIS + ['2021-01-01 15:52:03,945;aequilibrae;INFO ; Created project on D:/release/Sample models/nauru', ...] # If your project's log is getting cluttered, it is possible to clear it. # Use this option wiesly once the deletion of data in the log file can't be undone. From 12c22cb2ea3857213ba899ca47732d4c17388e01 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Mon, 16 Sep 2024 11:30:37 -0300 Subject: [PATCH 51/57] modify switcher --- docs/source/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 797084f0c..2e5323917 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -136,8 +136,8 @@ "navbar_start": ["navbar-logo"], "navbar_align": "left", "switcher": { - "json_url": "_static/switcher.json", - "version_match": switcher_version, + "json_url": "https://www.aequilibrae.com/python/latest/_static/switcher.json", + "version_match": version, }, # "check_switcher": False, "github_url": "https://github.com/AequilibraE/aequilibrae", From 10b11b7a9ddb1b19573cf59cd22a673d82df8229 Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Mon, 16 Sep 2024 14:41:57 -0300 Subject: [PATCH 52/57] change project structure --- .github/workflows/documentation.yml | 4 +- .gitignore | 4 +- docs/source/conf.py | 7 +- .../trip_distribution/just_matrices.omx | Bin 15369 -> 0 bytes .../select_link_analysis.omx | Bin 15429 -> 0 bytes .../data_model/transit_graph_configs.rst | 29 --------- .../project_database/data_model/zones.rst | 46 ------------- .../modeling_with_aequilibrae/index.rst | 6 +- .../parameter_file.rst | 14 ++-- .../index.rst => project.rst} | 61 ++++++------------ .../project_database/about.rst | 4 +- .../attributes_documentation.rst | 4 +- .../project_database/datamodel.rst.template | 2 - .../project_database/index.rst | 19 ++++-- .../project_database/link_types.rst | 4 +- .../project_database/matrices.rst | 6 +- .../project_database/modes.rst | 4 +- .../project_database/network.rst | 4 -- .../project_database/network_geometry.rst | 51 +++++++-------- .../network_import_and_export.rst | 6 +- .../project_database/periods.rst | 3 - .../results.rst} | 2 - .../project_database/transit_graph.rst | 2 - .../project_database/zones.rst | 4 +- .../accessing_project_data.rst | 5 +- .../aequilibrae_matrix.rst | 2 +- .../project_pieces/index.rst | 11 ++++ .../project_components.rst | 6 +- .../transit_database/datamodel.rst.template | 2 - .../transit_database/index.rst | 40 ++---------- docs/table_documentation.py | 20 ++---- 31 files changed, 112 insertions(+), 260 deletions(-) delete mode 100644 docs/source/examples/trip_distribution/just_matrices.omx delete mode 100644 docs/source/examples/trip_distribution/select_link_analysis.omx delete mode 100644 docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/data_model/transit_graph_configs.rst delete mode 100644 docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/data_model/zones.rst rename docs/source/modeling_with_aequilibrae/{aequilibrae_project => }/parameter_file.rst (95%) rename docs/source/modeling_with_aequilibrae/{aequilibrae_project/index.rst => project.rst} (90%) rename docs/source/modeling_with_aequilibrae/{aequilibrae_project => }/project_database/about.rst (92%) rename docs/source/modeling_with_aequilibrae/{aequilibrae_project => }/project_database/attributes_documentation.rst (84%) rename docs/source/modeling_with_aequilibrae/{aequilibrae_project => }/project_database/datamodel.rst.template (98%) rename docs/source/modeling_with_aequilibrae/{aequilibrae_project => }/project_database/index.rst (57%) rename docs/source/modeling_with_aequilibrae/{aequilibrae_project => }/project_database/link_types.rst (97%) rename docs/source/modeling_with_aequilibrae/{aequilibrae_project => }/project_database/matrices.rst (85%) rename docs/source/modeling_with_aequilibrae/{aequilibrae_project => }/project_database/modes.rst (97%) rename docs/source/modeling_with_aequilibrae/{aequilibrae_project => }/project_database/network.rst (95%) rename docs/source/modeling_with_aequilibrae/{aequilibrae_project => }/project_database/network_geometry.rst (88%) rename docs/source/modeling_with_aequilibrae/{aequilibrae_project => }/project_database/network_import_and_export.rst (97%) rename docs/source/modeling_with_aequilibrae/{aequilibrae_project => }/project_database/periods.rst (74%) rename docs/source/modeling_with_aequilibrae/{aequilibrae_project/results_database.rst => project_database/results.rst} (92%) rename docs/source/modeling_with_aequilibrae/{aequilibrae_project => }/project_database/transit_graph.rst (84%) rename docs/source/modeling_with_aequilibrae/{aequilibrae_project => }/project_database/zones.rst (89%) rename docs/source/modeling_with_aequilibrae/{ => project_pieces}/accessing_project_data.rst (99%) rename docs/source/modeling_with_aequilibrae/{ => project_pieces}/aequilibrae_matrix.rst (99%) create mode 100644 docs/source/modeling_with_aequilibrae/project_pieces/index.rst rename docs/source/modeling_with_aequilibrae/{ => project_pieces}/project_components.rst (99%) rename docs/source/modeling_with_aequilibrae/{aequilibrae_project => }/transit_database/datamodel.rst.template (98%) rename docs/source/modeling_with_aequilibrae/{aequilibrae_project => }/transit_database/index.rst (59%) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 3a9b3c5ab..6db583769 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -46,9 +46,7 @@ jobs: - name: Test docstrings run: | python -m pytest --doctest-modules aequilibrae/log.py aequilibrae/parameters.py aequilibrae/paths/vdf.py - python -m pytest docs/source/modeling_with_aequilibrae/project_components.rst --doctest-glob=*.rst - python -m pytest docs/source/modeling_with_aequilibrae/accessing_project_data.rst --doctest-glob=*.rst - python -m pytest docs/source/modeling_with_aequilibrae/aequilibrae_matrix.rst --doctest-glob=*.rst + python -m pytest docs/source/modeling_with_aequilibrae/project_pieces --doctest-glob=*.rst - name: Build documentation run: | diff --git a/.gitignore b/.gitignore index 7421c20e0..2153bfdde 100644 --- a/.gitignore +++ b/.gitignore @@ -5,8 +5,8 @@ _templates docs/source/sg_execution_times.rst docs/source/_auto_examples docs/source/api/generated -docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/data_model -docs/source/modeling_with_aequilibrae/aequilibrae_project/transit_database/data_model +docs/source/modeling_with_aequilibrae/project_database/data_model +docs/source/modeling_with_aequilibrae/transit_database/data_model # User-specific stuff: .idea/**/workspace.xml diff --git a/docs/source/conf.py b/docs/source/conf.py index 2e5323917..cd90b2050 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -177,12 +177,7 @@ # -- Options for Texinfo output ---------------------------------------------- -autodoc_default_options = { - "members": True, - "inherited-members": True, - "undoc-members": True, - 'autosummary': True -} +autodoc_default_options = {"members": True, "inherited-members": True, "undoc-members": True, "autosummary": True} autodoc_member_order = "groupwise" autoclass_content = "class" # classes should include both the class' and the __init__ method's docstring diff --git a/docs/source/examples/trip_distribution/just_matrices.omx b/docs/source/examples/trip_distribution/just_matrices.omx deleted file mode 100644 index 67345d255a02727ade6b2651d26ef504612dab44..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15369 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 diff --git a/docs/source/examples/trip_distribution/select_link_analysis.omx b/docs/source/examples/trip_distribution/select_link_analysis.omx deleted file mode 100644 index 2c97a0e39ca4078feedf28f91dbd550d4881afaf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15429 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 diff --git a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/data_model/transit_graph_configs.rst b/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/data_model/transit_graph_configs.rst deleted file mode 100644 index 44332333e..000000000 --- a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/data_model/transit_graph_configs.rst +++ /dev/null @@ -1,29 +0,0 @@ -*transit_graph_configs* table structure ---------------------------------------- - -The *transit_graph_configs* table holds configuration parameters for a TransitGraph of a particular `period_id` -Attributes follow - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - period_id*,INTEGER,NO, - config,TEXT,YES, - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - CREATE TABLE if not exists transit_graph_configs (period_id INTEGER UNIQUE NOT NULL PRIMARY KEY REFERENCES periods(period_id), - config TEXT); - - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('transit_graph_configs','period_id', 'The period this config is associated with.'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('transit_graph_configs','mode_id', 'JSON string containing the configuration parameters.'); diff --git a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/data_model/zones.rst b/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/data_model/zones.rst deleted file mode 100644 index 34d6d0393..000000000 --- a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/data_model/zones.rst +++ /dev/null @@ -1,46 +0,0 @@ -*zones* table structure ------------------------ - -The *zones* table holds information on the Traffic Analysis Zones (TAZs) -in AequilibraE's model. - -The **zone_id** field identifies the zone. - -The **area** field corresponds to the area of the zone in **km2**. -TAZs' area is automatically updated by triggers. - -The **name** fields allows one to identity the zone using a name -or any other description. - -.. csv-table:: - :header: "Field", "Type", "NULL allowed", "Default Value" - :widths: 30, 20, 20, 20 - - ogc_fid*,INTEGER,YES, - zone_id,INTEGER,NO, - area,NUMERIC,YES, - name,TEXT,YES, - geometry,MULTIPOLYGON,NO,'' - - -(* - Primary key) - - - -The SQL statement for table and index creation is below. - - -:: - - - CREATE TABLE 'zones' (ogc_fid INTEGER PRIMARY KEY, - zone_id INTEGER UNIQUE NOT NULL, - area NUMERIC, - "name" TEXT); - - SELECT AddGeometryColumn( 'zones', 'geometry', 4326, 'MULTIPOLYGON', 'XY', 1); - CREATE UNIQUE INDEX idx_zone ON zones (zone_id); - SELECT CreateSpatialIndex( 'zones' , 'geometry' ); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('zones','zone_id', 'Unique node ID'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('zones','area', 'Area of the zone in km2'); - INSERT INTO 'attributes_documentation' (name_table, attribute, description) VALUES('zones','name', 'Name of the zone, if any'); diff --git a/docs/source/modeling_with_aequilibrae/index.rst b/docs/source/modeling_with_aequilibrae/index.rst index 2b21d2a4d..f57d77dd3 100644 --- a/docs/source/modeling_with_aequilibrae/index.rst +++ b/docs/source/modeling_with_aequilibrae/index.rst @@ -18,7 +18,11 @@ a start guide to a complete view into AequilibraE's data structure. :maxdepth: 1 :caption: A guide to AequilibraE - aequilibrae_project/index + project + project_database/index + parameter_file + transit_database/index + project_pieces/index static_traffic_assignment/index transit_assignment/index route_choice/index \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/aequilibrae_project/parameter_file.rst b/docs/source/modeling_with_aequilibrae/parameter_file.rst similarity index 95% rename from docs/source/modeling_with_aequilibrae/aequilibrae_project/parameter_file.rst rename to docs/source/modeling_with_aequilibrae/parameter_file.rst index 82bd3b8e2..73b77669f 100644 --- a/docs/source/modeling_with_aequilibrae/aequilibrae_project/parameter_file.rst +++ b/docs/source/modeling_with_aequilibrae/parameter_file.rst @@ -14,7 +14,7 @@ The assignment section of the parameter file is the smallest one, and it contains only the convergence criteria for assignment in terms of the maximum number of iterations and target Relative Gap. -.. image:: ../../images/parameters_assignment_example.png +.. image:: ../images/parameters_assignment_example.png :align: center :scale: 80 % :alt: Assignment example @@ -32,7 +32,7 @@ contains only the parameters for number of maximum iterations, convergence level and maximum trip length to be applied in Iterative Proportional Fitting and synthetic gravity models, as shown below. -.. image:: ../../images/parameters_distribution_example.png +.. image:: ../images/parameters_distribution_example.png :align: center :scale: 80 % :alt: Distribution example @@ -66,7 +66,7 @@ The data types available are those that exist within the `SQLite specification `_ . We recommend limiting yourself to the use of **integer**, **numeric** and **varchar**. -.. image:: ../../images/parameters_links_example.png +.. image:: ../images/parameters_links_example.png :align: center :scale: 80 % :alt: Link example @@ -91,7 +91,7 @@ forward/backward values tagged). For this reason, one can use the parameter 'osm to define what to do with numeric tag values that have not been tagged for both directions. the allowed values for this parameter are **copy** and **divide**, as shown below. -.. image:: ../../images/parameters_links_osm_behaviour.png +.. image:: ../images/parameters_links_osm_behaviour.png :align: center :scale: 80 % :alt: OSM behaviour examples @@ -132,7 +132,7 @@ GMNS The **GMNS** group of parameters has four specifications: **critical_dist**, **link**, **node**, and **use_definition**. -.. image:: ../../images/parameter_yaml_files_gmns.png +.. image:: ../images/parameter_yaml_files_gmns.png :align: center :alt: GMNS parameter group @@ -154,7 +154,7 @@ number of threads used in multi-threaded processes, logging and temp folders and whether we should be saving information to a log file at all, as exemplified below. -.. image:: ../../images/parameters_system_example.png +.. image:: ../images/parameters_system_example.png :align: center :scale: 80 % :alt: System example @@ -180,7 +180,7 @@ The OSM section of the parameter file is relevant only when one plans to download a substantial amount of data from an Overpass API, in which case it is recommended to deploy a local Overpass server. -.. image:: ../../images/parameters_osm_example.png +.. image:: ../images/parameters_osm_example.png :align: center :scale: 80 % :alt: OSM example diff --git a/docs/source/modeling_with_aequilibrae/aequilibrae_project/index.rst b/docs/source/modeling_with_aequilibrae/project.rst similarity index 90% rename from docs/source/modeling_with_aequilibrae/aequilibrae_project/index.rst rename to docs/source/modeling_with_aequilibrae/project.rst index 8d700aedc..d143d1721 100644 --- a/docs/source/modeling_with_aequilibrae/aequilibrae_project/index.rst +++ b/docs/source/modeling_with_aequilibrae/project.rst @@ -27,31 +27,6 @@ please refer to :ref:`dependencies`. For QGIS users this is not a concern, while for Windows users this dependency is automatically handled under the hood, but the details are also discussed in the aforementioned dependencies section. -Package components: A conceptual view -------------------------------------- - -As all the components of an AequilibraE model are based on open-source software and -open-data standards, modeling with AequilibraE is a little different from -modeling with commercial packages, as the user can read and manipulate model -components outside the software modeling environments (Python and QGIS). - -Thus, using/manipulating each one of an AequilibraE model components can be done -in different ways depending on the tool you use for such. - -It is then important to highlight that AequilibraE, as a software, is divided in -three very distinctive layers. The first, which is responsible for tables -consistent with each other (including links and nodes, modes and link_types), -are embedded in the data layer in the form of geo-spatial database triggers. The -second is the Python API, which provides all of AequilibraE's core algorithms -and data manipulation facilities. The third is the GUI implemented in QGIS, -which provides a user-friendly interface to access the model, visualize results -and run procedures. - -These software layers are *stacked* and depend on each other, which means that any -network editing done in SQLite, Python or QGIS will go through the SpatiaLite triggers, -while any procedure such as traffic assignment done in QGIS is nothing more than an -API call to the corresponding Python method. - .. _aeq_project_structure: Project structure @@ -61,7 +36,7 @@ Since version 0.7, the AequilibraE project consists of a main folder, where a series of files and sub folders exist, and the current project organization is as follows: -.. image:: ../../images/project_structure.png +.. image:: ../images/project_structure.png :scale: 25 % :align: center :alt: AequilibraE project structure @@ -100,21 +75,27 @@ also created *on-the-fly* when the user imports a GTFS source into an Aequilibra model, but there is still no support for manually or programmatically adding routes to a route system as of yet. -The following sections presents the structure and contents for all databases in the project. +Package components: A conceptual view +------------------------------------- -.. toctree:: - :maxdepth: 1 - :caption: Get to know the project components +As all the components of an AequilibraE model are based on open-source software and +open-data standards, modeling with AequilibraE is a little different from +modeling with commercial packages, as the user can read and manipulate model +components outside the software modeling environments (Python and QGIS). - ../project_components - ../accessing_project_data - ../aequilibrae_matrix +Thus, using/manipulating each one of an AequilibraE model components can be done +in different ways depending on the tool you use for such. -.. toctree:: - :maxdepth: 1 - :caption: Or dive deep into project structure! +It is then important to highlight that AequilibraE, as a software, is divided in +three very distinctive layers. The first, which is responsible for tables +consistent with each other (including links and nodes, modes and link_types), +are embedded in the data layer in the form of geo-spatial database triggers. The +second is the Python API, which provides all of AequilibraE's core algorithms +and data manipulation facilities. The third is the GUI implemented in QGIS, +which provides a user-friendly interface to access the model, visualize results +and run procedures. - project_database/index - parameter_file - results_database - transit_database/index +These software layers are *stacked* and depend on each other, which means that any +network editing done in SQLite, Python or QGIS will go through the SpatiaLite triggers, +while any procedure such as traffic assignment done in QGIS is nothing more than an +API call to the corresponding Python method. \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/about.rst b/docs/source/modeling_with_aequilibrae/project_database/about.rst similarity index 92% rename from docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/about.rst rename to docs/source/modeling_with_aequilibrae/project_database/about.rst index 5a2ee701e..9656f0e50 100644 --- a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/about.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/about.rst @@ -19,7 +19,7 @@ We strongly recommend not to edit the information on **projection** and the software to produce valuable information to the user with regards to opportunities for version upgrades. -.. image:: ../../../images/about_table_example.png +.. image:: ../../images/about_table_example.png :alt: About table structure :align: center @@ -28,5 +28,3 @@ An API for editing the contents of this database is available from the API docum .. seealso:: :func:`aequilibrae.project.About` - -.. include:: data_model/about.rst \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/attributes_documentation.rst b/docs/source/modeling_with_aequilibrae/project_database/attributes_documentation.rst similarity index 84% rename from docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/attributes_documentation.rst rename to docs/source/modeling_with_aequilibrae/project_database/attributes_documentation.rst index 5ac56b796..8fb9327ff 100644 --- a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/attributes_documentation.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/attributes_documentation.rst @@ -12,12 +12,10 @@ itself. As a simple table, it looks as follows: -.. image:: ../../../images/attributes_documentation.png +.. image:: ../../images/attributes_documentation.png :align: center :alt: attributes documentation table .. seealso:: :func:`aequilibrae.project.FieldEditor` - -.. include:: data_model/attributes_documentation.rst \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/datamodel.rst.template b/docs/source/modeling_with_aequilibrae/project_database/datamodel.rst.template similarity index 98% rename from docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/datamodel.rst.template rename to docs/source/modeling_with_aequilibrae/project_database/datamodel.rst.template index 5cecdc360..b3980a814 100644 --- a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/datamodel.rst.template +++ b/docs/source/modeling_with_aequilibrae/project_database/datamodel.rst.template @@ -1,5 +1,3 @@ -:orphan: - .. _supply_data_model: SQL Data model diff --git a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/index.rst b/docs/source/modeling_with_aequilibrae/project_database/index.rst similarity index 57% rename from docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/index.rst rename to docs/source/modeling_with_aequilibrae/project_database/index.rst index c1036495a..6b64203b7 100644 --- a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/index.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/index.rst @@ -10,12 +10,21 @@ an AequilibraE model you intend to use in anger. .. toctree:: :maxdepth: 1 - about network - modes link_types - matrices + modes zones - attributes_documentation + matrices + results periods - transit_graph \ No newline at end of file + transit_graph + about + attributes_documentation + +A more technical view of the database structure, including the SQL queries used to create each table and the indices used for each table are also available. + +.. toctree:: + :caption: Get to know the data structures in the project database! + :maxdepth: 1 + + data_model/datamodel.rst \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/link_types.rst b/docs/source/modeling_with_aequilibrae/project_database/link_types.rst similarity index 97% rename from docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/link_types.rst rename to docs/source/modeling_with_aequilibrae/project_database/link_types.rst index a86292f96..54c644421 100644 --- a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/link_types.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/link_types.rst @@ -33,7 +33,7 @@ Adding new link types to a project Adding link types to a project can be done through the Python API or directly into the 'link_types' table, which could look like the following. -.. image:: ../../../images/link_types_table.png +.. image:: ../../images/link_types_table.png :align: center :alt: Link_types table structure @@ -121,5 +121,3 @@ required. .. seealso:: :func:`aequilibrae.project.network.LinkTypes` - -.. include:: data_model/link_types.rst \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/matrices.rst b/docs/source/modeling_with_aequilibrae/project_database/matrices.rst similarity index 85% rename from docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/matrices.rst rename to docs/source/modeling_with_aequilibrae/project_database/matrices.rst index 0a4b4f004..291b15748 100644 --- a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/matrices.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/matrices.rst @@ -11,7 +11,7 @@ This index, which looks like below, has two main columns. The first one is the AequilibraE to find the file, and **name**, which is the name by which the user should refer to the matrix in order to access it through the API. -.. image:: ../../../images/matrices_table.png +.. image:: ../../images/matrices_table.png :align: center :alt: Matrices table structure @@ -22,6 +22,4 @@ types (AEM and OMX) without prejudice to functionality. :func:`aequilibrae.project.Matrices` - :func:`aequilibrae.matrix.AequilibraeMatrix` - -.. include:: data_model/matrices.rst \ No newline at end of file + :func:`aequilibrae.matrix.AequilibraeMatrix` \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/modes.rst b/docs/source/modeling_with_aequilibrae/project_database/modes.rst similarity index 97% rename from docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/modes.rst rename to docs/source/modeling_with_aequilibrae/project_database/modes.rst index 1441a00a1..8ee7ffd43 100644 --- a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/modes.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/modes.rst @@ -16,7 +16,7 @@ As described in the SQL data model, all AequilibraE models are created with 4 standard modes, which can be added to or removed by the user, and would look like the following. -.. image:: ../../../images/modes_table.png +.. image:: ../../images/modes_table.png :align: center :alt: Modes table structure @@ -98,5 +98,3 @@ required. .. seealso:: :func:`aequilibrae.project.network.Modes` - -.. include:: data_model/modes.rst \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/network.rst b/docs/source/modeling_with_aequilibrae/project_database/network.rst similarity index 95% rename from docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/network.rst rename to docs/source/modeling_with_aequilibrae/project_database/network.rst index d9cc69b84..94d4f4801 100644 --- a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/network.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/network.rst @@ -33,7 +33,3 @@ other changes to the layers or preventing the changes. network_import_and_export.rst network_geometry.rst - -.. include:: data_model/links.rst - -.. include:: data_model/nodes.rst diff --git a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/network_geometry.rst b/docs/source/modeling_with_aequilibrae/project_database/network_geometry.rst similarity index 88% rename from docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/network_geometry.rst rename to docs/source/modeling_with_aequilibrae/project_database/network_geometry.rst index 8fe8f80fc..e1edf21aa 100644 --- a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/network_geometry.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/network_geometry.rst @@ -69,6 +69,7 @@ can do to links and nodes, and you can use the table below to navigate between each of the changes to see how they are treated through triggers. .. table:: + :width: 60% :align: center +--------------------------------------+-----------------------------------+ @@ -151,26 +152,25 @@ Moving a node There are two possibilities for moving a node: Moving to an empty space, and moving on top of another node. -- If a node is moved to an empty space +- If a node is moved to an empty space - + all links originated/ending at that node will + have its shape altered to conform + to that new node position and keep the network connected. The alteration of the + link happens only by changing the Latitude and Longitude of the link extremity + associated with that node. - All links originated/ending at that node will have its shape altered to conform - to that new node position and keep the network connected. The alteration of the - link happens only by changing the Latitude and Longitude of the link extremity - associated with that node. - -- If a node is moved on top of another node - - All the links that connected to the node on the bottom have their extremities - switched to the node on top - The node on the bottom gets eliminated as a consequence of the behavior listed - on *Deleting a node* +- If a node is moved on top of another node - + All the links that connected to the node on the bottom have their extremities + switched to the node on top. + The node on the bottom gets eliminated as a consequence of the behavior listed + on *Deleting a node* Behavior regarding the fields related to modes and link types is discussed in their respective table descriptions. .. seealso:: - :ref:`Example - Editing network nodes ` + :ref:`Example - Editing network nodes ` Adding a data field +++++++++++++++++++ @@ -215,20 +215,17 @@ Moving a link extremity This change can happen in two different forms: -- The link extremity is moved to an empty space - - In this case, a new node needs to be created, according to the behavior - described in *Creating a node* . The information of node ID (A or B - node, depending on the extremity) needs to be updated according to the ID for - the new node created. - -- The link extremity is moved from one node to another - - The information of node ID (A or B node, depending on the extremity) needs to be - updated according to the ID for the node the link now terminates in. - - Behavior regarding the fields regarding modes and link types is discussed in - their respective table descriptions. +- The link extremity is moved to an empty space - + In this case, a new node needs to be created, according to the behavior + described in *Creating a node*. The information of node ID (A or B + node, depending on the extremity) needs to be updated according to the ID for + the new node created. + +- The link extremity is moved from one node to another - + The information of node ID (A or B node, depending on the extremity) needs to be + updated according to the ID for the node the link now terminates in. + Behavior regarding the fields regarding modes and link types is discussed in + their respective table descriptions. .. seealso:: diff --git a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/network_import_and_export.rst b/docs/source/modeling_with_aequilibrae/project_database/network_import_and_export.rst similarity index 97% rename from docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/network_import_and_export.rst rename to docs/source/modeling_with_aequilibrae/project_database/network_import_and_export.rst index e393151b9..5d227e0d4 100644 --- a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/network_import_and_export.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/network_import_and_export.rst @@ -49,7 +49,7 @@ layers to the canvas, and selecting properties and clicking on *create spatial* *index* for each layer at a time. This action automatically saves the spatial indices to the sqlite database. -.. image:: ../../../images/qgis_creating_spatial_indices.png +.. image:: ../../images/qgis_creating_spatial_indices.png :align: center :alt: Adding Spatial indices with QGIS @@ -97,7 +97,7 @@ Before importing a network from a source in GMNS format, it is imperative to kno in which spatial reference its geometries (links and nodes) were created. If the SRID is different than 4326, it must be passed as an input using the argument 'srid'. -.. image:: ../../../images/plot_import_from_gmns.png +.. image:: ../../images/plot_import_from_gmns.png :align: center :alt: example :target: ../../../_auto_examples/plot_import_from_gmns.html @@ -141,7 +141,7 @@ Exporting AequilibraE model to GMNS format After loading an existing AequilibraE project, you can export it to GMNS format. -.. image:: ../../../images/plot_export_to_gmns.png +.. image:: ../../images/plot_export_to_gmns.png :align: center :alt: example :target: export_to_gmns diff --git a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/periods.rst b/docs/source/modeling_with_aequilibrae/project_database/periods.rst similarity index 74% rename from docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/periods.rst rename to docs/source/modeling_with_aequilibrae/project_database/periods.rst index 082ae8d8d..fe688c3cd 100644 --- a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/periods.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/periods.rst @@ -6,6 +6,3 @@ Periods table .. seealso:: * :func:`aequilibrae.project.network.Periods` - -.. include:: data_model/periods.rst - diff --git a/docs/source/modeling_with_aequilibrae/aequilibrae_project/results_database.rst b/docs/source/modeling_with_aequilibrae/project_database/results.rst similarity index 92% rename from docs/source/modeling_with_aequilibrae/aequilibrae_project/results_database.rst rename to docs/source/modeling_with_aequilibrae/project_database/results.rst index 5d70b28f3..d1e933b5f 100644 --- a/docs/source/modeling_with_aequilibrae/aequilibrae_project/results_database.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/results.rst @@ -17,5 +17,3 @@ As a simple table, it looks as follows: .. image:: ../../images/results_table.png :align: center :alt: results table structure - -.. include:: project_database/data_model/results.rst \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/transit_graph.rst b/docs/source/modeling_with_aequilibrae/project_database/transit_graph.rst similarity index 84% rename from docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/transit_graph.rst rename to docs/source/modeling_with_aequilibrae/project_database/transit_graph.rst index ea87d5248..00ed9e744 100644 --- a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/transit_graph.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/transit_graph.rst @@ -7,5 +7,3 @@ The 'transit_graph_configs' table holds information on the configuration paramet .. seealso:: * :func:`aequilibtae.transit.TransitGraphBuilder` - -.. include:: data_model/transit_graph_configs.rst \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/zones.rst b/docs/source/modeling_with_aequilibrae/project_database/zones.rst similarity index 89% rename from docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/zones.rst rename to docs/source/modeling_with_aequilibrae/project_database/zones.rst index 5849c7d01..d6d24e7aa 100644 --- a/docs/source/modeling_with_aequilibrae/aequilibrae_project/project_database/zones.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/zones.rst @@ -17,6 +17,4 @@ You can check :ref:`this example ` to learn how to add zones to yo .. seealso:: - :func:`aequilibrae.project.Zone` - -.. include:: data_model/zones.rst \ No newline at end of file + :func:`aequilibrae.project.Zone` \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/accessing_project_data.rst b/docs/source/modeling_with_aequilibrae/project_pieces/accessing_project_data.rst similarity index 99% rename from docs/source/modeling_with_aequilibrae/accessing_project_data.rst rename to docs/source/modeling_with_aequilibrae/project_pieces/accessing_project_data.rst index 6e9948b4a..32424063f 100644 --- a/docs/source/modeling_with_aequilibrae/accessing_project_data.rst +++ b/docs/source/modeling_with_aequilibrae/project_pieces/accessing_project_data.rst @@ -13,12 +13,15 @@ cover the main points regarding them. This method allows you to access the API resources to manipulate the 'links' table. Each item in the 'links' table is a ``Link`` object. +.. testsetup:: * + + >>> folder = getfixture("create_path") + .. code-block:: python >>> from shapely.geometry import LineString >>> from aequilibrae.utils.create_example import create_example - >>> folder = getfixture("create_path") >>> project = create_example(folder, "coquimbo") >>> project_links = project.network.links diff --git a/docs/source/modeling_with_aequilibrae/aequilibrae_matrix.rst b/docs/source/modeling_with_aequilibrae/project_pieces/aequilibrae_matrix.rst similarity index 99% rename from docs/source/modeling_with_aequilibrae/aequilibrae_matrix.rst rename to docs/source/modeling_with_aequilibrae/project_pieces/aequilibrae_matrix.rst index 703e89a1d..e55b1e5f5 100644 --- a/docs/source/modeling_with_aequilibrae/aequilibrae_matrix.rst +++ b/docs/source/modeling_with_aequilibrae/project_pieces/aequilibrae_matrix.rst @@ -18,7 +18,7 @@ There are three ways of creating an ``AequilibraeMatrix``: * from a trip list, which is nothing more than a CSV file containing the origins, destinations, and trip cores; * from an empty matrix. In this case, the data type must be one of the following NumPy data types: ``np.int32``, ``np.int64``, ``np.float32``, ``np.float64``. - + .. code-block:: python >>> import numpy as np diff --git a/docs/source/modeling_with_aequilibrae/project_pieces/index.rst b/docs/source/modeling_with_aequilibrae/project_pieces/index.rst new file mode 100644 index 000000000..33afb45b4 --- /dev/null +++ b/docs/source/modeling_with_aequilibrae/project_pieces/index.rst @@ -0,0 +1,11 @@ +Components of an AequilibraE Project +==================================== + +In this section, we present the main modules related to adding data to an AequilibraE project. + +.. toctree:: + :maxdepth: 1 + + project_components + accessing_project_data + aequilibrae_matrix \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/project_components.rst b/docs/source/modeling_with_aequilibrae/project_pieces/project_components.rst similarity index 99% rename from docs/source/modeling_with_aequilibrae/project_components.rst rename to docs/source/modeling_with_aequilibrae/project_pieces/project_components.rst index 5051c8347..b48aa61e8 100644 --- a/docs/source/modeling_with_aequilibrae/project_components.rst +++ b/docs/source/modeling_with_aequilibrae/project_pieces/project_components.rst @@ -1,7 +1,7 @@ .. _project_components: -Project Components -================== +Data Components +======================= In the :ref:`aeq_project_structure` section, we present the main file components and folders that consists an AequilibraE project. We also present in :ref:`aeq_project_database_tables` all tables @@ -25,7 +25,7 @@ object with similar name that corresponds to one object in the class. Thus ``pro enables the access to manipulate the 'links' table, and each item in the items table is a ``Link`` object. -.. image:: ../images/project_components_and_items.png +.. image:: ../../images/project_components_and_items.png :align: center :alt: basics on project components diff --git a/docs/source/modeling_with_aequilibrae/aequilibrae_project/transit_database/datamodel.rst.template b/docs/source/modeling_with_aequilibrae/transit_database/datamodel.rst.template similarity index 98% rename from docs/source/modeling_with_aequilibrae/aequilibrae_project/transit_database/datamodel.rst.template rename to docs/source/modeling_with_aequilibrae/transit_database/datamodel.rst.template index 2ebd72b2c..d732d4b0a 100644 --- a/docs/source/modeling_with_aequilibrae/aequilibrae_project/transit_database/datamodel.rst.template +++ b/docs/source/modeling_with_aequilibrae/transit_database/datamodel.rst.template @@ -1,5 +1,3 @@ -:orphan: - .. _transit_supply_data_model: SQL Data model diff --git a/docs/source/modeling_with_aequilibrae/aequilibrae_project/transit_database/index.rst b/docs/source/modeling_with_aequilibrae/transit_database/index.rst similarity index 59% rename from docs/source/modeling_with_aequilibrae/aequilibrae_project/transit_database/index.rst rename to docs/source/modeling_with_aequilibrae/transit_database/index.rst index 1b73cfaa4..fd28dccbd 100644 --- a/docs/source/modeling_with_aequilibrae/aequilibrae_project/transit_database/index.rst +++ b/docs/source/modeling_with_aequilibrae/transit_database/index.rst @@ -23,40 +23,8 @@ In the following sections, we'll dive deep into the tables existing in the publi Please notice that some tables are homonyms to the ones existing in the **project_database.sqlite**, but its contents are related to the public transport graph building and assignment processes. -.. include:: data_model/agencies.rst +.. toctree:: + :caption: Get to know the data structures in public transport database! + :maxdepth: 1 -.. include:: data_model/attributes_documentation.rst - -.. include:: data_model/fare_attributes.rst - -.. include:: data_model/fare_rules.rst - -.. include:: data_model/fare_zones.rst - -.. include:: data_model/link_types.rst - -.. include:: data_model/links.rst - -.. include:: data_model/modes.rst - -.. include:: data_model/node_types.rst - -.. include:: data_model/nodes.rst - -.. include:: data_model/pattern_mapping.rst - -.. include:: data_model/results.rst - -.. include:: data_model/route_links.rst - -.. include:: data_model/routes.rst - -.. include:: data_model/stop_connectors.rst - -.. include:: data_model/stops.rst - -.. include:: data_model/trigger_settings.rst - -.. include:: data_model/trips_schedule.rst - -.. include:: data_model/trips.rst + data_model/datamodel.rst \ No newline at end of file diff --git a/docs/table_documentation.py b/docs/table_documentation.py index 6d23c8310..c16dbef5d 100644 --- a/docs/table_documentation.py +++ b/docs/table_documentation.py @@ -30,13 +30,7 @@ def __init__(self, component: str, tgt_fldr: str): self.path = join( *Path(realpath(__file__)).parts[:-1], f"../aequilibrae/project/database_specification/{folder}/tables" ) - self.doc_path = str( - Path(realpath(__file__)).parent - / "source" - / "modeling_with_aequilibrae" - / "aequilibrae_project" - / tgt_fldr - ) + self.doc_path = str(Path(realpath(__file__)).parent / "source" / "modeling_with_aequilibrae" / tgt_fldr) Path(join(self.doc_path, self.stub)).mkdir(exist_ok=True, parents=True) @@ -51,11 +45,8 @@ def create(self): descr = self.conn.execute(f"pragma table_info({table_name})").fetchall() # Title of the page - title = f"*{table_name}* table structure" - txt = [title, "-" * len(title), ""] - - # intro = """A more technical view of the database structure, including the SQL queries used to create each table and the indices used are displayed below.\n""" - # txt.append(intro) + title = f'**{table_name.replace("_", " ")}** table structure' + txt = [title, "=" * len(title), ""] docstrings = self.__get_docstrings(table_name) sql_code = self.__get_sql_code(table_name) @@ -134,10 +125,7 @@ def readlines(filename): return [x.strip() for x in f.readlines()] -tables = [ - ("project_database", "project_database"), - ("transit_database", "transit_database"), -] +tables = [("project_database", "project_database"), ("transit_database", "transit_database")] for table, pth in tables: s = CreateTablesSRC(table, pth) From 0715ee5da119b6ab3059d15b81d0b205557b202a Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Tue, 17 Sep 2024 16:26:25 -0300 Subject: [PATCH 53/57] changes --- docs/create_docs_data.py | 14 ++++- .../assignment_workflows/plot_forecasting.py | 26 ++++----- .../plot_public_transit_assignment.py | 32 +++++------ .../plot_route_choice_basics.py | 25 +++++---- .../plot_route_choice_set.py | 21 ++++---- .../examples/visualization/plot_display.py | 5 ++ .../modeling_with_aequilibrae/index.rst | 1 - .../project_database/modes.rst | 3 +- .../project_database/network.rst | 7 ++- .../project_pieces/accessing_project_data.rst | 13 ++--- .../project_pieces/aequilibrae_matrix.rst | 16 +++--- .../project_pieces/project_components.rst | 54 ++++++++++++------- .../route_choice/index.rst | 4 +- docs/source/useful_information/support.rst | 22 ++++---- docs/table_documentation.py | 12 +++-- 15 files changed, 144 insertions(+), 111 deletions(-) diff --git a/docs/create_docs_data.py b/docs/create_docs_data.py index d94e41467..7138e343f 100644 --- a/docs/create_docs_data.py +++ b/docs/create_docs_data.py @@ -1,3 +1,4 @@ +import os import sys from pathlib import Path @@ -14,4 +15,15 @@ project = create_example("/tmp/test_project_gc") project.close() project = create_example("/tmp/test_project_ga") -project.close() \ No newline at end of file +project.close() +project = create_example("/tmp/accessing_sfalls_data") +project.close() +project = create_example("/tmp/accessing_nauru_data", "nauru") +project.close() +project = create_example("/tmp/accessing_coquimbo_data", "coquimbo") +project.close() + +# Create empty folder +if not os.path.exists("/tmp/matrix_example"): + + os.makedirs("/tmp/matrix_example") \ No newline at end of file diff --git a/docs/source/examples/assignment_workflows/plot_forecasting.py b/docs/source/examples/assignment_workflows/plot_forecasting.py index d2c079f3f..fd86d7b25 100644 --- a/docs/source/examples/assignment_workflows/plot_forecasting.py +++ b/docs/source/examples/assignment_workflows/plot_forecasting.py @@ -10,6 +10,19 @@ distributing these trips into the network. Later, we estimate a set of future demand vectors which are going to be the input of a future year assignnment with select link analysis. """ +# %% +# .. seealso:: +# The use of the following functions, methods, classes and modules is shown in this example: +# +# * :func:`aequilibrae.paths.Graph` +# * :func:`aequilibrae.paths.TrafficClass` +# * :func:`aequilibrae.paths.TrafficAssignment` +# * :func:`aequilibrae.distribution.Ipf` +# * :func:`aequilibrae.distribution.GravityCalibration` +# * :func:`aequilibrae.distribution.GravityApplication` +# * :func:`aequilibrae.distribution.SyntheticGravityModel` +# * :func:`aequilibrae.matrix.AequilibraeData` + # %% # Imports @@ -425,16 +438,3 @@ def plot_tlfd(demand, skim, name): # %% # Close the project project.close() - -# %% -# .. seealso:: -# The use of the following functions, methods, classes and modules is shown in this example: -# -# * :func:`aequilibrae.paths.Graph` -# * :func:`aequilibrae.paths.TrafficClass` -# * :func:`aequilibrae.paths.TrafficAssignment` -# * :func:`aequilibrae.distribution.Ipf` -# * :func:`aequilibrae.distribution.GravityCalibration` -# * :func:`aequilibrae.distribution.GravityApplication` -# * :func:`aequilibrae.distribution.SyntheticGravityModel` -# * :func:`aequilibrae.matrix.AequilibraeData` diff --git a/docs/source/examples/assignment_workflows/plot_public_transit_assignment.py b/docs/source/examples/assignment_workflows/plot_public_transit_assignment.py index 1459f47a2..2bdab2c14 100644 --- a/docs/source/examples/assignment_workflows/plot_public_transit_assignment.py +++ b/docs/source/examples/assignment_workflows/plot_public_transit_assignment.py @@ -9,6 +9,22 @@ We use data from Coquimbo, a city in La Serena Metropolitan Area in Chile. """ +# %% +# .. admonition:: References +# +# * :ref:`transit_assignment_graph` +# * :ref:`transit_hyperpath_routing` + +# %% +# .. seealso:: +# The use of the following functions, methods, classes and modules is shown in this example: +# +# * :func:`aequilibrae.transit.Transit` +# * :func:`aequilibrae.transit.TransitGraphBuilder` +# * :func:`aequilibrae.paths.TransitClass` +# * :func:`aequilibrae.paths.TransitAssignment` +# * :func:`aequilibrae.matrix.AequilibraeMatrix` + # %% # Imports for example construction @@ -170,19 +186,3 @@ # %% # Wrapping up project.close() - -# %% -# .. admonition:: References -# -# * :ref:`transit_assignment_graph` -# * :ref:`transit_hyperpath_routing` - -# %% -# .. seealso:: -# The use of the following functions, methods, classes and modules is shown in this example: -# -# * :func:`aequilibrae.transit.Transit` -# * :func:`aequilibrae.transit.TransitGraphBuilder` -# * :func:`aequilibrae.paths.TransitClass` -# * :func:`aequilibrae.paths.TransitAssignment` -# * :func:`aequilibrae.matrix.AequilibraeMatrix` \ No newline at end of file diff --git a/docs/source/examples/assignment_workflows/plot_route_choice_basics.py b/docs/source/examples/assignment_workflows/plot_route_choice_basics.py index 3e41d0eaa..90f51e60b 100644 --- a/docs/source/examples/assignment_workflows/plot_route_choice_basics.py +++ b/docs/source/examples/assignment_workflows/plot_route_choice_basics.py @@ -7,6 +7,18 @@ In this example, we show how to perform route choice set generation using BFSLE and Link penalisation, for a city in La Serena Metropolitan Area in Chile. """ +# %% +# .. admonition:: References +# +# :ref:`route_choice` + +# %% +# .. seealso:: +# The use of the following functions, methods, classes and modules is shown in this example: +# +# * :func:`aequilibrae.paths.Graph` +# * :func:`aequilibrae.paths.RouteChoice` +# * :func:`aequilibrae.matrix.AequilibraeMatrix` # %% @@ -224,16 +236,3 @@ def plot_results(link_loads): # %% project.close() - -# %% -# .. admonition:: References -# -# :ref:`route_choice` - -# %% -# .. seealso:: -# The use of the following functions, methods, classes and modules is shown in this example: -# -# * :func:`aequilibrae.paths.Graph` -# * :func:`aequilibrae.paths.RouteChoice` -# * :func:`aequilibrae.matrix.AequilibraeMatrix` \ No newline at end of file diff --git a/docs/source/examples/assignment_workflows/plot_route_choice_set.py b/docs/source/examples/assignment_workflows/plot_route_choice_set.py index fb3fcc91c..209194b9d 100644 --- a/docs/source/examples/assignment_workflows/plot_route_choice_set.py +++ b/docs/source/examples/assignment_workflows/plot_route_choice_set.py @@ -7,6 +7,16 @@ In this example, we show how to generate route choice sets for estimation of route choice models, using a a city in La Serena Metropolitan Area in Chile. """ +# %% +# .. admonition:: References +# +# :ref:`route_choice` + +# %% +# .. seealso:: +# The use of the following functions, methods, classes and modules is shown in this example: +# +# * :func:`aequilibrae.paths.RouteChoice` # %% @@ -147,14 +157,3 @@ # %% project.close() - -# %% -# .. admonition:: References -# -# :ref:`route_choice` - -# %% -# .. seealso:: -# The use of the following functions, methods, classes and modules is shown in this example: -# -# * :func:`aequilibrae.paths.RouteChoice` \ No newline at end of file diff --git a/docs/source/examples/visualization/plot_display.py b/docs/source/examples/visualization/plot_display.py index a070ef9cf..24198659e 100644 --- a/docs/source/examples/visualization/plot_display.py +++ b/docs/source/examples/visualization/plot_display.py @@ -33,6 +33,11 @@ links = project.network.links.data nodes = project.network.nodes.data +# %% +# And if you want to take a quick look in your GeoDataFrames, you can plot it! + +# links.plot() + # %% # We create our Folium layers network_links = folium.FeatureGroup("links") diff --git a/docs/source/modeling_with_aequilibrae/index.rst b/docs/source/modeling_with_aequilibrae/index.rst index f57d77dd3..be4daf525 100644 --- a/docs/source/modeling_with_aequilibrae/index.rst +++ b/docs/source/modeling_with_aequilibrae/index.rst @@ -16,7 +16,6 @@ a start guide to a complete view into AequilibraE's data structure. .. toctree:: :maxdepth: 1 - :caption: A guide to AequilibraE project project_database/index diff --git a/docs/source/modeling_with_aequilibrae/project_database/modes.rst b/docs/source/modeling_with_aequilibrae/project_database/modes.rst index 8ee7ffd43..c583769ef 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/modes.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/modes.rst @@ -97,4 +97,5 @@ required. .. seealso:: - :func:`aequilibrae.project.network.Modes` + * :func:`aequilibrae.project.network.Modes` + * :ref:`modes_network_data_model` diff --git a/docs/source/modeling_with_aequilibrae/project_database/network.rst b/docs/source/modeling_with_aequilibrae/project_database/network.rst index 94d4f4801..b246838ed 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/network.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/network.rst @@ -27,9 +27,14 @@ other changes to the layers or preventing the changes. implementation requires a complete overahaul of the path-building code, so that is still a long-term goal, barred specific development efforts. +.. seealso:: + + * :ref:`links_network_data_model` + * :ref:`nodes_network_data_model` + .. toctree:: :maxdepth: 1 :caption: Dive deep into network! - network_import_and_export.rst network_geometry.rst + network_import_and_export.rst diff --git a/docs/source/modeling_with_aequilibrae/project_pieces/accessing_project_data.rst b/docs/source/modeling_with_aequilibrae/project_pieces/accessing_project_data.rst index 32424063f..11a681b0c 100644 --- a/docs/source/modeling_with_aequilibrae/project_pieces/accessing_project_data.rst +++ b/docs/source/modeling_with_aequilibrae/project_pieces/accessing_project_data.rst @@ -13,16 +13,13 @@ cover the main points regarding them. This method allows you to access the API resources to manipulate the 'links' table. Each item in the 'links' table is a ``Link`` object. -.. testsetup:: * - - >>> folder = getfixture("create_path") - .. code-block:: python + >>> from aequilibrae import Project >>> from shapely.geometry import LineString - >>> from aequilibrae.utils.create_example import create_example - >>> project = create_example(folder, "coquimbo") + >>> project = Project() + >>> project.open("/tmp/accessing_coquimbo_data") >>> project_links = project.network.links @@ -208,9 +205,9 @@ Each item in the 'zones' table is a ``Zone`` object. >>> zones = project_zones.data # To get a Shapely Polygon or Multipolygon with the entire zoning coverage - >>> project_zones.coverage() # doctest: +SKIP + >>> boundaries = project_zones.coverage() - # And to get the nearest zone to giver geometry + # And to get the nearest zone to a given geometry >>> project_zones.get_closest_zone(Point(-71.3336, -29.9490)) 57 diff --git a/docs/source/modeling_with_aequilibrae/project_pieces/aequilibrae_matrix.rst b/docs/source/modeling_with_aequilibrae/project_pieces/aequilibrae_matrix.rst index e55b1e5f5..58699bcab 100644 --- a/docs/source/modeling_with_aequilibrae/project_pieces/aequilibrae_matrix.rst +++ b/docs/source/modeling_with_aequilibrae/project_pieces/aequilibrae_matrix.rst @@ -24,10 +24,8 @@ There are three ways of creating an ``AequilibraeMatrix``: >>> import numpy as np >>> from os.path import join >>> from aequilibrae.matrix import AequilibraeMatrix - - >>> folder = getfixture("tmp_path") - >>> file = join(folder, "path_to_my_matrix.aem") + >>> file = "/tmp/matrix_example/path_to_my_matrix.aem" >>> num_zones = 5 >>> index = np.arange(1, 6, dtype=np.int32) >>> mtx = np.ones((5, 5), dtype=np.float32) @@ -83,16 +81,16 @@ for CSV file, in which all cores will be exported as separate columns in the out .. code-block:: python - >>> mat.export(join(folder, 'my_new_omx_file.omx')) + >>> mat.export('/tmp/matrix_example/my_new_omx_file.omx') - >>> mat.export(join(folder, 'my_new_csv_file.csv')) + >>> mat.export('/tmp/matrix_example/my_new_csv_file.csv') The ``export`` method also allows you to change your mind and save your AequilibraE matrix into an AEM file, if it's only in memory. .. code-block:: python - >>> mat.export(join(folder, 'my_new_aem_file.aem')) + >>> mat.export('/tmp/matrix_example/my_new_aem_file.aem') .. is there a better name rather than error? @@ -109,7 +107,7 @@ AequilibraE matrices in disk can be reused and loaded once again. .. code-block:: python >>> mat = AequilibraeMatrix() - >>> mat.load(join(folder, 'my_new_aem_file.aem')) + >>> mat.load('/tmp/matrix_example/my_new_aem_file.aem') >>> mat.get_matrix("only_ones") # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE memmap([[1., 1., 1., 1., 1.], @@ -152,8 +150,8 @@ Creating an AequilibraE matrix from an OMX file is pretty straightforward. .. code-block:: python - >>> file_path = join(folder, "path_to_new_matrix.aem") - >>> omx_path = join(folder, 'my_new_omx_file.omx') + >>> file_path = "/tmp/matrix_example/path_to_new_matrix.aem" + >>> omx_path = '/tmp/matrix_example/my_new_omx_file.omx' >>> omx_mat = AequilibraeMatrix() >>> omx_mat.create_from_omx(file_path, omx_path) diff --git a/docs/source/modeling_with_aequilibrae/project_pieces/project_components.rst b/docs/source/modeling_with_aequilibrae/project_pieces/project_components.rst index b48aa61e8..83c52c477 100644 --- a/docs/source/modeling_with_aequilibrae/project_pieces/project_components.rst +++ b/docs/source/modeling_with_aequilibrae/project_pieces/project_components.rst @@ -31,15 +31,6 @@ enables the access to manipulate the 'links' table, and each item in the items t In this section, we'll briefly discuss about the project components without geo-spatial information. -.. testsetup:: * - - >>> from uuid import uuid4 - >>> from aequilibrae.utils.create_example import create_example - -.. testcleanup:: * - - >> project.close() - ``project.about`` ----------------- @@ -48,8 +39,10 @@ edit the existing ones as necessary, but everytime you add or modify a field, yo this information, otherwise it will be lost. .. doctest:: + >>> from aequilibrae import Project - >>> project = create_example(f"/tmp/{uuid4().hex}") + >>> project = Project() + >>> project.open("/tmp/accessing_sfalls_data") >>> project.about.add_info_field("my_new_field") >>> project.about.my_new_field = "add some useful information about the field" @@ -70,6 +63,8 @@ this information, otherwise it will be lost. # it is possible to create one too. >>> project.about.create() + >>> project.close() + .. admonition:: References * :ref:`tables_about` @@ -92,7 +87,8 @@ This class is directly accessed from within the corresponding module one wants t .. doctest:: - >>> project = create_example(f"/tmp/{uuid4().hex}", "nauru") + >>> project = Project() + >>> project.open("/tmp/accessing_nauru_data") # We'll edit the fields in the 'nodes' table >>> node_fields = project.network.nodes.fields @@ -114,6 +110,8 @@ This class is directly accessed from within the corresponding module one wants t >>> node_fields.all_fields() # doctest: +ELLIPSIS ['is_centroid', ..., 'my_new_field'] + >>> project.close() + All field descriptions are kept in the table 'attributes_documentation'. .. admonition:: References @@ -134,7 +132,8 @@ It is possible to access the log file contents, as presented in the next code bl .. doctest:: - >>> project = create_example(f"/tmp/{uuid4().hex}", "nauru") + >>> project = Project() + >>> project.open("/tmp/accessing_nauru_data") >>> project_log = project.log() @@ -146,6 +145,8 @@ It is possible to access the log file contents, as presented in the next code bl # Use this option wiesly once the deletion of data in the log file can't be undone. >>> project_log.clear() + >>> project.close() + .. admonition:: References * :ref:`useful-log-tips` @@ -164,7 +165,8 @@ records in the 'matrices' table. Each item in the 'matrices' table is a ``Matri .. doctest:: - >>> project = create_example(f"/tmp/{uuid4().hex}") + >>> project = Project() + >>> project.open("/tmp/accessing_sfalls_data") >>> matrices = project.matrices @@ -199,6 +201,8 @@ records in the 'matrices' table. Each item in the 'matrices' table is a ``Matri # get an AequilibraE matrix. >>> matrices.get_matrix("demand_aem") # doctest: +SKIP + >>> project.close() + .. admonition:: References * :ref:`matrix_table` @@ -217,7 +221,8 @@ Each item in the 'link_types' table is a ``LinkType`` object. .. doctest:: - >>> project = create_example(f"/tmp/{uuid4().hex}", "coquimbo") + >>> project = Project() + >>> project.open("/tmp/accessing_coquimbo_data") >>> link_types = project.network.link_types @@ -242,8 +247,8 @@ Each item in the 'link_types' table is a ``LinkType`` object. >>> link_types.save() # To check all `LinkTypes` in the project as a dictionary whose keys are the `link_type_id`'s - >>> link_types.all_types() # doctest: +SKIP - {'z': ...} + >>> link_types.all_types() # doctest: +ELLIPSIS + {'z': } # There are two ways to get a LinkType from the 'link_types' table # using the `link_type_id` @@ -252,6 +257,8 @@ Each item in the 'link_types' table is a ``LinkType`` object. # or using the `link_type` >>> get_link = link_types.get_by_name("primary") + >>> project.close() + .. admonition:: References * :ref:`tables_link_types` @@ -270,7 +277,8 @@ Each item in 'modes' table is a ``Mode`` object. .. doctest:: - >>> project = create_example(f"/tmp/{uuid4().hex}", "coquimbo") + >>> project = Project() + >>> project.open("/tmp/accessing_coquimbo_data") >>> modes = project.network.modes @@ -290,7 +298,8 @@ Each item in 'modes' table is a ``Mode`` object. >>> modes.delete("k") # To check all `Modes` in the project as a dictionary whose keys are the `mode_id`'s - >>> modes.all_modes() # doctest: +SKIP + >>> modes.all_modes() # doctest: +ELLIPSIS + {'b': } # There are two ways to get a Mode from the 'modes' table # using the ``mode_id`` @@ -299,6 +308,8 @@ Each item in 'modes' table is a ``Mode`` object. # or using the ``mode_name`` >>> get_mode = modes.get_by_name("car") + >>> project.close() + .. admonition:: References * :ref:`tables_modes` @@ -317,7 +328,8 @@ Each item in the 'periods' table is a ``Period`` object. .. doctest:: - >>> project = create_example(f"/tmp/{uuid4().hex}", "coquimbo") + >>> project = Project() + >>> project.open("/tmp/accessing_coquimbo_data") >>> periods = project.network.periods @@ -335,7 +347,7 @@ Each item in the 'periods' table is a ``Period`` object. >>> select_period.save() # To see all periods data as a Pandas' DataFrame - >>> periods.data # doctest: +SKIP + >>> all_periods = periods.data # To add a new period >>> new_period = periods.new_period(2, 21600, 43200, "6AM to noon") @@ -351,6 +363,8 @@ Each item in the 'periods' table is a ``Period`` object. # of this example, we'll save the addition of a new period to our table right away >>> periods.save() + >>> project.close() + .. admonition:: References * :ref:`tables_period` diff --git a/docs/source/modeling_with_aequilibrae/route_choice/index.rst b/docs/source/modeling_with_aequilibrae/route_choice/index.rst index 4eee3487b..87fb8a50b 100644 --- a/docs/source/modeling_with_aequilibrae/route_choice/index.rst +++ b/docs/source/modeling_with_aequilibrae/route_choice/index.rst @@ -80,8 +80,8 @@ be presented at the ATRF 2024 [1]_ [3]_ [4]_. .. [2] Rieser-Schüssler, N., Balmer, M., & Axhausen, K. W. (2012). Route choice sets for very high-resolution data. Transportmetrica A: Transport Science, 9(9), 825–845. DOI: https://doi.org/10.1080/18128602.2012.671383 -.. [3] Camargo, P. V. de, and R. Imai. Map-Matching Large Streams of Vehicle GPS Data into Bespoke Networks (Submitted). +.. [3] Camargo, P. V. de, and R. Imai. Map-Matching Large Streams of Vehicle GPS Data into Bespoke Networks. Presented at the ATRF, Melbourne, 2024. .. [4] Moss, J., P. V. de Camargo, C. de Freitas, and R. Imai. High-Performance Route Choice Set Generation on - Large Networks (Submitted). Presented at the ATRF, Melbourne, 2024. + Large Networks. Presented at the ATRF, Melbourne, 2024. diff --git a/docs/source/useful_information/support.rst b/docs/source/useful_information/support.rst index 05cd46fb5..a0bcbd935 100644 --- a/docs/source/useful_information/support.rst +++ b/docs/source/useful_information/support.rst @@ -10,6 +10,17 @@ Support AequilibraE is developed by a small but dedicated team of professionals with limited funding. +Paid support +~~~~~~~~~~~~ + +Paid support for AequilibraE is offered by Outer Loop Consulting, an Australia-based consulting company, +with support available in English, Portuguese, German & Spanish. + +All support is offered in prepaid packages of a minimum of 10h of consulting by phone, e-mail or Microsoft Teams +at a fixed rate of USD 150/h. + +To acquire a paid support package for AequilibraE, please e-mail aequilibrae@outerloop.io + Free support ~~~~~~~~~~~~ @@ -22,17 +33,6 @@ already asked in the past, with the first option being the most often used as of Please note that all questions and answers in both forums are public. -Paid support -~~~~~~~~~~~~ - -Paid support for AequilibraE is offered by Outer Loop Consulting, an Australia-based consulting company, -with support available in English, Portuguese, German & Spanish. - -All support is offered in prepaid packages of a minimum of 10h of consulting by phone, e-mail or Microsoft Teams -at a fixed rate of USD 150/h. - -To acquire a paid support package for AequilibraE, please e-mail aequilibrae@outerloop.io - .. _sponsors: Sponsors diff --git a/docs/table_documentation.py b/docs/table_documentation.py index c16dbef5d..5a27807b5 100644 --- a/docs/table_documentation.py +++ b/docs/table_documentation.py @@ -23,12 +23,13 @@ def __init__(self, component: str, tgt_fldr: str): self.proj.new(self.proj_path) Transit(self.proj) - folder = "network" if component == "project_database" else "transit" + self.__folder = "network" if component == "project_database" else "transit" self.stub = "data_model" # Get the appropriate data for the database we are documenting - self.conn = database_connection(db_type=folder, project_path=self.proj_path) + self.conn = database_connection(db_type=self.__folder, project_path=self.proj_path) self.path = join( - *Path(realpath(__file__)).parts[:-1], f"../aequilibrae/project/database_specification/{folder}/tables" + *Path(realpath(__file__)).parts[:-1], + f"../aequilibrae/project/database_specification/{self.__folder}/tables", ) self.doc_path = str(Path(realpath(__file__)).parent / "source" / "modeling_with_aequilibrae" / tgt_fldr) @@ -44,9 +45,12 @@ def create(self): for table_name in all_tables: descr = self.conn.execute(f"pragma table_info({table_name})").fetchall() + # Data model reference + reference = f".. _{table_name}_{self.__folder}_data_model:\n" + # Title of the page title = f'**{table_name.replace("_", " ")}** table structure' - txt = [title, "=" * len(title), ""] + txt = [reference, title, "=" * len(title), ""] docstrings = self.__get_docstrings(table_name) sql_code = self.__get_sql_code(table_name) From 8a8b08eafb4a80d5289c15c4206a30d76ded90ff Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Tue, 17 Sep 2024 16:26:36 -0300 Subject: [PATCH 54/57] Update plot_subarea_analysis.py --- .../plot_subarea_analysis.py | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/docs/source/examples/assignment_workflows/plot_subarea_analysis.py b/docs/source/examples/assignment_workflows/plot_subarea_analysis.py index 161c30021..0287beb2b 100644 --- a/docs/source/examples/assignment_workflows/plot_subarea_analysis.py +++ b/docs/source/examples/assignment_workflows/plot_subarea_analysis.py @@ -6,8 +6,19 @@ In this example, we show how to perform sub-area analysis using route choice assignment, for a city in La Serena Metropolitan Area in Chile. -""" +.. admonition:: References + + :ref:`route_choice` + +.. seealso:: + The use of the following functions, methods, classes and modules is shown in this example: + + * :func:`aequilibrae.paths.Graph` + * :func:`aequilibrae.paths.RouteChoice` + * :func:`aequilibrae.paths.SubAreaAnalysis` + * :func:`aequilibrae.matrix.AequilibraeMatrix` +""" # %% # Imports @@ -407,17 +418,3 @@ def plot_results(link_loads): map = plot_results(rc.get_load_results()["demand"]) subarea_zone.add_to(map) map - -# %% -# .. admonition:: References -# -# :ref:`route_choice` - -# %% -# .. seealso:: -# The use of the following functions, methods, classes and modules is shown in this example: -# -# * :func:`aequilibrae.paths.Graph` -# * :func:`aequilibrae.paths.RouteChoice` -# * :func:`aequilibrae.paths.SubAreaAnalysis` -# * :func:`aequilibrae.matrix.AequilibraeMatrix` \ No newline at end of file From 34076a4a96bd0c53168360ac58f3b732945aef4b Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Tue, 17 Sep 2024 16:36:49 -0300 Subject: [PATCH 55/57] Update project_components.rst --- .../project_pieces/project_components.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/modeling_with_aequilibrae/project_pieces/project_components.rst b/docs/source/modeling_with_aequilibrae/project_pieces/project_components.rst index 83c52c477..65d0369bb 100644 --- a/docs/source/modeling_with_aequilibrae/project_pieces/project_components.rst +++ b/docs/source/modeling_with_aequilibrae/project_pieces/project_components.rst @@ -247,7 +247,7 @@ Each item in the 'link_types' table is a ``LinkType`` object. >>> link_types.save() # To check all `LinkTypes` in the project as a dictionary whose keys are the `link_type_id`'s - >>> link_types.all_types() # doctest: +ELLIPSIS + >>> print(link_types.all_types()) # doctest: +ELLIPSIS {'z': } # There are two ways to get a LinkType from the 'link_types' table @@ -298,7 +298,7 @@ Each item in 'modes' table is a ``Mode`` object. >>> modes.delete("k") # To check all `Modes` in the project as a dictionary whose keys are the `mode_id`'s - >>> modes.all_modes() # doctest: +ELLIPSIS + >>> print(modes.all_modes()) # doctest: +ELLIPSIS {'b': } # There are two ways to get a Mode from the 'modes' table From 669e37b5fd994ec68d4133361c7c0772c15ae47c Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Wed, 18 Sep 2024 11:31:14 -0300 Subject: [PATCH 56/57] typos, tests, and admonitions --- .../transit/tables/agencies.sql | 2 +- .../transit/tables/fare_attributes.sql | 2 +- .../transit/tables/routes.sql | 2 +- .../transit/tables/stops.sql | 2 +- .../transit/tables/trips.sql | 3 +- docs/source/images/periods_table.png | Bin 0 -> 27906 bytes .../parameter_file.rst | 3 +- .../project_database/about.rst | 5 +- .../attributes_documentation.rst | 3 +- .../project_database/index.rst | 9 ++- .../project_database/link_types.rst | 6 +- .../project_database/matrices.rst | 7 ++- .../project_database/modes.rst | 2 + .../project_database/network.rst | 2 + .../project_database/network_geometry.rst | 9 ++- .../network_import_and_export.rst | 15 ++++- .../project_database/periods.rst | 7 +++ .../project_database/results.rst | 6 +- .../project_database/transit_graph.rst | 9 --- .../project_database/zones.rst | 5 +- .../project_pieces/accessing_project_data.rst | 20 +++--- .../project_pieces/aequilibrae_matrix.rst | 3 +- .../project_pieces/project_components.rst | 59 ++++++------------ .../assignment_mechanics.rst | 18 +++--- .../transit_assignment/hyperpath_routing.rst | 3 +- .../transit_database/index.rst | 7 ++- 26 files changed, 110 insertions(+), 99 deletions(-) create mode 100644 docs/source/images/periods_table.png delete mode 100644 docs/source/modeling_with_aequilibrae/project_database/transit_graph.rst diff --git a/aequilibrae/project/database_specification/transit/tables/agencies.sql b/aequilibrae/project/database_specification/transit/tables/agencies.sql index d40f1eb25..dc1cf0f17 100644 --- a/aequilibrae/project/database_specification/transit/tables/agencies.sql +++ b/aequilibrae/project/database_specification/transit/tables/agencies.sql @@ -2,7 +2,7 @@ --@ agencies within the GTFS data. This table information comes from --@ GTFS file *agency.txt*. --@ You can check out more information ---@ `on agency table here `_. +--@ `on agencies here `_. --@ --@ **agency_id** identifies the agency for the specified route --@ diff --git a/aequilibrae/project/database_specification/transit/tables/fare_attributes.sql b/aequilibrae/project/database_specification/transit/tables/fare_attributes.sql index dc779b45d..d9ae5b2f9 100644 --- a/aequilibrae/project/database_specification/transit/tables/fare_attributes.sql +++ b/aequilibrae/project/database_specification/transit/tables/fare_attributes.sql @@ -1,7 +1,7 @@ --@ The *fare_attributes* table holds information about the fare values. --@ This table information comes from the GTFS file *fare_attributes.txt*. --@ Given that this file is optional in GTFS, it can be empty. ---@ You can check out more information `on fare attributes here `_. +--@ You can check out more information `on fare attributes here `_. --@ --@ **fare_id** identifies a fare class --@ diff --git a/aequilibrae/project/database_specification/transit/tables/routes.sql b/aequilibrae/project/database_specification/transit/tables/routes.sql index 2ff34d67d..7882637b0 100644 --- a/aequilibrae/project/database_specification/transit/tables/routes.sql +++ b/aequilibrae/project/database_specification/transit/tables/routes.sql @@ -1,6 +1,6 @@ --@ The *routes* table holds information on the available transit routes for a --@ specific day. This table information comes from the GTFS file *routes.txt*. ---@ You can find more information about `the routes table here `_. +--@ You can find more information about `the routes table here `_. --@ --@ **pattern_id** is an unique pattern for the route --@ diff --git a/aequilibrae/project/database_specification/transit/tables/stops.sql b/aequilibrae/project/database_specification/transit/tables/stops.sql index d47bcfb12..08b8a096c 100644 --- a/aequilibrae/project/database_specification/transit/tables/stops.sql +++ b/aequilibrae/project/database_specification/transit/tables/stops.sql @@ -1,7 +1,7 @@ --@ The *stops* table holds information on the stops where vehicles --@ pick up or drop off riders. This table information comes from --@ the GTFS file *stops.txt*. You can find more information about ---@ `the stops table here `_. +--@ `the stops table here `_. --@ --@ **stop_id** is an unique identifier for a stop --@ diff --git a/aequilibrae/project/database_specification/transit/tables/trips.sql b/aequilibrae/project/database_specification/transit/tables/trips.sql index 55b64fc38..7dc6ae6d0 100644 --- a/aequilibrae/project/database_specification/transit/tables/trips.sql +++ b/aequilibrae/project/database_specification/transit/tables/trips.sql @@ -1,6 +1,7 @@ --@ The *trips* table holds information on trips for each route. --@ This table comes from the GTFS file *trips.txt*. ---@ You can find more information about it `here `_. +--@ You can find more information about the +--@ `trips table here `_. --@ --@ **trip_id** identifies a trip --@ diff --git a/docs/source/images/periods_table.png b/docs/source/images/periods_table.png new file mode 100644 index 0000000000000000000000000000000000000000..66a4ecc5f398320342d5f8bd9a3bf7f2ae6dfc46 GIT binary patch literal 27906 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 K1EqNX#eV_581ym# literal 0 HcmV?d00001 diff --git a/docs/source/modeling_with_aequilibrae/parameter_file.rst b/docs/source/modeling_with_aequilibrae/parameter_file.rst index 73b77669f..d7071af8d 100644 --- a/docs/source/modeling_with_aequilibrae/parameter_file.rst +++ b/docs/source/modeling_with_aequilibrae/parameter_file.rst @@ -194,4 +194,5 @@ use by AequilibraE is so small that it is likely not necessary to do so. .. seealso:: - :func:`aequilibrae.Parameters` \ No newline at end of file + * :func:`aequilibrae.Parameters` + Class documentation \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/project_database/about.rst b/docs/source/modeling_with_aequilibrae/project_database/about.rst index 9656f0e50..39de800b9 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/about.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/about.rst @@ -27,4 +27,7 @@ An API for editing the contents of this database is available from the API docum .. seealso:: - :func:`aequilibrae.project.About` + * :func:`aequilibrae.project.About` + Class documentation + * :ref:`about_network_data_model` + Data model \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/project_database/attributes_documentation.rst b/docs/source/modeling_with_aequilibrae/project_database/attributes_documentation.rst index 8fb9327ff..e9a80f7aa 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/attributes_documentation.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/attributes_documentation.rst @@ -18,4 +18,5 @@ As a simple table, it looks as follows: .. seealso:: - :func:`aequilibrae.project.FieldEditor` + * :ref:`attributes_documentation_network_data_model` + Data model diff --git a/docs/source/modeling_with_aequilibrae/project_database/index.rst b/docs/source/modeling_with_aequilibrae/project_database/index.rst index 6b64203b7..dc46dd310 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/index.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/index.rst @@ -10,16 +10,15 @@ an AequilibraE model you intend to use in anger. .. toctree:: :maxdepth: 1 + about network - link_types modes - zones + link_types matrices + zones + attributes_documentation results periods - transit_graph - about - attributes_documentation A more technical view of the database structure, including the SQL queries used to create each table and the indices used for each table are also available. diff --git a/docs/source/modeling_with_aequilibrae/project_database/link_types.rst b/docs/source/modeling_with_aequilibrae/project_database/link_types.rst index 54c644421..150ea2a68 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/link_types.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/link_types.rst @@ -120,4 +120,8 @@ required. .. seealso:: - :func:`aequilibrae.project.network.LinkTypes` + * :func:`aequilibrae.project.network.LinkTypes` + Class documentation + * :ref:`link_types_network_data_model` + Data model + \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/project_database/matrices.rst b/docs/source/modeling_with_aequilibrae/project_database/matrices.rst index 291b15748..32c832786 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/matrices.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/matrices.rst @@ -20,6 +20,7 @@ types (AEM and OMX) without prejudice to functionality. .. seealso:: - :func:`aequilibrae.project.Matrices` - - :func:`aequilibrae.matrix.AequilibraeMatrix` \ No newline at end of file + * :func:`aequilibrae.project.Matrices` + Class documentation + * :ref:`matrices_network_data_model` + Data model diff --git a/docs/source/modeling_with_aequilibrae/project_database/modes.rst b/docs/source/modeling_with_aequilibrae/project_database/modes.rst index c583769ef..662fb04e4 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/modes.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/modes.rst @@ -98,4 +98,6 @@ required. .. seealso:: * :func:`aequilibrae.project.network.Modes` + Class documentation * :ref:`modes_network_data_model` + Data model diff --git a/docs/source/modeling_with_aequilibrae/project_database/network.rst b/docs/source/modeling_with_aequilibrae/project_database/network.rst index b246838ed..59bce0461 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/network.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/network.rst @@ -30,7 +30,9 @@ other changes to the layers or preventing the changes. .. seealso:: * :ref:`links_network_data_model` + Data model * :ref:`nodes_network_data_model` + Data model .. toctree:: :maxdepth: 1 diff --git a/docs/source/modeling_with_aequilibrae/project_database/network_geometry.rst b/docs/source/modeling_with_aequilibrae/project_database/network_geometry.rst index e1edf21aa..3421c6975 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/network_geometry.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/network_geometry.rst @@ -170,7 +170,8 @@ their respective table descriptions. .. seealso:: - :ref:`Example - Editing network nodes ` + * :ref:`Editing network nodes ` + Usage example Adding a data field +++++++++++++++++++ @@ -229,7 +230,8 @@ This change can happen in two different forms: .. seealso:: - :ref:`Example - Editing network links ` + * :ref:`Editing network links ` + Usage example Re-shaping a link +++++++++++++++++ @@ -240,7 +242,8 @@ As of now, distance in AequilibraE is **ALWAYS** measured in meters. .. seealso:: - :ref:`Example - Splitting network links ` + * :ref:`Splitting network links ` + Usage example Deleting a required field +++++++++++++++++++++++++ diff --git a/docs/source/modeling_with_aequilibrae/project_database/network_import_and_export.rst b/docs/source/modeling_with_aequilibrae/project_database/network_import_and_export.rst index 5d227e0d4..62ec2694f 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/network_import_and_export.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/network_import_and_export.rst @@ -79,7 +79,10 @@ platform that does not support both R-Tree and SpatiaLite. .. seealso:: - :func:`aequilibrae.project.Network.create_from_osm` + * :func:`aequilibrae.project.Network.create_from_osm` + Function documentation + * :ref:`plot_from_osm` + Usage example Importing from link layer ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -132,7 +135,10 @@ specification. .. seealso:: - :func:`aequilibrae.project.Network.create_from_gmns` + * :func:`aequilibrae.project.Network.create_from_gmns` + Function documentation + * :ref:`import_from_gmns` + Usage example .. _aequilibrae_to_gmns: @@ -172,4 +178,7 @@ You can find the GMNS specification .. seealso:: - :func:`aequilibrae.project.Network.export_to_gmns` + * :func:`aequilibrae.project.Network.export_to_gmns` + Function documentation + * :ref:`export_to_gmns` + Usage example diff --git a/docs/source/modeling_with_aequilibrae/project_database/periods.rst b/docs/source/modeling_with_aequilibrae/project_database/periods.rst index fe688c3cd..096f3c47b 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/periods.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/periods.rst @@ -2,7 +2,14 @@ Periods table ============= + +.. image:: ../../images/periods_table.png + :align: center + :alt: periods table structure .. seealso:: * :func:`aequilibrae.project.network.Periods` + Class documentation + * :ref:`periods_network_data_model` + Data model \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/project_database/results.rst b/docs/source/modeling_with_aequilibrae/project_database/results.rst index d1e933b5f..f854c4cd1 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/results.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/results.rst @@ -1,6 +1,6 @@ .. _tables_results: -Results database +Results table ================ The **results** table exists to hold the metadata for the results stored in the @@ -17,3 +17,7 @@ As a simple table, it looks as follows: .. image:: ../../images/results_table.png :align: center :alt: results table structure + +.. seealso:: + * :ref:`results_network_data_model` + Data model \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/project_database/transit_graph.rst b/docs/source/modeling_with_aequilibrae/project_database/transit_graph.rst deleted file mode 100644 index 00ed9e744..000000000 --- a/docs/source/modeling_with_aequilibrae/project_database/transit_graph.rst +++ /dev/null @@ -1,9 +0,0 @@ -Transit graph configuration -=========================== - -The 'transit_graph_configs' table holds information on the configuration parameters for a -``TransitGraph`` of a particular 'period_id'. - -.. seealso:: - - * :func:`aequilibtae.transit.TransitGraphBuilder` diff --git a/docs/source/modeling_with_aequilibrae/project_database/zones.rst b/docs/source/modeling_with_aequilibrae/project_database/zones.rst index d6d24e7aa..0264a7f7c 100644 --- a/docs/source/modeling_with_aequilibrae/project_database/zones.rst +++ b/docs/source/modeling_with_aequilibrae/project_database/zones.rst @@ -17,4 +17,7 @@ You can check :ref:`this example ` to learn how to add zones to yo .. seealso:: - :func:`aequilibrae.project.Zone` \ No newline at end of file + * :func:`aequilibrae.project.Zone` + Class documentation + * :ref:`zones_network_data_model` + Data model \ No newline at end of file diff --git a/docs/source/modeling_with_aequilibrae/project_pieces/accessing_project_data.rst b/docs/source/modeling_with_aequilibrae/project_pieces/accessing_project_data.rst index 11a681b0c..3aa665b89 100644 --- a/docs/source/modeling_with_aequilibrae/project_pieces/accessing_project_data.rst +++ b/docs/source/modeling_with_aequilibrae/project_pieces/accessing_project_data.rst @@ -64,13 +64,14 @@ Each item in the 'links' table is a ``Link`` object. * :ref:`modifications_on_links_layer` - * :ref:`project_from_link_layer` | :ref:`editing_network_splitting_link` - Usage examples - .. seealso:: * :func:`aequilibrae.project.network.Links` Class documentation + * :ref:`project_from_link_layer` + Usage example + * :ref:`editing_network_splitting_link` + Usage example ``project.network.nodes`` ------------------------- @@ -130,13 +131,13 @@ Each item in the 'nodes' table is a ``Node`` object. * :ref:`modifications_on_nodes_layer` - * :ref:`editing_network_nodes` - Usage example - .. seealso:: * :func:`aequilibrae.project.network.Nodes` Class documentation + * :ref:`editing_network_nodes` + Usage example + .. _project_zoning: @@ -213,12 +214,9 @@ Each item in the 'zones' table is a ``Zone`` object. >>> project.close() -.. admonition:: References - - * :ref:`create_zones` - Usage example - .. seealso:: * :func:`aequilibrae.project.Zoning` Class documentation + * :ref:`create_zones` + Usage example diff --git a/docs/source/modeling_with_aequilibrae/project_pieces/aequilibrae_matrix.rst b/docs/source/modeling_with_aequilibrae/project_pieces/aequilibrae_matrix.rst index 58699bcab..ab1ac26f1 100644 --- a/docs/source/modeling_with_aequilibrae/project_pieces/aequilibrae_matrix.rst +++ b/docs/source/modeling_with_aequilibrae/project_pieces/aequilibrae_matrix.rst @@ -123,8 +123,7 @@ AequilibraE matrices in disk can be reused and loaded once again. .. seealso:: :func:`aequilibrae.matrix.AequilibraeMatrix` - Documentation for ``AequilibraeMatrix`` class - + Class documentatiom :ref:`plot_assignment_without_model` Usage example diff --git a/docs/source/modeling_with_aequilibrae/project_pieces/project_components.rst b/docs/source/modeling_with_aequilibrae/project_pieces/project_components.rst index 65d0369bb..6f2b50798 100644 --- a/docs/source/modeling_with_aequilibrae/project_pieces/project_components.rst +++ b/docs/source/modeling_with_aequilibrae/project_pieces/project_components.rst @@ -65,15 +65,12 @@ this information, otherwise it will be lost. >>> project.close() -.. admonition:: References - - * :ref:`tables_about` - Table documentation - .. seealso:: - * :func:`aequilibrae.procedure.About` + * :func:`aequilibrae.project.About` Class documentation + * :ref:`tables_about` + Table documentation ``project.FieldEditor`` ----------------------- @@ -114,11 +111,6 @@ This class is directly accessed from within the corresponding module one wants t All field descriptions are kept in the table 'attributes_documentation'. -.. admonition:: References - - * :ref:`parameters_metadata` - Table documentation - .. seealso:: * :func:`aequilibrae.project.FieldEditor` @@ -147,15 +139,12 @@ It is possible to access the log file contents, as presented in the next code bl >>> project.close() -.. admonition:: References - - * :ref:`useful-log-tips` - Usage example - .. seealso:: * :func:`aequilibrae.project.Log` Class documentation + * :ref:`useful-log-tips` + Usage example ``project.matrices`` -------------------- @@ -203,15 +192,12 @@ records in the 'matrices' table. Each item in the 'matrices' table is a ``Matri >>> project.close() -.. admonition:: References - - * :ref:`matrix_table` - Table documentation - .. seealso:: * :func:`aequilibrae.project.Matrices` Class documentation + * :ref:`matrix_table` + Table documentation ``project.network.link_types`` ------------------------------ @@ -247,7 +233,7 @@ Each item in the 'link_types' table is a ``LinkType`` object. >>> link_types.save() # To check all `LinkTypes` in the project as a dictionary whose keys are the `link_type_id`'s - >>> print(link_types.all_types()) # doctest: +ELLIPSIS + >>> link_types.all_types() # doctest: +SKIP {'z': } # There are two ways to get a LinkType from the 'link_types' table @@ -259,15 +245,12 @@ Each item in the 'link_types' table is a ``LinkType`` object. >>> project.close() -.. admonition:: References - - * :ref:`tables_link_types` - Table documentation - .. seealso:: - * :func:`aequilibrae.project.LinkTypes` + * :func:`aequilibrae.project.network.LinkTypes` Class documentation + * :ref:`tables_link_types` + Table documentation ``project.network.modes`` ------------------------- @@ -298,7 +281,7 @@ Each item in 'modes' table is a ``Mode`` object. >>> modes.delete("k") # To check all `Modes` in the project as a dictionary whose keys are the `mode_id`'s - >>> print(modes.all_modes()) # doctest: +ELLIPSIS + >>> modes.all_modes() # doctest: +SKIP {'b': } # There are two ways to get a Mode from the 'modes' table @@ -310,15 +293,12 @@ Each item in 'modes' table is a ``Mode`` object. >>> project.close() -.. admonition:: References - - * :ref:`tables_modes` - Table documentation - .. seealso:: - * :func:`aequilibrae.project.Modes` + * :func:`aequilibrae.project.network.Modes` Class documentation + * :ref:`tables_modes` + Table documentation ``project.network.periods`` --------------------------- @@ -365,12 +345,9 @@ Each item in the 'periods' table is a ``Period`` object. >>> project.close() -.. admonition:: References - - * :ref:`tables_period` - Table documentation - .. seealso:: - * :func:`aequilibrae.project.Periods` + * :func:`aequilibrae.project.network.Periods` Class documentation + * :ref:`tables_period` + Table documentation diff --git a/docs/source/modeling_with_aequilibrae/static_traffic_assignment/assignment_mechanics.rst b/docs/source/modeling_with_aequilibrae/static_traffic_assignment/assignment_mechanics.rst index 3c6d31f4a..96ee9ed30 100644 --- a/docs/source/modeling_with_aequilibrae/static_traffic_assignment/assignment_mechanics.rst +++ b/docs/source/modeling_with_aequilibrae/static_traffic_assignment/assignment_mechanics.rst @@ -27,7 +27,7 @@ etc.). The **Graph** object is rather complex, but the difference between the physical links and those that are available two class member variables consisting of Pandas DataFrames, the -***network** and the **graph**. +**network** and the **graph**. .. code-block:: python @@ -126,9 +126,10 @@ not blocking flows through "centroids". .. seealso:: - :func:`aequilibrae.paths.Graph` - - :func:`aequilibrae.paths.TransitGraph` + * :func:`aequilibrae.paths.Graph` + Class documentation + * :func:`aequilibrae.paths.TransitGraph` + Class documentation .. _traffic_assignment_procedure: @@ -194,7 +195,8 @@ To begin building the assignment it is easy: .. seealso:: - :func:`aequilibrae.paths.TrafficAssignment` + * :func:`aequilibrae.paths.TrafficAssignment` + Class documentation .. _assignment_class_object: @@ -250,7 +252,8 @@ required in the instantiation of this class: .. seealso:: - :func:`aequilibrae.paths.TrafficClass` + * :func:`aequilibrae.paths.TrafficClass` + Class documentation Volume Delay Function ^^^^^^^^^^^^^^^^^^^^^ @@ -313,7 +316,8 @@ choice and adding traffic classes to the assignment, or it will **fail**. .. seealso:: - :func:`aequilibrae.paths.VDF` + * :func:`aequilibrae.paths.VDF` + Class documentation Setting final parameters ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/source/modeling_with_aequilibrae/transit_assignment/hyperpath_routing.rst b/docs/source/modeling_with_aequilibrae/transit_assignment/hyperpath_routing.rst index a9e2a0c27..8b6a33d5f 100644 --- a/docs/source/modeling_with_aequilibrae/transit_assignment/hyperpath_routing.rst +++ b/docs/source/modeling_with_aequilibrae/transit_assignment/hyperpath_routing.rst @@ -464,7 +464,8 @@ apparent as we further increase :math:`\alpha`: .. seealso:: - :func:`aequilibrae.paths.HyperpathGenerating` + * :func:`aequilibrae.paths.HyperpathGenerating` + Class documentation .. [1] Spiess, H., Florian, M. (1989) "Optimal strategies: A new assignment model for transit networks". Transportation Research Part B: Methodological, 23(2), 83-102. diff --git a/docs/source/modeling_with_aequilibrae/transit_database/index.rst b/docs/source/modeling_with_aequilibrae/transit_database/index.rst index fd28dccbd..6bf4f6d1c 100644 --- a/docs/source/modeling_with_aequilibrae/transit_database/index.rst +++ b/docs/source/modeling_with_aequilibrae/transit_database/index.rst @@ -15,9 +15,10 @@ for the first time. .. seealso:: - :func:`aequilibrae.transit.Transit` - - :func:`aequilibrae.transit.TransitGraphBuilder` + * :func:`aequilibrae.transit.Transit` + Class documentation + * :func:`aequilibrae.transit.TransitGraphBuilder` + Class documentation In the following sections, we'll dive deep into the tables existing in the public transport database. Please notice that some tables are homonyms to the ones existing in the **project_database.sqlite**, From 70b4d7937863640eeda6c16e498d3e4f533696ca Mon Sep 17 00:00:00 2001 From: Renata Imai Date: Wed, 18 Sep 2024 11:58:37 -0300 Subject: [PATCH 57/57] admonitions --- .../plot_assignment_without_model.py | 30 +++++++++---------- .../plot_ipf_without_model.py | 26 ++++++++-------- .../assignment_workflows/plot_forecasting.py | 2 +- .../plot_public_transit_assignment.py | 5 ++-- .../plot_route_choice_basics.py | 4 +-- .../plot_route_choice_set.py | 4 +-- .../plot_subarea_analysis.py | 4 +-- .../creating_models/plot_create_from_gmns.py | 21 +++++++------ .../creating_models/plot_create_from_layer.py | 28 ++++++++--------- .../creating_models/plot_create_from_osm.py | 23 +++++++------- .../creating_models/plot_create_zoning.py | 24 +++++++-------- .../creating_models/plot_import_gtfs.py | 13 ++++---- .../plot_moving_link_extremity.py | 21 +++++++------ .../editing_networks/plot_moving_nodes.py | 21 +++++++------ .../editing_networks/plot_splitting_link.py | 23 +++++++------- .../other_applications/plot_export_to_gmns.py | 21 +++++++------ .../plot_find_disconnected.py | 12 ++++---- .../skimming/plot_path_computation.py | 12 ++++---- .../source/examples/skimming/plot_skimming.py | 11 ++++--- .../visualization/plot_delaunay_lines.py | 11 ++++--- 20 files changed, 152 insertions(+), 164 deletions(-) diff --git a/docs/source/examples/aequilibrae_without_a_model/plot_assignment_without_model.py b/docs/source/examples/aequilibrae_without_a_model/plot_assignment_without_model.py index 5863578a0..2e106ca07 100644 --- a/docs/source/examples/aequilibrae_without_a_model/plot_assignment_without_model.py +++ b/docs/source/examples/aequilibrae_without_a_model/plot_assignment_without_model.py @@ -8,6 +8,20 @@ We are using `Sioux Falls data `_, from TNTP. """ +# %% +# .. admonition:: References +# +# * :ref:`static_traffic_assignment` + +# %% +# .. seealso:: +# Several functions, methods, classes and modules are used in this example: +# +# * :func:`aequilibrae.paths.Graph` +# * :func:`aequilibrae.paths.TrafficClass` +# * :func:`aequilibrae.paths.TrafficAssignment` +# * :func:`aequilibrae.matrix.AequilibraeMatrix` + # %% # Imports @@ -133,19 +147,3 @@ # %% # And at the Assignment report assig.report() - -# %% -# .. admonition:: References -# -# :ref:`aequilibrae-graphs` -# :ref:`traffic_assignment_procedure` -# :ref:`multiclass_equilibrium` - -# %% -# .. seealso:: -# The use of the following functions, methods, classes and modules is shown in this example: -# -# * :func:`aequilibrae.paths.Graph` -# * :func:`aequilibrae.paths.TrafficClass` -# * :func:`aequilibrae.paths.TrafficAssignment` -# * :func:`aequilibrae.matrix.AequilibraeMatrix` \ No newline at end of file diff --git a/docs/source/examples/aequilibrae_without_a_model/plot_ipf_without_model.py b/docs/source/examples/aequilibrae_without_a_model/plot_ipf_without_model.py index 56f117d72..bb5caedf4 100644 --- a/docs/source/examples/aequilibrae_without_a_model/plot_ipf_without_model.py +++ b/docs/source/examples/aequilibrae_without_a_model/plot_ipf_without_model.py @@ -15,6 +15,19 @@ `Ortúzar & Willumsen (2011) `_. """ +# %% +# .. admonition:: References +# +# * :ref:`all_about_aeq_matrices` +# * :ref:`validation` + +# %% +# .. seealso:: +# Several functions, methods, classes and modules are used in this example: +# +# * :func:`aequilibrae.matrix.AequilibraeMatrix` +# * :func:`aequilibrae.matrix.AequilibraeData` +# * :func:`aequilibrae.distribution.Ipf` # %% @@ -80,16 +93,3 @@ for line in fratar.report: print(line) -# %% -# .. admonition:: References -# -# * :ref:`all_about_aeq_matrices` -# * :ref:`validation` - -# %% -# .. seealso:: -# The use of the following functions, methods, classes and modules is shown in this example: -# -# * :func:`aequilibrae.matrix.AequilibraeMatrix` -# * :func:`aequilibrae.matrix.AequilibraeData` -# * :func:`aequilibrae.distribution.Ipf` \ No newline at end of file diff --git a/docs/source/examples/assignment_workflows/plot_forecasting.py b/docs/source/examples/assignment_workflows/plot_forecasting.py index fd86d7b25..c6dcf5255 100644 --- a/docs/source/examples/assignment_workflows/plot_forecasting.py +++ b/docs/source/examples/assignment_workflows/plot_forecasting.py @@ -12,7 +12,7 @@ """ # %% # .. seealso:: -# The use of the following functions, methods, classes and modules is shown in this example: +# Several functions, methods, classes and modules are used in this example: # # * :func:`aequilibrae.paths.Graph` # * :func:`aequilibrae.paths.TrafficClass` diff --git a/docs/source/examples/assignment_workflows/plot_public_transit_assignment.py b/docs/source/examples/assignment_workflows/plot_public_transit_assignment.py index 2bdab2c14..8a4d213d9 100644 --- a/docs/source/examples/assignment_workflows/plot_public_transit_assignment.py +++ b/docs/source/examples/assignment_workflows/plot_public_transit_assignment.py @@ -12,12 +12,11 @@ # %% # .. admonition:: References # -# * :ref:`transit_assignment_graph` -# * :ref:`transit_hyperpath_routing` +# * :ref:`transit_assignment` # %% # .. seealso:: -# The use of the following functions, methods, classes and modules is shown in this example: +# Several functions, methods, classes and modules are used in this example: # # * :func:`aequilibrae.transit.Transit` # * :func:`aequilibrae.transit.TransitGraphBuilder` diff --git a/docs/source/examples/assignment_workflows/plot_route_choice_basics.py b/docs/source/examples/assignment_workflows/plot_route_choice_basics.py index 90f51e60b..8be03f0c4 100644 --- a/docs/source/examples/assignment_workflows/plot_route_choice_basics.py +++ b/docs/source/examples/assignment_workflows/plot_route_choice_basics.py @@ -10,11 +10,11 @@ # %% # .. admonition:: References # -# :ref:`route_choice` +# * :ref:`route_choice` # %% # .. seealso:: -# The use of the following functions, methods, classes and modules is shown in this example: +# Several functions, methods, classes and modules are used in this example: # # * :func:`aequilibrae.paths.Graph` # * :func:`aequilibrae.paths.RouteChoice` diff --git a/docs/source/examples/assignment_workflows/plot_route_choice_set.py b/docs/source/examples/assignment_workflows/plot_route_choice_set.py index 209194b9d..9c2c3e572 100644 --- a/docs/source/examples/assignment_workflows/plot_route_choice_set.py +++ b/docs/source/examples/assignment_workflows/plot_route_choice_set.py @@ -10,11 +10,11 @@ # %% # .. admonition:: References # -# :ref:`route_choice` +# * :ref:`route_choice` # %% # .. seealso:: -# The use of the following functions, methods, classes and modules is shown in this example: +# Several functions, methods, classes and modules are used in this example: # # * :func:`aequilibrae.paths.RouteChoice` diff --git a/docs/source/examples/assignment_workflows/plot_subarea_analysis.py b/docs/source/examples/assignment_workflows/plot_subarea_analysis.py index 0287beb2b..79b16b6a1 100644 --- a/docs/source/examples/assignment_workflows/plot_subarea_analysis.py +++ b/docs/source/examples/assignment_workflows/plot_subarea_analysis.py @@ -9,10 +9,10 @@ .. admonition:: References - :ref:`route_choice` + * :ref:`route_choice` .. seealso:: - The use of the following functions, methods, classes and modules is shown in this example: + Several functions, methods, classes and modules are used in this example: * :func:`aequilibrae.paths.Graph` * :func:`aequilibrae.paths.RouteChoice` diff --git a/docs/source/examples/creating_models/plot_create_from_gmns.py b/docs/source/examples/creating_models/plot_create_from_gmns.py index 956585ac0..1ebbad0ea 100644 --- a/docs/source/examples/creating_models/plot_create_from_gmns.py +++ b/docs/source/examples/creating_models/plot_create_from_gmns.py @@ -8,6 +8,16 @@ The source files of this network are publicly available in the `GMNS GitHub repository `_ itself. """ +# %% +# .. admonition:: References +# +# * :ref:`importing_from_gmns_file` + +# %% +# .. seealso:: +# Several functions, methods, classes and modules are used in this example: +# +# * :func:`aequilibrae.project.Network.create_from_gmns` # %% @@ -125,14 +135,3 @@ # %% project.close() - -# %% -# .. admonition:: References -# -# * :ref:`importing_from_gmns_file` - -# %% -# .. seealso:: -# The use of the following functions, methods, classes and modules is shown in this example: -# -# * :func:`aequilibrae.project.Network.create_from_gmns` \ No newline at end of file diff --git a/docs/source/examples/creating_models/plot_create_from_layer.py b/docs/source/examples/creating_models/plot_create_from_layer.py index c0abe4bc0..06fb05946 100644 --- a/docs/source/examples/creating_models/plot_create_from_layer.py +++ b/docs/source/examples/creating_models/plot_create_from_layer.py @@ -11,6 +11,20 @@ We use Folium to visualize the resulting network. """ +# %% +# .. admonition:: References +# +# * :ref:`accessing_project_data` + +# %% +# .. seealso:: +# Several functions, methods, classes and modules are used in this example: +# +# * :func:`aequilibrae.project.network.Links` +# * :func:`aequilibrae.project.network.Nodes` +# * :func:`aequilibrae.project.network.Modes` +# * :func:`aequilibrae.project.network.LinkTypes` + # %% # Imports @@ -143,17 +157,3 @@ # %% project.close() - -# %% -# .. admonition:: References -# -# * :ref:`accessing_project_data` - -# %% -# .. seealso:: -# The use of the following functions, methods, classes and modules is shown in this example: -# -# * :func:`aequilibrae.project.network.Links` -# * :func:`aequilibrae.project.network.Nodes` -# * :func:`aequilibrae.project.network.Modes` -# * :func:`aequilibrae.project.network.LinkTypes` \ No newline at end of file diff --git a/docs/source/examples/creating_models/plot_create_from_osm.py b/docs/source/examples/creating_models/plot_create_from_osm.py index 162cf5058..b41739fb5 100644 --- a/docs/source/examples/creating_models/plot_create_from_osm.py +++ b/docs/source/examples/creating_models/plot_create_from_osm.py @@ -6,8 +6,18 @@ In this example, we show how to create an empty project and populate it with a network from OpenStreetMap. -This time we will use Folium to visualize the network. +This time we will use GeoPandas to visualize the network. """ +# %% +# .. admonition:: References +# +# * :ref:`importing_from_osm` + +# %% +# .. seealso:: +# Several functions, methods, classes and modules are used in this example: +# +# * :func:`aequilibrae.project.Network.create_from_osm` # %% @@ -54,14 +64,3 @@ # %% project.close() - -# %% -# .. admonition:: References -# -# * :ref:`importing_from_osm` - -# %% -# .. seealso:: -# The use of the following functions, methods, classes and modules is shown in this example: -# -# * :func:`aequilibrae.project.Network.create_from_osm` diff --git a/docs/source/examples/creating_models/plot_create_zoning.py b/docs/source/examples/creating_models/plot_create_zoning.py index 1a909a4fc..e8f03afad 100644 --- a/docs/source/examples/creating_models/plot_create_zoning.py +++ b/docs/source/examples/creating_models/plot_create_zoning.py @@ -16,6 +16,17 @@ you have the geometries for them. In that case, you can just skip the hex bin computation part of this notebook. """ +# %% +# .. admonition:: References +# +# * :ref:`Accessing project zones ` + +# %% +# .. seealso:: +# Several functions, methods, classes and modules are used in this example: +# +# * :func:`aequilibrae.project.Zoning` +# * :func:`aequilibrae.project.network.Nodes` # %% @@ -147,16 +158,3 @@ # %% project.close() - -# %% -# .. admonition:: References -# -# * :ref:`tables_zones` -# * :ref:`Accessing project zones ` - -# %% -# .. seealso:: -# The use of the following functions, methods, classes and modules is shown in this example: -# -# * :func:`aequilibrae.project.Zoning` -# * :func:`aequilibrae.project.network.Nodes` \ No newline at end of file diff --git a/docs/source/examples/creating_models/plot_import_gtfs.py b/docs/source/examples/creating_models/plot_import_gtfs.py index daa510aed..afb4ee776 100644 --- a/docs/source/examples/creating_models/plot_import_gtfs.py +++ b/docs/source/examples/creating_models/plot_import_gtfs.py @@ -8,6 +8,12 @@ We use data from Coquimbo, a city in La Serena Metropolitan Area in Chile. """ +# %% +# .. seealso:: +# Several functions, methods, classes and modules are used in this example: +# +# * :func:`aequilibrae.transit.Transit` +# * :func:`aequilibrae.transit.lib_gtfs.GTFSRouteSystemBuilder` # %% @@ -121,10 +127,3 @@ # %% project.close() - -# %% -# .. seealso:: -# The use of the following functions, methods, classes and modules is shown in this example: -# -# * :func:`aequilibrae.transit.Transit` -# * :func:`aequilibrae.transit.lib_gtfs.GTFSRouteSystemBuilder` \ No newline at end of file diff --git a/docs/source/examples/editing_networks/plot_moving_link_extremity.py b/docs/source/examples/editing_networks/plot_moving_link_extremity.py index 62b7f9d2f..ad036c127 100644 --- a/docs/source/examples/editing_networks/plot_moving_link_extremity.py +++ b/docs/source/examples/editing_networks/plot_moving_link_extremity.py @@ -7,6 +7,16 @@ In this example, we move a link extremity from one point to another and see what happens to the network. """ +# %% +# .. admonition:: References +# +# * :ref:`modifications_on_links_layer` + +# %% +# .. seealso:: +# Several functions, methods, classes and modules are used in this example: +# +# * :func:`aequilibrae.project.network.Links` # %% @@ -75,14 +85,3 @@ # %% project.close() - -# %% -# .. admonition:: References -# -# * :ref:`modifications_on_links_layer` - -# %% -# .. seealso:: -# The use of the following functions, methods, classes and modules is shown in this example: -# -# * :func:`aequilibrae.project.network.Links` \ No newline at end of file diff --git a/docs/source/examples/editing_networks/plot_moving_nodes.py b/docs/source/examples/editing_networks/plot_moving_nodes.py index 11402d43f..348f57cc8 100644 --- a/docs/source/examples/editing_networks/plot_moving_nodes.py +++ b/docs/source/examples/editing_networks/plot_moving_nodes.py @@ -7,6 +7,16 @@ In this example, we show how to mode a node in the network and look into what happens to the links. """ +# %% +# .. admonition:: References +# +# * :ref:`modifications_on_nodes_layer` + +# %% +# .. seealso:: +# Several functions, methods, classes and modules are used in this example: +# +# * :func:`aequilibrae.project.network.Nodes` # %% @@ -64,14 +74,3 @@ # %% project.close() - -# %% -# .. admonition:: References -# -# * :ref:`modifications_on_nodes_layer` - -# %% -# .. seealso:: -# The use of the following functions, methods, classes and modules is shown in this example: -# -# * :func:`aequilibrae.project.network.Nodes` \ No newline at end of file diff --git a/docs/source/examples/editing_networks/plot_splitting_link.py b/docs/source/examples/editing_networks/plot_splitting_link.py index 18341ce0b..8abc42175 100644 --- a/docs/source/examples/editing_networks/plot_splitting_link.py +++ b/docs/source/examples/editing_networks/plot_splitting_link.py @@ -7,6 +7,18 @@ In this example, we split a link right in the middle, while keeping all fields in the database equal. Distance is proportionally computed automatically in the database. """ +# %% +# .. admonition:: References +# +# * :ref:`modifications_on_links_layer` + +# %% +# .. seealso:: +# Several functions, methods, classes and modules are used in this example: +# +# * :func:`aequilibrae.project.network.Links` + + # %% # Imports @@ -89,14 +101,3 @@ # %% project.close() - -# %% -# .. admonition:: References -# -# * :ref:`modifications_on_links_layer` - -# %% -# .. seealso:: -# The use of the following functions, methods, classes and modules is shown in this example: -# -# * :func:`aequilibrae.project.network.Links` diff --git a/docs/source/examples/other_applications/plot_export_to_gmns.py b/docs/source/examples/other_applications/plot_export_to_gmns.py index 2e50e6ab8..cf2c3c278 100644 --- a/docs/source/examples/other_applications/plot_export_to_gmns.py +++ b/docs/source/examples/other_applications/plot_export_to_gmns.py @@ -9,6 +9,16 @@ (``create_from_gmns()``) using the GMNS example of Arlington Signals, which can be found in the GMNS repository on GitHub: https://github.com/zephyr-data-specs/GMNS """ +# %% +# .. admonition:: References +# +# * :ref:`aequilibrae_to_gmns` + +# %% +# .. seealso:: +# Several functions, methods, classes and modules are used in this example: +# +# * :func:`aequilibrae.project.Network.export_to_gmns` # %% @@ -97,14 +107,3 @@ # %% project.close() - -# %% -# .. admonition:: References -# -# * :ref:`aequilibrae_to_gmns` - -# %% -# .. seealso:: -# The use of the following functions, methods, classes and modules is shown in this example: -# -# * :func:`aequilibrae.project.Network.export_to_gmns` \ No newline at end of file diff --git a/docs/source/examples/other_applications/plot_find_disconnected.py b/docs/source/examples/other_applications/plot_find_disconnected.py index c8f38f56e..1b98fe5e8 100644 --- a/docs/source/examples/other_applications/plot_find_disconnected.py +++ b/docs/source/examples/other_applications/plot_find_disconnected.py @@ -8,6 +8,12 @@ We use the Nauru example to find disconnected links. """ +# %% +# .. seealso:: +# Several functions, methods, classes and modules are used in this example: +# +# * :func:`aequilibrae.paths.PathResults` + # %% # Imports @@ -107,9 +113,3 @@ # %% project.close() - -# %% -# .. seealso:: -# The use of the following functions, methods, classes and modules is shown in this example: -# -# * :func:`aequilibrae.paths.PathResults` diff --git a/docs/source/examples/skimming/plot_path_computation.py b/docs/source/examples/skimming/plot_path_computation.py index a6f0c7829..b1a82e665 100644 --- a/docs/source/examples/skimming/plot_path_computation.py +++ b/docs/source/examples/skimming/plot_path_computation.py @@ -6,6 +6,12 @@ In this example, we show how to perform path computation for Coquimbo, a city in La Serena Metropolitan Area in Chile. """ +# %% +# .. seealso:: +# Several functions, methods, classes and modules are used in this example: +# +# * :func:`aequilibrae.paths.PathResults` + # %% # Imports @@ -144,9 +150,3 @@ # %% project.close() - -# %% -# .. seealso:: -# The use of the following functions, methods, classes and modules is shown in this example: -# -# * :func:`aequilibrae.paths.PathResults` \ No newline at end of file diff --git a/docs/source/examples/skimming/plot_skimming.py b/docs/source/examples/skimming/plot_skimming.py index 201e46f0d..f1fec8057 100644 --- a/docs/source/examples/skimming/plot_skimming.py +++ b/docs/source/examples/skimming/plot_skimming.py @@ -6,6 +6,11 @@ In this example, we show how to perform network skimming for Coquimbo, a city in La Serena Metropolitan Area in Chile. """ +# %% +# .. seealso:: +# Several functions, methods, classes and modules are used in this example: +# +# * :func:`aequilibrae.paths.NetworkSkimming` # %% @@ -96,9 +101,3 @@ # %% project.close() - -# %% -# .. seealso:: -# The use of the following functions, methods, classes and modules is shown in this example: -# -# * :func:`aequilibrae.paths.NetworkSkimming` \ No newline at end of file diff --git a/docs/source/examples/visualization/plot_delaunay_lines.py b/docs/source/examples/visualization/plot_delaunay_lines.py index d05f56ec0..26fec4ce4 100644 --- a/docs/source/examples/visualization/plot_delaunay_lines.py +++ b/docs/source/examples/visualization/plot_delaunay_lines.py @@ -10,6 +10,11 @@ We use the Sioux Falls example once again. """ +# %% +# .. seealso:: +# Several functions, methods, classes and modules are used in this example: +# +# * :func:`aequilibrae.utils.create_delaunay_network.DelaunayAnalysis` # %% @@ -71,9 +76,3 @@ # %% # Close the project project.close() - -# %% -# .. seealso:: -# The use of the following functions, methods, classes and modules is shown in this example: -# -# * :func:`aequilibrae.utils.create_delaunay_network.DelaunayAnalysis`