Duncan's blog

October 16, 2017

Animated Google Maps paths from a .csv file

Filed under: Google Maps,Javascript,jQuery — duncan @ 10:06 pm
Tags: ,

I got this question on one of my previous posts, Animated paths with Google Maps, from Shivani Arora, which I thought needed its own blog post to fully answer it:

“I am working on a project in which I have to plot a moving path on the map using the coordinates. I have more than 76000 coordinates. So I tried your second example with source and destination. With that code I am getting the path, but it is taking the shorter route to reach the destination.

In my project I need to show a particular moving path, but not the short route.
Is there any way by which we can do that? Also, I have a .csv file for my coordinates. Can you show a code by which I can access the coordinates from a .csv file using your code?”

So, there’s two parts here.  Firstly how to deal with lots of coordinates.  And secondly how to read them from a .csv file.

For the first part, I’d say the first method from my previous post is the better solution, than using the Directions Service.  Using the Service saves you from having to store all the coordinates yourself, provided you can rely on Google’s directions matching what you need.

But if you’ve already got all the coordinates sorted, and/or you need to plot your own path rather than be able to rely on the Directions Service, then just use your own coordinates entirely.

In my example I’d hardcoded something like 300 coordinates into the javascript in the page.  More likely you should use something server-side to take care of that for you, perhaps reading them from a database.

However if you’ve got them all in a .csv file, you could just use javascript to read that for you.  Here I’m just making an AJAX request to read the file directly, and then splitting it on line endings and commas to get the data into a giant 2D array.

So firstly just load the map, then call the getCoords function.

function initialize() {
	map = new google.maps.Map(document.getElementById("map"), {
	  center: {lat: 51.4788639, lng: -0.0062897},
	  zoom: 3,
	  mapTypeId: google.maps.MapTypeId.ROADMAP
	});
	
	getCoords();
}

Secondly, the getCoords function. I’m using jQuery to make an ajax request directly to the .csv file. Just setting the dataType to text, doesn’t seem to be any problem.

function getCoords() {
	$.ajax({
		url: 'coordinates4.csv',
		dataType: 'text',
		success: function(data) {
			var lines = data.split(/[\r\n]+/);
									
			lines.forEach(function(line){
				allCoords.push(line.split(','));
			});

			var bounds = new google.maps.LatLngBounds();

			allCoords.forEach(function(coords){
				bounds.extend(new google.maps.LatLng(coords[0], coords[1]));
			});
			
			map.fitBounds(bounds);
									
			drawLine();
		}
	});
}

My CSV file contents just look like:

8.52411,76.93534000000001
8.524170000000002,76.93523
8.52424,76.93537

So I use the String.split() function to split it into an array based on line endings.
I then loop over that array, and split again, to separate out the latitude and longitude.
At this point I’m also creating a Bounds object, and extending it with each of the coordinates. This is just so the map will be fully zoomed to a level where we can see all our path at once. I’ve differed from my previous example, in that I’m not constantly updating the centre of the map here.

Once the coordinates are loaded, I call drawLine:

function drawLine() {
	route = new google.maps.Polyline({
		path: [],
		geodesic : true,
		strokeColor: '#FF0000',
		strokeOpacity: 0.7,
		strokeWeight: 2,
		editable: false,
		map:map
	});
	
	marker = new google.maps.Marker({
		map: map,
		icon: "http://maps.google.com/mapfiles/ms/micons/blue.png"
	});
	
	for (var i = 0; i < allCoords.length; i++) {
		window.setTimeout(updatePath(coords), 100 * i, allCoords[i]);
	}
}

function updatePath(coords) {
	var latLng = new google.maps.LatLng(coords[0], coords[1]);
	route.getPath().push(latLng);
	marker.setPosition(latLng);
}

So much like before; starting out with an empty Polyline. Looping over all my coordinates, making a call to the updatePath function every 100 milliseconds. Or rather, I simultaneously setup thousands of calls to the function, to take place in 100ms, 200ms, 300ms, etc.

And that’s it! I tested it initially with a few hundred coordinates. I then wrote a little script to generate 70,000 slightly random coordinates. The file’s quite large, 2.5Mb, which might take a few seconds to read via ajax. And that’s a lot of coordinates to animate on the map. I’m adding a new point to the line every 100 milliseconds, which seems pretty fast, but it means it would still take nearly 2 hours to plot all the points. It also seemed to get slightly slower and jerkier the longer it went on.

For the record, here’s the script I used to generate all those coordinates. It’s not very sophisticated, and you could get something faster in any server-side language, but I just use javascript to dump it to a textarea where I then copy it to a text editor.

function getCoords() {
	var max = 70000;
	var start = {lat: 0, lng: 0};
	
	coords = start.lat + ',' + start.lng;
	
	var lat = start.lat;
	var lng = start.lng;
	var randomLat,randomLng, newLat, newLng;
	var plusOrMinusLat = 1;
	
	var maxLat = 85;
	var minLat = -85;
		
	for (var i = 0; i < max; i++) {
		randomLat = Math.random();
		randomLng = Math.random();
		
		newLat = lat + (randomLat * plusOrMinusLat);
		newLng = lng + randomLng;
		
		if (newLat > maxLat) {
			plusOrMinusLat = -1;
			continue;
		} else if (newLat < minLat) {
			plusOrMinusLat = 1;
			continue;
		}
		
		lat = newLat;
		lng = newLng;
		coords += '\n' + lat + ',' + lng;
	}
		
	document.getElementById('coords').value = coords;
}

If the latitude exceeds 90 or -90 degrees, the map stops working. So I check we don’t exceed that limit. I actually reduced that to 85 / -85 degrees; anything beyond that goes off the map with Google Maps. I don’t need to do anything similar with the longitude; if you go beyond 360 degrees, e.g. 365 degrees, it just wraps back round to 5 degrees.

Initially I was just picking random numbers either randomly added or subtracted from the previous coordinates, but that ended up like a giant scribble. So I deliberately just keep adding until I reach the upper limit, then just keep subtracting until I reach the lower limit. The end result is a slightly squiggly pattern like this:

random map

I’d be curious to see how Shivani’s map ends up!

Update – Here’s the map I ended up with using Shivani’s coordinates:
Chrome Legacy Window 24102017 230220

28 Comments »

  1. Animated route

    html, body, #map {
    height: 100%;
    margin: 0px;
    padding: 0px
    }

    https://maps.googleapis.com/maps/api/js?libraries=places&key=AIzaSyAbeZY_drCHzHUoWYl6EMgMdZsJxaVt3pk

    function initialize()
    {
    var map = new google.maps.Map(document.getElementById(“map”),
    {
    center: {lat: 51.5087531, lng: -0.0062897},
    zoom: 3,
    mapTypeId: google.maps.MapTypeId.ROADMAP
    });

    getCoords();
    }

    function getCoords()
    {
    $.ajax({
    url: ‘coordinates4.csv’,
    dataType: ‘text’,
    success: function(data)
    {
    var lines = data.split(/[\r\n]+/);

    lines.forEach(function(line)
    {
    allCoords.push(line.split(‘,’));
    });

    var bounds = new google.maps.LatLngBounds();

    allCoords.forEach(function(coords)
    {
    bounds.extend(new google.maps.LatLng(coords[0], coords[1]));
    });

    map.fitBounds(bounds);

    drawLine();
    }
    });
    }

    function drawLine()
    {
    route = new google.maps.Polyline
    ({
    path: [],
    geodesic : true,
    strokeColor: ‘#FF0000’,
    strokeOpacity: 0.7,
    strokeWeight: 2,
    editable: false,
    map:map
    });

    marker = new google.maps.Marker
    ({
    map: map,
    icon: “http://maps.google.com/mapfiles/ms/micons/blue.png”
    });

    for (var i = 0; i < allCoords.length; i++)
    {
    window.setTimeout(updatePath(coords), 100 * i, allCoords[i]);
    }
    }

    function updatePath(coords)
    {
    var latLng = new google.maps.LatLng(coords[0], coords[1]);
    route.getPath().push(latLng);
    marker.setPosition(latLng);
    }

    Comment by Shivani Arora — October 17, 2017 @ 12:52 am | Reply

  2. Hello Duncan, the previous mistake was a mistake. I was trying to share my code with you. I tried the code which you have given in this explaination but I am not getting any map generated on chrome. There is no error in console but I am getting a blank page. I have shared my code on this link-: https://gist.github.com/sarora7/4a7c58e31f6c1e64f4905b27c8c032d2. This link also contains my .csv file with 76000+ data.
    Can you please check it and let me know. My .csv file is the MAMP folder from which I’m running my code.

    Comment by Shivani Arora — October 17, 2017 @ 1:03 am | Reply

    • Hi Shivani. Make sure you load the jQuery library too, and update the `url` attribute to point to your gistfile3.txt file. Also either remove the `lat,lon` line from the start of that file, or amend the JS code to skip over the first line.

      Comment by Duncan — October 17, 2017 @ 9:22 am | Reply

      • Hi Dunan I have added the jQuery library and have chnaged the file name, but now the im getting error saying map.fitBounds is not a function.

        Comment by Shivani Arora — October 17, 2017 @ 8:02 pm

  3. Is it possible for you to share your full code on github or some other platform?

    Comment by Shivani Arora — October 17, 2017 @ 8:05 pm | Reply

  4. Ok, I have tried your code with another way. This time without changing anything other than file name from coordinates4.csv to original_toyota.csv and I replaced the your coordinates with my coordinates. Now I’m getting 3 erros-:

    Uncaught RangeError: Maximum call stack size exceeded —-> js?v=3.exp:41
    Uncaught RangeError: Maximum call stack size exceeded —-> js?v=3.exp:53
    Uncaught RangeError: Maximum call stack size exceeded —-> map.js:18

    Comment by Shivani Arora — October 17, 2017 @ 11:16 pm | Reply

  5. Hi Dunca, I have updated my code again and shared on github. This is the link for it-: https://github.com/sarora7/csvgoogle/tree/master/shivanicsv
    Can you please look into it. With your data file the code runs perfectly, but with my data file I get few errors.

    Comment by Shivani Arora — October 24, 2017 @ 5:25 pm | Reply

    • Delete the empty line from the end of the CSV file, then everything works fine.

      Comment by duncan — October 24, 2017 @ 9:39 pm | Reply

  6. * Hi Duncan….sorry for the typo

    Comment by Shivani Arora — October 24, 2017 @ 5:26 pm | Reply

  7. Hi Duncan, Thanks a lot!! You are awesome! I get the same map as you have updated in your post. It works perfectly now. I’m sorry for bothering you again and again with my silly mistakes. Really appreciate for your time and help. 🙂

    Comment by Shivani Arora — October 26, 2017 @ 8:42 pm | Reply

  8. Also, I wanted to ask that what if we don’t use bound function for the coordinates and instead increase the zoom value? Is that option fine too? As with the current bound function I still need to zoom-in the map. The other thing is that how can we increase the speed of the plotting of points? Since I have thousands and lakh of coordinates?

    Comment by Shivani Arora — October 26, 2017 @ 8:45 pm | Reply

    • Yeah you can certainly leave off the bounds function, whatever suits you really. Just set the initial zoom property on the map to whatever works best.

      And to increase the speed, just amend the value in the line:

      window.setTimeout(updatePath(coords), 100 * i, allCoords[i]);

      Decrease that from 100 to perhaps just 5, and you’ll find it much better!

      Comment by duncan — October 28, 2017 @ 3:10 pm | Reply

      • That works! Thanks once again 🙂

        Comment by Shivani Arora — October 30, 2017 @ 8:01 pm

  9. I was wondering that if we need to pause, continue and stop the moving path on the map then how is that possible? I have tried to stop the path but that is clearing the whole path. What if I want the path covered to remain until I stop it?

    Comment by Shivani Arora — October 30, 2017 @ 8:05 pm | Reply

  10. I have posted my doubt here-: https://stackoverflow.com/questions/47024103/how-to-pause-resume-and-restart-the-google-path-using-javascript. Can you pls go through it.

    Comment by Shivani Arora — October 30, 2017 @ 10:20 pm | Reply

  11. I have updated my code and posted it here. In that I am trying to do mouseover on the google map path and also to resume the path after pausing it. have posted my question here stackoverflow.com/questions/47082870/…. Can you please go through it. Thanks a lot!

    Comment by Shivani Arora — November 2, 2017 @ 10:02 pm | Reply

  12. @duncan I have updated my question for resume and onmouseover on this link stackoverflow.com/questions/47168445/…. Can you please check? Thanks a lot in advance!

    Comment by Shivani Arora — November 7, 2017 @ 10:28 pm | Reply

  13. @duncan Hi, can you please let me know where I am going wrong?

    Comment by Shivani Arora — November 8, 2017 @ 4:38 pm | Reply

  14. Hi Duncan,

    Really nice solution. I am working on a similar problem but I have to plot paths of around 500+ couriers from a csv file and animate them simultaneously. Kind of a timelapse for the day’s activities. Can you help with multiple paths starting from single csv?

    Comment by usman — November 23, 2018 @ 6:54 am | Reply

    • Hi Usman, sure, that shouldn’t be a problem. What do the contents of the csv file look like?

      Comment by duncan — November 23, 2018 @ 7:51 am | Reply

      • The CSV will have (latitude, longitude, courier id, delivery(yes/no), delivery time). If at certain point courier makes a delivery, a pin will be shown there else the path continues. Thanks alot for replying.

        Comment by usman — December 10, 2018 @ 7:46 am

      • Hi Duncan,

        Did you got chance to work on the issue I referred? Waiting for you 🙂

        Comment by usman — December 18, 2018 @ 8:05 am

      • Not yet, sorry!

        Comment by duncan — December 18, 2018 @ 8:30 am


RSS feed for comments on this post. TrackBack URI

Leave a comment

Create a free website or blog at WordPress.com.