Навигация | Directions API | Примеры | 2GIS Documentation
Directions API

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

{
    "points": [
        {
            "type": "walking",
            "x": 82.93057,
            "y": 54.943207
        },
        {
            "type": "walking",
            "x": 82.945039,
            "y": 55.033879
        }
    ],
    "type": "jam"
}

Вместо текущих пробок можно использовать статистическую информацию по пробкам. Для этого нужно указать тип маршрута statistic и нужную дату-время в виде Unix-времени в поле utc. Например, автомобильный маршрут на основе статистических данных по пробкам на 1 декабря 2020 года, 12 часов UTC.

{
    "points": [
        {
            "type": "walking",
            "x": 82.93057,
            "y": 54.943207
        },
        {
            "type": "walking",
            "x": 82.945039,
            "y": 55.033879
        }
    ],
    "type": "statistic",
    "utc": 1606826131
}

Чтобы построить самый короткий маршрут, даже если он не является оптимальным по времени из-за пробок, нужно указать тип shortest. Автомобильный маршрут без учета пробок.

{
    "points": [
        {
            "type": "walking",
            "x": 82.93057,
            "y": 54.943207
        },
        {
            "type": "walking",
            "x": 82.945039,
            "y": 55.033879
        }
    ],
    "type": "shortest"
}

Также можно строить маршруты с учётом полос общественного транспорта (удобно для такси и автобусов). Для этого нужно к выбранному типу маршрута добавить префикс "taxi_": taxi_jam, taxi_statistic, taxi_shortest.

{
    "points": [
        {
            "type": "walking",
            "x": 82.93057,
            "y": 54.943207
        },
        {
            "type": "walking",
            "x": 82.945039,
            "y": 55.033879
        }
    ],
    "type": "taxi_jam"
}

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

  • разрешается движение по односторонним дорогам в противоположном направлении;
  • разрешается использование проездов-дублёров;
  • снимаются ограничения скорости движения (игнорируются знаки ограничения скорости).

Чтобы построить такой маршрут, нужно указать тип emergency.

{
    "points": [
        {
            "type": "walking",
            "x": 82.93057,
            "y": 54.943207
        },
        {
            "type": "walking",
            "x": 82.945039,
            "y": 55.033879
        }
    ],
    "type": "emergency"
}

Чтобы проложить пешеходный маршрут, нужно указать тип pedestrian.

{
    "points": [
        {
            "type": "walking",
            "x": 82.93057,
            "y": 54.943207
        },
        {
            "type": "walking",
            "x": 82.945039,
            "y": 55.033879
        }
    ],
    "type": "pedestrian"
}
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title>2GIS Directions API floors</title>
    <meta name="description" content="Using Directions API through floors plans" />
    <meta name="category" content="Directions API" />

    <style>
        html,
        body,
        #map {
            margin: 0;
            width: 100%;
            height: 100%;
            overflow: hidden;
        }

        .buttons {
            position: absolute;
            top: 5px;
            left: 5px;
        }

        .buttons button {
            background: #fff;
            border: none;
            border-radius: 4px;
            box-shadow: 0 1px 3px 0 rgb(38 38 38 / 50%);
            cursor: pointer;
            padding: 5px 10px;
        }

        .buttons button:hover {
            color: #777;
        }
    </style>
</head>

<body>
    <div id="map"></div>
    <div class="buttons">
        <button id="show-always">Disable dependency on floors</button>
        <button id="toggle-points">Toggle points</button>
    </div>
    <script src="https://mapgl.2gis.com/api/js/v1"></script>
    <script>
        const key = 'Your API access key';
        const directionsKey = 'Your directions API access key';

        const map = new mapgl.Map('map', {
            center: [82.89050491182851, 54.98378006147128],
            zoom: 18.5,
            key,
        });
        window.addEventListener('resize', () => map.invalidateSize());

        // Enable floor control on the right
        const floorControl = new mapgl.FloorControl(map, {
            position: 'topRight',
        });

        let showPoints = true;
        let showAlways = false;
        map.setStyleState({
            showPoints,
            showAlways,
        });
        document.querySelector('#toggle-points').addEventListener('click', () => {
            showPoints = !showPoints;
            map.setStyleState({
                showPoints,
                showAlways,
            });
        });
        document.querySelector('#show-always').addEventListener('click', (ev) => {
            showAlways = !showAlways;
            ev.target.innerHTML = showAlways
                ? 'Enable dependency on floors'
                : 'Disable dependency on floors';
            map.setStyleState({
                showPoints,
                showAlways,
            });
        });

        // Add styles for points and segments
        map.once('styleload', () => {
            const whiteLineStyle = {
                width: 9,
                color: '#fff',
            };
            const lineStyle = {
                width: 5,
                color: ['match', ['get', 'index'], [-1], '#ccc', [0], '#00ff00', [1], '#0000ff', '#ff0000'],
            };

            // Lines which are showing under houses
            const lineFilter = [
                'all',
                ['==', ['sourceAttr', 'foo'], 'bar'],
                ['==', ['get', 'type'], 'path'],
                ['==', ['global', 'showAlways'], false],
                [
                    'any',
                    ['==', ['get', 'floorId'], null],
                    ['in', ['get', 'floorId'], ['global', '_activeFloorIds']],
                ],
            ];
            map.addLayer(
                {
                    id: 'my-line-white',
                    type: 'line',
                    filter: lineFilter,
                    style: whiteLineStyle,
                },
                '344517',
            );
            map.addLayer(
                {
                    id: 'my-line',
                    type: 'line',
                    filter: lineFilter,
                    style: lineStyle,
                },
                '344517',
            );

            // Lines which are showing under houses
            const lineAboveFilter = [
                'all',
                ['==', ['sourceAttr', 'foo'], 'bar'],
                ['==', ['get', 'type'], 'path'],
                ['==', ['global', 'showAlways'], true],
            ];
            map.addLayer({
                id: 'my-line-white-above',
                type: 'line',
                filter: lineAboveFilter,
                style: whiteLineStyle,
            });
            map.addLayer({
                id: 'my-line-above',
                type: 'line',
                filter: lineAboveFilter,
                style: lineStyle,
            });

            map.addLayer({
                id: 'my-point',
                type: 'point',
                filter: [
                    'all',
                    ['global', 'showPoints'],
                    ['==', ['sourceAttr', 'foo'], 'bar'],
                    ['==', ['get', 'type'], 'point'],
                    [
                        'any',
                        ['==', ['global', 'showAlways'], true],
                        ['==', ['get', 'floorId'], null],
                        ['in', ['get', 'floorId'], ['global', '_activeFloorIds']],
                    ],
                ],
                style: {
                    iconImage: 'ent',
                    iconWidth: 15,
                    textFont: 'Noto_Sans',
                    textFontSize: 10,
                    textField: ['get', 'comment'],
                    iconPriority: 1000,
                    textPriority: 1000,
                    textHaloWidth: 1,
                    textHaloColor: '#fff',
                    allowOverlap: true,
                },
            });
            map.addLayer({
                id: 'my-point-end',
                type: 'point',
                filter: ['all', ['==', ['sourceAttr', 'foo'], 'bar'], ['==', ['get', 'type'], 'end']],
                style: {
                    iconImage: [
                        'match',
                        [
                            'any',
                            ['==', ['get', 'floorId'], null],
                            ['==', ['global', 'showAlways'], true],
                            ['in', ['get', 'floorId'], ['global', '_activeFloorIds']],
                        ],
                        [true],
                        'ent_i',
                        'ent',
                    ],
                    iconWidth: 30,
                    textFont: 'Noto_Sans_Semibold',
                    textFontSize: 14,
                    textField: ['get', 'comment'],
                    iconPriority: 2000,
                    textPriority: 2000,
                    textColor: '#fff',
                    textPlacement: 'centerCenter',
                },
            });
        });

        const points = [
            {
                type: 'pedo',
                x: 82.88828966022959,
                y: 54.983109254770376,
            },
            {
                type: 'pedo',
                x: 82.89149408367815,
                y: 54.98388809715867,
                object_id: '141265770013202',
                floor_id: '141832716803532',
            },
        ];

        fetchAndDrawRoute();

        function fetchAndDrawRoute() {
            const query = {
                type: 'pedestrian',
                points,
                use_indoor: true,
                options: ['pedestrian_instructions'],
            };

            return fetch(`https://catalog.api.2gis.ru/carrouting/6.0.0/global?key=${directionsKey}`, {
                method: 'post',
                body: JSON.stringify(query),
            })
                .then((r) => {
                    if (r.status !== 200) {
                        throw new Error(`HTTP code is ${r.status}`);
                    }
                    return r.json();
                })
                .then((r) => drawRoute(query.points, r.result && r.result[0]))
                .catch((reason) => console.error(reason));
        }

        const geojsonSource = new mapgl.GeoJsonSource(map, {
            data: {
                type: 'FeatureCollection',
                features: [],
            },
            attributes: {
                foo: 'bar',
            },
        });

        function drawRoute(points, result) {
            if (!result) {
                return;
            }

            const data = {
                type: 'FeatureCollection',
                features: [
                    {
                        type: 'Feature',
                        properties: {
                            type: 'end',
                            comment: 'A',
                            floorId: points[0].floor_id,
                        },
                        geometry: {
                            type: 'Point',
                            coordinates: [points[0].x, points[0].y],
                        },
                    },
                    {
                        type: 'Feature',
                        properties: {
                            type: 'end',
                            comment: 'B',
                            floorId: points[1].floor_id,
                        },
                        geometry: {
                            type: 'Point',
                            coordinates: [points[1].x, points[1].y],
                        },
                    },
                ],
            };

            let colorIndex = 0;
            let isFirstSegmentAdded = false;
            let lastPoint = {
                coordinates: [points[0].x, points[0].y],
                floorId: points[0].floor_id,
            };

            result.maneuvers.forEach((maneuver) => {
                if (maneuver.outcoming_path && maneuver.outcoming_path.geometry.length) {
                    const firstCoord = parserLineStringWKT(
                        maneuver.outcoming_path.geometry[0].selection,
                    )[0];

                    if (maneuver.comment) {
                        data.features.push({
                            type: 'Feature',
                            properties: {
                                type: 'point',
                                comment: maneuver.comment,
                                floorId: maneuver.outcoming_path.floor_from,
                            },
                            geometry: {
                                type: 'Point',
                                coordinates: firstCoord,
                            },
                        });
                    }

                    if (!isFirstSegmentAdded) {
                        isFirstSegmentAdded = true;
                        data.features.push({
                            type: 'Feature',
                            properties: {
                                type: 'path',
                                index: -1,
                                floorId: maneuver.outcoming_path.floor_from,
                            },
                            geometry: {
                                type: 'LineString',
                                coordinates: [[points[0].x, points[0].y], firstCoord],
                            },
                        });
                    }

                    maneuver.outcoming_path.geometry.forEach((geometry) => {
                        const coordinates = parserLineStringWKT(geometry.selection);
                        data.features.push({
                            type: 'Feature',
                            properties: {
                                type: 'path',
                                index: colorIndex++ % 3,
                                floorId: maneuver.outcoming_path.floor_to,
                            },
                            geometry: {
                                type: 'LineString',
                                coordinates,
                            },
                        });
                        lastPoint = {
                            coordinates: coordinates[coordinates.length - 1],
                            floorId: maneuver.outcoming_path.floor_to,
                        };
                    });
                } else if (maneuver.comment) {
                    data.features.push({
                        type: 'Feature',
                        properties: {
                            type: 'point',
                            comment: maneuver.comment,
                            floorId: lastPoint.floorId,
                        },
                        geometry: {
                            type: 'Point',
                            coordinates: lastPoint.coordinates,
                        },
                    });
                }
            });

            if (lastPoint) {
                data.features.push({
                    type: 'Feature',
                    properties: {
                        type: 'path',
                        floorId: lastPoint.floorId,
                        index: -1,
                    },
                    geometry: {
                        type: 'LineString',
                        coordinates: [lastPoint.coordinates, [points[1].x, points[1].y]],
                    },
                });
            }

            geojsonSource.setData(data);
        }

        function parserLineStringWKT(wkt) {
            return wkt
                .slice('LINESTRING('.length, -1)
                .split(',')
                .map((c) => c.trim().split(' ').map(Number));
        }
    </script>
</body>

</html>

Чтобы построить пешеходный маршрут внутри зданий с поэтажными планами, нужно указать параметры "use_indoor": true и "options": ["pedestrian_instructions"]. При этом хотя бы одна из конечных точек маршрута должна находиться внутри здания или быть привязана к зданию с помощью параметра object_id.

Идентификатор этажа для точки можно указать при помощи параметра floor_id.

{
    "points": [
        {
            "type": "walking",
            "x": 82.891385,
            "y": 54.983477,
            "object_id": "70000001057266023",
            "floor_id": "141832716803532"
        },
        {
            "type": "walking",
            "x": 82.897328,
            "y": 54.980198,
            "floor_id": "70030076256452036"
        }
    ],
    "type": "pedestrian",
    "use_indoor": true,
    "options": ["pedestrian_instructions"]
}

Ответ будет содержать маршрут, построенный на основе этажных схем зданий, а также инструкции по пешеходной навигации: входы и выходы из зданий, перемещение между этажами на лифте, эскалаторе или по лестнице.

Чтобы проложить велосипедный маршрут, нужно указать тип bicycle.

{
    "points": [
        {
            "type": "walking",
            "x": 82.93057,
            "y": 54.943207
        },
        {
            "type": "walking",
            "x": 82.945039,
            "y": 55.033879
        }
    ],
    "type": "bicycle"
}

По умолчанию велосипедные маршруты включают лестницы, автомобильные дороги, подземные и надземные переходы. Чтобы их исключить, можно указать параметр filters (см. Исключение типов дорог).

Если начальная или конечная точка автомобильного маршрута расположена не на дороге, то в маршрут будет добавлен путь, соединяющий её с ближайшей дорогой. Для этого можно использовать два типа точек:

  • walking - будет добавлен путь с обходом препятствий, таких как здания (пешеходный);
  • stop - будет добавлен прямой путь, игнорирующий препятствия.
"points": [
   {
       "type": "walking",
       "x": 82.93057,
       "y": 54.943207
   },
   {
       "type": "stop",
       "x": 82.945039,
       "y": 55.033879
   }
]

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

Чтобы отделить начальные точки от конечных в массиве points, можно использовать параметр start или добавить промежуточные точки (см. следующий раздел).

"points": [
   {
       "type": "stop",
       "x": 82.93057,
       "y": 54.943207,
       "start": true
   },
   {
       "type": "stop",
       "x": 82.93856,
       "y": 54.943112,
       "start": true
   },
   {
       "type": "stop",
       "x": 82.945039,
       "y": 55.033879
   }
]

В любой маршрут можно добавить промежуточные точки, указав тип pref. Общее количество точек в маршруте не должно превышать 10.

"points": [
   {
       "type": "walking",
       "x": 82.93057,
       "y": 54.943207
   },
   {
       "type": "pref",
       "x": 82.941984,
       "y": 54.974563
   },
   {
       "type": "walking",
       "x": 82.945039,
       "y": 55.033879
   }
]

При построении маршрута можно исключить определенные типы дорог, такие как грунтовые и платные (для автомобильного маршрута) или лестницы и переходы (для велосипедного маршрута). Чтобы это сделать, нужно указать в запросе поле filters.

Например, чтобы исключить грунтовые дороги из автомобильного маршрута, нужно указать значение "dirt_road":

{
    "points": [...],
    "filters": ["dirt_road"]
}

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

{
    "points": [...],
    "type": "bicycle",
    "filters": [
        "ban_stairway",
        "ban_over",
        "ban_car_road"
    ]
}

Если маршрут без указанных типов дорог получается слишком длинным или его невозможно построить, часть получившегося маршрута может включать исключённые типы дорог.

Также можно исключить некоторые типы дорог из пешеходного маршрута. Полный список возможных значений можно найти в Справочнике API.

В запросе можно указать области, которые будут избегаться при построении маршрута. Для этого используется поле exclude.

Область можно указать в виде круга, многоугольника или толстой линии. Для описания области используется три параметра:

  • type - форма исключаемой области (круг, многоугольник или линия);
  • points - координаты области;
  • extent - размер области в метрах (радиус круга или ширина линии).
{
    "points": [...],
    "exclude": [
        {
            "type": "point",
            "points": [
                {
                    "x": 82.03057,
                    "y": 54.043207
                }
            ],
            "extent": 100
        },
        {
            "type": "polyline",
            "points": [
                {
                    "x": 82.13057,
                    "y": 54.143207
                },
                {
                    "x": 82.23057,
                    "y": 54.243207
                }
            ],
            "extent": 1000
        }
    ]
}

Для каждой области можно дополнительно указать параметр severity - насколько строго нужно избегать выбранную область (soft - избегать по возможности; hard - избегать всегда). Например, исключить область в виде многоугольника.

{
    "points": [...],
    "exclude": [
        {
            "type": "polygon",
            "points": [
                {"x": 55.28770929, "y": 25.22069944},
                {"x": 55.28976922, "y": 25.25656786},
                {"x": 55.33302789, "y": 25.25687836},
                {"x": 55.33096795, "y": 25.22007825},
                {"x": 55.28770929, "y": 25.22069944}
            ],
            "severity": "hard"
        }
    ]
}

Получение высот и информации по высотам на маршруте включается флагом need_altitudes.

{
    "alternative": 0,
    "locale": "ru",
    "point_a_name": "Source",
    "point_b_name": "Target",
    "type": "jam",
    "points": [
        {
            "start": true,
            "type": "walking",
            "x": 82.93057,
            "y": 54.943207
        },
        {
            "start": false,
            "type": "walking",
            "x": 82.945039,
            "y": 55.033879,
            "object_id": "141476222741292"
        }
    ],
    "need_altitudes": true
}

При этом ответ будет содержать:

  • статистику по высотам (максимальная/минимальная высота, набор/спад высоты, максимальный угол) altitudes_info
{
    "altitudes_info": {
        "elevation_gain": 12440,
        "elevation_loss": 11020,
        "max_altitude": 10700,
        "max_road_angle": 8,
        "min_altitude": 6600
    }
}
  • у геометрий появится массив углов сегментов геометрии angles
  • у точек геометрии появится третья координата (высота над уровнем моря в см)
{
    "geometry": [
        {
            "angles": "LINESTRING(1, -1)",
            "color": "ignore",
            "length": 22,
            "selection": "LINESTRING(82.930722 54.943655 9200, 82.930815 54.943650 9220, 82.930926 54.943640 9190)",
            "style": "normal"
        }
    ]
}

Если на маршруте есть точки особого типа, то в ответе будет содержаться массив точек на маршруте. Например, на маршруте есть участок платной дороги. Для этого участка в ответе в массиве точек на маршруте формируются две точки (начала и конца платного участка).

{
    "route_points": [
        {
            "coordinates": {
                "lat": 55.73942822546596,
                "lon": 37.37259908712576
            },
            "distance": 746,
            "type": "TollRoadBegin"
        },
        {
            "coordinates": {
                "lat": 55.71525675255834,
                "lon": 37.31850882892307
            },
            "distance": 5784,
            "type": "TollRoadEnd"
        }
    ]
}