Skip to content

Instantly share code, notes, and snippets.

@ais-one
Created April 30, 2017 16:05
Show Gist options
  • Save ais-one/d497fe5ca3d2ef5276468743442d1aab to your computer and use it in GitHub Desktop.
Save ais-one/d497fe5ca3d2ef5276468743442d1aab to your computer and use it in GitHub Desktop.
NASA Space Apps Challenge 2017 Code
<!--
https://github.com/abwood/d3cesium
https://opensky-network.org/apidoc/rest.html
https://www.w3.org/TR/geolocation-API/
https://dev.w3.org/geo/api/spec-source.html#heading
var x = document.getElementById("demo");
function getLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(showPosition);
} else {
x.innerHTML = "Geolocation is not supported by this browser.";
}
}
function showPosition(position) {
x.innerHTML = "Latitude: " + position.coords.latitude + // decimal degrees
"<br>Longitude: " + position.coords.longitude;
.heading // degrees
.speed
}
var polylineEntity = viewer.entities.add({
//...
});
viewer.entities.remove(polylineEntity);
viewer.dataSources.removeAll();
viewer.entities.removeAll();
viewer.scene.primitives.removeAll();
Purpose
1) viewing of airborne instrument/sensor
- readings in relation to flight path
- coverage area
2) comparison of airborne readings versus satellite readings of same area and around the same time
Coverage
Area -118.677007474 34.323718467 -115.198994919 35.96987025
Time (UTC) - 2015-07-14T18:35:00.000
- 2015-07-14T21:06:00.000
Single Instrument - CO2 concentration ppm
Challenges
- Erratic raw flight data - need to filter readings
- Crash occurs due to large data set - down sampling required
- Search for data, need to ensure comparable data sets are used (location, time, measurement type)
Findings
- resolution between airborne and satellite readings may be different
- airborne location and data reading needs to be in sync
Need to
- Altitude
- Real aircraft altitude data and visual on 3d virtual map does not match
- always flat (0 meters elevation) on 3d virtual map, but different in actual world
- improve on results presentation
- e.g. aggregate airborne measurements to match with satellite resolution
- many other factors which we may not have taken into account
Dataset Sources
- Flightpath (https://asp-archive.arc.nasa.gov/PV/N806NA/2015-07-14/) ER-2 (N806NA)
- CO2 (High-Sensitivity Fast-Response CO2 Analyzer) - https://airbornescience.nasa.gov/tracker/#!/menu/instrument/about?callsign=NASA806&measurements=CO2&instrument=586e98813461353c0a7701d7
- Satellite data
http://www.livescience.com/49196-nasa-satellite-oco2-carbon-maps.html
(0.1 degree resolution reading)
-->
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Use correct character set. -->
<meta charset="utf-8">
<!-- Tell IE to use the latest, best version. -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- Make the application on mobile take up the full browser screen and disable user scaling. -->
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<title>2017 Space Apps Challenge</title>
<script src="jquery-3.2.1.min.js"></script>
<script src="turf.min.js"></script>
<script src="../Build/Cesium/Cesium.js"></script>
<style>
@import url(../Build/Cesium/Widgets/widgets.css);
html, body, #cesiumContainer {
width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden;
}
</style>
</head>
<body>
<div id="cesiumContainer"></div>
<script>
var f_data={}; // flight path data
console.log('aaa')
//Set the random number seed for consistent results.
Cesium.Math.setRandomNumberSeed(3);
//Set bounds of our simulation time
//var start = Cesium.JulianDate.fromDate(new Date('2015-07-14T17:36:30.000'));
var start = Cesium.JulianDate.fromDate(new Date('2015-07-14T18:35:00.000'));
var stop = Cesium.JulianDate.fromDate(new Date('2015-07-14T21:06:00.000'));
//var start = Cesium.JulianDate.fromDate(new Date(2015, 2, 25, 16));
//var stop = Cesium.JulianDate.addSeconds(start, 360, new Cesium.JulianDate());
if (1) {
var clock = new Cesium.Clock({
//startTime : Cesium.JulianDate.fromIso8601("2013-12-25"),
//currentTime : Cesium.JulianDate.fromIso8601("2013-12-25"),
//stopTime : Cesium.JulianDate.fromIso8601("2013-12-26"),
startTime : start.clone(),
currentTime : start.clone(),
stopTime : stop.clone(),
clockRange : Cesium.ClockRange.LOOP_STOP, //Loop at the end,
//clockStep : Cesium.ClockStep.TICK_DEPENDENT
clockStep : Cesium.ClockStep.SYSTEM_CLOCK_MULTIPLIER,
multiplier: 30
});
}
// console.log(Cesium.ClockStep.SYSTEM_CLOCK_MULTIPLIER)
var viewer = new Cesium.Viewer('cesiumContainer', {
//terrainProviderViewModels : [], //Disable terrain changing
//infoBox : false, //Disable InfoBox widget
//selectionIndicator : false, //Disable selection indicator
clock: clock // pass in clock information
});
var credit = new Cesium.Credit('Nasa Space Apps 2017', '', 'https://2017.spaceappschallenge.org/');
viewer.scene.frameState.creditDisplay.addDefaultCredit(credit)
//Set timeline to simulation bounds
viewer.timeline.zoomTo(start, stop);
//For the use of STK World Terrain
//viewer.terrainProvider = new Cesium.CesiumTerrainProvider({
// url : 'https://assets.agi.com/stk-terrain/world',
// requestWaterMask : true,
// requestVertexNormals : true
//});
//INTENSIVE - Enable depth testing so things behind the terrain disappear.
//viewer.scene.globe.depthTestAgainstTerrain = true;
var scene = viewer.scene;
//INTENSIVE - Enable lighting based on sun/moon positions
//viewer.scene.globe.enableLighting = true;
var entity_flyby = null
var start_flyby = null
var stop_flyby = null
var plat = null
var plon = null
var maxlon = -999, maxlat = -999, minlon = 999, minlat = 999
$.getJSON('IWG1.14Jul2015-2108.json', function(data) {
f_data = data;
//console.log(f_data)
var property_flyby = new Cesium.SampledPositionProperty();
for (var i = 0, len = f_data.length; i < len; i++) {
var pt = f_data[i];
if (i == 0) console.log(pt.System_Timestamp)
if (i == f_data.length - 1) console.log(pt.System_Timestamp)
var lat = pt.Latitude
var lon = pt.Longitude
var ts = pt.System_Timestamp
var alt = pt.GPS_Altitude_MSL
var hdg = pt.True_Heading
var spd = pt.Ground_Speed
if (lat=="" || lon=="" || ts=="") continue;
var from = {
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [plon, plat]
}
};
var to = {
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [lon, lat]
}
};
var units = "kilometers";
//// Smooth the results
if (plat == null) {
plat = lat
plon = lon
}
else {
var distance = turf.distance(from, to, units);
if (distance < 1.0) continue
else {
plat = lat
plon = lon
}
}
if (lat < minlat) minlat = lat
if (lat > maxlat) maxlat = lat
if (lon < minlon) minlon = lon
if (lon > maxlon) maxlon = lon
var CO2 = (380 + (Math.random() * 25) ).toFixed(2)
var pos_ll = Cesium.Cartesian3.fromDegrees(lon, lat, alt);
var time_flyby = Cesium.JulianDate.fromDate(new Date(ts));
if (start_flyby == null) {
start_flyby = time_flyby
// console.log('start flyby',ts) // 2015-07-14T17:36:35.003
}
stop_flyby = time_flyby
//console.log('stop flyby',ts) // 2015-07-14T17:36:35.003
property_flyby.addSample(time_flyby, pos_ll);
//Also create a point for each sample we generate.
viewer.entities.add({
name : 'Time: '+ts,
position : pos_ll,
point : {
pixelSize : 8,
color : Cesium.Color.TRANSPARENT,
outlineColor : Cesium.Color.YELLOW,
outlineWidth : 3
},
description: 'Lat: '+lat+'<br/>'
+ 'Lon: '+lon+'<br/>'
+ 'Alt: '+alt+'<br/>'
+ 'Spd: '+spd+' kts<br/>'
//+ 'CO2: '+CO2+' ppm'
,
label : {
text : CO2,
fillColor: (CO2 > 400.0)? Cesium.Color.RED : Cesium.Color.BLUE,
font : '12pt monospace',
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
//outlineWidth : 1,
verticalOrigin : Cesium.VerticalOrigin.TOP,
pixelOffset : new Cesium.Cartesian2(0, 8)
}
});
// create radius around
viewer.entities.add({
position: pos_ll,
name : 'Sensor Radius',
ellipse : {
semiMinorAxis : 100.0 + (alt / 3),
semiMajorAxis : 100.0 + (alt / 3),
height: 0.0,
//material : Cesium.Color.GREEN,
material : Cesium.Color.GREEN.withAlpha(0.25),
outline : true,
outlineColor : Cesium.Color.YELLOW
//extrudedHeight : 200000.0,
//rotation : Cesium.Math.toRadians(45)
}
});
}
//console.log( start_flyby )
console.log(minlon, minlat, maxlon, maxlat)
//Actually create the entity
entity_flyby = viewer.entities.add({
//Set the entity availability to the same interval as the simulation time.
//availability : new Cesium.TimeIntervalCollection([new Cesium.TimeInterval({ start : start_flyby, stop : stop_flyby })]),
name: 'Research Aircraft',
position : property_flyby, //Use our computed positions
orientation : new Cesium.VelocityOrientationProperty(property_flyby), //Automatically compute orientation based on position movement.
model : { uri : '../../Apps/SampleData/models/CesiumAir/Cesium_Air.gltf', minimumPixelSize : 128 }, //Load the Cesium plane model to represent the entity
//Show the path as a pink line sampled in 1 second increments.
path : {
resolution : 1,
material : new Cesium.PolylineGlowMaterialProperty({ glowPower : 0.1, color : Cesium.Color.YELLOW }), //// DRAW THE LINES
width : 8
}
});
//viewer.timeline.zoomTo(start_flyby, stop_flyby);
viewer.trackedEntity = entity_flyby;
});
// Picking Lat/Lon
viewer.canvas.addEventListener('click', function(e){
var mousePosition = new Cesium.Cartesian2(e.clientX, e.clientY);
var ellipsoid = viewer.scene.globe.ellipsoid;
var cartesian = viewer.camera.pickEllipsoid(mousePosition, ellipsoid);
if (cartesian) {
var cartographic = ellipsoid.cartesianToCartographic(cartesian);
var longitudeString = Cesium.Math.toDegrees(cartographic.longitude).toFixed(3);
var latitudeString = Cesium.Math.toDegrees(cartographic.latitude).toFixed(3);
console.log(longitudeString + ', ' + latitudeString);
} else {
console.log('Globe was not picked');
}
}, false);
// PICKING
//https://groups.google.com/forum/#!msg/cesium-dev/agtOrHYEhUo/lGlFb5q4EQAJ
var leftClickHandler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
leftClickHandler.setInputAction(function(action) {
////reset visibility of selectionIndicator and infoBox
//viewer.selectionIndicator.viewModel.selectionIndicatorElement.style.visibility = 'visible';
//document.getElementsByClassName('cesium-infoBox')[0].style.visibility = "visible";
//console.log(scene)
var pickedObject = scene.pick(action.position);
//don't do anything if we didn't click on an object
if (!Cesium.defined(pickedObject)) {
viewer.trackedEntity = null;
return;
}
console.log(pickedObject.id, pickedObject.id.name) // check if 3d mesh or other type of object
viewer.trackedEntity = pickedObject;
//viewer.trackedEntity = entity;
/*
//don't do anything if it's not the entity we care about
if(entity !== pickedObject.id){ return; }
//if this is the entity we care about, hide the selectionIndicator and infoBox
viewer.selectionIndicator.viewModel.selectionIndicatorElement.style.visibility = 'hidden';
document.getElementsByClassName('cesium-infoBox')[0].style.visibility = "hidden";
*/
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
/*
You can use camera.getPickRay to get the height:
var ray = viewer.camera.getPickRay(movement.endPosition);
var position = viewer.scene.globe.pick(ray, viewer.scene);
if (Cesium.defined(position)) {
var cartographic = Cesium.Ellipsoid.WGS84.cartesianToCartographic(position);
var height = cartographic.height
}
You don't have to use Cesium.Math.toDegrees on the height, it is given in meters.
You can also use the ScreenSpaceEventHandler to handle the click event directly.
Use ScreenSpaceEventType.LEFT_CLICK. Then, replace movement.endPosition with movement.position in the handler function.
)
*/
/*
var handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
handler.setInputAction(
function (movement) {
//var pick = scene.pick(movement.endPosition);
var pick = scene.pick(movement.position);
if (Cesium.defined(pick) && Cesium.defined(pick.node) && Cesium.defined(pick.mesh)) {
console.log('node: ' + pick.node.name + '. mesh: ' + pick.mesh.name);
}
},
//Cesium.ScreenSpaceEventType.MOUSE_MOVE
Cesium.ScreenSpaceEventType.LEFT_CLICK
);
*/
var instances = [];
for (var lon = -119; lon < -114.0; lon += 0.1) {
for (var lat = 34.0; lat < 37.0; lat += 0.1) {
instances.push(new Cesium.GeometryInstance({
geometry : new Cesium.RectangleGeometry({
rectangle : Cesium.Rectangle.fromDegrees(lon, lat, lon + 0.1, lat + 0.1),
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
}),
attributes : {
color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromRandom({alpha : 0.3}))
}
}));
}
}
scene.primitives.add(new Cesium.Primitive({
geometryInstances : instances,
appearance : new Cesium.PerInstanceColorAppearance()
}));
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment