def handle_GET(self, request, context, entity=None): point = get_point(request, entity) if point is None: return self.render(request, {'entity': entity}, 'places/entity_without_location') if entity: return_url = reverse( 'places:entity-nearby-list', args=[entity.identifier_scheme, entity.identifier_value]) else: return_url = reverse('places:nearby-list') # Get entity types to show on nearby page entity_types = EntityType.objects.filter(show_in_nearby_list=True) for et in entity_types: # For each et, get the entities that belong to it et.max_distance = humanise_distance(0) et.entities_found = 0 es = et.entities_completion.filter( location__isnull=False, location__distance_lt=(point, D( km=5))).distance(point).order_by('distance') for e in es: # Selection criteria for whether or not to count this entity if (e.distance.m**0.75) * (et.entities_found + 1) > 500: break et.max_distance = humanise_distance(e.distance.m) et.entities_found += 1 categorised_entity_types = defaultdict(list) for et in filter(lambda et: et.entities_found > 0, entity_types): categorised_entity_types[_(et.category.name)].append(et) categorised_entity_types = dict( (k, sorted(v, key=lambda x: x.verbose_name.lower())) for k, v in categorised_entity_types.items()) context.update({ 'entity_types': categorised_entity_types, 'entity': entity, 'return_url': return_url, # entity is None => we've searched around the user's location 'exposes_user_data': entity is None, }) if entity and not entity.location: return self.render(request, context, 'places/entity_without_location') return self.render(request, context, 'places/nearby_list')
def get_location_response(self, request, context, form=None): if context.get('return_url').startswith('/'): redirect = context['return_url'] else: redirect = reverse('home:index') if context['format'] == 'json': return self.render(request, { 'name': request.session['geolocation:name'], 'redirect': redirect, 'accuracy': humanise_distance(request.session['geolocation:accuracy']), 'longitude': request.session['geolocation:location'][0], 'latitude': request.session['geolocation:location'][1], 'history': request.session.get('geolocation:history', ()), 'alternatives': form.cleaned_data.get('alternatives') if form else None, 'favourites': [dict(favourite.items() + [('id', id)]) for id, favourite in request.session.get('geolocation:favourites', {}).items()], }, None) elif context['format'] == 'embed': response = HttpResponse('') response['X-Embed-Redirect'] = redirect response['X-Embed-Location-Name'] = form.cleaned_data['name'] return response else: alternatives = form.cleaned_data.get('alternatives') if form else None if alternatives != None and len(alternatives) > 0: # Not doing AJAX update, so show the form allowing users to # choose a location from alternatives before returning to their # original screen context.update({ 'geolocation_alternatives': form.cleaned_data.get('alternatives') }) return self.handle_GET(request, context) else: return self.redirect(redirect, request, 'seeother')
def get_metadata(self, request, scheme, value): entity = get_entity(scheme, value) user_location = request.session.get('geolocation:location') distance, bearing = entity.get_distance_and_bearing_from(user_location) additional = '<strong>%s</strong>' % capfirst( entity.primary_type.verbose_name) if distance: additional += ', ' + _('about %(distance)s %(bearing)s') % { 'distance': humanise_distance(distance), 'bearing': bearing } routes = sorted(set(sor.route.service_id for sor in entity.stoponroute_set.all()), key=bus_route_sorter) if routes: additional += ', ' + ungettext('service %(services)s stops here', 'services %(services)s stop here', len(routes)) % { 'services': ' '.join(routes) } return { 'title': entity.title, 'additional': additional, 'entity': entity, }
def handle_GET(self, request, context, entity=None): point = get_point(request, entity) if point is None: return self.render(request, { 'entity': entity }, 'places/entity_without_location') if entity: return_url = reverse('places:entity-nearby-list', args=[entity.identifier_scheme, entity.identifier_value]) else: return_url = reverse('places:nearby-list') # Get entity types to show on nearby page entity_types = EntityType.objects.filter(show_in_nearby_list=True) for et in entity_types: # For each et, get the entities that belong to it et.max_distance = humanise_distance(0) et.entities_found = 0 es = et.entities_completion.filter(location__isnull=False, location__distance_lt=(point, D(km=5))).distance(point).order_by('distance') for e in es: # Selection criteria for whether or not to count this entity if (e.distance.m ** 0.75) * (et.entities_found + 1) > 500: break et.max_distance = humanise_distance(e.distance.m) et.entities_found += 1 categorised_entity_types = defaultdict(list) for et in filter(lambda et: et.entities_found > 0, entity_types): categorised_entity_types[_(et.category.name)].append(et) categorised_entity_types = dict( (k, sorted(v, key=lambda x: x.verbose_name.lower())) for k, v in categorised_entity_types.items()) context.update({ 'entity_types': categorised_entity_types, 'entity': entity, 'return_url': return_url, # entity is None => we've searched around the user's location 'exposes_user_data': entity is None, }) if entity and not entity.location: return self.render(request, context, 'places/entity_without_location') return self.render(request, context, 'places/nearby_list')
def get_location_response(self, request, context, form=None): if context.get('return_url').startswith('/'): redirect = context['return_url'] else: redirect = reverse('home:index') if context['format'] == 'json': return self.render( request, { 'name': request.session['geolocation:name'], 'redirect': redirect, 'accuracy': humanise_distance(request.session['geolocation:accuracy']), 'longitude': request.session['geolocation:location'][0], 'latitude': request.session['geolocation:location'][1], 'history': request.session.get('geolocation:history', ()), 'alternatives': form.cleaned_data.get('alternatives') if form else None, 'favourites': [ dict(favourite.items() + [('id', id)]) for id, favourite in request.session.get( 'geolocation:favourites', {}).items() ], }, None) elif context['format'] == 'embed': response = HttpResponse('') response['X-Embed-Redirect'] = redirect response['X-Embed-Location-Name'] = form.cleaned_data['name'] return response else: alternatives = form.cleaned_data.get( 'alternatives') if form else None if alternatives != None and len(alternatives) > 0: # Not doing AJAX update, so show the form allowing users to # choose a location from alternatives before returning to their # original screen context.update({ 'geolocation_alternatives': form.cleaned_data.get('alternatives') }) return self.handle_GET(request, context) else: return self.redirect(redirect, request, 'seeother')
def get_metadata(self, request, scheme, value): entity = get_entity(scheme, value) user_location = request.session.get('geolocation:location') distance, bearing = entity.get_distance_and_bearing_from(user_location) additional = '<strong>%s</strong>' % capfirst(entity.primary_type.verbose_name) if distance: additional += ', ' + _('about %(distance)s %(bearing)s') % { 'distance': humanise_distance(distance), 'bearing': bearing } routes = sorted(set(sor.route.service_id for sor in entity.stoponroute_set.all()), key=bus_route_sorter) if routes: additional += ', ' + ungettext('service %(services)s stops here', 'services %(services)s stop here', len(routes)) % { 'services': ' '.join(routes) } return { 'title': entity.title, 'additional': additional, 'entity': entity, }
def generate_route(points, type): """ Given 2 Points, this will return a route between them. The route consists of a dictionary with the following keys: * error (optional, and if set means that the object contains no route), which is a string describing any errors that occurred in plotting the route * total_time: An int of the number of seconds this route is estimated to take * total_distance: An int of the number of metres this route is expected to take * waypoints: A list of dictionaries, where each dictionary has 2 keys: 'instruction', which is a human-readable description of the steps to be taken here, and 'location', which is a Point describing the route to be taken @param points: An ordered list of points to be included in this route @type points: [Point] @param type: The type of route to generate (foot, car or bike) @type type: str @return: A dictionary containing the route and metadata associated with it @rtype: dict """ # Build Cyclestreets request: url = CYCLESTREETS_URL % urlencode({ 'key': settings.API_KEYS['cyclestreets'], 'plan': 'balanced', 'itinerarypoints': '|'.join('%f,%f' % (p[0], p[1]) for p in points) }) json = simplejson.load(urlopen(url)) if not json: return { 'error': _('Unable to plot route') } else: summary = json['marker'][0]['@attributes'] waypoints = [] for i, waypoint in enumerate(json['marker'][1:]): segment = waypoint['@attributes'] waypoints.append({ 'instruction': _('%(instruction)s at %(name)s') % { 'instruction': capfirst(segment['turn']), 'name': segment['name'] }, 'additional': _('%(direction)s for %(distance)s (taking approximately %(time)s)') % { 'direction': bearing_to_compass(int(segment['startBearing'])), 'distance': humanise_distance(segment['distance'], False), 'time': humanise_seconds(segment['time']) }, 'waypoint_type': { 'straight on': 'straight', 'turn left': 'left', 'bear left': 'slight-left', 'sharp left': 'sharp-left', 'turn right': 'right', 'bear right': 'slight-right', 'sharp right': 'sharp-right', 'double-back': 'turn-around', }.get(segment['turn']), 'location': Point(*map(float, segment['points'].split(' ')[0].split(','))), 'path': LineString(map(lambda ps: Point(*map(float, ps.split(','))), segment['points'].split(' '))) }) return { 'total_time': summary['time'], 'total_distance': summary['length'], 'waypoints': waypoints, 'path': LineString(map(lambda ps: Point(*map(float, ps.split(','))), summary['coordinates'].split(' '))) }
def simplify_value(value): if hasattr(value, 'simplify_for_render'): return value.simplify_for_render(simplify_value, simplify_model) elif isinstance(value, dict): out = {} for key in value: new_key = key if isinstance(key, (basestring, int)) else str(key) try: out[new_key] = simplify_value(value[key]) except NotImplementedError: logger.info('Could not simplify field %s of type %s', key, type(value[key]), exc_info=True) pass return out elif isinstance(value, tuple) and hasattr(value, '_asdict'): # Handle named tuples as dicts return simplify_value(value._asdict()) elif isinstance(value, (list, tuple, set, frozenset)): out = [] for subvalue in value: try: out.append(simplify_value(subvalue)) except NotImplementedError: logger.info('Could not simplify a value of type %s', type(subvalue), exc_info=True) if isinstance(value, tuple): return tuple(out) else: return out elif isinstance(value, (basestring, int, float)): return value elif isinstance(value, lazy_translation): return unicode(value) elif isinstance(value, datetime.datetime): return DateTimeUnicode(value.isoformat(' ')) elif isinstance(value, datetime.date): return DateUnicode(value.isoformat()) elif hasattr(type(value), '__mro__') and models.Model in type(value).__mro__: return simplify_model(value) elif isinstance(value, Page): return { 'has_next': value.has_next(), 'has_previous': value.has_next(), 'next_page_number': value.has_next(), 'previous_page_number': value.has_next(), 'number': value.number, 'objects': simplify_value(value.object_list), 'num_pages': value.paginator.num_pages, 'num_objects': value.paginator.count, } elif value is None: return None elif isinstance(value, Point): return simplify_value(list(value)) elif isinstance(value, Distance): # This is here to avoid a circular import from molly.utils.templatetags.molly_utils import humanise_distance return simplify_value(humanise_distance(value.m)) elif hasattr(value, '__iter__'): # Iterators may be unbounded; silently ignore elements once we've already had 1000. return [simplify_value(item) for item in itertools.islice(value, 1000)] else: raise NotImplementedError
def generate_route(points, type): """ Given 2 Points, this will return a route between them. The route consists of a dictionary with the following keys: * error (optional, and if set means that the object contains no route), which is a string describing any errors that occurred in plotting the route * total_time: An int of the number of seconds this route is estimated to take * total_distance: An int of the number of metres this route is expected to take * waypoints: A list of dictionaries, where each dictionary has 2 keys: 'instruction', which is a human-readable description of the steps to be taken here, and 'location', which is a Point describing the route to be taken @param points: An ordered list of points to be included in this route @type points: [Point] @param type: The type of route to generate (foot, car or bike) @type type: str @return: A dictionary containing the route and metadata associated with it @rtype: dict """ def to_comma_string(p): return ','.join(reversed(map(str, p))) # Build cloudmade request: urlpoints = to_comma_string(points[0]) if points[1:-1]: urlpoints += ',[' + ','.join(map(to_comma_string, points[1:-1])) + ']' urlpoints += ',' + to_comma_string(points[-1]) url = CLOUDMADE_URL + '%s/%s.js?lang=%s' % (urlpoints, type, get_language()[:2]) json = simplejson.load(urlopen(url)) if json['status'] != 0: return {'error': json['status_message']} else: points = [Point(p[1], p[0], srid=4326) for p in json['route_geometry']] waypoints = [] for i, waypoint in enumerate(json['route_instructions']): if i == 0: (instruction, length, position, time, length_caption, earth_direction, azimuth) = waypoint turn_type = 'start' else: (instruction, length, position, time, length_caption, earth_direction, azimuth, turn_type, turn_angle) = waypoint turn_type = { 'C': 'straight', 'TL': 'left', 'TSLL': 'slight-left', 'TSHL': 'sharp-left', 'TR': 'right', 'TSLR': 'slight-right', 'TSHR': 'sharp-right', 'TU': 'turn-around', }.get(turn_type) waypoints.append({ 'instruction': instruction, 'additional': ugettext( '%(direction)s for %(distance)s (taking approximately %(time)s)' ) % { 'direction': earth_direction, 'distance': humanise_distance(length, False), 'time': humanise_seconds(time) }, 'waypoint_type': turn_type, 'location': points[position], 'path': LineString( map( lambda ps: Point(*ps), points[position:json['route_instructions'][i + 1][2] + 1] if i + 1 < len(json['route_instructions']) else points[position:])) }) return { 'total_time': json['route_summary']['total_time'], 'total_distance': json['route_summary']['total_distance'], 'waypoints': waypoints, 'path': LineString( map(lambda ps: Point(*ps), map(reversed, json['route_geometry']))) }
def generate_route(points, type): """ Given 2 Points, this will return a route between them. The route consists of a dictionary with the following keys: * error (optional, and if set means that the object contains no route), which is a string describing any errors that occurred in plotting the route * total_time: An int of the number of seconds this route is estimated to take * total_distance: An int of the number of metres this route is expected to take * waypoints: A list of dictionaries, where each dictionary has 2 keys: 'instruction', which is a human-readable description of the steps to be taken here, and 'location', which is a Point describing the route to be taken @param points: An ordered list of points to be included in this route @type points: [Point] @param type: The type of route to generate (foot, car or bike) @type type: str @return: A dictionary containing the route and metadata associated with it @rtype: dict """ def to_comma_string(p): return ','.join(reversed(map(str, p))) # Build cloudmade request: urlpoints = to_comma_string(points[0]) if points[1:-1]: urlpoints += ',[' + ','.join(map(to_comma_string, points[1:-1])) + ']' urlpoints += ',' + to_comma_string(points[-1]) url = CLOUDMADE_URL + '%s/%s.js?lang=%s' % (urlpoints, type, get_language()[:2]) json = simplejson.load(urlopen(url)) if json['status'] != 0: return { 'error': json['status_message'] } else: points = [Point(p[1], p[0], srid=4326) for p in json['route_geometry']] waypoints = [] for i, waypoint in enumerate(json['route_instructions']): if i == 0: (instruction, length, position, time, length_caption, earth_direction, azimuth) = waypoint turn_type = 'start' else: (instruction, length, position, time, length_caption, earth_direction, azimuth, turn_type, turn_angle) = waypoint turn_type = { 'C': 'straight', 'TL': 'left', 'TSLL': 'slight-left', 'TSHL': 'sharp-left', 'TR': 'right', 'TSLR': 'slight-right', 'TSHR': 'sharp-right', 'TU': 'turn-around', }.get(turn_type) waypoints.append({ 'instruction': instruction, 'additional': ugettext('%(direction)s for %(distance)s (taking approximately %(time)s)') % { 'direction': earth_direction, 'distance': humanise_distance(length, False), 'time': humanise_seconds(time) }, 'waypoint_type': turn_type, 'location': points[position], 'path': LineString(map(lambda ps: Point(*ps), points[position:json['route_instructions'][i+1][2]+1] if i+1 < len(json['route_instructions']) else points[position:] )) }) return { 'total_time': json['route_summary']['total_time'], 'total_distance': json['route_summary']['total_distance'], 'waypoints': waypoints, 'path': LineString(map(lambda ps: Point(*ps), map(reversed, json['route_geometry']))) }