Example #1
0
    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')
Example #2
0
 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')
Example #3
0
 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,
     }
Example #4
0
    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')
Example #5
0
 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')
Example #6
0
 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(' ')))
        }
Example #8
0
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
Example #9
0
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
Example #10
0
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'])))
        }
Example #11
0
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'])))
        }