Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

generate key in function #48

Merged
merged 66 commits into from
Mar 3, 2021

Conversation

singhish
Copy link
Contributor

  • added a timespan key to handle information regarding the timespans of reroutes (will talk to you @shankari during our meeting tomorrow to figure out where to grab this information -- not sure if this is already encoded in the start_fmt_date and end_fmt_date keys?)
  • created a testing folder final_sfbayarea_filled_reroutes for updated data containing reroute information

Copy link
Collaborator

@shankari shankari left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to see that you have figured out how to run the autofill code.
That is roughly the change I had in mind, with the modifications below.


# key that holds information regarding timespan of ground truth data
# TODO: determine where to find timespan info for trip
t["timespan"] = ""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is roughly what I had in mind. But I think that the timespan needs to be at the section/leg level, and not the trip level. The commit I made yesterday has an example of how a user might specify the valid timespan.

d647389#diff-babc6ee3762947af73e5aaae138f9c22badd4b75cd4c84b06d7cd0017ac36f62R397

So there are two "Wurster bikeshare station" entries and each of them is valid for some time range.
Note that the "Wurster bikeshare station" entries are in the leg and not the trip because the rest of the trip is not necessarily modified.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the copy code seems like it would be fairly similar

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a new commit, let me know if my updates reflect what you have in mind!

Copy link
Collaborator

@shankari shankari Jan 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait, why is there only one timespan in the filled leg?
shouldn't there be one for each leg?

Also, what did you think that the format of the timespan would look like?
it can't be a single duration number because then you don't know when that duration happened
if it is a range, would you have to parse it?

which is why I have valid_start_ts and valid_end_ts in my proposal

@@ -308,6 +308,11 @@ def validate_and_fill_eval_trips(curr_spec):
# Let's check again after we have inserted the shim legs
assert not has_duplicate_legs(t), \
"Found duplicate leg ids in trip %s" % t["id"]

Copy link
Collaborator

@shankari shankari Jan 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems like there should be one timespan entry for each l in ret_leg_list

@shankari
Copy link
Collaborator

looks a lot better.

  • Need to support shims as well
  • Need to support start_loc as well
  • Need to support trajectory as well

… this method to add temporal ground truths to start locs, shim legs, and trajectories
@singhish
Copy link
Contributor Author

my last commit should address those comments, take a look when you get time @shankari

Copy link
Collaborator

@shankari shankari left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks a lot better. Now you just have to fill in the values as we discussed yesterday:

  • override-specified values if they exist
  • spec-wide values if they don't

Have you pulled from new_spec_edits yet? the ucb_mtv timeline has some overriden entries so you should be able to test the fill code against that.

@@ -156,9 +156,28 @@ def get_route_from_relation(r):
return get_coords_for_relation(r["relation_id"],
r["start_node"], r["end_node"])

def _add_temporal_ground_truth(orig_loc):
# fill in timespan for which ground truth is valid (see issue #11)
# first, if start_loc/end_loc are dicts, we need to convert end_loc to a list of dicts.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: in the second instance, not only end_loc, both

@singhish
Copy link
Contributor Author

singhish commented Jan 25, 2021

ran into a couple issues while working on the new commit:

  • kept running into the error File "/Users/isingh/mobilitynet-analysis-scripts/spec_creation/osrm.py", line 33, in get_route url_to_query = OSRM_HOST + "/" + OSRM_ROUTES[mode_name] + "/" + route_coords_string NameError: name 'OSRM_HOST' is not defined
  • kept running into the error File "autofill_eval_spec.py", line 220, in validate_and_fill_leg rclist.append(get_route_from_relation(r)) UnboundLocalError: local variable 'r' referenced before assignment

also, there were a few escape characters that weren't fully escaped in train_bus_ebike_mtv_ucb.json, which I attempted to address

@shankari
Copy link
Collaborator

Addressing the issues in order:

kept running into the error File "/Users/isingh/mobilitynet-analysis-scripts/spec_creation/osrm.py", line 33, in get_route url_to_query = OSRM_HOST + "/" + OSRM_ROUTES[mode_name] + "/" + route_coords_string NameError: name 'OSRM_HOST' is not defined

short answer: you need to set the values in conf/net/ext_service/osrm.json (e.g. http://map.project-osrm.org/?hl=en)
long answer: I've been waffling a bit with this. If we only store the start and the end, and reconstruct the route on the fly, then the route can change if the routing engine changes. This is not a big issue if you create the spec just before you collect data with it, but I've realized that it doesn't necessarily work for the long term.

An alternative is to just specify polylines directly. Ask users to get polylines from the API (maybe using spec_creation/create_ground_truth_for_legs.ipynb). But then is there actually any advantage to auto-filling the route? That is the waffling immortalized here:
MobilityNet/mobilitynet.github.io#11 (comment)

What do you think?

@shankari
Copy link
Collaborator

kept running into the error File "autofill_eval_spec.py", line 220, in validate_and_fill_leg rclist.append(get_route_from_relation(r)) UnboundLocalError: local variable 'r' referenced before assignment

ah this should probably be rclist.append(get_route_from_relation(t["relation"])

@shankari
Copy link
Collaborator

shankari commented Jan 26, 2021

also, there were a few escape characters that weren't fully escaped in train_bus_ebike_mtv_ucb.json, which I attempted to address

I spot-checked a couple of those, and it looks like I manually created those polylines by encoding the coordinates from the filled spec. I don't see them in either spec_creation/final_sfbayarea/train_bus_ebike_sm_reroute_mtv_ucb.json or spec_creation/final_sfbayarea/train_bus_ebike_mtv_ucb.json

I wish I had taken better notes on how I was making the changes, but I didn't think I would return to them almost a year later.

edit:

One option is to have both the relation and the polyline as backup. we can always remove the polyline later. I'm going to go with this option even though I can think of many reasons why it is overkill just to get over my dithering.

Copy link
Collaborator

@shankari shankari left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems correct and complete for locations.
Next steps:

  • do something similar for trajectories
  • change the ground truth retrieval code - i.e. get_ground_truth_for_leg

spec_creation/autofill_eval_spec.py Outdated Show resolved Hide resolved
spec_creation/autofill_eval_spec.py Outdated Show resolved Hide resolved
# next, add dates if they do not exist
for l in loc:
if not l["properties"].get("valid_start_fmt_date"):
l["properties"]["valid_start_fmt_date"] = start_fmt_date
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We haven't finished discussing the design completely, but I think that the filled spec should store timestamps instead of a formatted string.

This related to the final step in the issue:

Change the code that lookup up the ground truth for the evaluation (in emeval/input/spec_details.py - e.g. get_ground_truth_for_leg)

Recall that we will need to retrieve the ground truth in get_ground_truth_leg to evaluate the accuracy. It will be much easier to retrieve the correct ground truth object if we can compare timestamps directly.

spec_creation/autofill_eval_spec.py Outdated Show resolved Hide resolved
@singhish
Copy link
Contributor Author

thanks for the comments -- would you be able to meet at our pencilled in 10am ET time tomorrow morning to go over them? had a few questions regarding trajectories, ORSM, and your point about design. sorry if this is late!

@singhish
Copy link
Contributor Author

thanks for the comments -- would you be able to meet at our pencilled in 10am ET time tomorrow morning to go over them? had a few questions regarding trajectories, ORSM, and your point about design. sorry if this is late!

Just now seeing that the 10am slot was only penciled in for last week -- I will follow up with you during our usual 1pm time. Thanks!

@singhish
Copy link
Contributor Author

incorporated the suggestions I didn't have questions about in my most recent commit. For now I am copy-pasting the following lines into osrm.py at line 32 to make things work (basically just copy-pasting everything in osrm.json):

OSRM_HOST = "https://routing.openstreetmap.de"
OSRM_ROUTES = {
    "CAR": "routed-car/route/v1/driving",
    "WALKING": "routed-foot/route/v1/driving",
    "BICYCLING": "routed-bike/route/v1/driving",
    "BUS": "routed-car/route/v1/driving"
}

though I end up running into the error:

Traceback (most recent call last):
  File "autofill_eval_spec.py", line 394, in <module>
    eval_spec = validate_and_fill_eval_trips(calib_spec)
  File "autofill_eval_spec.py", line 331, in validate_and_fill_eval_trips
    ret_leg_list.append(validate_and_fill_leg(l, default_start_fmt_date, default_end_fmt_date))
  File "autofill_eval_spec.py", line 238, in validate_and_fill_leg
    "coordinates": [coords_swap(rc) for rc in route_coords]
UnboundLocalError: local variable 'route_coords' referenced before assignment

Will ask you about the OSRM issues at 1 -- it was working fine with the car_scooter_brex_san_jose data, but I seem to only be running into errors with the train_bus_ebike_mtv_ucb data.

@singhish
Copy link
Contributor Author

saw that the path to osrm.json was wrong in osrm.py, fixed it in my most recent commit. this might not be path you had originally intended, however

@shankari
Copy link
Collaborator

shankari commented Jan 26, 2021

in general, during the evaluation, we read the actual sensed data from the e-mission server (cardshark -> localhost). We also read the ground truth spec from the e-mission server (cardshark -> localhost). This is the part in the notebooks about

sdunp = eisd.SpecDetails(DATASTORE_URL, AUTHOR_EMAIL, \"car_scooter_brex_san_jose\")

if you dump out sdunp, you will find the same filled spec that it checked in to the repo (car_scooter_brex_san_jose.filled.json). As an aside, if/when we move to livewire, we can consider reading this directly from GitHub instead of the e-mission server.

then, when we want to compare whatever analysis we have done against the ground truth to generate metrics, we need to get the ground truth for each leg. We do this by traversing the tree of sensed data in all its configurations and then finding the ground truth each leg. Note that the ground truth does not depend on the sensing configuration, only the leg of the trip.

e.g. in Evaluate_power_vs_motion_activity.ipynb

250     "                    for section in tr[\"evaluation_section_ranges\"]:\n",
251     "                        section_gt_leg = pv.spec_details.get_ground_truth_for_leg(tr[\"trip_id_base\"],\n",
252     "                                                                                  section[\"trip_id_base\"])\n",
253     "                        if section_gt_leg[\"type\"] == \"WAITING\":\n",
254     "                            print(\"Skipping WAITING section %s %s with potential partway transitions\" %\n",
255     "                                  (tr[\"trip_id\"], section[\"trip_id\"]))\n",
256     "                            continue\n",
257     "                        print(12 * ' ',section[\"trip_id\"], section[\"trip_id_base\"], tr[\"trip_id\"])\n",
258     "                        sensed_section_range = matching_section_map[section[\"trip_id\"]]\n",
259     "                        results = ems.get_count_start_end_ts_diff(section, sensed_section_range)\n",

in particular pv.spec_details.get_ground_truth_for_leg

Now, the ground truth depends on the time at which the data was collected. So you need to change the implementation of get_ground_truth_for_leg to see the section time and return the appropriate choice of ground truth.

the get_ground_truth_for_leg is defined in emeval/input/spec_details.py

 98     def get_ground_truth_for_leg(self, trip_id, leg_id):
 99         for t in self.curr_spec_entry["data"]["label"]["evaluation_trips"]:
100             if t["id"] == trip_id:
101                 ll = [l for l in t["legs"] if l["id"] == leg_id]
102                 # print(leg_id, len(ll), [l["id"] for l in ll])
103                 if len(ll) == 1:
104                     return ll[0]

This will not affect the ground truth generation, which is what filling the spec does. This affects ground truth lookup during evaluation.

To test it, there are no automated unit tests, unfortunately.

We need to test two things:

  • that there are no regressions (which you can test using run_notebooks)
  • that it actually works properly for the case where there is multiple ground truth
    • run the evaluation notebooks on the berkeley timeline and check that the correct ground truth is returned for the temporally-significant ground truth cases.

@shankari
Copy link
Collaborator

wrt the route_coords error, looking at the if statement before it, I wonder if you are running into this branch. you could add some logging to double-check.

    elif "relation" in t:
        if isinstance(t["relation"], list):
            for r in t["relation"]:
                rclist.append(get_route_from_relation(r))
        else:
            rclist.append(get_route_from_relation(r))

a fix might be to assign the get_route_from_relation to route_coords before returning it, similar to the if branch above. so

route_coords = get_route_from_relation(r)
rclist.append(route_coords)

@singhish
Copy link
Contributor Author

On line 238, should we be iterating over rclist instead of route_coords?

@singhish
Copy link
Contributor Author

Getting the error

Traceback (most recent call last):
  File "autofill_eval_spec.py", line 395, in <module>
    eval_spec = validate_and_fill_eval_trips(calib_spec)
  File "autofill_eval_spec.py", line 332, in validate_and_fill_eval_trips
    ret_leg_list.append(validate_and_fill_leg(l, default_start_fmt_date, default_end_fmt_date))
  File "autofill_eval_spec.py", line 220, in validate_and_fill_leg
    rclist.append(get_route_from_relation(t["relation"]))
  File "autofill_eval_spec.py", line 156, in get_route_from_relation
    return get_coords_for_relation(r["relation_id"],
  File "autofill_eval_spec.py", line 149, in get_coords_for_relation
    start_index = on_list.index(start_node)
ValueError: 6410508153 is not in list

after fixing the above as such:

rclist = []
    if "polyline" in t:
        route_coords = get_route_from_polyline(t)
        rclist.append(route_coords)
    elif "relation" in t:
        if isinstance(t["relation"], list):
            for r in t["relation"]:
                rclist.append(get_route_from_relation(r))
        else:
            rclist.append(get_route_from_relation(t["relation"]))
    else:
        # We need to find a point within the polygon to pass to the routing engine
        start_coords_shp = geo.Polygon(start_polygon["geometry"]["coordinates"][0]).representative_point()
        start_coords = geo.mapping(start_coords_shp)["coordinates"]
        end_coords_shp = geo.Polygon(end_polygon["geometry"]["coordinates"][0]).representative_point()
        end_coords = geo.mapping(end_coords_shp)["coordinates"]
        print("Representative_coords: start = %s, end = %s" % (start_coords, end_coords))
        route_coords = get_route_from_osrm(t, start_coords, end_coords)
        rclist.append(route_coords)

Is this on the right track?

@shankari
Copy link
Collaborator

@singhish at a high level, I wonder if we should just make the decision to skip the relations and go to polylines and manually specified polygons right now. It looks like that kind of lookup is increasingly brittle.

To debug your error further:
6410508153 is expected to be an OSM node (variable name node_id). It still exists (https://www.openstreetmap.org/node/6410508153)

we expect it to be in the list of nodes associated with the relation. it apparently is not.
What do the print statements on the lines above show?

print("Relation %d mapped to %d ways" % (rid, len(wl)))
...
    print("After adding %d entries from wid %d, curr count = %d" % (len(w_on_list), wid, len(coords_list)))

@shankari
Copy link
Collaborator

$ grep -r 6410508153 spec_creation/final_sfbayarea
spec_creation/final_sfbayarea/train_bus_ebike_mtv_ucb.json:                        "start_node": 6410508153,
spec_creation/final_sfbayarea/train_bus_ebike_sm_reroute_mtv_ucb.json:                        "start_node": 6410508153,

leads us to

                    "relation": {
                        "relation_id": 9709380,
                        "start_node": 6410508153,
                        "end_node": 6561896571,
                        "polyline": "aucfFdlgiVQ?G?M?????AUIgAAU??CW?GAOAECYC_@KiBCYNbCIuAASCYI@mAH??wAHU@??M?k@DcAF??i@BK@??M@Q@m@B??E@]@??WBQN??A[GeA??C[Ew@AGAYM_BC[GWIW??EMAK??EOCK??CMEQ??Bc@O_CYaEe@iHCKG[??AGC]CS??CMGu@Ca@Eq@K{AMiBAO??AOAMCc@"
                    }

Checking that relation in OSM today, we find
https://www.openstreetmap.org/relation/9709380

which does not go through node https://www.openstreetmap.org/node/6410508153

The Bear Transit Shuttle bus has been re-routed to go through the east fork on Shattuck instead of the west fork.

@shankari
Copy link
Collaborator

@singhish I think that for the immediate term, we should focus on getting the berkeley reroutes into one filled spec, even if you have to merge them manually.

Concretely, we want to the final filled spec to have:

  • arrays for all the fields such as start_loc, end_loc etc
  • valid_start_ts and valid_end_ts for each entry in the array
  • multiple entries in the array for the reroutes; single entry in the array for non-reroutes

I thought that modifying the input spec and the filled spec creation script would be easier than manually copy/pasting lines across files. But if it looks like this is dragging on, maybe we should just manually fix the filled spec and move on?

How close do you think we are?

@singhish
Copy link
Contributor Author

After adding support for Addison and Shattuck, the script broke due to the "Only simple polygons supported" error. As a temporary fix, I'm enclosing any coordinates are flagged as more than length one within a list, and I'm getting plots produced that way. Why are some coordinates arrays nested enclosed within outer lists and others not? Noticed this was the case in the input file as well

@shankari
Copy link
Collaborator

that might be an issue with the input, let me take a look. I didn't investigate further when I saw the error since I wasn't sure if the error was with the spec or with the script.

@singhish
Copy link
Contributor Author

Checking leg mtv_to_berkeley_sf_bart, walk_to_caltrain
0    POINT (-122.08337 37.39025)
1    POINT (-122.08338 37.39022)
dtype: geometry
83    POINT (-122.07657 37.39452)
84    POINT (-122.07652 37.39450)
85    POINT (-122.07649 37.39454)
86    POINT (-122.07601 37.39435)
87    POINT (-122.07604 37.39430)
88    POINT (-122.07573 37.39418)
dtype: geometry
Checking leg mtv_to_berkeley_sf_bart, commuter_rail_aboveground
0    POINT (-122.07630 37.39460)
1    POINT (-122.07667 37.39474)
dtype: geometry
425    POINT (-122.38759 37.60071)
dtype: geometry
Checking leg mtv_to_berkeley_sf_bart, subway_underground
0    POINT (-122.38606 37.59948)
dtype: geometry
624    POINT (-122.26803 37.86947)
625    POINT (-122.26829 37.87122)
dtype: geometry
Checking leg mtv_to_berkeley_sf_bart, walk_to_bus
0    POINT (-122.26817 37.87030)
1    POINT (-122.26806 37.87032)
dtype: geometry
6    POINT (-122.26771 37.87105)
7    POINT (-122.26771 37.87110)
dtype: geometry
0    POINT (-122.26817 37.87030)
1    POINT (-122.26806 37.87032)
dtype: geometry
GeoSeries([], dtype: geometry)
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-47-ea6610975be3> in <module>
      4         if l["type"] == "TRAVEL" and l["id"] not in invalid_legs:
      5             print("Checking leg %s, %s" % (t["id"], l["id"]))
----> 6             check_start_end_contains(l)

<ipython-input-46-2be646730551> in check_start_end_contains(leg)
     36 
     37                 assert start_contains.any()
---> 38                 assert end_contains.any(), el

AssertionError: {'type': 'Feature', 'properties': {'name': 'Addison and Shattuck', 'valid_start_fmt_date': '2020-01-01', 'valid_end_fmt_date': '2020-04-30'}, 'geometry': {'type': 'Polygon', 'coordinates': [[[-122.26831823587418, 37.870920568422385], [-122.26826995611191, 37.87070989434301], [-122.26806476712227, 37.870724715654724], [-122.26810097694396, 37.87093221370567], [-122.26831823587418, 37.870920568422385]]]}}

I suspect the issues with the geometry verification have to do with the ill formatting that's going on with the nesting in the input spec. We can discuss this as well tomorrow.

(l["mode"], l["start_loc"]["properties"]["name"]),
"loc": _add_temporal_ground_truth(l["start_loc"], default_start_fmt_date, default_end_fmt_date)
})
if isinstance(l["start_loc"], list):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes the specific case of Addison and Shattuck. Does it fix all such similar cases? Can you test/walk through the use cases?

The points should be in an array, not in as separate points which look like
multiple polygons
@shankari
Copy link
Collaborator

After adding support for Addison and Shattuck, the script broke due to the "Only simple polygons supported" error.

Fixed in the spec (singhish#8)

I suspect the issues with the geometry verification have to do with the ill formatting that's going on with the nesting in the input spec. We can discuss this as well tomorrow.

This has nothing to do with the geometry verification. It is because of the nested if for the start and end loc entries.

        for sloc in leg["start_loc"]:
            for eloc in leg["end_loc"]:

You are effectively checking that the trajectories are between all pairs of start and end locations. Which is true if there is one start and one end location but breaks otherwise.

@shankari
Copy link
Collaborator

shankari commented Mar 2, 2021

The wait_for_city_bus_short_0 shim leg is not represented as a single leg with two reroutes, but rather, as two separate disjoint entries. See screenshot

Screen Shot 2021-03-01 at 9 39 31 PM

]
},
{
"id": "wait_for_city_bus_short_0",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First id

]
},
{
"id": "wait_for_city_bus_short_1",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

second ID

@shankari
Copy link
Collaborator

shankari commented Mar 2, 2021

Also for check_start_end_contains, you don't check to see that there are any matches. Since you have for loops for the checks, if there were zero start_loc and zero end_loc matches, you would get no errors.

After adding an assert

        print("Found %d start_locs and %d end_locs " % (len(start_locs), len(end_locs)))
        assert len(start_locs) >= 1 and len(end_locs) >= 1
        
        for sl in start_locs:

It turns out that is indeed the case

Checking leg mtv_to_berkeley_sf_bart, subway_underground
Found 1 start_locs and 1 end_locs 
0    POINT (-122.38606 37.59948)
dtype: geometry
622    POINT (-122.26803 37.86947)
623    POINT (-122.26829 37.87122)
dtype: geometry
Checking leg mtv_to_berkeley_sf_bart, walk_to_bus
Found 0 start_locs and 1 end_locs 
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-17-ea6610975be3> in <module>
      4         if l["type"] == "TRAVEL" and l["id"] not in invalid_legs:
      5             print("Checking leg %s, %s" % (t["id"], l["id"]))
----> 6             check_start_end_contains(l)

<ipython-input-16-5adc62ee8347> in check_start_end_contains(leg)
     17 
     18         print("Found %d start_locs and %d end_locs " % (len(start_locs), len(end_locs)))
---> 19         assert len(start_locs) >= 1 and len(end_locs) >= 1
     20         for sl in start_locs:
     21             start_contains = points.apply(lambda p: sl.contains(p))

AssertionError: 

@shankari
Copy link
Collaborator

shankari commented Mar 2, 2021

for the overlapping time validation, can you add a small sample spec with an overlapping time range showing that it works? It looks right, but I don't know that it works without seeing that it can correctly detect an overlap.

@singhish
Copy link
Contributor Author

singhish commented Mar 2, 2021

yes that ended up doing the trick. realized that it is the route that has to satisfy the start/end locations, not vice versa, as it the route that is being changed between dates.

@shankari
Copy link
Collaborator

shankari commented Mar 3, 2021

#48 (comment) is not yet fixed - I just double checked.

Unfortunately, I cannot wait any more to merge this because I'm improving the evaluation sample for e-mission/e-mission-docs#624 and it needs to use the new format for the filled spec.

So I am merging this and creating a new issue for the pending problem.

@shankari
Copy link
Collaborator

shankari commented Mar 3, 2021

Created new issue MobilityNet/mobilitynet.github.io#20

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants