This example shows our approach to animated flight paths using the Google Maps JavaScript API.
Last active
November 30, 2021 19:18
-
-
Save SimonJThompson/810c4f6495bc5016f6a2 to your computer and use it in GitHub Desktop.
Google Maps Animated Flight Paths
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 | |
); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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