SitePoint Sponsor

User Tag List

Results 1 to 7 of 7
  1. #1
    SitePoint Addict jamus's Avatar
    Join Date
    Jul 2004
    Location
    Devon, UK
    Posts
    301
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Find record with closest latitude / longitude from stringify'ed data in localstorage

    Hi,

    Have a stored some json data for locations in local storage as I dont want to load the list every time. The data looks like this.

    If I have the users latitude & longitude how would I go about getting the ID of record with closest latitude & longitude values?

    Do I need to loop through them all comparing how different each one is and store the ID of the one that is 'Least' different? Or is there a function that could perform a search of this type?


    I really dont know where to start with this so any pointers would be appreciated.

    Code:
    {"Locations":{"Location":[{"id":"3066","latitude":"57.6494","longitude":"-3.5606","name":"Kinloss"},{"id":"3080","latitude":"57.077","longitude":"-2.836","name":"Aboyne"},{"id":"3091","latitude":"57.206","longitude":"-2.202","name":"Aberdeen Dyce"},{"id":"3134","latitude":"55.907","longitude":"-4.533","name":"Glasgow/Bishopton"},{"id":"3136","latitude":"55.515","longitude":"-4.585","name":"Prestwick Rnas"},{"id":"3144","latitude":"56.326","longitude":"-3.729","name":"Strathallan"},

  2. #2
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,702
    Mentioned
    101 Post(s)
    Tagged
    4 Thread(s)
    Some simple vector math where you get the hypotenuse (with the vectorDistance function), can result in a solution for this.

    Code javascript:
    function closestLocation(targetLocation, locationData) {
        function vectorDistance(dx, dy) {
            return Math.sqrt(dx * dx + dy * dy);
        }
     
        function locationDistance(location1, location2) {
            var dx = location1.latitude - location2.latitude,
                dy = location1.longitude - location2.longitude;
     
            return vectorDistance(dx, dy);
        }
     
        return locationData.reduce(function(prev, curr) {
            var prevDistance = locationDistance(targetLocation , prev),
                currDistance = locationDistance(targetLocation , curr);
            return (prevDistance < currDistance) ? prev : curr;
        });
    }
     
    var data = {
        "Locations": {
            "Location": [
                {
                "id": "3066",
                "latitude": "57.6494",
                "longitude": "-3.5606",
                "name": "Kinloss"},
            {
                "id": "3080",
                "latitude": "57.077",
                "longitude": "-2.836",
                "name": "Aboyne"},
            {
                "id": "3091",
                "latitude": "57.206",
                "longitude": "-2.202",
                "name": "Aberdeen Dyce"},
            {
                "id": "3134",
                "latitude": "55.907",
                "longitude": "-4.533",
                "name": "Glasgow/Bishopton"},
            {
                "id": "3136",
                "latitude": "55.515",
                "longitude": "-4.585",
                "name": "Prestwick Rnas"},
            {
                "id": "3144",
                "latitude": "56.326",
                "longitude": "-3.729",
                "name": "Strathallan"}
            ]
        }
    },
        targetLocation = {
            latitude: 56,
            longitude: -5
        },
        closest = closestLocation(targetLocation, data.Locations.Location);
    // closest is now the location that is closest to the target location

    If you need to support older web browsers that don't know how to do the Array.reduce() method, you can get polyfills from http://www.calormen.com/polyfill/polyfill.js or you can just use the part of that code directly related to providing the reduce functionality itself:

    Code javascript:
    // ES5 15.4.4.21 Array.prototype.reduce ( callbackfn [ , initialValue ] )
    // From https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/Reduce
    if (!Array.prototype.reduce) {
      Array.prototype.reduce = function (fun /*, initialValue */) {
        "use strict";
     
        if (this === void 0 || this === null) { throw new TypeError(); }
     
        var t = Object(this);
        var len = t.length >>> 0;
        if (typeof fun !== "function") { throw new TypeError(); }
     
        // no value to return if no initial value and an empty array
        if (len === 0 && arguments.length === 1) { throw new TypeError(); }
     
        var k = 0;
        var accumulator;
        if (arguments.length >= 2) {
          accumulator = arguments[1];
        } else {
          do {
            if (k in t) {
              accumulator = t[k++];
              break;
            }
     
            // if array contains no values, no initial value to return
            if (++k >= len) { throw new TypeError(); }
          }
          while (true);
        }
     
        while (k < len) {
          if (k in t) {
            accumulator = fun.call(undefined, accumulator, t[k], k, t);
          }
          k++;
        }
     
        return accumulator;
      };
    }
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  3. #3
    SitePoint Addict jamus's Avatar
    Join Date
    Jul 2004
    Location
    Devon, UK
    Posts
    301
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Wow. Thanks Paul. I wasn't expecting such a finished solution!

    I hate to be a pain but could you please explain how the reduce function works? I've just been through some definitions where it is used to total up the contents of an array but I can't see how it working here. Is the distance from one location being compared the the next and the location with the shortest distance is kept and this repeated until one remains?

    I hope it's ok to ask - I'd like to understand the code if possible!

  4. #4
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,702
    Mentioned
    101 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by jamus View Post
    I hate to be a pain but could you please explain how the reduce function works?
    Sure thing - the reduce function compares the first item with either an initial value or the second value, and whatever the custom function returns is then compared with the next item in the array, until there's only one item left.

    Details on how it works are found at https://developer.mozilla.org/en/Jav...s/Array/Reduce

    The reduce method is a built-in feature of JavaScript now - it's just older web browsers that you might need to provide additional support, by adding the reduce method if the browser doesn't know how to do it.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  5. #5
    SitePoint Addict jamus's Avatar
    Join Date
    Jul 2004
    Location
    Devon, UK
    Posts
    301
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ahhhhhh - *penny drops* it's a way of doing the same function on two things at once and returning one. I think I can follow the process (function by function) now.

    Thank you so much. I'll give it a whirl and see how I get on!

  6. #6
    SitePoint Addict jamus's Avatar
    Join Date
    Jul 2004
    Location
    Devon, UK
    Posts
    301
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    You code works perfectly. I have created a page that performs the calculation using lon and lat obtained from the browser using geolocation.

    However I have strange issue. The first time the page is loaded and the locations data is loaded directly from the json source the find closest function works perfectly. Giving me the nearest location from the list to where I am.

    But from there after when the data is pulled from local storage object the result is incorrect.

    At first I thought it was because there must be a limit to how much data can be stored in a single localstorage item. When I use Chromes inspector to see whats been stored - sure enough the last viewable record matches that returned as closest.

    However, if I pass the lon and lat as fixed values ( not from geolocation) but still use the locations data from local storage the closest returned location is correct again.

    I am baffled.

    Is there a limit on local storage items?

  7. #7
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,702
    Mentioned
    101 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by jamus View Post
    Is there a limit on local storage items?
    This sounds like a situation where investigating a test web page that demonstrates the problem, would be required.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •