コード例 #1
0
    def __init__(self, timetable_files, graph_with_knn):
        #
        timetable_files = list(timetable_files)

        self.routes = {
            ROUTE_KEY(tt['route']): tt['route']
            for tt in map(commons.zipjson_load, timetable_files)
        }

        # Note: 'datetime64[ms]' appears necessary here to properly parse the JSON
        #       but the resulting datatype is '...[ns]'
        self.timetables = {
            ROUTE_KEY(tt['route']):
            pd.read_json(tt['timetable_df'],
                         dtype='datetime64[ms]').dropna().astype(pd.Timestamp)
            for tt in map(commons.zipjson_load, timetable_files)
        }

        self.stop_pos = {
            stop['StopUID']:
            commons.inspect({'StopPosition':
                             ('PositionLat', 'PositionLon')})(stop)
            for route in self.routes.values() for stop in route['Stops']
        }

        (g, knn) = commons.inspect(('g', 'knn'))(graph_with_knn)
        self.node_pos = nx.get_node_attributes(g, 'pos')

        self.bb = BusstopBusser(self.routes, self.stop_pos, self.timetables)
        self.bw = BusstopWalker(self.stop_pos)
        self.gw = GraphWalker(g, knn, self.node_pos)
コード例 #2
0
def get_neighbors(knn, x, radius=None, k=None):
    tree: sklearn.neighbors.BallTree
    (I, tree) = commons.inspect(('node_ids', 'knn_tree'))(knn)
    if radius:
        return [I[j] for j in tree.query_radius([x], r=radius)[0]]
    if k:
        return [I[j] for j in tree.query([x], k=k, return_distance=False)[0]]
    raise ValueError(
        "Either *radius* or *number of neighbors* should be provided")
コード例 #3
0
ファイル: 03_osm_motc.py プロジェクト: numpde/transport
			def matchratio_ab(motc_route) :
				# motc_name = motc_route['RouteName']['Zh_tw']
				for (dir, stops) in zip_listify(motc_route['Direction'], motc_route['Stops']) :

					(motc_a, motc_b) = map(commons.inspect({'StopName' : 'Zh_tw'}), [stops[0], stops[-1]])

					ab_ratio = (matchratio_stop_names(route_a, motc_a) + matchratio_stop_names(route_b, motc_b)) / 2
					assert((0 <= ab_ratio) and (ab_ratio <= 1))

					yield (ab_ratio, { 'SubRouteUID' : motc_route['SubRouteUID'], 'Direction' : dir })
コード例 #4
0
ファイル: 20_transit.py プロジェクト: numpde/transport
    def keep_ttfile(fn):
        return True

        J = commons.zipjson_load(fn)

        # "Inner" Kaohsiung
        bbox = (120.2593, 22.5828, 120.3935, 22.6886)
        (left, bottom, right, top) = bbox

        (lat, lon) = map(
            np.asarray,
            zip(*map(
                commons.inspect(
                    {'StopPosition': ('PositionLat',
                                      'PositionLon')}), J['route']['Stops'])))
        return all([bottom <= lat, lat <= top, left <= lon, lon <= right])
コード例 #5
0
ファイル: 14_mapmatch.py プロジェクト: numpde/transport
    def make_figure(result) -> dict:
        ax: plt.Axes

        if ('plt' in result):
            (fig, ax) = commons.inspect({'plt': ('fig', 'ax')})(result)
        else:
            (fig, ax) = plt.subplots()
            result['plt'] = {'fig': fig, 'ax': ax}
            result['auto_close_fig'] = commons.UponDel(lambda: plt.close(fig))

        ax.cla()

        ax.set_title("{} ({}%)".format(
            result['status'], math.floor(100 * result.get('progress', 0))))

        if ('waypoints_all' in result):
            (y, x) = zip(*result['waypoints_all'])
            ax.plot(x, y, 'o', c='m', markersize=2)

        if ('geo_path' in result):
            (y, x) = zip(*result['geo_path'])
            ax.plot(x, y, 'b--', linewidth=2, zorder=100)

        return result['plt']
コード例 #6
0
def bus_at_stops(run, stops):
    # Note: pint does not serialize well
    Units = pint.UnitRegistry()

    # Return this if the input seems nonsensical
    default_result = [None] * len(stops)

    # These are sparse samples of a bus trajectory
    candidate_gps = list(map(tuple, run['BusPosition']))
    # Timestamps of GPS records as datetime objects
    candidate_tdt = list(map(dateutil.parser.parse, run['GPSTime']))

    # These are fixed platform locations
    reference_gps = list(
        commons.inspect({'StopPosition': ('PositionLat', 'PositionLon')})(stop)
        for stop in stops)

    # Goal: obtain an estimate ref_guess_tdt of when the bus is nearest to the platforms

    #print(candidate_gps)
    #print(run['GPSTime'])
    #print(candidate_tdt)
    #print(reference_gps)

    segments = list(zip(candidate_gps, candidate_gps[1:]))
    segm_tdt = list(zip(candidate_tdt, candidate_tdt[1:]))

    M = np.vstack([graph.dist_to_segment(r, s)[0] for s in segments]
                  for r in reference_gps)

    assert (M.size), "Undefined behavior for missing data"

    # M contains distances in meters
    # We expect the bus to travel about 17km/h on average, say 5m/s
    # So, a penalty of p = 0.1m/s should not make much of a difference,
    # unless the bus is idling

    # Penalty rate
    p = -0.1  # m/s
    # Penalty matrix
    P = np.vstack(
        len(reference_gps) *
        [np.cumsum([p * (t1 - t0).seconds for (t0, t1) in segm_tdt])])

    # Add idling penalty
    M += P

    # Make a copy for imshow
    M_image = M.copy()

    match = commons.align(M_image)

    segments = [segments[j] for j in match]
    segm_tdt = [segm_tdt[j] for j in match]
    seg_dist = [
        graph.dist_to_segment(r, s) for (r, s) in zip(reference_gps, segments)
    ]

    ref_guess_tdt = [
        t0 + q * (t1 - t0) for ((d, q), (t0, t1)) in zip(seg_dist, segm_tdt)
    ]

    if not (PARAM['min_near_run'] <= sum(
        (d <= PARAM['tail_eta_patch_dist']) for (d, q) in seg_dist)):
        return default_result

    try:
        # Patch ETA at the tails using this travel speed estimate
        typical_speed = np.median([s for s in run['Speed'] if s
                                   ]) * (Units.km / Units.hour)
        # Look at front and back tails
        for (direction, traversal) in [(+1, commons.identity), (-1, reversed)]:
            # Indices and distances
            tail = [(n, d, d <= PARAM['tail_eta_patch_dist'])
                    for (n, (d, q)) in traversal(list(enumerate(seg_dist)))]
            # Tail characterized by large distances
            tail = tail[0:([is_close
                            for (n, d, is_close) in tail].index(True) + 1)]
            # Indices of the tail
            tail = [n for (n, d, is_close) in tail]
            # No tail means there is nothing to patch
            if not tail: continue
            # First visited waypoint
            first = tail.pop()
            # Patch ETA for prior waypoints
            for n in tail:
                if (typical_speed == 0):
                    ref_guess_tdt[n] = None
                else:
                    td = dt.timedelta(
                        seconds=(commons.geodesic(reference_gps[n],
                                                  reference_gps[first]) *
                                 Units.meter /
                                 typical_speed).to(Units.second).magnitude)
                    ref_guess_tdt[n] = ref_guess_tdt[first] - direction * td
    except:
        print("Warning: Patching tail ETA failed")
        print(traceback.format_exc())

    is_monotone = all(
        (s < t) for (s, t) in zip(ref_guess_tdt, ref_guess_tdt[1:]))
    #assert(is_monotone), "Time stamps not monotone"

    # Diagnostics
    if False:
        mpl.use('GTK3Agg')
        import matplotlib.pyplot as plt

        print("Match:", match)

        print("Dist wpt to closest seg:", seg_dist)

        print("ETA (sec):", [
            round((t - ref_guess_tdt[0]).total_seconds())
            for t in ref_guess_tdt
        ])
        print("Strict monotonicity of ETA:", is_monotone)

        (fig, ax_array) = plt.subplots(nrows=1, ncols=2)
        (ax1, ax2) = ax_array

        ax1.imshow(M_image)

        (y, x) = zip(*candidate_gps)
        ax2.plot(x, y, '-')

        (y, x) = zip(*reference_gps)
        ax2.plot(x, y, 'bo')

        for ((wy, wx), ((ay, ax), (by, bx)),
             (d, q)) in zip(reference_gps, segments, seg_dist):
            x = (wx, ax + q * (bx - ax))
            y = (wy, ay + q * (by - ay))
            ax2.plot(x, y, 'r-')

        while plt.fignum_exists(fig.number):
            try:
                plt.pause(0.1)
            except:
                break

        plt.close(fig)

    # Sanity check
    assert (len(ref_guess_tdt) == len(stops)
            ), "Stop and ETA vector length mismatch"

    # Convert times to UTC timezone
    ref_guess_tdt = [t.astimezone(dt.timezone.utc) for t in ref_guess_tdt]

    return ref_guess_tdt
コード例 #7
0
def distill_geopath_ver2(sources):

    # Processing flowchart:
    #
    # geopaths ==> templates --> candidates --> route
    #                 ^==============="

    all_waypoints = set(
        map(tuple,
            chain.from_iterable(src['waypoints_used'] for src in sources)))

    geopaths = [tuple(src['geo_path']) for src in sources]

    if (len(geopaths) < 2):
        raise ValueError("At least two paths are required")

    # Image of provided route variants and the original waypoints
    with open(
            OFILE['progress'].format(
                stage='candidates_{round:02d}'.format(round=0), ext='png'),
            'wb') as fd:
        maps.write_track_img(waypoints=all_waypoints,
                             tracks=geopaths,
                             fd=fd,
                             mapbox_api_token=PARAM['mapbox_api_token'])

    # Return for each point in 'pp' its minimal distance to the cloud of points 'cloud'
    def dist2closest(pp: list, cloud: set):
        knn = graph.compute_geo_knn(dict(enumerate(cloud)),
                                    leaf_size=20)['knn_tree']
        return [
            np.min(knn.query(np.asarray(p).reshape(1, -1), k=1)[0]) for p in pp
        ]

    from collections import Counter

    # Returns a dictionary
    # Directed edge --> Counter of possible next points
    def predictor(paths):
        from collections import defaultdict
        node_next = defaultdict(Counter)
        for p in paths:
            p = list(p) + [None]
            for (e, c) in zip(zip(p, p[1:]), p[2:]):
                node_next[e].update([c])
        return dict(node_next)

    # Estimate path tail from an edge using a predictor
    def complete(e, node_next, use_most_common=True):
        node_next = deepcopy(node_next)
        while node_next.get(e):
            nn: Counter
            nn = node_next[e]
            if use_most_common:
                a = nn.most_common(1).pop()[0]
            else:
                a = random.choices(list(nn.keys()),
                                   weights=list(nn.values()),
                                   k=1).pop()
            # Reduce the likelihood of this choice for next time to avoid loops
            nn[a] *= 0.5
            e = (e[1], a)
            if a: yield a

    #
    def get_candidates_from(templates, rounds=0):

        # # Show all route candidates in one image
        # with open(OFILE['progress'].format(stage='templates_{round:02d}'.format(round=rounds), ext='png'), 'wb') as fd :
        # 	maps.write_track_img(waypoints=[], tracks=templates, fd=fd, mapbox_api_token=PARAM['mapbox_api_token'])

        if (rounds >= PARAM['candidates_rounds']):
            return templates

        if (len(set(templates)) < 2):
            return templates

        # commons.logger.info("Launched candidate round {} with {} templates...".format(rounds, len(templates)))

        node_forw = predictor(templates)
        node_back = predictor(map(reversed, templates))

        # Collect route candidates, some of them multiple times
        candidates = []
        while (len(candidates) < PARAM['candidates_min#']) or (
                len(candidates) <= len(set(candidates)) *
            (1 + PARAM['candidates_oversampling'])):

            if (len(candidates) >= PARAM['candidates_max#']): break

            # Pick an edge to extend a route in both directions
            root_edge = tuple(
                random.choice(
                    list(
                        chain.from_iterable(
                            zip(gp, gp[1:]) for gp in templates))))

            # Extended route
            candidate = tuple(
                reversed(list(complete(tuple(reversed(root_edge)),
                                       node_back)))) + root_edge + tuple(
                                           complete(root_edge, node_forw))

            # Record candidate
            if (len(candidate) >= 2):
                candidates.append(candidate)

        assert (len(candidates)), "No route candidates!"

        # Next hierarchy round
        return get_candidates_from(candidates, rounds + 1)

    # COLLECT ALL CANDIDATES REPEATEDLY
    commons.logger.info("Collecting candidates...")
    candidates = list(
        chain.from_iterable(
            Parallel(n_jobs=PARAM['#jobs'], batch_size=1)(
                delayed(get_candidates_from)(geopaths)
                for __ in progressbar(range(PARAM['candidates_all_repeat'])))))

    # Compute the relative frequency of the candidates, which we interpret as likelihood of being the correct route
    route_freq = {
        route: (len(list(g)) / len(candidates))
        for (route, g) in groupby(sorted(candidates))
    }

    # Keep only the most frequent candidates
    route_freq = {
        r: f
        for (r, f) in route_freq.items()
        if (f >= min(sorted(route_freq.values(), reverse=True)[0:10]))
    }

    # Q: individual coverage for each set of waypoints?

    def compute_route_metrics(route):
        # Subdivide long edges
        fine_route = refine_georoute(route)
        # Collect the metrics
        # commons.logger.debug("Total number of waypoints: {}, length of route candidate: {}".format(len(all_waypoints), len(fine_route)))
        metrics = {
            'path':
            route,
            # (*): Less is better
            # Coverage of waypoints (*)
            'covr':
            np.mean(dist2closest(all_waypoints, fine_route)),
            # Deviation from waypoints (*)
            'miss':
            np.mean(dist2closest(fine_route, all_waypoints)),
            # Total length (*)
            'dist':
            sum(commons.geodesic(*e) for e in zip(route, route[1:])),
            # Total turns, in degrees (*)
            'turn':
            sum(
                abs(graph.angle(p, q, r))
                for (p, q, r) in zip(route, route[1:], route[2:])),
        }
        # Final score
        metrics['CALL'] = PARAM['route_fitness'](metrics)

        return metrics

    # COLLECT METRICS FOR EACH CANDIDATE
    commons.logger.info("Computing candidate metrics...")
    candidate_metrics = Parallel(n_jobs=PARAM['#jobs'], batch_size=1)(
        delayed(compute_route_metrics)(route)
        for route in progressbar(route_freq))

    # Rekey by route
    candidate_metrics = {m['path']: m for m in candidate_metrics}

    # Additional metrics, in the order of decreasing prior likelihood
    candidate_metrics = {
        route: {
            'frrk': n,
            'freq': route_freq[route],
            **candidate_metrics[route]
        }
        for (n, route
             ) in enumerate(sorted(route_freq, key=(lambda r: -route_freq[r])))
    }

    # Attach rank by CALL
    for (r, m) in enumerate(
            sorted(candidate_metrics.values(), key=commons.inspect('CALL'))):
        m['rank'] = r

    # WINNER CANDIDATE
    route = min(candidate_metrics,
                key=(lambda r: candidate_metrics[r]['CALL']))

    # Display metrics
    commons.logger.info("Candidates summary:")
    for m in sorted(candidate_metrics.values(), key=commons.inspect('rank')):
        commons.logger.info(
            "Candidate #{rank:02d}: CALL={call:E}, freqrank={frrk}".format(
                rank=m['rank'], call=m['CALL'], frrk=m['frrk']))

    # Make images
    commons.logger.info("Making candidate images...")
    for metrics in candidate_metrics.values():
        fn = OFILE['progress'].format(
            stage='candidate_{rank:02d}'.format(rank=metrics['rank']),
            ext='png')
        with open(fn, 'wb') as fd:
            maps.write_track_img(waypoints=[],
                                 tracks=[metrics['path']],
                                 fd=fd,
                                 mapbox_api_token=PARAM['mapbox_api_token'])

    return route
コード例 #8
0
ファイル: 14_mapmatch.py プロジェクト: numpde/transport
def mapmatch_runs(scenario, runs):

    # Road network (main graph component) with nearest-neighbor tree for the nodes
    g: nx.DiGraph

    (g, knn) = commons.inspect(['g', 'knn'])(pickle.load(
        open(IFILE['OSM_graph_file'], 'rb'))['main_component_with_knn'])

    g = trim_graph_to_busable(g)

    # Nearest edges computer
    kne = (lambda q: graph.estimate_kne(g, knn, q, ke=20))

    mpl.use('Agg')
    mpl.rcParams['figure.max_open_warning'] = 100

    import matplotlib.pyplot as plt

    #
    def make_figure(result) -> dict:
        ax: plt.Axes

        if ('plt' in result):
            (fig, ax) = commons.inspect({'plt': ('fig', 'ax')})(result)
        else:
            (fig, ax) = plt.subplots()
            result['plt'] = {'fig': fig, 'ax': ax}
            result['auto_close_fig'] = commons.UponDel(lambda: plt.close(fig))

        ax.cla()

        ax.set_title("{} ({}%)".format(
            result['status'], math.floor(100 * result.get('progress', 0))))

        if ('waypoints_all' in result):
            (y, x) = zip(*result['waypoints_all'])
            ax.plot(x, y, 'o', c='m', markersize=2)

        if ('geo_path' in result):
            (y, x) = zip(*result['geo_path'])
            ax.plot(x, y, 'b--', linewidth=2, zorder=100)

        return result['plt']

    #
    def mm_callback(result) -> None:

        if (result['status'] == "opti"):
            if (dt.datetime.now() < result.get('nfu', dt.datetime.min)):
                return

        # Log into a GPX file
        if ('waypoints_all' in result):
            with open(OFILE['progress'].format(ext="gpx"), 'w') as fd:
                fd.write(
                    graph.simple_gpx(result['waypoints_all'],
                                     [result.get('geo_path', [])]).to_xml())

        # Save figure
        make_figure(result)
        with open(OFILE['progress'].format(ext="png"), 'wb') as fd:
            fig.savefig(fd, bbox_inches='tight', pad_inches=0)

        # Next figure update
        result['nfu'] = dt.datetime.now() + dt.timedelta(seconds=5)

    # Collect all bus runs
    runs_by_runid = {run[KEYS.runid]: run for run in runs}

    # Collect all waypoints
    waypoints_by_runid = {
        runid: list(map(tuple, run[KEYS.pos]))
        for (runid, run) in runs_by_runid.items()
    }

    #commons.logger.debug(json.dumps(runs, indent=2))

    commons.logger.info("Running mapmatch on {} runs".format(len(runs)))

    # MAPMATCH RUNS
    results = graph.mapmatch(waypoints_by_runid,
                             g,
                             kne,
                             knn=knn,
                             callback=None,
                             stubborn=0.2,
                             many_partial=True)

    for result in results:

        commons.logger.info("Got mapmatch with waypoints {}".format(
            result['waypoints_used']))

        # The run on which mapmatch operated
        run = runs_by_runid[result['waypoint_setid']]

        # Collect initial info about the mapmatch attempt
        mapmatch_attempt = {
            k: run[k]
            for k in [KEYS.routeid, KEYS.dir, KEYS.runid, KEYS.busid]
        }

        # Attach a unique identifier for this mapmatch
        mapmatch_attempt['MapMatchUUID'] = uuid.uuid4().hex

        # Filename without the extension
        fn = OFILE['mapmatched'].format(
            scenario=scenario,
            routeid=mapmatch_attempt[KEYS.routeid],
            direction=mapmatch_attempt[KEYS.dir],
            mapmatch_uuid=mapmatch_attempt['MapMatchUUID'],
            ext="{ext}")

        # Copy relevant fields from the mapmatcher result
        for k in ['waypoints_used', 'path', 'geo_path', 'mapmatcher_version']:
            mapmatch_attempt[k] = result[k]

        # Save the result in different formats, in this directory
        commons.makedirs(fn.format(ext='~~~'))

        #  o) Image

        try:
            # Make and save the figure
            fig = make_figure(result)['fig']
            with open(fn.format(ext="png"), 'wb') as fd:
                fig.savefig(fd, bbox_inches='tight', pad_inches=0)
        except Exception as e:
            commons.logger.warning("Could not save figure {} ({})".format(
                fn.format(ext="png"), e))

        #  o) JSON

        try:
            with open(fn.format(ext="json"), 'w') as fd:
                json.dump(mapmatch_attempt, fd)
        except Exception as e:
            commons.logger.warning(
                "Failed to write mapmatch file {} ({})".format(
                    fn.format(ext="json"), e))

        #  o) GPX

        try:
            with open(fn.format(ext="gpx"), 'w') as fd:
                fd.write(
                    graph.simple_gpx(mapmatch_attempt['waypoints_used'],
                                     [mapmatch_attempt['geo_path']]).to_xml())
        except Exception as e:
            commons.logger.warning("Failed to write GPX file {} ({})".format(
                fn.format(ext="gpx"), e))

        time.sleep(1)
コード例 #9
0
    def mm_callback(result):

        if (result['status'] == "zero"):
            print("(Preparing)")

        if (result['status'] == "init"):
            print("(Optimizing)")

        if (result['status'] == "opti"):
            if (dt.datetime.now() < result.get('nfu', dt.datetime.min)):
                return

        if (result['status'] == "done"):
            print("(Done)")

        if (result['status'] == "zero"):
            fig: plt.Figure
            ax: plt.Axes
            (fig, ax) = plt.subplots()

            for (n, (y, x)) in enumerate(result['waypoints']):
                ax.plot(x, y, 'bo')

            ax.axis(commons.niceaxis(ax.axis(), expand=1.1))
            ax.autoscale(enable=False)

            # Download the background map
            try:
                mapi = maps.get_map_by_bbox(maps.ax2mb(*ax.axis()),
                                            token=mapbox_token)
            except maps.URLError:
                commons.logger.warning("No map (no connection?)")
                mapi = None

            result['plt'] = {
                'fig': fig,
                'ax': ax,
                'map': mapi,
                'bbox': ax.axis()
            }

        if (result['status'] in ["zero", "opti", "done"]):

            fig: plt.Figure
            ax: plt.Axes
            (fig, ax, mapi,
             bbox) = commons.inspect({'plt':
                                      ('fig', 'ax', 'map', 'bbox')})(result)

            # Clear the axes
            ax.cla()

            # Apply the background map
            if mapi:
                img = ax.imshow(mapi,
                                extent=bbox,
                                interpolation='none',
                                zorder=-100)

            for (n, (y, x)) in enumerate(result['waypoints']):
                ax.plot(x, y, 'o', c='m', markersize=4)

            if ('geo_path' in result):
                (y, x) = zip(*result['geo_path'])
                ax.plot(x, y, 'b--', linewidth=2, zorder=-50)

            if ('(edge_clouds)' in result):
                for (nc, pc) in enumerate(result['(edge_clouds)']):
                    for (e, p) in pc.items():
                        (y, x) = zip(*[g.nodes[i]['pos'] for i in e])
                        c = ('g' if
                             (result['(active_edges)'][nc] == e) else 'r')
                        ax.plot(x,
                                y,
                                '-',
                                c=c,
                                linewidth=2,
                                alpha=(p / max(pc.values())),
                                zorder=150)

            ax.axis(bbox)

            plt.pause(0.1)

        if (result['status'] == "opti"):
            # Next figure update
            result['nfu'] = dt.datetime.now() + dt.timedelta(seconds=4)

        if (result['status'] == "done"):
            open("graph_mm_callback.gpx", 'w').write(
                simple_gpx(result['waypoints'], [result['geo_path']]).to_xml())
コード例 #10
0
def test_mapmatch():

    mapbox_token = open("../.credentials/UV/mapbox-token.txt", 'r').read()

    osm_graph_file = "../OUTPUT/02/UV/kaohsiung.pkl"

    print("Loading OSM...")
    OSM = pickle.load(open(osm_graph_file, 'rb'))

    print("Retrieving the KNN tree...")

    # Road network (main graph component) with nearest-neighbor tree for the nodes
    g: nx.DiGraph
    (g, knn) = commons.inspect(('g', 'knn'))(OSM['main_component_with_knn'])

    kne = (lambda q: estimate_kne(g, knn, q, ke=20))

    # Try to free up memory
    del OSM

    #

    # # Get some waypoints
    # routes_file = "../OUTPUT/00/ORIGINAL_MOTC/Kaohsiung/CityBusApi_StopOfRoute.json"
    #
    # motc_routes = commons.index_dicts_by_key(
    # 	commons.zipjson_load(routes_file),
    # 	lambda r: r['RouteUID'],
    # 	preserve_singletons=['Direction', 'Stops']
    # )

    # Waypoints
    # # (route_id, direction) = ('KHH1221', 0)
    # # (route_id, direction) = ('KHH29', 0)
    # # (route_id, direction) = ('KHH38', 0)
    # # (route_id, direction) = ('KHH87', 1)
    # # (route_id, direction) = ('KHH121', 1)
    # (route_id, direction) = ('KHH11', 1)
    # #
    # waypoints = list(map(commons.inspect({'StopPosition': ('PositionLat', 'PositionLon')}), motc_routes[route_id]['Stops'][direction]))

    #waypoints = [(22.622249, 120.368713), (22.621929, 120.367332), (22.622669, 120.367736), (22.623569, 120.366722), (22.624959, 120.364402), (22.625329, 120.36338), (22.625379, 120.362777), (22.62565, 120.361061), (22.62594, 120.359947), (22.62602, 120.354911), (22.62577, 120.351226), (22.625219, 120.34732), (22.62494, 120.3442), (22.624849, 120.34317), (22.62597, 120.342582), (22.626169, 120.344428), (22.62811, 120.344451), (22.62968, 120.33908), (22.63017, 120.337562), (22.63042, 120.336341), (22.631919, 120.331932), (22.632989, 120.327766), (22.632789, 120.325233), (22.632829, 120.324371), (22.633199, 120.32283), (22.633449, 120.321639), (22.63459, 120.31707), (22.636629, 120.314437), (22.63758, 120.308952), (22.6375, 120.307777), (22.637899, 120.301162), (22.63788, 120.298866), (22.637899, 120.297393), (22.63718, 120.294151), (22.636989, 120.293609), (22.6354, 120.288566), (22.635179, 120.287719), (22.634139, 120.284576), (22.632179, 120.28379), (22.631229, 120.283309), (22.628789, 120.28199), (22.62507, 120.28054), (22.624259, 120.282028), (22.622869, 120.284973), (22.62247, 120.285827), (22.623029, 120.286407), (22.62531, 120.28524)]
    #waypoints = [(22.62269, 120.367767), (22.623899, 120.366409), (22.626039, 120.359397), (22.62615, 120.357887), (22.62602, 120.35337), (22.625059, 120.345809), (22.625989, 120.342529), (22.625999, 120.343856), (22.626169, 120.344413), (22.628049, 120.344436), (22.628969, 120.340843), (22.62993, 120.338348), (22.63025, 120.337356), (22.631309, 120.334068), (22.63269, 120.329841), (22.63307, 120.328491), (22.63297, 120.326713), (22.632949, 120.324851), (22.63385, 120.319831), (22.637609, 120.307678), (22.637609, 120.305633), (22.63762, 120.304847), (22.637859, 120.300231), (22.63796, 120.297439), (22.63787, 120.296707), (22.63739, 120.294357), (22.637079, 120.293472), (22.6359, 120.289939), (22.63537, 120.288353), (22.634149, 120.284728), (22.629299, 120.28228), (22.62652, 120.280738), (22.62354, 120.283637), (22.622549, 120.28572), (22.622999, 120.28627), (22.625379, 120.285156)]
    #waypoints = [(22.62202, 120.368789), (22.62198, 120.368133), (22.62191, 120.367233), (22.62384, 120.366401), (22.624929, 120.364402), (22.625329, 120.363342), (22.62593, 120.363357), (22.62569, 120.360771), (22.6261, 120.357803), (22.62601, 120.355743), (22.62578, 120.351692), (22.625539, 120.349586), (22.62494, 120.344642), (22.62515, 120.3423), (22.62598, 120.343742), (22.627559, 120.344482), (22.629569, 120.339309), (22.630359, 120.336929), (22.63124, 120.333846), (22.6322, 120.330856), (22.632869, 120.326393), (22.632879, 120.324172), (22.63344, 120.321502), (22.63418, 120.318351), (22.637369, 120.312362), (22.637639, 120.303802), (22.637779, 120.301971), (22.63787, 120.30104), (22.63775, 120.300231), (22.637859, 120.297416), (22.6373, 120.294448), (22.63697, 120.293418), (22.636289, 120.291076), (22.635129, 120.287742), (22.634969, 120.287078), (22.631259, 120.283332), (22.627559, 120.281402), (22.626689, 120.280967), (22.624849, 120.280937), (22.623979, 120.282623), (22.623739, 120.283187), (22.62317, 120.286453), (22.625259, 120.285423)]
    #waypoints = [(22.62203, 120.368293), (22.62195, 120.367401), (22.624559, 120.36515), (22.624929, 120.364448), (22.62585, 120.363113), (22.625549, 120.36177), (22.6261, 120.357627), (22.625509, 120.349677), (22.62503, 120.345596), (22.62589, 120.342307), (22.627979, 120.344459), (22.628539, 120.34201), (22.629989, 120.33805), (22.63025, 120.337219), (22.63211, 120.331581), (22.633039, 120.328659), (22.63307, 120.327308), (22.63294, 120.326156), (22.632989, 120.323699), (22.63342, 120.321418), (22.63743, 120.310119), (22.63755, 120.305641), (22.637639, 120.304267), (22.636949, 120.293319), (22.6355, 120.289062), (22.63454, 120.285987), (22.63076, 120.283088), (22.62968, 120.282478), (22.627229, 120.281188), (22.62647, 120.280693), (22.62516, 120.280387), (22.624099, 120.282401), (22.622669, 120.285308), (22.62313, 120.286369), (22.625169, 120.285667)]
    #waypoints = [(22.666889, 120.358613), (22.666389, 120.358893), (22.667886, 120.357973), (22.669096, 120.35728), (22.672, 120.356413), (22.673586, 120.356866), (22.67395, 120.35764), (22.670996, 120.359653), (22.669636, 120.360426), (22.667536, 120.361346), (22.665766, 120.361893), (22.663703, 120.362173), (22.661463, 120.362533), (22.66128, 120.363266), (22.659683, 120.364013), (22.65876, 120.361999), (22.658496, 120.360746), (22.656686, 120.360226), (22.653473, 120.359653), (22.650909, 120.359719), (22.650743, 120.357439), (22.65097, 120.356733), (22.650973, 120.355746), (22.651303, 120.351026), (22.651933, 120.349693), (22.65304, 120.349733), (22.652703, 120.349173), (22.651806, 120.348826), (22.65078, 120.348439), (22.649753, 120.348079), (22.643733, 120.346253), (22.642279, 120.345799), (22.642226, 120.343906), (22.642313, 120.343186), (22.642483, 120.342386), (22.639283, 120.340866), (22.639399, 120.340186), (22.639863, 120.338213), (22.640733, 120.332533), (22.639569, 120.3322), (22.638956, 120.332173), (22.639066, 120.328613), (22.639433, 120.326866), (22.639306, 120.326026), (22.6397, 120.322373), (22.639946, 120.319919), (22.640243, 120.316946), (22.637166, 120.311866), (22.637503, 120.306773), (22.63753, 120.304773), (22.637616, 120.304079), (22.637709, 120.302853), (22.636179, 120.302306), (22.635326, 120.302373), (22.634396, 120.302373), (22.631286, 120.301599), (22.630943, 120.300679), (22.630089, 120.298066), (22.628786, 120.294079), (22.627633, 120.290586), (22.62723, 120.289253), (22.62659, 120.287533), (22.62601, 120.286213), (22.626173, 120.28564), (22.624866, 120.285866), (22.623696, 120.2866), (22.623153, 120.286386), (22.621536, 120.284959), (22.620976, 120.284999)]
    #waypoints = [(22.666556, 120.358786), (22.66751, 120.35836), (22.668576, 120.357613), (22.672313, 120.356319), (22.673593, 120.356906), (22.665803, 120.361893), (22.663703, 120.362239), (22.662706, 120.362399), (22.659723, 120.363933), (22.658963, 120.363186), (22.658523, 120.360759), (22.6508, 120.357293), (22.65096, 120.356733), (22.650733, 120.355213), (22.650686, 120.352519), (22.652966, 120.34976), (22.645576, 120.346626), (22.643599, 120.346106), (22.641999, 120.345106), (22.642226, 120.343879), (22.642373, 120.343119), (22.642366, 120.341933), (22.640166, 120.341533), (22.639253, 120.341279), (22.639373, 120.34028), (22.639679, 120.338506), (22.640713, 120.332719), (22.63971, 120.33216), (22.638663, 120.332093), (22.638593, 120.331199), (22.638933, 120.32952), (22.639263, 120.326546), (22.63934, 120.325826), (22.639453, 120.324773), (22.639633, 120.323279), (22.63988, 120.320959), (22.640173, 120.317826), (22.637606, 120.3048), (22.637653, 120.304106), (22.637756, 120.302306), (22.636829, 120.302213), (22.636233, 120.302199)]

    #waypoints = [(22.60642, 120.338256), (22.60642, 120.338256), (22.60642, 120.338256), (22.60642, 120.338256), (22.60642, 120.338256), (22.60642, 120.338256), (22.60642, 120.338256), (22.60642, 120.338256), (22.60642, 120.338256), (22.60642, 120.338256), (22.60642, 120.338256), (22.60642, 120.338256), (22.60642, 120.338256), (22.60642, 120.338256), (22.60642, 120.338256), (22.60642, 120.338256), (22.60642, 120.338256), (22.60642, 120.338256), (22.60642, 120.338256), (22.60642, 120.338256), (22.60642, 120.338256), (22.60642, 120.338256), (22.60642, 120.338256), (22.60642, 120.338256), (22.60642, 120.338256), (22.60642, 120.338256), (22.60651, 120.338188), (22.606119, 120.338569), (22.606109, 120.33834), (22.605649, 120.333862), (22.60655, 120.33345), (22.60831, 120.333213), (22.617879, 120.332069), (22.619409, 120.331848), (22.61968, 120.33184), (22.622409, 120.331458), (22.622329, 120.329803), (22.622289, 120.329437), (22.62141, 120.327056), (22.62124, 120.326507), (22.62095, 120.325752), (22.62008, 120.323219), (22.618999, 120.319877), (22.61882, 120.319168), (22.61853, 120.318038), (22.617969, 120.316169), (22.617969, 120.316169), (22.617319, 120.314498), (22.61683, 120.313072), (22.616478999999998, 120.31208), (22.616478999999998, 120.31208), (22.615419, 120.308982), (22.615159, 120.30812), (22.61498, 120.307182), (22.61498, 120.307182), (22.614429, 120.305999), (22.614429, 120.305999), (22.614099, 120.305229), (22.61411, 120.30516), (22.61375, 120.303573), (22.61359, 120.303199), (22.61359, 120.303199), (22.613389, 120.302856), (22.613109, 120.301681), (22.612779, 120.301078), (22.61264, 120.300666), (22.61255, 120.300239), (22.6123, 120.298408), (22.61313, 120.29811), (22.621259, 120.295913), (22.62136, 120.296142), (22.62174, 120.296768), (22.622539, 120.29956), (22.622659, 120.299919), (22.62294, 120.300529), (22.62302, 120.300811), (22.62351, 120.301116), (22.624648, 120.301299), (22.6268, 120.301521)]
    waypoints = [(22.60642, 120.338256), (22.60651, 120.338188),
                 (22.606119, 120.338569), (22.606109, 120.33834),
                 (22.605649, 120.333862), (22.60655, 120.33345),
                 (22.60831, 120.333213), (22.617879, 120.332069)]

    # waypoints = [(22.60642, 120.338256), (22.605649, 120.333862), (22.60655, 120.33345), (22.60831, 120.333213), (22.617879, 120.332069), (22.619409, 120.331848), (22.622409, 120.331458), (22.622329, 120.329803), (22.62141, 120.327056), (22.62095, 120.325752), (22.62008, 120.323219), (22.618999, 120.319877), (22.61882, 120.319168), (22.61853, 120.318038), (22.617969, 120.316169), (22.617319, 120.314498), (22.61683, 120.313072), (22.616478999999998, 120.31208), (22.615419, 120.308982), (22.615159, 120.30812), (22.61498, 120.307182), (22.614429, 120.305999), (22.614099, 120.305229), (22.61375, 120.303573), (22.613389, 120.302856), (22.613109, 120.301681), (22.612779, 120.301078), (22.61255, 120.300239), (22.6123, 120.298408), (22.61313, 120.29811), (22.621259, 120.295913), (22.62174, 120.296768), (22.622539, 120.29956), (22.62294, 120.300529), (22.62351, 120.301116), (22.624648, 120.301299), (22.6268, 120.301521)]
    # wp1 = (22.61313, 120.29811) # lower
    # wp2 = (22.621259, 120.295913) # upper
    # print(waypoints.index(wp1), waypoints.index(wp2)) # 29 30

    # waypoints = [(22.591286, 120.305706), (22.593253, 120.305906), (22.59474, 120.305306), (22.597746, 120.304186), (22.596886, 120.305133)]

    # waypoints = [(22.613109, 120.301681), (22.612779, 120.301078), (22.61255, 120.300239), (22.6123, 120.298408), (22.61313, 120.29811), (22.621259, 120.295913), (22.62174, 120.296768), (22.622539, 120.29956)]

    # # Off-graph
    # waypoints = [(23.158953, 120.764319), (23.159556, 120.766213), (23.159126, 120.764453), (23.158566, 120.76336), (23.15574, 120.760866), (23.154659, 120.760319), (23.151839, 120.757533), (23.14992, 120.75664), (23.14823, 120.755399), (23.146363, 120.754159), (23.145453, 120.750733), (23.144263, 120.749773), (23.138233, 120.738666), (23.137466, 120.7296), (23.13557, 120.724706), (23.134416, 120.723146), (23.133646, 120.72244), (23.132079, 120.720439), (23.130616, 120.71916), (23.12967, 120.717573), (23.129436, 120.71688), (23.127803, 120.714626), (23.119859, 120.710013), (23.116743, 120.712706), (23.11632, 120.713466), (23.114913, 120.713279), (23.110603, 120.712013), (23.109433, 120.710333), (23.110526, 120.7086), (23.108769, 120.699866), (23.105703, 120.694919), (23.103096, 120.690733), (23.101586, 120.688879), (23.100159, 120.685826), (23.095286, 120.682013), (23.09337, 120.681706), (23.08988, 120.682866), (23.08783, 120.681039), (23.086396, 120.679666), (23.08473, 120.679426), (23.079733, 120.676773), (23.079039, 120.676333), (23.077429, 120.675373), (23.073783, 120.673479), (23.073319, 120.673133), (23.072413, 120.672933), (23.06987, 120.672719), (23.067569, 120.673386), (23.06427, 120.672773), (23.064096, 120.671453), (23.062183, 120.670893), (23.052396, 120.667506), (23.049193, 120.669039), (23.046186, 120.667866), (23.042326, 120.667413), (23.03734, 120.665693), (23.025093, 120.663466), (23.020016, 120.664373), (23.015033, 120.665293), (23.012633, 120.665346), (23.007923, 120.664519), (23.00765, 120.662826), (23.006326, 120.654946), (23.006913, 120.651799), (23.006509, 120.649106), (23.004886, 120.647413), (23.003593, 120.646879), (22.999726, 120.644453), (22.996513, 120.643013), (22.995793, 120.642453), (22.996606, 120.641493), (22.996259, 120.64068), (22.995889, 120.634906), (22.996983, 120.63436), (22.99773, 120.634213)]

    # commons.seed()
    # mapmatch(waypoints, g, kne, None, stubborn=0.2)
    # commons.seed()
    # mapmatch(waypoints, g, kne, None, stubborn=0.2)
    # return

    #

    # TODO: abort if nearest edges are too far
    # TODO: no path to node issue

    # Includes renderer selection:
    import matplotlib.pyplot as plt

    def mm_callback(result):

        if (result['status'] == "zero"):
            print("(Preparing)")

        if (result['status'] == "init"):
            print("(Optimizing)")

        if (result['status'] == "opti"):
            if (dt.datetime.now() < result.get('nfu', dt.datetime.min)):
                return

        if (result['status'] == "done"):
            print("(Done)")

        if (result['status'] == "zero"):
            fig: plt.Figure
            ax: plt.Axes
            (fig, ax) = plt.subplots()

            for (n, (y, x)) in enumerate(result['waypoints']):
                ax.plot(x, y, 'bo')

            ax.axis(commons.niceaxis(ax.axis(), expand=1.1))
            ax.autoscale(enable=False)

            # Download the background map
            try:
                mapi = maps.get_map_by_bbox(maps.ax2mb(*ax.axis()),
                                            token=mapbox_token)
            except maps.URLError:
                commons.logger.warning("No map (no connection?)")
                mapi = None

            result['plt'] = {
                'fig': fig,
                'ax': ax,
                'map': mapi,
                'bbox': ax.axis()
            }

        if (result['status'] in ["zero", "opti", "done"]):

            fig: plt.Figure
            ax: plt.Axes
            (fig, ax, mapi,
             bbox) = commons.inspect({'plt':
                                      ('fig', 'ax', 'map', 'bbox')})(result)

            # Clear the axes
            ax.cla()

            # Apply the background map
            if mapi:
                img = ax.imshow(mapi,
                                extent=bbox,
                                interpolation='none',
                                zorder=-100)

            for (n, (y, x)) in enumerate(result['waypoints']):
                ax.plot(x, y, 'o', c='m', markersize=4)

            if ('geo_path' in result):
                (y, x) = zip(*result['geo_path'])
                ax.plot(x, y, 'b--', linewidth=2, zorder=-50)

            if ('(edge_clouds)' in result):
                for (nc, pc) in enumerate(result['(edge_clouds)']):
                    for (e, p) in pc.items():
                        (y, x) = zip(*[g.nodes[i]['pos'] for i in e])
                        c = ('g' if
                             (result['(active_edges)'][nc] == e) else 'r')
                        ax.plot(x,
                                y,
                                '-',
                                c=c,
                                linewidth=2,
                                alpha=(p / max(pc.values())),
                                zorder=150)

            ax.axis(bbox)

            plt.pause(0.1)

        if (result['status'] == "opti"):
            # Next figure update
            result['nfu'] = dt.datetime.now() + dt.timedelta(seconds=4)

        if (result['status'] == "done"):
            open("graph_mm_callback.gpx", 'w').write(
                simple_gpx(result['waypoints'], [result['geo_path']]).to_xml())

    print("Calling mapmatch...")

    plt.ion()

    commons.seed()

    for _ in range(2):
        try:
            result = mapmatch(waypoints, g, kne, mm_callback, stubborn=0.2)
            print(result['path'])
        except MapMatchingError as e:
            print("Mapmatch failed ({})".format(e))

        plt.pause(5)

    plt.ioff()
    plt.show()

    return
コード例 #11
0
ファイル: 16_realtimevis.py プロジェクト: numpde/transport
def bus_at_stops(run, stops) :

	# TODO: some candidate_tdt have large time-gaps

	# These are sparse samples of a bus trajectory
	candidate_gps = list(zip(run['PositionLat'], run['PositionLon']))
	# Timestamps of GPS records as datetime objects
	candidate_tdt = list(map(dateutil.parser.parse, run['GPSTime']))

	# These are fixed platform locations
	reference_gps = list(commons.inspect({'StopPosition': ('PositionLat', 'PositionLon')})(stop) for stop in stops)

	# Goal: obtain an estimate ref_guess_tdt of when the bus is nearest to the platforms

	print(candidate_gps)
	print(run['GPSTime'])
	print(candidate_tdt)
	print(reference_gps)

	segments = list(zip(candidate_gps[:-1], candidate_gps[1:]))
	segm_tdt = list(zip(candidate_tdt[:-1], candidate_tdt[1:]))

	M = np.vstack([dist_to_segment(r, s)[0] for s in segments] for r in reference_gps)

	if not M.size : return

	# M contains distances in meters
	# We expect the bus to travel about 17km/h on average, say 5m/s
	# So, a penalty of p = 0.1m/s should not make much of a difference,
	# unless the bus is idling

	# Penalty rate
	p = 0.1 # m/s
	# Penalty matrix
	P = np.vstack(len(reference_gps) * [np.cumsum([p * (t1 - t0).seconds for (t0, t1) in segm_tdt])])

	# Add idling penalty
	M += P

	match = commons.align(M)
	print(match)

	segments = [segments[j] for j in match]
	segm_tdt = [segm_tdt[j] for j in match]
	seg_dist = [dist_to_segment(r, s) for (r, s) in zip(reference_gps, segments)]

	ref_guess_tdt = [
		t0 + q * (t1 - t0)
		for ((d, q), (t0, t1)) in zip(seg_dist, segm_tdt)
	]

	for t in ref_guess_tdt : print(t)

	is_monotone = all((s <= t) for (s, t) in zip(ref_guess_tdt[:-1], ref_guess_tdt[1:]))
	print("Monotonicity:", is_monotone)

	(fig, ax) = plt.subplots()
	ax.imshow(M)

	while plt.fignum_exists(fig.number) :
		try :
			plt.pause(0.1)
		except :
			break

	plt.close(fig)

	pass
コード例 #12
0
ファイル: 16_realtimevis.py プロジェクト: numpde/transport
def vis1() :

	# OSM = pickle.load(open(IFILE['OSM'], 'rb'))
	# for (route_id, route) in OSM['rels']['route'].items():
	# 	# Skip non-bus routes
	# 	if not (route['t'].get('route') == 'bus'): continue
	#
	# 	route_name = route['t'].get('name')
	#
	# 	route_ref = route['t']['ref']
	# 	#if (route_ref == '88') :
	# 	print(route_name, route_id, route['t'])
	# exit(39)

	routeid_of = (lambda r: r['SubRouteUID'])

	# List of filenames, one file per physical bus, identified by plate number
	bus_files = commons.ls(IFILE['busses'].format(busid="*"))

	# Refile bus runs by their route ID
	runs_by_route = defaultdict(list)
	for fn in bus_files :
		runs = commons.zipjson_load(fn)
		for run in runs :
			runs_by_route[routeid_of(run)].append(run)

	#
	route_stops = commons.index_dicts_by_key(commons.zipjson_load(IFILE['route-stops']), routeid_of)

	# Are those valid route ID that can be found among the routes?
	unknown_route_ids = sorted(set(runs_by_route.keys()) - set(route_stops.keys()))

	if unknown_route_ids :
		print("The following route IDs from bus records are unknown:")
		print(", ".join(unknown_route_ids))
		raise KeyError("Unkown route IDs in bus records")

	#

	route_uid = 'KHH24'

	runs = runs_by_route[route_uid]
	route = route_stops[route_uid]

	# Kaohsiung (left, bottom, right, top)
	bbox = (120.2593, 22.5828, 120.3935, 22.6886)
	(left, bottom, right, top) = bbox

	# Download the background map
	i = maps.get_map_by_bbox(bbox, token=PARAM['mapbox_api_token'])

	# Show the background map
	(fig, ax) = plt.subplots()
	plt.ion()
	ax.axis([left, right, bottom, top])
	ax.imshow(i, extent=(left, right, bottom, top), interpolation='quadric')

	#fig.canvas.draw_idle()

	plt.pause(0.1)


	stops_by_direction = dict(zip(route['Direction'], route['Stops']))

	# Draw stops for both route directions
	for (dir, stops) in stops_by_direction.items() :

		# Stop locations
		(y, x) = zip(*[
			commons.inspect({'StopPosition': ('PositionLat', 'PositionLon')})(stop)
			for stop in stops
		])

		# Plot as dots
		ax.scatter(x, y, c=('b' if dir else 'g'), marker='o', s=4)


	# Show bus location

	for run in runs :

		# Trace bus
		(y, x) = (run['PositionLat'], run['PositionLon'])
		h1 = ax.plot(x, y, '--+', c='r', linewidth=1)
		h2 = ax.plot(x[0], y[0], 'o', c='r')
		h3 = ax.plot(x[-1], y[-1], 's', c='r')

		plt.title(run['PlateNumb'])

		#plt.savefig("{}.png".format(route_uid), dpi=180)
		plt.pause(0.1)

		bus_at_stops(run, stops_by_direction[run['Direction']])

		plt.pause(0.1)
		[h[0].remove() for h in [h1, h2, h3]]

	return