~genbyte/gensite

5fceb6226e706c17c18947a0da4611264207c103 — Brad Alfirevic 18 days ago 31f45a1 master
Spotts MVP
4 files changed, 283 insertions(+), 1 deletions(-)

M geohash/index.html
A spotts/geohash.js
A spotts/index.html
A spotts/spotts.js
M geohash/index.html => geohash/index.html +1 -1
@@ 110,7 110,7 @@
				</div>
				<div class="card long-card">
					<div id="direction">&larr;</div>
				</div>--!>
				</div>-->
			</section>
		</main>
		</article>

A spotts/geohash.js => spotts/geohash.js +135 -0
@@ 0,0 1,135 @@
class LocationProvider {
	constructor() {
		this.setHighAccuracy(false);
		this.setTimeout(1000);

		this.location = new Vec2(0, 0);
	}

	setSuccessCallback(fn) {
		this.successCallback = fn;
	}

	setErrorCallback(fn) {
		this.errorCallback = fn;
	}

	setTickCallback(fn) {
		this.tickCallback = fn;
	}

	setHighAccuracy(tf) {
		this.isHighAccuracy = tf;
	}

	setTimeout(num) {
		this.timeout = num;
	}

	internalSuccess(pos) {
		this.location = new Vec2(
			pos.coords.latitude,
			pos.coords.longitude
		);
		this.age = 0;
		this.successCallback(this);
		this.tickCallback(this);
	}

	internalError(err) {
		if (err.code == err.TIMEOUT) {
			this.age += this.timeout/1000;
			this.tickCallback(this);
		} else {
			this.errorCallback(this);
		}
	}

	get() {
		if (this.watcher) {
			return false;
		}

		navigator.geolocation.getCurrentPosition(
			this.internalSuccess.bind(this),
			this.internalError.bind(this),
			{"enableHighAccuracy": this.isHighAccuracy, "timeout": this.timeout}
		);
	}

	watch() {
		this.watcher = navigator.geolocation.watchPosition(
			this.internalSuccess.bind(this),
			this.internalError.bind(this),
			{"enableHighAccuracy": this.isHighAccuracy, "timeout": this.timeout}
		);
	}

	stop() {
		this.age = -1;
		navigator.geolocation.clearWatch(this.watcher);
	}

	distanceKm(vec) {
		return this.getDistanceFromLatLonInKm(this.location.x, this.location.y, vec.x, vec.y);
	}

	distanceMi(vec) {
		return this.distanceKm(vec) * 0.6213712;
	}

	// https://stackoverflow.com/a/27943/10354782
	getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2) {
		var R = 6371; // Radius of the earth in km
		var dLat = this.deg2rad(lat2-lat1);  // deg2rad below
		var dLon = this.deg2rad(lon2-lon1);
		var a =
			Math.sin(dLat/2) * Math.sin(dLat/2) +
 			Math.cos(this.deg2rad(lat1)) * Math.cos(this.deg2rad(lat2)) *
			Math.sin(dLon/2) * Math.sin(dLon/2);

		var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
		var d = R * c; // Distance in km
		return d;
	}

	deg2rad(deg) {
		return deg * (Math.PI/180);
	}
}

// https://stackoverflow.com/a/9232092/10354782
Math.truncateDecimals = function(num, digits) {
	var numS = num.toString(),
		decPos = numS.indexOf('.'),
		substrLength = decPos == -1 ? numS.length : 1 + decPos + digits,
		trimmedResult = numS.substr(0, substrLength),
		finalResult = isNaN(trimmedResult) ? 0 : trimmedResult;

	return parseFloat(finalResult);
}

Math.absAdd = function(whole, frac) {
	return (Math.abs(whole) + Math.abs(frac)) * Math.sign(whole) * Math.sign(frac);
}

class Vec2 {
	constructor(x, y) {
		this.x = x;
		this.y = y;
	}

	truncateDecimals(n) {
		return new Vec2(
			Math.truncateDecimals(this.x, n),
			Math.truncateDecimals(this.y, n)
		);
	}

	add(x, y) {
		return new Vec2(
			this.x+x,
			this.y+y
		);
	}
}
\ No newline at end of file

A spotts/index.html => spotts/index.html +50 -0
@@ 0,0 1,50 @@
<html>
	<head>
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
		<title>Spotts</title>
		<link rel="icon" type="image/svg+xml" href="/vertex.svg">
		<link rel="icon" type="image/png" href="/vertex.png">
		<link rel="stylesheet" href="/styles/master.css">
		<link rel="stylesheet" href="/styles/single.css">
		<link rel="stylesheet" href="/styles/cards.css">
		<link rel="stylesheet" href="style.css">
		<script src="geohash.js"></script>
		<script src="spotts.js" defer></script>
		<style>
			#coords {
				display: grid;
				grid-template-columns: 1fr;
				text-align: left;
			}

			input {
				width: 100%;
			}

			input[type=text] {
				border-width: 1px;
			}
		</style>
	</head>
	<body>
		<article>
			<header>
				<h1>Spotts</h1>
				<h2 style="float: right"><a href="/">genbyte.dev</a></h2>
				<h2>Save GPS Locations</h2>
			</header>
			<main>
			<section class="grid">
				<div class="card long-card">
					<input type="text" id="name" placehodler="name">
					<input type="button" value="Save Point" onclick="spotts.saveAction()">
					<input type="button" id="clear" value="Clear Spotts" onclick="spotts.clearAction()">
				</div>
				<section class="card long-card" id="coords">
				</section>
			</section>
		</main>
		</article>
	</body>
</html>

A spotts/spotts.js => spotts/spotts.js +97 -0
@@ 0,0 1,97 @@
class Spotts {
	constructor() {
		this.provider = new LocationProvider();
		if (!this.provider) {
			//TODO: Fail better
			alert("Failed to find LocationProvider!");
			return;
		}
		this.provider.setSuccessCallback(this.locationCallback.bind(this));
		this.provider.setErrorCallback(this.errorCallback.bind(this));
		this.provider.setTickCallback(function(){});

		this.ui = {
			name: document.getElementById("name"),
			coords: document.getElementById("coords"),
			clear: document.getElementById("clear")
		};
		this.clearDefText = this.ui.clear.value;
		this.clearDef = 4; 
		this.clearCur = 4;

		this.loadLocations();
	}

	saveAction() {
		console.log("Saving spot..");
		this.provider.get();
	}

	clearAction() {
		if (this.clearCur === 0) {
			localStorage.removeItem("spotts");
			this.loadLocations();
			this.clearCur = this.clearDef;
			this.ui.clear.value = this.clearDefText;
			alert("Spotts cleared!");
			return;
		}
		this.clearCur--;
		this.ui.clear.value = "Click " + (this.clearCur+1) + " more times";
	}

	locationCallback(provider) {
		let name = this.ui.name.value;
		if (name === "") {
			alert("The position must have a name");
			return;
		}

		let lat = provider.location.x;
		let lon = provider.location.y;

		let spott = {
			name: name,
			latitude: lat ? lat : 0,
			longitude: lon ? lon : 0,
			timestamp: Date.now()
		};

		let spotts = localStorage.getItem("spotts");
		if (spotts == undefined) {
			spotts = [];
		} else {
			spotts = JSON.parse(spotts);
		}
		console.log(spotts);
		spotts.push(spott);
		localStorage.setItem("spotts", JSON.stringify(spotts));
		this.ui.name.value = "";
		this.displaySpot(spott);
	}

	errorCallback(provider) {
		//alert("Error failed to save point!");
		this.locationCallback(this.provider);
	}

	loadLocations() {
		this.ui.coords.innerHTML = "";

		let spottsRaw = localStorage.getItem("spotts");
		if (spottsRaw) {
			let spotts = JSON.parse(spottsRaw);
			for (let i = 0; i < spotts.length; ++i) {
				this.displaySpot(spotts[i]);
			}
		}
	}

	displaySpot(spot) {
		let coord = document.createElement('div');
		coord.textContent = spot.name + ": " + spot.latitude + ", " + spot.longitude;
		this.ui.coords.appendChild(coord);
	}
}

window.spotts = new Spotts();
\ No newline at end of file