opengeoguess

small client-side geoguessr clone

by John Doe

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/leaflet.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/leaflet.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/Turf.js/5.1.6/turf.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
<!DOCTYPE html>
<html>
  <head>
    <title>OpenGeoGuess</title>
    <script
      src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg&callback=initPano&libraries=&v=weekly"
      defer
    ></script>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
  </head>
  <body>
    <div id="wrapper">
      <div id="spinner" >
        <div class="lds-dual-ring"></div>
      </div>
      <div id="controls">
        <div class="btn" id="next">New location</div>
      </div>
      <div id="pano"></div>
      <div id="map-overlay-wrap">
        <div class="btn" id="guess">Guess Location</div>
        <div id="map"></div>
      </div>
    </div>
  </body>
</html>

CSS

/* globals */
html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}

/* game window */
#wrapper {
  height: 100%;
  margin: 0;
  padding: 0;
}
/* google street view */
#pano {
  height: 100%;
  z-index: 1000;
  background-color: black;
}


/* ***************** */
/* map related stuff */
/* ***************** */
#map-overlay-wrap {
  cursor: pointer;
  position: absolute;
  bottom: 2rem;
  right: 3rem;
  opacity: 1;
  transform: scale(.8);
  transform-origin: bottom right;
  visibility: visible;
  transition: all 1s;
  z-index: 1001;
}
#map-overlay-wrap:hover {
  transform: scale(1);
}
#map {
  position: relative;
  overflow: hidden;
  height: 300px;
  width: 420px;
}


/* ******** */
/* Controls */
/* ******** */
#controls {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 1001;
}
.btn {
  color: #fff;
  display: inline-block;
  font-weight: 700;
  letter-spacing: .025em;
  width: 100%;
  text-align: center;
  border-radius: .5rem;
  margin-bottom: .75rem;
}
#next {
  background-color: #2196f3;
}
#guess {
  background-color: #66cc00;
}


/* ********************* */
/* spinner loader thingy */
/* ********************* */
#spinner {
  display: none;
  background-color: #000000aa;
  position: absolute;
  height: 100%;
  width: 100%;
  z-index: 1010;
}
.lds-dual-ring {
  display: inline-block;
  width: 80px;
  height: 80px;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%,-50%);
}
.lds-dual-ring:after {
  content: " ";
  display: block;
  width: 64px;
  height: 64px;
  margin: 8px;
  border-radius: 50%;
  border: 6px solid #fff;
  border-color: #fff transparent #fff transparent;
  animation: lds-dual-ring 1.2s linear infinite;
}
@keyframes lds-dual-ring {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

JavaScript

/*
TODO:
 - better random coordinates
 - show distance difference
*/


/* spinner */
class Spinner {
	constructor() {
  	this.block = document.getElementById('spinner');
  }
  show() {
  	this.block.style.display = 'block';
  }
  hide() {
  	this.block.style.display = 'none';
  }
}


/* control buttons */
class Controls {
	constructor() {
    this.nextButton = document.getElementById('next');
    this.guessButton = document.getElementById('guess');
  }
  setEvents(props) {
  	this.onNext = props.onNext;
    this.onGuess = props.onGuess;
    this.nextButton.addEventListener('click', () => {
    	this.onNext();
    })
    this.guessButton.addEventListener('click', () => {
    	this.onGuess();
    })
  }
}


/* leaflet map for guessing*/
class GuessMap {
	constructor() {
    // create a slippy map and show the basemap
    this.mapDOM = document.getElementById('map');
  	this.map = new L.map(this.mapDOM).setView([0, 0], 2)
  	this.basemap = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(this.map);
    
    // default functions
    this.addMarker();
  }
  
  addMarker() {
    // add user marker
    this.userMarker = L.marker(this.map.getCenter(), {draggable: true});
		this.userMarker.addTo(this.map);
    
    // move marker on map click
    this.map.on('click', (e) => {
    	this.userMarker.setLatLng([e.latlng.lat, e.latlng.lng]);
    });
  }
  
  showRealPlace(coordinates) {
  	this.resetElements();
    // show real place
    this.realMarker = L.circleMarker(coordinates, {
      "radius": 5,
      "fillColor": "#03a9f4",
      "fillOpacity": 0.8,
      "color": "#03a9f4",
      "weight": 1,
      "opacity": 1
    }).addTo(this.map);

    // show polyline between your guess and a real place
    let polylineCoords = [this.realMarker.getLatLng(), this.userMarker.getLatLng()];
    this.polyline = L.polyline(polylineCoords, {color: 'red'}).addTo(this.map);
    this.map.fitBounds(this.polyline.getBounds());
  }
  
  resetElements() {
    // remove...