Discover the nearest locations along your route using Google Maps API V3's boundary feature

I am trying to find locations that fall within a specific boundary along a route. I need the results to be ordered by distance from the route.

I attempted to use rankby=distance in my Nearby Search request, but it didn't work because it requires a location and radius, whereas I only have a LatLngBounds object and a route. Is it possible to achieve this?

Below is the script I am using to retrieve places along a route within a boundary.

http://jsfiddle.net/FMpBU/

The script does return results, but they are in a random order...

Note: My objective is to use bounds instead of location.

Answer №1

In order to sort the results by distance from the route, it is essential to have basic functions for calculating distance. The challenge arises from the fact that the Earth is a sphere, making distances on a sphere more complex than those on a plane.

For shorter distances, such as from Tamiami to Miami, we can simplify by treating the distances as if they were on a plane to obtain reasonable results. To achieve this approximation, we need a method to determine the minimum distance between a point and a line segment. Rather than starting from scratch, I modified existing code from a Stack Overflow answer.

    function sqr(x) { return x * x }
    function dist2(v, w) { return sqr(v.x - w.x) + sqr(v.y - w.y) }
    function distToSegment2(p, v, w) {
      return dist2(getClosestPoint(p,v,w));
    }
    function getClosestPoint( p, v, w ) {
      var l2 = dist2(v, w);
      if (l2 === 0) return v;
      var t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;
      if (t < 0) return v;
      if (t > 1) return w;
      return { x: v.x + t * (w.x - v.x), y: v.y + t * (w.y - v.y) };
    }
    function distToSegment(p, v, w) { return Math.sqrt(distToSegmentSquared(p, v, w)); }

Since we are currently only using distance for sorting purposes, we can save computation by utilizing the dist2 (squared distance) function instead of taking the square root.

The Google route query results contain an array named overview_path, which includes all the line segments used to create the route on the map. We can leverage these segments to find the closest point:

    function closestPointOnPath_Cartesian( place, path, cb ) {
        var min = Number.MAX_VALUE;
        var closestPoint = null;
        for( var i=0; i<path.length-1; i++ ) {
            var v = { x: path[i].lng(), y: path[i].lat() };
            var w = { x: path[i+1].lng(), y: path[i+1].lat() };
            var p1 = { x: place.geometry.location.lng(), 
                        y: place.geometry.location.lat() };
            var p2 = getClosestPoint( p1, v, w );
            var d2 = dist2( p1, p2 );
            if( d2 < min ) {
                min = d2;
                closestPoint = new google.maps.LatLng( p2.y, p2.x );
            }
        }
        cb( closestPoint, min );
    }

It's important to note that the function I named indicates it calculates Cartesian distance, which may introduce inaccuracies for long routes or routes near the poles.

With this function in place, we can annotate each result with its distance for sorting, and then proceed with the sorting:

for( var i=0; i<results.length; i++ ) {
        closestPointOnPath_Cartesian( results[i], 
            result.routes[0].overview_path, 
            function( closestPoint, coordDist2 ){
                results[i].closestPointOnPath = closestPoint;
                results[i].coordDist2 = coordDist2;
                results[i].geoDistKm = geoDistanceKm( results[i].geometry.location, closestPoint );
        });
    }
    results.sort( function(a,b) { return a.coordDist2 - b.coordDist2; } );

For visualization during debugging, I included code to draw a line from the closest point on the path to each location:

       var distLine = new google.maps.Polyline({
               path: [place.closestPointOnPath, place.geometry.location],
               strokeColor: '#ff0000',
               strokeOpacity: 1.0,
               strokeWeight: 2
           });
           distLine.setMap( map );

As an additional feature, I implemented the haversine formula for calculating geographic distance, allowing for the display of accurate geographic distance in the results list:

    Number.prototype.toRad = function() {
       return this * Math.PI / 180;
    }
    function geoDistanceKm(p1,p2) {
        var R = 6371; // km 
        var x1 = p2.lat()-p1.lat();
        var dLat = x1.toRad();  
        var x2 = p2.lng()-p1.lng();
        var dLon = x2.toRad();  
        var a = Math.sin(dLat/2) * Math.sin(dLat/2) + 
            Math.cos(p1.lat().toRad()) * Math.cos(p2.lat().toRad()) * 
            Math.sin(dLon/2) * Math.sin(dLon/2);  
        var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
        return R * c;
    }

It is worth noting that due to the calculation of distances on a plane, the order of results may not align with the true geographic distance computed using the haversine formula, particularly for long routes or those near the poles. Addressing this discrepancy would involve developing an algorithm for determining point/segment distance on a sphere, a task for future exploration.

For a demonstration of the complete solution in action, you can visit: http://jsfiddle.net/UqLTE/4/

Answer №2

When utilizing the Place Search Request method, consider using the bounds parameter instead of location and radius.

Alternatively, you can leverage the Google Maps API method by setting the center of your box as the circle's center and half of the diagonal of the box as its radius. This approach will provide you with all places within the circle that encloses the box. You can then filter out any places inside the circle but outside of the box.

By adding rankby=distance to the method call, the results can be sorted based on distance from the specified location (e.g. center of the box). To sort by distance from the route, you would need to calculate the distance from the route. One way to do this is by computing a perpendicular to each line segment in the route using vector projection. The distance to the closest line segment represents the shortest distance to the route.

The closest method in Google Maps API V3 is the DistanceMatrixService, which determines the distance between multiple source and destination locations. While there are other methods that may assist with various parts of the calculation, there doesn't appear to be one specifically for finding the distance between a route/polyline and a place/point.

Answer №3

If you're looking to organize a list of places based on their distances from a starting point, one approach is to assign each distance as an attribute to the list items and then sort the list according to these values.

Here's a sample illustrating how you can sort a list based on attribute values:

HTML

<ul id="place-list">
<li data-id="4">Tokyo 4</li>
<li data-id="0">Paris 0</li>
<li data-id="5">Frankfurt 5</li>
<li data-id="2">London 2</li>
<li data-id="1">Greece 1</li>
<li data-id="3">Munich 3</li>
</ul>
<button id="asc">ASC</button>
<button id="desc">DESC</button>

jQuery

var sortArray = function (items, inverse) {
    var inverse = inverse || false;

    var sortedArray = items.map(function () {
        return {
            id: $(this).data("id"),
            element: $(this)[0].outerHTML
        };
    });

    var appendTo = items.parent();
    items.remove();

    sortedArray.sort(function (a, b) {
        return a.id > b.id ? (inverse ? -1 : 1) : (inverse ? 1 : -1);
    });

    sortedArray.each(function () {
        $(appendTo).append(this.element);
    });
}

$("#asc").click(function () {
    sortArray($("#place-list").find("li"));
});

$("#desc").click(function () {
    sortArray($("#place-list").find("li"), true);
});

EXAMPLE: http://jsfiddle.net/995dY/

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Filter out unnecessary attributes while utilizing an anonymous type in ClearScript

In my development project, I am creating a .NET wrapper for the popular java-script library called Linkify.js, utilizing Microsoft's ClearScript. The challenge I am facing involves implementing the attributes property within the options object parame ...

What is the best way to fix character encoding issues with native JSON in Internet Explorer 8

When working with JSON containing Unicode text, I encountered an issue with the IE8 native json implementation. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script> var stringified = JSON.stringify("สวัส ...

preserving the current state even after refreshing the page

Currently, I am teaching myself React. When I click on the favorites button, which is represented by a heart symbol, it changes color. However, upon refreshing the page, the color change disappears. To address this issue, I came across the following helpfu ...

I'm experiencing a lack of feedback while implementing jQuery/AJAX with JSONP

Attempting to perform a cross-domain request using jQuery/AJAX, I have the code below; $.ajax({ url: "http://www.cjihrig.com/development/jsonp/jsonp.php?callback=jsonpCallback&message=Hello", crossDomain:true }) .done(function( msg ) { alert( ...

Sorting an array of Material-UI's <TableRow> alphabetically using ReactJS and Material-UI. How to do it!

I am currently utilizing Material-UI's <Table> and <TableRow> components by rendering an array of <TableRow>s using the .map() method. Each <TableRow> contains a <TableRowColumn> representing a first name, for example: &l ...

Developing a transparent "cutout" within a colored container using CSS in React Native (Layout design for a QR code scanner)

I'm currently utilizing react-native-camera for QR scanning, which is functioning properly. However, I want to implement a white screen with opacity above the camera, with a blank square in the middle to indicate where the user should scan the QR code ...

Tips for sending a tab id to a URL using jQuery

Upon examining the code snippet below, it is apparent that I am attempting to pass the value of a tab's id to a URL. In this instance, I am displaying it in HTML just for illustrative purposes; however, the hashtag id fails to be transferred to the UR ...

Is there a permanent solution to fixing the error code -4094 that is repeatedly occurring during React Native startup?

When attempting to execute react-native start, an error occurred which has not been encountered before. The error message is as follows: ERROR ENCOUNTERED Loading dependency graph...events.js:287 throw er; // Unhandled 'error' event ...

What is the best way to retrieve the current URL with a hashtag symbol using JavaScript?

I am attempting to display the current URL after the question mark with a hash symbol using PHP, but unfortunately, it is not achievable. Therefore, I need to utilize JavaScript for this task, even though I have limited knowledge of it. This is the specifi ...

Displaying time text in input element due to browser bug

I am faced with a perplexing puzzle that has left me scratching my head. Here are two seemingly identical pieces of javascript code, but one behaves unexpectedly (take note of the Console.Log): Updates the UI once, then abruptly stops updating: http://js ...

What is the process for creating a server-side API call?

I've designed a front-end application that uses an API to retrieve data. The problem I'm facing is that in order to access the API, I need to use an API Key. If I include the API key in the client-side code, it will be visible to users. How can I ...

Whenever I try to include something within the `componentWillUnmount` function,

I'm currently learning React on my own. I've been trying to save session data in my componentWillUnmount method. However, when I add code inside componentWillUnmount, nothing seems to happen. I tried debugging by adding console logs and debugger ...

Error in Redux-tookit: The store is missing a valid reducer. Ensure that the argument provided to combineReducers is an object containing reducers as values

Uh oh! Looks like there's an error with the Store reducer. The argument passed to combineReducers needs to be an object with valid reducers. I'm having trouble setting up a Store for my app and I can't figure out where I went wrong. Could s ...

Alignment of Material-UI dialogue buttons on the left side

I have a Dialog containing three buttons as shown below: https://i.stack.imgur.com/T6o35.png Here is the JSX code: <DialogActions classes={{ root: this.props.classes.dialogActionsRoot }} > <Button classes={{ root: this.props ...

Determine whether the element is visible in at least half of the viewport

I am working on a project that involves 4 cards with images. I want the image to animate in from the left when it comes into viewport. I have finalized the CSS for this effect, but the JavaScript code always returns false even when the element is visible o ...

Define the input field as a specific type and disable the Material-UI text formatting

I am using a Texfield component from Material UI, but I have noticed that every time I type, the input stops and doesn't continue to the next letter. I have to click on it again in order to continue typing. When I remove the onChange method, the data ...

Learn how to extract substrings from a variable within an API using Vue.js

Exploring VueJs for the first time and looking to split a string by comma (,) retrieved from an API into separate variables. The data is coming from a JSON server. "featured-property": [ { "id": "1", " ...

Error message: "AngularJS encountered a $injector:modulerr error in Internet Explorer 11."

I've successfully created an AngularJS App that functions properly on most browsers like Firefox, Opera, Safari, Edge, and Chrome. However, there seems to be a compatibility issue with IE 11. When attempting to open the app in IE 11, the following er ...

Trouble with integrating HTML5 canvas from an external JavaScript file

Having trouble with storing canvas js in an external file. If the javascript responsible for drawing on the canvas is included in the html header, then the rectangle is displayed correctly. Here is the working html (javascript in html header): <!DOCT ...

changing size when hovered over with the mouse is not consistent between entering and exiting

Hi there, I'm currently utilizing the transform: scale(); property on a website and could use some assistance with a particular issue I haven't been able to find an answer for online. Here is the code snippet I'm working with: HTML: <d ...