move and fix up geo2 work
parent
6ea5907c5d
commit
906c95041e
268
geo2/geo.css
268
geo2/geo.css
|
@ -1,268 +0,0 @@
|
|||
body {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
float: left;
|
||||
background-color: #ccc;
|
||||
font-family: verdana, helvetica, arial, sans-serif;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 5px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
#galmongeo {
|
||||
border: blue 1px solid;
|
||||
margin: 5px;
|
||||
padding: 10px;
|
||||
width: 98%;
|
||||
background-color: white;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
#galmoninfo {
|
||||
border-top: blue 1px solid;
|
||||
margin-bottom: 0px;
|
||||
display: block;
|
||||
}
|
||||
#galmontext {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
float: left;
|
||||
font-size: 20px;
|
||||
}
|
||||
#galmonchoice {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
float: right;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
#combined {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
background-color: transparent;
|
||||
height: 620px;
|
||||
width: 100%;
|
||||
//margin-left: auto;
|
||||
//margin-right: auto;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
border: gray 1px solid;
|
||||
}
|
||||
|
||||
#svgworld {
|
||||
position: absolute; top: 0; right: 0; bottom: 0; left: 0;
|
||||
display: inline-block;
|
||||
// border: green 1px solid;
|
||||
}
|
||||
|
||||
#svggraticule {
|
||||
position: absolute; top: 0; right: 0; bottom: 0; left: 0;
|
||||
display: inline-block;
|
||||
// border: blue 1px solid;
|
||||
}
|
||||
|
||||
#svgobservers {
|
||||
position: absolute; top: 0; right: 0; bottom: 0; left: 0;
|
||||
display: inline-block;
|
||||
// border: blue 1px solid;
|
||||
}
|
||||
|
||||
#svgalmanac {
|
||||
position: absolute; top: 0; right: 0; bottom: 0; left: 0;
|
||||
display: inline-block;
|
||||
// border: blue 1px solid;
|
||||
}
|
||||
|
||||
#controls {
|
||||
position: absolute; bottom: 0; right: 0;
|
||||
display: inline-block;
|
||||
margin: 5px;
|
||||
}
|
||||
#rotation {
|
||||
position: absolute; bottom: 0; left: 0;
|
||||
display: inline-block;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
svg {
|
||||
//padding: 20px;
|
||||
}
|
||||
|
||||
// #Coverage_95: { fill: #800; fill-opacity: 0.2; }
|
||||
// #Coverage_95: { fill: #800; fill-opacity: 0.2; }
|
||||
// #Coverage_95: { fill: #800; fill-opacity: 0.2; }
|
||||
// #Coverage_98: { fill: #800; fill-opacity: 0.2; }
|
||||
// #Coverage_99: { fill: #800; fill-opacity: 0.2; }
|
||||
|
||||
path.countries {
|
||||
background: yellow;
|
||||
stroke-width: 1;
|
||||
stroke: #75739F;
|
||||
fill: #7ECFE6;
|
||||
}
|
||||
path.coverage {
|
||||
stroke-width: 1;
|
||||
stroke: #888;
|
||||
fill: #888;
|
||||
fill-opacity: 0.1;
|
||||
}
|
||||
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: red;
|
||||
fill: red;
|
||||
fill-opacity: .75;
|
||||
}
|
||||
text.observers {
|
||||
font-family: courier new, courier;
|
||||
font-style: italic;
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
}
|
||||
text.observers.down {
|
||||
color: red;
|
||||
}
|
||||
circle.satellites {
|
||||
stroke-width: 1;
|
||||
stroke: #4F442B;
|
||||
}
|
||||
.radials {
|
||||
stroke-width: 1;
|
||||
stroke: red;
|
||||
fill: none;
|
||||
}
|
||||
text.labels {
|
||||
font-family: courier new, courier;
|
||||
font-style: italic;
|
||||
}
|
||||
circle.centroid {
|
||||
fill: #75739F;
|
||||
pointer-events: none;
|
||||
}
|
||||
rect.bbox {
|
||||
fill: none;
|
||||
stroke-dasharray: 5 5;
|
||||
stroke: #75739F;
|
||||
stroke-width: 2;
|
||||
pointer-events: none;
|
||||
}
|
||||
path.graticule {
|
||||
fill: none;
|
||||
stroke-width: 1;
|
||||
stroke: #9A8B7A;
|
||||
}
|
||||
path.graticule.line {
|
||||
stroke: #E5E1DE;
|
||||
}
|
||||
path.graticule.outline {
|
||||
stroke: #9A8B7A;
|
||||
}
|
||||
|
||||
path.merged {
|
||||
fill: #9A8B7A;
|
||||
stroke: #4F442B;
|
||||
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;
|
||||
}
|
||||
|
||||
.mybutton {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
padding-left: 35px;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.checkmark {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
background-color: #eee;
|
||||
}
|
||||
.mybutton:hover input ~ .checkmark {
|
||||
background-color: #ccc;
|
||||
}
|
||||
.mybutton input {
|
||||
display: none;
|
||||
}
|
||||
.mybutton input:checked ~ .checkmark {
|
||||
background-color: #2196F3;
|
||||
}
|
||||
.mybutton input:checked ~ .checkmark:after {
|
||||
display: block;
|
||||
}
|
||||
.checkmark:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
display: none;
|
||||
}
|
||||
.mybutton .checkmark:after {
|
||||
left: 9px;
|
||||
top: 5px;
|
||||
width: 5px;
|
||||
height: 10px;
|
||||
border: solid white;
|
||||
border-width: 0 3px 3px 0;
|
||||
-webkit-transform: rotate(45deg);
|
||||
-ms-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.rotatetable {
|
||||
display: table;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0px;
|
||||
}
|
||||
.rotatetable td {
|
||||
vertical-align: middle;
|
||||
padding: 0px 3px 0px 3px;
|
||||
margin: 0px;
|
||||
}
|
||||
.myrotate {
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
#controls {
|
||||
margin: 0px 5px 5px 0px;
|
||||
}
|
||||
#rotation {
|
||||
margin: 0px 0px 5px 5px;
|
||||
border: 1px gray solid;
|
||||
background: #eee;
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>galmon.eu geo</title>
|
||||
<link rel="stylesheet" type="text/css" href="geo.css">
|
||||
|
||||
<script src='//cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js'></script>
|
||||
|
||||
<script src="https://d3js.org/d3.v4.min.js"></script>
|
||||
<script src="https://d3js.org/topojson.v1.min.js"></script>
|
||||
<script src="https://d3js.org/d3-geo-projection.v2.min.js"></script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="galmongeo">
|
||||
<h1>galmon.eu geo</h1>
|
||||
<div id="galmoninfo">
|
||||
<span id="galmontext">
|
||||
This is a live map from the <a href="/">galmon.eu</a> project
|
||||
</span>
|
||||
<span id="galmonchoice">
|
||||
<label id="observer_map" class="mybutton">Observer<input type="checkbox" checked="checked" ><span class="checkmark"></span></label>
|
||||
|
|
||||
<label id="coverage_map" class="mybutton">Coverage<input type="checkbox" checked="checked" ><span class="checkmark"></span></label>
|
||||
|
|
||||
<label id="constilation1" class="mybutton">GPS <input type="checkbox" checked="checked" ><span class="checkmark"></span></label>
|
||||
<label id="constilation2" class="mybutton">Galileo <input type="checkbox" checked="checked" ><span class="checkmark"></span></label>
|
||||
<label id="constilation3" class="mybutton">BeiBou <input type="checkbox" checked="checked" ><span class="checkmark"></span></label>
|
||||
<!--
|
||||
<label id="constilation4" class="mybutton">IMES <input type="checkbox" disabled="disabled"><span class="checkmark"></span></label>
|
||||
<label id="constilation5" class="mybutton">QZSS <input type="checkbox" disabled="disabled"><span class="checkmark"></span></label>
|
||||
-->
|
||||
<label id="constilation6" class="mybutton">GLONASS <input type="checkbox" checked="checked" ><span class="checkmark"></span></label>
|
||||
</span>
|
||||
</div>
|
||||
<div id="combined">
|
||||
<svg id="svgworld"></svg>
|
||||
<svg id="svggraticule"></svg>
|
||||
<svg id="svgobservers"></svg>
|
||||
<svg id="svgalmanac"></svg>
|
||||
<span id="controls">
|
||||
<label id="display_all" class="mybutton">Display All<input type="checkbox"><span class="checkmark"></span></label>
|
||||
</p>
|
||||
<!--
|
||||
<label id="show_history" class="mybutton">History<input type="checkbox"><span class="checkmark"></span></label>
|
||||
</p>
|
||||
<label id="show_animate" class="mybutton">Animate<input type="checkbox"><span class="checkmark"></span></label>
|
||||
</p>
|
||||
-->
|
||||
<label id="update_projection" class="mybutton">Projection<span class="checkmark"></span></label>
|
||||
</span>
|
||||
<span id="rotation">
|
||||
<table class="rotatetable">
|
||||
<tr>
|
||||
<td></td>
|
||||
<td><span id="rotate_north" class="myrotate">⇧</span></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span id="rotate_west" class="myrotate">⇦</span></td>
|
||||
<td><span id="rotate_center" class="myrotate">◯</span></td>
|
||||
<td><span id="rotate_east" class="myrotate">⇨</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td><span id="rotate_south" class="myrotate">⇩</span></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="geo.js"></script>
|
||||
</body>
|
||||
</html>
|
930
geo2/geo.js
930
geo2/geo.js
|
@ -1,930 +0,0 @@
|
|||
//
|
||||
//
|
||||
//
|
||||
|
||||
var fileWorld = 'world.geojson';
|
||||
var fileAlmanac = '//galmon.eu/experimental/almanac.json'
|
||||
var fileObservers = '//galmon.eu/experimental/observers.json'
|
||||
|
||||
var projectionChoices = [
|
||||
'Fahey', // The prefered one
|
||||
'Aitoff', // Interesting; but like Fahey
|
||||
// 'Orthographic', // 3d globe - not perfectly coded for now XXX
|
||||
'CylindricalStereographic', // Beyond square
|
||||
'Equirectangular', // The boring one
|
||||
]
|
||||
|
||||
var projectionChoice = 0;
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
var svgWorld = d3.select('#svgworld');
|
||||
var idWorld = document.getElementById('svgworld');
|
||||
|
||||
var svgGraticule = d3.select('#svggraticule');
|
||||
var idGraticule = document.getElementById('svggraticule');
|
||||
|
||||
var svgObservers = d3.select('#svgobservers');
|
||||
var idObservers = document.getElementById('svgobservers');
|
||||
|
||||
var svgAlmanac = d3.select('#svgalmanac');
|
||||
var idAlmanac = document.getElementById('svgalmanac');
|
||||
|
||||
var geoPath;
|
||||
var aProjection;
|
||||
|
||||
var speed = 1e-2;
|
||||
var start = now();
|
||||
|
||||
function now()
|
||||
{
|
||||
return Math.round(Date.now() / 1000);
|
||||
}
|
||||
|
||||
function draw_world(data_world)
|
||||
{
|
||||
svgWorld.html("");
|
||||
|
||||
svgWorld.selectAll("path")
|
||||
.data(data_world.features)
|
||||
.enter()
|
||||
.append("path")
|
||||
.attr("class", "countries")
|
||||
.attr("d", geoPath);
|
||||
}
|
||||
|
||||
function draw_graticule()
|
||||
{
|
||||
var graticule = d3.geoGraticule();
|
||||
|
||||
svgGraticule.html("");
|
||||
|
||||
svgGraticule.selectAll("path")
|
||||
.data(graticule.lines())
|
||||
.enter()
|
||||
.append("path")
|
||||
.attr("class", "graticule line")
|
||||
.attr("id", function(d) {
|
||||
var c = d.coordinates;
|
||||
if (c[0][0] == c[1][0]) {
|
||||
return (c[0][0] < 0) ? -c[0][0] + "W" : +c[0][0] + "E";
|
||||
} else if (c[0][1] == c[1][1]) {
|
||||
return (c[0][1] < 0) ? -c[0][1] + "S" : c[0][1] + "N";
|
||||
}
|
||||
})
|
||||
.attr("d", geoPath);
|
||||
var draw_text = false;
|
||||
if (draw_text) {
|
||||
svgGraticule.selectAll('text')
|
||||
.data(graticule.lines())
|
||||
.enter()
|
||||
.append("text")
|
||||
.text(function(d) {
|
||||
var c = d.coordinates;
|
||||
if ((c[0][0] == c[1][0]) && (c[0][0] % 30 == 0)) {
|
||||
return (c[0][0]);
|
||||
} else if (c[0][1] == c[1][1]) {
|
||||
return (c[0][1]);
|
||||
}
|
||||
})
|
||||
.attr("class", "label")
|
||||
.attr("style", function(d) {
|
||||
var c = d.coordinates;
|
||||
return (c[0][1] == c[1][1]) ? "text-anchor: end" : "text-anchor: middle";
|
||||
})
|
||||
.attr("dx", function(d) {
|
||||
var c = d.coordinates;
|
||||
return (c[0][1] == c[1][1]) ? -10 : 0;
|
||||
})
|
||||
.attr("dy", function(d) {
|
||||
var c = d.coordinates;
|
||||
return (c[0][1] == c[1][1]) ? 4 : 10;
|
||||
})
|
||||
.attr('transform', function(d) {
|
||||
var c = d.coordinates;
|
||||
return ('translate(' + aProjection(c[0]) + ')')
|
||||
});
|
||||
}
|
||||
|
||||
var draw_outline = true;
|
||||
if (draw_outline) {
|
||||
svgGraticule.append("path")
|
||||
.datum(graticule.outline)
|
||||
.attr("class", "graticule outline")
|
||||
.attr("d", geoPath);
|
||||
}
|
||||
}
|
||||
|
||||
function get_almanac_valid(data_almanac)
|
||||
{
|
||||
var a = [];
|
||||
Object.keys(data_almanac).forEach(function(e) {
|
||||
var o = data_almanac[e];
|
||||
o.sv = e;
|
||||
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);
|
||||
}
|
||||
});
|
||||
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_radials(satellite, observer)
|
||||
{
|
||||
// in order to draw the line between observer and satellite we need long/lat vs x/y
|
||||
var observer_center = aProjection.invert(geoPath.centroid(observer)); // find the center of the projected rectangle
|
||||
var satellite_center = aProjection.invert([satellite.attr("cx"), satellite.attr("cy")]); // center of satellite
|
||||
svgObservers.append("path")
|
||||
.attr("class", "radials")
|
||||
.attr("d", function(r) { return geoPath({type: "LineString", coordinates: [observer_center, satellite_center]}); });
|
||||
}
|
||||
|
||||
function draw_satellite_to_operator(d)
|
||||
{
|
||||
// get list of observers for this satellite
|
||||
satellite = d3.select("#Satellite_" + d.name);
|
||||
a = observers_list_almanac_raw(d);
|
||||
for (aa=0;aa<a.length;aa++) {
|
||||
observer_id = a[aa];
|
||||
observer = observer_shape[observer_id];
|
||||
if (!observer) {
|
||||
// should not happen
|
||||
continue;
|
||||
}
|
||||
draw_radials(satellite, observer);
|
||||
}
|
||||
}
|
||||
|
||||
function draw_operator_to_satellite(d)
|
||||
{
|
||||
observer_id = d.id;
|
||||
// get list of satellites for this observer
|
||||
observer = observer_shape[observer_id];
|
||||
if (!observer) {
|
||||
// should not happen
|
||||
return;
|
||||
}
|
||||
|
||||
a = svs_list_observer_raw(d);
|
||||
for (aa=0;aa<a.length;aa++) {
|
||||
satellite_id = a[aa];
|
||||
// check the satellite is seen in almanac data - ie. double check.
|
||||
if (!data_almanac[satellite_id] || !data_almanac[satellite_id].observed) {
|
||||
// no line needed
|
||||
continue;
|
||||
}
|
||||
var s = $("[id^='Satellite_" + satellite_id + "']");
|
||||
if (!s.is(":visible")) {
|
||||
continue;
|
||||
}
|
||||
satellite = d3.select("#Satellite_" + satellite_id);
|
||||
draw_radials(satellite, observer);
|
||||
}
|
||||
}
|
||||
|
||||
function color_of(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
|
||||
}
|
||||
return "magenta"; // - should not happen
|
||||
}
|
||||
|
||||
var tooltop_deltax = 10; // was 30
|
||||
|
||||
function draw_almanac(data_almanac)
|
||||
{
|
||||
var arr = get_almanac_valid(data_almanac);
|
||||
|
||||
svgAlmanac.html("");
|
||||
|
||||
// Three function that change the tooltip when user hover / move / leave a cell
|
||||
var mouseover = function(d) {
|
||||
var o = observers_list_almanac(d);
|
||||
s = d.name + " [" + to2(d.eph_longitude) + "," + to2(d.eph_latitude) + "] " + ((o == "") ? "" : " seen by " + o);
|
||||
Tooltip.html(s)
|
||||
.style("opacity", 1)
|
||||
.style("left", (d3.mouse(this)[0] + tooltop_deltax) + "px")
|
||||
.style("top", (d3.mouse(this)[1]) + "px");
|
||||
d3.selectAll(".radials").remove();
|
||||
draw_satellite_to_operator(d);
|
||||
}
|
||||
var mousemove = function(d) {
|
||||
var o = observers_list_almanac(d);
|
||||
s = d.name + " [" + to2(d.eph_longitude) + "," + to2(d.eph_latitude) + "] " + ((o == "") ? "" : " seen by " + o);
|
||||
Tooltip.html(s)
|
||||
.style("left", (d3.mouse(this)[0] + tooltop_deltax) + "px")
|
||||
.style("top", (d3.mouse(this)[1]) + "px");
|
||||
}
|
||||
var mouseleave = function(d) {
|
||||
Tooltip.html("")
|
||||
.style("opacity", 0);
|
||||
d3.selectAll(".radials").remove();
|
||||
display_all_refresh();
|
||||
}
|
||||
|
||||
// text first as we want the satellite circles to be always above them!
|
||||
svgAlmanac.selectAll("text")
|
||||
.data(arr)
|
||||
.enter()
|
||||
.append("text")
|
||||
.text(r => r.sv)
|
||||
.attr("class", "labels")
|
||||
.attr("id", r => "Satellite_" + r.name + "_label")
|
||||
.attr("dx", r => (aProjection([r.eph_longitude, r.eph_latitude])[0] + 0))
|
||||
.attr("dy", r => (aProjection([r.eph_longitude, r.eph_latitude])[1] + 0))
|
||||
.attr("fill", r => ((r.observed) ? "black" : "#666666"))
|
||||
.attr("text-anchor", r => ((r.eph_longitude > 0) ? "start" : "end"))
|
||||
.attr("baseline-shift", r => ((r.eph_latitude > 0) ? "+30%" : "-90%"))
|
||||
.attr("fill-opacity", r => ((r.observed) ? "1.0" : ".5"))
|
||||
.attr("stroke-opacity", r => ((r.observed) ? "1.0" : ".5"))
|
||||
.attr("font-weight", r => ((r.observed) ? "bold" : null));
|
||||
|
||||
svgAlmanac.selectAll("circle")
|
||||
.data(arr)
|
||||
.enter()
|
||||
.append("circle")
|
||||
.attr("class", "satellites")
|
||||
.attr("id", r => "Satellite_" + 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", r => color_of(r))
|
||||
.attr("fill-opacity", r => ((r.observed) ? "1.0" : ".5"))
|
||||
.attr("stroke-opacity", r => ((r.observed) ? "1.0" : ".5"))
|
||||
.on("mouseover", mouseover)
|
||||
.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(r)
|
||||
{
|
||||
var considered_old = 15 * 60; // 15 mins
|
||||
|
||||
if (age_in_seconds(r["last-seen"]) > considered_old) {
|
||||
// data is old
|
||||
return false;
|
||||
}
|
||||
if (Object.keys(r.svs).length == 0) {
|
||||
// nothing visible
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
var observer_shape = [];
|
||||
|
||||
function draw_observers(data_observers)
|
||||
{
|
||||
// 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] + tooltop_deltax) + "px")
|
||||
.style("top", (d3.mouse(this)[1]) + "px");
|
||||
d3.selectAll(".radials").remove();
|
||||
draw_operator_to_satellite(d);
|
||||
|
||||
// redraw only the one coverage circle
|
||||
coverages = $("[id^='Coverage_']");
|
||||
coverages.hide();
|
||||
coverages = $("[id^='Coverage_" + d.id + "']");
|
||||
coverages.show();
|
||||
}
|
||||
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] + tooltop_deltax) + "px")
|
||||
.style("top", (d3.mouse(this)[1]) + "px");
|
||||
}
|
||||
var mouseleave = function(d) {
|
||||
Tooltip.html("")
|
||||
.style("opacity", 0);
|
||||
d3.selectAll(".radials").remove();
|
||||
display_all_refresh();
|
||||
coverage_map_refresh();
|
||||
}
|
||||
|
||||
// text first as we want the observer rectangle to be always above them!
|
||||
svgAlmanac.selectAll("olables")
|
||||
.data(data_observers)
|
||||
.enter()
|
||||
.append("text")
|
||||
.text(r => r.id)
|
||||
.attr("class", r => (observer_up(r) ? "observers" : "observers down"))
|
||||
.attr("id", r => "Observer_" + r.id + "_label")
|
||||
.attr("dx", r => (aProjection([r.longitude, r.latitude])[0]))
|
||||
.attr("dy", r => (aProjection([r.longitude, r.latitude])[1]))
|
||||
.attr("fill", r => (observer_up(r) ? "black" : "#666666"))
|
||||
.attr("text-anchor", r => ((r.longitude > 0) ? "start" : "end"))
|
||||
.attr("baseline-shift", r => ((r.latitude > 0) ? "+30%" : "-90%"))
|
||||
.attr("fill-opacity", r => (observer_up(r) ? "1.0" : ".5"))
|
||||
.attr("stroke-opacity", r => (observer_up(r) ? "1.0" : ".5"))
|
||||
.attr("font-weight", r => (observer_up(r) ? "bold" : null));
|
||||
|
||||
// we draw a geo correct observer rectangle - mapped onto whatever globe we have projected
|
||||
var observer_degrees = 2.5;
|
||||
svgAlmanac.selectAll("div")
|
||||
.data(data_observers)
|
||||
.enter()
|
||||
.append("path")
|
||||
.attr("class", r => (observer_up(r) ? "observers" : "observers down"))
|
||||
.attr("id", r => ("Observer_" + r.id))
|
||||
.attr("d", function(r) {
|
||||
var path = {
|
||||
type: "LineString",
|
||||
coordinates: [
|
||||
[r.longitude - observer_degrees/2, r.latitude - observer_degrees/2],
|
||||
[r.longitude - observer_degrees/2, r.latitude + observer_degrees/2],
|
||||
[r.longitude + observer_degrees/2, r.latitude + observer_degrees/2],
|
||||
[r.longitude + observer_degrees/2, r.latitude - observer_degrees/2],
|
||||
]
|
||||
};
|
||||
observer_shape[r.id] = path; // save away for later
|
||||
return geoPath(path);
|
||||
})
|
||||
.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++){
|
||||
//if (down[ii].classList.contains('down')) {
|
||||
// down[ii].classList.remove('down');
|
||||
//} else {
|
||||
// down[ii].classList.add('down');
|
||||
//}
|
||||
down[ii].style.fill = (down[ii].style.fill == "black") ? "red" : "black";
|
||||
down[ii].style.stroke = (down[ii].style.stroke == "black") ? "red" : "black";
|
||||
}
|
||||
}
|
||||
var seconds = 2;
|
||||
down_timer = setTimeout(annimate_down_observers, seconds * 1000.0);
|
||||
}
|
||||
|
||||
function draw_observers_coverage(data_observers)
|
||||
{
|
||||
var radius = 65; // XXX fix
|
||||
var geoCircle = d3.geoCircle();
|
||||
|
||||
svgObservers.html("");
|
||||
|
||||
// we draw a geo correct circle
|
||||
svgObservers.selectAll("div")
|
||||
.data(data_observers)
|
||||
.enter()
|
||||
.append("path")
|
||||
.attr("class", r => (observer_up(r) ? "coverage" : "coverage down"))
|
||||
.attr("id", r => ("Coverage_" + r.id))
|
||||
.attr("d", function(r) { return geoPath(geoCircle.center([r.longitude, r.latitude]).radius(radius)());
|
||||
});
|
||||
}
|
||||
|
||||
var data_almanac = null;
|
||||
var data_observers = null;
|
||||
var time_last_data_received = 0;
|
||||
|
||||
function do_update_almanac_observers(error, results)
|
||||
{
|
||||
var updated_almanac = false;
|
||||
var updated_observers = false;
|
||||
|
||||
if (results && results.length > 0 && results[0]) {
|
||||
data_almanac = results[0];
|
||||
updated_almanac = true;
|
||||
time_last_data_received = now();
|
||||
}
|
||||
if (results.length > 1 && results[1]) {
|
||||
data_observers = results[1];
|
||||
updated_observers = true;
|
||||
time_last_data_received = now();
|
||||
}
|
||||
|
||||
// XXX cheat for now - we need until we fix d3/svg bug above
|
||||
updated_almanac = true;
|
||||
updated_observers = true;
|
||||
|
||||
if (updated_almanac) {
|
||||
// We write into the svgalmanac area - so clean it and rewrite it
|
||||
if (draw_almanac) {
|
||||
draw_almanac(data_almanac);
|
||||
}
|
||||
// now hide/show the ones that should be seen
|
||||
constellation_refresh();
|
||||
}
|
||||
if (updated_observers) {
|
||||
if (data_observers) {
|
||||
draw_observers_coverage(data_observers)
|
||||
draw_observers(data_observers);
|
||||
}
|
||||
coverage_map_refresh();
|
||||
observer_map_refresh();
|
||||
}
|
||||
display_all_refresh();
|
||||
}
|
||||
|
||||
var redisplay_timer = null;
|
||||
var display_observers_count = 0;
|
||||
|
||||
function stop_redisplay_timer()
|
||||
{
|
||||
clearTimeout(redisplay_timer);
|
||||
redisplay_timer = null;
|
||||
}
|
||||
|
||||
function do_redisplay_timer()
|
||||
{
|
||||
stop_redisplay_timer();
|
||||
|
||||
if ((now() - time_last_data_received) >= 55) {
|
||||
// refresh data from afar
|
||||
if (display_observers_count == 0) {
|
||||
// observers does not need that much updating!
|
||||
d3.queue(1)
|
||||
.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--;
|
||||
} else {
|
||||
// just use existing data
|
||||
do_update_almanac_observers(null, []);
|
||||
}
|
||||
|
||||
var seconds = 60;
|
||||
redisplay_timer = setTimeout(do_redisplay_timer, seconds * 1000.0);
|
||||
}
|
||||
|
||||
function set_projection(data_world)
|
||||
{
|
||||
// var aProjection = d3.geoMercator().scale(100).translate([250, 250]);
|
||||
// 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
|
||||
|
||||
var center = [0, 0]; // This is very Euro-centric - but that's how these projections works.
|
||||
var scale = 191; // No idea what this does
|
||||
|
||||
var svgCombined = d3.select('#combined');
|
||||
var idCombined = document.getElementById("combined");
|
||||
|
||||
svgWorld = d3.select("#svgworld");
|
||||
idWorld = document.getElementById("svgworld");
|
||||
|
||||
svgGraticule = d3.select("#svggraticule");
|
||||
idGraticule = document.getElementById("svggraticule");
|
||||
|
||||
var offset = [idCombined.clientWidth/2, idCombined.clientHeight/2];
|
||||
|
||||
switch(projectionChoices[projectionChoice]) {
|
||||
default:
|
||||
// fall thru to Equirectangular
|
||||
case 'Equirectangular':
|
||||
aProjection = d3.geoEquirectangular()
|
||||
.scale(scale)
|
||||
.translate(offset);
|
||||
break;
|
||||
case 'Aitoff':
|
||||
aProjection = d3.geoAitoff()
|
||||
.scale(scale)
|
||||
.translate(offset);
|
||||
break;
|
||||
case 'CylindricalStereographic':
|
||||
aProjection = d3.geoCylindricalStereographic()
|
||||
.scale(scale)
|
||||
.translate(offset);
|
||||
break;
|
||||
case 'Fahey':
|
||||
aProjection = d3.geoFahey()
|
||||
.scale(scale)
|
||||
.translate(offset);
|
||||
break;
|
||||
case 'Orthographic':
|
||||
aProjection = d3.geoOrthographic()
|
||||
.scale(scale)
|
||||
.translate(offset);
|
||||
break;
|
||||
}
|
||||
|
||||
geoPath = d3.geoPath()
|
||||
.projection(aProjection);
|
||||
|
||||
// using the path determine the bounds of the current map and use
|
||||
// these to determine better values for the scale and translation
|
||||
var bounds = geoPath.bounds(data_world);
|
||||
var hscale = scale * (idCombined.clientWidth - 40) / (bounds[1][0] - bounds[0][0]);
|
||||
var vscale = scale * (idCombined.clientHeight - 40) / (bounds[1][1] - bounds[0][1]);
|
||||
scale = (hscale < vscale) ? hscale : vscale;
|
||||
var offset = [
|
||||
idCombined.clientWidth - (bounds[0][0] + bounds[1][0])/2,
|
||||
idCombined.clientHeight - (bounds[0][1] + bounds[1][1])/2
|
||||
];
|
||||
|
||||
if (0) {
|
||||
// new projection
|
||||
switch(projectionChoices[projectionChoice]) {
|
||||
default:
|
||||
// fall thru to Equirectangular
|
||||
case 'Equirectangular':
|
||||
aProjection = d3.geoEquirectangular()
|
||||
.center(center)
|
||||
.scale(scale)
|
||||
.translate(offset);
|
||||
break;
|
||||
case 'Aitoff':
|
||||
aProjection = d3.geoAitoff()
|
||||
.center(center) .scale(scale)
|
||||
.translate(offset);
|
||||
break;
|
||||
case 'CylindricalStereographic':
|
||||
aProjection = d3.geoCylindricalStereographic()
|
||||
.center(center)
|
||||
.scale(scale)
|
||||
.translate(offset);
|
||||
break;
|
||||
case 'Fahey':
|
||||
aProjection = d3.geoFahey()
|
||||
.center(center)
|
||||
.scale(scale)
|
||||
.translate(offset);
|
||||
break;
|
||||
case 'Orthographic':
|
||||
aProjection = d3.geoOrthographic()
|
||||
.center(center)
|
||||
.scale(scale)
|
||||
.translate(offset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
svgCombined.attr("width", idCombined.clientWidth);
|
||||
svgCombined.attr("height", idCombined.clientHeight);
|
||||
|
||||
padding = 0 // 20 - see svg: padding in css
|
||||
|
||||
svgWorld.attr("width", idCombined.clientWidth - padding * 2);
|
||||
svgWorld.attr("height", idCombined.clientHeight - padding * 2);
|
||||
|
||||
svgObservers.attr("width", idCombined.clientWidth - padding * 2);
|
||||
svgObservers.attr("height", idCombined.clientHeight - padding * 2);
|
||||
|
||||
svgGraticule.attr("width", idCombined.clientWidth - padding * 2);
|
||||
svgGraticule.attr("height", idCombined.clientHeight - padding * 2);
|
||||
|
||||
svgAlmanac.attr("width", idCombined.clientWidth - padding * 2);
|
||||
svgAlmanac.attr("height", idCombined.clientHeight - padding * 2);
|
||||
|
||||
}
|
||||
|
||||
function do_draw_world(data_world)
|
||||
{
|
||||
set_projection(data_world);
|
||||
|
||||
aProjection.rotate([globe_rotate.lambda, globe_rotate.phi]); // show globe where it belongs for current timezone
|
||||
|
||||
create_tooltop();
|
||||
|
||||
draw_world(data_world);
|
||||
draw_graticule();
|
||||
|
||||
if (0) {
|
||||
// XXX recode - this is bad.
|
||||
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, [])
|
||||
});
|
||||
}
|
||||
// XXX this will work one day ...
|
||||
// var node = {id: "rotate_west"}; // The "id" is the only part used.
|
||||
// rotate_globe(node)
|
||||
}
|
||||
}
|
||||
|
||||
var data_world = null;
|
||||
|
||||
function read_world()
|
||||
{
|
||||
if (data_world == null) {
|
||||
d3.json(fileWorld, function(result) {
|
||||
data_world = result;
|
||||
do_draw_world(data_world);
|
||||
// after the world is read in and displayed - then start the timers!
|
||||
// we don't redraw the world!
|
||||
do_redisplay_timer();
|
||||
});
|
||||
} else {
|
||||
// after the world is read in and displayed - then start the timers!
|
||||
// we don't redraw the world!
|
||||
do_draw_world(data_world);
|
||||
do_redisplay_timer();
|
||||
}
|
||||
}
|
||||
|
||||
var globe_rotate = {lambda: 0.0, phi: 0.0};
|
||||
var globe_rotate_center = {lambda: 0.0, phi: 0.0};
|
||||
|
||||
function set_rotate_from_tz()
|
||||
{
|
||||
var offset = new Date().getTimezoneOffset(); // in minutes from UTC
|
||||
|
||||
globe_rotate_center.lambda = (offset/(60.0*24.0)) * 360.0;
|
||||
globe_rotate_center.phi = 0.0;
|
||||
|
||||
globe_rotate.lambda = globe_rotate_center.lambda;
|
||||
globe_rotate.phi = globe_rotate_center.phi;
|
||||
}
|
||||
|
||||
function geo_start()
|
||||
{
|
||||
set_rotate_from_tz();
|
||||
read_world();
|
||||
}
|
||||
|
||||
geo_start();
|
||||
|
||||
// d3.select("body").onresize = do_redisplay_timer;
|
||||
|
||||
var constellation_state = {G: true, E: true, C: true, I: false, J: false, R: true};
|
||||
|
||||
function constellation_click(node)
|
||||
{
|
||||
// Satellites are named LETTER+NUMBERS
|
||||
var c = node.innerText.trim();
|
||||
if (c == "GPS") { c = "G"; } // G = GPS (American)
|
||||
else if (c == "Galileo") { c = "E"; } // E = Europe
|
||||
else if (c == "BeiBou") { c = "C"; } // C = China
|
||||
else if (c == "IMES") { c = "I"; } // I = Indoor QZSS (doubtfully seen)
|
||||
else if (c == "QZSS") { c = "J"; } // J = Japan
|
||||
else if (c == "GLONASS") { c = "R"; } // R = Russia
|
||||
|
||||
satellites = $("[id^='Satellite_" + c + "']");
|
||||
|
||||
if (node.childNodes[1].checked) {
|
||||
satellites.show();
|
||||
constellation_state[c] = true;
|
||||
} else {
|
||||
satellites.hide();
|
||||
constellation_state[c] = false;
|
||||
}
|
||||
d3.selectAll(".radials").remove();
|
||||
display_all_refresh();
|
||||
}
|
||||
|
||||
function constellation_refresh()
|
||||
{
|
||||
for (c in constellation_state) {
|
||||
satellites = $("[id^='Satellite_" + c + "']");
|
||||
if (constellation_state[c]) {
|
||||
satellites.show();
|
||||
} else {
|
||||
satellites.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var coverage_map_state = true;
|
||||
var observer_map_state = true;
|
||||
|
||||
function coverage_map_click(node)
|
||||
{
|
||||
if (node.childNodes[1].checked) {
|
||||
coverage_map_state = true;
|
||||
} else {
|
||||
coverage_map_state = false;
|
||||
}
|
||||
coverage_map_refresh();
|
||||
}
|
||||
|
||||
function coverage_map_refresh()
|
||||
{
|
||||
coverages = $("[id^='Coverage_']");
|
||||
if (coverage_map_state) {
|
||||
coverages.show();
|
||||
} else {
|
||||
coverages.hide();
|
||||
}
|
||||
}
|
||||
|
||||
function observer_map_click(node)
|
||||
{
|
||||
if (node.childNodes[1].checked) {
|
||||
observer_map_state = true;
|
||||
} else {
|
||||
observer_map_state = false;
|
||||
}
|
||||
observer_map_refresh();
|
||||
}
|
||||
|
||||
function observer_map_refresh()
|
||||
{
|
||||
observers = $("[id^='Observer_']");
|
||||
if (observer_map_state) {
|
||||
observers.show();
|
||||
} else {
|
||||
observers.hide();
|
||||
}
|
||||
}
|
||||
|
||||
var display_all_state = false;
|
||||
|
||||
function display_all_click(node)
|
||||
{
|
||||
if (node.childNodes[1].checked) {
|
||||
display_all_state = true;
|
||||
} else {
|
||||
display_all_state = false;
|
||||
}
|
||||
display_all_refresh();
|
||||
}
|
||||
|
||||
function display_all_refresh()
|
||||
{
|
||||
if (display_all_state) {
|
||||
for (d in data_observers) {
|
||||
draw_operator_to_satellite(data_observers[d]);
|
||||
}
|
||||
} else {
|
||||
d3.selectAll(".radials").remove();
|
||||
}
|
||||
}
|
||||
|
||||
function show_history_click(node)
|
||||
{
|
||||
var c = node.innerText.trim();
|
||||
if (node.childNodes[1].checked) {
|
||||
// XXX
|
||||
console.log(c + " :checked");
|
||||
} else {
|
||||
// XXX
|
||||
console.log(c + " :not checked");
|
||||
}
|
||||
}
|
||||
|
||||
function show_animate_click(node)
|
||||
{
|
||||
var c = node.innerText.trim();
|
||||
if (node.childNodes[1].checked) {
|
||||
// XXX
|
||||
console.log(c + " :checked");
|
||||
} else {
|
||||
// XXX
|
||||
console.log(c + " :not checked");
|
||||
}
|
||||
}
|
||||
|
||||
function update_projection_click(node)
|
||||
{
|
||||
// cycle thru known globe projections
|
||||
projectionChoice++;
|
||||
if (projectionChoice >= projectionChoices.length)
|
||||
projectionChoice = 0;
|
||||
// now redraw everything!
|
||||
read_world();
|
||||
}
|
||||
|
||||
function rotate_globe(node)
|
||||
{
|
||||
var t = node.id;
|
||||
if (t == "rotate_north") {
|
||||
globe_rotate.phi += 10; // North
|
||||
} else if (t == "rotate_west") {
|
||||
globe_rotate.lambda -= 5; // West
|
||||
} else if (t == "rotate_center") {
|
||||
globe_rotate.lambda = globe_rotate_center.lambda;
|
||||
globe_rotate.phi = globe_rotate_center.phi; // Reset to center
|
||||
} else if (t == "rotate_east") {
|
||||
globe_rotate.lambda += 5; // East
|
||||
} else if (t == "rotate_south") {
|
||||
globe_rotate.phi -= 10; // South
|
||||
}
|
||||
|
||||
read_world();
|
||||
}
|
||||
|
||||
// JQuery also has a startup
|
||||
|
||||
$(document).ready(function () {
|
||||
$("[id^='constilation']").click(function() { constellation_click(this); });
|
||||
$('#coverage_map').click(function() { coverage_map_click(this); });
|
||||
$('#observer_map').click(function() { observer_map_click(this); });
|
||||
|
||||
$('#display_all').click(function() { display_all_click(this); });
|
||||
$('#show_history').click(function() { show_history_click(this); });
|
||||
$('#show_animate').click(function() { show_animate_click(this); });
|
||||
$('#update_projection').click(function() { update_projection_click(this); });
|
||||
|
||||
$("[id^='rotate_']").click(function() { rotate_globe(this); });
|
||||
});
|
||||
|
File diff suppressed because one or more lines are too long
200
html/geo/geo.css
200
html/geo/geo.css
|
@ -1,12 +1,18 @@
|
|||
body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
float: left;
|
||||
background-color: #ccc;
|
||||
font-family: verdana, helvetica, arial, sans-serif;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 5px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
#galmongeo {
|
||||
border: blue 1px solid;
|
||||
margin: 5px;
|
||||
|
@ -19,64 +25,85 @@ body {
|
|||
|
||||
#galmoninfo {
|
||||
border-top: blue 1px solid;
|
||||
margin-bottom: 0px;
|
||||
display: block;
|
||||
}
|
||||
#galmontext {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
float: left;
|
||||
font-size: 20px;
|
||||
}
|
||||
#galmonchoice {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
float: right;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
#combined {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
background-color: transparent;
|
||||
// background-color: yellow;
|
||||
height: 700px;
|
||||
height: 620px;
|
||||
width: 100%;
|
||||
//margin-left: auto;
|
||||
//margin-right: auto;
|
||||
margin: 10px;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
border: gray 1px solid;
|
||||
}
|
||||
|
||||
#svgworld {
|
||||
position: absolute; top: 0; right: 0; bottom: 0; left: 0;
|
||||
// border: blue 1px solid;
|
||||
display: inline-block;
|
||||
// margin-left: auto;
|
||||
// margin-right: auto;
|
||||
// border: green 1px solid;
|
||||
}
|
||||
|
||||
#svggraticule {
|
||||
position: absolute; top: 0; right: 0; bottom: 0; left: 0;
|
||||
// border: blue 1px solid;
|
||||
display: inline-block;
|
||||
// margin-left: auto;
|
||||
// margin-right: auto;
|
||||
// border: blue 1px solid;
|
||||
}
|
||||
|
||||
#svgobservers {
|
||||
position: absolute; top: 0; right: 0; bottom: 0; left: 0;
|
||||
// border: blue 1px solid;
|
||||
display: inline-block;
|
||||
// margin-left: auto;
|
||||
// margin-right: auto;
|
||||
// border: blue 1px solid;
|
||||
}
|
||||
|
||||
#svgalmanac {
|
||||
position: absolute; top: 0; right: 0; bottom: 0; left: 0;
|
||||
// border: blue 1px solid;
|
||||
display: inline-block;
|
||||
// margin-left: auto;
|
||||
// margin-right: auto;
|
||||
// border: blue 1px solid;
|
||||
}
|
||||
|
||||
#controls {
|
||||
position: absolute; bottom: 0; right: 0;
|
||||
display: inline-block;
|
||||
margin: 5px;
|
||||
}
|
||||
#rotation {
|
||||
position: absolute; bottom: 0; left: 0;
|
||||
display: inline-block;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
svg {
|
||||
// border: red 1px solid;
|
||||
// background-color: transparent;
|
||||
padding: 0px 20px 20px 20px;
|
||||
// margin-left: auto;
|
||||
// margin-right: auto;
|
||||
//padding: 20px;
|
||||
}
|
||||
|
||||
// #Coverage_95: { fill: #800; fill-opacity: 0.2; }
|
||||
// #Coverage_95: { fill: #800; fill-opacity: 0.2; }
|
||||
// #Coverage_95: { fill: #800; fill-opacity: 0.2; }
|
||||
// #Coverage_98: { fill: #800; fill-opacity: 0.2; }
|
||||
// #Coverage_99: { fill: #800; fill-opacity: 0.2; }
|
||||
|
||||
path.countries {
|
||||
background: yellow;
|
||||
stroke-width: 1;
|
||||
stroke: #75739F;
|
||||
fill: #5EAFC6;
|
||||
fill: #7ECFE6;
|
||||
}
|
||||
path.coverage {
|
||||
stroke-width: 1;
|
||||
|
@ -84,15 +111,42 @@ path.coverage {
|
|||
fill: #888;
|
||||
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: red;
|
||||
fill: red;
|
||||
fill-opacity: .75;
|
||||
}
|
||||
text.observers {
|
||||
font-family: courier new, courier;
|
||||
font-style: italic;
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
}
|
||||
text.observers.down {
|
||||
color: red;
|
||||
}
|
||||
circle.satellites {
|
||||
stroke-width: 1;
|
||||
stroke: #4F442B;
|
||||
//fill: #FCBC34;
|
||||
}
|
||||
.radials {
|
||||
stroke-width: 1;
|
||||
stroke: red;
|
||||
fill: none;
|
||||
}
|
||||
text.labels {
|
||||
// stroke-width: 1;
|
||||
// stroke: #4F442B;
|
||||
// fill: #FCBC34;
|
||||
font-family: courier new, courier;
|
||||
font-style: italic;
|
||||
}
|
||||
|
@ -124,13 +178,91 @@ path.merged {
|
|||
stroke: #4F442B;
|
||||
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;
|
||||
}
|
||||
|
||||
label {
|
||||
cursor: pointer;
|
||||
.mybutton {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
padding-left: 35px;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
input[type="checkbox"] {
|
||||
cursor: pointer;
|
||||
.checkmark {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
background-color: #eee;
|
||||
}
|
||||
.mybutton:hover input ~ .checkmark {
|
||||
background-color: #ccc;
|
||||
}
|
||||
.mybutton input {
|
||||
display: none;
|
||||
}
|
||||
.mybutton input:checked ~ .checkmark {
|
||||
background-color: #2196F3;
|
||||
}
|
||||
.mybutton input:checked ~ .checkmark:after {
|
||||
display: block;
|
||||
}
|
||||
.checkmark:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
display: none;
|
||||
}
|
||||
.mybutton .checkmark:after {
|
||||
left: 9px;
|
||||
top: 5px;
|
||||
width: 5px;
|
||||
height: 10px;
|
||||
border: solid white;
|
||||
border-width: 0 3px 3px 0;
|
||||
-webkit-transform: rotate(45deg);
|
||||
-ms-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.rotatetable {
|
||||
display: table;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0px;
|
||||
}
|
||||
.rotatetable td {
|
||||
vertical-align: middle;
|
||||
padding: 0px 3px 0px 3px;
|
||||
margin: 0px;
|
||||
}
|
||||
.myrotate {
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
#controls {
|
||||
margin: 0px 5px 5px 0px;
|
||||
}
|
||||
#rotation {
|
||||
margin: 0px 0px 5px 5px;
|
||||
border: 1px gray solid;
|
||||
background: #eee;
|
||||
}
|
||||
#options_other {
|
||||
padding-top:10px;
|
||||
}
|
997
html/geo/geo.js
997
html/geo/geo.js
File diff suppressed because it is too large
Load Diff
|
@ -4,36 +4,73 @@
|
|||
<title>galmon.eu geo</title>
|
||||
<link rel="stylesheet" type="text/css" href="geo.css">
|
||||
|
||||
<script src='../ext/jquery.min.js'></script>
|
||||
|
||||
<script src="../d3.v4.min.js"></script>
|
||||
<script src="../ext/topojson.v1.min.js"></script>
|
||||
<script src="../ext/d3-geo-projection.v2.min.js"></script>
|
||||
<script src="../ext/d3-geo-projection.v2.min.js"></script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="galmongeo">
|
||||
<h1>galmon.eu geo</h1>
|
||||
<div id="galmoninfo">
|
||||
<span id="galmontext">
|
||||
This is a live map from the <a href="/">galmon.eu</a> project
|
||||
</span>
|
||||
<span id="galmonchoice">
|
||||
<label id="observer_map" class="mybutton">Observer<input type="checkbox" checked="checked" ><span class="checkmark"></span></label>
|
||||
|
|
||||
<label id="coverage_map" class="mybutton">Coverage<input type="checkbox" checked="checked" ><span class="checkmark"></span></label>
|
||||
|
|
||||
<label id="constilation1" class="mybutton">GPS <input type="checkbox" checked="checked" ><span class="checkmark"></span></label>
|
||||
<label id="constilation2" class="mybutton">Galileo <input type="checkbox" checked="checked" ><span class="checkmark"></span></label>
|
||||
<label id="constilation3" class="mybutton">BeiDou <input type="checkbox" checked="checked" ><span class="checkmark"></span></label>
|
||||
<!--
|
||||
<label id="constilation4" class="mybutton">IMES <input type="checkbox" disabled="disabled"><span class="checkmark"></span></label>
|
||||
<label id="constilation5" class="mybutton">QZSS <input type="checkbox" disabled="disabled"><span class="checkmark"></span></label>
|
||||
-->
|
||||
<label id="constilation6" class="mybutton">GLONASS <input type="checkbox" checked="checked" ><span class="checkmark"></span></label>
|
||||
</span>
|
||||
</div>
|
||||
<center id="options_sats">
|
||||
<input type="checkbox" id="Gal" onclick="do_timer();" checked> <label for="Gal">Galileo</label>
|
||||
<input type="checkbox" id="BeiDou" onclick="do_timer();" checked> <label for="BeiDou">BeiDou</label>
|
||||
<input type="checkbox" id="Glonass" onclick="do_timer();" checked> <label for="Glonass">Glonass</label>
|
||||
<input type="checkbox" id="GPS" onclick="do_timer();" checked> <label for="GPS">GPS</label>
|
||||
</center>
|
||||
<center id="options_other">
|
||||
<input type="checkbox" id="Obs" onclick="do_timer();" checked> <label for="Obs">Observers</label>
|
||||
</center>
|
||||
<div id="combined">
|
||||
<svg id="svgworld"></svg>
|
||||
<svg id="svggraticule"></svg>
|
||||
<svg id="svgobservers"></svg>
|
||||
<svg id="svgalmanac"></svg>
|
||||
<span id="controls">
|
||||
<label id="display_all" class="mybutton">Display All<input type="checkbox"><span class="checkmark"></span></label>
|
||||
</p>
|
||||
<!--
|
||||
<label id="show_history" class="mybutton">History<input type="checkbox"><span class="checkmark"></span></label>
|
||||
</p>
|
||||
<label id="show_animate" class="mybutton">Animate<input type="checkbox"><span class="checkmark"></span></label>
|
||||
</p>
|
||||
-->
|
||||
<label id="update_projection" class="mybutton">Projection<span class="checkmark"></span></label>
|
||||
</span>
|
||||
<span id="rotation">
|
||||
<table class="rotatetable">
|
||||
<tr>
|
||||
<td></td>
|
||||
<td><span id="rotate_north" class="myrotate">⇧</span></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span id="rotate_west" class="myrotate">⇦</span></td>
|
||||
<td><span id="rotate_center" class="myrotate">◯</span></td>
|
||||
<td><span id="rotate_east" class="myrotate">⇨</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td><span id="rotate_south" class="myrotate">⇩</span></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="geo.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue