-2

I have this map https://jsfiddle.net/e80zkacn/ with an icon of car. At 0 degrees the icon of the car faces north but faces to where its headed when i rotate it at 270 degrees. I want the car to always face the correct position using the bearing angle.

The formula i am using is atan2 as described here https://www.igismap.com/formula-to-find-bearing-or-heading-angle-between-two-points-latitude-longitude/

This is the code

    var map, marker;
var startPos = [42.42679066670903, -83.29210638999939];
var speed = 50; // km/h
var bearing = 2.6;

var symbol = {  
    path: "M62.1,36.5c-0.9-1.2-3.6-1.5-3.6-1.5c0.1-3.5,0.5-6.9,0.7-8.2c1.9-7.3-1.7-11.8-1.7-11.8c-4.8-4.8-9.1-5-12.5-5   c-3.4,0-7.8,0.3-12.5,5c0,0-3.6,4.5-1.7,11.8c0.2,1.2,0.5,4.6,0.7,8.2c0,0-2.7,0.3-3.6,1.5c-0.9,1.2-0.9,1.9,0,1.9   c0.9,0,2.9-2.3,3.6-2.3V35c0,1,0.1,2,0.1,3c0,4.4,0,33.7,0,33.7s-0.3,6.1,5,7.8c1.2,0,4.6,0.4,8.4,0.5c3.8-0.1,7.3-0.5,8.4-0.5   c5.3-1.7,5-7.8,5-7.8s0-29.3,0-33.7c0-1,0-2,0.1-3v1.2c0.7,0,2.7,2.3,3.6,2.3C63,38.5,63,37.7,62.1,36.5z M34.7,66.5   c-0.3,3.3-2.3,4.1-2.3,4.1V37.4c0.8,1.2,2.3,6.8,2.3,6.8S34.9,63.2,34.7,66.5z M54.8,75.2c0,0-4.2,2.3-9.8,2.2   c-5.6,0.1-9.8-2.2-9.8-2.2v-2.8c4.9,2.2,9.8,2.2,9.8,2.2s4.9,0,9.8-2.2V75.2z M35.2,41.1l-1.7-10.2c0,0,4.5-3.2,11.5-3.2   s11.5,3.2,11.5,3.2l-1.7,10.2C51.4,39.2,38.6,39.2,35.2,41.1z M57.7,70.6c0,0-2.1-0.8-2.3-4.1c-0.3-3.3,0-22.4,0-22.4   s1.5-5.6,2.3-6.8V70.6z",
    fillColor: '#FF0000',
    fillOpacity: .5,
    anchor: new google.maps.Point(0, 0),
    strokeWeight: 1,
    scale: .5,
    rotation: bearing  
}

var delay = 100;
// If you set the delay below 1000ms and you go to another tab,
// the setTimeout function will wait to be the active tab again
// before running the code.
// See documentation :
// https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout#Inactive_tabs

function animateMarker(marker, coords, km_h)
{
    var target = 0;
    var km_h = km_h || 50;
    coords.push([startPos[0], startPos[1]]);

    function goToPoint()
    {

    function radians_to_degrees(radians)
{
  var pi = Math.PI;
  return radians * (180/pi);
}

        var lat = marker.position.lat();
        var lng = marker.position.lng();
        var step = (km_h * 1000 * delay) / 3600000; // in meters

        var dest = new google.maps.LatLng(
        coords[target][0], coords[target][1]);

        var distance =
        google.maps.geometry.spherical.computeDistanceBetween(
        dest, marker.position); // in meters

        var numStep = distance / step;
        var i = 0;
        var deltaLat = (coords[target][0] - lat) / numStep;
        var deltaLng = (coords[target][1] - lng) / numStep;

        function the_bearing(){
        //console.log('x', lat);
        //console.log('y', lng);
        let bear = Math.atan2(lat, lng);

       var brng = radians_to_degrees(bear);
       brng = (brng + 360) % 360;

        console.log('angle is ', brng);
        }   
        the_bearing();


        function moveMarker()
        {
            lat += deltaLat;
            lng += deltaLng;
            i += step;

            if (i < distance)
            {
                marker.setPosition(new google.maps.LatLng(lat, lng));
                setTimeout(moveMarker, delay);
            }
            else
            {   marker.setPosition(dest);
                target++;
                if (target == coords.length){ target = 0; }

                setTimeout(goToPoint, delay);
            }
        }
        moveMarker();   

    }
    goToPoint();
}

function initialize()
{
    var myOptions = {
        zoom: 16,
        center: new google.maps.LatLng(42.425175091823974, -83.2943058013916),
        mapTypeId: google.maps.MapTypeId.ROADMAP
    };
    map = new google.maps.Map(document.getElementById("map"), myOptions);
    var icon = symbol;

    marker = new google.maps.Marker({
        position: new google.maps.LatLng(startPos[0], startPos[1]),
        map: map,
        icon: icon
    });

    google.maps.event.addListenerOnce(map, 'idle', function()
    {
        animateMarker(marker, [
            // The coordinates of each point you want the marker to go to.
            // You don't need to specify the starting position again.
            [42.42666395645802, -83.29694509506226],
            [42.42300508749226, -83.29679489135742],
            [42.42304468678425, -83.29434871673584],
            [42.424882066428424, -83.2944130897522],
            [42.42495334300206, -83.29203128814697]
        ], speed);
    });
}

initialize();

Currently the results of atan2 are being converted to degrees and i am doubting my method. How can i make the svg icon to always face where its going and is there a reliable method i can use to always make the car icon look towards the direction its heading?.

Gandalf
  • 1
  • 29
  • 94
  • 165
  • possible duplicate of [Change Google map marker orientation according path direction](https://stackoverflow.com/questions/23149613/change-google-map-marker-orientation-according-path-direction) – geocodezip May 27 '19 at 13:14
  • @geocodezip mine doesnt have a polyline, but the question you linked is somewhat related but not exactly related. – Gandalf May 27 '19 at 13:17

1 Answers1

3

The geometry library for Google Maps' JavaScript API provides a useful method called google.maps.geometry.spherical.computeHeading(p1, p2) that exactly computes the heading from point1 to point2.

I took the liberty to modify your code a bit. Here's a jsfiddle.

Be careful to correctly anchor you icon, the anchor was at (0, 0) which made the car move outside of the roads. I tried to change it so that it looks more natural.

<!Doctype html>
<meta charset="UTF-8">
<head>
<style>
#map {
    width: 600px;
    height: 400px;
}
</style>

</head>
<body>
<div id="map"></div>
<script src="https://code.jquery.com/jquery-3.4.1.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBx6MYRybsImp6aSn4xPphPFlGF3IaOr88&libraries=geometry"></script>
<script type="text/javascript">
var map, marker;
var startPos = [42.42679066670903, -83.29210638999939];
var speed = 270; // km/h
var bearing = 270;

var symbol = {  
    path: "M62.1,36.5c-0.9-1.2-3.6-1.5-3.6-1.5c0.1-3.5,0.5-6.9,0.7-8.2c1.9-7.3-1.7-11.8-1.7-11.8c-4.8-4.8-9.1-5-12.5-5   c-3.4,0-7.8,0.3-12.5,5c0,0-3.6,4.5-1.7,11.8c0.2,1.2,0.5,4.6,0.7,8.2c0,0-2.7,0.3-3.6,1.5c-0.9,1.2-0.9,1.9,0,1.9   c0.9,0,2.9-2.3,3.6-2.3V35c0,1,0.1,2,0.1,3c0,4.4,0,33.7,0,33.7s-0.3,6.1,5,7.8c1.2,0,4.6,0.4,8.4,0.5c3.8-0.1,7.3-0.5,8.4-0.5   c5.3-1.7,5-7.8,5-7.8s0-29.3,0-33.7c0-1,0-2,0.1-3v1.2c0.7,0,2.7,2.3,3.6,2.3C63,38.5,63,37.7,62.1,36.5z M34.7,66.5   c-0.3,3.3-2.3,4.1-2.3,4.1V37.4c0.8,1.2,2.3,6.8,2.3,6.8S34.9,63.2,34.7,66.5z M54.8,75.2c0,0-4.2,2.3-9.8,2.2   c-5.6,0.1-9.8-2.2-9.8-2.2v-2.8c4.9,2.2,9.8,2.2,9.8,2.2s4.9,0,9.8-2.2V75.2z M35.2,41.1l-1.7-10.2c0,0,4.5-3.2,11.5-3.2   s11.5,3.2,11.5,3.2l-1.7,10.2C51.4,39.2,38.6,39.2,35.2,41.1z M57.7,70.6c0,0-2.1-0.8-2.3-4.1c-0.3-3.3,0-22.4,0-22.4   s1.5-5.6,2.3-6.8V70.6z",
    fillColor: '#FF0000',
    fillOpacity: .5,
    anchor: new google.maps.Point(45, 45),
    strokeWeight: 1,
    scale: .5,
    rotation: bearing  
}

var delay = 100;
// If you set the delay below 1000ms and you go to another tab,
// the setTimeout function will wait to be the active tab again
// before running the code.
// See documentation :
// https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout#Inactive_tabs

function animateMarker(marker, coords, km_h)
{
    var target = 0;
    var km_h = km_h || 50;
    coords.push([startPos[0], startPos[1]]);
    console.log(coords);

    function goToPoint()
    {
        var lastPosn = marker.getPosition();
        var lat = marker.position.lat();
        var lng = marker.position.lng();
        var step = (km_h * 1000 * delay) / 3600000; // in meters

        var start = new google.maps.LatLng(startPos[0], startPos[1]);
        var dest = new google.maps.LatLng(coords[target][0], coords[target][1]);

        var distance = google.maps.geometry.spherical.computeDistanceBetween(dest, marker.position); // in meters

        var numStep = distance / step;
        var i = 0;
        var deltaLat = (coords[target][0] - lat) / numStep;
        var deltaLng = (coords[target][1] - lng) / numStep;

        function moveMarker()
        {
            lat += deltaLat;
            lng += deltaLng;
            i += step;

            if (i < distance)
            {
                var p = new google.maps.LatLng(lat, lng);
                marker.setPosition(p);

                var heading = google.maps.geometry.spherical.computeHeading(lastPosn, p);
                symbol.rotation = heading;
                marker.setIcon(symbol);

                setTimeout(moveMarker, delay);
            }
            else
            {   
                marker.setPosition(dest);

                var heading = google.maps.geometry.spherical.computeHeading(lastPosn, dest);
                symbol.rotation = heading;
                marker.setIcon(symbol);

                target++;
                if (target == coords.length){ target = 0; }

                setTimeout(goToPoint, delay);
            }
        }

        moveMarker();
    }
    goToPoint();
}

function initialize()
{
    var myOptions = {
        zoom: 16,
        center: new google.maps.LatLng(42.425175091823974, -83.2943058013916),
        mapTypeId: google.maps.MapTypeId.ROADMAP
    };
    map = new google.maps.Map(document.getElementById("map"), myOptions);

    marker = new google.maps.Marker({
        position: new google.maps.LatLng(startPos[0], startPos[1]),
        map: map,
        icon: symbol
    });

    google.maps.event.addListenerOnce(map, 'idle', function()
    {
        animateMarker(marker, [
            // The coordinates of each point you want the marker to go to.
            // You don't need to specify the starting position again.
            [42.42666395645802, -83.29694509506226],
            [42.42300508749226, -83.29679489135742],
            [42.42304468678425, -83.29434871673584],
            [42.424882066428424, -83.2944130897522],
            [42.42495334300206, -83.29203128814697]
        ], speed);
    });
}

initialize();
</script>
</body>
</html>

I think some parts of the code could use other methods from the API, for instance the computation of deltaLat and deltaLng, but I'm not sure.

I'm also not certain that the moveMarker() method has to be split in two conditional cases, but I may be wrong.

salixor
  • 112
  • 1
  • 6