예제 #1
0
def google_style(request, max_results=10, db_alias=None):
    """
    A view that takes an address (from the request's querystring) and
    geocodes it, returning JSON in a structure that mimics Google's
    geocoding API.

    Takes one required and two optional arguments:
        *   request: the calling HTTP request.
        +   max_results: the number of matching address results to be
                returned. Defaults to ten results.
        +   db_alias: the name given to the geocoder's database in your
                settings.py file. Defaults to None (though a null value
                will be overridden in lines 32-37).

    Returns a JSON response containing the results of the query, ordered
    by the geocoder's confidence in how well they match the search term.
    """
    # First, check to make sure both required parameters were sent in the
    # request. If not, return an error much the same as Google does.
    try:
        request.GET['sensor']
    except MultiValueDictKeyError:
        json_response = {
            'results': [],
            'status': "REQUEST_DENIED"
        }
        return HttpResponse(
            simplejson.dumps(json_response, indent=4),
            mimetype="application/json")

    try:
        address = request.GET['address']
    except MultiValueDictKeyError:
        json_response = {
            'results': [],
            'status': "ZERO_RESULTS"
        }
        return HttpResponse(
            simplejson.dumps(json_response, indent=4),
            mimetype="application/json")

    if not db_alias:
        db_alias = getattr(
            settings,
            'GEOCODER_DB_ALIAS',
            "geocoder"
        )

    geocoded = geocode_address(
            address,
            max_results=max_results,
            db_alias=db_alias
        )

    results = []
    for geocode_result in geocoded:
        # First, compute the lat and lng of the result, and also its viewport
        # ()
        lat = geocode_result.lat
        lng = geocode_result.lng
        ne_lat = lat + .001
        ne_lng = lng + .001
        sw_lat = lat - .001
        sw_lng = lng - .001

        # Then, build up the result's JSON values, apart from the address'
        # component parts (we'll build those in the next step.
        json_result = {}
        json_result['geometry'] = {}
        json_result['geometry']['location_type'] = 'RANGE_INTERPOLATED'
        json_result['geometry']['viewport'] = {}
        json_result['geometry']['viewport']['northeast'] = {}
        json_result['geometry']['viewport']['northeast']['lat'] = ne_lat
        json_result['geometry']['viewport']['northeast']['lng'] = ne_lng
        json_result['geometry']['viewport']['southwest'] = {}
        json_result['geometry']['viewport']['southwest']['lat'] = sw_lat
        json_result['geometry']['viewport']['southwest']['lng'] = sw_lng
        json_result['geometry']['location'] = {}
        json_result['geometry']['location']['lat'] = lat
        json_result['geometry']['location']['lng'] = lng
        json_result['formatted_address'] = geocode_result.render_one_line()
        json_result['address_components'] = []
        json_result['types'] = ['street_address']

        # Now construct the address components.
        address_components = geocode_result.components

        # Attach the location's street address number to the per-address
        # values, if it has been given.
        if address_components[0] != '':
            new_component = {
                'types': ['street_number'],
                'short_name': address_components[0],
                'long_name': address_components[0],
            }
            json_result['address_components'].append(new_component)

        # Attach the street name to the per-address values, if it has been
        # given.
        if address_components[2] != '':
            address_street = [item for item in address_components[1:5] \
                                if item != '']
            new_component = {
                'types': ['route'],
                'short_name': " ".join(item for item in address_street),
                'long_name': " ".join(item for item in address_street),
            }
            json_result['address_components'].append(new_component)

        # Attach the apartment number to the per-address values, if it has been
        # given.
        if address_components[5] != '':
            new_component = {
                'types': ['subpremise'],
                'short_name': address_components[5].strip('"'),
                'long_name': address_components[5].strip('"'),
            }
            json_result['address_components'].append(new_component)

        # Attach the city name to the per-address values, if it has been given.
        if address_components[6] != '':
            new_component = {
                'types': ['locality', 'political'],
                'short_name': address_components[6],
                'long_name': address_components[6],
            }
            json_result['address_components'].append(new_component)

        # Attach the state to the per-address values, if it has been given.
        if address_components[7] != '':
            new_component = {
                'types': ['administrative_area_level_1', 'political'],
                'short_name': address_components[7],
                'long_name': address_components[7],
            }
            json_result['address_components'].append(new_component)

        # Attach the postal (ZIP) code to the per-address values, if it has
        # been given.
        if address_components[8] != '':
            new_component = {
                'types': ['postal_code'],
                'short_name': address_components[8],
                'long_name': address_components[8],
            }
            json_result['address_components'].append(new_component)

        # Add the country to the per-address values.
        new_component = {
            'types': ['country', 'political'],
            'short_name': 'US',
            'long_name': 'United States',
        }
        json_result['address_components'].append(new_component)

        # Finally, add the result we've just constructed to the list of all
        # results for this address.
        results.append(json_result)

    # Now format the per-entire-request values.
    json_response = {}
    json_response['results'] = results
    json_response['status'] = "OK"

    # Return a JSON response using simplejson and mimetype.
    return HttpResponse(
            simplejson.dumps(json_response, indent=4),
            mimetype="application/json"
        )
예제 #2
0
def search(search_string, max_results=10, db_alias=None):
    """
    Given a search string, attempts to find matching addresses or
    intersections based on input format. (Eventually, this will attempt
    to find matching points of interest as well.)

    Takes one required and one optional argument:
        *   search_string: the address or intersection, as a string,
                to be geocoded.
        +   max_results: the maximum number of results to be returned
                for any search. Defaults to ten results.
        +   db_alias: the name given to the geocoder's database in your
                settings.py file. Defaults to None (though a null value
                will be overridden in lines 47-52).

    Returns a list of lieux.objects.GeocodedIntersection objects or
    lieux.objects.GeocodedAddress objects representing possible
    intersection- or address-coordinate pairs and the geocoder's
    confidence each result matches the query, if results are found.
    Otherwise returns a value of None.
    """
    # Before anything else, SQL-escape the address by removing any single and
    # double quotes from the raw string. This may screw with some obscure
    # addresses that have apostrophes; we'll have to see if it does.
    search_string = search_string.replace("'", "").replace('"', '')

    # Unless otherwise specified, the database alias will be that which has
    # been specified in settings.GEOCODER_DB_ALIAS (or, failing that, the
    # string 'geocoder').
    if not db_alias:
        db_alias = getattr(
                settings,
                'GEOCODER_DB_ALIAS',
                'geocoder'
            )

    # Search for a match. If the address string includes an '@' symbol, look no
    # further and start processing the input as an intersection. Else if the
    # string has a match for one of the other union symbols, parse to see if it
    # is an address (whether it begins with a number). Note that we want to
    # protect against ordinal streets ('1st', '58th', etc. setting off this
    # filter condition), so we'll screen to see if the first word is an ordinal
    # first.
    certain_match = ALWAYS_DENOTES_INTERSECTION_RE.search(
            search_string.upper()
        )
    possible_match = SOMETIMES_DENOTES_INTERSECTION_RE.search(
            search_string.upper()
        )

    results = None
    if certain_match:
        results = geocode_intersection(
                search_string,
                max_results=max_results,
                db_alias=db_alias
            )
    elif possible_match:
        if search_string.split(' ')[0][0].isdigit():
            if search_string.split(' ')[0] \
                            in STREET_NUMBERS_TO_ORDINALS.values():
                try:
                    results = geocode_intersection(
                            search_string,
                            max_results=max_results,
                            db_alias=db_alias
                        )
                except IntersectionInputError:
                    pass
                except IntersectionNotFoundError:
                    pass
            else:
                pass
        else:
            try:
                results = geocode_intersection(
                        search_string,
                        max_results=max_results,
                        db_alias=db_alias
                    )
            except IntersectionInputError:
                pass
            except IntersectionNotFoundError:
                pass

    if not results:
        try:
            results = geocode_address(
                    search_string,
                    max_results=max_results,
                    db_alias=db_alias
                )
        except AddressNotFoundError:
            raise NoResultsError("No matching addresses or intersections " \
                                "were found based on your search.")

    return results