geo updates from martin
parent
54097c0058
commit
f9f2f4f262
|
@ -1,7 +1,7 @@
|
||||||
body {
|
body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin: 0;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
float: left;
|
float: left;
|
||||||
background-color: #ccc;
|
background-color: #ccc;
|
||||||
font-family: verdana, helvetica, arial, sans-serif;
|
font-family: verdana, helvetica, arial, sans-serif;
|
||||||
|
@ -26,55 +26,45 @@ body {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
// background-color: yellow;
|
height: 620px;
|
||||||
height: 700px;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
//margin-left: auto;
|
//margin-left: auto;
|
||||||
//margin-right: auto;
|
//margin-right: auto;
|
||||||
margin: 10px;
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
border: gray 1px solid;
|
||||||
}
|
}
|
||||||
|
|
||||||
#svgworld {
|
#svgworld {
|
||||||
position: absolute; top: 0; right: 0; bottom: 0; left: 0;
|
position: absolute; top: 0; right: 0; bottom: 0; left: 0;
|
||||||
// border: blue 1px solid;
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
// margin-left: auto;
|
// border: green 1px solid;
|
||||||
// margin-right: auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#svggraticule {
|
#svggraticule {
|
||||||
position: absolute; top: 0; right: 0; bottom: 0; left: 0;
|
position: absolute; top: 0; right: 0; bottom: 0; left: 0;
|
||||||
// border: blue 1px solid;
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
// margin-left: auto;
|
// border: blue 1px solid;
|
||||||
// margin-right: auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#svgobservers {
|
#svgobservers {
|
||||||
position: absolute; top: 0; right: 0; bottom: 0; left: 0;
|
position: absolute; top: 0; right: 0; bottom: 0; left: 0;
|
||||||
// border: blue 1px solid;
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
// margin-left: auto;
|
// border: blue 1px solid;
|
||||||
// margin-right: auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#svgalmanac {
|
#svgalmanac {
|
||||||
position: absolute; top: 0; right: 0; bottom: 0; left: 0;
|
position: absolute; top: 0; right: 0; bottom: 0; left: 0;
|
||||||
// border: blue 1px solid;
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
// margin-left: auto;
|
// border: blue 1px solid;
|
||||||
// margin-right: auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
// border: red 1px solid;
|
//padding: 20px;
|
||||||
// background-color: transparent;
|
|
||||||
padding: 20px;
|
|
||||||
// margin-left: auto;
|
|
||||||
// margin-right: auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
path.countries {
|
path.countries {
|
||||||
|
background: yellow;
|
||||||
stroke-width: 1;
|
stroke-width: 1;
|
||||||
stroke: #75739F;
|
stroke: #75739F;
|
||||||
fill: #5EAFC6;
|
fill: #5EAFC6;
|
||||||
|
@ -85,11 +75,38 @@ path.coverage {
|
||||||
fill: #888;
|
fill: #888;
|
||||||
fill-opacity: 0.1;
|
fill-opacity: 0.1;
|
||||||
}
|
}
|
||||||
circle.sats {
|
path.coverage.down {
|
||||||
|
stroke-width: 0;
|
||||||
|
fill: transparent;
|
||||||
|
fill-opacity: 0;
|
||||||
|
}
|
||||||
|
path.observers {
|
||||||
|
stroke-width: 1;
|
||||||
|
stroke: black;
|
||||||
|
fill: black;
|
||||||
|
fill-opacity: .75;
|
||||||
|
}
|
||||||
|
path.observers.down {
|
||||||
|
stroke-width: 1;
|
||||||
|
stroke: black;
|
||||||
|
fill: red;
|
||||||
|
fill-opacity: .75;
|
||||||
|
}
|
||||||
|
rect.observers {
|
||||||
|
stroke-width: 1;
|
||||||
|
stroke: black;
|
||||||
|
fill: black;
|
||||||
|
fill-opacity: 1;
|
||||||
|
}
|
||||||
|
circle.satellites {
|
||||||
stroke-width: 1;
|
stroke-width: 1;
|
||||||
stroke: #4F442B;
|
stroke: #4F442B;
|
||||||
//fill: #FCBC34;
|
//fill: #FCBC34;
|
||||||
}
|
}
|
||||||
|
.radials {
|
||||||
|
stroke-width: 1;
|
||||||
|
stroke: red;
|
||||||
|
}
|
||||||
text.labels {
|
text.labels {
|
||||||
// stroke-width: 1;
|
// stroke-width: 1;
|
||||||
// stroke: #4F442B;
|
// stroke: #4F442B;
|
||||||
|
@ -125,4 +142,15 @@ path.merged {
|
||||||
stroke: #4F442B;
|
stroke: #4F442B;
|
||||||
stroke-width: 2px;
|
stroke-width: 2px;
|
||||||
}
|
}
|
||||||
|
.tooltip {
|
||||||
|
font-family: courier new, courier;
|
||||||
|
font-style: italic;
|
||||||
|
font-size: 12px;
|
||||||
|
width: 200px;
|
||||||
|
position: relative;
|
||||||
|
background-color: #ddd;
|
||||||
|
border: solid;
|
||||||
|
border-width: 1px;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
|
692
html/geo/geo.js
692
html/geo/geo.js
|
@ -2,97 +2,106 @@
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
|
||||||
var fileWorld = "world.geojson";
|
var fileWorld = 'world.geojson';
|
||||||
var fileAlmanac = "../almanac.json" // "https://galmon.eu/almanac"
|
var fileAlmanac = '../almanac.json' // 'https://galmon.eu/almanac'
|
||||||
var fileObservers = "../observers.json" // "https://galmon.eu/observers"
|
var fileObservers = '../observers.json' // 'https://galmon.eu/observers'
|
||||||
|
|
||||||
var projectionChoice = "Fahey";
|
var projectionChoice = 'Gilbert';
|
||||||
var projectionChoice = "CylindricalStereographic";
|
var projectionChoice = 'CylindricalStereographic';
|
||||||
var projectionChoice = "Equirectangular";
|
var projectionChoice = 'Aitoff';
|
||||||
//var projectionChoice = "Aitoff";
|
var projectionChoice = 'Orthographic';
|
||||||
|
var projectionChoice = 'Fahey';
|
||||||
|
var projectionChoice = 'Equirectangular';
|
||||||
|
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
|
||||||
var svgWorld = d3.select("#svgworld");
|
var svgWorld = d3.select('#svgworld');
|
||||||
var idWorld = document.getElementById("svgworld");
|
var idWorld = document.getElementById('svgworld');
|
||||||
|
|
||||||
var svgGraticule = d3.select("#svggraticule");
|
var svgGraticule = d3.select('#svggraticule');
|
||||||
var idGraticule = document.getElementById("svggraticule");
|
var idGraticule = document.getElementById('svggraticule');
|
||||||
|
|
||||||
var svgObservers = d3.select("#svgobservers");
|
var svgObservers = d3.select('#svgobservers');
|
||||||
var idObservers = document.getElementById("svgobservers");
|
var idObservers = document.getElementById('svgobservers');
|
||||||
|
|
||||||
var svgAlmanac = d3.select("#svgalmanac");
|
var svgAlmanac = d3.select('#svgalmanac');
|
||||||
var idAlmanac = document.getElementById("svgalmanac");
|
var idAlmanac = document.getElementById('svgalmanac');
|
||||||
|
|
||||||
var geoPath;
|
var geoPath;
|
||||||
var aProjection;
|
var aProjection;
|
||||||
|
|
||||||
|
var speed = 1e-2;
|
||||||
|
var start = now();
|
||||||
|
|
||||||
|
function now()
|
||||||
|
{
|
||||||
|
return Math.round(Date.now() / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
function draw_world(data_world)
|
function draw_world(data_world)
|
||||||
{
|
{
|
||||||
// console.log("draw_world() " + data_world.features.length);
|
svgWorld.html("");
|
||||||
|
|
||||||
svgWorld.selectAll("path")
|
svgWorld.selectAll("path")
|
||||||
.data(data_world.features)
|
.data(data_world.features)
|
||||||
.enter()
|
.enter()
|
||||||
.append("path")
|
.append("path")
|
||||||
.attr("class", "countries")
|
.attr("class", "countries")
|
||||||
.attr("d", geoPath);
|
.attr("d", geoPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
function draw_graticule()
|
function draw_graticule()
|
||||||
{
|
{
|
||||||
// Graticule
|
|
||||||
var graticule = d3.geoGraticule();
|
var graticule = d3.geoGraticule();
|
||||||
|
|
||||||
// console.log("draw_graticule()");
|
svgGraticule.html("");
|
||||||
|
|
||||||
svgGraticule.selectAll("path")
|
svgGraticule.selectAll("path")
|
||||||
.data(graticule.lines())
|
.data(graticule.lines())
|
||||||
.enter()
|
.enter()
|
||||||
.append("path")
|
.append("path")
|
||||||
.attr("class", "graticule line")
|
.attr("class", "graticule line")
|
||||||
.attr("id", function(d) {
|
.attr("id", function(d) {
|
||||||
var c = d.coordinates;
|
var c = d.coordinates;
|
||||||
if (c[0][0] == c[1][0]) {
|
if (c[0][0] == c[1][0]) {
|
||||||
return (c[0][0] < 0) ? -c[0][0] + "W" : +c[0][0] + "E";
|
return (c[0][0] < 0) ? -c[0][0] + "W" : +c[0][0] + "E";
|
||||||
} else if (c[0][1] == c[1][1]) {
|
} else if (c[0][1] == c[1][1]) {
|
||||||
return (c[0][1] < 0) ? -c[0][1] + "S" : c[0][1] + "N";
|
return (c[0][1] < 0) ? -c[0][1] + "S" : c[0][1] + "N";
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.attr("d", geoPath);
|
.attr("d", geoPath);
|
||||||
|
|
||||||
svgGraticule.selectAll('text')
|
svgGraticule.selectAll('text')
|
||||||
.data(graticule.lines())
|
.data(graticule.lines())
|
||||||
.enter()
|
.enter()
|
||||||
.append("text")
|
.append("text")
|
||||||
.text(function(d) {
|
.text(function(d) {
|
||||||
var c = d.coordinates;
|
var c = d.coordinates;
|
||||||
if ((c[0][0] == c[1][0]) && (c[0][0] % 30 == 0)) {
|
if ((c[0][0] == c[1][0]) && (c[0][0] % 30 == 0)) {
|
||||||
return (c[0][0]);
|
return (c[0][0]);
|
||||||
} else if (c[0][1] == c[1][1]) {
|
} else if (c[0][1] == c[1][1]) {
|
||||||
return (c[0][1]);
|
return (c[0][1]);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.attr("class","label")
|
.attr("class", "label")
|
||||||
.attr("style", function(d) {
|
.attr("style", function(d) {
|
||||||
var c = d.coordinates;
|
var c = d.coordinates;
|
||||||
return (c[0][1] == c[1][1]) ? "text-anchor: end" : "text-anchor: middle";
|
return (c[0][1] == c[1][1]) ? "text-anchor: end" : "text-anchor: middle";
|
||||||
})
|
})
|
||||||
.attr("dx", function(d) {
|
.attr("dx", function(d) {
|
||||||
var c = d.coordinates;
|
var c = d.coordinates;
|
||||||
return (c[0][1] == c[1][1]) ? -10 : 0;
|
return (c[0][1] == c[1][1]) ? -10 : 0;
|
||||||
})
|
})
|
||||||
.attr("dy", function(d) {
|
.attr("dy", function(d) {
|
||||||
var c = d.coordinates;
|
var c = d.coordinates;
|
||||||
return (c[0][1] == c[1][1]) ? 4 : 10;
|
return (c[0][1] == c[1][1]) ? 4 : 10;
|
||||||
})
|
})
|
||||||
.attr('transform', function(d) {
|
.attr('transform', function(d) {
|
||||||
var c = d.coordinates;
|
var c = d.coordinates;
|
||||||
return ('translate(' + aProjection(c[0]) + ')')
|
return ('translate(' + aProjection(c[0]) + ')')
|
||||||
});
|
});
|
||||||
|
|
||||||
svgGraticule.append("path")
|
svgGraticule.append("path")
|
||||||
.datum(graticule.outline)
|
.datum(graticule.outline)
|
||||||
|
@ -102,143 +111,403 @@ function draw_graticule()
|
||||||
|
|
||||||
function get_almanac_valid(data_almanac)
|
function get_almanac_valid(data_almanac)
|
||||||
{
|
{
|
||||||
var a=[];
|
var a = [];
|
||||||
Object.keys(data_almanac).forEach(function(e) {
|
Object.keys(data_almanac).forEach(function(e) {
|
||||||
var o = data_almanac[e];
|
var o = data_almanac[e];
|
||||||
o.sv = e;
|
o.sv = e;
|
||||||
if (o["eph-latitude"] != null) {
|
if (o["eph-latitude"] != null) {
|
||||||
|
o.eph_latitude = o["eph-latitude"]; // json variables with dashes - bad bad
|
||||||
|
o.eph_longitude = o["eph-longitude"];
|
||||||
a.push(o);
|
a.push(o);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var Tooltip;
|
||||||
|
|
||||||
|
function create_tooltop()
|
||||||
|
{
|
||||||
|
// create a tooltip
|
||||||
|
Tooltip = d3.select("#combined")
|
||||||
|
.append("span")
|
||||||
|
.attr("class", "tooltip")
|
||||||
|
.attr("id", "tooltip")
|
||||||
|
.style("opacity", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function to2(num)
|
||||||
|
{
|
||||||
|
return parseFloat(num).toFixed(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw_satellite_to_operator(d)
|
||||||
|
{
|
||||||
|
// get list of observers for this satellite
|
||||||
|
satellite = d3.select("#" + d.name);
|
||||||
|
a = observers_list_almanac_raw(d);
|
||||||
|
for (aa=0;aa<a.length;aa++) {
|
||||||
|
observer = d3.select("#Observer_" + a[aa]);
|
||||||
|
|
||||||
|
if (1) {
|
||||||
|
svgAlmanac.append("line")
|
||||||
|
.attr("class", "radials")
|
||||||
|
.attr("x1", Math.round(satellite.attr("cx")))
|
||||||
|
.attr("y1", Math.round(satellite.attr("cy")))
|
||||||
|
.attr("x2", Math.round(observer.attr("x")) + 4)
|
||||||
|
.attr("y2", Math.round(observer.attr("y")) + 4);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
svgAlmanac.append("line")
|
||||||
|
.attr("class", "radials")
|
||||||
|
.attr("d", function(d) {
|
||||||
|
return {
|
||||||
|
type: "LineString",
|
||||||
|
coordinates: [
|
||||||
|
[Math.round(satellite.attr("cx")), Math.round(satellite.attr("cy"))],
|
||||||
|
[Math.round(observer.attr("x")) + 4, Math.round(observer.attr("y")) + 4]
|
||||||
|
]
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw_operator_to_satellite(d)
|
||||||
|
{
|
||||||
|
// get list of satellites for this observer
|
||||||
|
observer = d3.select("#Observer_" + d.id);
|
||||||
|
a = svs_list_observer_raw(d);
|
||||||
|
for (aa=0;aa<a.length;aa++) {
|
||||||
|
// check the satellite is seen in almanac data - ie. double check.
|
||||||
|
if (data_almanac[a[aa]] && data_almanac[a[aa]].observed) {
|
||||||
|
satellite = d3.select("#" + a[aa]);
|
||||||
|
svgAlmanac.append("line")
|
||||||
|
.attr("class", "radials")
|
||||||
|
.attr("x1", Math.round(satellite.attr("cx")))
|
||||||
|
.attr("y1", Math.round(satellite.attr("cy")))
|
||||||
|
.attr("x2", Math.round(observer.attr("x")) + 4)
|
||||||
|
.attr("y2", Math.round(observer.attr("y")) + 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function draw_almanac(data_almanac)
|
function draw_almanac(data_almanac)
|
||||||
{
|
{
|
||||||
var arr = get_almanac_valid(data_almanac);
|
var arr = get_almanac_valid(data_almanac);
|
||||||
|
|
||||||
console.log("draw_almanac() " + arr.length);
|
svgAlmanac.html("");
|
||||||
|
|
||||||
svgAlmanac.selectAll("circle")
|
// Three function that change the tooltip when user hover / move / leave a cell
|
||||||
.data(arr)
|
var mouseover = function(d) {
|
||||||
.enter()
|
var o = observers_list_almanac(d);
|
||||||
.append("circle")
|
s = d.name + " [" + to2(d.eph_longitude) + "," + to2(d.eph_latitude) + "] " + ((o == "") ? "" : " seen by " + o);
|
||||||
.attr("class", "sats")
|
Tooltip.html(s)
|
||||||
.attr("r", 3)
|
.style("opacity", 1)
|
||||||
.attr("cx", d => aProjection([d["eph-longitude"],d["eph-latitude"]])[0])
|
.style("left", (d3.mouse(this)[0]+30) + "px")
|
||||||
.attr("cy", d => aProjection([d["eph-longitude"],d["eph-latitude"]])[1])
|
.style("top", (d3.mouse(this)[1]) + "px");
|
||||||
.attr("fill", function(d) {
|
draw_satellite_to_operator(d);
|
||||||
switch (d.gnssid) {
|
}
|
||||||
case 0: return "green"; // GPS
|
var mousemove = function(d) {
|
||||||
case 1: return "gray"; // SBAS - not coded
|
var o = observers_list_almanac(d);
|
||||||
case 2: return "blue"; // Galileo
|
s = d.name + " [" + to2(d.eph_longitude) + "," + to2(d.eph_latitude) + "] " + ((o == "") ? "" : " seen by " + o);
|
||||||
case 3: return "red"; // BeiDou
|
Tooltip.html(s)
|
||||||
case 4: return "gray"; // IMES - not coded
|
.style("left", (d3.mouse(this)[0]+30) + "px")
|
||||||
case 5: return "gray"; // QZSS - not coded
|
.style("top", (d3.mouse(this)[1]) + "px");
|
||||||
case 6: return "yellow"; // GLONASS
|
}
|
||||||
default: return "magenta"; // - should not happen
|
var mouseleave = function(d) {
|
||||||
}
|
Tooltip.html("")
|
||||||
});
|
.style("opacity", 0);
|
||||||
|
d3.selectAll(".radials").remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
// text first as we want the satellite circles to be always above them!
|
||||||
svgAlmanac.selectAll("text")
|
svgAlmanac.selectAll("text")
|
||||||
.data(arr)
|
.data(arr)
|
||||||
.enter()
|
.enter()
|
||||||
.append("text")
|
.append("text")
|
||||||
.attr("class", "labels")
|
.attr("class", "labels")
|
||||||
.text(d => d.sv)
|
.text(r => r.sv)
|
||||||
.attr("dx", d => 5+aProjection([d["eph-longitude"],d["eph-latitude"]])[0])
|
.attr("dx", r => (aProjection([r.eph_longitude, r.eph_latitude])[0] + 5))
|
||||||
.attr("dy", d => 5+aProjection([d["eph-longitude"],d["eph-latitude"]])[1])
|
.attr("dy", r => (aProjection([r.eph_longitude, r.eph_latitude])[1] + 5))
|
||||||
.attr("fill", function(d) {
|
.attr("fill", r => ((r.observed==true) ? "black" : "#666666"))
|
||||||
if (d.observed==true)
|
.attr("fill-opacity", r => ((r.observed==true) ? "1.0" : ".5"))
|
||||||
return "black";
|
.attr("stroke-opacity", r => ((r.observed==true) ? "1.0" : ".5"))
|
||||||
return "#666666";
|
.attr("font-weight", r => ((r.observed==true) ? "bold" : null));
|
||||||
|
|
||||||
|
svgAlmanac.selectAll("circle")
|
||||||
|
.data(arr)
|
||||||
|
.enter()
|
||||||
|
.append("circle")
|
||||||
|
.attr("class", "satellites")
|
||||||
|
.attr("id", r => r["name"])
|
||||||
|
.attr("r", 3)
|
||||||
|
.attr("cx", r => aProjection([r.eph_longitude, r.eph_latitude])[0])
|
||||||
|
.attr("cy", r => aProjection([r.eph_longitude, r.eph_latitude])[1])
|
||||||
|
.attr("fill", function(r) {
|
||||||
|
switch (r.gnssid) {
|
||||||
|
case 0: return "green"; // GPS
|
||||||
|
case 1: return "gray"; // SBAS - not coded
|
||||||
|
case 2: return "blue"; // Galileo
|
||||||
|
case 3: return "red"; // BeiDou
|
||||||
|
case 4: return "gray"; // IMES - not coded
|
||||||
|
case 5: return "gray"; // QZSS - not coded
|
||||||
|
case 6: return "yellow"; // GLONASS
|
||||||
|
default: return "magenta"; // - should not happen
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.attr("font-weight", function(d) {
|
.attr("fill-opacity", r => ((r.observed==true) ? "1.0" : ".5"))
|
||||||
if (d.observed==true)
|
.attr("stroke-opacity", r => ((r.observed==true) ? "1.0" : ".5"))
|
||||||
return "bold";
|
.on("mouseover", mouseover)
|
||||||
return null;
|
.on("mousemove", mousemove)
|
||||||
});
|
.on("mouseleave", mouseleave);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function age_in_seconds(t)
|
||||||
|
{
|
||||||
|
return now() - t;
|
||||||
|
}
|
||||||
|
|
||||||
|
function a_to_s(a)
|
||||||
|
{
|
||||||
|
var r = "";
|
||||||
|
for (aa=0;aa<a.length;aa++) {
|
||||||
|
r += a[aa] + " ";
|
||||||
|
}
|
||||||
|
if (r.length > 0) {
|
||||||
|
r = r.slice(0, -1);
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
function svs_list_observer_raw(d)
|
||||||
|
{
|
||||||
|
var r = []
|
||||||
|
var svs = d.svs;
|
||||||
|
for (s in svs) {
|
||||||
|
// check the satellite is seen in almanac data - ie. double check.
|
||||||
|
if (data_almanac[svs[s].name]) {
|
||||||
|
r.push(svs[s].name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
function observers_list_almanac_raw(d)
|
||||||
|
{
|
||||||
|
var a = [];
|
||||||
|
var s_id = d.name;
|
||||||
|
|
||||||
|
for (oo=0;oo<data_observers.length;oo++) {
|
||||||
|
o = data_observers[oo];
|
||||||
|
o_id = o.id;
|
||||||
|
for (s in o.svs) {
|
||||||
|
if (s_id == o.svs[s].name) {
|
||||||
|
a.push(o_id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
function observers_list_almanac(d)
|
||||||
|
{
|
||||||
|
return a_to_s(observers_list_almanac_raw(d));
|
||||||
|
}
|
||||||
|
|
||||||
|
function svs_list_observer(d)
|
||||||
|
{
|
||||||
|
return a_to_s(svs_list_observer_raw(d));
|
||||||
|
}
|
||||||
|
|
||||||
|
function observer_up(d)
|
||||||
|
{
|
||||||
|
var considered_old = 15 * 60; // 15 mins
|
||||||
|
|
||||||
|
if (age_in_seconds(d["last-seen"]) > considered_old) {
|
||||||
|
// data is old
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (Object.keys(d.svs).length == 0) {
|
||||||
|
// nothing visible
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function draw_observers(data_observers)
|
function draw_observers(data_observers)
|
||||||
{
|
{
|
||||||
console.log("draw_observers() " + data_observers.length);
|
// Three function that change the tooltip when user hover / move / leave a cell
|
||||||
|
var mouseover = function(d) {
|
||||||
|
var o = svs_list_observer(d);
|
||||||
|
s = d.id + ": [" + to2(d.longitude) + "," + to2(d.latitude) + "]" + ((o == "") ? "" : " sees " + svs_list_observer(d));
|
||||||
|
Tooltip.html(s)
|
||||||
|
.style("opacity", 1)
|
||||||
|
.style("left", (d3.mouse(this)[0]+30) + "px")
|
||||||
|
.style("top", (d3.mouse(this)[1]) + "px");
|
||||||
|
draw_operator_to_satellite(d);
|
||||||
|
}
|
||||||
|
var mousemove = function(d) {
|
||||||
|
var o = svs_list_observer(d);
|
||||||
|
s = d.id + ": [" + to2(d.longitude) + "," + to2(d.latitude) + "]" + ((o == "") ? "" : " sees " + svs_list_observer(d));
|
||||||
|
Tooltip.html(s)
|
||||||
|
.style("left", (d3.mouse(this)[0]+30) + "px")
|
||||||
|
.style("top", (d3.mouse(this)[1]) + "px");
|
||||||
|
}
|
||||||
|
var mouseleave = function(d) {
|
||||||
|
Tooltip.html("")
|
||||||
|
.style("opacity", 0);
|
||||||
|
d3.selectAll(".radials").remove();
|
||||||
|
}
|
||||||
|
|
||||||
svgObservers.selectAll("rect")
|
if (0) {
|
||||||
.data(data_observers)
|
// we draw a geo correct rectangle
|
||||||
.enter()
|
var observer_degrees = 3.0;
|
||||||
.append("rect")
|
svgAlmanac.selectAll("div")
|
||||||
.attr("class", "sats")
|
.data(data_observers)
|
||||||
.attr("width", 8)
|
.enter()
|
||||||
.attr("height", 8)
|
.append("path")
|
||||||
.attr("x", d => aProjection([d["longitude"],d["latitude"]])[0]-4)
|
.attr("class", r => (observer_up(r) ? "observers" : "observers down"))
|
||||||
.attr("y", d => aProjection([d["longitude"],d["latitude"]])[1]-4)
|
.attr("id", r => ("Observer_" + r["id"]))
|
||||||
.attr("fill", function(d) { return "black"; });
|
.attr("d", function(d) {
|
||||||
|
return geoPath({
|
||||||
|
type: "LineString",
|
||||||
|
coordinates: [
|
||||||
|
[d.longitude - observer_degrees/2, d.latitude - observer_degrees/2],
|
||||||
|
[d.longitude - observer_degrees/2, d.latitude + observer_degrees/2],
|
||||||
|
[d.longitude + observer_degrees/2, d.latitude + observer_degrees/2],
|
||||||
|
[d.longitude + observer_degrees/2, d.latitude - observer_degrees/2],
|
||||||
|
]
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.on("mouseover", mouseover)
|
||||||
|
.on("mousemove", mousemove)
|
||||||
|
.on("mouseleave", mouseleave);
|
||||||
|
} else {
|
||||||
|
rect_size = 8;
|
||||||
|
svgAlmanac.selectAll("div")
|
||||||
|
.data(data_observers)
|
||||||
|
.enter()
|
||||||
|
.append("rect")
|
||||||
|
.attr("class", r => (observer_up(r) ? "observers" : "observers down"))
|
||||||
|
.attr("id", r => ("Observer_" + r["id"]))
|
||||||
|
.attr("width", rect_size)
|
||||||
|
.attr("height", rect_size)
|
||||||
|
.attr("x", r => (aProjection([r.longitude, r.latitude])[0] - rect_size/2))
|
||||||
|
.attr("y", r => (aProjection([r.longitude, r.latitude])[1] - rect_size/2))
|
||||||
|
.on("mouseover", mouseover)
|
||||||
|
.on("mousemove", mousemove)
|
||||||
|
.on("mouseleave", mouseleave);
|
||||||
|
}
|
||||||
|
|
||||||
|
// kick off the annimation - if needed
|
||||||
|
annimate_down_observers();
|
||||||
|
}
|
||||||
|
|
||||||
|
var down_timer;
|
||||||
|
|
||||||
|
function annimate_down_observers()
|
||||||
|
{
|
||||||
|
clearTimeout(down_timer);
|
||||||
|
|
||||||
|
var down = document.getElementsByClassName('observers down');
|
||||||
|
if (down.length > 0) {
|
||||||
|
// if we have an observer that is down - lets annoutate it!
|
||||||
|
for (var ii=0, ll=down.length; ii<ll; ii++){
|
||||||
|
down[ii].style.fill = (down[ii].style.fill == "black") ? "red" : "black";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var seconds = 1;
|
||||||
|
down_timer = setTimeout(annimate_down_observers, seconds * 1000.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function draw_observers_coverage(data_observers)
|
function draw_observers_coverage(data_observers)
|
||||||
{
|
{
|
||||||
var radius = 55; // XXX fix
|
var radius = 65; // XXX fix
|
||||||
var geoCircle = d3.geoCircle();
|
var geoCircle = d3.geoCircle();
|
||||||
svgObservers.selectAll("path")
|
|
||||||
|
svgObservers.html("");
|
||||||
|
|
||||||
|
// we draw a geo correct circle
|
||||||
|
svgObservers.selectAll("div")
|
||||||
.data(data_observers)
|
.data(data_observers)
|
||||||
.enter()
|
.enter()
|
||||||
.append("path")
|
.append("path")
|
||||||
.attr("class", "coverage")
|
.attr("class", r => (observer_up(r) ? "coverage" : "coverage down"))
|
||||||
.attr("d", function(r) {
|
.attr("d", function(r) {
|
||||||
// console.log([r["longitude"], r["latitude"]] + " = " + aProjection([r["longitude"], r["latitude"]]));
|
return geoPath(geoCircle.center([r["longitude"], r["latitude"]]).radius(radius)());
|
||||||
return geoPath(geoCircle.center([r["longitude"],r["latitude"]]).radius(radius)());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var display_observers_count = 0;
|
var data_almanac = null;
|
||||||
|
var data_observers = null;
|
||||||
|
|
||||||
function do_update_almanac(error, results)
|
function do_update_almanac_observers(error, results)
|
||||||
{
|
{
|
||||||
var data_almanac = results[0];
|
var updated_almanac = false;
|
||||||
var data_observers = results[1];
|
var updated_observers = false;
|
||||||
|
|
||||||
// console.log("do_update_almanac() " + Object.keys(data_almanac).length + " " + data_observers.length);
|
if (results.length > 0 && results[0]) {
|
||||||
|
data_almanac = results[0];
|
||||||
if (display_observers_count == 0) {
|
updated_almanac = true;
|
||||||
// does not need that much updating!
|
}
|
||||||
svgObservers.html("");
|
if (results.length > 1 && results[1]) {
|
||||||
draw_observers(data_observers);
|
data_observers = results[1];
|
||||||
draw_observers_coverage(data_observers)
|
updated_observers = true;
|
||||||
display_observers_count = 10;
|
|
||||||
}
|
}
|
||||||
display_observers_count--;
|
|
||||||
|
|
||||||
// We write into the svgalmanac area - so clean it and rewrite it
|
// XXX cheat for now - we need until we fix d3/svg bug above
|
||||||
svgAlmanac.html("");
|
updated_almanac = true;
|
||||||
draw_almanac(data_almanac);
|
updated_observers = true;
|
||||||
|
|
||||||
|
if (updated_almanac) {
|
||||||
|
// We write into the svgalmanac area - so clean it and rewrite it
|
||||||
|
draw_almanac(data_almanac);
|
||||||
|
}
|
||||||
|
if (updated_observers) {
|
||||||
|
draw_observers_coverage(data_observers)
|
||||||
|
draw_observers(data_observers);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var repeat;
|
var repeat_timer;
|
||||||
|
var display_observers_count = 0;
|
||||||
|
|
||||||
function do_timer()
|
function do_timer()
|
||||||
{
|
{
|
||||||
var seconds = 60;
|
clearTimeout(repeat_timer);
|
||||||
clearTimeout(repeat);
|
|
||||||
repeat = setTimeout(do_timer, 1000.0*seconds);
|
|
||||||
|
|
||||||
d3.queue(1)
|
if (display_observers_count == 0) {
|
||||||
.defer(d3.json, fileAlmanac)
|
// observers does not need that much updating!
|
||||||
.defer(d3.json, fileObservers)
|
d3.queue(1)
|
||||||
.awaitAll(do_update_almanac);
|
.defer(d3.json, fileAlmanac + '?t=' + now())
|
||||||
|
.defer(d3.json, fileObservers + '?t=' + now())
|
||||||
|
.awaitAll(do_update_almanac_observers);
|
||||||
|
display_observers_count = 10;
|
||||||
|
} else {
|
||||||
|
// just queue an update to almanac
|
||||||
|
d3.queue(1)
|
||||||
|
.defer(d3.json, fileAlmanac + '?t=' + now())
|
||||||
|
.awaitAll(do_update_almanac_observers);
|
||||||
|
}
|
||||||
|
display_observers_count--;
|
||||||
|
|
||||||
|
var seconds = 60;
|
||||||
|
repeat_timer = setTimeout(do_timer, seconds * 1000.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_projection(data_world)
|
function set_projection(data_world)
|
||||||
{
|
{
|
||||||
// var aProjection = d3.geoMercator().scale(100).translate([250,250]);
|
// var aProjection = d3.geoMercator().scale(100).translate([250, 250]);
|
||||||
// all this complexity is so we can scale to full screen.
|
// all this complexity is so we can scale to full screen.
|
||||||
// see: https://stackoverflow.com/questions/14492284/center-a-map-in-d3-given-a-geojson-object
|
// see: https://stackoverflow.com/questions/14492284/center-a-map-in-d3-given-a-geojson-object
|
||||||
|
|
||||||
var center = [0,0]; // This is very Euro-centric - but that's how these projections works.
|
var center = [0, 0]; // This is very Euro-centric - but that's how these projections works.
|
||||||
var scale = 210; // No idea what this does
|
var scale = 191; // No idea what this does
|
||||||
|
|
||||||
|
var svgCombined = d3.select('#combined');
|
||||||
var idCombined = document.getElementById("combined");
|
var idCombined = document.getElementById("combined");
|
||||||
|
|
||||||
svgWorld = d3.select("#svgworld");
|
svgWorld = d3.select("#svgworld");
|
||||||
|
@ -247,34 +516,41 @@ function set_projection(data_world)
|
||||||
svgGraticule = d3.select("#svggraticule");
|
svgGraticule = d3.select("#svggraticule");
|
||||||
idGraticule = document.getElementById("svggraticule");
|
idGraticule = document.getElementById("svggraticule");
|
||||||
|
|
||||||
|
var offset = [idCombined.clientWidth/2, idCombined.clientHeight/2];
|
||||||
|
|
||||||
switch(projectionChoice) {
|
switch(projectionChoice) {
|
||||||
default:
|
default:
|
||||||
console.log(projectionChoice + ": not coded");
|
console.log(projectionChoice + ': not coded');
|
||||||
// fall thru to Equirectangular
|
// fall thru to Equirectangular
|
||||||
case 'Equirectangular':
|
case 'Equirectangular':
|
||||||
aProjection = d3.geoEquirectangular()
|
aProjection = d3.geoEquirectangular()
|
||||||
.scale(scale)
|
.scale(scale)
|
||||||
.translate([idCombined.clientWidth/2,idCombined.clientHeight/2]);
|
.translate(offset);
|
||||||
break;
|
break;
|
||||||
case 'Aitoff':
|
case 'Aitoff':
|
||||||
aProjection = d3.geoAitoff()
|
aProjection = d3.geoAitoff()
|
||||||
.scale(scale)
|
.scale(scale)
|
||||||
.translate([idCombined.clientWidth/2,idCombined.clientHeight/2]);
|
.translate(offset);
|
||||||
break;
|
break;
|
||||||
case 'CylindricalStereographic':
|
case 'CylindricalStereographic':
|
||||||
aProjection = d3.geoCylindricalStereographic()
|
aProjection = d3.geoCylindricalStereographic()
|
||||||
.scale(scale)
|
.scale(scale)
|
||||||
.translate([idCombined.clientWidth/2,idCombined.clientHeight/2]);
|
.translate(offset);
|
||||||
break;
|
break;
|
||||||
case 'Fahey':
|
case 'Fahey':
|
||||||
aProjection = d3.geoFahey()
|
aProjection = d3.geoFahey()
|
||||||
.scale(scale)
|
.scale(scale)
|
||||||
.translate([idCombined.clientWidth/2,idCombined.clientHeight/2]);
|
.translate(offset);
|
||||||
break;
|
break;
|
||||||
case 'Gilbert':
|
case 'Gilbert':
|
||||||
aProjection = d3.geoGilbert()
|
aProjection = d3.geoGilbert()
|
||||||
.scale(scale)
|
.scale(scale)
|
||||||
.translate([idCombined.clientWidth/2,idCombined.clientHeight/2]);
|
.translate(offset);
|
||||||
|
break;
|
||||||
|
case 'Orthographic':
|
||||||
|
aProjection = d3.geoOrthographic()
|
||||||
|
.scale(scale)
|
||||||
|
.translate(offset);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,87 +559,104 @@ function set_projection(data_world)
|
||||||
|
|
||||||
// using the path determine the bounds of the current map and use
|
// using the path determine the bounds of the current map and use
|
||||||
// these to determine better values for the scale and translation
|
// these to determine better values for the scale and translation
|
||||||
var bounds = geoPath.bounds(data_world);;
|
var bounds = geoPath.bounds(data_world);
|
||||||
var hscale = scale*idCombined.clientWidth / (bounds[1][0] - bounds[0][0]);
|
var hscale = scale * (idCombined.clientWidth - 40) / (bounds[1][0] - bounds[0][0]);
|
||||||
var vscale = scale*idCombined.clientHeight / (bounds[1][1] - bounds[0][1]);
|
var vscale = scale * (idCombined.clientHeight - 40) / (bounds[1][1] - bounds[0][1]);
|
||||||
scale = (hscale < vscale) ? hscale : vscale;
|
scale = (hscale < vscale) ? hscale : vscale;
|
||||||
var offset = [
|
var offset = [
|
||||||
idCombined.clientWidth - (bounds[0][0] + bounds[1][0])/2,
|
idCombined.clientWidth - (bounds[0][0] + bounds[1][0])/2,
|
||||||
idCombined.clientHeight - (bounds[0][1] + bounds[1][1])/2
|
idCombined.clientHeight - (bounds[0][1] + bounds[1][1])/2
|
||||||
];
|
];
|
||||||
|
|
||||||
if (0) {
|
if (0) {
|
||||||
|
// new projection
|
||||||
// new projection
|
switch(projectionChoice) {
|
||||||
switch(projectionChoice) {
|
default:
|
||||||
default:
|
console.log(projectionChoice + ': not coded');
|
||||||
console.log(projectionChoice + ": not coded");
|
// fall thru to Equirectangular
|
||||||
// fall thru to Equirectangular
|
case 'Equirectangular':
|
||||||
case 'Equirectangular':
|
aProjection = d3.geoEquirectangular()
|
||||||
aProjection = d3.geoEquirectangular()
|
.center(center)
|
||||||
.center(center)
|
.scale(scale)
|
||||||
.scale(scale)
|
.translate(offset);
|
||||||
.translate(offset);
|
break;
|
||||||
break;
|
case 'Aitoff':
|
||||||
case 'Aitoff':
|
aProjection = d3.geoAitoff()
|
||||||
aProjection = d3.geoAitoff()
|
.center(center) .scale(scale)
|
||||||
.center(center) .scale(scale)
|
.translate(offset);
|
||||||
.translate(offset);
|
break;
|
||||||
break;
|
case 'CylindricalStereographic':
|
||||||
case 'CylindricalStereographic':
|
aProjection = d3.geoCylindricalStereographic()
|
||||||
aProjection = d3.geoCylindricalStereographic()
|
.center(center)
|
||||||
.center(center)
|
.scale(scale)
|
||||||
.scale(scale)
|
.translate(offset);
|
||||||
.translate(offset);
|
break;
|
||||||
break;
|
case 'Fahey':
|
||||||
case 'Fahey':
|
aProjection = d3.geoFahey()
|
||||||
aProjection = d3.geoFahey()
|
.center(center)
|
||||||
.center(center)
|
.scale(scale)
|
||||||
.scale(scale)
|
.translate(offset);
|
||||||
.translate(offset);
|
break;
|
||||||
break;
|
case 'Gilbert':
|
||||||
case 'Gilbert':
|
aProjection = d3.geoGilbert()
|
||||||
aProjection = d3.geoGilbert()
|
.center(center)
|
||||||
.center(center)
|
.scale(scale)
|
||||||
.scale(scale)
|
.translate(offset);
|
||||||
.translate(offset);
|
break;
|
||||||
break;
|
case 'Orthographic':
|
||||||
|
aProjection = d3.geoOrthographic()
|
||||||
|
.center(center)
|
||||||
|
.scale(scale)
|
||||||
|
.translate(offset);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
svgCombined.attr("width", idCombined.clientWidth);
|
||||||
|
svgCombined.attr("height", idCombined.clientHeight);
|
||||||
|
|
||||||
console.log("do_draw_world() " + "width=" + idCombined.clientWidth + "," + "height=" + idCombined.clientHeight);
|
padding = 0 // 20 - see svg: padding in css
|
||||||
|
|
||||||
svgWorld.attr("width", idCombined.clientWidth);
|
svgWorld.attr("width", idCombined.clientWidth - padding * 2);
|
||||||
svgWorld.attr("height", idCombined.clientHeight);
|
svgWorld.attr("height", idCombined.clientHeight - padding * 2);
|
||||||
|
|
||||||
svgObservers.attr("width", idCombined.clientWidth);
|
svgObservers.attr("width", idCombined.clientWidth - padding * 2);
|
||||||
svgObservers.attr("height", idCombined.clientHeight);
|
svgObservers.attr("height", idCombined.clientHeight - padding * 2);
|
||||||
|
|
||||||
svgGraticule.attr("height", idCombined.clientHeight);
|
svgGraticule.attr("width", idCombined.clientWidth - padding * 2);
|
||||||
svgGraticule.attr("width", idCombined.clientWidth);
|
svgGraticule.attr("height", idCombined.clientHeight - padding * 2);
|
||||||
|
|
||||||
svgAlmanac.attr("height", idCombined.clientHeight);
|
svgAlmanac.attr("width", idCombined.clientWidth - padding * 2);
|
||||||
svgAlmanac.attr("width", idCombined.clientWidth);
|
svgAlmanac.attr("height", idCombined.clientHeight - padding * 2);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function do_draw_world(data_world)
|
function do_draw_world(data_world)
|
||||||
{
|
{
|
||||||
// console.log("do_draw_world()");
|
|
||||||
|
|
||||||
set_projection(data_world);
|
set_projection(data_world);
|
||||||
|
|
||||||
svgWorld.html("");
|
create_tooltop();
|
||||||
draw_world(data_world);
|
|
||||||
|
|
||||||
svgGraticule.html("");
|
draw_world(data_world);
|
||||||
draw_graticule();
|
draw_graticule();
|
||||||
|
|
||||||
|
if (projectionChoice == 'Orthographic') {
|
||||||
|
// rotating globe is done with a constant timer
|
||||||
|
d3.timer(function() {
|
||||||
|
var lambda = speed * (Date.now() - start); // λ
|
||||||
|
var phi = -15; // φ
|
||||||
|
aProjection.rotate([lambda + 180, -phi]);
|
||||||
|
|
||||||
|
draw_world(data_world);
|
||||||
|
draw_graticule();
|
||||||
|
do_update_almanac_observers(null, [])
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function read_world()
|
function read_world()
|
||||||
{
|
{
|
||||||
// console.log("read_world()");
|
|
||||||
d3.json(fileWorld, function(result) {
|
d3.json(fileWorld, function(result) {
|
||||||
var data_world = result;
|
var data_world = result;
|
||||||
do_draw_world(data_world);
|
do_draw_world(data_world);
|
||||||
|
@ -375,7 +668,6 @@ function read_world()
|
||||||
|
|
||||||
function geo_start()
|
function geo_start()
|
||||||
{
|
{
|
||||||
// console.log("geo_start()");
|
|
||||||
read_world();
|
read_world();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue