开发者

Smart location form field

开发者 https://www.devze.com 2023-02-22 11:31 出处:网络
I have a single text field on a user registration form for location. I would essentially like this field to be validated against Google Maps (or equivilent) -

I have a single text field on a user registration form for location.

I would essentially like this field to be validated against Google Maps (or equivilent) - only allowing valid locations to pass (ideally in a format something like Waterloo, London or London, England).

Requirements:

  1. As well as a location name, I also would like to return co-ordinates (lat, lng) for the center of that location (even if broad e.g. London, England) as I will be cross referencing this to serve relevent information to the user.
  2. An autocomplete list/ smart suggestion list based on valid locations (ideally just political locations - not street specific) as the user is typing in the field *(Edit: See below).
  3. Force the user to use a location from the autocomplete list.

Privilages permitting this field will be autofilled using HTML5 Geolocation API to get the user lat lng and then converted to a general location (i.e. general town - a bit like Tweetdeck for Chrome does when you hit 'Add your location').

The question is - what is the best way of going about this? What API's do I use? What sections of the API's do I use to achieve this?

*Edit: After experimenting with Google Maps API v3 geocoding it seems there is an issue regarding the result开发者_运维百科s (a bug or by design I'm not sure - http://code.google.com/p/gmaps-api-issues/issues/detail?id=2042). For example if I type in Waterloo all I get is Waterloo, Canada. What about all the other Waterloos http://en.wikipedia.org/wiki/Waterloo? I want a list of X many (where X !== 1) for the user to select from. Ideally where I predefine X. Also if I type in 'L' to the form field I would like to see a list of relevent suggestions returned by order of most likely (perhaps influenced by the bias GeocodeRequest parameter) - so London, UK, Liverpool, UK, Leeds, UK etc etc. Instead I get Limburg, The Netherlands - 1 random result. WTF? What are the alternatives to doing this through Google Maps API?

*Update/Solution: Okay I've worked up a little something that uses geonames http://jsfiddle.net/ekzMN/95/. It's working pretty much okay, although I'm sure it can be massively improved on. You're all free to use this code as you wish. In my real world example I also add google geocode as a secondary source, as it's much smarter than geonames (i.e. if I type in a postcode, or a street address). This is dependent on intialising Geocoder:

// Get more specific results from Google geocode
geocoder.geocode( { 'address': request.term, 'region': 'GB' }, function(results, status) {
    $.map(results, function(item) {

        var ignore = ['route', 'country', 'natural_feature', 'intersection', 'premise', 'subpremise', 'airport', 'park', 'point_of_interest', 'establishment', 'transit_station'];

        if (valid_location(item.types, ignore) == 1) {                  
            var sublocality;
            var locality;
            var region;
            var country;

            $.each(item.address_components, function (i, data) {
                if ($.inArray('sublocality', data.types) > -1) {
                    sublocality = data.long_name;
                }
                if ($.inArray('locality', data.types) > -1) {
                    if (typeof sublocality == 'undefined') {
                        sublocality = data.long_name;
                    }
                }
                if ($.inArray('administrative_area_level_2', data.types) > -1) {
                    locality = data.long_name;
                }
                if ($.inArray('administrative_area_level_1', data.types) > -1) {
                    region = data.long_name;
                }
                if ($.inArray('country', data.types) > -1) {
                    country = data.long_name;
                }
            });

            if ((country != 'United Kingdom') || ($.inArray('administrative_area_level_1', item.types) == -1)) {

                var location = format_location(sublocality, locality, region, country);

                if (duplicate(matches, location) == 0) {
                    matches.push({
                        label: location,
                        value: location,
                        latitude: item.geometry.location.lat(),
                        longitude: item.geometry.location.lng()
                    });
                }                       
            }
        }
    });
    // Update autocomplete
    response(matches);
})

The combination of the two works well providing smart search and multiple suggestions. The one issue that's bugging me is when I use both sources (geonames & google geocode) then the input select range is very unreliable - sometimes it autofills, selects the additional section and then stays highlighted as it should, other times the blue highlight dissapears (although it acts as it's still there i.e. on keypress the selection dissapears), and other times the cursor goes straight to the end of the input. Some conflict with google maps I think, rather than multiple source issue, as this doesn't happen if I have two geonames sources instead?

Correct answer goes to anyone who can improve upon this!

Thanks


There is a page of related jQuery plugins here: http://plugins.jquery.com/plugin-tags/geocoding

Specifically, for your questions:

  1. Google geocoding does this by default. It will give a center coordinate for broad areas, etc. Related question here: using jquery.getJson with Google's GeoCoding HTTP Service

  2. http://code.google.com/p/geo-autocomplete/ is one example

  3. This is form validation for the most part. You should be able to simply do a final autocomplete lookup and if it returns no suggestions, flag a warning, etc.

As to what sections to use, that is up to you. Going much further, we would be writing it for you. I am sure many of the API's above have examples you can glean from.


OK.. so looking at your "solution", I notice several issues. First, it completely breaks in every browser I try....

After some fixes, I have identified a number of issues with your "solution":

  1. .autocomplete() is an initialization function. It does not need to be called on every keyup. This is handled automatically after calling .autocomplete() on a jQuery element. Calling it on every keyup will cause a huge performance hit as well as a number of other mysterious and/or unintended consequences.

  2. Storing legal values in a global variable, and then using blur to check against those is a bad idea. While I am not sold on the idea of storing every legal value that comes back from autocomplete, I can't think of a better solution for your case at the moment. What I did do, was to store each legal value in the .data() of the input item, to be checked against on .blur(). This is stored as a hash/associative-array, which means we get duplicates removed for free.

  3. If you want your users to only select legal values from the back-end, the best solution is to validate the input against the back-end on form submit. (Blur has issues because selecting an item from the autocomplete suggestions inherently blurs the input before filling in the selected value. In other words, you will most likely have an invalid location.) This would require you to save, as part of your match data, a string that can be sent back to the server for validation. The other issue is that if you have a slow server, such as is the case with GeoNames free access, you may not get immediate feedback for the user.

That being said, I have fixed your code (with regard to points 1 and 2 above) to call autocomplete correctly, and to store valid solutions in the input object as jQuery data. I also coded it as an extension, so it can be called on any jQuery selector:

http://jsfiddle.net/jtbowden/7NmLr/

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号