diff --git a/.github/workflows/deploy-docs.yaml b/.github/workflows/deploy-docs.yaml index 043bfe2..81fbe61 100644 --- a/.github/workflows/deploy-docs.yaml +++ b/.github/workflows/deploy-docs.yaml @@ -29,6 +29,10 @@ jobs: conda install -c conda-forge osmnx pip install ".[dev]" + - name: Convert examples to notebooks + run: | + python docs/examples/_convert_examples_to_notebooks.py + - name: Build book run: | jupyter-book build docs/ diff --git a/.github/workflows/lint-test.yml b/.github/workflows/lint-test.yml index bbfcade..6107f31 100644 --- a/.github/workflows/lint-test.yml +++ b/.github/workflows/lint-test.yml @@ -18,15 +18,23 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} + - name: Install dependencies run: | python -m pip install --upgrade pip pip install .[tests] + - name: Run ruff linting run: ruff check . + - name: Run ruff formatting run: ruff format --check + - name: Run mypy run: mypy . + - name: Run Tests run: pytest tests + + - name: Run Examples + run: python docs/examples/lcss_example.py diff --git a/.gitignore b/.gitignore index f1b217f..6ef6b8f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ .DS_Store +*.ipynb + .idea/ cache/ .vscode/ @@ -18,6 +20,7 @@ __pycache__/ # Distribution / packaging .Python build/ +_build/ develop-eggs/ dist/ downloads/ @@ -79,6 +82,7 @@ instance/ # Sphinx documentation docs/_build/ +docs/_autosummary/ # PyBuilder .pybuilder/ diff --git a/CLAUDE.md b/CLAUDE.md index 07804a9..d624a1c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -14,4 +14,9 @@ Mappymatch is a python package used to match a series of GPS waypoints (Trace) t ``` pixi run -e dev check ``` +### building the docs + +``` +pixi run docs +``` diff --git a/LICENSE b/LICENSE index 2682a06..690f3a4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 3-Clause License -Copyright (c) 2022, Alliance for Sustainable Energy, LLC +Copyright (c) 2022, Alliance for Energy Innovation, LLC All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/README.md b/README.md index e4cc696..33510c0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # mappymatch -Mappymatch is a pure-python package developed and open sourced by the National Renewable Energy Laboratory. It contains a collection of "Matchers" that enable matching a GPS trace (series of GPS coordinates) to a map. +Mappymatch is a pure-python package developed and open sourced by the National Laboratory of the Rockies. It contains a collection of "Matchers" that enable matching a GPS trace (series of GPS coordinates) to a map.  diff --git a/docs/_autosummary/mappymatch.constructs.coordinate.rst b/docs/_autosummary/mappymatch.constructs.coordinate.rst deleted file mode 100644 index e5ffeed..0000000 --- a/docs/_autosummary/mappymatch.constructs.coordinate.rst +++ /dev/null @@ -1,12 +0,0 @@ -mappymatch.constructs.coordinate -================================ - -.. automodule:: mappymatch.constructs.coordinate - - - .. rubric:: Classes - - .. autosummary:: - - Coordinate - \ No newline at end of file diff --git a/docs/_autosummary/mappymatch.constructs.geofence.rst b/docs/_autosummary/mappymatch.constructs.geofence.rst deleted file mode 100644 index 71f2a8a..0000000 --- a/docs/_autosummary/mappymatch.constructs.geofence.rst +++ /dev/null @@ -1,12 +0,0 @@ -mappymatch.constructs.geofence -============================== - -.. automodule:: mappymatch.constructs.geofence - - - .. rubric:: Classes - - .. autosummary:: - - Geofence - \ No newline at end of file diff --git a/docs/_autosummary/mappymatch.constructs.match.rst b/docs/_autosummary/mappymatch.constructs.match.rst deleted file mode 100644 index 578163d..0000000 --- a/docs/_autosummary/mappymatch.constructs.match.rst +++ /dev/null @@ -1,12 +0,0 @@ -mappymatch.constructs.match -=========================== - -.. automodule:: mappymatch.constructs.match - - - .. rubric:: Classes - - .. autosummary:: - - Match - \ No newline at end of file diff --git a/docs/_autosummary/mappymatch.constructs.road.rst b/docs/_autosummary/mappymatch.constructs.road.rst deleted file mode 100644 index ffc1cf0..0000000 --- a/docs/_autosummary/mappymatch.constructs.road.rst +++ /dev/null @@ -1,13 +0,0 @@ -mappymatch.constructs.road -========================== - -.. automodule:: mappymatch.constructs.road - - - .. rubric:: Classes - - .. autosummary:: - - Road - RoadId - \ No newline at end of file diff --git a/docs/_autosummary/mappymatch.constructs.rst b/docs/_autosummary/mappymatch.constructs.rst deleted file mode 100644 index 70cb562..0000000 --- a/docs/_autosummary/mappymatch.constructs.rst +++ /dev/null @@ -1,17 +0,0 @@ -mappymatch.constructs -===================== - -.. automodule:: mappymatch.constructs - - -.. rubric:: Modules - -.. autosummary:: - :toctree: - :recursive: - - coordinate - geofence - match - road - trace diff --git a/docs/_autosummary/mappymatch.constructs.trace.rst b/docs/_autosummary/mappymatch.constructs.trace.rst deleted file mode 100644 index 4aba5d7..0000000 --- a/docs/_autosummary/mappymatch.constructs.trace.rst +++ /dev/null @@ -1,12 +0,0 @@ -mappymatch.constructs.trace -=========================== - -.. automodule:: mappymatch.constructs.trace - - - .. rubric:: Classes - - .. autosummary:: - - Trace - \ No newline at end of file diff --git a/docs/_autosummary/mappymatch.maps.igraph.igraph_map.rst b/docs/_autosummary/mappymatch.maps.igraph.igraph_map.rst deleted file mode 100644 index b3f81ec..0000000 --- a/docs/_autosummary/mappymatch.maps.igraph.igraph_map.rst +++ /dev/null @@ -1,12 +0,0 @@ -mappymatch.maps.igraph.igraph\_map -================================== - -.. automodule:: mappymatch.maps.igraph.igraph_map - - - .. rubric:: Classes - - .. autosummary:: - - IGraphMap - \ No newline at end of file diff --git a/docs/_autosummary/mappymatch.maps.igraph.rst b/docs/_autosummary/mappymatch.maps.igraph.rst deleted file mode 100644 index 0eb47e0..0000000 --- a/docs/_autosummary/mappymatch.maps.igraph.rst +++ /dev/null @@ -1,13 +0,0 @@ -mappymatch.maps.igraph -====================== - -.. automodule:: mappymatch.maps.igraph - - -.. rubric:: Modules - -.. autosummary:: - :toctree: - :recursive: - - igraph_map diff --git a/docs/_autosummary/mappymatch.maps.map_interface.rst b/docs/_autosummary/mappymatch.maps.map_interface.rst deleted file mode 100644 index 28b4ee1..0000000 --- a/docs/_autosummary/mappymatch.maps.map_interface.rst +++ /dev/null @@ -1,12 +0,0 @@ -mappymatch.maps.map\_interface -============================== - -.. automodule:: mappymatch.maps.map_interface - - - .. rubric:: Classes - - .. autosummary:: - - MapInterface - \ No newline at end of file diff --git a/docs/_autosummary/mappymatch.maps.nx.nx_map.rst b/docs/_autosummary/mappymatch.maps.nx.nx_map.rst deleted file mode 100644 index 1ea5049..0000000 --- a/docs/_autosummary/mappymatch.maps.nx.nx_map.rst +++ /dev/null @@ -1,12 +0,0 @@ -mappymatch.maps.nx.nx\_map -========================== - -.. automodule:: mappymatch.maps.nx.nx_map - - - .. rubric:: Classes - - .. autosummary:: - - NxMap - \ No newline at end of file diff --git a/docs/_autosummary/mappymatch.maps.nx.readers.osm_readers.rst b/docs/_autosummary/mappymatch.maps.nx.readers.osm_readers.rst deleted file mode 100644 index bdbf13b..0000000 --- a/docs/_autosummary/mappymatch.maps.nx.readers.osm_readers.rst +++ /dev/null @@ -1,20 +0,0 @@ -mappymatch.maps.nx.readers.osm\_readers -======================================= - -.. automodule:: mappymatch.maps.nx.readers.osm_readers - - - .. rubric:: Functions - - .. autosummary:: - - compress - nx_graph_from_osmnx - parse_osmnx_graph - - .. rubric:: Classes - - .. autosummary:: - - NetworkType - \ No newline at end of file diff --git a/docs/_autosummary/mappymatch.maps.nx.readers.rst b/docs/_autosummary/mappymatch.maps.nx.readers.rst deleted file mode 100644 index e40e0bb..0000000 --- a/docs/_autosummary/mappymatch.maps.nx.readers.rst +++ /dev/null @@ -1,13 +0,0 @@ -mappymatch.maps.nx.readers -========================== - -.. automodule:: mappymatch.maps.nx.readers - - -.. rubric:: Modules - -.. autosummary:: - :toctree: - :recursive: - - osm_readers diff --git a/docs/_autosummary/mappymatch.maps.nx.rst b/docs/_autosummary/mappymatch.maps.nx.rst deleted file mode 100644 index d5f45ee..0000000 --- a/docs/_autosummary/mappymatch.maps.nx.rst +++ /dev/null @@ -1,14 +0,0 @@ -mappymatch.maps.nx -================== - -.. automodule:: mappymatch.maps.nx - - -.. rubric:: Modules - -.. autosummary:: - :toctree: - :recursive: - - nx_map - readers diff --git a/docs/_autosummary/mappymatch.maps.rst b/docs/_autosummary/mappymatch.maps.rst deleted file mode 100644 index f9a0dd5..0000000 --- a/docs/_autosummary/mappymatch.maps.rst +++ /dev/null @@ -1,15 +0,0 @@ -mappymatch.maps -=============== - -.. automodule:: mappymatch.maps - - -.. rubric:: Modules - -.. autosummary:: - :toctree: - :recursive: - - igraph - map_interface - nx diff --git a/docs/_autosummary/mappymatch.matchers.lcss.constructs.rst b/docs/_autosummary/mappymatch.matchers.lcss.constructs.rst deleted file mode 100644 index 4d9268f..0000000 --- a/docs/_autosummary/mappymatch.matchers.lcss.constructs.rst +++ /dev/null @@ -1,13 +0,0 @@ -mappymatch.matchers.lcss.constructs -=================================== - -.. automodule:: mappymatch.matchers.lcss.constructs - - - .. rubric:: Classes - - .. autosummary:: - - CuttingPoint - TrajectorySegment - \ No newline at end of file diff --git a/docs/_autosummary/mappymatch.matchers.lcss.lcss.rst b/docs/_autosummary/mappymatch.matchers.lcss.lcss.rst deleted file mode 100644 index 1b53867..0000000 --- a/docs/_autosummary/mappymatch.matchers.lcss.lcss.rst +++ /dev/null @@ -1,12 +0,0 @@ -mappymatch.matchers.lcss.lcss -============================= - -.. automodule:: mappymatch.matchers.lcss.lcss - - - .. rubric:: Classes - - .. autosummary:: - - LCSSMatcher - \ No newline at end of file diff --git a/docs/_autosummary/mappymatch.matchers.lcss.ops.rst b/docs/_autosummary/mappymatch.matchers.lcss.ops.rst deleted file mode 100644 index 60f6596..0000000 --- a/docs/_autosummary/mappymatch.matchers.lcss.ops.rst +++ /dev/null @@ -1,23 +0,0 @@ -mappymatch.matchers.lcss.ops -============================ - -.. automodule:: mappymatch.matchers.lcss.ops - - - .. rubric:: Functions - - .. autosummary:: - - add_matches_for_stationary_points - drop_stationary_points - find_stationary_points - new_path - same_trajectory_scheme - split_trajectory_segment - - .. rubric:: Classes - - .. autosummary:: - - StationaryIndex - \ No newline at end of file diff --git a/docs/_autosummary/mappymatch.matchers.lcss.rst b/docs/_autosummary/mappymatch.matchers.lcss.rst deleted file mode 100644 index e8d106f..0000000 --- a/docs/_autosummary/mappymatch.matchers.lcss.rst +++ /dev/null @@ -1,16 +0,0 @@ -mappymatch.matchers.lcss -======================== - -.. automodule:: mappymatch.matchers.lcss - - -.. rubric:: Modules - -.. autosummary:: - :toctree: - :recursive: - - constructs - lcss - ops - utils diff --git a/docs/_autosummary/mappymatch.matchers.lcss.utils.rst b/docs/_autosummary/mappymatch.matchers.lcss.utils.rst deleted file mode 100644 index cac3410..0000000 --- a/docs/_autosummary/mappymatch.matchers.lcss.utils.rst +++ /dev/null @@ -1,15 +0,0 @@ -mappymatch.matchers.lcss.utils -============================== - -.. automodule:: mappymatch.matchers.lcss.utils - - - .. rubric:: Functions - - .. autosummary:: - - compress - forward_merge - merge - reverse_merge - \ No newline at end of file diff --git a/docs/_autosummary/mappymatch.matchers.line_snap.rst b/docs/_autosummary/mappymatch.matchers.line_snap.rst deleted file mode 100644 index 8a1d37e..0000000 --- a/docs/_autosummary/mappymatch.matchers.line_snap.rst +++ /dev/null @@ -1,12 +0,0 @@ -mappymatch.matchers.line\_snap -============================== - -.. automodule:: mappymatch.matchers.line_snap - - - .. rubric:: Classes - - .. autosummary:: - - LineSnapMatcher - \ No newline at end of file diff --git a/docs/_autosummary/mappymatch.matchers.match_result.rst b/docs/_autosummary/mappymatch.matchers.match_result.rst deleted file mode 100644 index e9831c9..0000000 --- a/docs/_autosummary/mappymatch.matchers.match_result.rst +++ /dev/null @@ -1,12 +0,0 @@ -mappymatch.matchers.match\_result -================================= - -.. automodule:: mappymatch.matchers.match_result - - - .. rubric:: Classes - - .. autosummary:: - - MatchResult - \ No newline at end of file diff --git a/docs/_autosummary/mappymatch.matchers.matcher_interface.rst b/docs/_autosummary/mappymatch.matchers.matcher_interface.rst deleted file mode 100644 index 502963b..0000000 --- a/docs/_autosummary/mappymatch.matchers.matcher_interface.rst +++ /dev/null @@ -1,12 +0,0 @@ -mappymatch.matchers.matcher\_interface -====================================== - -.. automodule:: mappymatch.matchers.matcher_interface - - - .. rubric:: Classes - - .. autosummary:: - - MatcherInterface - \ No newline at end of file diff --git a/docs/_autosummary/mappymatch.matchers.osrm.rst b/docs/_autosummary/mappymatch.matchers.osrm.rst deleted file mode 100644 index 9463901..0000000 --- a/docs/_autosummary/mappymatch.matchers.osrm.rst +++ /dev/null @@ -1,18 +0,0 @@ -mappymatch.matchers.osrm -======================== - -.. automodule:: mappymatch.matchers.osrm - - - .. rubric:: Functions - - .. autosummary:: - - parse_osrm_json - - .. rubric:: Classes - - .. autosummary:: - - OsrmMatcher - \ No newline at end of file diff --git a/docs/_autosummary/mappymatch.matchers.rst b/docs/_autosummary/mappymatch.matchers.rst deleted file mode 100644 index f842aa3..0000000 --- a/docs/_autosummary/mappymatch.matchers.rst +++ /dev/null @@ -1,18 +0,0 @@ -mappymatch.matchers -=================== - -.. automodule:: mappymatch.matchers - - -.. rubric:: Modules - -.. autosummary:: - :toctree: - :recursive: - - lcss - line_snap - match_result - matcher_interface - osrm - valhalla diff --git a/docs/_autosummary/mappymatch.matchers.valhalla.rst b/docs/_autosummary/mappymatch.matchers.valhalla.rst deleted file mode 100644 index 39edf42..0000000 --- a/docs/_autosummary/mappymatch.matchers.valhalla.rst +++ /dev/null @@ -1,19 +0,0 @@ -mappymatch.matchers.valhalla -============================ - -.. automodule:: mappymatch.matchers.valhalla - - - .. rubric:: Functions - - .. autosummary:: - - build_match_result - build_path_from_result - - .. rubric:: Classes - - .. autosummary:: - - ValhallaMatcher - \ No newline at end of file diff --git a/docs/_autosummary/mappymatch.utils.crs.rst b/docs/_autosummary/mappymatch.utils.crs.rst deleted file mode 100644 index e6126d8..0000000 --- a/docs/_autosummary/mappymatch.utils.crs.rst +++ /dev/null @@ -1,6 +0,0 @@ -mappymatch.utils.crs -==================== - -.. automodule:: mappymatch.utils.crs - - \ No newline at end of file diff --git a/docs/_autosummary/mappymatch.utils.exceptions.rst b/docs/_autosummary/mappymatch.utils.exceptions.rst deleted file mode 100644 index a1996b5..0000000 --- a/docs/_autosummary/mappymatch.utils.exceptions.rst +++ /dev/null @@ -1,12 +0,0 @@ -mappymatch.utils.exceptions -=========================== - -.. automodule:: mappymatch.utils.exceptions - - - .. rubric:: Exceptions - - .. autosummary:: - - MapException - \ No newline at end of file diff --git a/docs/_autosummary/mappymatch.utils.geo.rst b/docs/_autosummary/mappymatch.utils.geo.rst deleted file mode 100644 index 2016df4..0000000 --- a/docs/_autosummary/mappymatch.utils.geo.rst +++ /dev/null @@ -1,14 +0,0 @@ -mappymatch.utils.geo -==================== - -.. automodule:: mappymatch.utils.geo - - - .. rubric:: Functions - - .. autosummary:: - - coord_to_coord_dist - latlon_to_xy - xy_to_latlon - \ No newline at end of file diff --git a/docs/_autosummary/mappymatch.utils.plot.rst b/docs/_autosummary/mappymatch.utils.plot.rst deleted file mode 100644 index 95536b0..0000000 --- a/docs/_autosummary/mappymatch.utils.plot.rst +++ /dev/null @@ -1,17 +0,0 @@ -mappymatch.utils.plot -===================== - -.. automodule:: mappymatch.utils.plot - - - .. rubric:: Functions - - .. autosummary:: - - plot_geofence - plot_map - plot_match_distances - plot_matches - plot_path - plot_trace - \ No newline at end of file diff --git a/docs/_autosummary/mappymatch.utils.process_trace.rst b/docs/_autosummary/mappymatch.utils.process_trace.rst deleted file mode 100644 index 21c0d84..0000000 --- a/docs/_autosummary/mappymatch.utils.process_trace.rst +++ /dev/null @@ -1,13 +0,0 @@ -mappymatch.utils.process\_trace -=============================== - -.. automodule:: mappymatch.utils.process_trace - - - .. rubric:: Functions - - .. autosummary:: - - remove_bad_start_from_trace - split_large_trace - \ No newline at end of file diff --git a/docs/_autosummary/mappymatch.utils.rst b/docs/_autosummary/mappymatch.utils.rst deleted file mode 100644 index 614dedc..0000000 --- a/docs/_autosummary/mappymatch.utils.rst +++ /dev/null @@ -1,18 +0,0 @@ -mappymatch.utils -================ - -.. automodule:: mappymatch.utils - - -.. rubric:: Modules - -.. autosummary:: - :toctree: - :recursive: - - crs - exceptions - geo - plot - process_trace - url diff --git a/docs/_autosummary/mappymatch.utils.url.rst b/docs/_autosummary/mappymatch.utils.url.rst deleted file mode 100644 index b927a28..0000000 --- a/docs/_autosummary/mappymatch.utils.url.rst +++ /dev/null @@ -1,12 +0,0 @@ -mappymatch.utils.url -==================== - -.. automodule:: mappymatch.utils.url - - - .. rubric:: Functions - - .. autosummary:: - - multiurljoin - \ No newline at end of file diff --git a/docs/_config.yml b/docs/_config.yml index 6077352..e6656ad 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -2,7 +2,7 @@ # Learn more at https://jupyterbook.org/customize/config.html title: mappymatch -author: National Renewable Energy Laboratory +author: National Laboratory of the Rockies # Force re-execution of notebooks on each build. # See https://jupyterbook.org/content/execute.html @@ -17,7 +17,7 @@ latex: # Information about where the book exists on the web repository: - url: https://github.com/NREL/mappymatch # Online location of your book + url: https://github.com/NLR/mappymatch # Online location of your book path_to_book: docs # Optional path to your book, relative to the repository root branch: main # Which branch of the repository should be used when creating links (optional) @@ -33,7 +33,6 @@ sphinx: - "sphinx.ext.autodoc" - "sphinx.ext.autosummary" - "sphinx.ext.viewcode" - - "sphinx_autodoc_typehints" - "sphinxcontrib.autoyaml" - "sphinxcontrib.mermaid" config: @@ -55,5 +54,4 @@ sphinx: member-order: bysource undoc-members: true private-members: false - autodoc_typehints: both mermaid_version: "10.8" diff --git a/docs/_toc.yml b/docs/_toc.yml index 223a3db..895f519 100644 --- a/docs/_toc.yml +++ b/docs/_toc.yml @@ -14,7 +14,7 @@ parts: - caption: Example chapters: - - file: lcss-example + - file: examples/lcss_example - caption: Reference chapters: diff --git a/docs/examples/_convert_examples_to_notebooks.py b/docs/examples/_convert_examples_to_notebooks.py new file mode 100644 index 0000000..e217f96 --- /dev/null +++ b/docs/examples/_convert_examples_to_notebooks.py @@ -0,0 +1,76 @@ +from pathlib import Path + +import nbformat + + +def script_to_notebook(script_path: Path, notebook_path: Path) -> None: + # Read the script + with open(script_path, "r") as script_file: + lines = script_file.readlines() + + notebook = nbformat.v4.new_notebook() + current_code_block: list[str] = [] + current_markdown_block: list[str] = [] + + def add_code_cell(block: list[str]) -> None: + if block: + notebook.cells.append(nbformat.v4.new_code_cell("".join(block))) + + def add_markdown_cell(block: list[str]) -> None: + if block: + notebook.cells.append(nbformat.v4.new_markdown_cell("".join(block).strip())) + + # markdown cells will be enclosed in triple quotes + # the remaining cells will be code cells + in_markdown = False + in_main = False + for line in lines: + # strip any ipython code blocks + if line.strip().startswith("# %%"): + continue + # strip def main(): line and track indentation state + if line.strip() == "def main():": + in_main = True + continue + # strip if __name__ == "__main__": and main() call + if line.strip() in ( + 'if __name__ == "__main__":', + "main()", + ): + continue + # dedent lines inside def main() + if in_main and not line.strip().startswith('"""'): + line = line[4:] if line.startswith(" ") else line + if line.strip().startswith('"""'): + # handle triple-quote toggle; dedent the """ line itself if inside main + if in_main and line.startswith(" "): + line = line[4:] + in_markdown = not in_markdown + if in_markdown: + add_code_cell(current_code_block) + current_code_block = [] + else: + add_markdown_cell(current_markdown_block) + current_markdown_block = [] + elif in_markdown: + # dedent markdown content inside def main() + if in_main and line.startswith(" "): + line = line[4:] + current_markdown_block.append(line) + else: + current_code_block.append(line) + + add_code_cell(current_code_block) + add_markdown_cell(current_markdown_block) + + with open(notebook_path, "w") as notebook_file: + nbformat.write(notebook, notebook_file) + + +if __name__ == "__main__": + here = Path(__file__).parent + examples = here.glob("*example.py") + for example in examples: + notebook = example.with_suffix(".ipynb") + script_to_notebook(example, notebook) + print(f"Converted {example} to {notebook}") diff --git a/docs/examples/lcss_example.py b/docs/examples/lcss_example.py new file mode 100644 index 0000000..1b351e1 --- /dev/null +++ b/docs/examples/lcss_example.py @@ -0,0 +1,216 @@ +""" +# LCSS Example + +An example of using the LCSSMatcher to match a gps trace to the Open Street Maps road network +""" + + +def main(): + from mappymatch import package_root + + """ + First, we load the trace from a file. + The mappymatch package has a few sample traces included that we can use for demonstration. + + Before we build the trace, though, let's take a look at the file to see how mappymatch expects the input data: + """ + + import pandas as pd + + df = pd.read_csv(package_root() / "resources/traces/sample_trace_3.csv") + df.head() + + """ + Notice that we expect the input data to be in the EPSG:4326 coordinate reference system. + If your input data is not in this format, you'll need to convert it prior to building a Trace object. + + In order to idenfiy which coordinate is which in a trace, mappymatch uses the dataframe index as the coordinate index and so in this case, we just have a simple range based index for each coordinate. + We could set a different index on the dataframe and mappymatch would use that to identify the coordinates. + + Now, let's load the trace from the same file: + """ + + from mappymatch.constructs.trace import Trace + + trace = Trace.from_csv( + package_root() / "resources/traces/sample_trace_3.csv", + lat_column="latitude", + lon_column="longitude", + xy=True, + ) + + """ + Notice here that we pass three optional arguments to the `from_csv` function. + By default, mappymatch expects the latitude and longitude columns to be named "latitude" and "longitude" but you can pass your own values if needed. + Also by default, mappymatch converts the trace into the web mercator coordinate reference system (EPSG:3857) by setting `xy=True`. + The LCSS matcher computes the cartesian distance between geometries and so a projected coordiante reference system is ideal. + In a future version of mappymatch we hope to support any projected coordiante system but right now we only support EPSG:3857. + + Okay, let's plot the trace to see what it looks like (mappymatch uses folium under the hood for plotting): + """ + + from mappymatch.utils.plot import plot_trace + + plot_trace(trace, point_color="black", line_color="yellow") + + """ + Next, we need to get a road map to match our Trace to. + One way to do this is to build a small geofence around the trace and then download a map that just fits around our trace: + """ + + from mappymatch.constructs.geofence import Geofence + + geofence = Geofence.from_trace(trace, padding=2e3) + + """ + Notice that we pass an optional argument to the constructor. + The padding defines how large around the trace we should build our geofence and is in the same units as the trace. + In our case, the trace has been projected to the web mercator CRS and so our units would be in approximate meters, 1e3 meters or 1 kilomter + + Now, let's plot both the trace and the geofence: + """ + + from mappymatch.utils.plot import plot_geofence + + plot_trace(trace, point_color="black", m=plot_geofence(geofence)) + + """ + At this point, we're ready to download a road network. + Mappymatch has a couple of ways to represent a road network: The `NxMap` and the `IGraphMap` which use `networkx` and `igraph`, respectively, under the hood to represent the road graph structure. + You might experiment with both to see if one is more performant or memory efficient in your use case. + + In this example we'll use the `NxMap`: + """ + + from mappymatch.maps.nx.nx_map import NxMap, NetworkType + + nx_map = NxMap.from_geofence( + geofence, + network_type=NetworkType.DRIVE, + ) + + """ + The `from_geofence` constructor uses the osmnx package under the hood to download a road network. + + Notice we pass the optional argument `network_type` which defaults to `NetworkType.DRIVE` but can be used to get a different network like `NetworkType.BIKE` or `NetworkType.WALK` + + Now, we can plot the map to make sure we have the network that we want to match to: + """ + + from mappymatch.utils.plot import plot_map + + plot_map(nx_map) + + """ + Now, we're ready to perform the actual map matching. + + In this example we'll use the `LCSSMatcher` which implements the algorithm described in this paper: + + [Zhu, Lei, Jacob R. Holden, and Jeffrey D. Gonder. + "Trajectory Segmentation Map-Matching Approach for Large-Scale, High-Resolution GPS Data." + Transportation Research Record: Journal of the Transportation Research Board 2645 (2017): 67-75.](https://doi.org/10.3141%2F2645-08) + + We won't go into detail here for how to tune the paramters but checkout the referenced paper for more details if you're interested. + The default parameters have been set based on internal testing on high resolution driving GPS traces. + """ + + from mappymatch.matchers.lcss.lcss import LCSSMatcher + + matcher = LCSSMatcher(nx_map) + + match_result = matcher.match_trace(trace) + + """ + Now that we have the results, let's plot them: + """ + + from mappymatch.utils.plot import plot_matches + + plot_matches(match_result.matches) + + match_result.path_to_geodataframe().plot() + + """ + The `plot_matches` function plots the roads that each point has been matched to and labels them with the road id. + + In some cases, if the trace is much sparser (for example if it was collected a lower resolution), you might want see the estimated path, rather than the explict matched roads. + + For example, let's reduce the trace frequency to every 30th point and re-match it: + """ + + reduced_trace = trace[0::30] + + plot_trace(reduced_trace, point_color="black", line_color="yellow") + + reduced_matches = matcher.match_trace(reduced_trace) + + plot_matches(reduced_matches.matches) + + """ + The match result also has a `path` attribute with the estiamted path through the network: + """ + + from mappymatch.utils.plot import plot_path + + plot_trace( + reduced_trace, + point_color="blue", + m=plot_path(reduced_matches.path, crs=trace.crs), + ) + + """ + Lastly, we might want to convert the results into a format more suitible for saving to file or merging with some other dataset. + To do this, we can convert the result into a dataframe: + """ + + result_df = reduced_matches.matches_to_dataframe() + result_df.head() + + """ + Here, for each coordinate, we have the distance to the matched road, and then attributes of the road itself like the geometry, the OSM node id and the road distance and travel time. + + We can also get a dataframe for the path: + """ + + path_df = reduced_matches.path_to_dataframe() + path_df.head() + + """ + Another thing we can do is to only get a certain set of road types to match to. For example, let's say I only want to consider highways and primary roads for matching, I can do so by passing a custom filter when building the road network: + """ + + nx_map = NxMap.from_geofence( + geofence, + network_type=NetworkType.DRIVE, + custom_filter='["highway"~"motorway|primary"]', + ) + + plot_map(nx_map) + + """ + Above you can see that now we have a much reduced graph to match to, let's see what happens + """ + + matcher = LCSSMatcher(nx_map) + + match_result = matcher.match_trace(trace) + + plot_matches(match_result.matches) + + """ + Plot the path + """ + + plot_path(match_result.path, crs=trace.crs) + + """ + Plot the geodataframe version of the path + """ + + path_gdf = match_result.path_to_geodataframe() + + path_gdf.plot() + + +if __name__ == "__main__": + main() diff --git a/docs/home.md b/docs/home.md index d5caaf3..8bacdac 100644 --- a/docs/home.md +++ b/docs/home.md @@ -1,6 +1,6 @@ # Mappymatch -Mappymatch is a pure-Python package developed and open-sourced by the National Renewable Energy Laboratory. It contains a collection of "Matchers" that enable matching a GPS trace (series of GPS coordinates) to a map. +Mappymatch is a pure-Python package developed and open-sourced by the National Laboratory of the Rockies. It contains a collection of "Matchers" that enable matching a GPS trace (series of GPS coordinates) to a map. ## The Current Matchers diff --git a/docs/lcss-example.ipynb b/docs/lcss-example.ipynb deleted file mode 100644 index 815eb4e..0000000 --- a/docs/lcss-example.ipynb +++ /dev/null @@ -1,158207 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "320f93ac-5965-44a4-8d20-40b8944a60a5", - "metadata": {}, - "source": [ - "# LCSS Example\n", - "\n", - "An example of using the LCSSMatcher to match a gps trace to the Open Street Maps road network" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "id": "e8e17861-dfc0-4ae8-a509-2ae7fe6ff608", - "metadata": {}, - "outputs": [], - "source": [ - "%%capture\n", - "!pip install mappymatch" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "id": "dbf22ede-f3b6-4940-b866-15263b8eeaff", - "metadata": {}, - "outputs": [], - "source": [ - "from mappymatch import package_root" - ] - }, - { - "cell_type": "markdown", - "id": "fcbd7d7d-91fc-4bbe-849f-b91b4d919c6f", - "metadata": {}, - "source": [ - "First, we load the trace from a file. \n", - "The mappymatch package has a few sample traces included that we can use for demonstration.\n", - "\n", - "Before we build the trace, though, let's take a look at the file to see how mappymatch expects the input data:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "a626d87a-382e-4d04-9e02-886f3a168701", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
| \n", - " | latitude | \n", - "longitude | \n", - "
|---|---|---|
| 0 | \n", - "39.655210 | \n", - "-104.919169 | \n", - "
| 1 | \n", - "39.655449 | \n", - "-104.919274 | \n", - "
| 2 | \n", - "39.655690 | \n", - "-104.919381 | \n", - "
| 3 | \n", - "39.655936 | \n", - "-104.919486 | \n", - "
| 4 | \n", - "39.656182 | \n", - "-104.919593 | \n", - "