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

Sunday 28 June 2020

ionic react Here driving route

long press to add start, tap to add destination, fastest route is calculated

add stop on the way

find the fastest route from LA to Alaska via Seattle and Vancouver
//here6.js

import React, { useState, useEffect } from 'react';
import PageForm from './PageForm'
import { star } from 'ionicons/icons';

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

    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 }

            //const start = objects[0].b
            //const stop1 = objects[1].b
            console.log(objects.length)
            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
                };

            //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'
                    }
                    else {
                        formStyle.display = 'none'
                    }
                }}>
                <i class="fa fa-bars"></i></button>

            <PageForm />
        </div>
    );

}

reference:
http://chuanshuoge2.blogspot.com/2020/06/ionic-react-here-find-distance-to.html

Map with Driving Route from A to B
https://developer.here.com/documentation/examples/maps-js/services/map-with-route-from-a-to-b

Saturday 27 June 2020

ionic react Here geolocate

app opens, press search address

type in address, submit

map is zoomed in and centered at address found

zoom out view

if more than 1 address found, map is centered at first address

zoom out and see the second address found

press search button to collapse addresses, press menu button to switch to other pages
//here5.js

import React, { useState, useEffect } from 'react';
import { Plugins } from '@capacitor/core';

export default function Here5() {
    const [map, setMap] = useState(null)
    const [layer, setLayer] = useState(null)
    const [houseNumber, setHouseNumber] = useState('')
    const [street, setStreet] = useState('')
    const [city, setCity] = useState('')
    const [country, setCountry] = useState('')

    const { Geolocation } = Plugins;

    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: 3,
                center: { lat: 48.30432303555956, lng: -104.94466241321628 },
                pixelRatio: window.devicePixelRatio || 1
            }
        );

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

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

        function geocode() {
            var geocoder = platform.getGeocodingService()

            if (country === '') { return }

            var geocodingParameters = {
                housenumber: houseNumber,
                street: street,
                city: city,
                country: country,
                jsonattributes: 1
            };

            geocoder.geocode(
                geocodingParameters,
                onSuccess,
                onError
            );
        }

        function onSuccess(result) {
            var locations = result.response.view[0].result;
            var center = null

            for (let i = 0; i < locations.length; i++) {

                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}', i + 1))
                const position = locations[i].location.displayPosition
                console.log(position)

                const coord = { lat: position.latitude, lng: position.longitude }
                if (i === 0) { center = coord }

                const marker = new H.map.Marker(coord, { icon: icon })
                map.addObject(marker);
            }

            //zoom into 1st location found
            //create boundary
            var CircleBoundary = new H.map.Circle(
                new H.geo.Point(center.lat, center.lng), //center
                11703, // Radius
                { style: { fillColor: 'rgba(0, 0, 0, 0)' } }
            );

            map.getViewModel().setLookAtData({
                zoom: 15,
                bounds: CircleBoundary.getBoundingBox()
            });
        }

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

        geocode()

        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>

            <span id='headingDom' style={{
                position: 'fixed', top: '40px', left: '10px',
                zIndex: 2, fontSize: '13px'
            }}></span>

            <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'
                    }
                    else {
                        formStyle.display = 'none'
                    }
                }}>
                <i class="fa fa-bars"></i></button>

            <form id='pageForm' style={{ display: 'none' }}>
                <input id='page1' name='page' type='radio' onClick={() => window.location.href = '/tab1'}></input>
                <label for="page1" style={{ fontSize: '13px' }}> measure</label><br></br>
                <input id='page2' name='page' type='radio' onClick={() => window.location.href = '/tab2'}></input>
                <label for="page2" style={{ fontSize: '13px' }}> domMarker</label><br></br>
                <input id='page3' name='page' type='radio' onClick={() => window.location.href = '/tab3'}></input>
                <label for="page3" style={{ fontSize: '13px' }}> cluster</label><br />
                <input id='page4' name='page' type='radio' onClick={() => window.location.href = '/tab4'}></input>
                <label for="page4" style={{ fontSize: '13px' }}> kml</label><br />
                <input id='page5' name='page' type='radio' onClick={() => window.location.href = '/tab5'}></input>
                <label for="page5" style={{ fontSize: '13px' }}> search</label><br />
            </form>

            <button onClick={() => {
                const searchFormStyle = document.getElementById('searchForm').style
                if (searchFormStyle.display === 'none') {
                    searchFormStyle.display = 'block'
                    searchFormStyle.position = 'fixed'
                    searchFormStyle.top = '60px'
                    searchFormStyle.left = '10px'
                    searchFormStyle.zIndex = 2
                }
                else {
                    searchFormStyle.display = 'none'
                }
            }}
                style={{
                    position: 'fixed', top: '40px', left: '10px', zIndex: 2,
                    border: '2px solid green'
                }}
            >search address</button>

            <form id='searchForm' style={{ display: 'none' }} onSubmit={e => e.preventDefault()}>
                <input id='houseNumber' placeholder='house #' style={{ width: '150px' }}
                    onChange={e => { setHouseNumber(e.target.value); }}>
                </input><br />
                <input id='street' placeholder='street' style={{ width: '150px' }}
                    onChange={e => setStreet(e.target.value)}>
                </input><br />
                <input id='city' placeholder='city' style={{ width: '150px' }}
                    onChange={e => setCity(e.target.value)}>
                </input><br />
                <input id='country' placeholder='country' style={{ width: '150px' }}
                    onChange={e => setCountry(e.target.value)}></input><br />
                <button style={{ border: '2px solid green' }}
                    onClick={() => {
                        document.getElementById('refreshButton').click();
                        setTimeout(() => {
                            document.getElementById('refreshButton').click();
                        }, 1000);
                    }}>
                    submit</button>
            </form>

        </div>
    );

}

reference:
https://developer.here.com/documentation/examples/maps-js/services/geocode-a-location-from-structured-address

zoom in boundary
https://developer.here.com/documentation/examples/maps-js/maps/custom-zooming-into-bounds
https://developer.here.com/documentation/examples/maps-js/maps/map-using-view-bounds

How The Rock Spent Dollars

Friday 26 June 2020

ionic react Here display kml

draw area on https://google.com/maps/d, export to kml

copy kml file in public folder

open app

tap on area will alter area name
//here4.js

import React, { useState, useEffect } from 'react';
import { Plugins } from '@capacitor/core';

export default function Here4() {
    const [map, setMap] = useState(null)
    const [layer, setLayer] = useState(null)

    const { Geolocation } = Plugins;

    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: 2,
                center: { lat: 48.30432303555956, lng: -104.94466241321628 },
                pixelRatio: window.devicePixelRatio || 1
            }
        );

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

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

        function renderKML() {
            // Create a reader object passing in the URL of our KML file
            const reader = new H.data.kml.Reader('areas.kml');
            reader.addEventListener("statechange", function (evt) {
                if (evt.state === H.data.AbstractReader.State.READY) {
                    // Get KML layer from the reader object and add it to the map
                    map.addLayer(reader.getLayer());
                    reader.getLayer().getProvider().addEventListener("tap", (evt) => {
                        alert(evt.target.getData().name)
                    });
                }
                if (evt.state === H.data.AbstractReader.State.ERROR) {
                    alert('KML parsing error')
                }
            });

            // Parse the document
            reader.parse();
        }

        renderKML()

        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>

            <span id='headingDom' style={{
                position: 'fixed', top: '40px', left: '10px',
                zIndex: 2, fontSize: '13px'
            }}></span>

            <form style={{ position: 'fixed', top: '10px', right: '10px', zIndex: 2 }}>
                <input id='page1' name='page' type='radio' onClick={() => window.location.href = '/tab1'}></input>
                <label for="page1" style={{ fontSize: '13px' }}> page 1</label><br></br>
                <input id='page2' name='page' type='radio' onClick={() => window.location.href = '/tab2'}></input>
                <label for="page2" style={{ fontSize: '13px' }}> page 2</label><br></br>
                <input id='page3' name='page' type='radio' onClick={() => window.location.href = '/tab3'}></input>
                <label for="page3" style={{ fontSize: '13px' }}> page 3</label>
            </form>

        </div>
    );

}

reference:
https://developer.here.com/blog/how-to-migrate-kml-from-google-to-here-maps-and-xyz
https://developer.here.com/documentation/examples/maps-js/maps/display-kml-on-map

Export map data from Google My Maps to a KML file
https://help.scribblemaps.com/knowledgebase/articles/1917415-export-map-data-from-google-my-maps-to-a-kml-file
https://google.com/maps/d

H.data.kml.Reader
https://developer.here.com/cn/documentation/maps/hls-chn/topics_api/h-data-kml-reader.html

Thursday 25 June 2020

How Commercials Scenes Were Made

ionic react Here reverse geocode

tap on point, id and address are shown

addresses are looked up from point latitude and longitude

//here3.js

import React, { useState, useEffect } from 'react';
import { Plugins } from '@capacitor/core';
import '../pages/Tab3.css';

export default function Here3() {
    const [map, setMap] = useState(null)
    const [layer, setLayer] = useState(null)
    const [data, setData] = useState([])

    const { Geolocation } = Plugins;

    useEffect(() => {
        getMap()
        generateMarkers()
        setTimeout(() => {
            document.getElementById('refreshButton').click()
        }, 1000);

        return () => map.dispose();
    }, []);

    const generateMarkers = () => {
        const xMin = 50.84561389596551
        const xMax = 51.21708943492305
        const xDiff = xMax - xMin
        const yMax = -113.80644441313935
        const yMin = -114.36916364528605
        const yDiff = yMax - yMin
        const randomPositions = []

        for (let i = 0; i < 1000; i++) {
            const position = {
                lat: Math.random() * xDiff + xMin,
                lng: Math.random() * yDiff + yMin
            }
            randomPositions.push(position)
        }

        setData(randomPositions)
    }

    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);

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

        // create the default UI component, for displaying bubbles
        var ui = H.ui.UI.createDefault(map, defaultLayers);

        function startClustering(map, data) {
            // First we need to create an array of DataPoint objects,
            // for the ClusterProvider
            var dataPoints = data.map(function (item, index) {
                //datapoint ={lat, lng, attitude, data}, id is stored in point.data,
                //image URL can be stored in data as well
                return new H.clustering.DataPoint(item.lat, item.lng, null, index);
            });

            // Create a clustering provider with custom options for clusterizing the input
            var clusteredDataProvider = new H.clustering.Provider(dataPoints, {
                clusteringOptions: {
                    // Maximum radius of the neighbourhood
                    eps: 32,
                    // minimum weight of points required to form a cluster
                    minWeight: 2
                }
            });

            function reverseGeocode(location) {
                var geocoder = platform.getGeocodingService(),
                    reverseGeocodingParameters = {
                        prox: location.lat.toString() + ',' + location.lng.toString(),
                        mode: 'retrieveAddresses',
                        maxresults: '1',
                        jsonattributes: 1
                    };

                geocoder.reverseGeocode(
                    reverseGeocodingParameters,
                    reverseGeocodeSuccess,
                    reverseGeocodeError
                );
            }

            function reverseGeocodeSuccess(result) {
                const address = result.response.view[0].result;
                console.log(address)

                document.getElementById('bubbleDomTextArea').innerHTML = address[0].location.address.label
            }

            function reverseGeocodeError(error) {
                console.log(error)
            }

            // Note that we attach the event listener to the cluster provider, and not to
            // the individual markers
            clusteredDataProvider.addEventListener('tap', e => {
                // Get position of the "clicked" marker
                const position = e.target.getGeometry();

                const viewportX = (e.currentPointer.viewportX - 70).toString() + 'px'
                const viewportY = (e.currentPointer.viewportY - 160).toString() + 'px'

                const bubbleId = e.target.data.a.data;

                const bubbleDom = document.getElementById('bubbleDom')
                bubbleDom.style.position = 'fixed'
                bubbleDom.style.top = viewportY
                bubbleDom.style.left = viewportX
                bubbleDom.style.display = 'block'
                bubbleDom.style.zIndex = 2
                bubbleDom.style.backgroundColor = 'white'
                bubbleDom.style.fontSize = '13px'
                bubbleDom.style.width = '150px'

                reverseGeocode(position)

                document.getElementById('bubbleDomText').innerHTML =
                    'ID: ' + bubbleId + '<br/>'
                //   + 'Lat: ' + position.lat.toString() + '<br/>Lng' + position.lng.toString()
            }
            );


            // Create a layer tha will consume objects from our clustering provider
            var clusteringLayer = new H.map.layer.ObjectLayer(clusteredDataProvider);

            // To make objects from clustering provder visible,
            // we need to add our layer to the map
            map.addLayer(clusteringLayer);
        }

        if (data.length > 0) {
            startClustering(map, data);
        }

        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>

            <span id='headingDom' style={{
                position: 'fixed', top: '40px', left: '10px',
                zIndex: 2, fontSize: '13px'
            }}></span>

            <form style={{ position: 'fixed', top: '10px', right: '10px', zIndex: 2 }}>
                <input id='page1' name='page' type='radio' onClick={() => window.location.href = '/tab1'}></input>
                <label for="page1" style={{ fontSize: '13px' }}> page 1</label><br></br>
                <input id='page2' name='page' type='radio' onClick={() => window.location.href = '/tab2'}></input>
                <label for="page2" style={{ fontSize: '13px' }}> page 2</label><br></br>
                <input id='page3' name='page' type='radio' onClick={() => window.location.href = '/tab3'}></input>
                <label for="page3" style={{ fontSize: '13px' }}> page 3</label>
            </form>

            <div id='bubbleDom' style={{ display: 'none' }}>
                <span style={{ float: 'right', fontSize: '20px', cursor: 'pointer' }}
                    onClick={() => document.getElementById('bubbleDom').style.display = 'none'}>
                    X</span>

                <img src="https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcQleYrlNGWPV7IqQspqT71rj9Ht-gFTGRku4q7anMgFqymF-qDI&usqp=CAU"
                    style={{ marginLeft: '20px' }}
                    alt="house for sale" width="100" height="100"></img><br />

                <span id='bubbleDomText'></span>
                <textarea id='bubbleDomTextArea' rows='2' cols='20' wrap="hard"
                    style={{ resize: 'none', borderWidth: 0 }}></textarea>
            </div>
        </div>
    );

}

reference:
https://developer.here.com/documentation/examples/maps-js/services/reverse-geocode-an-address-from-location
http://chuanshuoge2.blogspot.com/2020/06/ionic-react-here-marker-cluster-2.html