Skip to content

Instantly share code, notes, and snippets.

@bradoyler
Last active April 29, 2023 19:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save bradoyler/d2725b17d38239e7de49c39ebdeae94f to your computer and use it in GitHub Desktop.
Save bradoyler/d2725b17d38239e7de49c39ebdeae94f to your computer and use it in GitHub Desktop.
US Flight animation (svg)
license: mit
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.2/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/3.0.2/topojson.min.js"></script>
<script src="routes.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
.states {
fill: #696969;
stroke: #6cb0e0;
stroke-width: 0.5px;
stroke-linecap: round;
stroke-linejoin: round;
}
.airports {
fill: #000;
stroke: transparent;
stroke-width: 0.5px;
stroke-linecap: round;
stroke-linejoin: round;
}
.route {
fill: none;
stroke-width: 0;
}
.plane {
fill: #fff;
stroke-width: 0;
}
</style>
</head>
<body>
<div id="flightMap"></div>
<script>
function initMap(selector) {
const $flightmap = $(selector)
if (!$flightmap) return
const width = 720
const height = 512
const pro = d3.geoAlbersUsa()
.scale(900)
.translate([width / 2, height / 2])
const path = d3.geoPath()
.pointRadius(1.2)
.projection(pro)
const svg = d3.select(selector)
.append('svg')
.attr('preserveAspectRatio', 'xMidYMid')
.attr('viewBox', '0 0 ' + width + ' ' + height)
.attr('width', width)
.attr('height', height)
.attr('class', 'intrinsic__element')
const airportMap = {}
function transition (plane, route) {
const l = route.node().getTotalLength()
plane.transition()
.duration(l * 20)
.attr('style', 'display:block')
.attrTween('transform', delta(plane, route.node()))
.on('end', () => { route.remove() })
.remove()
}
function delta (plane, path) {
const l = path.getTotalLength()
return function (i) {
return function (t) {
const p = path.getPointAtLength(t * l)
const t2 = Math.min(t + 0.05, 1)
const p2 = path.getPointAtLength(t2 * l)
const x = p2.x - p.x
const y = p2.y - p.y
const r = 90 - Math.atan2(-y, x) * 180 / Math.PI
const s = Math.min(Math.sin(Math.PI * t) * 0.7, 0.3)
return 'translate(' + p.x + ',' + p.y + ') scale(' + s + ') rotate(' + r + ')'
}
}
}
function fly (origin, destination) {
if (!airportMap[origin] || !airportMap[destination]) return
const route = svg.append('path')
.datum({type: 'LineString', coordinates: [airportMap[origin], airportMap[destination]]})
.attr('class', 'route')
.attr('d', path)
const plane = svg.append('path')
.attr('class', 'plane')
.attr('d', 'm25.21488,3.93375c-0.44355,0 -0.84275,0.18332 -1.17933,0.51592c-0.33397,0.33267 -0.61055,0.80884 -0.84275,1.40377c-0.45922,1.18911 -0.74362,2.85964 -0.89755,4.86085c-0.15655,1.99729 -0.18263,4.32223 -0.11741,6.81118c-5.51835,2.26427 -16.7116,6.93857 -17.60916,7.98223c-1.19759,1.38937 -0.81143,2.98095 -0.32874,4.03902l18.39971,-3.74549c0.38616,4.88048 0.94192,9.7138 1.42461,13.50099c-1.80032,0.52703 -5.1609,1.56679 -5.85232,2.21255c-0.95496,0.88711 -0.95496,3.75718 -0.95496,3.75718l7.53,-0.61316c0.17743,1.23545 0.28701,1.95767 0.28701,1.95767l0.01304,0.06557l0.06002,0l0.13829,0l0.0574,0l0.01043,-0.06557c0,0 0.11218,-0.72222 0.28961,-1.95767l7.53164,0.61316c0,0 0,-2.87006 -0.95496,-3.75718c-0.69044,-0.64577 -4.05363,-1.68813 -5.85133,-2.21516c0.48009,-3.77545 1.03061,-8.58921 1.42198,-13.45404l18.18207,3.70115c0.48009,-1.05806 0.86881,-2.64965 -0.32617,-4.03902c-0.88969,-1.03062 -11.81147,-5.60054 -17.39409,-7.89352c0.06524,-2.52287 0.04175,-4.88024 -0.1148,-6.89989l0,-0.00476c-0.15655,-1.99844 -0.44094,-3.6683 -0.90277,-4.8561c-0.22699,-0.59493 -0.50356,-1.07111 -0.83754,-1.40377c-0.33658,-0.3326 -0.73578,-0.51592 -1.18194,-0.51592l0,0l-0.00001,0l0,0z')
transition(plane, route)
}
function ready (error, topo, airports) {
if (error) throw error
svg.append('g')
.attr('class', 'states')
.selectAll('path')
.data(topojson.feature(topo, topo.objects.states).features)
.enter()
.append('path')
.attr('d', path)
svg.append('g')
.attr('class', 'airports')
.selectAll('path')
.data(topojson.feature(airports, airports.objects.airports).features)
.enter()
.append('path')
.attr('id', (d) => d.id)
.attr('d', path)
const geos = topojson.feature(airports, airports.objects.airports).features
geos.forEach(function (geo) {
airportMap[geo.id] = geo.geometry.coordinates
})
let start = 0
function flyBatch (flightCnt = 30) {
if ($flightmap.offset().top + $flightmap.height() < $(window).scrollTop()) return
let end = start + flightCnt
const pairs = routes.slice(start, end)
pairs.forEach((pair, idx) => {
fly(pair[0], pair[1])
})
start = end
// reset the flight loop
if (pairs.length === 0) {
start = 0
}
}
flyBatch(200)
setInterval(flyBatch, 300)
}
d3.queue()
.defer(d3.json, 'https://nodeassets.nbcnews.com/cdnassets/projects/2017/08/airplane-mode/us-states.json')
.defer(d3.json, 'https://nodeassets.nbcnews.com/cdnassets/projects/2017/08/airplane-mode/us-airports-major.topo02.json')
.await(ready)
}
initMap('#flightMap')
</script>
</body>
window.routes = [
['LAS', 'MSY'],
['SPI', 'IAD'],
['MSP', 'IND'],
['CLT', 'DAB'],
['CLE', 'PVD'],
['CLE', 'DTW'],
['SYR', 'ATL'],
['MSY', 'DFW'],
['ORD', 'CAE'],
['IAH', 'LAS'],
['ATL', 'SAN'],
['MSP', 'FNT'],
['PIR', 'JAX'],
['BOS', 'MYR'],
['CVG', 'MDT'],
['SYR', 'MCO'],
['DFW', 'MSN'],
['DFW', 'IAH'],
['PNS', 'ATL'],
['VPS', 'CVG'],
['CVG', 'MCO'],
['CLT', 'CVG'],
['BWI', 'ABQ'],
['BFL', 'FAT'],
['MFR', 'RDM'],
['GSP', 'ORD'],
['IAH', 'PDX'],
['CLE', 'ROC'],
['IAH', 'TYS'],
['BOI', 'PHX'],
['ATL', 'PBI'],
['DSM', 'PHX'],
['DEN', 'SEA'],
['LAS', 'FNT'],
['IAD', 'DAY'],
['SAT', 'MCI'],
['KOA', 'LIH'],
['BUR', 'RNO'],
['MEM', 'MLI'],
['PHL', 'STT'],
['ORF', 'DTW'],
['IND', 'BOS'],
['ATL', 'MOB'],
['ATL', 'DTW'],
['CMH', 'CLT'],
['RNO', 'ONT'],
['MSY', 'MEM'],
['HPN', 'PBI'],
['LEX', 'COS'],
['BUR', 'PDX'],
['EWR', 'MTJ'],
['MSP', 'CVG'],
['LAX', 'MFR'],
['ACV', 'SFO'],
['FNT', 'DTW'],
['DEN', 'ABQ'],
['AEX', 'DFW'],
['MIA', 'SFO'],
['ATL', 'SBN'],
['LEX', 'CVG'],
['OTZ', 'ANC'],
['ORD', 'VPS'],
['PNS', 'MEM'],
['ASE', 'RFD'],
['IAD', 'AUS'],
['RNO', 'PHX'],
['CVG', 'ANC'],
['MSY', 'SAT'],
['DTW', 'AZO'],
['BIL', 'SLC'],
['ATL', 'PHF'],
['SAN', 'AUS'],
['FAT', 'SAN'],
['MEM', 'SDF'],
['ORD', 'MQT'],
['MIA', 'ORF'],
['SAT', 'CVG'],
['ATL', 'MYR'],
['PHL', 'PIT'],
['CVG', 'DCA'],
['LSE', 'MSP'],
['SJC', 'LAX'],
['SLC', 'JAC'],
['SEA', 'BOI'],
['ISP', 'MCO'],
['IAH', 'MCI'],
['SAN', 'HNL'],
['LGA', 'FLL'],
['TPA', 'CLE'],
['STL', 'ABQ'],
['AMA', 'ABQ'],
['LAX', 'MCI'],
['CMH', 'EWR'],
['ICT', 'MSP'],
['STT', 'CLT'],
['SAV', 'IAD'],
['SEA', 'BUR'],
['BDL', 'IND'],
['ANC', 'SEA'],
['ORD', 'PHL'],
['IAD', 'MDT'],
['DEN', 'CRW'],
['ORD', 'FSD'],
['SHV', 'DTW'],
['TPA', 'PHX'],
['RNO', 'SJC'],
['TYS', 'CVG'],
['IAD', 'MIA'],
['PHL', 'MSP'],
['PHX', 'GEG'],
['PHL', 'BUF'],
['BOI', 'SFO'],
['LAX', 'MTJ'],
['TLH', 'FLL'],
['PHL', 'LGA'],
['MCI', 'PIT'],
['AUS', 'BNA'],
['SFO', 'PIT'],
['DEN', 'ONT'],
['CLT', 'LIT'],
['JFK', 'ALB'],
['BOS', 'MDW'],
['BDL', 'ORD'],
['PWM', 'CLT'],
['ATL', 'LIT'],
['EWR', 'PDX'],
['CLE', 'CVG'],
['ATL', 'ICT'],
['SMF', 'ORD'],
['SDF', 'EWR'],
['DEN', 'IAH'],
['DFW', 'DTW'],
['SAN', 'STL'],
['DTW', 'MCI'],
['EWR', 'AVL'],
['TLH', 'CLT'],
['SCC', 'BRW'],
['SMF', 'BUR'],
['ATL', 'CAK'],
['LAX', 'EWR'],
['CMH', 'ICT'],
['ILM', 'CLT'],
['ITO', 'HNL'],
['CLT', 'BOS'],
['FLL', 'OAK'],
['ATL', 'JAC'],
['FLL', 'LAS'],
['ATL', 'SJC'],
['LGA', 'PHL'],
['SBP', 'SLC'],
['DEN', 'CYS'],
['BMI', 'MCO'],
['ORD', 'OAK'],
['BWI', 'LIT'],
['BOI', 'GEG'],
['DTW', 'RIC'],
['SNA', 'LAS'],
['SGF', 'DTW'],
['SAT', 'AMA'],
['DAB', 'CVG'],
['SEA', 'SIT'],
['MKE', 'EWR'],
['PIT', 'BWI'],
['ORD', 'FCA'],
['FLL', 'LAX'],
['STL', 'SFO'],
['BOS', 'IAD'],
['BQN', 'JFK'],
['DTW', 'CRW'],
['BQN', 'MCO'],
['SGF', 'STL'],
['EUG', 'LAX'],
['GEG', 'PHX'],
['DCA', 'XNA'],
['FNT', 'ORD'],
['HSV', 'MCO'],
['PHF', 'ATL'],
['CMH', 'GRR'],
['TPA', 'DCA'],
['SLC', 'CLT'],
['CVG', 'CLE'],
['PHX', 'LAS'],
['PHX', 'MKE'],
['SLC', 'ORD'],
['IAH', 'SAV'],
['DEN', 'MTJ'],
['GRK', 'IAH'],
['BOS', 'LAX'],
['IAH', 'SAN'],
['MKE', 'BDL'],
['LGA', 'XNA'],
['CVG', 'ROA'],
['MDW', 'SAT'],
['ROC', 'LAS'],
['BNA', 'DFW'],
['IAH', 'MAF'],
['HOU', 'TUL'],
['PHX', 'MRY'],
['RSW', 'DEN'],
['OKC', 'CVG'],
['DEN', 'RKS'],
['DEN', 'BFL'],
['MCI', 'LNK'],
['PHX', 'OAK'],
['LGB', 'PDX'],
['SEA', 'DEN'],
['MFE', 'DFW'],
['SLC', 'SJC'],
['PDX', 'SNA'],
['DAB', 'JAX'],
['LAX', 'SFO'],
['MFR', 'RDD'],
['MSP', 'GTF'],
['CVG', 'COS'],
['DEN', 'LIH'],
['CAE', 'MEM'],
['IAH', 'DEN'],
['ACK', 'EWR'],
['ATL', 'MEI'],
['DAY', 'DTW'],
['ORD', 'EWR'],
['DCA', 'BNA'],
['BIL', 'PIH'],
['CVG', 'PBI'],
['LGA', 'PIT'],
['LAN', 'MSP'],
['ICT', 'DEN'],
['ORD', 'CID'],
['IND', 'DFW'],
['LAX', 'FAT'],
['ICT', 'COS'],
['BHM', 'SDF'],
['RSW', 'ATL'],
['FLL', 'IAD'],
['CDC', 'LAX'],
['SAN', 'LAX'],
['BTR', 'DFW'],
['DCA', 'RDU'],
['SAN', 'BWI'],
['SLC', 'FLL'],
['RAP', 'TWF'],
['IAD', 'ABQ'],
['DCA', 'TPA'],
['DFW', 'BUR'],
['SNA', 'LAX'],
['PHX', 'OKC'],
['ELP', 'TUS'],
['SFO', 'TWF'],
['CWA', 'ORD'],
['XNA', 'SGF'],
['JAN', 'BWI'],
['CRW', 'ORD'],
['BDL', 'GRB'],
['BWI', 'MIA'],
['SJC', 'SAN'],
['IND', 'MDW'],
['IAD', 'BUF'],
['ACY', 'ATL'],
['LIH', 'HNL'],
['DFW', 'MEM'],
['DEN', 'DRO'],
['MSY', 'CMH'],
['IAH', 'BWI'],
['PBI', 'CMH'],
['BUF', 'JFK'],
['SLC', 'PSC'],
['MCO', 'JAX'],
['CLT', 'ORF'],
['RNO', 'IAH'],
['HNL', 'DEN'],
['TYS', 'MCO'],
['CLE', 'BHM'],
['SLC', 'OKC'],
['DSM', 'IAH'],
['SDF', 'SLC'],
['SYR', 'CVG'],
['ORF', 'MDW'],
['OKC', 'DFW'],
['FAI', 'SLC'],
['DEN', 'ROC'],
['CIC', 'FAT'],
['JFK', 'SRQ'],
['CLE', 'IAD'],
['JFK', 'OAK'],
['TPA', 'FNT'],
['XNA', 'LAX'],
['FSD', 'SLC'],
['GJT', 'LAX'],
['DEN', 'FAT'],
['DEN', 'ELP'],
['BWI', 'ALB'],
['RDU', 'DEN'],
['SEA', 'MCI'],
['CLT', 'MEM'],
['PSP', 'SFO'],
['SAT', 'MEM'],
['PHX', 'BUR'],
['BUR', 'PMD'],
['MSP', 'CLE'],
['BDL', 'LAS'],
['SLC', 'MSY'],
['ORD', 'ELP'],
['SJC', 'EWR'],
['ATL', 'OMA'],
['SYR', 'CLE'],
['MRY', 'SLC'],
['AVP', 'ORD'],
['RST', 'MSP'],
['PHL', 'ORF'],
['ROC', 'BWI'],
['MSO', 'SFO'],
['MSP', 'MDT'],
['HPN', 'TPA'],
['TUS', 'MDW'],
['EUG', 'PDX'],
['JFK', 'CMH'],
['HNL', 'ORD'],
['ORD', 'RDU'],
['ATL', 'CHS'],
['PSP', 'SEA'],
['CLT', 'SDF'],
['COS', 'MEM'],
['VLD', 'ATL'],
['BUF', 'PHL'],
['ATL', 'TOL'],
['DFW', 'EGE'],
['YUM', 'LAS'],
['OAK', 'MCI'],
['MCI', 'TUS'],
['PNS', 'DFW'],
['GEG', 'LGB'],
['SEA', 'JNU'],
['DAB', 'ATL'],
['SFO', 'BWI'],
['BOS', 'CVG'],
['SLC', 'CMH'],
['MIA', 'EWR'],
['ATL', 'PDX'],
['IAD', 'PHL'],
['JFK', 'AUS'],
['JFK', 'HDN'],
['MCO', 'IAD'],
['LGA', 'CMH'],
['BNA', 'STL'],
['BNA', 'MSY'],
['PBI', 'ATL'],
['MKE', 'SFO'],
['JFK', 'ORD'],
['IAD', 'DTW'],
['SLC', 'LAS'],
['SEA', 'PHL'],
['LGA', 'VPS'],
['DRO', 'ABQ'],
['MDT', 'DFW'],
['BOS', 'STT'],
['IND', 'PIT'],
['IAD', 'SAN'],
['ANC', 'ATL'],
['JAX', 'BHM'],
['BWI', 'HOU'],
['SAT', 'CLT'],
['GUC', 'DEN'],
['CAE', 'DFW'],
['FLL', 'AUS'],
['HOU', 'HRL'],
['STT', 'ATL'],
['EWR', 'CMH'],
['SBP', 'FAT'],
['MEM', 'CLE'],
['SWF', 'ATL'],
['MKE', 'TUS'],
['CLE', 'ABQ'],
['SAT', 'SLC'],
['CLE', 'SAV'],
['ISP', 'TPA'],
['MCO', 'LAS'],
['LAS', 'MKE'],
['PSP', 'DFW'],
['CVG', 'JFK'],
['IAD', 'SPI'],
['OAK', 'SNA'],
['DTW', 'ITH'],
['BHM', 'STL'],
['IAH', 'BNA'],
['BWI', 'BUF'],
['MCO', 'IND'],
['DCA', 'MHT'],
['EYW', 'ATL'],
['ORD', 'GEG'],
['MAF', 'DAL'],
['LAS', 'GEG'],
['IAD', 'BUR'],
['MDT', 'DTW'],
['BNA', 'MSP'],
['LEX', 'LGA'],
['CHS', 'EWR'],
['SLC', 'PSP'],
['ORD', 'MDT'],
['ONT', 'TUS'],
['PDX', 'ONT'],
['MYR', 'LGA'],
['PHX', 'OGG'],
['DTW', 'LAS'],
['ANC', 'JNU'],
['RDU', 'CVG'],
['ATL', 'VPS'],
['JFK', 'BGR'],
['MEM', 'BDL'],
['OTH', 'PDX'],
['PHX', 'MDW'],
['DEN', 'GRR'],
['MSP', 'LIT'],
['LIH', 'KOA'],
['BUR', 'PSP'],
['MCI', 'OMA'],
['CMH', 'MCI'],
['OAK', 'TUS'],
['MEM', 'CLT'],
['BHM', 'EWR'],
['TPA', 'CLT'],
['OMA', 'CVG'],
['TUS', 'ABQ'],
['JFK', 'CLE'],
['PHL', 'SEA'],
['PHX', 'LAX'],
['BIS', 'DEN'],
['PHX', 'ABQ'],
['BDL', 'PHL'],
['SLC', 'FCA'],
['BUF', 'RSW'],
['MHT', 'IAD'],
['DEN', 'MSO'],
['STL', 'OMA'],
['MSP', 'MCI'],
['SMF', 'LAX'],
['BZN', 'MSP'],
['SFO', 'EUG'],
['ABQ', 'CLE'],
['OME', 'ANC'],
['SFO', 'MDW'],
['AVL', 'CVG'],
['COS', 'MKE'],
['MFR', 'PHX'],
['MCO', 'PBI'],
['IAH', 'BDL'],
['MKE', 'GRR'],
['MCI', 'ABQ'],
['DFW', 'GRR'],
['MKE', 'BNA'],
['HDN', 'ORD'],
['DCA', 'LAX'],
['KOA', 'SAN'],
['SBN', 'ORD'],
['PBI', 'LGA'],
['DFW', 'CHA'],
['FLL', 'LGB'],
['SBP', 'LAS'],
['GSO', 'MSP'],
['SNA', 'PDX'],
['ORF', 'CLT'],
['SLC', 'ATL'],
['MCO', 'CLT'],
['DSM', 'DEN'],
['JAX', 'IAH'],
['BOI', 'DEN'],
['DTW', 'EWR'],
['ALB', 'ATL'],
['ABQ', 'LAX'],
['MKE', 'GRB'],
['EVV', 'DTW'],
['BTR', 'CVG'],
['EGE', 'MIA'],
['TYS', 'IAD'],
['MKE', 'STL'],
['MSY', 'LAX'],
['ROC', 'ATL'],
['IAH', 'CRW'],
['AGS', 'LGA'],
['DCA', 'MSN'],
['MKE', 'PIT'],
['TPA', 'SDF'],
['SJU', 'FLL'],
['DFW', 'SJC'],
['LFT', 'IAH'],
['MLI', 'LAS'],
['MKE', 'PHL'],
['IND', 'XNA'],
['TPA', 'CMH'],
['LGA', 'GSP'],
['BNA', 'MEM'],
['IAH', 'OKC'],
['RDU', 'MIA'],
['OKC', 'PHX'],
['MSY', 'MCO'],
['ANC', 'DLG'],
['DEN', 'RSW'],
['CLT', 'PDX'],
['IND', 'MKE'],
['TPA', 'BUF'],
['DFW', 'GRK'],
['CLT', 'IAH'],
['PHX', 'TPA'],
['DTW', 'PIT'],
['LGA', 'PBI'],
['SAN', 'BOS'],
['OKC', 'BWI'],
['CMH', 'BOS'],
['ANC', 'FAI'],
['SLC', 'STL'],
['SLC', 'COD'],
['PHX', 'MCI'],
['PIT', 'GRB'],
['BWI', 'SAV'],
['PHL', 'DEN'],
['GSO', 'LGA'],
['BWI', 'CLE'],
['SBA', 'PHX'],
['ORD', 'CMI'],
['SLC', 'SBA'],
['SLC', 'YUM'],
['DTW', 'SCE'],
['SEA', 'MCO'],
['MSY', 'DEN'],
['IAH', 'MGM'],
['FAR', 'MSP'],
['SBA', 'SJC'],
['ABQ', 'PDX'],
['SYR', 'PHL'],
['ATL', 'BZN'],
['MCO', 'MHT'],
['SFO', 'CLE'],
['PSG', 'JNU'],
['PIT', 'DEN'],
['PBI', 'CLE'],
['LEX', 'DFW'],
['HSV', 'MEM'],
['EGE', 'JFK'],
['TYS', 'ORD'],
['ATL', 'AUS'],
['STL', 'PHL'],
['BWI', 'GRR'],
['CLE', 'BDL'],
['ORD', 'GPT'],
['TUL', 'MCI'],
['ABQ', 'DEN'],
['DFW', 'MSP'],
['AZO', 'XNA'],
['PBI', 'JFK'],
['DFW', 'BUF'],
['PDX', 'MCI']
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment