Esempio n. 1
0
 def test_full_geocode__convert_to_block(self):
     from ebpub.geocoder.base import full_geocode
     # This block goes up to 298.
     result = full_geocode('299 S. Wabash Ave.', convert_to_block=True)
     self.failIf(result['ambiguous'])
     self.assertEqual(result['type'], 'block')
     self.assertEqual(result['result']['address'], '200 block of S. Wabash Ave.')
     # This is also the default behavior.
     self.assertEqual(result, full_geocode('299 S. Wabash Ave.'))
Esempio n. 2
0
    def geocode(self, location_name, **kwargs):
        """
        Tries to geocode the given location string, returning a Point object
        or None.

        Override this if you want to replace with (or fall back to) an
        external geocoding service.

        If the result is ambiguous -- multiple matches -- then tries
        to use optional args to resolve the ambiguity, and if still
        ambiguous, returns the first remaining result.
        """
        try:
            result = full_geocode(location_name, guess=True, **kwargs)
            if result['result']:
                if result['type'] == 'block' and result.get('ambiguous'):
                    self.logger.debug(
                        "Invalid Block but valid street for %r; results unlikely to be useful, giving up"
                        % location_name)
                    return None

                return result['result']
        except (GeocodingException, ParsingError):
            self.logger.debug("Could not geocode location: %s: %s" %
                              (location_name, traceback.format_exc()))
        return None
Esempio n. 3
0
    def geocode(self, location_name, **kwargs):
        """
        Tries to geocode the given location string, returning a Point object
        or None.

        Override this if you want to replace with (or fall back to) an
        external geocoding service.

        If the result is ambiguous -- multiple matches -- then tries
        to use optional args to resolve the ambiguity, and if still
        ambiguous, returns the first remaining result.
        """
        try:
            result = full_geocode(location_name, guess=True, **kwargs)
            if result['result']:
                if result['type'] == 'block' and result.get('ambiguous'):
                    self.logger.debug("Invalid Block but valid street for %r; results unlikely to be useful, giving up" % location_name)
                    return None

                return result['result']
        except (GeocodingException, ParsingError):
            self.logger.debug(
                "Could not geocode location: %s: %s" %
                (location_name, traceback.format_exc()))
        return None
Esempio n. 4
0
    def test_full_geocode__convert_to_block(self):
        from ebpub.geocoder.base import full_geocode

        # This block goes up to 298.
        result = full_geocode("299 S. Wabash Ave.", convert_to_block=True)
        self.failIf(result["ambiguous"])
        self.assertEqual(result["type"], "block")
        self.assertEqual(result["result"]["address"], "200 block of S. Wabash Ave.")
Esempio n. 5
0
 def test_full_geocode__bad_block_on_good_street(self):
     from ebpub.geocoder.base import full_geocode
     # This block goes up to 298.
     result = full_geocode('299 S. Wabash Ave.', convert_to_block=False)
     self.assert_(result['ambiguous'])
     self.assertEqual(result['type'], 'block')
     self.assertEqual(result['street_name'], 'Wabash Ave.')
     self.assertEqual(len(result['result']), 3)
Esempio n. 6
0
    def test_full_geocode__bad_block_on_good_street(self):
        from ebpub.geocoder.base import full_geocode

        # This block goes up to 298.
        result = full_geocode("299 S. Wabash Ave.")
        self.assert_(result["ambiguous"])
        self.assertEqual(result["type"], "block")
        self.assertEqual(result["street_name"], "Wabash Ave.")
        self.assertEqual(len(result["result"]), 3)
Esempio n. 7
0
def _build_geocoder_results(query):
    results = full_geocode(query)
    if results['type'] == 'block':
        return []

    if results['ambiguous'] == True:
        rs = results['result']
    else:
        rs = [results['result']]
        
    return [_build_json_result(query, r, results) for r in rs]
Esempio n. 8
0
    def clean(self):
        self._clean_captcha()
        cleaned_data = super(NewsItemForm, self).clean()
        # Reverse-geocode if we need to - eg. user clicked map
        # but didn't give an address.
        if not cleaned_data.get('location_name'):
            if cleaned_data.get('location'):
                from ebpub.geocoder.reverse import reverse_geocode
                from ebpub.geocoder.reverse import ReverseGeocodeError
                try:
                    block, distance = reverse_geocode(cleaned_data['location'])
                    cleaned_data['location_name'] = block.pretty_name
                except ReverseGeocodeError:
                    logger.info(
                        "Saving NewsItem with no location_name because reverse-geocoding %(location)s failed"
                        % cleaned_data)

        # Geocode if we can, and need to.
        # Should not be necessary, but this is groundwork for #284.
        if not cleaned_data.get('location'):
            if cleaned_data.get('location_name'):
                from ebpub.geocoder.base import full_geocode
                try:
                    geocoded = full_geocode(
                        cleaned_data['location_name'].strip(), guess=True)
                    cleaned_data['location'] = geocoded['result'].location.wkt
                except (IndexError, KeyError, AttributeError):
                    logger.info(
                        "Saving NewsItem with no location because geocoding %(location_name)s failed"
                        % cleaned_data)

        # Note, any NewsItem fields that aren't listed in self._meta.fields
        # have to be manually saved here, because that's the list that's
        # normally consulted when setting attributes on the instance.
        # ... And yes, clean() is normally responsible for setting
        # attributes on the bound instance.
        for key in forms.fields_for_model(self._meta.model):
            if key in cleaned_data.keys():
                setattr(self.instance, key, cleaned_data[key])

        return cleaned_data
Esempio n. 9
0
    def clean(self):
        self._clean_captcha()
        cleaned_data = super(NewsItemForm, self).clean()
        # Reverse-geocode if we need to - eg. user clicked map
        # but didn't give an address.
        if not cleaned_data.get('location_name'):
            if cleaned_data.get('location'):
                from ebpub.geocoder.reverse import reverse_geocode
                from ebpub.geocoder.reverse import ReverseGeocodeError
                try:
                    block, distance = reverse_geocode(cleaned_data['location'])
                    cleaned_data['location_name'] = block.pretty_name
                except ReverseGeocodeError:
                    logger.info("Saving NewsItem with no location_name because reverse-geocoding %(location)s failed" % cleaned_data)

        # Geocode if we can, and need to.
        # Should not be necessary, but this is groundwork for #284.
        if not cleaned_data.get('location'):
            if cleaned_data.get('location_name'):
                from ebpub.geocoder.base import full_geocode
                try:
                    geocoded = full_geocode(cleaned_data['location_name'].strip(),
                                            guess=True)
                    cleaned_data['location'] = geocoded['result'].location.wkt
                except (IndexError, KeyError, AttributeError):
                    logger.info("Saving NewsItem with no location because geocoding %(location_name)s failed" % cleaned_data)

        # Note, any NewsItem fields that aren't listed in self._meta.fields
        # have to be manually saved here, because that's the list that's
        # normally consulted when setting attributes on the instance.
        # ... And yes, clean() is normally responsible for setting
        # attributes on the bound instance.
        for key in forms.fields_for_model(self._meta.model):
            if key in cleaned_data.keys():
                setattr(self.instance, key, cleaned_data[key])

        return cleaned_data
Esempio n. 10
0
def _geocode_geojson(query):
    """Geocode a string and return the result as a list of
    GeoJSON Features.
    """
    if not query:
        return []

    try:
        res = full_geocode(query)
        # normalize a bit
        if not res['ambiguous']:
            res['result'] = [res['result']]
    except DoesNotExist:
        return []

    features = []
    if res['type'] == 'location':
        for r in res['result']:
            feature = {
                'type': 'Feature',
                'geometry': simplejson.loads(r.location.centroid.geojson),
                'properties': {
                    'type': r.location_type.slug,
                    'name': r.name,
                    'city': r.city,
                    'query': query,
                }
            }
            features.append(feature)
    elif res['type'] == 'place':
        for r in res['result']:
            feature = {
                'type': 'Feature',
                'geometry': simplejson.loads(r.location.geojson),
                'properties': {
                    'type': 'place',
                    'name': r.pretty_name,
                    'address': r.address,
                    'query': query,
                }
            }
            features.append(feature)
    elif res['type'] == 'address':
        for r in res['result']:
            feature = {
                'type': 'Feature',
                'geometry': {
                    'type': 'Point',
                    'coordinates': [r.lng, r.lat],
                },
                'properties': {
                    'type': 'address',
                    'address': r.get('address'),
                    'city': r.get('city'),
                    'state': r.get('state'),
                    'zip': r.get('zip'),
                    'query': query
                }
            }
            features.append(feature)
    # we could get type == 'block', but
    # ebpub.db.views.ajax_wkt returned nothing for this,
    # so for now we follow their lead.
    # elif res['type'] == 'block':
    #     pass

    return features
Esempio n. 11
0
def _geocode_geojson(query):
    if not query: 
        return []
        
    try: 
        res = full_geocode(query)
        # normalize a bit
        if not res['ambiguous']: 
            res['result'] = [res['result']]
    except DoesNotExist:
        return []
        
    features = []
    if res['type'] == 'location':
        for r in res['result']: 
            feature = {
                'type': 'Feature',
                'geometry': simplejson.loads(r.location.centroid.geojson),
                'properties': {
                    'type': r.location_type.slug,
                    'name': r.name,
                    'city': r.city,
                    'query': query,
                }
            }
            features.append(feature)
    elif res['type'] == 'place':
        for r in res['result']: 
            feature = {
                'type': 'Feature',
                'geometry': simplejson.loads(r.location.geojson),
                'properties': {
                    'type': 'place',
                    'name': r.pretty_name,
                    'address': r.address, 
                    'query': query,
                }
            }
            features.append(feature)
    elif res['type'] == 'address':
        for r in res['result']:
            feature = {
                'type': 'Feature',
                'geometry': {
                    'type': 'Point',
                    'coordinates': [r.lng, r.lat],
                },
                'properties': {
                    'type': 'address',
                    'address': r.get('address'),
                    'city': r.get('city'),
                    'state': r.get('state'),
                    'zip': r.get('zip'),
                    'query': query
                }
            }
            features.append(feature)
    # we could get type == 'block', but 
    # ebpub.db.views.ajax_wkt returned nothing for this,
    # so for now we follow their lead.
    # elif res['type'] == 'block': 
    #     pass

    return features
Esempio n. 12
0
 def test_full_geocode_place(self):
     from ebpub.geocoder.base import full_geocode
     place = full_geocode('Sears Tower', search_places=True)
     self.assertEqual(place['type'], 'place')
     self.assertEqual(place['result'].normalized_name, 'SEARS TOWER')
Esempio n. 13
0
    def test_full_geocode_place(self):
        from ebpub.geocoder.base import full_geocode

        place = full_geocode("Sears Tower", search_places=True)
        self.assertEqual(place["type"], "place")
        self.assertEqual(place["result"].normalized_name, "SEARS TOWER")
Esempio n. 14
0
def search(request, schema_slug=''):
    "Performs a location search and redirects to the address/xy page."
    # Check whether a schema was provided.
    if schema_slug:
        try:
            schema = get_schema_manager(request).get(slug=schema_slug)
        except Schema.DoesNotExist:
            raise Http404('Schema does not exist')
        url_prefix = schema.url()[:-1]
    else:
        schema = None
        url_prefix = ''

    # Get the query.
    q = request.GET.get('q', '').strip()
    if not q:
        return HttpResponseRedirect(url_prefix + '/') # TODO: Do something better than redirecting.

    # For /search/?type=alert, we redirect results to the alert page, not the
    # place page.
    if request.GET.get('type', '') == 'alert':
        url_method = 'alert_url'
    else:
        url_method = 'url'

    # Try to geocode it using full_geocode().
    try:
        result = full_geocode(q, search_places=False)
    except:
        logger.debug('Unhandled exception from full_geocode:')
        log_exception(level=logging.DEBUG, logger=logger)
    else:
        if result['ambiguous']:
            if result['type'] == 'block':
                streets = []
                street_blocks = {}
                for block in result['result']:
                    street_name = block.street_pretty_name
                    if street_name not in streets:
                        streets.append(street_name)
                        street_blocks[street_name] = []
                    street_blocks[street_name].append(block)

                choices = [{'name': s, 'blocks': street_blocks[s]} for s in streets]
                return eb_render(request, 'db/search_invalid_block.html', {
                    'query': q,
                    'choices': choices,
                })
            else:
                return eb_render(request, 'db/did_you_mean.html', {'query': q, 'choices': result['result']})
        elif result['type'] == 'location':
            return HttpResponseRedirect(url_prefix + getattr(result['result'], url_method)())
        elif result['type'] == 'address':
            # Block
            if result['result']['block']:
                return HttpResponseRedirect(url_prefix + getattr(result['result']['block'], url_method)())
            # Intersection
            try:
                intersection = Intersection.objects.get(id=result['result']['intersection_id'])
            except Intersection.DoesNotExist:
                pass
            else:
                return HttpResponseRedirect(url_prefix + getattr(intersection, url_method)())

    # Failing the geocoding, look in the special-case table.
    try:
        special_case = SearchSpecialCase.objects.get(query=normalize(q))
    except SearchSpecialCase.DoesNotExist:
        pass
    else:
        if special_case.redirect_to:
            return HttpResponseRedirect(special_case.redirect_to)
        else:
            return eb_render(request, 'db/search_special_case.html', {'query': q, 'special_case': special_case})

    # Failing that, display a list of ZIP codes if this looks like a ZIP.
    if re.search(r'^\s*\d{5}(?:-\d{4})?\s*$', q):
        z_list = Location.objects.filter(location_type__slug='zipcodes', is_public=True).select_related().order_by('name')
        if z_list:
            return eb_render(request, 'db/search_error_zip_list.html', {'query': q, 'zipcode_list': z_list})

    # Failing all of that, display the search error page.
    lt_list = LocationType.objects.filter(is_significant=True).order_by('name')
    return eb_render(request, 'db/search_error.html', {'query': q, 'locationtype_list': lt_list})