def calculate_measurement_matrix( service_area_ids, locations, measurer_name, engine=connect.create_db_engine(), ): """ Calculate a measurement matrix for the given service area IDs. The measurement between point i and location j in the cell with row i, column j. """ # TODO: Share introduction of this function with calculate.adequacy. location_to_id_map = collections.defaultdict(list) for j, location in enumerate(locations): # TODO - Permanently fix this on the frontend side. location.pop('id') location_to_id_map[Point(**location)].append(j) locations = list(location_to_id_map.keys()) points = representative_points.minimal_fetch_representative_points( service_area_ids=service_area_ids, engine=engine) logger.debug('{} pairwise distances to calculate.'.format( len(locations) * len(points))) measurer = get_measurer(measurer_name) measurer_config = config.get('measurer_config')[measurer_name] executor_type = measurer_config['adequacy_executor_type'] n_processors = measurer_config['n_adequacy_processors'] logger.debug('Starting {} executors for gravity calculations...'.format( n_processors)) with executor_type(processes=n_processors) as executor: measurements_by_point = executor.starmap( func=_measure_one_to_many, iterable=zip( points, itertools.repeat(locations), itertools.repeat(measurer), )) measurements_by_point = _add_provider_ids( measurements_by_point=measurements_by_point, location_to_id_map=location_to_id_map) measurement_matrix = np.full(shape=(len(points), len(locations)), fill_value=float('inf')) for i, response in enumerate(measurements_by_point): for j, distance in zip(response['location_ids'], response['measurements']): measurement_matrix[i][j] = distance return measurement_matrix
def test_measure_one_to_many(self): """Test measure_one_to_many.""" point = { 'id': 0, 'latitude': 32.74753421600008, 'longitude': -122.2316317029999 } output = gravity._measure_one_to_many(point, self.locations, get_measurer('haversine')) expected = { 'id': 0, 'locations': self.locations, 'location_ids': [1, 2], 'measurements': [399476.6135406669, 0], } assert output == expected
def test_measure_one_to_many(self): """Test measure_one_to_many.""" point = { 'id': 0, 'latitude': 32.74753421600008, 'longitude': -122.2316317029999 } locations = [ Point(latitude=location['latitude'], longitude=location['longitude']) for location in self.locations ] output = gravity._measure_one_to_many(point, locations, get_measurer('haversine')) expected = { 'id': 0, 'locations': locations, 'measurements': [399476.6135406669, 0], } assert output == expected
def test_find_closest_location(): point = { 'id': 0, 'latitude': 37.74753421600008, 'longitude': -122.2316317029999, } locations = [ Point(**{'latitude': 37.74753421600008, 'longitude': -122.2316317029999}), Point(**{'latitude': 32.74753421600008, 'longitude': -122.2316317029999}), ] output = adequacy._find_closest_location( point=point, measurer=get_measurer('haversine'), locations=locations, ) expected = { 'id': point['id'], 'closest_point': Point(latitude=37.74753421600008, longitude=-122.2316317029999), 'to_closest_provider': 0 } assert output == expected
def calculate_adequacies(service_area_ids, locations, engine, measurer_name, radius_in_meters=RELEVANCY_RADIUS_IN_METERS): """ Calculate adequacies. The calculation proceeds as follows: - Determine nearby providers for each service area using the radius_in_meters. - If no nearby providers are found, use the full provider list. - Fetch representative points. - Compare each representative point to the subset of nearby providers to determine the closest provider. - Aggregate the information for each point and return. """ # TODO - Split analyis by specialty. location_mapping = collections.defaultdict(list) for i, location in enumerate(locations): # TODO - Permanently fix this on the frontend side. point_id = location.pop('id', i) location_mapping[Point(**location)].append(point_id) locations = list(location_mapping.keys()) logger.debug( 'Calculating adequacies for {} locations ({} unique) and {} service areas using {}.' .format(len(locations), len(location_mapping), len(service_area_ids), measurer_name)) points = representative_points.minimal_fetch_representative_points( service_area_ids=service_area_ids, engine=engine) locations_to_check_by_service_area = _get_locations_to_check_by_service_area( service_area_ids=service_area_ids, locations=locations, engine=engine, radius_in_meters=radius_in_meters) locations_to_check_by_point = [ locations_to_check_by_service_area[point['service_area_id']] for point in points ] logger.debug('{} pairwise distances to calculate.'.format( str(sum(len(locations) for locations in locations_to_check_by_point)))) measurer = get_measurer(measurer_name) measurer_config = config.get('measurer_config')[measurer_name] executor_type = measurer_config['adequacy_executor_type'] n_processors = measurer_config['n_adequacy_processors'] exit_distance = measurer_config[ 'exit_distance_in_miles'] * ONE_MILE_IN_METERS logger.debug('Starting {} executors for adequacy calculations...'.format( n_processors)) with executor_type(processes=n_processors) as executor: adequacies = executor.starmap(func=_find_closest_location, iterable=zip( points, locations_to_check_by_point, itertools.repeat(measurer), itertools.repeat(exit_distance))) adequacies_response = _add_closest_provider_id(adequacies, location_mapping) logger.debug('Returning adequacy results.') return list(adequacies_response)