Добавить контрол построения маршрутов на карту

Open in CodeSandbox

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
        <script crossorigin src="https://cdn.jsdelivr.net/npm/@babel/standalone@7/babel.min.js"></script>
        <!-- To make the map appear, you must add your apikey -->
        <script src="https://api-maps.yandex.ru/v3/?apikey=<YOUR_APIKEY>&lang=en_US" type="text/javascript"></script>

        <script
            data-plugins="transform-modules-umd"
            data-presets="typescript"
            type="text/babel"
            src="./common.ts"
        ></script>
        <script
            data-plugins="transform-modules-umd"
            data-presets="typescript"
            type="text/babel"
            src="../variables.ts"
        ></script>
        <script data-plugins="transform-modules-umd" data-presets="typescript" type="text/babel">
            import type {BaseRouteResponse, LngLat, RouteOptions, YMapFeature} from '@yandex/ymaps3-types';
            import type {YMapDefaultMarker} from '@yandex/ymaps3-default-ui-theme';
            import {FROM_POINT_STYLE, LOCATION, MARGIN, PREVIEW_POINT_STYLE, TO_POINT_STYLE, getStroke} from '../variables';
            import {TRUCK_PARAMS} from './common';
            
            window.map = null;
            
            main();
            async function main() {
                // Waiting for all api elements to be loaded
                await ymaps3.ready;
                const {YMap, YMapDefaultSchemeLayer, YMapDefaultFeaturesLayer, YMapControls} = ymaps3;
            
                const {YMapRouteControl, YMapDefaultMarker} = await ymaps3.import('@yandex/ymaps3-default-ui-theme');
            
                map = new YMap(document.getElementById('app'), {location: LOCATION, margin: MARGIN}, [
                    new YMapDefaultSchemeLayer({}),
                    new YMapDefaultFeaturesLayer({})
                ]);
            
                const dragEndHandler = () => {
                    routeControl.update({
                        waypoints: [
                            map.children.includes(fromPoint) ? fromPoint.coordinates : null,
                            map.children.includes(toPoint) ? toPoint.coordinates : null
                        ]
                    });
                };
            
                const fromPoint: YMapDefaultMarker = new YMapDefaultMarker({
                    coordinates: map.center as LngLat,
                    onDragEnd: dragEndHandler,
                    draggable: true,
                    ...FROM_POINT_STYLE
                });
                const toPoint: YMapDefaultMarker = new YMapDefaultMarker({
                    coordinates: map.center as LngLat,
                    onDragEnd: dragEndHandler,
                    draggable: true,
                    ...TO_POINT_STYLE
                });
                let previewPoint: YMapDefaultMarker = new YMapDefaultMarker({
                    coordinates: map.center as LngLat,
                    ...PREVIEW_POINT_STYLE
                });
            
                let featuresOnMap: YMapFeature[] = [];
            
                const routeControl = new YMapRouteControl({
                    truckParameters: TRUCK_PARAMS,
                    waypoints: [map.center as LngLat, null],
                    onBuildRouteError() {
                        featuresOnMap.forEach((f) => map.removeChild(f));
                        featuresOnMap = [];
                    },
                    onRouteResult(result, type) {
                        featuresOnMap.forEach((f) => map.removeChild(f));
                        featuresOnMap = getFeatures(result, type);
                        featuresOnMap.forEach((f) => map.addChild(f));
                    },
                    onUpdateWaypoints(waypoints) {
                        const [from, to] = waypoints;
                        if (from) {
                            const {coordinates} = from.geometry;
                            fromPoint.update({coordinates});
                            map.addChild(fromPoint);
                        } else {
                            map.removeChild(fromPoint);
                        }
            
                        if (to) {
                            const {coordinates} = to.geometry;
                            toPoint.update({coordinates});
                            map.addChild(toPoint);
                        } else {
                            map.removeChild(toPoint);
                        }
                        if (!to || !from) {
                            featuresOnMap.forEach((f) => map.removeChild(f));
                            featuresOnMap = [];
                        }
                    },
                    onMouseMoveOnMap(coordinates, index, lastCall) {
                        if (!lastCall) {
                            previewPoint.update({coordinates});
            
                            if (!map.children.includes(previewPoint)) {
                                map.addChild(previewPoint);
                            }
                        } else {
                            map.removeChild(previewPoint);
                        }
                    }
                });
            
                map.addChild(new YMapControls({position: 'top left'}).addChild(routeControl));
            
                const getFeatures = (result: BaseRouteResponse, type: RouteOptions['type']): YMapFeature[] => {
                    if (type !== 'transit') {
                        const {geometry} = result.toRoute();
                        return [new ymaps3.YMapFeature({geometry, style: {stroke: getStroke(type), simplificationRate: 0}})];
                    }
                    return result.toSteps().map(
                        (step) =>
                            new ymaps3.YMapFeature({
                                geometry: step.geometry,
                                style: {stroke: getStroke(step.properties.mode as RouteOptions['type']), simplificationRate: 0}
                            })
                    );
                };
            }
        </script>

        <style> html, body, #app { width: 100%; height: 100%; margin: 0; padding: 0; font-family: Arial, Helvetica, sans-serif; } .toolbar { position: absolute; z-index: 1000; top: 0; left: 0; display: flex; align-items: center; padding: 16px; } .toolbar a { padding: 16px; }  </style>
        <link rel="stylesheet" href="./common.css" />
    </head>
    <body>
        <div id="app"></div>
    </body>
</html>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
        <script crossorigin src="https://cdn.jsdelivr.net/npm/react@17/umd/react.production.min.js"></script>
        <script crossorigin src="https://cdn.jsdelivr.net/npm/react-dom@17/umd/react-dom.production.min.js"></script>
        <script crossorigin src="https://cdn.jsdelivr.net/npm/@babel/standalone@7/babel.min.js"></script>
        <!-- To make the map appear, you must add your apikey -->
        <script src="https://api-maps.yandex.ru/v3/?apikey=<YOUR_APIKEY>&lang=en_US" type="text/javascript"></script>

        <script
            data-plugins="transform-modules-umd"
            data-presets="typescript"
            type="text/babel"
            src="./common.ts"
        ></script>
        <script
            data-plugins="transform-modules-umd"
            data-presets="typescript"
            type="text/babel"
            src="../variables.ts"
        ></script>
        <script data-plugins="transform-modules-umd" data-presets="react, typescript" type="text/babel">
            import {BaseRouteResponse, LngLat, YMapLocationRequest, RouteOptions} from '@yandex/ymaps3-types';
            import {YMapRouteControlProps, WaypointsArray} from '@yandex/ymaps3-default-ui-theme';
            import {FROM_POINT_STYLE, LOCATION, MARGIN, PREVIEW_POINT_STYLE, TO_POINT_STYLE, getStroke} from '../variables';
            import {computeBoundsForPoints, TRUCK_PARAMS} from './common';
            
            window.map = null;
            
            main();
            async function main() {
                // For each object in the JS API, there is a React counterpart
                // To use the React version of the API, include the module @yandex/ymaps3-reactify
                const [ymaps3React] = await Promise.all([ymaps3.import('@yandex/ymaps3-reactify'), ymaps3.ready]);
                const reactify = ymaps3React.reactify.bindTo(React, ReactDOM);
            
                const {YMap, YMapDefaultSchemeLayer, YMapDefaultFeaturesLayer, YMapControls, YMapFeature} = reactify.module(ymaps3);
                const {YMapRouteControl, YMapDefaultMarker} = reactify.module(
                    await ymaps3.import('@yandex/ymaps3-default-ui-theme')
                );
            
                ReactDOM.render(
                    <React.StrictMode>
                        <App />
                    </React.StrictMode>,
                    document.getElementById('app')
                );
            
                function App() {
                    const [routeType, setRouteType] = React.useState<RouteOptions['type']>('driving');
                    const [routeResult, setRouteResult] = React.useState<BaseRouteResponse>();
                    const [showFeature, setShowFeature] = React.useState(false);
                    const [fromCoords, setFromCoords] = React.useState<LngLat | undefined>();
                    const [toCoords, setToCoords] = React.useState<LngLat | undefined>();
                    const [previewCoords, setPreviewCoords] = React.useState<LngLat | undefined>();
                    const [waypoints, setWaypoints] = React.useState<[LngLat, LngLat]>([LOCATION.center, null]);
            
                    const onRouteResult = React.useCallback((result: BaseRouteResponse, type: RouteOptions['type']) => {
                        setRouteType(type);
                        setRouteResult(result);
                        setShowFeature(true);
                    }, []);
            
                    const onUpdateWaypoints = React.useCallback((waypoints: WaypointsArray) => {
                        const [from, to] = waypoints;
                        setFromCoords(from?.geometry?.coordinates);
                        setToCoords(to?.geometry?.coordinates);
            
                        if (!from || !to) {
                            setShowFeature(false);
                        }
                        setPreviewCoords(undefined);
                    }, []);
            
                    const onBuildRouteError = React.useCallback(() => {
                        setShowFeature(false);
                    }, []);
            
                    const onMouseMoveOnMap = React.useCallback<YMapRouteControlProps['onMouseMoveOnMap']>(
                        (coordinates, index, lastCall) => {
                            setPreviewCoords(() => (lastCall ? undefined : coordinates));
                        },
                        []
                    );
            
                    const onDragEndHandler = React.useCallback(
                        (coordinates: LngLat, type: 'from' | 'to') => {
                            if (type === 'from') {
                                setFromCoords(coordinates);
                                setWaypoints([coordinates, toCoords]);
                            } else {
                                setToCoords(coordinates);
                                setWaypoints([fromCoords, coordinates]);
                            }
                        },
                        [fromCoords, toCoords]
                    );
            
                    const features = React.useMemo(() => {
                        if (!routeResult) {
                            return null;
                        }
                        if (routeType !== 'transit') {
                            const {geometry} = routeResult.toRoute();
                            return [
                                <YMapFeature geometry={geometry} style={{stroke: getStroke(routeType), simplificationRate: 0}} />
                            ];
                        }
                        return routeResult
                            .toSteps()
                            .map((step) => (
                                <YMapFeature
                                    geometry={step.geometry}
                                    style={{stroke: getStroke(step.properties.mode as RouteOptions['type']), simplificationRate: 0}}
                                />
                            ));
                    }, [routeResult, routeType]);
            
                    return (
                        <YMap location={reactify.useDefault(LOCATION)} margin={MARGIN} ref={(x) => (map = x)}>
                            <YMapDefaultSchemeLayer />
                            <YMapDefaultFeaturesLayer />
                            <YMapControls position="top left">
                                <YMapRouteControl
                                    truckParameters={TRUCK_PARAMS}
                                    waypoints={waypoints}
                                    onRouteResult={onRouteResult}
                                    onUpdateWaypoints={onUpdateWaypoints}
                                    onBuildRouteError={onBuildRouteError}
                                    onMouseMoveOnMap={onMouseMoveOnMap}
                                />
                            </YMapControls>
            
                            {showFeature && features}
                            {fromCoords !== undefined && (
                                <YMapDefaultMarker
                                    coordinates={fromCoords}
                                    draggable
                                    onDragEnd={(coordinates) => onDragEndHandler(coordinates, 'from')}
                                    {...FROM_POINT_STYLE}
                                />
                            )}
                            {toCoords !== undefined && (
                                <YMapDefaultMarker
                                    coordinates={toCoords}
                                    draggable
                                    onDragEnd={(coordinates) => onDragEndHandler(coordinates, 'to')}
                                    {...TO_POINT_STYLE}
                                />
                            )}
                            {previewCoords !== undefined && (
                                <YMapDefaultMarker coordinates={previewCoords} {...PREVIEW_POINT_STYLE} />
                            )}
                        </YMap>
                    );
                }
            }
        </script>

        <style> html, body, #app { width: 100%; height: 100%; margin: 0; padding: 0; font-family: Arial, Helvetica, sans-serif; } .toolbar { position: absolute; z-index: 1000; top: 0; left: 0; display: flex; align-items: center; padding: 16px; } .toolbar a { padding: 16px; }  </style>
        <link rel="stylesheet" href="./common.css" />
    </head>
    <body>
        <div id="app"></div>
    </body>
</html>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
        <script crossorigin src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.js"></script>
        <script crossorigin src="https://cdn.jsdelivr.net/npm/@babel/standalone@7/babel.min.js"></script>

        <script src="https://api-maps.yandex.ru/v3/?apikey=<YOUR_APIKEY>&lang=en_US" type="text/javascript"></script>

        <script
            data-plugins="transform-modules-umd"
            data-presets="typescript"
            type="text/babel"
            src="./common.ts"
        ></script>
        <script
            data-plugins="transform-modules-umd"
            data-presets="typescript"
            type="text/babel"
            src="../variables.ts"
        ></script>
        <script data-plugins="transform-modules-umd" data-presets="typescript" type="text/babel">
            import {BaseRouteResponse, LngLat, YMapLocationRequest, RouteOptions} from '@yandex/ymaps3-types';
            import {YMapRouteControlProps, WaypointsArray} from '@yandex/ymaps3-default-ui-theme';
            import {FROM_POINT_STYLE, LOCATION, MARGIN, PREVIEW_POINT_STYLE, TO_POINT_STYLE, getStroke} from '../variables';
            import {TRUCK_PARAMS} from './common';
            
            window.map = null;
            
            main();
            async function main() {
                // For each object in the JS API, there is a Vue counterpart
                // To use the Vue version of the API, include the module @yandex/ymaps3-vuefy
                const [ymaps3Vue] = await Promise.all([ymaps3.import('@yandex/ymaps3-vuefy'), ymaps3.ready]);
                const vuefy = ymaps3Vue.vuefy.bindTo(Vue);
            
                const {YMap, YMapDefaultSchemeLayer, YMapDefaultFeaturesLayer, YMapControls, YMapFeature} = vuefy.module(ymaps3);
            
                const {YMapRouteControl, YMapDefaultMarker} = vuefy.module(await ymaps3.import('@yandex/ymaps3-default-ui-theme'));
            
                const app = Vue.createApp({
                    components: {
                        YMap,
                        YMapDefaultSchemeLayer,
                        YMapDefaultFeaturesLayer,
                        YMapControls,
                        YMapFeature,
                        YMapRouteControl,
                        YMapDefaultMarker
                    },
                    setup() {
                        const location = Vue.ref<YMapLocationRequest>(LOCATION);
                        const routeType = Vue.ref<RouteOptions['type']>('driving');
                        const routeResult = Vue.shallowRef<BaseRouteResponse>();
                        const showFeature = Vue.ref(false);
                        const fromCoords = Vue.ref<LngLat | undefined>();
                        const toCoords = Vue.ref<LngLat | undefined>();
                        const previewCoords = Vue.ref<LngLat | undefined>();
                        const waypoints = Vue.ref<[LngLat, LngLat]>([LOCATION.center, null]);
            
                        const refMap = (ref: any) => {
                            window.map = ref?.entity;
                        };
                        const onRouteResult = (result: BaseRouteResponse, type: RouteOptions['type']) => {
                            routeType.value = type;
                            routeResult.value = result;
                            showFeature.value = true;
                        };
                        const onUpdateWaypoints = (waypoints: WaypointsArray) => {
                            const [from, to] = waypoints;
                            fromCoords.value = from?.geometry?.coordinates;
                            toCoords.value = to?.geometry?.coordinates;
            
                            if (!from || !to) {
                                showFeature.value = false;
                            }
                            previewCoords.value = undefined;
                        };
                        const onBuildRouteError = () => {
                            showFeature.value = false;
                        };
                        const onMouseMoveOnMap: YMapRouteControlProps['onMouseMoveOnMap'] = (coordinates, index, lastCall) => {
                            previewCoords.value = lastCall ? undefined : coordinates;
                        };
                        const onDragEndHandler = (coordinates: LngLat, type: 'from' | 'to') => {
                            if (type === 'from') {
                                waypoints.value = [coordinates, toCoords.value];
                            } else {
                                waypoints.value = [fromCoords.value, coordinates];
                            }
                        };
                        const features = Vue.computed(() => {
                            if (!routeResult) {
                                return null;
                            }
                            if (routeType.value !== 'transit') {
                                const {geometry} = routeResult.value.toRoute();
                                return [
                                    {
                                        geometry,
                                        style: {stroke: getStroke(routeType.value), simplificationRate: 0}
                                    }
                                ];
                            }
                            return routeResult.value.toSteps().map((step) => ({
                                geometry: step.geometry,
                                style: {
                                    stroke: getStroke(step.properties.mode as RouteOptions['type']),
                                    simplificationRate: 0
                                }
                            }));
                        });
            
                        return {
                            LOCATION,
                            MARGIN,
                            FROM_POINT_STYLE,
                            PREVIEW_POINT_STYLE,
                            TO_POINT_STYLE,
                            TRUCK_PARAMS,
                            location,
                            routeType,
                            routeResult,
                            showFeature,
                            fromCoords,
                            toCoords,
                            previewCoords,
                            waypoints,
                            features,
                            onRouteResult,
                            onUpdateWaypoints,
                            onBuildRouteError,
                            onMouseMoveOnMap,
                            onDragEndHandler,
                            refMap
                        };
                    },
                    template: `
                        <YMap :location="location" :margin="MARGIN" :ref="refMap">
                            <YMapDefaultSchemeLayer />
                            <YMapDefaultFeaturesLayer />
                            <YMapControls position="top left">
                                <YMapRouteControl
                                    :truckParameters="TRUCK_PARAMS"
                                    :waypoints="waypoints"
                                    :onRouteResult="onRouteResult"
                                    :onUpdateWaypoints="onUpdateWaypoints"
                                    :onBuildRouteError="onBuildRouteError"
                                    :onMouseMoveOnMap="onMouseMoveOnMap" />
                            </YMapControls>
                            <template v-if="showFeature">
                                <YMapFeature v-for="feature in features"
                                    :geometry="feature.geometry"
                                    :style="feature.style" />
                            </template>
                            <YMapDefaultMarker
                                v-if="fromCoords !== undefined"
                                :coordinates="fromCoords"
                                draggable
                                :onDragEnd="(coordinates) => onDragEndHandler(coordinates, 'from')"
                                v-bind="FROM_POINT_STYLE" />
                            <YMapDefaultMarker
                                v-if="toCoords !== undefined"
                                :coordinates="toCoords"
                                draggable
                                :onDragEnd="(coordinates) => onDragEndHandler(coordinates, 'to')"
                                v-bind="TO_POINT_STYLE" />
                            <YMapDefaultMarker
                                v-if="previewCoords !== undefined"
                                :coordinates="previewCoords" v-bind="PREVIEW_POINT_STYLE" />
            
                        </YMap>`
                });
                app.mount('#app');
            }
        </script>

        <style> html, body, #app { width: 100%; height: 100%; margin: 0; padding: 0; font-family: Arial, Helvetica, sans-serif; } .toolbar { position: absolute; z-index: 1000; top: 0; left: 0; display: flex; align-items: center; padding: 16px; } .toolbar a { padding: 16px; }  </style>
        <link rel="stylesheet" href="./common.css" />
    </head>
    <body>
        <div id="app"></div>
    </body>
</html>
import type {
    LngLat,
    LngLatBounds,
    YMapCenterLocation,
    YMapZoomLocation,
    Margin,
    RouteOptions,
    Stroke,
    TruckParameters
} from '@yandex/ymaps3-types';

export const LOCATION: YMapCenterLocation & YMapZoomLocation = {
    center: [37.62315, 55.752507],
    zoom: 12
};

export const MARGIN: Margin = [10, 10, 10, 10];

export const FROM_POINT_STYLE = Object.freeze({
    size: 'normal',
    color: {day: '#FF4433', night: '#FF5B4D'},
    iconName: 'fallback'
});
export const TO_POINT_STYLE = Object.freeze({
    size: 'normal',
    color: {day: '#333333', night: '#999999'},
    iconName: 'fallback'
});
export const PREVIEW_POINT_STYLE = Object.freeze({
    size: 'normal',
    color: {day: '#FF443380', night: '#FF5B4D80'},
    iconName: 'fallback'
});

export const getStroke = (type: RouteOptions['type']): Stroke => {
    if (type === 'walking') {
        return [
            {width: 4, color: '#7D90F0', dash: [4, 8]},
            {width: 8, color: '#ffffff'}
        ];
    }
    return [
        {width: 6, color: '#83C753'},
        {width: 8, color: '#0000004D'}
    ];
};
import {LngLat, LngLatBounds, TruckParameters} from '@yandex/ymaps3-types';

ymaps3.ready.then(() => {
    ymaps3.import.registerCdn('https://cdn.jsdelivr.net/npm/{package}', ['@yandex/ymaps3-default-ui-theme@0.0']);
    // 
    ymaps3.getDefaultConfig().setApikeys({
        router: '<YOUR_APIKEY>'
    });
    // 
});

export const TRUCK_PARAMS: TruckParameters = {
    weight: 40,
    maxWeight: 40,
    axleWeight: 10,
    payload: 20,
    height: 4,
    width: 2.5,
    length: 16,
    ecoClass: 4,
    hasTrailer: true
};