简体   繁体   中英

Request entity too large - google map directions error 413

First, I want to say I'm not too experienced with Google Maps Javascript API v3 (my only experience are these last 3 days). I have managed to display a map with a list of markers and connect them using DirectionsService , but sometimes my list is pretty large and I get the following error:

Failed to load resource: the server responded with a status of 413 (Request Entity Too Large)

This is my code:

// List of all locations for device 
var locations = [];
// example: locations = [
//     {datetime: '2014/09/28 20:20', location: '41.99999 21.99999 30.0'},
//     ... {...} ... may be more than 200 entries for locations
//     {datetime: '2014/09/28 20:25', location: '41.99999 21.99999 30.0'}
// ]

var map;
var markers = [];
var bounds = new google.maps.LatLngBounds();
var pathPoints = [];
var infoWindow = new google.maps.InfoWindow();
var accuracy = new google.maps.Circle({
    fillColor: '#ff4080',
    fillOpacity: 0.5,
    strokeOpacity: 0,
    zIndex: 0
});
var path = new google.maps.Polyline(polyOptions);
var geocoder;
var directionsService = new google.maps.DirectionsService();
var directionsDisplay;
var polyOptions = {
    geodesic: true,
    strokeColor: '#28b8b8',
    strokeOpacity: 1.0,
    strokeWeight: 8,
    zIndex: 1
}
function showInfoWindow(marker, datetime, acc){
    geocoder = new google.maps.Geocoder();
    geocoder.geocode({
        'latLng': marker.getPosition()
    }, function(results, status) {
        if (status == google.maps.GeocoderStatus.OK) {
            if (results[1]) {
                infoWindow.close();
                var date = datetime.split(" ")[0];
                var time = datetime.split(" ")[1];
                var content = '<div class="infowindow">'
                        + results[1].formatted_address.trim() + '<br />'
                        + 'Date: ' + date + '<br />'
                        + 'Time: ' + time + '<br />'
                        + 'Accuracy: ' + acc + 'm'
                        + '</div>';
                infoWindow.setContent(content);
                infoWindow.open(map, marker);
                accuracy.setMap(null);
                accuracy.setMap(map);
                accuracy.setCenter(marker.getPosition());
                accuracy.setRadius(acc/1.6);
            } else {
                // alert('No results found');
            }
        } else {
            alert('Geocoder failed due to: ' + status);
        }
    });
}
function addMultiMarker(latLng, num, datetime, acc){
    // Create marker at provided location
    var marker = new google.maps.Marker({
        position: latLng,
        map: map,
        icon: image_circle,
        title: 'Location #' + num,
        zIndex: num + 1
    });
    // On marker click center it inside map and show infoWindow
    google.maps.event.addListener(marker, 'click', function() {
        map.panTo(marker.getPosition());
        showInfoWindow(marker, datetime, acc);
    });
    return marker;
}
function showRoute() {
    var rendererOptions = {
        draggable: false,
        hideRouteList: true,
        suppressMarkers: true,
        infoWindow: infoWindow,
        polylineOptions: polyOptions,
        map: map
    };
    directionsDisplay = new google.maps.DirectionsRenderer(rendererOptions);
    var len = markers.length;
    var start = markers[0].getPosition();
    var end = markers[len - 1].getPosition();
    new google.maps.event.trigger(markers[len - 1], 'click');
    var wayPts = [];
    for(var i = 1; i < len - 1; i++){
        wayPts.push({
            location: markers[i].getPosition(),
            stopover: true
        });
    }
    var request = {
        origin: start,
        destination: end,
        waypoints: wayPts,
        optimizeWaypoints: true,
        travelMode: google.maps.TravelMode.DRIVING
    };
    directionsService.route(request, function(response, status) {
        if (status == google.maps.DirectionsStatus.OK) {
            directionsDisplay.setDirections(response);
        }
    });
}
function showMapPeriod(periodStart, periodEnd){
    // Simple map options
    var mapOptions = {
        zoom: 15,
        center: new google.maps.LatLng(41, 21),
        mapTypeId : google.maps.MapTypeId.ROADMAP,
        mapTypeControl: true,
        mapTypeControlOptions: {
            style: google.maps.MapTypeControlStyle.HORIZONTAL_BAR,
            position: google.maps.ControlPosition.TOP_RIGHT
        },
        zoomControl: true,
        zoomControlOptions: {
            style: google.maps.ZoomControlStyle.LARGE,
            position: google.maps.ControlPosition.LEFT_CENTER
        },
        panControl: false,
        streetViewControl: false
    };
    $("#map").html("");
    // Create and show map
    openMapContainer(); // just a function that shows the map <div> element
    map = new google.maps.Map(document.getElementById('map'), mapOptions);
    // Create and display markers
    bounds = new google.maps.LatLngBounds();
    markers = [];
    var len = 0;
    for(var i = periodStart; i <= periodEnd; i++, len++){
        var loc_vals = locations[i].location.trim().split(" ");
        var lat = parseFloat(loc_vals[0]);
        var lng = parseFloat(loc_vals[1]);
        var acc = parseFloat(loc_vals[2]);
        // Create marker at provided location
        var datetime = locations[i].datetime;
        var latLng = new google.maps.LatLng(lat, lng);
        markers[len] = addMultiMarker(latLng, len+1, datetime, acc);
        bounds.extend(latLng);
    }
    showRoute();
    map.fitBounds(bounds);
}

Well my code works partially and if someone can help me to eliminate the problem I would very much appreciate it. To explain it better, I need some kind of solution to request Directions with a lot (200+) waypoints or some way to connect the locations through the roads (I don't really need the directions, but I don't want to connect the locations with a simple Polyline ).

EDIT: Here I have provided a simple demo, to see the problem just uncomment at line 15 .

Since nobody was able to help me with my problem, I had to solve it myself. I'm answering my own question because this seems to be quite a problem for a lot of people and it's not well enough explained on the web, so this may help developers that have similar problems.

Anyway, let's get to the point. The error was thrown because of Google limitations as OxyDesign suggested, the error was:

  • google.maps.DirectionsResult.MAX_WAYPOINTS_EXCEEDED - This is because I sent a request with origin , destination and more than 8 (Google limit) waypoints .

that I easily solved (I just split the array in chunks of 10 points with the last point of one request as the first point of the next), but then I got another error:

  • google.maps.DirectionsResult.OVER_QUERY_LIMIT - This is because I tried to send more than 10 (again, Google limit) requests per second.

that took a little more experimenting, searching and testing to solve, but I got it solved.

Now, here is the code that works for me (at least at the time of posting, until Google changes something):

// list of randomly generated locations (max 288)
var locations = [];
for(var i=0; i<288; i++){
    locations[i] = {
        datetime: '2014/10/01 12:10',
        location: (51 + Math.random()) +
        ' ' + (-0.5 + Math.random()) + ' 30.0'
    };
}
// Marker images
var image_pin = new google.maps.MarkerImage(
    'http://stedkomerc.com.mk/gpslocator/images/mPin.svg',  
    new google.maps.Size(25, 41),  // size
    new google.maps.Point(0, 0),   // origin, top-left corner
    new google.maps.Point(12, 40)  // anchor
);
var image_circle = new google.maps.MarkerImage(
    'http://stedkomerc.com.mk/gpslocator/images/mCircle.svg',
    new google.maps.Size(19, 19),  // size
    new google.maps.Point(0, 0),   // origin, top-left corner
    new google.maps.Point(9, 9)    // anchor
);
// Variables
var map;
var bounds = new google.maps.LatLngBounds();
var markers = [];
var pathPoints = [];
var geocoder;
var infoWindow = new google.maps.InfoWindow();
var accuracy = new google.maps.Circle({
    fillColor: '#ff4080',
    fillOpacity: 0.4,
    strokeOpacity: 0,
    zIndex: 0
});
var polyOptions = {
    geodesic: true,
    strokeColor: '#28b8b8',
    strokeOpacity: 1.0,
    strokeWeight: 8,
    zIndex: 1
};
var path = new google.maps.Polyline(polyOptions);
var directionsService = new google.maps.DirectionsService();
var directions = [];
var rendererOptions = {
    draggable: false,
    hideRouteList: true,
    suppressMarkers: true,
    preserveViewport: true,
    infoWindow: infoWindow,
    polylineOptions: polyOptions
};
var requests = [];
var MAX_POINTS_PER_REQUEST = 10; // 8*waypts Google limit + start + end
var MAX_REQUESTS_PER_SECOND = 10; // Google limit
// functions
function showInfoWindow(marker, datetime, acc){
    geocoder = new google.maps.Geocoder();
    geocoder.geocode({
        'latLng': marker.getPosition()
    }, function(results, status) {
        if (status == google.maps.GeocoderStatus.OK) {
            if (results[1]) {
                infoWindow.close();
                var date = datetime.split(" ")[0];
                var time = datetime.split(" ")[1];
                var content = '<div class="infowindow">' +
                    results[1].formatted_address.trim() + '<br />' +
                    'Date: ' + date + '<br />' +
                    'Time: ' + time + '<br />' +
                    'Accuracy: ' + acc + 'm' +
                    '</div>';
                infoWindow.setContent(content);
                infoWindow.open(map, marker);
                accuracy.setMap(null);
                accuracy.setMap(map);
                accuracy.setCenter(marker.getPosition());
                accuracy.setRadius(acc);
            }
        } else {
            console.log('Geocoder failed due to: ' + status);
        }
    });
}
function addMultiMarker(latLng, num, datetime, acc){
    // Create marker at provided location
    var marker = new google.maps.Marker({
        position: latLng,
        map: map,
        icon: image_circle,
        title: 'Location #' + num,
        zIndex: num + 1
    });
    // On marker click center it inside map and show infoWindow
    google.maps.event.addListener(marker, 'click', function() {
        //map.panTo(marker.getPosition());
        showInfoWindow(marker, datetime, acc);
    });
    return marker;
}
function connectMarkersPolyline(mrkrs){
    path.setMap(map);
    pathPoints = path.getPath();
    len = mrkrs.length;
    for(var i = 0; i < len; i++){
        pathPoints.push(mrkrs[i].getPosition());
    }
}
function connectMarkersRoute(mrkrs, part, maxParts){
    var directionsDisplay = new google.maps.DirectionsRenderer(rendererOptions);
    directionsDisplay.setMap(map);
    var len = mrkrs.length;
    var start = mrkrs[0].getPosition();
    var end = mrkrs[len - 1].getPosition();
    var wayPts = [];
    for(var i = 1; i < len - 1; i++){
        wayPts.push({
            location: mrkrs[i].getPosition(),
            stopover: true
        });
    }
    var request = {
        origin: start,
        destination: end,
        waypoints: wayPts,
        optimizeWaypoints: false,
        travelMode: google.maps.TravelMode.DRIVING,
        unitSystem: google.maps.UnitSystem.METRIC
    };
    directionsService.route(request, function(response, status) {
        if (status == google.maps.DirectionsStatus.OK) {
            // request status OK, display route
            directionsDisplay.setDirections(response);
            // save it in array in case we want to remove it later
            directions.push(directionsDisplay);
            // if not last chunk, send next chunk after 100ms
            // 1 request per 100ms => 10 requests per 1s
            if(part+1 < maxParts)
                setTimeout(connectMarkersRoute(requests[part+1], part+1, maxParts), 100);
            else showLastMarker();
        } else if (status == google.maps.DirectionsStatus.OVER_QUERY_LIMIT) {
            // if we get error, send same request after bigger delay (120ms)
            setTimeout(connectMarkersRoute(requests[part], part, maxParts), 120);
        } else {
            // if all fails, connect with simple polyline
            console.log('Directions failed due to: ' + status);
            connectMarkersPolyline(mrkrs);
        }
    });
}
function connectMarkers(markers){
    path.setMap(null);
    path.setPath([]);
    directions = [];
    requests = [];
    var len = markers.length;
    console.log('connecting ' + len + ' markers');
    var i, j;
    // split markers array into chunks of 10 (start + waypts + end)
    for(i=0; i<len; i+=MAX_POINTS_PER_REQUEST-1){
        if(i<len-1)
            requests.push(markers.slice(i, i+MAX_POINTS_PER_REQUEST));
    }
    // send first chunk to connectMarkersRoute()
    connectMarkersRoute(requests[0], 0, requests.length);
}
function showMapPeriod(periodStart, periodEnd){
    // Map options
    var mapOptions = {
        zoom: 16,
        center: new google.maps.LatLng(41.995922, 21.431465),
        mapTypeId : google.maps.MapTypeId.ROADMAP,
        mapTypeControl: true,
        mapTypeControlOptions: {
            style: google.maps.MapTypeControlStyle.HORIZONTAL_BAR,
            position: google.maps.ControlPosition.TOP_RIGHT
        },
        zoomControl: true,
        zoomControlOptions: {
            style: google.maps.ZoomControlStyle.LARGE,
            position: google.maps.ControlPosition.LEFT_CENTER
        },
        panControl: false,
        streetViewControl: false
    };
    $("#map").html("");
    //openMapContainer();
    // Create and show map
    map = new google.maps.Map(document.getElementById('map'), mapOptions);
    // Create and display markers
    bounds = new google.maps.LatLngBounds();
    markers = [];
    var len = 0;
    for(var i = periodStart; i <= periodEnd; i++, len++){
        var loc_vals = locations[i].location.trim().split(" ");
        var lat = parseFloat(loc_vals[0]);
        var lng = parseFloat(loc_vals[1]);
        var acc = parseFloat(loc_vals[2]);
        // Create marker at provided location
        var datetime = locations[i].datetime;
        var latLng = new google.maps.LatLng(lat, lng);
        markers[len] = addMultiMarker(latLng, len+1, datetime, acc);
        bounds.extend(latLng);
    }
    connectMarkers(markers);
    map.fitBounds(bounds);
    if(map.getZoom() > 16) map.setZoom(16);  
}
function showLastMarker(){
    new google.maps.event.trigger(markers[markers.length - 1], 'click');  
}
// show map
showMapPeriod(1, 280);
// --------

As you can see, the only real change I made was the showRoute() function became connectMarkers() combined with connectMarkersRoute() and connectMarkersPolyline() that handle both errors.

Working example can be seen here . It takes a little time to load all route chunks, but at least it works. Hope this helps somebody else too.

Note that there is another Google limit of 2500 Directions requests per day, so be careful. That being said, this answer works even for a lot larger requests than 200 locations, I have tested with 1250 locations and it worked.

这项服务的Gmaps Waypoints似乎有一个局限性(我尝试过823 ,直到39时才起作用,然后40才失效),除了折线以外,没有其他方法可以实现,但您不希望这样做我认为没有可行的解决方案...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM