globe sizing
This commit is contained in:
parent
ac3a890824
commit
fb78deeed4
2 changed files with 394 additions and 392 deletions
|
@ -1,335 +1,304 @@
|
||||||
<div class="globeContainer">
|
|
||||||
<svg id="globe" width="400" height="400"></svg>
|
|
||||||
<div class="globeText">
|
|
||||||
click and drag
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.globeContainer {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
background-color: #00111d;
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.footprint--LEO) {
|
|
||||||
fill: rgba(255, 177, 64, 0.08);
|
|
||||||
stroke: rgba(255, 177, 64, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.globeText{
|
|
||||||
text-align: center;
|
|
||||||
font-size: 0.6em;
|
|
||||||
color: #aaaaaa;
|
|
||||||
}
|
|
||||||
|
|
||||||
#globe {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import * as d3 from "d3";
|
import * as d3 from "d3";
|
||||||
import * as sjs from 'satellite.js';
|
import * as sjs from "satellite.js";
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from "svelte";
|
||||||
import tles from "../../static/tle.js"
|
import tles from "../../static/tle.js";
|
||||||
import world110m from "../../static/world-110m.js"
|
import world110m from "../../static/world-110m.js";
|
||||||
import * as topojson from "topojson";
|
import * as topojson from "topojson";
|
||||||
|
|
||||||
export let width = 300;
|
|
||||||
|
|
||||||
async function doThing() {
|
|
||||||
let satelliteJs = sjs;
|
|
||||||
var RADIANS = Math.PI / 180;
|
|
||||||
var DEGREES = 180 / Math.PI;
|
|
||||||
var R_EARTH = 6378.137; // equatorial radius (km)
|
|
||||||
|
|
||||||
/* =============================================== */
|
export let width = 300;
|
||||||
/* =============== CLOCK ========================= */
|
|
||||||
/* =============================================== */
|
|
||||||
|
|
||||||
/**
|
async function doThing() {
|
||||||
* Factory function for keeping track of elapsed time and rates.
|
let satelliteJs = sjs;
|
||||||
*/
|
var RADIANS = Math.PI / 180;
|
||||||
class Clock {
|
var DEGREES = 180 / Math.PI;
|
||||||
constructor() {
|
var R_EARTH = 6378.137; // equatorial radius (km)
|
||||||
this._rate = 60; // 1ms elapsed : 60sec simulated
|
|
||||||
this._date = d3.now();
|
/* =============================================== */
|
||||||
this._elapsed = 0;
|
/* =============== CLOCK ========================= */
|
||||||
|
/* =============================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory function for keeping track of elapsed time and rates.
|
||||||
|
*/
|
||||||
|
class Clock {
|
||||||
|
constructor() {
|
||||||
|
this._rate = 60; // 1ms elapsed : 60sec simulated
|
||||||
|
this._date = d3.now();
|
||||||
|
this._elapsed = 0;
|
||||||
|
}
|
||||||
|
async date(timeInMs) {
|
||||||
|
if (!arguments.length) return this._date + this._elapsed * this._rate;
|
||||||
|
this._date = timeInMs;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
async elapsed(ms) {
|
||||||
|
if (!arguments.length) return this._date - d3.now(); // calculates elapsed
|
||||||
|
this._elapsed = ms;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
async rate(secondsPerMsElapsed) {
|
||||||
|
if (!arguments.length) return this._rate;
|
||||||
|
this._rate = secondsPerMsElapsed;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
async date(timeInMs) {
|
|
||||||
if (!arguments.length) return this._date + this._elapsed * this._rate;
|
|
||||||
this._date = timeInMs;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
async elapsed(ms) {
|
|
||||||
if (!arguments.length) return this._date - d3.now(); // calculates elapsed
|
|
||||||
this._elapsed = ms;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
async rate(secondsPerMsElapsed) {
|
|
||||||
if (!arguments.length) return this._rate;
|
|
||||||
this._rate = secondsPerMsElapsed;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ==================================================== */
|
/* ==================================================== */
|
||||||
/* =============== CONVERSION ========================= */
|
/* =============== CONVERSION ========================= */
|
||||||
/* ==================================================== */
|
/* ==================================================== */
|
||||||
|
|
||||||
async function satrecToFeature(satrec, date, props) {
|
async function satrecToFeature(satrec, date, props) {
|
||||||
var properties = props || {};
|
var properties = props || {};
|
||||||
var positionAndVelocity = satelliteJs.propagate(satrec, date);
|
var positionAndVelocity = satelliteJs.propagate(satrec, date);
|
||||||
var gmst = satelliteJs.gstime(date);
|
var gmst = satelliteJs.gstime(date);
|
||||||
var positionGd = satelliteJs.eciToGeodetic(
|
var positionGd = satelliteJs.eciToGeodetic(
|
||||||
positionAndVelocity.position,
|
positionAndVelocity.position,
|
||||||
gmst
|
gmst
|
||||||
);
|
);
|
||||||
properties.height = positionGd.height;
|
properties.height = positionGd.height;
|
||||||
return {
|
return {
|
||||||
type: "Feature",
|
type: "Feature",
|
||||||
properties: properties,
|
properties: properties,
|
||||||
geometry: {
|
geometry: {
|
||||||
type: "Point",
|
type: "Point",
|
||||||
coordinates: [
|
coordinates: [
|
||||||
positionGd.longitude * DEGREES,
|
positionGd.longitude * DEGREES,
|
||||||
positionGd.latitude * DEGREES,
|
positionGd.latitude * DEGREES,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/* ==================================================== */
|
/* ==================================================== */
|
||||||
/* =============== TLE ================================ */
|
/* =============== TLE ================================ */
|
||||||
/* ==================================================== */
|
/* ==================================================== */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory function for working with TLE.
|
* Factory function for working with TLE.
|
||||||
*/
|
*/
|
||||||
class TLE {
|
class TLE {
|
||||||
constructor() {
|
constructor() {
|
||||||
this._properties;
|
this._properties;
|
||||||
|
this._date;
|
||||||
|
}
|
||||||
|
async _lines(arry) {
|
||||||
|
return arry.slice(0, 2);
|
||||||
|
}
|
||||||
|
async satrecs(tles) {
|
||||||
|
return tles.map(function (d) {
|
||||||
|
return satelliteJs.twoline2satrec.apply(null, this._lines(d));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async features(tles) {
|
||||||
|
var date = this._date || d3.now();
|
||||||
|
|
||||||
|
return tles.map(function (d) {
|
||||||
|
var satrec = satelliteJs.twoline2satrec.apply(null, this._lines(d));
|
||||||
|
return satrecToFeature(satrec, date, this._properties(d));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async lines(func) {
|
||||||
|
if (!arguments.length) return this._lines;
|
||||||
|
this._lines = func;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
async properties(func) {
|
||||||
|
if (!arguments.length) return this._properties;
|
||||||
|
this._properties = func;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
async date(ms) {
|
||||||
|
if (!arguments.length) return this._date;
|
||||||
|
this._date = ms;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==================================================== */
|
||||||
|
/* =============== PARSE ============================== */
|
||||||
|
/* ==================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses text file string of tle into groups.
|
||||||
|
* @return {string[][]} Like [['tle line 1', 'tle line 2'], ...]
|
||||||
|
*/
|
||||||
|
async function parseTle(tleString) {
|
||||||
|
// remove last newline so that we can properly split all the lines
|
||||||
|
var lines = tleString.replace(/\r?\n$/g, "").split(/\r?\n/);
|
||||||
|
|
||||||
|
return lines.reduce(function (acc, cur, index) {
|
||||||
|
if (index % 2 === 0) acc.push([]);
|
||||||
|
acc[acc.length - 1].push(cur);
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==================================================== */
|
||||||
|
/* =============== SATELLITE ========================== */
|
||||||
|
/* ==================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Satellite factory function that wraps satellitejs functionality
|
||||||
|
* and can compute footprints based on TLE and date
|
||||||
|
*
|
||||||
|
* @param {string[][]} tle two-line element
|
||||||
|
* @param {Date} date date to propagate with TLE
|
||||||
|
*/
|
||||||
|
function Satellite(tle, date) {
|
||||||
|
this._satrec = satelliteJs.twoline2satrec(tle[0], tle[1]);
|
||||||
|
this._satNum = this._satrec.satnum; // NORAD Catalog Number
|
||||||
|
|
||||||
|
this._altitude; // km
|
||||||
|
this._position = {
|
||||||
|
lat: null,
|
||||||
|
lng: null,
|
||||||
|
};
|
||||||
|
this._halfAngle; // degrees
|
||||||
this._date;
|
this._date;
|
||||||
}
|
this._gmst;
|
||||||
async _lines(arry) {
|
|
||||||
return arry.slice(0, 2);
|
|
||||||
}
|
|
||||||
async satrecs(tles) {
|
|
||||||
return tles.map(function (d) {
|
|
||||||
return satelliteJs.twoline2satrec.apply(null, this._lines(d));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
async features(tles) {
|
|
||||||
var date = this._date || d3.now();
|
|
||||||
|
|
||||||
return tles.map(function (d) {
|
this.setDate(date);
|
||||||
var satrec = satelliteJs.twoline2satrec.apply(null, this._lines(d));
|
this.update();
|
||||||
return satrecToFeature(satrec, date, this._properties(d));
|
this._orbitType = this.orbitTypeFromAlt(this._altitude); // LEO, MEO, or GEO
|
||||||
});
|
|
||||||
}
|
}
|
||||||
async lines(func) {
|
|
||||||
if (!arguments.length) return this._lines;
|
/**
|
||||||
this._lines = func;
|
* Updates satellite position and altitude based on current TLE and date
|
||||||
|
*/
|
||||||
|
Satellite.prototype.update = async function () {
|
||||||
|
var positionAndVelocity = satelliteJs.propagate(this._satrec, this._date);
|
||||||
|
var positionGd = satelliteJs.eciToGeodetic(
|
||||||
|
positionAndVelocity.position,
|
||||||
|
this._gmst
|
||||||
|
);
|
||||||
|
|
||||||
|
this._position = {
|
||||||
|
lat: positionGd.latitude * DEGREES,
|
||||||
|
lng: positionGd.longitude * DEGREES,
|
||||||
|
};
|
||||||
|
this._altitude = positionGd.height;
|
||||||
return this;
|
return this;
|
||||||
}
|
|
||||||
async properties(func) {
|
|
||||||
if (!arguments.length) return this._properties;
|
|
||||||
this._properties = func;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
async date(ms) {
|
|
||||||
if (!arguments.length) return this._date;
|
|
||||||
this._date = ms;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ==================================================== */
|
|
||||||
/* =============== PARSE ============================== */
|
|
||||||
/* ==================================================== */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses text file string of tle into groups.
|
|
||||||
* @return {string[][]} Like [['tle line 1', 'tle line 2'], ...]
|
|
||||||
*/
|
|
||||||
async function parseTle(tleString) {
|
|
||||||
// remove last newline so that we can properly split all the lines
|
|
||||||
var lines = tleString.replace(/\r?\n$/g, "").split(/\r?\n/);
|
|
||||||
|
|
||||||
return lines.reduce(function (acc, cur, index) {
|
|
||||||
if (index % 2 === 0) acc.push([]);
|
|
||||||
acc[acc.length - 1].push(cur);
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ==================================================== */
|
|
||||||
/* =============== SATELLITE ========================== */
|
|
||||||
/* ==================================================== */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Satellite factory function that wraps satellitejs functionality
|
|
||||||
* and can compute footprints based on TLE and date
|
|
||||||
*
|
|
||||||
* @param {string[][]} tle two-line element
|
|
||||||
* @param {Date} date date to propagate with TLE
|
|
||||||
*/
|
|
||||||
function Satellite(tle, date) {
|
|
||||||
this._satrec = satelliteJs.twoline2satrec(tle[0], tle[1]);
|
|
||||||
this._satNum = this._satrec.satnum; // NORAD Catalog Number
|
|
||||||
|
|
||||||
this._altitude; // km
|
|
||||||
this._position = {
|
|
||||||
lat: null,
|
|
||||||
lng: null,
|
|
||||||
};
|
};
|
||||||
this._halfAngle; // degrees
|
|
||||||
this._date;
|
|
||||||
this._gmst;
|
|
||||||
|
|
||||||
this.setDate(date);
|
/**
|
||||||
this.update();
|
* @returns {GeoJSON.Polygon} GeoJSON describing the satellite's current footprint on the Earth
|
||||||
this._orbitType = this.orbitTypeFromAlt(this._altitude); // LEO, MEO, or GEO
|
*/
|
||||||
}
|
Satellite.prototype.getFootprint = function () {
|
||||||
|
var theta = this._halfAngle * RADIANS;
|
||||||
|
|
||||||
/**
|
let coreAngle = this._coreAngle(theta, this._altitude, R_EARTH) * DEGREES;
|
||||||
* Updates satellite position and altitude based on current TLE and date
|
|
||||||
*/
|
|
||||||
Satellite.prototype.update = async function () {
|
|
||||||
var positionAndVelocity = satelliteJs.propagate(this._satrec, this._date);
|
|
||||||
var positionGd = satelliteJs.eciToGeodetic(
|
|
||||||
positionAndVelocity.position,
|
|
||||||
this._gmst
|
|
||||||
);
|
|
||||||
|
|
||||||
this._position = {
|
return d3
|
||||||
lat: positionGd.latitude * DEGREES,
|
.geoCircle()
|
||||||
lng: positionGd.longitude * DEGREES,
|
.center([this._position.lng, this._position.lat])
|
||||||
|
.radius(coreAngle)();
|
||||||
};
|
};
|
||||||
this._altitude = positionGd.height;
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {GeoJSON.Polygon} GeoJSON describing the satellite's current footprint on the Earth
|
* A conical satellite with half angle casts a circle on the Earth. Find the angle
|
||||||
*/
|
* from the center of the earth to the radius of this circle
|
||||||
Satellite.prototype.getFootprint = function () {
|
* @param {number} theta: Satellite half angle in radians
|
||||||
var theta = this._halfAngle * RADIANS;
|
* @param {number} altitude Satellite altitude
|
||||||
|
* @param {number} r Earth radius
|
||||||
|
* @returns {number} core angle in radians
|
||||||
|
*/
|
||||||
|
Satellite.prototype._coreAngle = function (theta, altitude, r) {
|
||||||
|
// if FOV is larger than Earth, assume it goes to the tangential point
|
||||||
|
if (Math.sin(theta) > r / (altitude + r)) {
|
||||||
|
return Math.acos(r / (r + altitude));
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
Math.abs(Math.asin(((r + altitude) * Math.sin(theta)) / r)) - theta
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
let coreAngle = this._coreAngle(theta, this._altitude, R_EARTH) * DEGREES;
|
Satellite.prototype.halfAngle = function (halfAngle) {
|
||||||
|
if (!arguments.length) return this._halfAngle;
|
||||||
|
this._halfAngle = halfAngle;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
return d3
|
Satellite.prototype.satNum = function (satNum) {
|
||||||
.geoCircle()
|
if (!arguments.length) return this._satNum;
|
||||||
.center([this._position.lng, this._position.lat])
|
this._satNum = satNum;
|
||||||
.radius(coreAngle)();
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
Satellite.prototype.altitude = function (altitude) {
|
||||||
* A conical satellite with half angle casts a circle on the Earth. Find the angle
|
if (!arguments.length) return this._altitude;
|
||||||
* from the center of the earth to the radius of this circle
|
this._altitude = altitude;
|
||||||
* @param {number} theta: Satellite half angle in radians
|
return this;
|
||||||
* @param {number} altitude Satellite altitude
|
};
|
||||||
* @param {number} r Earth radius
|
|
||||||
* @returns {number} core angle in radians
|
|
||||||
*/
|
|
||||||
Satellite.prototype._coreAngle = function (theta, altitude, r) {
|
|
||||||
// if FOV is larger than Earth, assume it goes to the tangential point
|
|
||||||
if (Math.sin(theta) > r / (altitude + r)) {
|
|
||||||
return Math.acos(r / (r + altitude));
|
|
||||||
}
|
|
||||||
return Math.abs(Math.asin(((r + altitude) * Math.sin(theta)) / r)) - theta;
|
|
||||||
};
|
|
||||||
|
|
||||||
Satellite.prototype.halfAngle = function (halfAngle) {
|
Satellite.prototype.position = function (position) {
|
||||||
if (!arguments.length) return this._halfAngle;
|
if (!arguments.length) return this._position;
|
||||||
this._halfAngle = halfAngle;
|
this._position = position;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
Satellite.prototype.satNum = function (satNum) {
|
Satellite.prototype.getOrbitType = function () {
|
||||||
if (!arguments.length) return this._satNum;
|
return this._orbitType;
|
||||||
this._satNum = satNum;
|
};
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
Satellite.prototype.altitude = function (altitude) {
|
/**
|
||||||
if (!arguments.length) return this._altitude;
|
* sets both the date and the Greenwich Mean Sidereal Time
|
||||||
this._altitude = altitude;
|
* @param {Date} date
|
||||||
return this;
|
*/
|
||||||
};
|
Satellite.prototype.setDate = function (date) {
|
||||||
|
this._date = date;
|
||||||
|
this._gmst = satelliteJs.gstime(date);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
Satellite.prototype.position = function (position) {
|
/**
|
||||||
if (!arguments.length) return this._position;
|
* Maps an altitude to a type of satellite
|
||||||
this._position = position;
|
* @param {number} altitude (in KM)
|
||||||
return this;
|
* @returns {'LEO' | 'MEO' | 'GEO'}
|
||||||
};
|
*/
|
||||||
|
Satellite.prototype.orbitTypeFromAlt = function (altitude) {
|
||||||
|
this._altitude = altitude || this._altitude;
|
||||||
|
return this._altitude < 1200
|
||||||
|
? "LEO"
|
||||||
|
: this._altitude > 22000
|
||||||
|
? "GEO"
|
||||||
|
: "MEO";
|
||||||
|
};
|
||||||
|
|
||||||
Satellite.prototype.getOrbitType = function () {
|
/* =============================================== */
|
||||||
return this._orbitType;
|
/* =================== GLOBE ===================== */
|
||||||
};
|
/* =============================================== */
|
||||||
|
|
||||||
/**
|
// Approximate date the tle data was aquired from https://www.space-track.org/#recent
|
||||||
* sets both the date and the Greenwich Mean Sidereal Time
|
var TLE_DATA_DATE = new Date(2015, 11, 3, 17, 36).getTime();
|
||||||
* @param {Date} date
|
|
||||||
*/
|
|
||||||
Satellite.prototype.setDate = function (date) {
|
|
||||||
this._date = date;
|
|
||||||
this._gmst = satelliteJs.gstime(date);
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
var activeClock;
|
||||||
* Maps an altitude to a type of satellite
|
var sats;
|
||||||
* @param {number} altitude (in KM)
|
|
||||||
* @returns {'LEO' | 'MEO' | 'GEO'}
|
|
||||||
*/
|
|
||||||
Satellite.prototype.orbitTypeFromAlt = function (altitude) {
|
|
||||||
this._altitude = altitude || this._altitude;
|
|
||||||
return this._altitude < 1200
|
|
||||||
? "LEO"
|
|
||||||
: this._altitude > 22000
|
|
||||||
? "GEO"
|
|
||||||
: "MEO";
|
|
||||||
};
|
|
||||||
|
|
||||||
/* =============================================== */
|
var svg = d3.select("#globe");
|
||||||
/* =================== GLOBE ===================== */
|
|
||||||
/* =============================================== */
|
|
||||||
|
|
||||||
// Approximate date the tle data was aquired from https://www.space-track.org/#recent
|
var marginTop = 0;
|
||||||
var TLE_DATA_DATE = new Date(2015, 11, 3, 17 ,36).getTime();
|
var width = svg.attr("width");
|
||||||
|
var height = svg.attr("height") - marginTop;
|
||||||
|
|
||||||
var activeClock;
|
var projection = d3
|
||||||
var sats;
|
.geoOrthographic()
|
||||||
|
.scale((height - 10) / 2)
|
||||||
|
.translate([width / 2, height / 2 + marginTop])
|
||||||
|
.rotate([45, -30]);
|
||||||
|
|
||||||
var svg = d3.select("#globe");
|
var geoPath = d3.geoPath().projection(projection);
|
||||||
|
|
||||||
var marginTop = 0;
|
svg
|
||||||
var width = svg.attr("width");
|
.append("path")
|
||||||
var height = svg.attr("height") - marginTop;
|
.datum({
|
||||||
|
type: "Sphere",
|
||||||
|
})
|
||||||
|
.style("cursor", "grab")
|
||||||
|
.attr("fill", "#2E86AB")
|
||||||
|
.attr("d", geoPath);
|
||||||
|
|
||||||
var projection = d3
|
function initGlobe() {
|
||||||
.geoOrthographic()
|
let worldData = world110m;
|
||||||
.scale((height - 10) / 2)
|
|
||||||
.translate([width / 2, height / 2 + marginTop])
|
|
||||||
.rotate([45, -30]);
|
|
||||||
|
|
||||||
var geoPath = d3.geoPath().projection(projection);
|
|
||||||
|
|
||||||
svg
|
|
||||||
.append("path")
|
|
||||||
.datum({
|
|
||||||
type: "Sphere",
|
|
||||||
})
|
|
||||||
.style("cursor", "grab")
|
|
||||||
.attr("fill", "#2E86AB")
|
|
||||||
.attr("d", geoPath);
|
|
||||||
|
|
||||||
function initGlobe() {
|
|
||||||
let worldData = world110m;
|
|
||||||
svg
|
svg
|
||||||
.selectAll(".segment")
|
.selectAll(".segment")
|
||||||
.data([topojson.feature(worldData, worldData.objects.land)])
|
.data([topojson.feature(worldData, worldData.objects.land)])
|
||||||
|
@ -342,111 +311,144 @@
|
||||||
.style("stroke-width", "0px")
|
.style("stroke-width", "0px")
|
||||||
.style("fill", "#ffffff")
|
.style("fill", "#ffffff")
|
||||||
.style("opacity", "1");
|
.style("opacity", "1");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateSats(date) {
|
async function updateSats(date) {
|
||||||
sats.forEach(async function (sat) {
|
sats.forEach(async function (sat) {
|
||||||
return sat.setDate(date).update();
|
return sat.setDate(date).update();
|
||||||
});
|
});
|
||||||
return sats;
|
return sats;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create satellite objects for each record in the TLEs and begin animation
|
* Create satellite objects for each record in the TLEs and begin animation
|
||||||
* @param {string[][]} parsedTles
|
* @param {string[][]} parsedTles
|
||||||
*/
|
*/
|
||||||
async function initSats(parsedTles) {
|
async function initSats(parsedTles) {
|
||||||
activeClock = new Clock();
|
activeClock = new Clock();
|
||||||
|
|
||||||
await activeClock.rate(100);
|
await activeClock.rate(100);
|
||||||
await activeClock.date(TLE_DATA_DATE);
|
await activeClock.date(TLE_DATA_DATE);
|
||||||
|
|
||||||
sats = await Promise.all(
|
sats = await Promise.all(
|
||||||
parsedTles.map(async function (tle) {
|
parsedTles.map(async function (tle) {
|
||||||
var sat = new Satellite(tle, new Date(2015, 11, 3, 17, 36));
|
var sat = new Satellite(tle, new Date(2015, 11, 3, 17, 36));
|
||||||
sat.halfAngle(61.73);
|
sat.halfAngle(61.73);
|
||||||
return sat;
|
return sat;
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
window.requestAnimationFrame(animateSats);
|
|
||||||
return sats;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function draw() {
|
|
||||||
// redrawGlobe();
|
|
||||||
let allFootprints = svg.selectAll(".footprint");
|
|
||||||
|
|
||||||
let allFootprintsData = allFootprints.data(sats, async function (sat) {
|
|
||||||
return sat.satNum();
|
|
||||||
});
|
|
||||||
|
|
||||||
let allFootprintsDataJoin = allFootprintsData.join(function (enter) {
|
|
||||||
return enter
|
|
||||||
.append("path")
|
|
||||||
.attr("class", function (sat) {
|
|
||||||
return "footprint footprint--" + sat.getOrbitType();
|
|
||||||
})
|
})
|
||||||
.style("cursor", "grab");
|
);
|
||||||
});
|
|
||||||
|
|
||||||
allFootprintsDataJoin.attr("d", function (sat) {
|
window.requestAnimationFrame(animateSats);
|
||||||
return geoPath(sat.getFootprint());
|
return sats;
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function redrawGlobe() {
|
|
||||||
let allSelectedSegment = svg.selectAll(".segment");
|
|
||||||
allSelectedSegment.attr("d", geoPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
var m0;
|
|
||||||
var o0;
|
|
||||||
|
|
||||||
async function mousedown(e) {
|
|
||||||
m0 = [e.pageX, e.pageY];
|
|
||||||
o0 = projection.rotate();
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function mousemove(e) {
|
|
||||||
if (m0) {
|
|
||||||
var m1 = [e.pageX, e.pageY];
|
|
||||||
const o1 = [o0[0] + (m1[0] - m0[0]) / 2.5, o0[1] + (m0[1] - m1[1]) / 2.5];
|
|
||||||
projection.rotate(o1);
|
|
||||||
redrawGlobe();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async function mouseup(e) {
|
async function draw() {
|
||||||
if (m0) {
|
// redrawGlobe();
|
||||||
mousemove(e);
|
let allFootprints = svg.selectAll(".footprint");
|
||||||
m0 = null;
|
|
||||||
|
let allFootprintsData = allFootprints.data(sats, async function (sat) {
|
||||||
|
return sat.satNum();
|
||||||
|
});
|
||||||
|
|
||||||
|
let allFootprintsDataJoin = allFootprintsData.join(function (enter) {
|
||||||
|
return enter
|
||||||
|
.append("path")
|
||||||
|
.attr("class", function (sat) {
|
||||||
|
return "footprint footprint--" + sat.getOrbitType();
|
||||||
|
})
|
||||||
|
.style("cursor", "grab");
|
||||||
|
});
|
||||||
|
|
||||||
|
allFootprintsDataJoin.attr("d", function (sat) {
|
||||||
|
return geoPath(sat.getFootprint());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function redrawGlobe() {
|
||||||
|
let allSelectedSegment = svg.selectAll(".segment");
|
||||||
|
allSelectedSegment.attr("d", geoPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
var m0;
|
||||||
|
var o0;
|
||||||
|
|
||||||
|
async function mousedown(e) {
|
||||||
|
m0 = [e.pageX, e.pageY];
|
||||||
|
o0 = projection.rotate();
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function mousemove(e) {
|
||||||
|
if (m0) {
|
||||||
|
var m1 = [e.pageX, e.pageY];
|
||||||
|
const o1 = [
|
||||||
|
o0[0] + (m1[0] - m0[0]) / 2.5,
|
||||||
|
o0[1] + (m0[1] - m1[1]) / 2.5,
|
||||||
|
];
|
||||||
|
projection.rotate(o1);
|
||||||
|
redrawGlobe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function mouseup(e) {
|
||||||
|
if (m0) {
|
||||||
|
mousemove(e);
|
||||||
|
m0 = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
svg.on("mousedown", mousedown);
|
||||||
|
d3.select(window).on("mousemove", mousemove).on("mouseup", mouseup);
|
||||||
|
|
||||||
|
async function animateSats(elapsed) {
|
||||||
|
var dateInMsI1 = await activeClock.elapsed(elapsed);
|
||||||
|
var dateInMs = dateInMsI1.date();
|
||||||
|
var date = new Date(await dateInMs);
|
||||||
|
|
||||||
|
updateSats(date);
|
||||||
|
draw();
|
||||||
|
window.requestAnimationFrame(animateSats);
|
||||||
|
}
|
||||||
|
|
||||||
|
initGlobe();
|
||||||
|
|
||||||
|
await initSats(await parseTle(tles));
|
||||||
}
|
}
|
||||||
|
|
||||||
svg.on("mousedown", mousedown);
|
onMount(async () => {
|
||||||
d3.select(window).on("mousemove", mousemove).on("mouseup", mouseup);
|
doThing();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
async function animateSats(elapsed) {
|
<div class="globeContainer">
|
||||||
var dateInMsI1 = await activeClock.elapsed(elapsed);
|
<svg id="globe" {width} height={width} />
|
||||||
var dateInMs = dateInMsI1.date();
|
<div class="globeText">click and drag</div>
|
||||||
var date = new Date(await dateInMs);
|
</div>
|
||||||
|
|
||||||
updateSats(date);
|
<style>
|
||||||
draw();
|
.globeContainer {
|
||||||
window.requestAnimationFrame(animateSats);
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: #00111d;
|
||||||
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
initGlobe();
|
:global(.footprint--LEO) {
|
||||||
|
fill: rgba(255, 177, 64, 0.08);
|
||||||
await initSats(await parseTle(tles))
|
stroke: rgba(255, 177, 64, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
onMount(async () => {
|
|
||||||
doThing();
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
.globeText {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 0.6em;
|
||||||
|
color: #aaaaaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
#globe {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<div class="site">
|
<div class="site">
|
||||||
<div class="hero">
|
<div class="hero">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<Globe />
|
<Globe width="250" />
|
||||||
<FemtoHeader />
|
<FemtoHeader />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -105,7 +105,7 @@
|
||||||
|
|
||||||
.hero .container {
|
.hero .container {
|
||||||
grid-template-columns: auto;
|
grid-template-columns: auto;
|
||||||
grid-template-rows: 200px auto;
|
grid-template-rows: 260px auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Reference in a new issue