first commit

This commit is contained in:
fred 2023-08-24 18:26:16 +02:00
commit 148c0f264b
25 changed files with 2987 additions and 0 deletions

6
README.md Normal file
View File

@ -0,0 +1,6 @@
# OSM2IPFS
Starts from the world divided in 36 x 36 sections.
Each division being from 0, 10, 20, to 360°
https://ipfs.asycn.io/ipfs/QmPcwC8tQRCNq1VvqsV7zLD6mMui5TgSmbu5suqiFR4Zfh/welcome.html

39
earth/demo.js Normal file
View File

@ -0,0 +1,39 @@
function selectExample(example) {
$('#sphere').earth3d('destroy');
$('#sphere').replaceWith($('<canvas id="sphere" width="400" height="400"></canvas>'));
$('.location').remove();
//~ $('.flight').remove();
//~ $('#flights')[0].getContext('2d').clearRect(0, 0, 400, 400);
if (example == 'simple_mars') {
$('#glow-shadows').removeClass('earth').addClass('mars');
} else {
$('#glow-shadows').removeClass('mars').addClass('earth');
}
var code = examples[example].toString();
code = code.substring(14);
code = code.substring(0, code.length - 2);
var lines = code.split("\n");
for (var i = 0; i < lines.length; i++) {
lines[i] = lines[i].substring(2);
}
code = lines.join("\n");
$('#example_code').val(code);
examples[example]();
}
$(document).ready(function() {
selectExample('locations');
$('#example').change(function() {
selectExample($(this).val());
});
});
function addPath() {
$('#sphere').earth3d('changePaths', {path2: {
origin: 'obj1',
destination: 'obj3'
}});
}

129
earth/earth.css Normal file
View File

@ -0,0 +1,129 @@
body {
background: black;
margin: 0;
padding: 0;
color: #DDD;
text-align: center;
padding-bottom: 20px;
}
h1 {
margin-top: 10px;
font-size: 40px;
margin-bottom: 10px;
}
.subtitle {
font-size: 20px;
margin-bottom: 20px;
color: #777;
}
a {
color: white;
}
#demo {
overflow: hidden;
}
#description {
text-align: left;
float: left;
width: 49%;
max-width: 500px;
padding-top: 50px;
padding-left: 20px;
}
#showoff {
float: left;
width: 49%;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
#container {
position: relative;
display: inline-block;
width: 400px;
height: 400px;
}
#sphere, #flights, #glow-shadows, #locations, #drag {
position: absolute;
top: 0px;
left: 0px;
width: 400px;
height: 400px;
}
#glow-shadows {
top: 1px;
left: 1px;
}
.location {
position: absolute;
width: 10px;
height: 10px;
left: 10px;
top: 10px;
border: 2px solid white;
margin-left: -5px;
margin-top: -5px;
border-radius: 50%;
cursor: pointer;
}
.location:hover {
width: 15px;
height: 15px;
margin-left: -7.5px;
margin-top: -7.5px;
}
.flight:hover {
width: 36px;
height: 37.5px;
margin-left: -18px;
margin-top: -18.75px;
}
.choose_example {
width: 35%;
margin-left: 32.5%;
}
#example_code {
width: 100%;
height: 200px;
background-color: black;
color: white;
border: 0px;
resize: none;
display: none;
}
.code {
margin-top: 10px;
}
.social {
display: inline-block;
}
.social.twitter {
vertical-align: -3px;
}
.social.google {
vertical-align: -7px;
}

BIN
earth/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 870 B

136
earth/index.html Normal file
View File

@ -0,0 +1,136 @@
<!DOCTYPE html>
<html>
<head>
<title>OSM2IPFS : Choose your spot</title>
<meta charset="UTF-8">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="jquery-ui.min.css">
<script type="text/javascript" src="requestanimationframe.polyfill.js"></script>
<script type="text/javascript" src="jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="jquery-ui.0.min.js"></script>
<script type="text/javascript" src="sphere-hacked.js"></script>
<script type="text/javascript" src="jquery.earth-3d.js"></script>
<script type="text/javascript" src="world.js"></script>
<script type="text/javascript" src="demo.js"></script>
<script type="text/javascript">
examples['simple_mars'] = function() {
$('#sphere').earth3d({
texture: 'maps/mars1024x1024.jpg', // texture used by G1Wish planet
dragElement: $('#locations') // where do we catch the mouse drag
});
};
</script>
<style>
body {
padding: 0;
margin: 0;
font-family: sans-serif;
}
.slidecontainer {
width: 80%;
margin: 0 auto;
text-align: center;
}
.gif-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.gif-container img {
margin: 10px;
max-width: 200px;
}
.slider {
width: 100%;
text-align: center;
}
.slider #prev {
float: left;
font-size: 40px;
}
.slider #next {
float: right;
font-size: 40px;
}
#glow-shadows.earth {
background: url(maps/earth-glow-shadows.png);
}
#glow-shadows.mars {
background: url(maps/mars-glow-shadows.png);
}
.flight {
position: absolute;
width: 24px;
height: 25px;
left: 10px;
top: 10px;
background: url(maps/plain.png);
background-size: 100% 100%;
margin-left: -12px;
margin-top: -12.5px;
cursor: pointer;
}
</style>
<link rel="stylesheet" href="earth.css">
</head>
<body>
<h1>HELLO WORLD</h1>
<div id="container">
<br><br>
<br><br>
<div id="sphere"></div>
<div id="glow-shadows" class="earth"></div>
<div id="flights"></div>
<div id="locations"></div>
</div>
<!--
<div class="choose_example">
Choose Planet: <select id="example">
-->
<!--
<option value="simple">Simple earth</option>
<option value="simple_tilted">Simple tilted earth</option>
-->
<!--
<option value="simple_mars">Simple mars</option>
-->
<!--
<option value="flights">Earth with locations and flights</option>
-->
<!--
<option value="locations" selected >Ŋ1 Friends</option>
</select>
</div>
<div class="code">
<a href="#" onclick="$('#example_code').show(); $(this).hide().siblings('a').show(); return false;">Show code</a>
<a href="#" style="display: none;" onclick="$('#example_code').hide(); $(this).hide().siblings('a').show(); return false;">Hide code</a>
<textarea id="example_code" onclick="this.focus();this.select();"></textarea>
</div>
-->
</body></html>

4
earth/jquery-1.7.2.min.js vendored Normal file

File diff suppressed because one or more lines are too long

5
earth/jquery-ui.0.min.js vendored Normal file

File diff suppressed because one or more lines are too long

7
earth/jquery-ui.min.css vendored Normal file

File diff suppressed because one or more lines are too long

6
earth/jquery-ui.min.js vendored Normal file

File diff suppressed because one or more lines are too long

851
earth/jquery.earth-3d.js Normal file
View File

@ -0,0 +1,851 @@
/*
jquery.earth3d.js
jQuery ui plugin that allow you to draw a beautiful 3d spinning earth on canvas
Author: Sebastien Drouyer
Based on the amazing sphere.js plug of Sam Hasler
Licensed under the MIT license (MIT-LICENSE.txt)
http://sdrdis.github.com/jquery.earth-3d/
Depends:
ui.core.js
Options:
* texture: texture map used by the planet
* sphere: rotation and size of the planet
* defaultSpeed: default spinning speed of the planet
* backToDefaultTime: time (in ms) to return by to default speed when planet is dragged
* locations: locations to display on the planet:
* Each position must have a key, an alpha and delta position (or x and y if you want to display a static location).
Any additional key can be reached via callbacks functions
Example:
{
obj1: {
alpha: Math.PI / 4,
delta: 0,
name: 'location 1'
}
}
* paths: paths and flights to display over the planet:
Each path must have a key, an origin and a destination. The values are the location's key.
You can, if you want to, define flights on these paths.
Each flight has a key, a destination (the location's key) and a position.
The position is the progress a fleet has made on its path.
Any additional key can be reach via callbacks functions.
Example:
{
path: {
origin: 'obj1',
destination: 'obj2',
flights: {
flight: {
position: 0.25,
destination: 'obj2',
name: 'Flight 1'
},
flight2: {
position: 0.25,
destination: 'obj1',
name: 'Flight 2'
}
}
}
}
* flightsCanvas: Dom element which is a canvas and where the flights and paths are drawn
* dragElement: Dom element where we catch the mouse drag
* locationsElement: Dom elements where the locations are drawn
* flightsCanvasPosition: position of the flight canvas (can be use if you have some gap between your planet and your flights
* pixelRadiusMultiplier: (TEMPORARY) used by the getSphereRadiusInPixel (see the functions)
* onInitLocation: callback function which allows you to define what to do when the locations are initialized
* Parameters:
* location: location (coming from locations option)
* widget: earth3d widget object
* onShowLocation: callback function which allows you to define what to do when a location becomes visible (was behind the planet and is now in front of it)
* Parameters:
* location: location (coming from locations option)
* x: 2d left position
* y: 2d top position
* widget: earth3d widget object
* onRefreshLocation: callback function which allows you to define what to do when a location is refreshed (it moves)
* Parameters:
* location: location (coming from locations option)
* x: 2d left position
* y: 2d top position
* widget: earth3d widget object
* onHideLocation: callback function which allows you to define what to do when a location becomes invisible (was in front of the planet and is now behind it)
* Parameters:
* location: location (coming from locations option)
* x: 2d left position
* y: 2d top position
* widget: earth3d widget object
* onInitFlight: callback function which allows you to define what to do when the flights are initialized
* Parameters:
* flight: flight (coming from flights option)
* widget: earth3d widget object
* onShowFlight: callback function which allows you to define what to do when a flight becomes visible (was behind the planet and is now in front of it)
* Parameters:
* flight: flight (coming from flights option)
* widget: earth3d widget object
* onRefreshFlight: callback function which allows you to define what to do when a flight is refreshed (it moves)
* Parameters:
* flight: flight (coming from flights option)
* x: 2d left position
* y: 2d top position
* widget: earth3d widget object
* onHideFlight: callback function which allows you to define what to do when a flight becomes invisible (was in front of the planet and is now behind it)
* Parameters:
* flight: flight (coming from flights option)
* widget: earth3d widget object
Functions
* getSphereRadiusInPixel: function which allows you to get the sphere radius in pixel
/!| WARNING: this function needs to be refactored, since I didn't find out (my maths courses are far away) how to
get the exact value. I did a basic linear regression, but it is not exact, and you will have to change the pixelRadiusMultiplier
option to get the correct value
* destroy: use this function when you want to destroy the object. It will throw a cancel animation frame, so the
CPU won't be used anymore.
* changePaths: use this function when you want to update paths and flights (options on widget)
it will add the callback functions support
*/
var earth3d;
(function($) {
$.widget('ui.earth3d', {
options: {
texture: 'maps/earth1024x1024.jpg',
sphere: {
tilt: 0,
turn: 0,
r: 10
},
defaultSpeed: 20,
backToDefaultTime: 4000,
locations: {
},
paths: {
},
flightsCanvas: null,
dragElement: null,
locationsElement: null,
flightsCanvasPosition: {
x: 0,
y: 0
},
tiling: {horizontal: 1, vertical: 1},
pixelRadiusMultiplier: 0.97,
onInitLocation: function(location, widget) {
var $elem = $('<div class="location"></div>');
$elem.appendTo(widget.options.locationsElement);
$elem.click(function() {
alert('Clicked on ' + location.name + ' : ' + location.link );
window.open( location.link, "AstroTab");
});
location.$element = $elem;
},
onShowLocation: function(location, x, y) {
location.$element.show();
},
onRefreshLocation: function(location, x, y) {
//console.log(x, y);
location.$element.css({
left: x,
top: y
});
},
onHideLocation: function(location, x, y) {
location.$element.hide();
},
onDeleteLocation: function(location) {
location.$element.remove();
},
onInitFlight: function(flight, widget) {
var $elem = $('<div class="flight"></div>');
$elem.appendTo(widget.options.locationsElement);
$elem.click(function() {
alert('Clicked on ' + flight.name);
});
flight.$element = $elem;
},
onShowFlight: function(flight) {
flight.$element.show();
},
onRefreshFlight: function(flight, x, y, angle, widget) {
flight.$element.css({
left: x,
top: y,
'-webkit-transform':'rotate(' + ((angle + Math.PI / 2) * 360 / (2 * Math.PI)) + 'deg)',
'-moz-transform':'rotate(' + ((angle + Math.PI / 2) * 360 / (2 * Math.PI)) + 'deg)',
'-o-transform':'rotate(' + ((angle + Math.PI / 2) * 360 / (2 * Math.PI)) + 'deg)'
});
},
onHideFlight: function(flight) {
flight.$element.hide();
},
onDeleteFlight: function(flight) {
flight.$element.remove();
}
},
earth: null,
posVar: 24 * 3600 * 1000,
lastMousePos: null,
lastSpeed: null,
lastTime: null,
lastTurnByTime: null,
textureWidth: null,
textureHeight: null,
obj: null,
flightsCtx: null,
renderAnimationFrameId: null,
mousePressed: null,
_create: function() {
earth3d = this;
var self = this;
this.obj = $('div');
if (this.options.flightsCanvas !== null) {
this.flightsCtx = this.options.flightsCanvas[0].getContext('2d');
}
createSphere(this.element[0], this.options.texture, function(earth, textureWidth, textureHeight) { self._onSphereCreated(earth, textureWidth, textureHeight); }, this.options.tiling);
if (this.options.dragElement !== null) {
this.options.dragElement
.bind('mousedown vmousedown', function(e) {
self._mouseDragStart(e);
self.mousePressed = true;
})
.bind('mouseup vmouseup', function(e) {
self._mouseDragStop(e);
self.mousePressed = false;
})
.bind('mousemove vmousemove', function(e){
if (self.mousePressed) {
self._mouseDrag(e);
}
});
}
this._initLocations();
this._initFlights();
},
_initLocations: function() {
for (var key in this.options.locations) {
var location = this.options.locations[key];
location.visible = true;
this.options.onInitLocation(location, this);
}
},
_initFlights: function() {
for (var key in this.options.paths) {
var path = this.options.paths[key];
for (var key in path.flights) {
path.flights[key].visible = true;
this.options.onInitFlight(path.flights[key], this);
}
}
},
getSphereRadiusInPixel: function() {
return this.earth.getRadius() / 2;
},
_onSphereCreated: function(earth, textureWidth, textureHeight) {
var self = this;
this.textureWidth = textureWidth;
this.textureHeight = textureHeight;
this.earth = earth;
this.earth.init(this.options.sphere);
this.earth.turnBy = function(time) { return self._turnBy(time); };
var renderAnimationFrame = function(/* time */ time) {
/* time ~= +new Date // the unix time */
earth.renderFrame(time);
self._renderAnimationFrame(time);
self.renderAnimationFrameId = window.requestAnimationFrame(renderAnimationFrame);
};
this.renderAnimationFrameId = window.requestAnimationFrame(renderAnimationFrame);
},
destroy: function() {
window.cancelAnimationFrame(this.renderAnimationFrameId);
},
_renderAnimationFrame: function(time) {
var ry=90+this.options.sphere.tilt;
var rz=180+this.options.sphere.turn;
var RY = (90-ry);
var RZ = (180-rz);
var RX = 0,RY,RZ;
var rx=RX*Math.PI/180;
var ry=RY*Math.PI/180;
var rz=RZ*Math.PI/180;
//console.log(rx, ry, rz);
var r = this.getSphereRadiusInPixel();
var center = {
x: this.element.width() / 2,
y: this.element.height() / 2
}
for (var key in this.options.locations) {
var location = this.options.locations[key];
if (typeof location.delta === 'undefined') {
location.flatPosition = {x: location.x, y: location.y};
this.options.onRefreshLocation(location, location.x, location.y, this);
continue;
}
/*
WARNING: calculation of alphaAngle and deltaAngle is not exact
I had to create the _calibrated functions to modify the deltaAngle to make the result look good on
a spinning planet without rotation. It will totally bug with rotation!
* */
var progression = (((this.posVar + this.textureWidth * location.delta / (2 * Math.PI)) % this.textureWidth) / this.textureWidth);
var alphaAngle = progression * 2 * Math.PI;
var deltaAngle = this._calibrated(progression, location.alpha) * 2 * Math.PI;
var objAlpha = ry + location.alpha - Math.sin(alphaAngle / 2) * 0.15 * (location.alpha - Math.PI / 2) / (Math.PI / 4);
var objDelta = rz + deltaAngle;
var a = this._orbitalTo3d(objAlpha, objDelta, r);
var flatPosition = this._orthographicProjection(a);
if (a.x < 0 && !location.visible) {
this.options.onShowLocation(location, flatPosition.x, flatPosition.y, this);
}
if (a.x > 0 && location.visible) {
this.options.onHideLocation(location, flatPosition.x, flatPosition.y, this);
}
this.options.onRefreshLocation(location, flatPosition.x, flatPosition.y, this);
location.visible = a.x < 0;
location.position = a;
location.flatPosition = flatPosition;
location.rAlpha = objAlpha;
location.rDelta = objDelta;
}
if (this.flightsCtx !== null) {
this.flightsCtx.clearRect(0, 0, this.options.flightsCanvas.width(), this.options.flightsCanvas.height());
for (var key in this.options.paths) {
this._drawPath(this.options.paths[key], center, r);
}
}
},
_line_circle_intersection: function(A, B, C, r) {
var d = {
x: B.x - A.x,
y: B.y - A.y
};
var f = {
x: A.x - C.x,
y: A.y - C.y
};
var a = this._dot(d, d);
var b = 2 * this._dot(f, d);
var c = this._dot(f, f) - r * r;
var discriminant = b * b - 4 * a * c;
if (discriminant < 0) {
return false;
} else {
discriminant = Math.sqrt(discriminant);
var t1 = (-b + discriminant) / (2 * a);
var t2 = (-b - discriminant) / (2 * a);
var sols = [];
if (t1 >= 0 && t1 <= 1) {
sols.push({
x:A.x + t1 * d.x,
y:A.y + t1 * d.y
});
}
if (t2 >= 0 && t2 <= 1) {
sols.push({
x:A.x + t2 * d.x,
y:A.y + t2 * d.y
});
}
return sols;
}
},
_dot: function(A, B) {
return A.x * B.x + A.y * B.y;
},
_drawPath: function(path, center, r) {
var originLocation = this.options.locations[path.origin];
var destinationLocation = this.options.locations[path.destination];
var dotSize = 50;
var spacing = 0.15;
if (typeof originLocation.delta === 'undefined' || typeof destinationLocation.delta === 'undefined') {
var pathVisible = originLocation.visible && destinationLocation.visible;
if (pathVisible) {
var flatDistance = this._distance(originLocation.flatPosition, destinationLocation.flatPosition);
var nb = flatDistance * 0.9 / 20;
// WARNING: we are drawing the paths on canvas, intensively using CPU. Could we gain by instead using SVG or the DOM ?
for (var i = 0; i < nb; i++) {
var fromFlatPosition = {
x: ((nb - i) / nb) * originLocation.flatPosition.x + (i / nb) * destinationLocation.flatPosition.x,
y: ((nb - i) / nb) * originLocation.flatPosition.y + (i / nb) * destinationLocation.flatPosition.y
};
var toFlatPosition = {
x: Math.max(((nb - (i + 1)) / nb), 0) * originLocation.flatPosition.x + Math.min(((i + 1) / nb), 1) * destinationLocation.flatPosition.x,
y: Math.max(((nb - (i + 1)) / nb), 0) * originLocation.flatPosition.y + Math.min(((i + 1) / nb), 1) * destinationLocation.flatPosition.y
};
var diff = {
x: fromFlatPosition.x - toFlatPosition.x,
y: fromFlatPosition.y - toFlatPosition.y,
z: fromFlatPosition.z - toFlatPosition.z
};
fromFlatPosition.x -= diff.x * spacing;
fromFlatPosition.y -= diff.y * spacing;
fromFlatPosition.z -= diff.z * spacing;
toFlatPosition.x += diff.x * spacing;
toFlatPosition.y += diff.y * spacing;
toFlatPosition.z += diff.z * spacing;
this.flightsCtx.lineWidth = 3;
this.flightsCtx.beginPath();
this.flightsCtx.strokeStyle = 'rgba(255, 255, 255, 0.5)';
this.flightsCtx.moveTo(fromFlatPosition.x + this.options.flightsCanvasPosition.x, fromFlatPosition.y + this.options.flightsCanvasPosition.y);
this.flightsCtx.lineTo(toFlatPosition.x + this.options.flightsCanvasPosition.x, toFlatPosition.y + this.options.flightsCanvasPosition.y);
this.flightsCtx.stroke();
}
}
for (var key in path.flights) {
var flight = path.flights[key];
var position = flight.destination == path.destination ? flight.position : (1 - flight.position);
var flightFlatPosition = {
x: (1 - position) * originLocation.flatPosition.x + position * destinationLocation.flatPosition.x,
y: (1 - position) * originLocation.flatPosition.y + position * destinationLocation.flatPosition.y
};
if (!flight.visible && pathVisible) {
this.options.onShowFlight(flight, this);
flight.visible = true;
}
if (flight.visible && !pathVisible) {
this.options.onHideFlight(flight, this);
flight.visible = false;
}
var angle = Math.atan2(destinationLocation.flatPosition.y - originLocation.flatPosition.y, destinationLocation.flatPosition.x - originLocation.flatPosition.x) + (flight.destination == path.destination ? 0 : Math.PI);
//console.log(flightAheadFlatPosition.y - flightFlatPosition.y);
this.options.onRefreshFlight(flight, flightFlatPosition.x, flightFlatPosition.y, angle, this);
}
return;
}
var locationsDistance = this._distance(originLocation.position, destinationLocation.position);
var middlePosition = {
x: 0,
y: 0,
z: 0
};
var radius = this._distance(originLocation.position, middlePosition);
var originP = {
delta: Math.atan2((originLocation.position.y - middlePosition.y), (originLocation.position.x - middlePosition.x)),
alpha: Math.acos((originLocation.position.z - middlePosition.z) / radius)
};
var destinationP = {
delta: Math.atan2((destinationLocation.position.y - middlePosition.y), (destinationLocation.position.x - middlePosition.x)),
alpha: Math.acos((destinationLocation.position.z - middlePosition.z) / radius)
};
if (Math.abs(originP.delta - destinationP.delta) > Math.PI) {
if ((originP.delta - destinationP.delta) > Math.PI) {
originP.delta -= 2 * Math.PI;
} else {
originP.delta += 2 * Math.PI;
}
}
if (path.sens) {
if (((originP.delta - destinationP.delta) > 0 ? 1 : -1) != path.sens) {
if (Math.abs(originP.delta - destinationP.delta) > Math.PI / 2) {
originP.delta += path.sens * 2 * Math.PI;
}
}
} else {
path.sens = (originP.delta - destinationP.delta) > 0 ? 1 : -1;
}
if (!path.nb) {
path.nb = Math.round(((locationsDistance / (2 * r)) * Math.PI * 2 * r + (1 - (locationsDistance / (2 * r))) * locationsDistance) / dotSize);
}
var nb = path.nb;
var maxDistance = 1.2;
for (var i = 0; i < nb; i++) {
var fromP = {
alpha: ((nb - i) / nb) * originP.alpha + (i / nb) * destinationP.alpha,
delta: ((nb - i) / nb) * originP.delta + (i / nb) * destinationP.delta
};
var toP = {
alpha: ((nb - 1 - i) / nb) * originP.alpha + ((i + 1) / nb) * destinationP.alpha,
delta: ((nb - 1 - i) / nb) * originP.delta + ((i + 1) / nb) * destinationP.delta
};
//console.log(i, fromP.alpha, fromP.delta, toP.alpha, toP.delta);
var fromPosition = this._orbitalTo3d(fromP.alpha, fromP.delta, -(Math.sin(Math.PI * i / nb) * (maxDistance - 1) + 1) * radius);
var toPosition = this._orbitalTo3d(toP.alpha, toP.delta, -(Math.sin(Math.PI * (i + 1) / nb) * (maxDistance - 1) + 1) * radius);
var diff = {
x: fromPosition.x - toPosition.x,
y: fromPosition.y - toPosition.y,
z: fromPosition.z - toPosition.z
};
fromPosition.x -= diff.x * spacing;
fromPosition.y -= diff.y * spacing;
fromPosition.z -= diff.z * spacing;
toPosition.x += diff.x * spacing;
toPosition.y += diff.y * spacing;
toPosition.z += diff.z * spacing;
fromPosition.x += middlePosition.x;
fromPosition.y += middlePosition.y;
fromPosition.z += middlePosition.z;
toPosition.x += middlePosition.x;
toPosition.y += middlePosition.y;
toPosition.z += middlePosition.z;
var fromFlatPosition = this._orthographicProjection(fromPosition);
var toFlatPosition = this._orthographicProjection(toPosition);
var fromDistanceCenter = this._distance(fromFlatPosition, center);
var toDistanceCenter = this._distance(toFlatPosition, center);
var fromVisible = true;
var toVisible = true;
if (fromPosition.x > 0) {
if (fromDistanceCenter <= r) {
fromVisible = false;
}
}
if (toPosition.x > 0) {
if (toDistanceCenter <= r) {
toVisible = false;
}
}
//console.log(i, fromVisible, toVisible);
if (!fromVisible && !toVisible) {
continue;
}
if (!fromVisible) {
var intersection = this._line_circle_intersection(fromFlatPosition, toFlatPosition, center, r);
if (intersection.length == 0) {
continue;
}
fromFlatPosition = intersection[0];
}
if (!toVisible) {
var intersection = this._line_circle_intersection(fromFlatPosition, toFlatPosition, center, r);
if (intersection.length == 0) {
continue;
}
toFlatPosition = intersection[0];
}
this.flightsCtx.lineWidth = 3;
this.flightsCtx.beginPath();
this.flightsCtx.strokeStyle = 'rgba(255, 255, 255, 0.5)';
this.flightsCtx.moveTo(fromFlatPosition.x + this.options.flightsCanvasPosition.x, fromFlatPosition.y + this.options.flightsCanvasPosition.y);
this.flightsCtx.lineTo(toFlatPosition.x + this.options.flightsCanvasPosition.x, toFlatPosition.y + this.options.flightsCanvasPosition.y);
this.flightsCtx.stroke();
}
for (var key in path.flights) {
var flight = path.flights[key];
var position = flight.destination == path.destination ? flight.position : (1 - flight.position);
var positionAhead = flight.destination == path.destination ? (flight.position + 0.01) : (1 - (flight.position + 0.01));
var flightP = {
alpha: (1 - position) * originP.alpha + position * destinationP.alpha,
delta: (1 - position) * originP.delta + position * destinationP.delta
};
var flightAheadP = {
alpha: (1 - positionAhead) * originP.alpha + positionAhead * destinationP.alpha,
delta: (1 - positionAhead) * originP.delta + positionAhead * destinationP.delta
};
var flightPosition = this._orbitalTo3d(flightP.alpha, flightP.delta, -(Math.sin(Math.PI * position) * (maxDistance - 1) + 1) * radius);
var flightAheadPosition = this._orbitalTo3d(flightAheadP.alpha, flightAheadP.delta, -(Math.sin(Math.PI * positionAhead) * (maxDistance - 1) + 1) * radius);
flightPosition.x += middlePosition.x;
flightPosition.y += middlePosition.y;
flightPosition.z += middlePosition.z;
flightAheadPosition.x += middlePosition.x;
flightAheadPosition.y += middlePosition.y;
flightAheadPosition.z += middlePosition.z;
var flightFlatPosition = this._orthographicProjection(flightPosition);
var flightAheadFlatPosition = this._orthographicProjection(flightAheadPosition);
var flightDistanceCenter = this._distance(flightFlatPosition, center);
if (!flight.visible && (flightPosition.x < 0 || flightDistanceCenter > r)) {
this.options.onShowFlight(flight, this);
flight.visible = true;
}
if (flight.visible && (flightPosition.x > 0 && flightDistanceCenter < r)) {
this.options.onHideFlight(flight, this);
flight.visible = false;
}
var angle = Math.atan2(flightAheadFlatPosition.y - flightFlatPosition.y, flightAheadFlatPosition.x - flightFlatPosition.x);
//console.log(flightAheadFlatPosition.y - flightFlatPosition.y);
this.options.onRefreshFlight(flight, flightFlatPosition.x, flightFlatPosition.y, angle, this);
}
},
_distance: function(A, B) {
if (A.z) {
return Math.sqrt(
(A.x - B.x) * (A.x - B.x) +
(A.y - B.y) * (A.y - B.y) +
(A.z - B.z) * (A.z - B.z)
);
} else {
return Math.sqrt(
(A.x - B.x) * (A.x - B.x) +
(A.y - B.y) * (A.y - B.y)
);
}
},
// WARNING: temporary function to make the locations look good on a spinning planet without rotation
_calibrated: function(x, alpha) {
var calib = 0.3 + 0.15 * Math.abs(alpha - Math.PI / 2) / (Math.PI / 4);
//console.log(calib);
var y = calib * (4 * (x - 0.5) * (x - 0.5) * (x - 0.5) + 0.5) + (1 - calib) * x;
return y;
},
/* WARNING:
Obviously there is something wrong with _orbitalTo3d and _orthographicProjection, since
I can't get a descent display of locations when the planet is rotated. That's why I had to create the _calibrated
function in the first place. I didn't have time to look precisely into it, and I probably don't know enough math.
I leaved the _3dProjection function I found on wikipedia but is not working. (I might not have correctly understood / write it)
*/
_orbitalTo3d: function(alpha, delta, r) {
return {
x: -r * Math.sin(alpha) * Math.cos(delta),
y: -r * Math.sin(alpha) * Math.sin(delta),
z: -r * Math.cos(alpha)
};
},
_orthographicProjection: function(position) {
return {x: position.y + this.element.width() / 2, y: position.z + this.element.height() / 2};
},
_3dProjection: function(a, c, delta, e) {
// Wikipedia is your friend :) : http://en.wikipedia.org/wiki/3D_projection
var d = {x: 0, y: 0, z: 0};
d.x = Math.cos(delta.y) * (Math.sin(delta.z) * (a.y - c.y) + Math.cos(delta.z) * (a.x - c.x)) - Math.sin(delta.y) * (a.z - c.z);
d.y = Math.sin(delta.x) * (Math.cos(delta.y) * (a.z - c.z) + Math.sin(delta.y) * (Math.sin(delta.z) * (a.y - c.y) + Math.cos(delta.z) * (a.x - c.x)))
+ Math.cos(delta.x) * (Math.cos(delta.z) * (a.y - c.y) - Math.sin(delta.z) * (a.x - c.x))
d.z = Math.cos(delta.x) * (Math.cos(delta.y) * (a.z - c.z) + Math.sin(delta.y) * (Math.sin(delta.z) * (a.y - c.y) + Math.cos(delta.z) * (a.x - c.x)))
- Math.sin(delta.x) * (Math.cos(delta.z) * (a.y - c.y) - Math.sin(delta.z) * (a.x - c.x));
return {
x: d.z, //(d.x - e.x) * (e.y / d.y),
y: d.y //(d.z - e.z) * (e.y / d.y)
};
},
_mouseDragStart: function(e) {
this.lastMousePos = e.clientX;
this.lastSpeed = null;
},
_mouseDrag: function(e) {
this.lastSpeed = (e.clientX - this.lastMousePos);
this.posVar = this.posVar - this.lastSpeed;
this.lastMousePos = e.clientX;
},
_mouseDragStop: function(e) {
this.lastMousePos = null;
this.lastTime = null;
},
_turnBy: function(time) {
if (this.lastTurnByTime === null) {
this.lastTurnByTime = time;
}
var timeDiff = (time - this.lastTurnByTime) / 1000;
if (this.lastMousePos === null) {
if (this.lastSpeed !== null) {
if (this.lastTime === null) {
this.lastTime = time;
}
if (this.options.backToDefaultTime + this.lastTime - time < 0) {
this.lastSpeed = null;
} else {
var backToDef = (this.options.backToDefaultTime + this.lastTime - time) / this.options.backToDefaultTime;
this.posVar -= this.lastSpeed * backToDef + (this.options.defaultSpeed * timeDiff) * (1 - backToDef);
}
} else {
this.posVar -= this.options.defaultSpeed * timeDiff;
}
}
this.lastTurnByTime = time;
return this.posVar;
},
_getQBezierValue: function (t, p1, p2, p3) {
var iT = 1 - t;
return iT * iT * p1 + 2 * iT * t * p2 + t * t * p3;
},
_getQBezierDerivation: function(t, p1, p2, p3) {
return (2 * p1 - 4 * p2 + 2 * p3) * t + 2 * p2 - 2 * p1;
},
_getQBezierAngle: function(startX, startY, cpX, cpY, endX, endY, position) {
var x = this._getQBezierDerivation(position, startX, cpX, endX);
var y = this._getQBezierDerivation(position, startY, cpY, endY);
return Math.atan2(y, x);
},
_getQuadraticCurvePoint: function(startX, startY, cpX, cpY, endX, endY, position) {
return {
x: this._getQBezierValue(position, startX, cpX, endX),
y: this._getQBezierValue(position, startY, cpY, endY),
angle: this._getQBezierAngle(startX, startY, cpX, cpY, endX, endY, position)
};
},
changeLocations: function(locations) {
for (var key in this.options.locations) {
this.options.onDeleteLocation(this.options.locations[key], this);
}
this.options.locations = locations;
this._initLocations();
},
changePaths: function(paths) {
for (var key in this.options.paths) {
var path = this.options.paths[key];
for (var keyFlight in path.flights) {
var flight = path.flights[keyFlight];
this.options.onDeleteFlight(flight, this);
}
}
this.options.paths = paths;
this._initFlights();
}
});
})($);

640
earth/leaflet.css Normal file
View File

@ -0,0 +1,640 @@
/* required styles */
.leaflet-pane,
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-tile-container,
.leaflet-pane > svg,
.leaflet-pane > canvas,
.leaflet-zoom-box,
.leaflet-image-layer,
.leaflet-layer {
position: absolute;
left: 0;
top: 0;
}
.leaflet-container {
overflow: hidden;
}
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
-webkit-user-drag: none;
}
/* Prevents IE11 from highlighting tiles in blue */
.leaflet-tile::selection {
background: transparent;
}
/* Safari renders non-retina tile on retina better with this, but Chrome is worse */
.leaflet-safari .leaflet-tile {
image-rendering: -webkit-optimize-contrast;
}
/* hack that prevents hw layers "stretching" when loading new tiles */
.leaflet-safari .leaflet-tile-container {
width: 1600px;
height: 1600px;
-webkit-transform-origin: 0 0;
}
.leaflet-marker-icon,
.leaflet-marker-shadow {
display: block;
}
/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */
/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */
.leaflet-container .leaflet-overlay-pane svg,
.leaflet-container .leaflet-marker-pane img,
.leaflet-container .leaflet-shadow-pane img,
.leaflet-container .leaflet-tile-pane img,
.leaflet-container img.leaflet-image-layer,
.leaflet-container .leaflet-tile {
max-width: none !important;
max-height: none !important;
}
.leaflet-container.leaflet-touch-zoom {
-ms-touch-action: pan-x pan-y;
touch-action: pan-x pan-y;
}
.leaflet-container.leaflet-touch-drag {
-ms-touch-action: pinch-zoom;
/* Fallback for FF which doesn't support pinch-zoom */
touch-action: none;
touch-action: pinch-zoom;
}
.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom {
-ms-touch-action: none;
touch-action: none;
}
.leaflet-container {
-webkit-tap-highlight-color: transparent;
}
.leaflet-container a {
-webkit-tap-highlight-color: rgba(51, 181, 229, 0.4);
}
.leaflet-tile {
filter: inherit;
visibility: hidden;
}
.leaflet-tile-loaded {
visibility: inherit;
}
.leaflet-zoom-box {
width: 0;
height: 0;
-moz-box-sizing: border-box;
box-sizing: border-box;
z-index: 800;
}
/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
.leaflet-overlay-pane svg {
-moz-user-select: none;
}
.leaflet-pane { z-index: 400; }
.leaflet-tile-pane { z-index: 200; }
.leaflet-overlay-pane { z-index: 400; }
.leaflet-shadow-pane { z-index: 500; }
.leaflet-marker-pane { z-index: 600; }
.leaflet-tooltip-pane { z-index: 650; }
.leaflet-popup-pane { z-index: 700; }
.leaflet-map-pane canvas { z-index: 100; }
.leaflet-map-pane svg { z-index: 200; }
.leaflet-vml-shape {
width: 1px;
height: 1px;
}
.lvml {
behavior: url(#default#VML);
display: inline-block;
position: absolute;
}
/* control positioning */
.leaflet-control {
position: relative;
z-index: 800;
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
pointer-events: auto;
}
.leaflet-top,
.leaflet-bottom {
position: absolute;
z-index: 1000;
pointer-events: none;
}
.leaflet-top {
top: 0;
}
.leaflet-right {
right: 0;
}
.leaflet-bottom {
bottom: 0;
}
.leaflet-left {
left: 0;
}
.leaflet-control {
float: left;
clear: both;
}
.leaflet-right .leaflet-control {
float: right;
}
.leaflet-top .leaflet-control {
margin-top: 10px;
}
.leaflet-bottom .leaflet-control {
margin-bottom: 10px;
}
.leaflet-left .leaflet-control {
margin-left: 10px;
}
.leaflet-right .leaflet-control {
margin-right: 10px;
}
/* zoom and fade animations */
.leaflet-fade-anim .leaflet-tile {
will-change: opacity;
}
.leaflet-fade-anim .leaflet-popup {
opacity: 0;
-webkit-transition: opacity 0.2s linear;
-moz-transition: opacity 0.2s linear;
transition: opacity 0.2s linear;
}
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
opacity: 1;
}
.leaflet-zoom-animated {
-webkit-transform-origin: 0 0;
-ms-transform-origin: 0 0;
transform-origin: 0 0;
}
.leaflet-zoom-anim .leaflet-zoom-animated {
will-change: transform;
}
.leaflet-zoom-anim .leaflet-zoom-animated {
-webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
-moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
transition: transform 0.25s cubic-bezier(0,0,0.25,1);
}
.leaflet-zoom-anim .leaflet-tile,
.leaflet-pan-anim .leaflet-tile {
-webkit-transition: none;
-moz-transition: none;
transition: none;
}
.leaflet-zoom-anim .leaflet-zoom-hide {
visibility: hidden;
}
/* cursors */
.leaflet-interactive {
cursor: pointer;
}
.leaflet-grab {
cursor: -webkit-grab;
cursor: -moz-grab;
cursor: grab;
}
.leaflet-crosshair,
.leaflet-crosshair .leaflet-interactive {
cursor: crosshair;
}
.leaflet-popup-pane,
.leaflet-control {
cursor: auto;
}
.leaflet-dragging .leaflet-grab,
.leaflet-dragging .leaflet-grab .leaflet-interactive,
.leaflet-dragging .leaflet-marker-draggable {
cursor: move;
cursor: -webkit-grabbing;
cursor: -moz-grabbing;
cursor: grabbing;
}
/* marker & overlays interactivity */
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-image-layer,
.leaflet-pane > svg path,
.leaflet-tile-container {
pointer-events: none;
}
.leaflet-marker-icon.leaflet-interactive,
.leaflet-image-layer.leaflet-interactive,
.leaflet-pane > svg path.leaflet-interactive,
svg.leaflet-image-layer.leaflet-interactive path {
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
pointer-events: auto;
}
/* visual tweaks */
.leaflet-container {
background: #ddd;
outline: 0;
}
.leaflet-container a {
color: #0078A8;
}
.leaflet-container a.leaflet-active {
outline: 2px solid orange;
}
.leaflet-zoom-box {
border: 2px dotted #38f;
background: rgba(255,255,255,0.5);
}
/* general typography */
.leaflet-container {
font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif;
}
/* general toolbar styles */
.leaflet-bar {
box-shadow: 0 1px 5px rgba(0,0,0,0.65);
border-radius: 4px;
}
.leaflet-bar a,
.leaflet-bar a:hover {
background-color: #fff;
border-bottom: 1px solid #ccc;
width: 26px;
height: 26px;
line-height: 26px;
display: block;
text-align: center;
text-decoration: none;
color: black;
}
.leaflet-bar a,
.leaflet-control-layers-toggle {
background-position: 50% 50%;
background-repeat: no-repeat;
display: block;
}
.leaflet-bar a:hover {
background-color: #f4f4f4;
}
.leaflet-bar a:first-child {
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.leaflet-bar a:last-child {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
border-bottom: none;
}
.leaflet-bar a.leaflet-disabled {
cursor: default;
background-color: #f4f4f4;
color: #bbb;
}
.leaflet-touch .leaflet-bar a {
width: 30px;
height: 30px;
line-height: 30px;
}
.leaflet-touch .leaflet-bar a:first-child {
border-top-left-radius: 2px;
border-top-right-radius: 2px;
}
.leaflet-touch .leaflet-bar a:last-child {
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
}
/* zoom control */
.leaflet-control-zoom-in,
.leaflet-control-zoom-out {
font: bold 18px 'Lucida Console', Monaco, monospace;
text-indent: 1px;
}
.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out {
font-size: 22px;
}
/* layers control */
.leaflet-control-layers {
box-shadow: 0 1px 5px rgba(0,0,0,0.4);
background: #fff;
border-radius: 5px;
}
.leaflet-control-layers-toggle {
background-image: url(images/layers.png);
width: 36px;
height: 36px;
}
.leaflet-retina .leaflet-control-layers-toggle {
background-image: url(images/layers-2x.png);
background-size: 26px 26px;
}
.leaflet-touch .leaflet-control-layers-toggle {
width: 44px;
height: 44px;
}
.leaflet-control-layers .leaflet-control-layers-list,
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
display: none;
}
.leaflet-control-layers-expanded .leaflet-control-layers-list {
display: block;
position: relative;
}
.leaflet-control-layers-expanded {
padding: 6px 10px 6px 6px;
color: #333;
background: #fff;
}
.leaflet-control-layers-scrollbar {
overflow-y: scroll;
overflow-x: hidden;
padding-right: 5px;
}
.leaflet-control-layers-selector {
margin-top: 2px;
position: relative;
top: 1px;
}
.leaflet-control-layers label {
display: block;
}
.leaflet-control-layers-separator {
height: 0;
border-top: 1px solid #ddd;
margin: 5px -10px 5px -6px;
}
/* Default icon URLs */
.leaflet-default-icon-path {
background-image: url(images/marker-icon.png);
}
/* attribution and scale controls */
.leaflet-container .leaflet-control-attribution {
background: #fff;
background: rgba(255, 255, 255, 0.7);
margin: 0;
}
.leaflet-control-attribution,
.leaflet-control-scale-line {
padding: 0 5px;
color: #333;
}
.leaflet-control-attribution a {
text-decoration: none;
}
.leaflet-control-attribution a:hover {
text-decoration: underline;
}
.leaflet-container .leaflet-control-attribution,
.leaflet-container .leaflet-control-scale {
font-size: 11px;
}
.leaflet-left .leaflet-control-scale {
margin-left: 5px;
}
.leaflet-bottom .leaflet-control-scale {
margin-bottom: 5px;
}
.leaflet-control-scale-line {
border: 2px solid #777;
border-top: none;
line-height: 1.1;
padding: 2px 5px 1px;
font-size: 11px;
white-space: nowrap;
overflow: hidden;
-moz-box-sizing: border-box;
box-sizing: border-box;
background: #fff;
background: rgba(255, 255, 255, 0.5);
}
.leaflet-control-scale-line:not(:first-child) {
border-top: 2px solid #777;
border-bottom: none;
margin-top: -2px;
}
.leaflet-control-scale-line:not(:first-child):not(:last-child) {
border-bottom: 2px solid #777;
}
.leaflet-touch .leaflet-control-attribution,
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar {
box-shadow: none;
}
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar {
border: 2px solid rgba(0,0,0,0.2);
background-clip: padding-box;
}
/* popup */
.leaflet-popup {
position: absolute;
text-align: center;
margin-bottom: 20px;
}
.leaflet-popup-content-wrapper {
padding: 1px;
text-align: left;
border-radius: 12px;
}
.leaflet-popup-content {
margin: 13px 19px;
line-height: 1.4;
}
.leaflet-popup-content p {
margin: 18px 0;
}
.leaflet-popup-tip-container {
width: 40px;
height: 20px;
position: absolute;
left: 50%;
margin-left: -20px;
overflow: hidden;
pointer-events: none;
}
.leaflet-popup-tip {
width: 17px;
height: 17px;
padding: 1px;
margin: -10px auto 0;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}
.leaflet-popup-content-wrapper,
.leaflet-popup-tip {
background: white;
color: #333;
box-shadow: 0 3px 14px rgba(0,0,0,0.4);
}
.leaflet-container a.leaflet-popup-close-button {
position: absolute;
top: 0;
right: 0;
padding: 4px 4px 0 0;
border: none;
text-align: center;
width: 18px;
height: 14px;
font: 16px/14px Tahoma, Verdana, sans-serif;
color: #c3c3c3;
text-decoration: none;
font-weight: bold;
background: transparent;
}
.leaflet-container a.leaflet-popup-close-button:hover {
color: #999;
}
.leaflet-popup-scrolled {
overflow: auto;
border-bottom: 1px solid #ddd;
border-top: 1px solid #ddd;
}
.leaflet-oldie .leaflet-popup-content-wrapper {
-ms-zoom: 1;
}
.leaflet-oldie .leaflet-popup-tip {
width: 24px;
margin: 0 auto;
-ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
}
.leaflet-oldie .leaflet-popup-tip-container {
margin-top: -1px;
}
.leaflet-oldie .leaflet-control-zoom,
.leaflet-oldie .leaflet-control-layers,
.leaflet-oldie .leaflet-popup-content-wrapper,
.leaflet-oldie .leaflet-popup-tip {
border: 1px solid #999;
}
/* div icon */
.leaflet-div-icon {
background: #fff;
border: 1px solid #666;
}
/* Tooltip */
/* Base styles for the element that has a tooltip */
.leaflet-tooltip {
position: absolute;
padding: 6px;
background-color: #fff;
border: 1px solid #fff;
border-radius: 3px;
color: #222;
white-space: nowrap;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
pointer-events: none;
box-shadow: 0 1px 3px rgba(0,0,0,0.4);
}
.leaflet-tooltip.leaflet-clickable {
cursor: pointer;
pointer-events: auto;
}
.leaflet-tooltip-top:before,
.leaflet-tooltip-bottom:before,
.leaflet-tooltip-left:before,
.leaflet-tooltip-right:before {
position: absolute;
pointer-events: none;
border: 6px solid transparent;
background: transparent;
content: "";
}
/* Directions */
.leaflet-tooltip-bottom {
margin-top: 6px;
}
.leaflet-tooltip-top {
margin-top: -6px;
}
.leaflet-tooltip-bottom:before,
.leaflet-tooltip-top:before {
left: 50%;
margin-left: -6px;
}
.leaflet-tooltip-top:before {
bottom: 0;
margin-bottom: -12px;
border-top-color: #fff;
}
.leaflet-tooltip-bottom:before {
top: 0;
margin-top: -12px;
margin-left: -6px;
border-bottom-color: #fff;
}
.leaflet-tooltip-left {
margin-left: -6px;
}
.leaflet-tooltip-right {
margin-left: 6px;
}
.leaflet-tooltip-left:before,
.leaflet-tooltip-right:before {
top: 50%;
margin-top: -6px;
}
.leaflet-tooltip-left:before {
right: 0;
margin-right: -12px;
border-left-color: #fff;
}
.leaflet-tooltip-right:before {
left: 0;
margin-left: -12px;
border-right-color: #fff;
}

6
earth/leaflet.js Normal file

File diff suppressed because one or more lines are too long

97
earth/map_render.html Normal file
View File

@ -0,0 +1,97 @@
<!DOCTYPE html>
<html>
<head>
<title>Leaflet Map</title>
<link rel="stylesheet" href="leaflet.css" />
</head>
<body>
<div id="map" style="width: 800px; height: 600px;"></div>
<br>
<script src="leaflet.js"></script>
<script>
// const desiredImageWidthInKm = 11; // Now Is calculated from northEastLon - southWestLon
const tileSizeInPixels = 256;
function calculateZoomLevel(desiredImageWidthInKm, latitude) {
const earthEquatorialCircumference = 40075016.686; // Earth's equatorial circumference in meters
// Calculate the distance covered by one degree of longitude at the given latitude
const metersPerLongitudeDegree = earthEquatorialCircumference * Math.cos((Math.PI / 180) * latitude) / 360;
const metersPerPixel = metersPerLongitudeDegree * 360 / (tileSizeInPixels * Math.pow(2, 20));
// Calculate the number of pixels needed to achieve the desired width
const numberOfPixelsHorizontally = (desiredImageWidthInKm * 1000) / metersPerPixel;
// Calculate the appropriate zoom level
const zoomLevel = Math.log2(tileSizeInPixels * Math.pow(2, 20) / numberOfPixelsHorizontally);
return zoomLevel;
}
// Function to extract URL parameters
function getUrlParameter(name) {
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
const regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
const results = regex.exec(location.search);
return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
}
const defaultSouthWestLat = 44.000;
const defaultSouthWestLon = -1.000;
const defaultOffsetDegrees = 10.000; // 10° offset
const southWestLat = parseFloat(getUrlParameter('southWestLat')) || defaultSouthWestLat;
console.log('Lat:', southWestLat);
const southWestLon = parseFloat(getUrlParameter('southWestLon')) || defaultSouthWestLon;
console.log('Lon:', southWestLon);
const deg = parseFloat(getUrlParameter('deg')) || defaultOffsetDegrees;
console.log('Offset:', deg);
// Calculate northEastLat and northEastLon with deg° offset only if parameters are empty
const northEastLatParam = getUrlParameter('northEastLat');
const northEastLonParam = getUrlParameter('northEastLon');
const northEastLat = northEastLatParam ? parseFloat(northEastLatParam) : southWestLat + deg;
const northEastLon = northEastLonParam ? parseFloat(northEastLonParam) : southWestLon + deg;
// Calculate the longitudinal distance in degrees
const lonDistanceInDegrees = Math.abs(northEastLon - southWestLon);
console.log('lonDistanceInDegrees:', lonDistanceInDegrees);
// Calculate the desired image width in kilometers
const earthEquatorialCircumference = 40075016.686; // Earth's equatorial circumference in meters
const metersPerLongitudeDegree = Math.abs(earthEquatorialCircumference * Math.cos((Math.PI / 180) * southWestLat) / 360);
const desiredImageWidthInMeters = lonDistanceInDegrees * metersPerLongitudeDegree;
const desiredImageWidthInKm = desiredImageWidthInMeters / 1000;
console.log('desiredImageWidthInKm:', desiredImageWidthInKm);
const centerLat = (southWestLat + northEastLat) / 2;
const centerLon = (southWestLon + northEastLon) / 2;
// Provide the latitude for adjustment
const latitude = centerLat;
const zoomLevel = calculateZoomLevel(desiredImageWidthInKm, latitude);
console.log('Recommended zoom level:', zoomLevel);
const map = L.map('map').setView([centerLat, centerLon], zoomLevel);
// Use the Mapbox Satellite tile layer
//~ L.tileLayer('https://api.mapbox.com/styles/v1/mapbox/satellite-v9/tiles/{z}/{x}/{y}?access_token=YOUR_MAPBOX_ACCESS_TOKEN', {
//~ attribution: '© Mapbox',
//~ tileSize: 512,
//~ zoomOffset: -1,
//~ accessToken: 'YOUR_MAPBOX_ACCESS_TOKEN'
//~ }).addTo(map);
//~ // Use the OpenAerialMap (OAM) tile layer for satellite view
//~ L.tileLayer('https://tiles.openaerialmap.org/5d6d1e370f437a00106e06ef/0/{z}/{x}/{y}.jpg', {
//~ attribution: '© OpenAerialMap',
//~ maxZoom: 18
//~ }).addTo(map);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'U Planet'
}).addTo(map);
</script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 KiB

BIN
earth/maps/plain.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -0,0 +1,30 @@
// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
// requestAnimationFrame polyfill by Erik Möller
// fixes from Paul Irish and Tino Zijdel
(function() {
var lastTime = 0;
var vendors = ['ms', 'moz', 'webkit', 'o'];
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame']
|| window[vendors[x]+'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame)
window.requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function() { callback(currTime + timeToCall); },
timeToCall);
lastTime = currTime + timeToCall;
return id;
};
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}());

608
earth/sphere-hacked.js Normal file
View File

@ -0,0 +1,608 @@
// **Sphere** renders a mathematically perfect textured sphere.
// It calculates the surface of the sphere instead of approximating it with triangles.
// Shamefully hacked by Sébastien Drouyer
/*jshint laxcomma: true, laxbreak: true, browser: true */
(function() {
"use strict";
var opts = { tilt: 40
, turn: 20
};
// Tiling informations
var tiling = {
horizontal: 1,
vertical: 1
};
// frame count, current angle of rotation. inc/dec to turn.
var gCtx;
var gImage, gCtxImg;
//Variable to hold the size of the canvas
var size;
var canvasImageData, textureImageData;
// Constants for indexing dimentions
var X=0, Y=1, Z=2;
var textureWidth, textureHeight;
var hs=30; // Horizontal scale of viewing area
var vs=30; // Vertical scale of viewing area
// NB The viewing area is an abstract rectangle in the 3d world and is not
// the same as the canvas used to display the image.
var F = [0,0,0]; // Focal point of viewer
var S = [0,30,0]; // Centre of sphere/planet
var r=12; // Radius of sphere/planet
// Distance of the viewing area from the focal point. This seems
// to give strange results if it is not equal to S[Y]. It should
// theoreticaly be changable but hs & vs can still be used along
// with r to change how large the sphere apears on the canvas.
// HOWEVER, the values of hs, vs, S[Y], f & r MUST NOT BE TOO BIG
// as this will result in overflow errors which are not traped
// and do not stop the script but will result in incorrect
// displaying of the texture upon the sphere.
var f = 30;
// There may be a solution to the above problem by finding L in
// a slightly different way.
// Since the problem is equivelent to finding the intersection
// in 2D space of a line and a circle then each view area pixel
// and associated vector can be used define a 2D plane in the 3D
// space that 'contains' the vector S-F which is the focal point
// to centre of the sphere.
//
// This is essentialy the same problem but I belive/hope it will
// not result in the same exact solution. I have hunch that the
// math will not result in such big numbers. Since this abstract
// plane will be spinning, it may be posilbe to use the symetry
// of the arangement to reuse 1/4 of the calculations.
// Variables to hold rotations about the 3 axis
var RX = 0,RY,RZ;
// Temp variables to hold them whilst rendering so they won't get updated.
var rx,ry,rz;
var a;
var b;
var b2; // b squared
var bx=F[X]-S[X]; // = 0 for current values of F and S
var by=F[Y]-S[Y];
var bz=F[Z]-S[Z]; // = 0 for current values of F and S
// c = Fx^2 + Sx^2 -2FxSx + Fy^2 + Sy^2 -2FySy + Fz^2 + Sz^2 -2FzSz - r^2
// for current F and S this means c = Sy^2 - r^2
var c = F[X]*F[X] + S[X]*S[X]
+ F[Y]*F[Y] + S[Y]*S[Y]
+ F[Z]*F[Z] + S[Z]*S[Z]
- 2*(F[X]*S[X] + F[Y]*S[Y] + F[Z]*S[Z])
- r*r
;
var c4 = c*4; // save a bit of time maybe during rendering
var s;
var m1 = 0;
//double m2 = 0;
// The following are use to calculate the vector of the current pixel to be
// drawn from the focus position F
var hs_ch; // horizontal scale divided by canvas width
var vs_cv; // vertical scale divided by canvas height
var hhs = 0.5*hs; // half horizontal scale
var hvs = 0.5*vs; // half vertical scale
var V = new Array(3); // vector for storing direction of each pixel from F
var L = new Array(3); // Location vector from S that pixel 'hits' sphere
var VY2=f*f; // V[Y] ^2 NB May change if F changes
var rotCache = {};
var calculateVector = function(h,v) {
// Calculate vector from focus point (Origin, so can ignor) to pixel
V[X]=(hs_ch*h)-hhs;
// V[Y] always the same as view frame doesn't mov
V[Z]=(vs_cv*v)-hvs;
// Vector (L) from S where m*V (m is an unknown scalar) intersects
// surface of sphere is as follows
//
// <pre>
// L = F + mV - S
//
// ,-------.
// / \ -----m------
// | S<-L->| <-V->F
// \ /
// `-------'
//
// L and m are unknown so find magnitude of vectors as the magnitude
// of L is the radius of the sphere
//
// |L| = |F + mV - S| = r
//
// Can be rearranged to form a quadratic
//
// 0 = am&sup2; +bm + c
//
// and solved to find m, using the following formula
//
// <pre>
// ___________
// m = ( -b &PlusMinus; \/(b&sup2;) - 4ac ) /2a
// </pre>
//
// r = |F + mV - S|
// __________________________________________________
// r = v(Fx + mVx -Sx)&sup2; + (Fy + mVy -Sy)&sup2; + (Fz + mVz -Sz)&sup2;
//
// r&sup2; = (Fx + mVx -Sx)&sup2; + (Fy + mVy -Sy)&sup2; + (Fz + mVz -Sz)&sup2;
//
// r&sup2; = (Fx + mVx -Sx)&sup2; + (Fy + mVy -Sy)&sup2; + (Fz + mVz -Sz)&sup2;
//
// 0 = Fx&sup2; + FxVxm -FxSx + FxVxm + Vx&sup2;m&sup2; -SxVxm -SxFx -SxVxm + Sx&sup2;
// +Fy&sup2; + FyVym -FySy + FyVym + Vy&sup2;m&sup2; -SyVym -SyFy -SyVym + Sy&sup2;
// +Fz&sup2; + FzVzm -FzSz + FzVzm + Vz&sup2;m&sup2; -SzVzm -SzFz -SzVzm + Sz&sup2; - r&sup2;
//
// 0 = Vx&sup2;m&sup2; + FxVxm + FxVxm -2SxVxm + Fx&sup2; -FxSx -SxFx + Sx&sup2;
// +Vy&sup2;m&sup2; + FyVym + FyVym -2SyVym + Fy&sup2; -FySy -SyFy + Sy&sup2;
// +Vz&sup2;m&sup2; + FzVzm + FzVzm -2SzVzm + Fz&sup2; -FzSz -SzFz + Sz&sup2; - r&sup2;
//
// 0 = (Vx&sup2; + Vy&sup2; + Vz&sup2;)m&sup2; + (FxVx + FxVx -2SxVx)m + Fx&sup2; - 2FxSx + Sx&sup2;
// + (FyVy + FyVy -2SyVy)m + Fy&sup2; - 2FySy + Sy&sup2;
// + (FzVz + FzVz -2SzVz)m + Fz&sup2; - 2FzSz + Sz&sup2; - r&sup2;
//
// 0 = |Vz|m&sup2; + (FxVx + FxVx -2SxVx)m + |F| - 2FxSx + |S|
// + (FyVy + FyVy -2SyVy)m - 2FySy
// + (FyVy + FyVy -2SyVy)m - 2FySy - r&sup2;
//
// a = |Vz|
// b =
// c = Fx&sup2; + Sx&sup2; -2FxSx + Fy&sup2; + Sy&sup2; -2FySy + Fz&sup2; + Sz&sup2; -2FzSz - r&sup2;
// for current F and S this means c = Sy&sup2; - r&sup2;
// </pre>
// Where a, b and c are as in the code.
// Only the solution for the negative square root term is needed as the
// closest intersection is wanted. The other solution to m would give
// the intersection of the 'back' of the sphere.
a=V[X]*V[X]+VY2+V[Z]*V[Z];
s=(b2-a*c4); // the square root term
// if s is negative then there are no solutions to m and the
// sphere is not visible on the current pixel on the canvas
// so only draw a pixel if the sphere is visable
// 0 is a special case as it is the 'edge' of the sphere as there
// is only one solution. (I have never seen it happen though)
// of the two solutions m1 & m2 the nearest is m1, m2 being the
// far side of the sphere.
if (s > 0) {
m1 = ((-b)-(Math.sqrt(s)))/(2*a);
L[X]=m1*V[X]; // bx+m1*V[X];
L[Y]=by+(m1*V[Y]);
L[Z]=m1*V[Z]; // bz+m1*V[Z];
// Do a couple of rotations on L
var lx=L[X];
var srz = Math.sin(rz);
var crz = Math.cos(rz);
L[X]=lx*crz-L[Y]*srz;
L[Y]=lx*srz+L[Y]*crz;
var lz;
lz=L[Z];
var sry = Math.sin(ry);
var cry = Math.cos(ry);
L[Z]=lz*cry-L[Y]*sry;
L[Y]=lz*sry+L[Y]*cry;
// Calculate the position that this location on the sphere
// coresponds to on the texture
var lh = textureWidth + textureWidth * ( Math.atan2(L[Y],L[X]) + Math.PI ) / (2*Math.PI);
// %textureHeight at end to get rid of south pole bug. probaly means that one
// pixel may be a color from the opposite pole but as long as the
// poles are the same color this won't be noticed.
var lv = textureWidth * Math.floor(textureHeight-1-(textureHeight*(Math.acos(L[Z]/r)/Math.PI)%textureHeight));
return {lv:lv,lh:lh};
}
return null;
};
/**
* Create the sphere function opject
*/
var sphere = function(){
var textureData = textureImageData.data;
var canvasData = canvasImageData.data;
var copyFnc;
if (canvasData.splice){
//2012-04-19 splice on canvas data not supported in any current browser
copyFnc = function(idxC, idxT){
canvasData.splice(idxC, 4 , textureData[idxT + 0]
, textureData[idxT + 1]
, textureData[idxT + 2]
, 255);
};
} else {
copyFnc = function(idxC, idxT){
canvasData[idxC + 0] = textureData[idxT + 0];
canvasData[idxC + 1] = textureData[idxT + 1];
canvasData[idxC + 2] = textureData[idxT + 2];
canvasData[idxC + 3] = 255;
};
}
var getVector = (function(){
var cache = new Array(size*size);
return function(pixel){
if (cache[pixel] === undefined){
var v = Math.floor(pixel / size);
var h = pixel - v * size;
cache[pixel] = calculateVector(h,v);
}
return cache[pixel];
};
})();
var posDelta = textureWidth*0.2/(20*1000);
//var firstFramePos = (new Date()) * posDelta;
var stats = {fastCount: 0, fastSumMs: 0};
return {
posDelta: posDelta,
firstFramePos: (new Date()) * posDelta,
positionsCache: [],
minX: null,
minY: null,
maxX: null,
maxY: null,
init: function(options) {
this.changeRotation(options);
hs=30; // Horizontal scale of viewing area
vs=30; // Vertical scale of viewing area
F = [0,0,0]; // Focal point of viewer
S = [0,30,0]; // Centre of sphere/planet
r=options.r; // Radius of sphere/planet
f = 30;
bx=F[X]-S[X]; // = 0 for current values of F and S
by=F[Y]-S[Y];
bz=F[Z]-S[Z]; // = 0 for current values of F and S
c = F[X]*F[X] + S[X]*S[X]
+ F[Y]*F[Y] + S[Y]*S[Y]
+ F[Z]*F[Z] + S[Z]*S[Z]
- 2*(F[X]*S[X] + F[Y]*S[Y] + F[Z]*S[Z])
- r*r
;
c4 = c*4; // save a bit of time maybe during rendering
m1 = 0;
hhs = 0.5*hs; // half horizontal scale
hvs = 0.5*vs; // half vertical scale
/*V = new Array(3);*/ // vector for storing direction of each pixel from F
L = new Array(3); // Location vector from S that pixel 'hits' sphere
VY2=f*f; // V[Y] ^2 NB May change if F changes
rotCache = {};
if (canvasData.splice){
//2012-04-19 splice on canvas data not supported in any current browser
copyFnc = function(idxC, idxT){
canvasData.splice(idxC, 4 , textureData[idxT + 0]
, textureData[idxT + 1]
, textureData[idxT + 2]
, 255);
};
} else {
copyFnc = function(idxC, idxT){
canvasData[idxC + 0] = textureData[idxT + 0];
canvasData[idxC + 1] = textureData[idxT + 1];
canvasData[idxC + 2] = textureData[idxT + 2];
canvasData[idxC + 3] = 255;
};
}
posDelta = textureWidth*0.2/(20*1000);
//var firstFramePos = (new Date()) * posDelta;
stats = {fastCount: 0, fastSumMs: 0};
getVector = (function(){
var cache = new Array(size*size);
return function(pixel){
if (cache[pixel] === undefined){
var v = Math.floor(pixel / size);
var h = pixel - v * size;
cache[pixel] = calculateVector(h,v);
}
return cache[pixel];
};
})();
},
renderFrame: function(time){
this.RF(time);
return;
stats.firstMs = new Date() - time;
this.renderFrame = this.sumRF;
console.log(rotCache);
for (var key in rotCache){
if (rotCache[key] > 1){
console.log(rotCache[key]);
}
}
},
sumRF: function(time){
this.RF(time);
stats.fastSumMs += new Date() - time;
stats.fastCount++;
if (stats.fastSumMs > stats.firstMs) {
// alert("calc:precompute ratio = 1:"+ stats.fastCount +" "+ stats.fastSumMs +" "+ stats.firstMs);
this.renderFrame = this.RF;
}
},
turnBy: function(time){
return 24*60*60 + this.firstFramePos - time * this.posDelta
},
changeRotation: function(opts) {
ry=90+opts.tilt;
rz=180+opts.turn;
RY = (90-ry);
RZ = (180-rz);
RX = 0,RY,RZ;
},
getRadius: function() {
if (this.minX === null) {
return null;
} else {
return ((this.maxX - this.minX) + (this.maxY - this.minY)) / 2;
}
},
getTexturePointPosition: function(x, y) {
var maxDistance = 30;
for (var i = 0; i < maxDistance; i++) {
var xx
var yy;
var pos;
for (xx = x - i; xx < x + i + 1; xx++) {
yy = y - i;
pos = this.getTexturePointPositionExact(xx, yy);
if (typeof pos !== 'undefined') {
return pos;
}
yy = y + i;
pos = this.getTexturePointPositionExact(xx, yy);
if (typeof pos !== 'undefined') {
return pos;
}
}
for (yy = y - i + 1; yy < y + i; yy++) {
xx = x - i;
pos = this.getTexturePointPositionExact(xx, yy);
if (typeof pos !== 'undefined') {
return pos;
}
xx = x + i;
pos = this.getTexturePointPositionExact(xx, yy);
if (typeof pos !== 'undefined') {
return pos;
}
}
}
},
getTexturePointPositionExact: function(x, y) {
var pixel = this.positionsCache[x + y * textureWidth];
if (typeof pixel === 'undefined') {
return pixel;
} else {
return {x: pixel % size, y: Math.floor(pixel / size), pixel: pixel, originalX: x, originalY: y};
}
},
RF: function(time){
// RX, RY & RZ may change part way through if the newR? (change tilt/turn) meathods are called while
// this meathod is running so put them in temp vars at render start.
// They also need converting from degrees to radians
rx=RX*Math.PI/180;
ry=RY*Math.PI/180;
rz=RZ*Math.PI/180;
// add to 24*60*60 so it will be a day before turnBy is negative and it hits the slow negative modulo bug
var turnBy = this.turnBy(time);
var pixel = size*size;
var h2 = (textureHeight * textureHeight);
this.positionsCache = new Array(h2);
this.minX = null;
this.minY = null;
this.maxX = null;
this.maxY = null;
while(pixel--){
var vector = getVector(pixel);
if (vector !== null){
var x = pixel % size;
var y = Math.floor(pixel / size);
if (this.minX == null) {
this.minX = x;
this.maxX = x;
this.minY = y;
this.maxY = y;
} else {
if (this.minX > x) {
this.minX = x;
}
if (this.maxX < x) {
this.maxX = x;
}
if (this.minY > y) {
this.minY = y;
}
if (this.maxY < y) {
this.maxY = y;
}
}
//rotate texture on sphere
var lh = Math.floor(vector.lh * tiling.horizontal + turnBy * tiling.horizontal) % textureWidth;
/* lh = (lh < 0)
? ((textureWidth-1) - ((lh-1)%textureWidth))
: (lh % textureWidth) ;
*/
var idxC = pixel * 4;
var idxT = ((lh + (vector.lv * tiling.vertical) % h2) * 4);
this.positionsCache[Math.floor(idxT / 4)] = Math.floor(idxC / 4);
/* TODO light for alpha channel or alter s or l in hsl color value?
- fn to calc distance between two points on sphere?
- attenuate light by distance from point and rotate point separate from texture rotation
*/
// Update the values of the pixel;
canvasData[idxC + 0] = textureData[idxT + 0];
canvasData[idxC + 1] = textureData[idxT + 1];
canvasData[idxC + 2] = textureData[idxT + 2];
canvasData[idxC + 3] = 255;
// Slower?
/*
canvasImageData.data[idxC + 0] = textureImageData.data[idxT + 0];
canvasImageData.data[idxC + 1] = textureImageData.data[idxT + 1];
canvasImageData.data[idxC + 2] = textureImageData.data[idxT + 2];
canvasImageData.data[idxC + 3] = 255;
*/
// Faster?
/* copyFnc(idxC,idxT); */
}
}
gCtx.putImageData(canvasImageData, 0, 0);
}};
};
function copyImageToBuffer(aImg)
{
gImage = document.createElement('canvas');
textureWidth = aImg.naturalWidth;
textureHeight = aImg.naturalHeight;
gImage.width = textureWidth;
gImage.height = textureHeight;
gCtxImg = gImage.getContext("2d");
gCtxImg.clearRect(0, 0, textureHeight, textureWidth);
gCtxImg.drawImage(aImg, 0, 0);
textureImageData = gCtxImg.getImageData(0, 0, textureHeight, textureWidth);
hs_ch = (hs / size);
vs_cv = (vs / size);
}
this.createSphere = function (gCanvas, textureUrl, callback, tilingInfos) {
size = Math.min(gCanvas.width, gCanvas.height);
gCtx = gCanvas.getContext("2d");
canvasImageData = gCtx.createImageData(size, size);
tiling = tilingInfos;
hs_ch = (hs / size);
vs_cv = (vs / size);
V[Y]=f;
b=(2*(-f*V[Y]));
b2=Math.pow(b,2);
var img = new Image();
img.onload = function() {
copyImageToBuffer(img);
var earth = sphere();
callback(earth, textureWidth, textureHeight);
// BAD! uses 100% CPU, stats.js runs at 38FPS
/*
function renderFrame(){
earth.renderFrame(new Date);
}
setInterval(renderFrame, 0);
*/
// Better - runs at steady state
/*
(function loop(){
setTimeout(function(){
earth.renderFrame(new Date);
loop();
}, 0);
})();
*/
// Best! only renders frames that will be seen. stats.js runs at 60FPS on my desktop
};
img.setAttribute("src", textureUrl);
};
}).call(this);

28
earth/welcome.html Normal file
View File

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="leaflet.css">
<style>
.clickable-area {
fill-opacity: 0.2; /* Adjust the opacity as needed */
fill: #0074d9; /* Adjust the color as needed */
stroke: #001f3f;
}
</style>
<script src="leaflet.js"></script>
<title>Welcome to Planet Earth</title>
<style>
#map {
width: 100%;
height: 100vh;
}
</style>
</head>
<body>
<div id="map"></div>
<script src="welcome.js"></script>
</body>
</html>

35
earth/welcome.js Normal file
View File

@ -0,0 +1,35 @@
// Set up initial variables
const tileSize = 10; // 10° interval
const gridSize = 36;
const initialZoom = 1; // Choose an initial zoom level
// Create the map
const map = L.map('map').setView([0, 0], initialZoom);
// Add OpenStreetMap tile layer
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'U Planet'
}).addTo(map);
// Create a clickable area for each grid cell
for (let latIndex = 0; latIndex < gridSize; latIndex++) {
for (let lonIndex = 0; lonIndex < gridSize; lonIndex++) {
const lat = -180 + latIndex * tileSize;
const lon = -180 + lonIndex * tileSize;
// Create a clickable rectangle
const rectangle = L.rectangle(
[[lat, lon], [lat + tileSize, lon + tileSize]],
{ color: 'transparent', weight: 1, className: 'clickable-area' } // Add CSS class
);
// Add a click event to the rectangle
rectangle.on('click', () => {
const url = `map_render.html?southWestLat=${lat}&southWestLon=${lon}`;
window.location.href = url;
});
// Add the rectangle to the map
rectangle.addTo(map);
}
}

227
earth/world.js Normal file
View File

@ -0,0 +1,227 @@
var examples = {};
//~ examples['simple'] = function() {
//~ $('#sphere').earth3d({
//~ dragElement: $('#locations') // where do we catch the mouse drag
//~ });
//~ };
//~ examples['simple_tilted'] = function() {
//~ $('#sphere').earth3d({
//~ dragElement: $('#locations'), // where do we catch the mouse drag
//~ sphere: { // rotation and size of the planet
//~ tilt: 40,
//~ turn: 20,
//~ r: 10
//~ }
//~ });
//~ };
examples['locations'] = function() {
/* defining locations to display.
Each position must have a key, an alpha and delta position (or x and y if you want to display a static location).
Any additional key can be reached via callbacks functions.
*/
var locations = {};
for (let latIndex = 0; latIndex < 36; latIndex++) {
for (let lonIndex = 0; lonIndex < 36; lonIndex++) {
const alpha = (90 - lonIndex * 10) * (Math.PI / 180);
const delta = (latIndex * 10) * (Math.PI / 180);
const southWestLat = 90 - latIndex * 10;
const southWestLon = lonIndex * 10;
const objKey = `obj_${latIndex}_${lonIndex}`;
const objName = `_${latIndex}_${lonIndex}_`;
const objLink = `map_render.html?southWestLat=${southWestLat}&southWestLon=${southWestLon}`;
locations[objKey] = {
alpha: alpha,
delta: delta,
name: objName,
link: objLink
};
}
}
//~ var locations = {
//~ obj1: {
//~ alpha: Math.PI / 4,
//~ delta: 0,
//~ name: '_usa_',
//~ link: 'https://oasis.astroport.com#usa'
//~ },
//~ obj2: {
//~ alpha: 1 * Math.PI / 4,
//~ delta: -2 * Math.PI / 4,
//~ name: '_africa_',
//~ link: 'https://oasis.astroport.com#africa'
//~ },
//~ obj3: {
//~ alpha: 2 * Math.PI / 4,
//~ delta: 0,
//~ name: '_hawai_',
//~ link: 'https://oasis.astroport.com#awai'
//~ },
//~ obj4: {
//~ alpha: 3 * Math.PI / 4,
//~ delta: 3 * Math.PI / 4,
//~ name: '_australia_',
//~ link: 'https://oasis.astroport.com#australia'
//~ },
//~ obj5: {
//~ alpha: 2.2 * Math.PI / 4,
//~ delta: -0.9 * Math.PI / 4,
//~ name: '_southamerica_',
//~ link: 'https://oasis.astroport.com#southamerica'
//~ },
//~ obj6: {
//~ alpha: 1.2 * Math.PI / 4,
//~ delta: -2 * Math.PI / 4,
//~ name: '_europe_',
//~ link: 'https://oasis.astroport.com#europe'
//~ }
//~ };
$('#sphere').earth3d({
locationsElement: $('#locations'),
dragElement: $('#locations'), // where do we catch the mouse drag
locations: locations
});
};
//~ examples['flights'] = function() {
//~ /* defining locations to display.
//~ Each position must have a key, an alpha and delta position (or x and y if you want to display a static location).
//~ Any additional key can be reached via callbacks functions.
//~ */
//~ var locations = {
//~ obj1: {
//~ alpha: Math.PI / 4,
//~ delta: 0,
//~ name: '_usa_',
//~ link: 'https://oasis.astroport.com#usa'
//~ },
//~ obj2: {
//~ alpha: 1 * Math.PI / 4,
//~ delta: -2 * Math.PI / 4,
//~ name: '_africa_',
//~ link: 'https://oasis.astroport.com#africa'
//~ },
//~ obj3: {
//~ alpha: 2 * Math.PI / 4,
//~ delta: 0,
//~ name: '_hawai_',
//~ link: 'https://oasis.astroport.com#awai'
//~ },
//~ obj4: {
//~ alpha: 3 * Math.PI / 4,
//~ delta: 3 * Math.PI / 4,
//~ name: '_australia_',
//~ link: 'https://oasis.astroport.com#australia'
//~ },
//~ obj5: {
//~ alpha: 2.2 * Math.PI / 4,
//~ delta: -0.9 * Math.PI / 4,
//~ name: '_southamerica_',
//~ link: 'https://oasis.astroport.com#southamerica'
//~ },
//~ obj6: {
//~ alpha: 1.2 * Math.PI / 4,
//~ delta: -2 * Math.PI / 4,
//~ name: '_europe_',
//~ link: 'https://oasis.astroport.com#europe'
//~ },
//~ zero: {
//~ alpha: 0 * Math.PI / 4,
//~ delta: 0 * Math.PI / 4,
//~ name: '_CraftYourWorld_',
//~ link: 'https://ipfs.copylaradio.com/ipfs/QmNcNcYRDUFmR1Ey1MAyhzzZRJEi1Dfq8YXRTXq6XZ9n4A'
//~ },
//~ pi: {
//~ alpha: -3 * Math.PI / 4,
//~ delta: -3 * Math.PI / 4,
//~ name: '_OpenTW_',
//~ link: 'https://astroport.copylaradio.com'
//~ }
//~ };
//~ /* defining paths to display.
//~ Each path must have a key, an origin and a destination. The values are the location's key.
//~ You can, if you want to, define flights on these paths.
//~ Each flight has a key, a destination (the location's key) and a position.
//~ The position is the progress a fleet has made on its path.
//~ Any additional key can be reach via callbacks functions.
//~ */
//~ var paths = {
//~ path: {
//~ origin: 'obj1',
//~ destination: 'obj2',
//~ flights: {
//~ flight: {
//~ position: 0.25,
//~ destination: 'obj2',
//~ name: 'Flight 1'
//~ },
//~ flight2: {
//~ position: 0.25,
//~ destination: 'obj1',
//~ name: 'Flight 2'
//~ }
//~ }
//~ },
//~ path2: {
//~ origin: 'obj1',
//~ destination: 'obj3',
//~ flights: {
//~ flight3: {
//~ position: 0.5,
//~ destination: 'obj3',
//~ name: 'Flight 3'
//~ }
//~ }
//~ },
//~ path3: {
//~ origin: 'obj1',
//~ destination: 'obj4',
//~ flights: {
//~ flight4: {
//~ position: 0.5,
//~ destination: 'obj4',
//~ name: 'Flight 4'
//~ }
//~ }
//~ },
//~ path4: {
//~ origin: 'obj1',
//~ destination: 'obj5'
//~ },
//~ path7: {
//~ origin: 'obj1',
//~ destination: 'obj5',
//~ flights: {
//~ flight5: {
//~ position: 0.25,
//~ destination: 'obj7',
//~ name: 'Flight 5'
//~ }
//~ }
//~ }
//~ }
$('#sphere').earth3d({
dragElement: $('#locations'), // where do we catch the mouse drag
sphere: { // rotation and size of the planet
tilt: 40,
turn: 20,
r: 100
}
});
//~ $('#sphere').earth3d({
//~ flightsCanvas: $('#flights'),
//~ locationsElement: $('#locations'),
//~ dragElement: $('#locations'), // where do we catch the mouse drag
//~ paths: paths,
//~ locations: locations
//~ });
//~ };

41
export-map-puppeteer.js Normal file
View File

@ -0,0 +1,41 @@
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
headless: true,
executablePath: '/usr/bin/chromium'
});
const page = await browser.newPage();
const zoomLevel = 13;
const tilesAroundCenter = 2; // Adjust as needed
const tileSize = 360 / Math.pow(2, zoomLevel);
const tileCount = (2 * tilesAroundCenter) + 1;
const tileIncrement = tileSize * tileCount;
const centerLat = 51.000;
const centerLon = 0.000;
for (let latOffset = -tilesAroundCenter; latOffset <= tilesAroundCenter; latOffset++) {
for (let lonOffset = -tilesAroundCenter; lonOffset <= tilesAroundCenter; lonOffset++) {
const tileLat = centerLat + latOffset * tileIncrement;
const tileLon = centerLon + lonOffset * tileIncrement;
//~ http://ipfs.asycn.io/ipfs/QmZzE8ypb2q9CF93B8gbjif3Qpqhsrh8y5DtgPPwGYTpdP/map_render.html?southWestLat=${tileLat}&southWestLon=${tileLon}&deg=1
const url = `file:///tmp/osm/index.html?lat=${tileLat}&lon=${tileLon}&zoom=${zoomLevel}`;
await page.goto(url);
await page.waitForTimeout(2000);
const fileName = `map_${tileLat.toFixed(6)}_${tileLon.toFixed(6)}.png`;
await page.screenshot({ path: fileName });
}
}
await browser.close();
})();

92
map_render.html Normal file
View File

@ -0,0 +1,92 @@
<!DOCTYPE html>
<html>
<head>
<title>Leaflet Map</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
</head>
<body>
<div id="map" style="width: 800px; height: 600px;"></div>
<br>
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
<script>
// const desiredImageWidthInKm = 11; // Now Is calculated from northEastLon - southWestLon
const tileSizeInPixels = 256;
function calculateZoomLevel(desiredImageWidthInKm, latitude) {
const earthEquatorialCircumference = 40075016.686; // Earth's equatorial circumference in meters
// Calculate the distance covered by one degree of longitude at the given latitude
const metersPerLongitudeDegree = earthEquatorialCircumference * Math.cos((Math.PI / 180) * latitude) / 360;
const metersPerPixel = metersPerLongitudeDegree * 360 / (tileSizeInPixels * Math.pow(2, 20));
// Calculate the number of pixels needed to achieve the desired width
const numberOfPixelsHorizontally = (desiredImageWidthInKm * 1000) / metersPerPixel;
// Calculate the appropriate zoom level
const zoomLevel = Math.log2(tileSizeInPixels * Math.pow(2, 20) / numberOfPixelsHorizontally);
return zoomLevel;
}
// Function to extract URL parameters
function getUrlParameter(name) {
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
const regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
const results = regex.exec(location.search);
return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
}
const defaultSouthWestLat = 51.000;
const defaultSouthWestLon = -1.000;
const defaultNorthEastLat = 52.000;
const defaultNorthEastLon = 1.000;
const southWestLat = parseFloat(getUrlParameter('southWestLat')) || defaultSouthWestLat;
const southWestLon = parseFloat(getUrlParameter('southWestLon')) || defaultSouthWestLon;
const northEastLat = parseFloat(getUrlParameter('northEastLat')) || defaultNorthEastLat;
const northEastLon = parseFloat(getUrlParameter('northEastLon')) || defaultNorthEastLon;
// Calculate the longitudinal distance in degrees
const lonDistanceInDegrees = Math.abs(northEastLon - southWestLon);
// Calculate the desired image width in kilometers
const earthEquatorialCircumference = 40075016.686; // Earth's equatorial circumference in meters
const metersPerLongitudeDegree = earthEquatorialCircumference * Math.cos((Math.PI / 180) * southWestLat) / 360;
const desiredImageWidthInMeters = lonDistanceInDegrees * metersPerLongitudeDegree;
const desiredImageWidthInKm = desiredImageWidthInMeters / 1000;
console.log('desiredImageWidthInKm:', desiredImageWidthInKm);
const centerLat = (southWestLat + northEastLat) / 2;
const centerLon = (southWestLon + northEastLon) / 2;
// Provide the latitude for adjustment
const latitude = centerLat;
const zoomLevel = calculateZoomLevel(desiredImageWidthInKm, latitude);
console.log('Recommended zoom level:', zoomLevel);
const map = L.map('map').setView([centerLat, centerLon], zoomLevel);
// Use the Mapbox Satellite tile layer
//~ L.tileLayer('https://api.mapbox.com/styles/v1/mapbox/satellite-v9/tiles/{z}/{x}/{y}?access_token=YOUR_MAPBOX_ACCESS_TOKEN', {
//~ attribution: '© Mapbox',
//~ tileSize: 512,
//~ zoomOffset: -1,
//~ accessToken: 'YOUR_MAPBOX_ACCESS_TOKEN'
//~ }).addTo(map);
//~ // Use the OpenAerialMap (OAM) tile layer for satellite view
//~ L.tileLayer('https://tiles.openaerialmap.org/5d6d1e370f437a00106e06ef/0/{z}/{x}/{y}.jpg', {
//~ attribution: '© OpenAerialMap',
//~ maxZoom: 18
//~ }).addTo(map);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
</script>
</body>
</html>