Rich
Rich

Reputation: 139

How to calculate distance of a given point to the nearest road?

Objective:

Calculate distance of a given point to the nearest road. I need to determine if a point falls on a road or not. To do this, I would like to see the distance from the point to the nearest road. A distance of 0 would therefore mean that the point is in fact on a road.

Additional info:

I am not constrained to Google Maps. If Bing maps or another mapping service provides this functionality, then that service is too a consideration.

Question:

Do you know of a way to calculate the distance to the nearest road from a given point, with Google Maps API v3? If Not, do you know of another approach or service with which I can achieve my objective?

Upvotes: 1

Views: 3651

Answers (4)

user3116232
user3116232

Reputation: 473

Another possibility is to make use of Google's Road Snap API: https://developers.google.com/maps/documentation/roads/snap

It's quite simple, really. You provide your location point and it returns the closest road point, e.g. like this: https://roads.googleapis.com/v1/snapToRoads?key=your_api_key&path=60.170880,24.942795

And it returns this:

{
  "snappedPoints": [
    {
      "location": {
        "latitude": 60.170877918672588,
        "longitude": 24.942699821922421
      },
      "originalIndex": 0,
      "placeId": "ChIJNX9BrM0LkkYRIM-cQg265e8"
    }
  ]
}

Upvotes: 2

Rich
Rich

Reputation: 139

Problem solved. Here is the demo: http://jsfiddle.net/63sWv/

var marker = null;
var roadMarker = null;
var geocoder = new google.maps.Geocoder();
var map;
var directionsService = new google.maps.DirectionsService();

$(document).ready(function(){

    var latlng = new google.maps.LatLng(54.97430382488778, -1.611546277999878);

    var mapOptions = {
        zoom: 22,
        center: latlng
    };

    map = new google.maps.Map(document.getElementById('map'), mapOptions);

    google.maps.event.addListener(map, 'click', function(event) {       
        placeMarkers(event);
    });

});

function placeMarkers(e) {

    var markerImage = "https://chart.googleapis.com/chart?chst=d_map_pin_icon&chld=home|FFFF00";    
    var roadMarkerImage ="https://chart.googleapis.com/chart?chst=d_map_xpin_letter&chld=pin||FF0000|000000";

    if(marker !== null){
        marker.setMap(null);
    }

    marker = new google.maps.Marker({
        position: e.latLng,
        map: map,
        icon: markerImage
    });

    var request = {
        origin:e.latLng, 
        destination:e.latLng,
        travelMode: google.maps.DirectionsTravelMode.DRIVING
      };

    directionsService.route(request, function(response, status) {

        if (status == google.maps.DirectionsStatus.OK) {        

            if(roadMarker !== null){
                roadMarker.setMap(null);    
            }

            roadMarker = new google.maps.Marker({
                position: response.routes[0].legs[0].start_location,
                map: map,
                icon: roadMarkerImage
            });         

            var distance = getDistance(request.origin,            
                            response.routes[0].legs[0].steps[0].end_point);

            $(".output .distance").html(distance);

        }
    });  
}

var rad = function(x) {
  return x * Math.PI / 180;
};

var getDistance = function(p1, p2) {

    //trim up to 5 decimal places because it is at that point where the difference
    p1.lat = parseFloat(p1.lat().toFixed(5));
    p1.lng = parseFloat(p1.lng().toFixed(5));

    p2.lat = parseFloat(p2.lat().toFixed(5));
    p2.lng = parseFloat(p2.lng().toFixed(5));

    var R = 6378137; // Earth’s mean radius in meter
    var dLat = rad(p2.lat - p1.lat);
    var dLong = rad(p2.lng - p1.lng);

    var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(rad(p1.lat)) * Math.cos(rad(p2.lat)) * Math.sin(dLong / 2) * Math.sin(dLong / 2);

    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    var d = R * c;

    return d; // returns the distance in meter

};

Upvotes: 8

Cybercartel
Cybercartel

Reputation: 12592

If it's a straight line you can pick the midpoint of the line and get it's euclidian distance to the point:http://en.m.wikipedia.org/wiki/Euclidean_distance.

Upvotes: 0

elrobis
elrobis

Reputation: 1562

I need to determine if a point falls on a road or not.


I don't believe there is a legitimate approach to do what you want with those APIs. However, you could probably do some hackerish, unrecommended trick using Google's Street View Image API, or with finer-grained control using the Google Maps API's StreetView objects.

Here's the thinking: If the Street View API returns a valid result (i.e. a panoramic street view image), then you might be in a street. If it doesn't, then you're not in a street.

There may be a challenge in identifying "true" responses with the stand-alone Street View Image API. One option would be to compare the image file size for a failed response, like this one, which has a Content-Length of 4868, to the image file size returned for a test point, such as a valid one like this, which has a Content-Length of 38701 ..any "true" response will result in an image with a significantly higher Content-Length value, because there is actual data in the image. Frankly, as long as you request same-size images (i.e. width and height), the Content-Length for a failure should always be the same. However the API could change even slightly and break this expectation. So it's risky.

The higher-grained control provided by the actual Google Maps JavaScript API's Street View objects will give you cleaner handling, as the google.maps.StreetViewStatus will return one of three values and resolve this condition: OK, UNKNOWN_ERROR, and ZERO_RESULTS, removing the fragile guess work necessary if you used the static image API, described previously.

But this approach is dirty. First, I think street view returns more than just near-street imagery. I'm thinking of trails in parks, user-uploaded panoramas, etc. Second, the Street View API seems to use sloshy "snapping" tolerances (like, 50 meters). In other words, if your point is close-enough, it'll give you the benefit of the doubt and return a nearby street view panorama anyway. In other words, beware false positives.

If you're considering pursuing this approach, here's an example of an endpoint that works:

http://maps.googleapis.com/maps/api/streetview?size=600x300&location=34.055526,-81.008087

And here's one that fails..

http://maps.googleapis.com/maps/api/streetview?size=600x300&location=34.05637,-81.00583

As an afterword, I don't recommend actually using this approach if the requirements of your app were defined by cast-iron business needs. But if it's just for fun and games, perhaps it's close enough?

If you want to get serious about this, you'd really need to get into PostGRESql and its PostGIS extension, and you'd need a super-detailed set of street centerlines. Open Street Map data could get you started, but be warned the quality of that data is variable ..amazing in some places ..less than in others.

Upvotes: 0

Related Questions