Giter VIP home page Giter VIP logo

mappymatch's Introduction

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.

Map Matching Animation

The current matchers are:

  • LCSSMatcher: A matcher that implements the LCSS algorithm described in this paper. Works best with high resolution GPS traces.
  • OsrmMatcher: A light matcher that pings an OSRM server to request map matching results. See the official documentation for more info.
  • ValhallaMatcher: A matcher to ping a Valhalla server for map matching results.

Currently supported map formats are:

  • Open Street Maps

Installation

pip install mappymatch

If you have trouble with that, check out the docs for more detailed install instructions.

Example Usage

The current primary workflow is to use osmnx to download a road network and match it using the LCSSMatcher.

The LCSSMatcher implements the map matching 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.

usage:

from mappymatch import package_root
from mappymatch.constructs.geofence import Geofence
from mappymatch.constructs.trace import Trace
from mappymatch.maps.nx.nx_map import NxMap
from mappymatch.matchers.lcss.lcss import LCSSMatcher

trace = Trace.from_csv(package_root() / "resources/traces/sample_trace_1.csv")

# generate a geofence polygon that surrounds the trace; units are in meters;
# this is used to query OSM for a small map that we can match to
geofence = Geofence.from_trace(trace, padding=1e3)

# uses osmnx to pull a networkx map from the OSM database
nx_map = NxMap.from_geofence(geofence)

matcher = LCSSMatcher(nx_map)

matches = matcher.match_trace(trace)

# convert the matches to a dataframe
df = matches.matches_to_dataframe()

Example Notebooks

Example JupyterLab notebooks making use of mappymatch can be found in the mappymatch examples repository.

mappymatch's People

Contributors

dependabot[bot] avatar dganske avatar grahamwaters avatar jakeholden avatar jhoshiko avatar jsexauer avatar jt-eic avatar machallboyd avatar nowheremanx avatar nreinicke avatar rowlando13 avatar ryanskeith avatar sjirwin avatar uranusjr avatar vendingmachinefinder avatar yle-kot avatar yungchidanielcho avatar zenon18 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mappymatch's Issues

Add/update type hints

For all new code and existing code, we should ensure that type hints are used. Arguments, return values, and instance variables should all include type hints. Below are some examples:

class Trace:
    # Instance variable type hints:
    _frame: GeoDataFrame

    coords: List[Coordinate]
    crs: CRS

    # method with both argument type hints and a return type type hint
    @classmethod
    def build(cls, frame: GeoDataFrame, xy: bool = True) -> Trace:
        if xy:
            frame = frame.to_crs(XY_CRS)
        return Trace(frame)

Match expects the distance attribute to be a float

Match() defines its distance attribute as float and code elsewhere assumes that it is a float.
e.g., from compute_cutting_points()

if abs(m.distance - distance_epsilon) < cutting_thresh:
                        cutting_points.append(CuttingPoint(i))

however, in orsm'.py the function _parse_leg() (inner function of parse_osrm_json()) constructs Match objects with distance=None. this is both a mypy error and can cause an error in functions like compute_cutting_points()

discussed with @nreinicke and decided to change the None to float('infinity')

Known CI Issues

Have usort in CI. Need to remove. Isort is integrated.

Initially, will implement flake8 and eventually migrate to mypy. Not sure whether implement as precommit or CI

add shorthands for constructs classes, for easier notebook plotting

Currently, we have to call utils to plot the objects like trace, geofence, which always calls folium.

However, users might want a plot method just like geopandas.GeoDataframe:

  • trace.plot() which is equals to trace._frame.plot()
  • geofence.plot() which is equals to geofence.geometry

Update docstrings of classes/methods/functions

Add or update docstrings on existing classes/ functions. Function/method docstrings should include:

  • function/method description
  • arguments and return values

Class docstrings should include:

  • class description
  • public methods and instance variables

An example function docstring from Trace.from_geo_dataframe:

"""
Builds a trace from a geopandas dataframe
Expects the dataframe to have geometry column

:param frame: geopandas dataframe with _one_ trace
:param xy: should the trace be projected to epsg 3857?
:return: the trace built from the dataframe
"""

Examples fail to open HTML in WSL

Python’s webbrowser module (as of 3.10) cannot correctly launch an HTML file from inside WSL. python/cpython#89752

Nothing much we can do here (short of coming up with a fix for CPython), but something to keep in mind when someone else fails to run an example.

As a workaround, you can always launch the HTML file manually with /mnt/c/Windows/explorer.exe <path/to/html> (replace c with wherever your Windows system drive is).

Update read-the-docs to only build on merge into main

Right now we have a webhook into read the docs that will build the docs anytime there is any pull request related action. It would be more efficient to add a set into our CI workflow that just re-builds the docs when we have a pull request that gets merged into main.

Type checking error in matchers/lcss/constructs.py

In matchers/lcss/constructs.py (lines 157 - 160) np.argmax can return array of indices where the highest value is found. If there is only one highest value an int is returned. If an array is returned by argmax, this throws an error. Make a check for if the return value of argmax isn't an int.

"add_to_map" method for constructs classes

Current plot utils do not support layer overlapping. We may want to implement a method called "add_to_map" which accepts a folium map object and additional arguments for styling...

or we can create a method to return an object (maybe a geojson layer) which can be accepted by folium.

I propose to use GeoJSON as the data format internally.

pip install results in GEOS mismatch

When installing yamm via pip, there ends up being a mismatch between the shapely GEOS version and the pygeos version (see below). This doesn't seem to happen when installing via conda-forge.

../yamm/lib/python3.8/site-packages/geopandas/_compat.py:111: UserWarning: The Shapely GEOS version (3.10.2-CAPI-1.16.0) is incompatible with the GEOS version PyGEOS was compiled with (3.10.1-CAPI-1.16.0). Conversions between both will be slow.

Functional test for accuracy of map matcher

Create a functional test that can assess the accuracy of a particular map-matcher. This probably has to be done with respect to a particular road network.

An idea for how to do this:

  • create / download a set of sample traces that have a variety of frequencies / noise.
  • manually identify which points should go with which road network link
  • run a matcher over the sample traces
  • compare the matcher results to the hand-built results to define how accurate the matcher did (with respect to human classification).

Setup GitHub Actions

This might break out into separate sub issues but it would be nice to setup some GitHub actions for the following:

  • publishing the package on PyPi (see #12)
  • building the package over multiple python versions
  • automated testing
  • building documentation and publish to GitHub pages (see #16)

What else?

Add support for python 3.9 / 3.10

Most of the development for this package has been done with python 3.8. Investigate what it would take to support python 3.9 and python 3.10.

Improve visualization of matches

We currently have simple visualizations in yamm.utils.plots, but we should improve these visualizations to plot the points with a line connecting to the matching road segment.

Support other OSM road network modes

Right now the OSM map reader only support the "drive" mode.

Parameterize this such that a user could specify which mode to use for creating a road network to match to.

We could provide this input in a string, or, we could create an Enum for the network types that we support. Looking at the osmnx documentation, it looks like the current supported network types are: "all_private", "all", "bike", "drive", "drive_service", "walk".

Remove pygeos as a direct dependency

Since pygeos is going to be incorporated into shapely 2.0, we can remove all of our direct usages of pygeos and only use shapely for all of our geometry. Once pygeos gets incorporated into shapely we can reap the benefits of the performance at that point.

Check for optional metadata in nx_map

Currently, Road has optional metadata. However, in nx_map, there is no check to see if the Road has metadata and the code assumes that metadata is present. We should add a check to confirm the existence of metadata if it is optional or change Road.metadata to no longer be optional.

Closing this issue in favor of #60

Load Trace from gpx file

Add a new @classmethod to the Trace class that can load a trace from a provided GPX file.

This could probably be done with some regex parsing, similar to here.

It would be nice to test this using a small sample / dummy .gpx file.

Pre Open Source TODOs

A set of tasks that need to be ticked off before we open source this package.

  • remove direct references to any internal trolley schemas (I think this is fully contained here)
  • update the README with more descriptive language
  • add license
  • add a set of examples

Figure out read the docs versioning

Read the docs supports versioning (right now we don't have multiple versions so this doesn't matter yet). We'll need to figure out how to configure read the docs to maintain a different set of documents for each version.

Create Developer Documentation

Create developer documentation. Some sections we could include:

  • Get started or quick start guide

  • a variety of examples/use cases ranging from simple to complex

  • API documentation

  • matcher documentation

Add method / utility for writing matches to file

Right now matches are returned as a list of Match objects. It might make sense to encapsulate the result of a matcher as an MatchResult object that could implement methods like to_csv().

Another idea is to add a matches attribute to the Trace construct. Then, the matcher could just return a copy of the input trace with the matches appended to it, allowing the user to write trace and matches to file in the same place.

Get existing unit tests working

When checking out the project at the beginning of the sprint, tests were initially failing. This appears to be because the length of sample_trace_1.txt changed without the test being updated.

I also added a basic asserts to test_osm so that test makes sure the correct osm_map is returned.

Fix easy mypy errors

mypy yamm shows a few errors, but two sets of errors are easily resolvable

Errors in class Trace

yamm/constructs/trace.py:45: error: Name "coords" already defined on line 20
yamm/constructs/trace.py:68: error: Name "crs" already defined on line 21

since coords and crs are defined as properties, the declaration at the top of the class are an unneeded extra definitions

Errors in plot_map()

yamm/utils/plot.py:124: error: "MapInterface" has no attribute "g"
yamm/utils/plot.py:126: error: "MapInterface" has no attribute "_geom_key"
yamm/utils/plot.py:126: error: "MapInterface" has no attribute "crs"

plot_map() is a utility function which is only used in the examples. its type hint is MapInterface but the implementation is specific to NxMap. Given the function's limited usage, this type hint can be changed to the specific type.

Tests fails out of box when environment.yml is used to set up the environment.

After installing the environment via environment.yml and then running tests, it fails like so:

=============================================================================== test session starts ===============================================================================
platform darwin -- Python 3.8.8, pytest-6.2.3, py-1.10.0, pluggy-0.13.1
rootdir: /Users/ryan/code/yamm
plugins: anyio-2.2.0
collected 0 items / 4 errors

===================================================================================== ERRORS ======================================================================================
_____________________________________________________________________ ERROR collecting tests/test_geofence.py _____________________________________________________________________
ImportError while importing test module '/Users/ryan/code/yamm/tests/test_geofence.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
../../anaconda3/lib/python3.8/importlib/__init__.py:127: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
tests/test_geofence.py:4: in <module>
    from yamm.constructs.geofence import Geofence
yamm/constructs/geofence.py:4: in <module>
    from geopandas import read_file
E   ModuleNotFoundError: No module named 'geopandas'
_______________________________________________________________________ ERROR collecting tests/test_osm.py ________________________________________________________________________
ImportError while importing test module '/Users/ryan/code/yamm/tests/test_osm.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
../../anaconda3/lib/python3.8/importlib/__init__.py:127: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
tests/test_osm.py:4: in <module>
    from yamm.constructs.geofence import Geofence
yamm/constructs/geofence.py:4: in <module>
    from geopandas import read_file
E   ModuleNotFoundError: No module named 'geopandas'
__________________________________________________________________ ERROR collecting tests/test_process_trace.py ___________________________________________________________________
ImportError while importing test module '/Users/ryan/code/yamm/tests/test_process_trace.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
../../anaconda3/lib/python3.8/importlib/__init__.py:127: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
tests/test_process_trace.py:4: in <module>
    from yamm.constructs.trace import Trace
yamm/constructs/trace.py:9: in <module>
    from geopandas import GeoDataFrame, points_from_xy, read_file, read_parquet
E   ModuleNotFoundError: No module named 'geopandas'
______________________________________________________________________ ERROR collecting tests/test_trace.py _______________________________________________________________________
ImportError while importing test module '/Users/ryan/code/yamm/tests/test_trace.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
../../anaconda3/lib/python3.8/importlib/__init__.py:127: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
tests/test_trace.py:6: in <module>
    from yamm.constructs.trace import Trace
yamm/constructs/trace.py:9: in <module>
    from geopandas import GeoDataFrame, points_from_xy, read_file, read_parquet
E   ModuleNotFoundError: No module named 'geopandas'
============================================================================= short test summary info =============================================================================
ERROR tests/test_geofence.py
ERROR tests/test_osm.py
ERROR tests/test_process_trace.py
ERROR tests/test_trace.py

Distribute type information

When installing mappymatch from PyPI and importing it into another project I get the mypy error:

Skipping analyzing "mappymatch.constructs.geofence": module is installed, but missing library stubs or py.typed marker

We should update our setup.py file to distribute type information (see here)

LCSSMatcher forward and reverse merge simplifying

The forward_merge and reverse_merge functions share the same core code but just reversed. The reverse_merge function could be done by reversing the inputted list and call the forward_merge function and then reverse the results.

Also there is a small issue in the forward merge see the following:
forward_merge([5, 4, 3, 2, 1], lambda x: x < 3) returns [5, 4, 3, 3]
but if there were merges already done in the list like:
forward_merge([1, 2, 3, 4, 5, 4, 3, 2, 1], lambda x: x < 3) return [6, 4, 5, 4, 3, 2, 1]

This can be fixed by running the _flatten command when there are items to merge.

Setting up dev env on Windows throws GDAL exception

While following the install documents on Windows, the Fiona package fails to install with a GDAL Exception.

In order to replicate the issue:

  1. Create environment conda create -n yammbug python=3.8
  2. Activate environment conda activate yammbug
  3. Install yamm in editable mode pip install -m .

Exception:

Collecting fiona>=1.8
  Using cached Fiona-1.8.21.tar.gz (1.0 MB)
    ERROR: Command errored out with exit status 1:
     command: 'c:\Users\willc\miniconda3\envs\gdaltest1\python.exe' -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'C:\\Users\\willc\\AppData\\Local\\Temp\\pip-install-5zcy45b7\\fiona\\setup.py'"'"'; __
file__='"'"'C:\\Users\\willc\\AppData\\Local\\Temp\\pip-install-5zcy45b7\\fiona\\setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();ex
ec(compile(code, __file__, '"'"'exec'"'"'))' egg_info --egg-base 'C:\Users\willc\AppData\Local\Temp\pip-pip-egg-info-iigt1ecz'
         cwd: C:\Users\willc\AppData\Local\Temp\pip-install-5zcy45b7\fiona\
    Complete output (1 lines):
    A GDAL API version must be specified. Provide a path to gdal-config using a GDAL_CONFIG environment variable or use a GDAL_VERSION environment variable.
    ----------------------------------------
ERROR: Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.