Monday, 29 June 2020

ionic react Here route optimize waypoint sequence

long press to add way points, tap to set destination
drive though way points sequentially, route back and forth

check optimize route, way points are reordered for fastest route to cover all way points
route always starts at way point 1 and ends at destination

plan a trip through several cities with route optimization
//here6.js

import React, { useState, useEffect } from 'react';
import PageForm from './PageForm'
import axios from 'axios';

export default function Here6() {
    const [map, setMap] = useState(null)
    const [layer, setLayer] = useState(null)
    const [optimizeWayPoint, setOptimizeWayPoint] = useState(true)

    useEffect(() => {
        getMap()
        setTimeout(() => {
            document.getElementById('refreshButton').click()
        }, 1000);
        return () => map.dispose();
    }, []);

    const H = window.H;

    const platform = new H.service.Platform({
        apikey: "JNIn_O9OQdca51JT5ofoou0WOKdp69bNG-XxHaHqPLo"
    });

    const defaultLayers = platform.createDefaultLayers();

    const getMap = () => {
        // Create an instance of the map
        const map = new H.Map(
            document.getElementById('mapView'),
            layer ? layer : defaultLayers.raster.normal.map,
            {
                // This map is centered over Europe
                zoom: 10,
                center: { lat: 51.048615, lng: -114.070847 },
                pixelRatio: window.devicePixelRatio || 1
            }
        );

        // Enable the event system on the map instance:
        const mapEvents = new H.mapevents.MapEvents(map);

        let markerNum = 1

        // Add longpress listeners:
        map.addEventListener('longpress', function (evt) {

            const coord = map.screenToGeo(evt.currentPointer.viewportX,
                evt.currentPointer.viewportY);

            //add a marker with number on left
            const svgMarkup = '<svg width="24" height="24" ' +
                'xmlns="http://www.w3.org/2000/svg">' +
                '<path d="M16.5 24.447v-0.996c3.352 0.099 5.993 1.174 5.993 2.487 0 1.379-2.906 2.56-6.492 2.56s-6.492-1.181-6.492-2.56c0-1.313 2.641-2.389 5.992-2.487v0.996c-2.799 0.069-4.993 0.71-4.993 1.491 0 0.827 2.459 1.623 5.493 1.623 3.033 0 5.492-0.796 5.492-1.623-0.001-0.781-2.194-1.421-4.993-1.491zM10.516 8.995c0-3.033 2.521-5.493 5.556-5.493 3.034 0 5.493 2.46 5.493 5.493 0 2.607-1.818 4.786-4.256 5.348l-1.309 13.219-1.313-13.256c-2.362-0.615-4.171-2.756-4.171-5.311zM16 7.524c0-0.828-0.671-1.498-1.498-1.498s-1.499 0.67-1.499 1.498c0 0.827 0.671 1.498 1.499 1.498s1.498-0.67 1.498-1.498z"></path>' +
                '<text x="6" y="18" font-size="12pt" font-family="Arial" font-weight="bold" ' +
                'text-anchor="middle" fill="black">${markerText}</text>' +
                '</svg>';

            const icon = new H.map.Icon(svgMarkup.replace('${markerText}', markerNum))
            const marker = new H.map.Marker(coord, { icon: icon })

            // add custom data to the marker
            marker.setData(markerNum);
            markerNum++

            map.addObject(marker);
        });

        let destinationMarker = null
        let routeLine = null

        // Add tap listeners:
        map.addEventListener('tap', function (evt) {

            const coord = map.screenToGeo(evt.currentPointer.viewportX,
                evt.currentPointer.viewportY);

            //remove last destination
            if (destinationMarker) { map.removeObject(destinationMarker) }

            //remove last route line
            if (routeLine) { map.removeObject(routeLine) }

            //find nearest marker
            calculateRouteFromAtoB(coord)

            //add a destination marker
            const svgMarkup = '<svg width="24" height="24" ' +
                'xmlns="http://www.w3.org/2000/svg">' +
                '<path d="M26 4v12h-16v12h-4v-24h20z"></path>' +
                '</svg>';

            const icon = new H.map.Icon(svgMarkup)
            destinationMarker = new H.map.Marker(coord, { icon: icon });

            map.addObject(destinationMarker);
        })

        function calculateRouteFromAtoB(coords) {
            //get start and way points
            const objects = map.getObjects()
            if (objects.length === 0) { return }

            var router = platform.getRoutingService(),
                routeRequestParams = {
                    mode: 'fastest;car',
                    representation: 'display',
                    routeattributes: 'waypoints,summary,shape,legs',
                    maneuverattributes: 'direction,action',
                    //waypoint0: start.lat + ',' + start.lng, // start
                    //waypoint1: stop1.lat + ',' + stop1.lng, // stop1
                    //waypoint2: coords.lat + ',' + coords.lng  // destination
                };

            if (document.getElementById('optimizeWayPointCheckBox').checked) {
                //reorder waypoint first, then route
                let webString = 'https://wse.ls.hereapi.com/2/findsequence.json?start=start;'
                    + objects[0].b.lat + ',' + objects[0].b.lng + '&'
                let j = 1
                for (; j < objects.length; j++) {
                    const wayPoint = objects[j].b
                    webString = webString + 'destination' + j.toString() + '=stop' + j.toString() + ';' + wayPoint.lat + ',' + wayPoint.lng + '&'
                }

                webString = webString + 'end=' + 'destination' + ';' + coords.lat + ',' + coords.lng + '&'
                    + 'mode=fastest;car&'
                    + 'apiKey=' + 'JNIn_O9OQdca51JT5ofoou0WOKdp69bNG-XxHaHqPLo'

                //console.log(webString)
                axios.get(webString)
                    .then(function (response) {
                        // handle success
                        const orderedWayPoint = response.data.results[0].waypoints
                        //console.log(orderedWayPoint);
                        for (let i = 0; i < orderedWayPoint.length; i++) {
                            routeRequestParams['waypoint' + i.toString()] = orderedWayPoint[i].lat + ',' + orderedWayPoint[i].lng
                        }

                        router.calculateRoute(
                            routeRequestParams,
                            onSuccess,
                            onError
                        );
                    })
                    .catch(function (error) {
                        // handle error
                        console.log(error);
                    })
            }
            else {
                //route waypoint in sequence without optimization
                //start and stop on the way
                let i = 0
                for (; i < objects.length; i++) {
                    const wayPoint = objects[i].b
                    routeRequestParams['waypoint' + i.toString()] = wayPoint.lat + ',' + wayPoint.lng
                }

                //destination
                routeRequestParams['waypoint' + i.toString()] = coords.lat + ',' + coords.lng

                //console.log(routeRequestParams)

                router.calculateRoute(
                    routeRequestParams,
                    onSuccess,
                    onError
                );
            }
        }

        function onSuccess(result) {
            var route = result.response.route[0];
            console.log(route)

            //add route to map
            var lineString = new H.geo.LineString(),
                routeShape = route.shape

            routeShape.forEach(function (point) {
                var parts = point.split(',');
                lineString.pushLatLngAlt(parts[0], parts[1]);
            });

            routeLine = new H.map.Polyline(lineString, {
                style: {
                    lineWidth: 3,
                    strokeColor: 'rgba(0, 128, 255, 0.7)'
                }
            });
            // Add the polyline to the map
            map.addObject(routeLine);
            // And zoom to its bounding rectangle
            map.getViewModel().setLookAtData({
                bounds: routeLine.getBoundingBox()
            });
        }

        function onError(error) {
            alert('Can\'t reach the remote server');
        }

        // Instantiate the default behavior, providing the mapEvents object:
        const behavior = new H.mapevents.Behavior(mapEvents);

        setMap(map)
    }

    const layerChange = async (selected) => {
        switch (selected) {
            case '1':
                await setLayer(defaultLayers.raster.normal.map)
                break
            case '2':
                await setLayer(defaultLayers.raster.normal.transit)
                break
            case '3':
                await setLayer(defaultLayers.raster.normal.mapnight)
                break
            case '4':
                await setLayer(defaultLayers.raster.normal.trafficincidents)
                break
            case '5':
                await setLayer(defaultLayers.raster.normal.xbase)
                break
            case '6':
                await setLayer(defaultLayers.raster.satellite.map)
                break
            case '7':
                await setLayer(defaultLayers.raster.satellite.xbase)
                break
            case '8':
                await setLayer(defaultLayers.raster.terrain.map)
                break
            case '9':
                await setLayer(defaultLayers.raster.terrain.xbase)
                break
            default:
                break
        }

        document.getElementById('refreshButton').click()
    }

    return (
        // Set a height on the map so it will display
        <div id='mapView' style={{ height: '100%' }}>
            <button id='refreshButton' onClick={() => { map.dispose(); getMap() }}
                style={{
                    position: 'fixed', top: '10px', left: '10px', zIndex: 2,
                    border: '2px solid green'
                }}
            >refresh</button>

            <select style={{
                position: 'fixed', top: '10px', left: '80px',
                height: '18px', width: '90px', zIndex: 2, fontSize: '13px'
            }}
                onChange={e => layerChange(e.target.value)}
            >
                <option value="1">default layer</option>
                <option value="2">transit</option>
                <option value="3">night</option>
                <option value="4">accident</option>
                <option value="5">xbase</option>
                <option value="6">satellite</option>
                <option value="7">satellite xbase</option>
                <option value="8">terrain</option>
                <option value="9">terrain xbase</option>
            </select>

            <button style={{ position: 'fixed', top: '10px', right: '10px', zIndex: 2 }}
                onClick={() => {
                    const formStyle = document.getElementById('pageForm').style
                    if (formStyle.display === 'none') {
                        formStyle.position = 'fixed'
                        formStyle.top = '30px'
                        formStyle.right = '10px'
                        formStyle.zIndex = 2
                        formStyle.display = 'block'
                        formStyle.backgroundColor = 'white'
                    }
                    else {
                        formStyle.display = 'none'
                    }
                }}>
                <i class="fa fa-bars"></i></button>

            <PageForm />

            <form style={{
                position: 'fixed', top: '40px', left: '10px', zIndex: 2,
                backgroundColor: 'white', fontSize: '13px', color: 'red'
            }}>
                <input type="checkbox" id="optimizeWayPointCheckBox" name="checkbox1"></input>
                <label for="checkbox1"><i> optimize route</i></label>
            </form>
        </div>
    );
}

------------------------------
//logs showing way points are reordered

(6) […]

0: Object { id: "start", lat: 41.84166172009164, lng: -87.66660758898932, … }

1: Object { id: "stop2", lat: 44.98497733195176, lng: -93.20431174861855, … }

2: Object { id: "stop4", lat: 39.239226589028085, lng: -94.6008687062527, … }

3: Object { id: "stop3", lat: 38.79007613047233, lng: -90.265330250886, … }

4: Object { id: "stop1", lat: 43.66199109727469, lng: -79.38986887781479, … }

5: Object { id: "destination", lat: 45.47785582867173, lng: -75.69460228415073, … }

length: 6

reference:
http://chuanshuoge2.blogspot.com/2020/06/ionic-react-here-driving-route.html

Optimized Waypoint Sequence for Fastest Car Route
https://developer.here.com/documentation/routing-waypoints/dev_guide/topics/quick-start-simple-car.html

No comments:

Post a Comment