Как получить матрицу маршрутов

Разработчики из Европейской экономической зоны (ЕЭЗ)

Матрица маршрутов – это двумерный массив информации о маршрутах, в котором строки соответствуют пунктам отправления, а столбцы – пунктам назначения. Для заданного списка пунктов отправления и назначения класс Route Matrix рассчитывает расстояние и продолжительность маршрута, начинающегося в каждом пункте отправления и заканчивающегося в каждом пункте назначения. Используйте класс Route Matrix, чтобы рассчитать расстояние и время в пути для маршрута с несколькими пунктами отправления и назначения.

Посмотреть полный пример исходного кода

В этом примере показано, как использовать класс Route Matrix для расчета расстояний и времени в пути между несколькими пунктами отправления и назначения.

TypeScript

// Initialize and add the map.
let map: google.maps.Map;
const markers: google.maps.marker.AdvancedMarkerElement[] = [];
const center = { lat: 51.55, lng: -1.8 };

async function init(): Promise<void> {
    //  Request the needed libraries.
    const [
        { Map },
        { Place },
        { AdvancedMarkerElement, PinElement },
        { RouteMatrix },
        { LatLngBounds, UnitSystem },
    ] = await Promise.all([
        google.maps.importLibrary('maps'),
        google.maps.importLibrary('places'),
        google.maps.importLibrary('marker'),
        google.maps.importLibrary('routes'),
        google.maps.importLibrary('core'),
    ]);

    const bounds = new LatLngBounds();

    map = new Map(document.getElementById('map')!, {
        zoom: 8,
        center,
        mapId: 'DEMO_MAP_ID',
    });

    // Build the request using Place instances.
    const origin1 = new Place({
        id: 'ChIJ83WZp86p2EcRbMrkYqGncBQ', // Greenwich, London, UK
    });
    const origin2 = new Place({
        id: 'ChIJCSkVvleJc0gR8HHaTGpajKc', // Southampton, UK
    });
    const destinationA = new Place({
        id: 'ChIJYdizgWaDcUgRH9eaSy6y5I4', // Bristol, UK
    });
    const destinationB = new Place({
        id: 'ChIJ9VPsNNQCbkgRDmeGZdsGNBQ', // Cardiff, UK
    });

    await Promise.all([
        origin1.fetchFields({ fields: ['location', 'displayName'] }),
        origin2.fetchFields({ fields: ['location', 'displayName'] }),
        destinationA.fetchFields({ fields: ['location', 'displayName'] }),
        destinationB.fetchFields({ fields: ['location', 'displayName'] }),
    ]);

    const destinations = [destinationA, destinationB];
    const origins = [origin1, origin2];
    const request: google.maps.routes.ComputeRouteMatrixRequest = {
        origins,
        destinations,
        travelMode: 'DRIVING',
        units: UnitSystem.METRIC,
        fields: ['distanceMeters', 'durationMillis', 'condition'],
    };

    // Show the request.
    document.getElementById('request')!.innerText = JSON.stringify(
        request,
        null,
        2
    );

    // Get the RouteMatrix response.
    const response = await RouteMatrix.computeRouteMatrix(request);

    // Show the response.
    document.getElementById('response')!.innerText = JSON.stringify(
        response,
        null,
        2
    );

    // Add markers for the origins.
    for (const origin of origins) {
        if (origin.location) {
            const pin = new PinElement({
                glyphText: 'O',
                glyphColor: 'white',
                background: '#137333',
                borderColor: 'white',
            });
            const marker = new AdvancedMarkerElement({
                map,
                position: origin.location,
                title: `Origin: ${origin.displayName}`,
            });
            marker.append(pin);
            markers.push(marker);
            bounds.extend(origin.location);
        }
    }

    // Add markers for the destinations.
    for (const destination of destinations) {
        if (destination.location) {
            const pin = new PinElement({
                glyphText: 'D',
                glyphColor: 'white',
                background: '#C5221F',
                borderColor: 'white',
            });

            const marker = new AdvancedMarkerElement({
                map,
                position: destination.location,
                content: pin,
                title: `Destination: ${destination.displayName ?? 'Unknown'}`,
            });

            markers.push(marker);
            bounds.extend(destination.location);
        }
    }

    // Fit the map to the bounds of all markers.
    map.fitBounds(bounds);
}

void init();

JavaScript

// Initialize and add the map.
let map;
const markers = [];
const center = { lat: 51.55, lng: -1.8 };

async function init() {
    //  Request the needed libraries.
    const [
        { Map },
        { Place },
        { AdvancedMarkerElement, PinElement },
        { RouteMatrix },
        { LatLngBounds, UnitSystem },
    ] = await Promise.all([
        google.maps.importLibrary('maps'),
        google.maps.importLibrary('places'),
        google.maps.importLibrary('marker'),
        google.maps.importLibrary('routes'),
        google.maps.importLibrary('core'),
    ]);

    const bounds = new LatLngBounds();

    map = new Map(document.getElementById('map'), {
        zoom: 8,
        center,
        mapId: 'DEMO_MAP_ID',
    });

    // Build the request using Place instances.
    const origin1 = new Place({
        id: 'ChIJ83WZp86p2EcRbMrkYqGncBQ', // Greenwich, London, UK
    });
    const origin2 = new Place({
        id: 'ChIJCSkVvleJc0gR8HHaTGpajKc', // Southampton, UK
    });
    const destinationA = new Place({
        id: 'ChIJYdizgWaDcUgRH9eaSy6y5I4', // Bristol, UK
    });
    const destinationB = new Place({
        id: 'ChIJ9VPsNNQCbkgRDmeGZdsGNBQ', // Cardiff, UK
    });

    await Promise.all([
        origin1.fetchFields({ fields: ['location', 'displayName'] }),
        origin2.fetchFields({ fields: ['location', 'displayName'] }),
        destinationA.fetchFields({ fields: ['location', 'displayName'] }),
        destinationB.fetchFields({ fields: ['location', 'displayName'] }),
    ]);

    const destinations = [destinationA, destinationB];
    const origins = [origin1, origin2];
    const request = {
        origins,
        destinations,
        travelMode: 'DRIVING',
        units: UnitSystem.METRIC,
        fields: ['distanceMeters', 'durationMillis', 'condition'],
    };

    // Show the request.
    document.getElementById('request').innerText = JSON.stringify(
        request,
        null,
        2
    );

    // Get the RouteMatrix response.
    const response = await RouteMatrix.computeRouteMatrix(request);

    // Show the response.
    document.getElementById('response').innerText = JSON.stringify(
        response,
        null,
        2
    );

    // Add markers for the origins.
    for (const origin of origins) {
        if (origin.location) {
            const pin = new PinElement({
                glyphText: 'O',
                glyphColor: 'white',
                background: '#137333',
                borderColor: 'white',
            });
            const marker = new AdvancedMarkerElement({
                map,
                position: origin.location,
                title: `Origin: ${origin.displayName}`,
            });
            marker.append(pin);
            markers.push(marker);
            bounds.extend(origin.location);
        }
    }

    // Add markers for the destinations.
    for (const destination of destinations) {
        if (destination.location) {
            const pin = new PinElement({
                glyphText: 'D',
                glyphColor: 'white',
                background: '#C5221F',
                borderColor: 'white',
            });

            const marker = new AdvancedMarkerElement({
                map,
                position: destination.location,
                content: pin,
                title: `Destination: ${destination.displayName ?? 'Unknown'}`,
            });

            markers.push(marker);
            bounds.extend(destination.location);
        }
    }

    // Fit the map to the bounds of all markers.
    map.fitBounds(bounds);
}

void init();

CSS

/*
 * Always set the map height explicitly to define the size of the div element
 * that contains the map.
 */
/* Optional: Makes the sample page fill the window. */
html,
body {
    height: 100%;
    margin: 0;
    padding: 0;
}

#container {
    height: 100%;
    display: flex;
}

#sidebar {
    flex-basis: 15rem;
    flex-grow: 1;
    padding: 1rem;
    max-width: 30rem;
    height: 100%;
    box-sizing: border-box;
    overflow: auto;
}

#map {
    flex-basis: 0;
    flex-grow: 4;
    height: 100%;
}

#sidebar {
    flex-direction: column;
}

HTML

<html>
    <head>
        <title>Route matrix</title>

        <link rel="stylesheet" type="text/css" href="./style.css" />
        <script type="module" src="./index.js"></script>
        <script>
            // prettier-ignore
            (g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({
                key: "GOOGLE_MAPS_API_KEY"
            });
        </script>
    </head>
    <body>
        <div id="container">
            <div id="map"></div>
            <div id="sidebar">
                <h3 style="flex-grow: 0">Request</h3>
                <pre style="flex-grow: 1" id="request"></pre>
                <h3 style="flex-grow: 0">Response</h3>
                <pre style="flex-grow: 1" id="response"></pre>
            </div>
        </div>
    </body>
</html>

Ограничения на количество запросов

Метод computeRouteMatrix применяет следующие ограничения запросов для путевых точек, использующих экземпляры адресов или мест, и для элементов. Элементы – это маршруты между каждым пунктом отправления и назначения в матрице маршрутов, поэтому количество элементов равно количеству пунктов отправления, умноженному на количество пунктов назначения. Например, если у вас 10 пунктов отправления и 10 пунктов назначения, то у вас 100 элементов:

  • Количество элементов не может превышать 625 для маршрутов, которые не являются маршрутами TRANSIT.
  • Если вы указываете маршрут TRANSIT, количество элементов не может превышать 100.
  • Если вы укажете TRAFFIC_AWARE_OPTIMAL, количество товаров не должно превышать 100.
  • Если вы указываете пункты отправления или назначения с помощью адресов или экземпляров Place, вы можете указать таким образом до 50 пунктов.

Дополнительные сведения можно найти в статье Как проложить маршрут на общественном транспорте.

Пример запроса матрицы маршрутов

Ниже приведен пример ComputeRouteMatrixRequest. В этом примере выполняются следующие действия:

  • Показано, как указать массив из двух путевых точек отправления и двух путевых точек назначения. Метод рассчитывает маршрут от каждого пункта отправления до каждого пункта назначения, поэтому ответ содержит четыре маршрута.
    В массиве первый элемент имеет индекс 0, второй – индекс 1 и т. д.
  • Указывает, какие поля нужно вернуть. В этом примере запрос настроен на возврат durationMillis, distanceMeters и condition для каждого маршрута.

TypeScript

const destinations = [destinationA, destinationB];
const origins = [origin1, origin2];
const request: google.maps.routes.ComputeRouteMatrixRequest = {
    origins,
    destinations,
    travelMode: 'DRIVING',
    units: UnitSystem.METRIC,
    fields: ['distanceMeters', 'durationMillis', 'condition'],
};

JavaScript

const destinations = [destinationA, destinationB];
const origins = [origin1, origin2];
const request = {
    origins,
    destinations,
    travelMode: 'DRIVING',
    units: UnitSystem.METRIC,
    fields: ['distanceMeters', 'durationMillis', 'condition'],
};

Ответ содержит четыре возможных маршрута для комбинации всех начальных и конечных путевых точек, как показано в следующем примере:

"matrix": {
  "rows": [
    {
      "items": [
        {
          "condition": "ROUTE_EXISTS",
          "distanceMeters": 202587,
          "durationMillis": 10040000
        },
        {
          "condition": "ROUTE_EXISTS",
          "distanceMeters": 252734,
          "durationMillis": 12240000
        }
      ]
    },
    {
      "items": [
        {
          "condition": "ROUTE_EXISTS",
          "distanceMeters": 166135,
          "durationMillis": 6596000
        },
        {
          "condition": "ROUTE_EXISTS",
          "distanceMeters": 216282,
          "durationMillis": 8797000
        }
      ]
    }
  ]
}
    

Определите каждый маршрут в результате, используя индекс начальной и конечной точек, чтобы найти соответствующий объект RouteMatrixItem в двумерном массиве. Например, RouteMatrixItem, описывающий маршрут, рассчитанный от пункта отправления с индексом 1 до пункта назначения с индексом 0 в запросе, будет вторым элементом массива RouteMatrix.rows и первым элементом массива RouteMatrixRow.items.

В следующем фрагменте кода показано, как определить RouteMatrixItem, чтобы найти маршрут для определенного пункта отправления и пункта назначения:

// Find the route for origin 'x' and destination 'y'.
const {matrix} = await RouteMatrix.computeRouteMatrix(request);
const myRouteMatrixItem = matrix.rows[x].items[y];
    

Выберите поля, которые нужно вернуть

При запросе матрицы расстояний необходимо использовать маску поля, чтобы указать, какую информацию должен возвращать ответ.

Маски полей также помогают не запрашивать ненужные данные, что позволяет сократить время ответа и избежать возврата информации, которая не нужна вашей системе.

Укажите список нужных полей, задав свойство ComputeRoutesMatrixRequest.fields, как показано в следующем фрагменте кода:

fields: ['durationMillis', 'distanceMeters', 'condition'],
    

Как определить, какие маски полей использовать

Вот как можно определить, какие поля вы хотите использовать, и создать для них маски полей:

  1. Запросите все поля, используя маску полей ['*'].
  2. Посмотрите иерархию полей в классе RouteMatrixItem, чтобы найти нужные поля.
  3. Создайте маски полей, используя иерархию полей, показанную на предыдущем шаге, в следующем формате:

    topLevelField[.secondLevelField][.thirdLevelField][...]

Например, для этого RouteMatrixItem:

  "travelAdvisory": {
    "fuelConsumptionMicroliters": 0,
    "tollInfo": {
      "estimatedPrices": [
        {
          "currencyCode": "USD",
          "units": 4,
          "nanos": 400000000
        }
      ]
    }
  },
    

Если вы хотите получить только поле tollInfo для RouteMatrixItem, маска поля будет выглядеть следующим образом:

fields: ['travelAdvisory.tollInfo']

Если вы хотите запросить расчетный расход топлива, маска поля будет выглядеть следующим образом:

fields: ['travelAdvisory.fuelConsumptionMicroliters']

Если вы хотите запросить оба поля, маска будет выглядеть следующим образом:

fields: ['travelAdvisory.fuelConsumptionMicroliters', 'travelAdvisory.tollInfo']

Если вы хотите запросить полный набор предупреждений для путешественников, используйте следующую маску поля:

fields: ['travelAdvisory']

Как запросить матрицу маршрутов общественного транспорта

Получите матрицу маршрутов, в которой используются варианты общественного транспорта, доступные в регионе. Это могут быть автобусы, метро, поезда и т. д. Чтобы запросить матрицу маршрутов общественного транспорта, выполните следующие действия:

  • Присвойте свойству travelMode значение TRANSIT.
  • Запросите поле travelAdvisory.

Подробнее о маршрутах общественного транспорта…