Skip to content

Instantly share code, notes, and snippets.

@SimonJThompson
Last active November 30, 2021 19:18
Show Gist options
  • Save SimonJThompson/810c4f6495bc5016f6a2 to your computer and use it in GitHub Desktop.
Save SimonJThompson/810c4f6495bc5016f6a2 to your computer and use it in GitHub Desktop.
Google Maps Animated Flight Paths

This example shows our approach to animated flight paths using the Google Maps JavaScript API.

@charset "utf-8";
/*! normalize.css v1.1.0 | MIT License | git.io/normalize */
article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none;height:0}[hidden]{display:none}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}html,button,input,select,textarea{font-family:sans-serif}body{margin:0}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{font-size:2em;margin:.67em 0}h2{font-size:1.5em;margin:.83em 0}h3{font-size:1.17em;margin:1em 0}h4{font-size:1em;margin:1.33em 0}h5{font-size:.83em;margin:1.67em 0}h6{font-size:.67em;margin:2.33em 0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}blockquote{margin:1em 40px}dfn{font-style:italic}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}mark{background:#ff0;color:#000}p,pre{margin:1em 0}code,kbd,pre,samp{font-family:monospace,serif;_font-family:'courier new',monospace;font-size:1em}pre{white-space:pre;white-space:pre-wrap;word-wrap:break-word}q{quotes:none}q:before,q:after{content:'';content:none}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}dl,menu,ol,ul{margin:1em 0}dd{margin:0 0 0 40px}menu,ol,ul{padding:0 0 0 40px}nav ul,nav ol{list-style:none;list-style-image:none}img{border:0;-ms-interpolation-mode:bicubic}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0;white-space:normal;*margin-left:-7px}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;*overflow:visible}button[disabled],html input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;*height:13px;*width:13px}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0}
/* Nicer box sizing */
* {-webkit-box-sizing: border-box;-moz-box-sizing: border-box;-ms-box-sizing: border-box;-o-box-sizing: border-box;box-sizing: border-box;}
/* >> The Magnificent CLEARFIX << */
.clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
.clearfix { display: inline-block; }
* html .clearfix { height: 1%; } /* Hides from IE-mac \*/
.clearfix { display: block; }
html, body {
margin: 0;
padding: 0;
text-align: center;
font-family: 'Lato';
}
select {
border: 2px solid #ccc;
margin: 0;
padding: 4px;
width: 150px;
z-index: 2;
cursor: pointer;
outline: none;
border-radius: 4px;
}
.map {
position: absolute;
bottom: 0px;
left: 0px;
width: 100%;
height: 100%;
background: #fff;
z-index: 0;
}
.controls {
position: absolute;
top: 0px;
left: 0px;
width: 100%;
height: 60px;
line-height: 60px;
background: rgba(255, 255, 255, 0.8);
z-index: 1;
font-size: 1em;
}
a.go {
display: inline;
margin: 0 8px;
padding: 4px;
color: #2eacd0;
text-align: center;
cursor: pointer;
background: #fff;
border: 1px solid #ccc;
}
// Animation / interval Vars
var animLoop = false,
animIndex = 0,
planePath = false,
trailPath = false;
// Reference of city lat / long points
var places = {
"london" : [51.5072,0.1275],
"rome" : [41.9000,12.5000],
"frankfurt" : [50.1167,8.6833],
"athens" : [37.9667,23.7167],
"cairo" : [30.0500,31.2333],
"bangalore" : [12.9667,77.5667]
};
// Set up a google maps object with disabled user interaction (no zoom, no pan etc.)
function loadMap() {
var options = {
draggable: false,
panControl: false,
streetViewControl: false,
scrollwheel: false,
scaleControl: false,
disableDefaultUI: true,
disableDoubleClickZoom: true,
zoom: 4,
center: new google.maps.LatLng(51.5072,0.1275),
styles: [{"featureType":"administrative","stylers":[{"visibility":"off"}]},{"featureType":"poi","stylers":[{"visibility":"simplified"}]},{"featureType":"road","elementType":"labels","stylers":[{"visibility":"simplified"}]},{"featureType":"water","stylers":[{"visibility":"simplified"}]},{"featureType":"transit","stylers":[{"visibility":"simplified"}]},{"featureType":"landscape","stylers":[{"visibility":"simplified"}]},{"featureType":"road.highway","stylers":[{"visibility":"off"}]},{"featureType":"road.local","stylers":[{"visibility":"on"}]},{"featureType":"road.highway","elementType":"geometry","stylers":[{"visibility":"on"}]},{"featureType":"water","stylers":[{"color":"#84afa3"},{"lightness":52}]},{"stylers":[{"saturation":-17},{"gamma":0.36}]},{"featureType":"transit.line","elementType":"geometry","stylers":[{"color":"#3f518c"}]}]
};
mapObject = new google.maps.Map(document.getElementById('mapCanvas'), options);
}
// Plane Symbol - uses an SVG path
var planeSymbol = {
path: 'M22.1,15.1c0,0-1.4-1.3-3-3l0-1.9c0-0.2-0.2-0.4-0.4-0.4l-0.5,0c-0.2,0-0.4,0.2-0.4,0.4l0,0.7c-0.5-0.5-1.1-1.1-1.6-1.6l0-1.5c0-0.2-0.2-0.4-0.4-0.4l-0.4,0c-0.2,0-0.4,0.2-0.4,0.4l0,0.3c-1-0.9-1.8-1.7-2-1.9c-0.3-0.2-0.5-0.3-0.6-0.4l-0.3-3.8c0-0.2-0.3-0.9-1.1-0.9c-0.8,0-1.1,0.8-1.1,0.9L9.7,6.1C9.5,6.2,9.4,6.3,9.2,6.4c-0.3,0.2-1,0.9-2,1.9l0-0.3c0-0.2-0.2-0.4-0.4-0.4l-0.4,0C6.2,7.5,6,7.7,6,7.9l0,1.5c-0.5,0.5-1.1,1-1.6,1.6l0-0.7c0-0.2-0.2-0.4-0.4-0.4l-0.5,0c-0.2,0-0.4,0.2-0.4,0.4l0,1.9c-1.7,1.6-3,3-3,3c0,0.1,0,1.2,0,1.2s0.2,0.4,0.5,0.4s4.6-4.4,7.8-4.7c0.7,0,1.1-0.1,1.4,0l0.3,5.8l-2.5,2.2c0,0-0.2,1.1,0,1.1c0.2,0.1,0.6,0,0.7-0.2c0.1-0.2,0.6-0.2,1.4-0.4c0.2,0,0.4-0.1,0.5-0.2c0.1,0.2,0.2,0.4,0.7,0.4c0.5,0,0.6-0.2,0.7-0.4c0.1,0.1,0.3,0.1,0.5,0.2c0.8,0.2,1.3,0.2,1.4,0.4c0.1,0.2,0.6,0.3,0.7,0.2c0.2-0.1,0-1.1,0-1.1l-2.5-2.2l0.3-5.7c0.3-0.3,0.7-0.1,1.6-0.1c3.3,0.3,7.6,4.7,7.8,4.7c0.3,0,0.5-0.4,0.5-0.4S22,15.3,22.1,15.1z',
fillColor: '#000',
fillOpacity: 1.5,
scale: 0.8,
anchor: new google.maps.Point(11, 11),
strokeWeight: 0
};
function animate(startPoint, endPoint) {
startPoint = places[startPoint],
endPoint = places[endPoint];
// Convert the points arrays into Lat / Lng objects
var sP = new google.maps.LatLng(startPoint[0],startPoint[1]);
var eP = new google.maps.LatLng(endPoint[0],endPoint[1]);
// Create a polyline for the planes path
planePath = new google.maps.Polyline({
path: [sP, eP],
strokeColor: '#0f0',
strokeWeight: 0,
icons: [{
icon: planeSymbol,
offset: '0%'
}],
map: mapObject,
geodesic: true
});
trailPath = new google.maps.Polyline({
path: [sP, sP],
strokeColor: '#2eacd0',
strokeWeight: 2,
map: mapObject,
geodesic: true
});
// Start the animation loop
animLoop = window.requestAnimationFrame(function(){
tick(sP, eP);
});
}
/*
Runs each animation "tick"
*/
function tick(startPoint, endPoint) {
animIndex+=0.2;
// Draw trail
var nextPoint = google.maps.geometry.spherical.interpolate(startPoint,endPoint,animIndex/100);
trailPath.setPath([startPoint, nextPoint]);
// Move the plane
planePath.icons[0].offset=Math.min(animIndex,100)+'%';
planePath.setPath(planePath.getPath());
// Ensure the plane is in the center of the screen
mapObject.panTo(nextPoint);
// We've reached the end, clear animLoop
if(animIndex>=100) {
window.cancelAnimationFrame(animLoop);
animIndex = 0;
}else{
animLoop = window.requestAnimationFrame(function(){
tick(startPoint, endPoint);
});
}
}
// Get values from select boxes, run the animation.
function go() {
window.cancelAnimationFrame(animLoop);
animIndex = 0;
animate(
document.getElementById('s_from').options[document.getElementById('s_from').selectedIndex].value,
document.getElementById('s_to').options[document.getElementById('s_to').selectedIndex].value
);
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="flightpath.css" />
<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false&libraries=geometry"></script>
<script src="flightpath.js"></script>
</head>
<body>
<div class="controls">
<select id="s_from" onChange="go();">
<option value="london">London</option>
<option value="rome">Rome</option>
<option value="frankfurt">Frankfurt</option>
</select> to
<select id="s_to" onChange="go();">
<option value="athens">Athens</option>
<option value="cairo">Cairo</option>
<option value="bangalore">Bangalore</option>
</select>
</div>
<div id="mapCanvas" class="map"></div>
<script type="text/javascript">loadMap();</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment