From e9f4de99e7f6cbc4ba74a77a0f6d76e9d89c3eda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Mon, 25 May 2020 18:51:15 +0200 Subject: [PATCH 1/9] MAPG-47 add php-curl to Dockerfile --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 9e02cc7..f75e34b 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -5,7 +5,7 @@ ENV DEBIAN_FRONTEND noninteractive # Install Apache, PHP and further necessary packages RUN apt update RUN apt install -y curl git mariadb-client apache2 \ - php-apcu php-xdebug php7.4-cli php7.4-fpm php7.4-mbstring php7.4-mysql php7.4-zip + php-apcu php-xdebug php7.4-cli php7.4-curl php7.4-fpm php7.4-mbstring php7.4-mysql php7.4-zip # Configure Apache with PHP RUN mkdir -p /run/php From 41e8564872bf5153d3291d778c2d45a4482a77e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Mon, 25 May 2020 18:51:44 +0200 Subject: [PATCH 2/9] MAPG-47 install romanpitak/php-rest-client --- composer.json | 5 +++-- composer.lock | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 8010c24..6325f0a 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,8 @@ "description": "MapGuesser Application", "license": "GNU GPL 3.0", "require": { - "vlucas/phpdotenv": "^4.1" + "vlucas/phpdotenv": "^4.1", + "romanpitak/php-rest-client": "^1.2" }, "require-dev": {}, "autoload": { @@ -17,4 +18,4 @@ "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" ] } -} \ No newline at end of file +} diff --git a/composer.lock b/composer.lock index 4bbcd4f..fc6069d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "29431cf83ee884f01ee954b1068c0ccc", + "content-hash": "3778b9431ef3d22705bdbf1653102a1a", "packages": [ { "name": "phpoption/phpoption", @@ -61,6 +61,51 @@ ], "time": "2020-03-21T18:07:53+00:00" }, + { + "name": "romanpitak/php-rest-client", + "version": "v1.2.1", + "source": { + "type": "git", + "url": "https://github.com/romanpitak/PHP-REST-Client.git", + "reference": "728b6c44040a13daeb8033953f5f3cdd3dde1980" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/romanpitak/PHP-REST-Client/zipball/728b6c44040a13daeb8033953f5f3cdd3dde1980", + "reference": "728b6c44040a13daeb8033953f5f3cdd3dde1980", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "RestClient\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Piták", + "email": "roman@pitak.net", + "homepage": "http://pitak.net", + "role": "Developer" + } + ], + "description": "REST client library.", + "homepage": "https://github.com/romanpitak/PHP-REST-Client", + "keywords": [ + "client", + "http", + "rest" + ], + "time": "2015-02-19T15:32:16+00:00" + }, { "name": "symfony/polyfill-ctype", "version": "v1.17.0", From 2d22d14817ce9e5e8c8d13205c998616dc8a7d0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Mon, 25 May 2020 18:52:31 +0200 Subject: [PATCH 3/9] MAPG-47 make possible to pass GET parameters to URL --- public/index.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/public/index.php b/public/index.php index ee4c162..8be460c 100644 --- a/public/index.php +++ b/public/index.php @@ -5,6 +5,9 @@ require '../main.php'; // very basic routing $host = $_SERVER["REQUEST_SCHEME"] . '://' . $_SERVER["SERVER_NAME"]; $url = $_SERVER['REQUEST_URI']; +if (($pos = strpos($url, '?')) !== false) { + $url = substr($url, 0, $pos); +} switch($url) { case '/game': $controller = new MapGuesser\Controller\GameController(); From e56361274cd8cbf1608cc0aeea43a347fcbd7cda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Mon, 25 May 2020 18:52:42 +0200 Subject: [PATCH 4/9] MAPG-47 start server-side session --- main.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/main.php b/main.php index b8c37f0..b2468f9 100644 --- a/main.php +++ b/main.php @@ -14,3 +14,5 @@ if (!empty($_ENV['DEV'])) { } else { ini_set('display_errors', '0'); } + +session_start(); From 55d5cad40a96984472b3d39266b506f5b4c4975c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Mon, 25 May 2020 18:53:01 +0200 Subject: [PATCH 5/9] MAPG-47 add GOOGLE_MAPS_SERVER_API_KEY to .env.example --- .env.example | 1 + 1 file changed, 1 insertion(+) diff --git a/.env.example b/.env.example index 097e309..6d5a0de 100644 --- a/.env.example +++ b/.env.example @@ -3,4 +3,5 @@ DB_HOST=mariadb DB_USER=mapguesser DB_PASSWORD=mapguesser DB_NAME=mapguesser +GOOGLE_MAPS_SERVER_API_KEY=your_google_maps_server_api_key GOOGLE_MAPS_JS_API_KEY=your_google_maps_js_api_key From 8eb3dcf2e203f54f1311ce4d04b294979dd353ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Mon, 25 May 2020 18:54:37 +0200 Subject: [PATCH 6/9] MAPG-47 add toArray() method to Bounds --- src/Util/Geo/Bounds.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Util/Geo/Bounds.php b/src/Util/Geo/Bounds.php index c77d2b1..7b448be 100644 --- a/src/Util/Geo/Bounds.php +++ b/src/Util/Geo/Bounds.php @@ -75,18 +75,23 @@ class Bounds return $m * ($a + $c) / 2; } - public function toJson(): string + public function toArray(): array { if (!$this->initialized) { throw new \Exception("Bounds are not initialized!"); } - return json_encode([ + return [ 'south' => $this->southLat, 'west' => $this->westLng, 'north' => $this->northLat, 'east' => $this->eastLng, - ]); + ]; + } + + public function toJson(): string + { + return json_encode($this->toArray()); } private function initialize(Position $position) From 567238c9796f547559f01c12ef01ace31046b80b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Mon, 25 May 2020 19:14:49 +0200 Subject: [PATCH 7/9] MAPG-47 refactor GameController and its view --- src/Controller/GameController.php | 35 ++++++++++++++++++++++++------- views/game.php | 3 +-- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/Controller/GameController.php b/src/Controller/GameController.php index 4e74cd0..43b04e0 100644 --- a/src/Controller/GameController.php +++ b/src/Controller/GameController.php @@ -1,28 +1,47 @@ mysql = new mysqli($_ENV['DB_HOST'], $_ENV['DB_USER'], $_ENV['DB_PASSWORD'], $_ENV['DB_NAME']); + } + public function run(): ViewBase { - $mysql = new mysqli($_ENV['DB_HOST'], $_ENV['DB_USER'], $_ENV['DB_PASSWORD'], $_ENV['DB_NAME']); + $bounds = $this->getMapBounds(); - // demo map - $mapId = 1; + if (!isset($_SESSION['state']) || $_SESSION['state']['mapId'] !== $this->mapId) { + $_SESSION['state'] = [ + 'mapId' => $this->mapId, + 'area' => $bounds->calculateApproximateArea(), + 'rounds' => [] + ]; + } - $stmt = $mysql->prepare('SELECT bound_south_lat, bound_west_lng, bound_north_lat, bound_east_lng FROM maps WHERE id=?'); - $stmt->bind_param("i", $mapId); + $data = ['bounds' => $bounds->toArray()]; + return new HtmlView('game', $data); + } + + private function getMapBounds(): Bounds + { + $stmt = $this->mysql->prepare('SELECT bound_south_lat, bound_west_lng, bound_north_lat, bound_east_lng FROM maps WHERE id=?'); + $stmt->bind_param("i", $this->mapId); $stmt->execute(); $map = $stmt->get_result()->fetch_assoc(); $bounds = Bounds::createDirectly($map['bound_south_lat'], $map['bound_west_lng'], $map['bound_north_lat'], $map['bound_east_lng']); - $data = compact('bounds'); - return new HtmlView('game', $data); + return $bounds; } } diff --git a/views/game.php b/views/game.php index 07a03e0..fee7284 100644 --- a/views/game.php +++ b/views/game.php @@ -48,8 +48,7 @@ From 72a79d42676b26a394a6400d0e84fb3caab97edd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Mon, 25 May 2020 19:46:31 +0200 Subject: [PATCH 8/9] MAPG-47 introduce PositionController which replaces GetNewPosition and also calculates results --- public/index.php | 4 +- src/Controller/GetNewPosition.php | 34 ----- src/Controller/PositionController.php | 180 ++++++++++++++++++++++++++ 3 files changed, 182 insertions(+), 36 deletions(-) delete mode 100644 src/Controller/GetNewPosition.php create mode 100644 src/Controller/PositionController.php diff --git a/public/index.php b/public/index.php index 8be460c..a20f4d7 100644 --- a/public/index.php +++ b/public/index.php @@ -12,8 +12,8 @@ switch($url) { case '/game': $controller = new MapGuesser\Controller\GameController(); break; - case '/getNewPosition.json': - $controller = new MapGuesser\Controller\GetNewPosition(); + case '/position.json': + $controller = new MapGuesser\Controller\PositionController(); break; case '/': header('Location: ' . $host . '/game', true, 302); diff --git a/src/Controller/GetNewPosition.php b/src/Controller/GetNewPosition.php deleted file mode 100644 index f0abf75..0000000 --- a/src/Controller/GetNewPosition.php +++ /dev/null @@ -1,34 +0,0 @@ -prepare('SELECT COUNT(*) AS num FROM places WHERE map_id=?'); - $stmt->bind_param("i", $mapId); - $stmt->execute(); - $numberOfPlaces = $stmt->get_result()->fetch_assoc()['num']; - - $randomOffset = random_int(0, $numberOfPlaces-1); - - $stmt = $mysql->prepare('SELECT lat, lng FROM places WHERE map_id=? ORDER BY id LIMIT 1 OFFSET ?'); - $stmt->bind_param("ii", $mapId, $randomOffset); - $stmt->execute(); - $place = $stmt->get_result()->fetch_assoc(); - - $position = new Position($place['lat'], $place['lng']); - - $data = ['position' => $position->toArray()]; - return new JsonView($data); - } -} diff --git a/src/Controller/PositionController.php b/src/Controller/PositionController.php new file mode 100644 index 0000000..900b1c0 --- /dev/null +++ b/src/Controller/PositionController.php @@ -0,0 +1,180 @@ +mysql = new mysqli($_ENV['DB_HOST'], $_ENV['DB_USER'], $_ENV['DB_PASSWORD'], $_ENV['DB_NAME']); + } + + public function run(): ViewBase + { + if (!isset($_SESSION['state']) || $_SESSION['state']['mapId'] !== $this->mapId) { + return new JsonView(['error' => 'No valid session found!']); + } + + if (count($_SESSION['state']['rounds']) === 0) { + $newPosition = $this->getNewPosition(); + $_SESSION['state']['rounds'][] = $newPosition; + + $data = ['panoId' => $newPosition['panoId']]; + } elseif (isset($_POST['guess'])) { + $last = &$_SESSION['state']['rounds'][count($_SESSION['state']['rounds']) - 1]; + + $position = $last['position']; + $guessPosition = new Position((float) $_POST['lat'], (float) $_POST['lng']); + + $last['guessPosition'] = $guessPosition; + + $distance = $this->calculateDistance($position, $guessPosition); + $score = $this->calculateScore($distance, $_SESSION['state']['area']); + + $last['distance'] = $distance; + $last['score'] = $score; + + if (count($_SESSION['state']['rounds']) < static::NUMBER_OF_ROUNDS) { + $exclude = []; + + foreach ($_SESSION['state']['rounds'] as $round) { + $exclude = array_merge($exclude, $round['placesWithoutPano'], [$round['placeId']]); + } + + $newPosition = $this->getNewPosition($exclude); + $_SESSION['state']['rounds'][] = $newPosition; + + $panoId = $newPosition['panoId']; + } else { + $_SESSION['state']['rounds'] = []; + + $panoId = null; + } + + $data = [ + 'result' => [ + 'position' => $position->toArray(), + 'distance' => $distance, + 'score' => $score + ], + 'panoId' => $panoId + ]; + } else { + $rounds = count($_SESSION['state']['rounds']); + $last = $_SESSION['state']['rounds'][$rounds - 1]; + + $history = []; + for ($i = 0; $i < $rounds - 1; ++$i) { + $round = $_SESSION['state']['rounds'][$i]; + $history[] = [ + 'position' => $round['position']->toArray(), + 'guessPosition' => $round['guessPosition']->toArray(), + 'distance' => $round['distance'], + 'score' => $round['score'] + ]; + } + + $data = [ + 'history' => $history, + 'panoId' => $last['panoId'] + ]; + } + + return new JsonView($data); + } + + private function getNewPosition(array $exclude = []): array + { + $placesWithoutPano = []; + + do { + $place = $this->selectNewPlace($exclude); + $position = new Position($place['lat'], $place['lng']); + $panoId = $this->getPanorama($position); + + if ($panoId === null) { + $placesWithoutPano[] = $place['id']; + } + } while ($panoId === null); + + return [ + 'placesWithoutPano' => $placesWithoutPano, + 'placeId' => $place['id'], + 'position' => $position, + 'panoId' => $panoId + ]; + } + + private function selectNewPlace(array $exclude): array + { + $condition = ''; + $params = ['i', &$this->mapId]; + if (($numExcluded = count($exclude)) > 0) { + $condition .= ' AND id NOT IN (' . implode(',', array_fill(0, $numExcluded, '?')) . ')'; + $params[0] .= str_repeat('i', $numExcluded); + foreach ($exclude as &$placeId) { + $params[] = &$placeId; + } + } + + $stmt = $this->mysql->prepare('SELECT COUNT(*) AS num FROM places WHERE map_id=? ' . $condition . ''); + call_user_func_array([$stmt, 'bind_param'], $params); + $stmt->execute(); + $numberOfPlaces = $stmt->get_result()->fetch_assoc()['num']; + $randomOffset = random_int(0, $numberOfPlaces - 1); + + $params[0] .= 'i'; + $params[] = &$randomOffset; + + $stmt = $this->mysql->prepare('SELECT id, lat, lng FROM places WHERE map_id=? ' . $condition . ' ORDER BY id LIMIT 1 OFFSET ?'); + call_user_func_array([$stmt, 'bind_param'], $params); + $stmt->execute(); + + return $stmt->get_result()->fetch_assoc(); + } + + private function getPanorama(Position $position): ?string + { + $query = [ + 'key' => $_ENV['GOOGLE_MAPS_SERVER_API_KEY'], + 'location' => $position->getLat() . ',' . $position->getLng(), + 'source' => 'outdoor' + ]; + + $client = new Client('https://maps.googleapis.com/maps/api/streetview'); + $request = $client->newRequest('metadata?' . http_build_query($query)); + $response = $request->getResponse(); + + $panoData = json_decode($response->getParsedResponse(), true); + + if ($panoData['status'] !== 'OK') { + return null; + } + + return $panoData['pano_id']; + } + + private function calculateDistance(Position $realPosition, Position $guessPosition): float + { + return $realPosition->calculateDistanceTo($guessPosition); + } + + private function calculateScore(float $distance, float $area) + { + $goodness = 1.0 - ($distance / sqrt($area)); + + return round(pow(static::MAX_SCORE, $goodness)); + } +} From 5e068e0aedb2fec8f369b6cfb3d9b7916022e684 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Mon, 25 May 2020 19:51:02 +0200 Subject: [PATCH 9/9] MAPG-47 refactor JS to be adapted to the new PositionController --- public/static/js/mapguesser.js | 214 ++++++++++++--------------------- 1 file changed, 79 insertions(+), 135 deletions(-) diff --git a/public/static/js/mapguesser.js b/public/static/js/mapguesser.js index 4ff40ae..8dcee39 100644 --- a/public/static/js/mapguesser.js +++ b/public/static/js/mapguesser.js @@ -7,7 +7,7 @@ rounds: [], scoreSum: 0, - realPosition: null, + panoId: null, panorama: null, map: null, guessMarker: null, @@ -15,32 +15,24 @@ googleLink: null, initialize: function () { - if (sessionStorage.rounds) { - var roundsLoaded = JSON.parse(sessionStorage.rounds); - for (var i = 0; i < roundsLoaded.length; ++i) { - var round = roundsLoaded[i]; - Core.rounds.push({ realPosition: round.realPosition, guessPosition: round.guessPosition, realMarker: null, guessMarker: null, line: null }); - if (round.guessPosition) { - Core.addRealGuessPair(round.realPosition, round.guessPosition, true); + document.getElementById('currentRound').innerHTML = '1/' + String(Core.NUMBER_OF_ROUNDS); + document.getElementById('currentScoreSum').innerHTML = '0/0'; + + Core.getNewPosition(function (response) { + if (response.history) { + for (var i = 0; i < response.history.length; ++i) { + var round = response.history[i]; + Core.rounds.push({ position: round.position, guessPosition: round.guessPosition, realMarker: null, guessMarker: null, line: null }); + Core.addRealGuessPair(round.position, round.guessPosition, true); + Core.scoreSum += round.score; } + + document.getElementById('currentRound').innerHTML = String(Core.rounds.length) + '/' + String(Core.NUMBER_OF_ROUNDS); + document.getElementById('currentScoreSum').innerHTML = String(Core.scoreSum) + '/' + String(Core.rounds.length * Core.MAX_SCORE); } - } - if (sessionStorage.scoreSum) { - Core.scoreSum = parseInt(sessionStorage.scoreSum); - } - - if (Core.rounds.length > 0 && !Core.rounds[Core.rounds.length - 1].guessPosition) { - Core.realPosition = Core.rounds[Core.rounds.length - 1].realPosition; - Core.loadPositionInfo(Core.realPosition); - - document.getElementById('currentRound').innerHTML = String(Core.rounds.length) + '/' + String(Core.NUMBER_OF_ROUNDS); - document.getElementById('currentScoreSum').innerHTML = String(Core.scoreSum) + '/' + String((Core.rounds.length - 1) * Core.MAX_SCORE); - } else { Core.startNewRound(); - - document.getElementById('currentScoreSum').innerHTML = String(0); - } + }); }, startNewGame: function () { @@ -64,9 +56,13 @@ document.getElementById('continueButton').style.display = null; document.getElementById('startNewGameButton').style.display = null; + document.getElementById('currentScoreSum').innerHTML = '0/0'; + Core.prepareNewRound(); - document.getElementById('currentScoreSum').innerHTML = String(0); + Core.getNewPosition(function () { + Core.startNewRound(); + }); }, prepareNewRound: function () { @@ -87,113 +83,107 @@ Core.map.setOptions({ draggableCursor: 'crosshair' }); - Core.map.fitBounds(guessMapBounds); - - Core.startNewRound(); + Core.map.fitBounds(mapBounds); }, startNewRound: function () { - Core.rounds.push({ realPosition: null, guessPosition: null, realMarker: null, guessMarker: null, line: null }); - - Core.panorama.setVisible(false); - document.getElementById('loading').style.visibility = 'visible'; + Core.rounds.push({ position: null, guessPosition: null, realMarker: null, guessMarker: null, line: null }); document.getElementById('currentRound').innerHTML = String(Core.rounds.length) + '/' + String(Core.NUMBER_OF_ROUNDS); - Core.getNewPosition(); + Core.loadPano(Core.panoId); }, - getNewPosition: function () { + getNewPosition: function (callback) { + document.getElementById('loading').style.visibility = 'visible'; + var xhr = new XMLHttpRequest(); xhr.responseType = 'json'; xhr.onreadystatechange = function () { if (this.readyState == 4 && this.status == 200) { - Core.realPosition = this.response.position; - Core.rounds[Core.rounds.length - 1].realPosition = this.response.position; - Core.loadPositionInfo(this.response.position); + document.getElementById('loading').style.visibility = 'hidden'; - Core.saveToSession(); + Core.panoId = this.response.panoId; + + callback(this.response); } }; - xhr.open('GET', 'getNewPosition.json', true); + xhr.open('GET', 'position.json', true); xhr.send(); }, - loadPositionInfo: function (position) { - var sv = new google.maps.StreetViewService(); - sv.getPanorama({ location: position, preference: google.maps.StreetViewPreference.NEAREST, source: google.maps.StreetViewSource.OUTDOOR }, Core.loadPano); - }, - - loadPano: function (data, status) { - if (status !== google.maps.StreetViewStatus.OK) { - Core.getNewPosition(); - return; - } - - document.getElementById('loading').style.visibility = 'hidden'; - + loadPano: function (panoId) { if (Core.adaptGuess) { document.getElementById('guess').classList.add('adapt'); } - Core.panorama.setVisible(true); Core.panorama.setPov({ heading: 0, pitch: 0 }); Core.panorama.setZoom(0); - Core.panorama.setPano(data.location.pano); + Core.panorama.setPano(panoId); }, evaluateGuess: function () { var guessPosition = Core.guessMarker.getPosition().toJSON(); Core.rounds[Core.rounds.length - 1].guessPosition = guessPosition; - var distance = Util.calculateDistance(Core.realPosition, guessPosition); - var score = Core.calculateScore(distance); - Core.scoreSum += score; - - document.getElementById('currentScoreSum').innerHTML = String(Core.scoreSum) + '/' + String(Core.rounds.length * Core.MAX_SCORE); - - Core.saveToSession(); - - Core.guessMarker.setMap(null); - Core.guessMarker = null; - if (Core.adaptGuess) { document.getElementById('guess').classList.remove('adapt'); } - document.getElementById('guess').classList.add('result'); + document.getElementById('loading').style.visibility = 'visible'; - Core.addRealGuessPair(Core.realPosition, guessPosition); + var xhr = new XMLHttpRequest(); + xhr.responseType = 'json'; + xhr.onreadystatechange = function () { + if (this.readyState == 4 && this.status == 200) { + Core.guessMarker.setMap(null); + Core.guessMarker = null; - var resultBounds = new google.maps.LatLngBounds(); - resultBounds.extend(Core.realPosition); - resultBounds.extend(guessPosition); + document.getElementById('loading').style.visibility = 'hidden'; + document.getElementById('guess').classList.add('result'); - Core.map.setOptions({ - draggableCursor: 'grab' - }); - Core.map.fitBounds(resultBounds); + Core.scoreSum += this.response.result.score; + document.getElementById('currentScoreSum').innerHTML = String(Core.scoreSum) + '/' + String(Core.rounds.length * Core.MAX_SCORE); - document.getElementById('distance').innerHTML = Util.printDistanceForHuman(distance); - document.getElementById('score').innerHTML = score; + Core.rounds[Core.rounds.length - 1].position = this.response.result.position; + Core.addRealGuessPair(this.response.result.position, guessPosition); - var scoreBarProperties = Core.calculateScoreBarProperties(score, Core.MAX_SCORE); - var scoreBar = document.getElementById('scoreBar'); - scoreBar.style.backgroundColor = scoreBarProperties.backgroundColor; - scoreBar.style.width = scoreBarProperties.width; + var resultBounds = new google.maps.LatLngBounds(); + resultBounds.extend(this.response.result.position); + resultBounds.extend(guessPosition); - if (Core.rounds.length == Core.NUMBER_OF_ROUNDS) { - document.getElementById('continueButton').style.display = 'none'; - document.getElementById('showSummaryButton').style.display = 'block'; - } + Core.map.setOptions({ + draggableCursor: 'grab' + }); + Core.map.fitBounds(resultBounds); + + document.getElementById('distance').innerHTML = Util.printDistanceForHuman(this.response.result.distance); + document.getElementById('score').innerHTML = this.response.result.score; + + var scoreBarProperties = Core.calculateScoreBarProperties(this.response.result.score, Core.MAX_SCORE); + var scoreBar = document.getElementById('scoreBar'); + scoreBar.style.backgroundColor = scoreBarProperties.backgroundColor; + scoreBar.style.width = scoreBarProperties.width; + + if (Core.rounds.length == Core.NUMBER_OF_ROUNDS) { + document.getElementById('continueButton').style.display = 'none'; + document.getElementById('showSummaryButton').style.display = 'block'; + } + + Core.panoId = this.response.panoId; + } + }; + xhr.open('POST', 'position.json', true); + xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + xhr.send('guess&lat=' + guessPosition.lat + '&lng=' + guessPosition.lng); }, - addRealGuessPair: function (realPosition, guessPosition, hidden) { + addRealGuessPair: function (position, guessPosition, hidden) { var round = Core.rounds[Core.rounds.length - 1]; round.realMarker = new google.maps.Marker({ map: Core.map, visible: !hidden, - position: realPosition, + position: position, title: 'Open in Google Maps', zIndex: Core.rounds.length * 2, clickable: true, @@ -224,7 +214,7 @@ map: Core.map, visible: !hidden, path: [ - realPosition, + position, guessPosition ], geodesic: true, @@ -245,12 +235,6 @@ }); }, - calculateScore: function (distance) { - var goodness = 1.0 - distance / Math.sqrt(mapArea); - - return Math.round(Math.pow(Core.MAX_SCORE, goodness)); - }, - calculateScoreBarProperties: function (score, maxScore) { var percent = Math.floor((score / maxScore) * 100); @@ -292,7 +276,7 @@ round.guessMarker.setVisible(true); round.line.setVisible(true); - resultBounds.extend(round.realPosition); + resultBounds.extend(round.position); resultBounds.extend(round.guessPosition); } @@ -324,51 +308,10 @@ Core.googleLink.href = 'https://maps.google.com/maps'; } }, 1); - }, - - saveToSession: function () { - if (Core.rounds.length == Core.NUMBER_OF_ROUNDS && Core.rounds[Core.rounds.length - 1].guessPosition) { - sessionStorage.removeItem('rounds'); - sessionStorage.removeItem('scoreSum'); - return; - } - - var roundsToSave = []; - for (var i = 0; i < Core.rounds.length; ++i) { - var round = Core.rounds[i]; - roundsToSave.push({ realPosition: round.realPosition, guessPosition: round.guessPosition }); - } - - sessionStorage.setItem('rounds', JSON.stringify(roundsToSave)); - sessionStorage.setItem('scoreSum', String(Core.scoreSum)); } }; var Util = { - EARTH_RADIUS_IN_METER: 6371000, - - deg2rad: function (deg) { - return deg * (Math.PI / 180.0); - }, - - calculateDistance: function (position1, position2) { - var lat1 = Util.deg2rad(position1.lat); - var lng1 = Util.deg2rad(position1.lng); - var lat2 = Util.deg2rad(position2.lat); - var lng2 = Util.deg2rad(position2.lng); - - var angleCos = Math.cos(lat1) * Math.cos(lat2) * Math.cos(lng2 - lng1) + - Math.sin(lat1) * Math.sin(lat2); - - if (angleCos > 1.0) { - angleCos = 1.0; - } - - var angle = Math.acos(angleCos); - - return angle * Util.EARTH_RADIUS_IN_METER; - }, - printDistanceForHuman: function (distance) { if (distance < 1000) { return Number.parseFloat(distance).toFixed(0) + ' m'; @@ -393,7 +336,7 @@ draggingCursor: 'grabbing' }); - Core.map.fitBounds(guessMapBounds); + Core.map.fitBounds(mapBounds); Core.map.addListener('click', function (e) { if (Core.rounds[Core.rounds.length - 1].guessPosition) { @@ -461,6 +404,7 @@ document.getElementById('continueButton').onclick = function () { Core.prepareNewRound(); + Core.startNewRound(); } document.getElementById('showSummaryButton').onclick = function () {