UNPKG

spash

Version:

Universal spacetime locator. Maps every location in the observable universe to a string.

295 lines (285 loc) 10.2 kB
<!doctype html><meta charset=utf-8><title> Spacetime Locator </title> <style> html { max-width: 12cm; margin: auto; } .centered { text-align: center; font-weight: bold; font-family: monospace; } .logo { position: relative; top: 5px; } .clickable { cursor: pointer; } #spashNode { font-size: 3em; } #googleMapNode { height: 400px; width: 100%; margin-top: 15px; display: none; } footer { font-size: 0.9em; } </style> <h1> Spash: Spacetime Locator </h1> <p class=centered> <a href='https://github.com/espadrine/spash'> <svg class=logo width="20" height="20" viewBox="12 12 40 40"> <path d="M32,13.4c-10.5,0-19,8.5-19,19c0,8.4,5.5,15.5,13,18c1,0.2,1.3-0.4,1.3-0.9c0-0.5,0-1.7,0-3.2 c-5.3,1.1-6.4-2.6-6.4-2.6C20,41.6,18.8,41,18.8,41c-1.7-1.2,0.1-1.1,0.1-1.1c1.9,0.1,2.9,2,2.9,2c1.7,2.9,4.5,2.1,5.5,1.6 c0.2-1.2,0.7-2.1,1.2-2.6c-4.2-0.5-8.7-2.1-8.7-9.4c0-2.1,0.7-3.7,2-5.1c-0.2-0.5-0.8-2.4,0.2-5c0,0,1.6-0.5,5.2,2 c1.5-0.4,3.1-0.7,4.8-0.7c1.6,0,3.3,0.2,4.7,0.7c3.6-2.4,5.2-2,5.2-2c1,2.6,0.4,4.6,0.2,5c1.2,1.3,2,3,2,5.1c0,7.3-4.5,8.9-8.7,9.4 c0.7,0.6,1.3,1.7,1.3,3.5c0,2.6,0,4.6,0,5.2c0,0.5,0.4,1.1,1.3,0.9c7.5-2.6,13-9.7,13-18.1C51,21.9,42.5,13.4,32,13.4z"/> </svg></a> <code class=selectable>npm install spash</code> </p> <p> Every day, humanity goes more vertical. Petty constraints like gravity are defied by <em>planes, kilometric skyscrapers, ambitious space programs</em>. <a href="http://iss.astroviewer.net">The ISS</a> peacefully maintains our orbital presence on the planet, and Elon Musk is already planning <a href="https://en.wikipedia.org/wiki/Interplanetary_Transport_System"> a Martian settlement</a>. </p> <p> Your place in the universe gets more important by the second. Here is its identifier: </p> <section class=centered> <output id=spashNode>E.ek0uvhuQQ1q</output><br> <input type=button id=addDigitNode value='+'> <input type=button id=rmDigitNode value='-'> <input type=button id=editNode value='Edit'> <label><input type=checkbox id=showTimeNode> Show time</label> <label><input type=checkbox id=showMapNode> Show map</label> </section> <div id=googleMapNode></div> <p id=geolocNode></p> <p id=precisionNode></p> <p id=noGeolocNode> Your device does not allow access to geolocation data. Using a default location. </p> <p id=warningsNode></p> <footer> <p> Full <a href=https://github.com/espadrine/spash/blob/master/Readme.md> specification</a>. Inspired by <a href=https://en.wikipedia.org/wiki/Geohash>Geohash</a>. Licensed <a href=https://creativecommons.org/publicdomain/zero/1.0/>CC0</a>. Built by <a href=https://twitter.com/espadrine>Thaddée Tyl</a>. </p> </footer> <script src=./spash.js></script> <script> var geoloc = { coords: { latitude: 51.5221656, longitude: -0.1086239, altitude: 22.725, accuracy: 2, altitudeAccuracy: 1e-9, }, timestamp: Date.now(), }; var geoSpash; var digits = 11; var setGeoloc = function(geo) { geoloc = geo; geoSpash = spash.encode(geo, digits, 6); var hash = geoSpash.iau + '.' + geoSpash.geolocHash; if (showTimeHash) { hash += '.' + geoSpash.timeHash; } spashNode.value = hash; location.hash = hash; showGeoloc(); accuracyWarnings(geoSpash, geo); }; var gotGeoloc = function(geo) { noGeolocNode.style.display = 'none'; setGeoloc(geo); }; var noGeolocWarning = function() { noGeolocNode.style.display = 'block'; setGeoloc(geoloc); }; if (!location.hash) { navigator.geolocation.getCurrentPosition(gotGeoloc, noGeolocWarning, { enableHighAccuracy: true, timeout: 25000, maximumAge: 0 }); } else { // The address bar already holds a hash. addEventListener('DOMContentLoaded', function() { updateSpash(location.hash); }); } var mapLink = function(geoloc, digits) { return "https://www.google.fr/maps/@" + geoloc.coords.latitude + "," + geoloc.coords.longitude + "," + getZoomFromDigits(digits) + "z"; }; var showGeoloc = function() { var values = [ '<a href="' + mapLink(geoloc, digits) + '">Map</a>', '<span class="clickable" onclick="changeLatitude(event)">' + 'Latitude</span>: ' + geoloc.coords.latitude + '°' + '</span>', '<span class="clickable" onclick="changeLongitude(event)">' + 'Longitude</span>: ' + geoloc.coords.longitude + '°' + '</span>', '<span class="clickable" onclick="changeAltitude(event)">' + 'Altitude</span>: ' + geoloc.coords.altitude + ' metres' + '</span>', ]; if (showTimeHash) { values.push('<span class="clickable" onclick="changeTime(event)">' + 'Time</span>: ' + new Date(geoloc.timestamp).toISOString()); } geolocNode.innerHTML = values.join('<br>'); }; var accuracyWarnings = function(geoSpash, geo) { var accuracies = [ '± ' + geoSpash.earthLatError + ' metres (latitude)', '± ' + geoSpash.earthLongError + ' metres (longitude)', '± ' + geoSpash.altError + ' metres (altitude)', ]; if (showTimeHash) { accuracies.push('± ' + geoSpash.timeError + ' seconds (time)'); } precisionNode.innerHTML = accuracies.join('<br>'); var geoAccuracy = geo.coords.accuracy || Infinity; // meters var altAccuracy = (geo.coords.altitudeAccuracy) || Infinity; var warn = []; if (geoAccuracy > geoSpash.earthLongError || geoAccuracy > geoSpash.earthLatError) { warn.push('Geographic coordinates are not accurate enough for this level of precision.'); } if (geo.coords.altitudeAccuracy === null) { warn.push('Altitude measurement unavailable; using sea level.'); } else if (altAccuracy > geoSpash.altError) { warn.push('Altitude measurement is not accurate enough for this level of precision.'); } warningsNode.innerHTML = warn.join('<br>'); }; var addDigit = function(event) { digits++; setGeoloc(geoloc); setMapCenter(geoloc); }; var rmDigit = function(event) { if (digits <= 0) { return; } digits--; setGeoloc(geoloc); setMapCenter(geoloc); }; var changeLatitude = function(event) { var newLat = prompt('Enter a new latitude.'); if (newLat === null) {return;} newLat = +newLat; if (newLat !== newLat) {return;} geoloc.coords.latitude = newLat; setGeoloc(geoloc); setMapCenter(geoloc); }; var changeLongitude = function(event) { var newLong = prompt('Enter a new longitude.'); if (newLong === null) {return;} newLong = +newLong; if (newLong !== newLong) {return;} geoloc.coords.longitude = newLong; setGeoloc(geoloc); setMapCenter(geoloc); }; var changeAltitude = function(event) { var newAlt = prompt('Enter a new altitude, in metres.'); if (newAlt === null) {return;} newAlt = +newAlt; if (newAlt !== newAlt) {return;} geoloc.coords.altitude = newAlt; setGeoloc(geoloc); setMapCenter(geoloc); }; var changeTime = function(event) { var newTime = prompt('Enter a new time, in ISO 8601.'); if (newTime === null) {return;} newTime = +new Date(newTime); if (+newTime !== newTime) {return;} geoloc.timestamp = newTime; setGeoloc(geoloc); setMapCenter(geoloc); }; var updateSpash = function(newSpash) { try { var newGeo = spash.decode(newSpash); } catch (e) { alert('Invalid Spash'); return; } var parts = newSpash.split('.'); digits = parts[1].length; setShowTimeHash(!!parts[2]); setGeoloc(newGeo); setMapCenter(geoloc); }; var edit = function(event) { var newSpash = prompt('Enter a spash.'); updateSpash(newSpash); }; var showTimeHash = false; var setShowTimeHash = function(show) { showTimeHash = show; showTimeNode.checked = show; setGeoloc(geoloc); }; var showTimeChanged = function(event) { setShowTimeHash(event.target.checked); }; var showMap = false; var showMapChanged = function(event) { showMap = event.target.checked; if (showMap) { loadMapLibrary(); googleMapNode.style.display = 'block'; } else { googleMapNode.style.display = 'none'; } }; var hashChanged = function(event) { updateSpash(location.hash); }; addDigitNode.addEventListener('click', addDigit); rmDigitNode.addEventListener('click', rmDigit); editNode.addEventListener('click', edit); showTimeNode.addEventListener('change', showTimeChanged); showMapNode.addEventListener('change', showMapChanged); addEventListener('hashchange', hashChanged); // Google Map var googleMap, googleMapMarker; var getZoomFromDigits = function(digits) { return Math.floor(digits * 19 / 11); }; var setMapCenter = function(geoloc) { if (!googleMap) { return; } var latLng = { lat: geoloc.coords.latitude, lng: geoloc.coords.longitude, }; googleMap.setCenter(latLng); var zoom = getZoomFromDigits(digits); googleMap.setZoom(zoom); googleMapMarker.setPosition(latLng); }; var geolocAtFetchedElevation = geoloc; var setSpashFromMapCenter = function() { if (!googleMap) { return; } var center = googleMap.getCenter(); var latLng = {lat: center.lat(), lng: center.lng()}; geoloc.coords.latitude = latLng.lat; geoloc.coords.longitude = latLng.lng; setGeoloc(geoloc); setGeoloc(spash.decode(geoSpash.hash)); latLng = { lat: geoloc.coords.latitude, lng: geoloc.coords.longitude, }; googleMapMarker.setPosition(latLng); }; var initMap = function() { googleMap = new google.maps.Map(googleMapNode); googleMapMarker = new google.maps.Marker({map: googleMap}); googleMap.addListener('center_changed', setSpashFromMapCenter); setMapCenter(geoloc); } var mapLibraryLoaded = false; var loadMapLibrary = function() { if (mapLibraryLoaded) { return; } mapLibraryLoaded = true; var script = document.createElement('script'); script.async = true; script.defer = true; script.src = "https://maps.googleapis.com/maps/api/js?key=AIzaSyB7tgHIP7-P8j_nV1W1ahUdXcXwB1fK18c&callback=initMap"; document.body.appendChild(script); }; </script>