Implementuję funkcjonalność systemu informatycznego, w którym potrzebuję obliczyć serię długości tras między dwoma punktami.

Działa to w ten sposób, że w programie jest osadzony komponent WebBrowser, który korzysta z przeglądarki Internet Explorer. Wewnątrz niego jest osadzany zadany kod HTML, a potem wywoływane funkcje JavaScript.

Są takie funkcje w kodzie JavaScript, które wywołują zdarzenia poza kontrolką WebBrowser.
window.external.GamEvent("START"); - po załadowaniu strony, po nieje system ma wprowadzić serię za pomocą funkcji DistClear i DistArrayAdd, a następnie wywołać funkcję DistStart
window.external.GamEvent("DISTANCE"); - po zakończeniu obliczeń, żeby system wywołał "GetResult" w celu pobrania wyników obliczeń

Kod przejąłem po kimś, udało mi się zrobić obliczenie całej serii za jednym wywołaniem w pętli. Przy obliczaniu do 10 przejazdów nie ma żadnego problemu. Okazuje się, że przy serii dłuższej niż 10 przejazdów, dostaję błąd "OVER_QUERY_LIMIT" po obliczeniu jedenastej lub dwunastej odległości i muszę czekać ok. 1 sekundy na każdą odległość ponad 10. Szukałem w Internecie i praktycznie wszystkie rozwiązania polegają na robieniu opóźnienia lub wymuszania obliczenia w pętli aż do uzyskania poprawnego wyniku.

Rozwiązanie, w którym obliczenie serii jest długotrwałe nie wchodzi w grę. Odległości muszą być obliczone w całej serii, a jak jest błąd uniemożliwiający obliczenie, to przyjmuje się odległość 50000km, żeby takie odległości były klasyfikowane jako najdłuższe.

Kod HTML odpowiadający za obliczenia osadzony w kontrolce WebBrowser, ona jest uruchamiana przy każdej procedurze obliczania serii odległości.

<!DOCTYPE html>
<html>
    <head>
        <title>Simple Map</title>
        <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=11">
        <style>
            html, body, #map-canvas {
                margin: 0;
                padding: 0;
                height: 100%;
            }
        </style>
        <script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=true"></script>
        <script>


            var Log_ = "";

            function GetLog()
            {
                return Log_;
            }

            function Log(XXX)
            {
                Log_ = Log_ + XXX + "\r\n";
            }

            // Tablica danych do obliczen
            var DistArrayId = new Array();
            var DistArrayFrom = new Array();
            var DistArrayTo = new Array();
            var DistArrayDist = new Array();
            var DistArrayN = 0;
            var DistArrayI = 0;

            // Czyszczenie struktur
            function DistClear()
            {
                Log("DistClear();");
                DistArrayN = 0;
                DistArrayI = 0;
            }

            // Dopisanie nowej danej
            function DistArrayAdd(XId, XFrom, XTo)
            {
                Log("DistArrayAdd(" + XId + ", \"" + XFrom + "\", \"" + XTo + "\");");
                DistArrayId[DistArrayN] = XId;
                DistArrayFrom[DistArrayN] = XFrom;
                DistArrayTo[DistArrayN] = XTo;
                DistArrayDist[DistArrayN] = 0;
                DistArrayN = DistArrayN + 1;
            }


            // Rozpoczecie obliczen
            function DistStart()
            {
                Log("Start obliczen " + DistArrayN);
                DistArrayI = 0;
                do_calc(DistArrayFrom[0], DistArrayTo[0], "");
            }

            // Nastepna trasa
            function DistNext()
            {
                DistArrayI = DistArrayI + 1;
                if (DistArrayI < DistArrayN)
                {
                    do_calc(DistArrayFrom[DistArrayI], DistArrayTo[DistArrayI], "");
                }
                else
                {
                    Log("Obliczono wszystkie trasy - DISTANCE");
                    window.external.GamEvent("DISTANCE");

                }
            }

            // Pobranie wynikow obliczen
            function GetResult()
            {
                var I;
                var Res = "";
                for (I = 0; I < DistArrayN; I++)
                {
                    Res = Res + "<DistRaw__recid>";
                    Res = Res + "<i>" + DistArrayId[I] + "</i>";
                    Res = Res + "<f>" + DistArrayFrom[I] + "</f>";
                    Res = Res + "<t>" + DistArrayTo[I] + "</t>";
                    Res = Res + "<d>" + DistArrayDist[I] + "</d>";
                    Res = Res + "</DistRaw__recid>\r\n";
                }
                return Res;
            }


            var directionsDisplay;
            var directionsService = new google.maps.DirectionsService();
            var map;
            var waypts = [];
            var start = "";
            var end = "";
            var waypoint_order = "";

            function initialize()
            {
                var rendererOptions = {
                    draggable: true
                };
                directionsDisplay = new google.maps.DirectionsRenderer(rendererOptions);
                var latlng = new google.maps.LatLng(52.35, 16.8);
                var mapOptions = {
                    zoom: 7,
                    mapTypeId: google.maps.MapTypeId.ROADMAP,
                    center: latlng
                }
                map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions);
                directionsDisplay.setMap(map);

                google.maps.event.addListener(directionsDisplay, "directions_changed", function() {
                    computeTotalDistance(directionsDisplay.directions);
                });
                window.external.GamEvent("START");
            }

            function computeDummyDistance()
            {
                document.getElementById("dist").value = "50000 km";
                DistArrayDist[DistArrayI] = 50000;
                DistNext();
            }

            function computeTotalDistance(result)
            {
                var total = 0;
                var myroute = result.routes[0];
                for (i = 0; i < myroute.legs.length; i++)
                {
                    total += myroute.legs[i].distance.value;
                }
                total = Math.round(total / 1000.);
                document.getElementById("dist").value = total + " km";
                DistArrayDist[DistArrayI] = total;
                DistNext();
            }

            function calcRoute()
            {
                var checkboxWpt = document.getElementById("wptOptimize");

                var request = {
                    origin: start,
                    destination: end,
                    waypoints: waypts,
                    optimizeWaypoints: checkboxWpt.checked,
                    travelMode: google.maps.TravelMode.DRIVING,
                    provideRouteAlternatives: false
                };

                Log("CalcRoute - start");

                directionsService.route(request, function(response, status)
                {
                    if (status == google.maps.DirectionsStatus.OK)
                    {
                        // directionsDisplay.setDirections(response);
                        Log("CalcRoute event OK - start " + DistArrayI);
                        computeTotalDistance(response);
                        var via=response.routes[0].legs[0].start_address;
                        for (var j = 0; j < response.routes.length; j++)
                        {
                            var route = response.routes[j];
                            // For each route, display summary information.
                            for (var i = 0; i < route.legs.length; i++)
                            {
                                via += "; "+route.legs[i].end_address;
                            }
                        }
                        document.getElementById("via").value = via;
                        waypoint_order = response.routes[0].waypoint_order;
                        Log("CalcRoute event OK - stop " + DistArrayI);
                    }
                    else
                    {
                        if (status == google.maps.GeocoderStatus.OVER_QUERY_LIMIT)
                        {
                            Log("CalcRoute event OVER_QUERY_LIMIT - start " + DistArrayI);
                            setTimeout(function()
                            {
                                calcRoute();
                            }, 100);
                            Log("CalcRoute event OVER_QUERY_LIMIT - stop " + DistArrayI);
                        }
                        else
                        {
                            Log("CalcRoute event ERROR (" + status + ") - start " + DistArrayI);
                            computeDummyDistance();
                            document.getElementById("via").value = "";
                            Log("CalcRoute event ERROR (" + status + ") - stop " + DistArrayI);
                        }
                    }
                });

                Log("CalcRoute - stop");
            }

            google.maps.event.addDomListener(window, "load", initialize);

            // zwrotne do Gardens
            function get_distance()
            {
                return document.getElementById("dist").value;
            }
            
            function get_via()
            {
                return waypoint_order;
            }

            function do_calc(b, e, wpt)
            {
                start = b;
                end = e;
                waypts = [];//JSON.parse(wpt);
                Log("do_calc - start " + DistArrayI);
                if ((DistArrayI > 0) & ((DistArrayI % 10) == 0))
                {
                    setTimeout(function()
                    {
                        calcRoute();
                    }, 20);
                }
                else
                {
                    setTimeout(function()
                    {
                        calcRoute();
                    }, 20);
                }
                Log("do_calc - stop " + DistArrayI);
            }

        </script>
    </head>
    <body>
        <table style="height:100%;width:100%">
            <tr>
                <td>
                    <input id="via" type="text" style="width:99%" readonly /><br>
                    <strong>Distance:</strong>
                    <input id="dist" type="text" style="width:60px" readonly />
                    <input type="checkbox" id="wptOptimize" onChange="calcRoute();"/>Optymalizacja waypoint
                </td>
            </tr>
            <tr style="height:100%;width:100%">
                <td>
                    <div id="map-canvas" style="width:100%; height:100%"></div>
                </td>
            </tr>
        </table>

    </body>
</html>

Po wywołaniu zdarzenia window.external.GamEvent("START"); należy uruchomić DistClear, a następnie uruchomić procedurę DistArrayAdd, którego parametrami jest identyfikator, adres początku i końca trasy. Adresy muszą być tak podane, żeby na mapie Google wstawił się punkt odpowiadający temu adresowi. Po dodaniu wszystkich adresów serii należy wywołać procedurę DistStart.

Po zakończeniu obliczeń zostanie wywołane zdarzenie window.external.GamEvent("DISTANCE");.

Po wywołaniu zdarzenia window.external.GamEvent("DISTANCE"); należy pobrać wynik obliczeń za pomocą GetResult i wykorzystać w aplikacji. Tutaj XML celowo jest bez węzła obejmującego całą treść ze względu na właściwości systemu, w którym ma być wykorzystany.

Pomijając fakt długiego czasu obliczeń wszystko działa prawidłowo, odległości są liczone poprawnie. W jaki sposób da się ominąć ten limit?

Czy wykupienie płatnego API lub dodatkowych limitów żadań rozwiąże problem?
https://developers.google.com/maps/faq#usage_pricing
https://developers.google.com/maps/documentation/geocoding/usage-limits

Zakładam, że wyczerpuje się limit częstotliwości zapytań, ale nie wyczerpuje się limitu zapytań za dobę.