def _draw_stations(self): with client.session(keyspace=settings.keyspace) as session: with session.transaction(grakn.TxType.WRITE) as transaction: """ Render a map of the London Underground Stations based on their grid coordinates. :return: """ station_name_labels = dict() suffix = " Underground Station" # Get the stations, but limit them to those which have at least one tunnel station_query = match_get( "$s isa station, has name $name, has lon $lon, has lat $lat; ($s) isa tunnel;" ) response = self.perform_query(station_query, transaction) for match in response: station_id = match.get('s').id name = match.get('name').value() if name.endswith(suffix): name = name[:-len(suffix)] print("drawing station: {}".format(station_id)) lon, lat = self._transform_coords( float(match.get('lon').value()), float(match.get('lat').value())) self._station_canvas_coords[station_id] = (lon, lat) station_tag = self._canvas.create_circle( lon, lat, self.STATION_CIRCLE_RADIUS, fill="white", outline="black") self._station_point_ids[station_id] = station_tag # We need to identify the station selected, so we should build a function for each station to hold this def callback_wrapper(event, id=station_id): return self._on_station_select(id) event_sequence = "<Shift-ButtonPress-1>" self._canvas.tag_bind(self._station_point_ids[station_id], event_sequence, callback_wrapper) station_label_tag = self._canvas.create_text( lon + self.STATION_CIRCLE_RADIUS, lat + self.STATION_CIRCLE_RADIUS, text=name, anchor=tk.NW, font=('Johnston', self.STATION_FONT_SIZE, 'bold'), fill="#666") station_name_labels[station_id] = station_label_tag self._canvas.tag_bind(station_label_tag, event_sequence, callback_wrapper)
def import_query_generator(perform_query, timetables_dir_path): """ Builds the Graql statements required to import the transportation data contained in all_routes.json :param perform_query: function to call to query the grakn server :param timetables_dir_path: path to :return: """ # Get the locations of the downloaded timetable json data timetable_paths = os.listdir(timetables_dir_path) station_query = "$s1 isa station, has naptan-id \"{}\";" def add_route_stop_relationship(naptan_id, role_played, route_id): match_query = station_query.format(naptan_id) insert_query = "$r({}: $s1) isa route, id {};".format( role_played, route_id) response = list(perform_query(match_insert(match_query, insert_query))) check_response_length(response, min_length=1, max_length=1) for timetable_path in timetable_paths: with open(timetables_dir_path + "/" + timetable_path, 'r') as f: data = json.load(f) tube_line_query = "$tl isa tube-line, has name \"{}\";".format( data['lineName']) response = list(perform_query(match_get(tube_line_query))) if len(response) < 1: # In this case we haven't already added this tube-line before # We do it this way so that we have explicitly asked the database if the tube-line exists, and if not # then we use the same query body but as an insert response = list(perform_query(insert(tube_line_query))) check_response_length( response, min_length=1, max_length=1) # Exactly one concept should be inserted for station in data["stops"]: response = list( perform_query( match_get(station_query.format(station["id"])))) if len(response) < 1: # Only proceed if there is this station isn't already in the database station_insert_query = ( station_query.format(station["id"]) + "$s1 has name \"{}\", " "has lat {}, has lon {};\n").format( station["name"], station["lat"], station["lon"], ) response = list(perform_query( insert(station_insert_query))) check_response_length(response, min_length=1, max_length=1) try: zone_name = station["zone"] except KeyError: # In the case that there is no zone information zone_name = -1 response = list( perform_query( match_get("$z isa zone, has name \"{}\";\n".format( zone_name)))) if len(response) < 1: # If the zone doesn't already exist then insert it zone_query = "$z(contained-station: $s1) isa zone, has name \"{}\";\n".format( zone_name) response = list( perform_query( match_insert( station_query.format(station["id"]), zone_query))) check_response_length( response, min_length=1, max_length=1) # Exactly one concept should be # inserted """ Get the time between stops """ for routes in data['timetable']["routes"]: for station_intervals in routes["stationIntervals"]: # This actually iterates over the routes, in TFL's infinite wisdom last_naptan_id = data['timetable']["departureStopId"] last_time_to_arrival = 0 route_query = "$r(route-operator: $tl) isa route;" response = list( perform_query( match_insert(tube_line_query, route_query))) check_response_length(response, min_length=1, max_length=1) route_id = get_match_id(response, "r") # TODO Here we need to execute the query in order to get the ID of the route inserted, since that's # the only way to uniquely identify it for the insertion of the route-sections below add_route_stop_relationship(last_naptan_id, "origin", route_id) for i, interval in enumerate( station_intervals['intervals']): # === TUNNELS === # Now we need to insert a tunnel that can make the connection if there isn't one, or if there # is one then don't add one and instead use its ID station_pair_query = ( "$s1 isa station, has naptan-id \"{}\";\n" "$s2 isa station, has naptan-id \"{}\";\n").format( last_naptan_id, interval["stopId"]) tunnel_query = "$t(beginning: $s1, end: $s2) isa tunnel;" response = list( perform_query( match_get(station_pair_query + tunnel_query))) if len(response) < 1: response = list( perform_query( match_insert(station_pair_query, tunnel_query))) # Get the ID of either the pre-existing tunnel or the one just inserted tunnel_id = get_match_id(response, "t") # === Connect Stations to Routes === if i == len(station_intervals['intervals']) - 1: # In this case we're at the last route-section of the route, ending at the last station role_played = "destination" else: role_played = "stop" add_route_stop_relationship(interval["stopId"], role_played, route_id) # === Link routes to tunnels with route-sections === duration = int(interval["timeToArrival"] - last_time_to_arrival) match_query = "$t id {};\n$r id {};".format( tunnel_id, route_id) # insert_query = "$rs(section: $t, service: $r) isa route-section, has duration {};".format(duration) insert_query = ( "$rs isa route-section, has duration {}; " "$r(section: $rs);" "$t(service: $rs);").format(duration) response = list( perform_query( match_insert(match_query, insert_query))) check_response_length(response, min_length=1, max_length=1) # Update variables for the next iteration last_time_to_arrival = interval["timeToArrival"] last_naptan_id = interval["stopId"]
# If you try b_name = "Upminster Underground Station" # Tries to return 1048576 paths, which is 2^20, because most # stations are connected by 2 tunnels. We need a different return type from compute path before we can practically # query for this many paths # If you need to kill a query that is taking too long, you may need: # ./grakn server engine stop # ./grakn server engine start # Choose whether to find paths via the fewest stations, or via the fewest changes between routes FEWEST_STOPS = 0 FEWEST_ROUTES = 1 score_by = FEWEST_STOPS a_id = get_match_id( list( transaction.query( match_get("$s1 isa station, has name \"{}\";".format( a_name)))), "s1") b_id = get_match_id( list( transaction.query( match_get("$s1 isa station, has name \"{}\";".format( b_name)))), "s1") if score_by == FEWEST_STOPS: compute_query = "compute path from {}, to {}, in [station, tunnel];".format( a_id, b_id) # Fewest stops elif score_by == FEWEST_ROUTES: compute_query = "compute path from {}, to {}, in [station, route];".format( a_id, b_id) # Fewest changes else: raise ValueError("No method has been selected") print("Finding shortest paths...")