class WMTLocations(): """ Service object used to find stops or stations (locations) - given a position, exact match or fuzzy match, will return the best matching stop. Subclassed and not called directly """ def __init__(self, instance_name): self.database = WMTDatabase('%s.geodata.db' % instance_name) self.network = None self.returned_object = Location def find_closest(self, position, params): """ Find the closest location to the (lat, long) position specified, querying the database with dictionary params, of the format { Column Name : value }. Returns an object of class returned_object, or None if none found nearby """ # GPSes use WGS84 model of Globe, but Easting/Northing based on OSGB36, so convert to an easting/northing logging.debug("Position in WGS84 determined as lat/long: %s %s", position[0], position[1]) easting, northing = convertWGS84toOSEastingNorthing(*position) logging.debug("Translated into OS Easting %s, Northing %s", easting, northing) # Do a funny bit of Pythagoras to work out closest stop. We can't find square root of a number in sqlite # but then again, we don't need to, the smallest square will do. Sort by this column in ascending order # and find the first row (where_statement, where_values) = self.database.make_where_statement('locations', params) query = """ SELECT (location_easting - %d)*(location_easting - %d) + (location_northing - %d)*(location_northing - %d) AS dist_squared, * FROM locations WHERE %s ORDER BY dist_squared LIMIT 1 """ % (easting, easting, northing, northing, where_statement) row = self.database.get_row(query, where_values) if row: obj = self.returned_object(Distance=sqrt(row['dist_squared']), **row) logging.debug("Have found nearest location %s", obj) return obj else: logging.debug("No location found near %s, sorry", position) return None def find_fuzzy_match(self, stop_or_station_name, params): """ Find the best fuzzy match to the query_string, querying the database with dictionary params, of the format { Column Name : value, }. Returns an object of class returned_object, or None if no fuzzy match found """ if not stop_or_station_name or stop_or_station_name == "Unknown": return None # Try to get an exact match first against station names in database exact_params = params.copy() exact_params.update({'name': stop_or_station_name}) exact_match = self.find_exact_match(exact_params) if exact_match: return exact_match # Users may not give exact details, so we try to match fuzzily (where_statement, where_values) = self.database.make_where_statement('locations', params) rows = self.database.get_rows("SELECT * FROM locations WHERE %s" % where_statement, where_values) possible_matches = [self.returned_object(**row) for row in rows] best_match = get_best_fuzzy_match(stop_or_station_name, possible_matches) if best_match: return best_match else: return None def find_exact_match(self, params): """ Find the exact match for an item matching params. Returns an object of class returned_object, or None if no fuzzy match found """ (where_statement, where_values) = self.database.make_where_statement('locations', params) row = self.database.get_row("SELECT * FROM locations WHERE %s LIMIT 1" % where_statement, where_values) if row: return self.returned_object(**row) else: return None