From d5c0e59d2981ef64610c2c27fa2904cb66326042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Sun, 17 May 2020 22:26:23 +0200 Subject: [PATCH 01/32] MAPG-3 move utils classes into Util namespace --- src/{ => Util}/Geo/Bounds.php | 2 +- src/{ => Util}/Geo/Position.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/{ => Util}/Geo/Bounds.php (97%) rename src/{ => Util}/Geo/Position.php (93%) diff --git a/src/Geo/Bounds.php b/src/Util/Geo/Bounds.php similarity index 97% rename from src/Geo/Bounds.php rename to src/Util/Geo/Bounds.php index f813f0e..e8c9710 100644 --- a/src/Geo/Bounds.php +++ b/src/Util/Geo/Bounds.php @@ -1,4 +1,4 @@ - Date: Sun, 17 May 2020 22:28:34 +0200 Subject: [PATCH 02/32] MAPG-3 move index.php's view part to views/guess.php --- public/index.php => views/guess.php | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) rename public/index.php => views/guess.php (69%) diff --git a/public/index.php b/views/guess.php similarity index 69% rename from public/index.php rename to views/guess.php index 2bb1e39..4658d5a 100644 --- a/public/index.php +++ b/views/guess.php @@ -1,26 +1,10 @@ -extend(new MapGuesser\Geo\Position(48.07683,7.35758)); -$bounds->extend(new MapGuesser\Geo\Position(47.57496, 19.08077)); - -?> - - MapGuesser -
@@ -36,5 +20,4 @@ $bounds->extend(new MapGuesser\Geo\Position(47.57496, 19.08077)); - - \ No newline at end of file + From 555984caf5b8de0a83e2daec8d3ffc4ceee967ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Sun, 17 May 2020 22:30:22 +0200 Subject: [PATCH 03/32] MAPG-3 create basic contoller functionality --- main.php | 4 +++- public/.htaccess | 5 +++++ public/index.php | 16 ++++++++++++++++ src/Controller/BaseController.php | 24 ++++++++++++++++++++++++ src/Controller/GuessController.php | 22 ++++++++++++++++++++++ 5 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 public/.htaccess create mode 100644 public/index.php create mode 100644 src/Controller/BaseController.php create mode 100644 src/Controller/GuessController.php diff --git a/main.php b/main.php index bd37059..b8c37f0 100644 --- a/main.php +++ b/main.php @@ -2,7 +2,9 @@ require 'vendor/autoload.php'; -$dotenv = Dotenv\Dotenv::createImmutable(__DIR__); +const ROOT = __DIR__; + +$dotenv = Dotenv\Dotenv::createImmutable(ROOT); $dotenv->load(); if (!empty($_ENV['DEV'])) { diff --git a/public/.htaccess b/public/.htaccess new file mode 100644 index 0000000..3a37342 --- /dev/null +++ b/public/.htaccess @@ -0,0 +1,5 @@ +RewriteEngine On +RewriteBase / +RewriteCond %{REQUEST_FILENAME} !-d +RewriteCond %{REQUEST_FILENAME} !-f +RewriteRule ^ index.php [L] diff --git a/public/index.php b/public/index.php new file mode 100644 index 0000000..cae72c9 --- /dev/null +++ b/public/index.php @@ -0,0 +1,16 @@ +render(); diff --git a/src/Controller/BaseController.php b/src/Controller/BaseController.php new file mode 100644 index 0000000..9e2c2a7 --- /dev/null +++ b/src/Controller/BaseController.php @@ -0,0 +1,24 @@ +operate(); + + extract($this->variables); + + ob_start(); + require ROOT . '/views/' . $this->view . '.php'; + $content = ob_get_contents(); + ob_end_clean(); + + return $content; + } + + abstract protected function operate() : void; +} diff --git a/src/Controller/GuessController.php b/src/Controller/GuessController.php new file mode 100644 index 0000000..68d2369 --- /dev/null +++ b/src/Controller/GuessController.php @@ -0,0 +1,22 @@ +extend(new Position(48.07683,7.35758)); + $bounds->extend(new Position(47.57496, 19.08077)); + + $this->variables = compact('realPosition', 'bounds'); + } +} From 033e03335f14250c6f53579ec7158990cdae6335 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Sun, 17 May 2020 22:31:13 +0200 Subject: [PATCH 04/32] MAPG-3 add initial SQL structure --- install/db.sql | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 install/db.sql diff --git a/install/db.sql b/install/db.sql new file mode 100644 index 0000000..538e3da --- /dev/null +++ b/install/db.sql @@ -0,0 +1,27 @@ +SET NAMES utf8mb4; +SET foreign_key_checks = 0; + + +DROP TABLE IF EXISTS `maps`; +CREATE TABLE `maps` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL, + `description` text NOT NULL, + `bound_south_lat` decimal(8,6) NOT NULL, + `bound_west_lng` decimal(9,6) NOT NULL, + `bound_north_lat` decimal(8,6) NOT NULL, + `bound_east_lng` decimal(9,6) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + + +DROP TABLE IF EXISTS `places`; +CREATE TABLE `places` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `map_id` int(10) unsigned NOT NULL, + `lat` decimal(8,6) NOT NULL, + `lng` decimal(9,6) NOT NULL, + PRIMARY KEY (`id`), + KEY `map_id` (`map_id`), + CONSTRAINT `places_map_id` FOREIGN KEY (`map_id`) REFERENCES `maps` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; From 731a168323200546d7ffc0cad3f428e1b2e13064 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Sun, 17 May 2020 22:33:48 +0200 Subject: [PATCH 05/32] MAPG-3 remove pov from panorama for the time being --- public/static/js/mapguesser.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/public/static/js/mapguesser.js b/public/static/js/mapguesser.js index bf76f05..1dc203d 100644 --- a/public/static/js/mapguesser.js +++ b/public/static/js/mapguesser.js @@ -53,10 +53,6 @@ var googleLink; function initialize() { panorama = new google.maps.StreetViewPanorama(document.getElementById('panorama'), { position: realPosition, - pov: { - heading: 34, - pitch: 10 - }, disableDefaultUI: true, linksControl: true, showRoadLabels: false From 153acd1a4e19d978fedd257859cb391890778349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Sun, 17 May 2020 23:32:57 +0200 Subject: [PATCH 06/32] MAPG-3 add DB connection variables to .env.example --- .env.example | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.env.example b/.env.example index 1570bcb..4cd7025 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,6 @@ DEV=true +DB_HOST=mariadb +DB_USER=mapguesser +DB_PASSWORD=mapguesser +DB_NAME=mapguesser GOOGLE_MAPS_JS_API_KEY=your_google_maps_js_api_key From 90a0e497b3026b3e49a1d16cc90e18150a6cc863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Sun, 17 May 2020 23:35:10 +0200 Subject: [PATCH 07/32] MAPG-3 select 1 place randomly from places (with demo map id 1) --- src/Controller/GuessController.php | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Controller/GuessController.php b/src/Controller/GuessController.php index 68d2369..55ec2f4 100644 --- a/src/Controller/GuessController.php +++ b/src/Controller/GuessController.php @@ -2,6 +2,7 @@ use MapGuesser\Util\Geo\Bounds; use MapGuesser\Util\Geo\Position; +use mysqli; class GuessController extends BaseController { @@ -9,8 +10,20 @@ class GuessController extends BaseController protected function operate() : void { - // demo position - $realPosition = new Position(47.85239, 13.35101); + $mysql = new mysqli($_ENV['DB_HOST'], $_ENV['DB_USER'], $_ENV['DB_PASSWORD'], $_ENV['DB_NAME']); + + // demo map + $mapId = 1; + + // using RAND() for the time being, could be changed in the future + $stmt = $mysql->prepare('SELECT lat, lng FROM places WHERE map_id=? ORDER BY RAND() LIMIT 1'); + $stmt->bind_param("i", $mapId); + $stmt->execute(); + + $result = $stmt->get_result(); + $row = $result->fetch_assoc(); + + $realPosition = new Position($row['lat'], $row['lng']); // demo bounds $bounds = new Bounds($realPosition); From ee636cae781256691a7dd99dcf6ab8538ad4fe07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Mon, 18 May 2020 00:23:45 +0200 Subject: [PATCH 08/32] MAPG-15 read map bounds from the database --- src/Controller/GuessController.php | 17 ++++++++--------- src/Util/Geo/Bounds.php | 24 +++++++++++++++++++----- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/Controller/GuessController.php b/src/Controller/GuessController.php index 55ec2f4..036d224 100644 --- a/src/Controller/GuessController.php +++ b/src/Controller/GuessController.php @@ -15,20 +15,19 @@ class GuessController extends BaseController // demo map $mapId = 1; + $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); + $stmt->execute(); + $map = $stmt->get_result()->fetch_assoc(); + // using RAND() for the time being, could be changed in the future $stmt = $mysql->prepare('SELECT lat, lng FROM places WHERE map_id=? ORDER BY RAND() LIMIT 1'); $stmt->bind_param("i", $mapId); $stmt->execute(); + $place = $stmt->get_result()->fetch_assoc(); - $result = $stmt->get_result(); - $row = $result->fetch_assoc(); - - $realPosition = new Position($row['lat'], $row['lng']); - - // demo bounds - $bounds = new Bounds($realPosition); - $bounds->extend(new Position(48.07683,7.35758)); - $bounds->extend(new Position(47.57496, 19.08077)); + $realPosition = new Position($place['lat'], $place['lng']); + $bounds = Bounds::createDirectly($map['bound_south_lat'], $map['bound_west_lng'], $map['bound_north_lat'], $map['bound_east_lng']); $this->variables = compact('realPosition', 'bounds'); } diff --git a/src/Util/Geo/Bounds.php b/src/Util/Geo/Bounds.php index e8c9710..bd1c458 100644 --- a/src/Util/Geo/Bounds.php +++ b/src/Util/Geo/Bounds.php @@ -10,13 +10,27 @@ class Bounds private bool $initialized = false; - public function __construct(Position $position = null) + public static function createWithPosition(Position $position) : Bounds { - if ($position === null) { - return; - } + $instance = new static(); - $this->initialize($position); + $instance->initialize($position); + + return $instance; + } + + public static function createDirectly(float $southLat, $westLng, $northLat, $eastLng) : Bounds + { + $instance = new static(); + + $instance->southLat = $southLat; + $instance->westLng = $westLng; + $instance->northLat = $northLat; + $instance->eastLng = $eastLng; + + $instance->initialized = true; + + return $instance; } public function extend(Position $position): void From 037d19b0b3941c18356c18c179f61662073571c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Tue, 19 May 2020 03:13:55 +0200 Subject: [PATCH 09/32] MAPG-10 refactor view-controller to be able to handle JSON response --- src/Controller/BaseController.php | 24 --------------------- src/Controller/ControllerInterface.php | 8 +++++++ src/View/HtmlView.php | 29 ++++++++++++++++++++++++++ src/View/JsonView.php | 21 +++++++++++++++++++ src/View/ViewBase.php | 15 +++++++++++++ 5 files changed, 73 insertions(+), 24 deletions(-) delete mode 100644 src/Controller/BaseController.php create mode 100644 src/Controller/ControllerInterface.php create mode 100644 src/View/HtmlView.php create mode 100644 src/View/JsonView.php create mode 100644 src/View/ViewBase.php diff --git a/src/Controller/BaseController.php b/src/Controller/BaseController.php deleted file mode 100644 index 9e2c2a7..0000000 --- a/src/Controller/BaseController.php +++ /dev/null @@ -1,24 +0,0 @@ -operate(); - - extract($this->variables); - - ob_start(); - require ROOT . '/views/' . $this->view . '.php'; - $content = ob_get_contents(); - ob_end_clean(); - - return $content; - } - - abstract protected function operate() : void; -} diff --git a/src/Controller/ControllerInterface.php b/src/Controller/ControllerInterface.php new file mode 100644 index 0000000..22d2f8f --- /dev/null +++ b/src/Controller/ControllerInterface.php @@ -0,0 +1,8 @@ +template = $template; + $this->data = &$data; + } + + public function &render(): string + { + extract($this->data); + + ob_start(); + require ROOT . '/views/' . $this->template . '.php'; + $content = ob_get_contents(); + ob_end_clean(); + + return $content; + } + + public function getContentType(): string + { + return 'text/html'; + } +} diff --git a/src/View/JsonView.php b/src/View/JsonView.php new file mode 100644 index 0000000..78eb90f --- /dev/null +++ b/src/View/JsonView.php @@ -0,0 +1,21 @@ +data = &$data; + } + + public function &render(): string + { + $content = json_encode($this->data); + + return $content; + } + + public function getContentType(): string + { + return 'application/json'; + } +} diff --git a/src/View/ViewBase.php b/src/View/ViewBase.php new file mode 100644 index 0000000..9bad81f --- /dev/null +++ b/src/View/ViewBase.php @@ -0,0 +1,15 @@ +data; + } + + abstract public function &render(): string; + + abstract public function getContentType(): string; +} From 61cde2d40b05741890aa714b0e02281c87552523 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Tue, 19 May 2020 03:14:20 +0200 Subject: [PATCH 10/32] MAPG-10 add toArray to Position --- src/Util/Geo/Position.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Util/Geo/Position.php b/src/Util/Geo/Position.php index 4e2499c..2ac9fc2 100644 --- a/src/Util/Geo/Position.php +++ b/src/Util/Geo/Position.php @@ -21,11 +21,16 @@ class Position return $this->lng; } - public function toJson(): string + public function toArray(): array { - return json_encode([ + return [ 'lat' => $this->lat, 'lng' => $this->lng, - ]); + ]; + } + + public function toJson(): string + { + return json_encode($this->toArray()); } } From 88e8a4e9e4b0a0e854019a9ca39b1467535a39bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Tue, 19 May 2020 03:17:40 +0200 Subject: [PATCH 11/32] MAPG-10 add GetNewPosition controller remove position selecing from GuessController adapt GuessController to the new view-controller concept --- src/Controller/GetNewPosition.php | 28 ++++++++++++++++++++++++++++ src/Controller/GuessController.php | 11 ++++++----- views/guess.php | 1 - 3 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 src/Controller/GetNewPosition.php diff --git a/src/Controller/GetNewPosition.php b/src/Controller/GetNewPosition.php new file mode 100644 index 0000000..565a3d2 --- /dev/null +++ b/src/Controller/GetNewPosition.php @@ -0,0 +1,28 @@ +prepare('SELECT lat, lng FROM places WHERE map_id=? ORDER BY RAND() LIMIT 1'); + $stmt->bind_param("i", $mapId); + $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/GuessController.php b/src/Controller/GuessController.php index 036d224..1b851ef 100644 --- a/src/Controller/GuessController.php +++ b/src/Controller/GuessController.php @@ -2,13 +2,13 @@ use MapGuesser\Util\Geo\Bounds; use MapGuesser\Util\Geo\Position; +use MapGuesser\View\HtmlView; +use MapGuesser\View\ViewBase; use mysqli; -class GuessController extends BaseController +class GuessController implements ControllerInterface { - protected string $view = 'guess'; - - protected function operate() : void + public function run(): ViewBase { $mysql = new mysqli($_ENV['DB_HOST'], $_ENV['DB_USER'], $_ENV['DB_PASSWORD'], $_ENV['DB_NAME']); @@ -29,6 +29,7 @@ class GuessController extends BaseController $realPosition = new Position($place['lat'], $place['lng']); $bounds = Bounds::createDirectly($map['bound_south_lat'], $map['bound_west_lng'], $map['bound_north_lat'], $map['bound_east_lng']); - $this->variables = compact('realPosition', 'bounds'); + $data = compact('bounds'); + return new HtmlView('guess', $data); } } diff --git a/views/guess.php b/views/guess.php index 4658d5a..2c96a51 100644 --- a/views/guess.php +++ b/views/guess.php @@ -14,7 +14,6 @@
From e0d65b6e275ad059a9b1961d18b31bf74be3eac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Tue, 19 May 2020 03:19:23 +0200 Subject: [PATCH 12/32] MAPG-10 adapt index.php to the new view-controller concept add GetNewPosition's route to index.php --- public/index.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/public/index.php b/public/index.php index cae72c9..45f4ab8 100644 --- a/public/index.php +++ b/public/index.php @@ -8,9 +8,16 @@ switch($url) { case '/': $controller = new MapGuesser\Controller\GuessController(); break; + case '/getNewPosition.json': + $controller = new MapGuesser\Controller\GetNewPosition(); + break; default: echo 'Error 404'; die; } -echo $controller->render(); +$view = $controller->run(); + +header('Content-Type: ' . $view->getContentType() . '; charset=UTF-8'); + +echo $view->render(); From 9555a43cf9a7f188b1af0dbdcc026fe2a06f1118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Tue, 19 May 2020 03:19:50 +0200 Subject: [PATCH 13/32] MAPG-10 reformat Bounds.php --- src/Util/Geo/Bounds.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Util/Geo/Bounds.php b/src/Util/Geo/Bounds.php index bd1c458..b34d583 100644 --- a/src/Util/Geo/Bounds.php +++ b/src/Util/Geo/Bounds.php @@ -10,7 +10,7 @@ class Bounds private bool $initialized = false; - public static function createWithPosition(Position $position) : Bounds + public static function createWithPosition(Position $position): Bounds { $instance = new static(); @@ -19,7 +19,7 @@ class Bounds return $instance; } - public static function createDirectly(float $southLat, $westLng, $northLat, $eastLng) : Bounds + public static function createDirectly(float $southLat, $westLng, $northLat, $eastLng): Bounds { $instance = new static(); From ef95f5b986e48ae46e05ec808568f033293a3684 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Tue, 19 May 2020 03:25:42 +0200 Subject: [PATCH 14/32] MAPG-10 query new position with AJAX check if pano at position exists load new pano after guess --- public/static/js/mapguesser.js | 68 ++++++++++++++++++++++++++-------- 1 file changed, 52 insertions(+), 16 deletions(-) diff --git a/public/static/js/mapguesser.js b/public/static/js/mapguesser.js index 1dc203d..4b85e78 100644 --- a/public/static/js/mapguesser.js +++ b/public/static/js/mapguesser.js @@ -45,26 +45,14 @@ var MapManipulator = { } }; +var realPosition; var panorama; var guessMap; var guessMarker; var googleLink; function initialize() { - panorama = new google.maps.StreetViewPanorama(document.getElementById('panorama'), { - position: realPosition, - disableDefaultUI: true, - linksControl: true, - showRoadLabels: false - }); - - panorama.addListener('position_changed', function () { - MapManipulator.rewriteGoogleLink(); - }); - - panorama.addListener('pov_changed', function () { - MapManipulator.rewriteGoogleLink(); - }); + getNewPosition(); guessMap = new google.maps.Map(document.getElementById('guessMap'), { disableDefaultUI: true, @@ -90,6 +78,48 @@ function initialize() { }); } +function getNewPosition() { + var xhr = new XMLHttpRequest(); + xhr.responseType = 'json'; + xhr.onreadystatechange = function () { + if (this.readyState == 4 && this.status == 200) { + realPosition = this.response.position; + + var sv = new google.maps.StreetViewService(); + sv.getPanorama({ location: this.response.position, preference: google.maps.StreetViewPreference.BEST }, loadPano); + } + }; + xhr.open('GET', 'getNewPosition.json', true); + xhr.send(); +} + +function loadPano(data, status) { + if (status !== google.maps.StreetViewStatus.OK) { + getNewPosition(); + return; + } + + if (panorama) { + panorama.setPano(data.location.pano); + return; + } + + panorama = new google.maps.StreetViewPanorama(document.getElementById('panorama'), { + pano: data.location.pano, + disableDefaultUI: true, + linksControl: true, + showRoadLabels: false + }); + + panorama.addListener('position_changed', function () { + MapManipulator.rewriteGoogleLink(); + }); + + panorama.addListener('pov_changed', function () { + MapManipulator.rewriteGoogleLink(); + }); +} + document.getElementById('guessButton').onclick = function () { if (!guessMarker) { return; @@ -98,7 +128,13 @@ document.getElementById('guessButton').onclick = function () { var guessedPosition = guessMarker.getPosition(); var distance = Util.calculateDistance(realPosition, { lat: guessedPosition.lat(), lng: guessedPosition.lng() }); - alert('You were ' + distance + 'm close!'); + alert('You were ' + (Math.round(distance) / 1000) + ' km close!'); - this.blur(); + this.disabled = true; + guessMarker.setMap(null); + guessMarker = null; + //TODO: fit to the same size as on init + guessMap.fitBounds(guessMapBounds); + + getNewPosition(); } From dc943fa3cbca02779d88e8f7328106ed11eb77a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Tue, 19 May 2020 16:13:57 +0200 Subject: [PATCH 15/32] MAPG-4 rename GuessController to GameController --- public/index.php | 4 ++-- src/Controller/{GuessController.php => GameController.php} | 4 ++-- views/{guess.php => game.php} | 0 3 files changed, 4 insertions(+), 4 deletions(-) rename src/Controller/{GuessController.php => GameController.php} (92%) rename views/{guess.php => game.php} (100%) diff --git a/public/index.php b/public/index.php index 45f4ab8..6f246a3 100644 --- a/public/index.php +++ b/public/index.php @@ -5,8 +5,8 @@ require '../main.php'; // very basic routing $url = $_SERVER['REQUEST_URI']; switch($url) { - case '/': - $controller = new MapGuesser\Controller\GuessController(); + case '/game': + $controller = new MapGuesser\Controller\GameController(); break; case '/getNewPosition.json': $controller = new MapGuesser\Controller\GetNewPosition(); diff --git a/src/Controller/GuessController.php b/src/Controller/GameController.php similarity index 92% rename from src/Controller/GuessController.php rename to src/Controller/GameController.php index 1b851ef..43230dd 100644 --- a/src/Controller/GuessController.php +++ b/src/Controller/GameController.php @@ -6,7 +6,7 @@ use MapGuesser\View\HtmlView; use MapGuesser\View\ViewBase; use mysqli; -class GuessController implements ControllerInterface +class GameController implements ControllerInterface { public function run(): ViewBase { @@ -30,6 +30,6 @@ class GuessController implements ControllerInterface $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('guess', $data); + return new HtmlView('game', $data); } } diff --git a/views/guess.php b/views/game.php similarity index 100% rename from views/guess.php rename to views/game.php From e4c59739dc50de3afd737329b9c144c0dcbbc41e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Tue, 19 May 2020 16:18:12 +0200 Subject: [PATCH 16/32] MAPG-4 show real position after guess make workflow better --- public/static/css/mapguesser.css | 90 ++++++++++++++++---- public/static/js/mapguesser.js | 137 +++++++++++++++++++++++-------- views/game.php | 10 ++- 3 files changed, 185 insertions(+), 52 deletions(-) diff --git a/public/static/css/mapguesser.css b/public/static/css/mapguesser.css index 71e354e..d271969 100644 --- a/public/static/css/mapguesser.css +++ b/public/static/css/mapguesser.css @@ -1,9 +1,53 @@ +* { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + vertical-align: baseline; +} + html, body { height: 100%; margin: 0; padding: 0; } +p, button { + font-family: 'Roboto', sans-serif; +} + +p { + font-weight: 300; + font-size: 12px; +} + +.bold { + font-weight: 500; +} + +button { + cursor: pointer; + font-size: 15px; + font-weight: 500; + color: #ffffff; + background-color: #5e77aa; + padding: 8px 15px; + border: none; + border-radius: 3px; +} + +button:hover, button:focus { + background-color: #29457f; + outline: none; +} + +button:disabled { + cursor: no-drop; + color: #dddddd; + background-color: #808080; + opacity: 0.7; +} + #panorama { height: 100%; width: 100%; @@ -18,6 +62,7 @@ html, body { height: 150px; opacity: 0.5; z-index: 2; + visibility: visible; transition-property: width, height, opacity; transition-duration: 0.1s; transition-delay: 0.8s; @@ -26,7 +71,7 @@ html, body { #guess:hover { width: 500px; height: 350px; - opacity: 1.0; + opacity: 0.95; transition-delay: 0s; } @@ -50,26 +95,39 @@ html, body { } #guessButton { - cursor: pointer; - font-size: 14px; - font-weight: bold; - color: #ffffff; - background-color: #5e77aa; - border: none; - border-radius: 3px; + padding: 0; width: 100%; height: 100%; box-sizing: border-box; } -#guessButton:hover, #guessButton:focus { - background-color: #29457f; - outline: none; +#result { + position: absolute; + top: 50px; + left: 50px; + right: 50px; + bottom: 50px; + opacity: 0.95; + z-index: 2; + visibility: hidden; + background-color: #ffffff; + border-radius: 3px; } -#guessButton:disabled { - cursor: no-drop; - color: #dddddd; - background-color: #808080; - opacity: 0.7; +#resultMap { + height: 70%; + width: 100%; +} + +#resultInfo { + height: 30%; + width: 100%; + padding: 20px; + text-align: center; + box-sizing: border-box; +} + +#resultInfo p { + margin-bottom: 20px; + font-size: 24px; } diff --git a/public/static/js/mapguesser.js b/public/static/js/mapguesser.js index 4b85e78..2b8090a 100644 --- a/public/static/js/mapguesser.js +++ b/public/static/js/mapguesser.js @@ -22,6 +22,18 @@ var Util = { ); return angle * Util.EARTH_RADIUS_IN_METER; + }, + + printDistanceForHuman: function (distance) { + if (distance < 1000) { + return Number.parseFloat(distance).toFixed(0) + ' m'; + } else if (distance < 10000) { + return Number.parseFloat(distance / 1000).toFixed(2) + ' km'; + } else if (distance < 100000) { + return Number.parseFloat(distance / 1000).toFixed(1) + ' km'; + } else { + return Number.parseFloat(distance / 1000).toFixed(0) + ' km'; + } } }; @@ -49,11 +61,11 @@ var realPosition; var panorama; var guessMap; var guessMarker; +var resultMap; +var resultMarkers = { guess: null, real: null }; var googleLink; function initialize() { - getNewPosition(); - guessMap = new google.maps.Map(document.getElementById('guessMap'), { disableDefaultUI: true, clickableIcons: false, @@ -71,41 +83,21 @@ function initialize() { guessMarker = new google.maps.Marker({ map: guessMap, position: e.latLng, - draggable: true + clickable: false, + draggable: true, + label: { + color: '#ffffff', + fontFamily: 'Roboto', + fontSize: '18px', + fontWeight: '500', + text: '?' + } }); document.getElementById('guessButton').disabled = false; }); -} - -function getNewPosition() { - var xhr = new XMLHttpRequest(); - xhr.responseType = 'json'; - xhr.onreadystatechange = function () { - if (this.readyState == 4 && this.status == 200) { - realPosition = this.response.position; - - var sv = new google.maps.StreetViewService(); - sv.getPanorama({ location: this.response.position, preference: google.maps.StreetViewPreference.BEST }, loadPano); - } - }; - xhr.open('GET', 'getNewPosition.json', true); - xhr.send(); -} - -function loadPano(data, status) { - if (status !== google.maps.StreetViewStatus.OK) { - getNewPosition(); - return; - } - - if (panorama) { - panorama.setPano(data.location.pano); - return; - } panorama = new google.maps.StreetViewPanorama(document.getElementById('panorama'), { - pano: data.location.pano, disableDefaultUI: true, linksControl: true, showRoadLabels: false @@ -118,6 +110,37 @@ function loadPano(data, status) { panorama.addListener('pov_changed', function () { MapManipulator.rewriteGoogleLink(); }); + + resultMap = new google.maps.Map(document.getElementById('resultMap'), { + disableDefaultUI: true, + clickableIcons: false, + }); + + getNewPosition(); +} + +function getNewPosition() { + var xhr = new XMLHttpRequest(); + xhr.responseType = 'json'; + xhr.onreadystatechange = function () { + if (this.readyState == 4 && this.status == 200) { + realPosition = this.response.position; + + var sv = new google.maps.StreetViewService(); + sv.getPanorama({ location: this.response.position, preference: google.maps.StreetViewPreference.NEAREST }, loadPano); + } + }; + xhr.open('GET', 'getNewPosition.json', true); + xhr.send(); +} + +function loadPano(data, status) { + if (status !== google.maps.StreetViewStatus.OK) { + getNewPosition(); + return; + } + + panorama.setPano(data.location.pano); } document.getElementById('guessButton').onclick = function () { @@ -126,14 +149,58 @@ document.getElementById('guessButton').onclick = function () { } var guessedPosition = guessMarker.getPosition(); - var distance = Util.calculateDistance(realPosition, { lat: guessedPosition.lat(), lng: guessedPosition.lng() }); - - alert('You were ' + (Math.round(distance) / 1000) + ' km close!'); this.disabled = true; guessMarker.setMap(null); guessMarker = null; - //TODO: fit to the same size as on init + + var distance = Util.calculateDistance(realPosition, { lat: guessedPosition.lat(), lng: guessedPosition.lng() }); + + document.getElementById('guess').style.visibility = 'hidden'; + document.getElementById('result').style.visibility = 'visible'; + + var resultBounds = new google.maps.LatLngBounds(); + resultBounds.extend(realPosition); + resultBounds.extend(guessedPosition); + + resultMap.fitBounds(resultBounds); + + resultMarkers.real = new google.maps.Marker({ + map: resultMap, + position: realPosition, + clickable: true, + draggable: false + }); + resultMarkers.guess = new google.maps.Marker({ + map: resultMap, + position: guessedPosition, + clickable: false, + draggable: false, + label: { + color: '#ffffff', + fontFamily: 'Roboto', + fontSize: '18px', + fontWeight: '500', + text: '?' + } + }); + + resultMarkers.real.addListener('click', function () { + window.open('https://www.google.com/maps/search/?api=1&query=' + realPosition.lat + ',' + realPosition.lng, '_blank'); + }); + + document.getElementById('distance').innerHTML = Util.printDistanceForHuman(distance); +} + +document.getElementById('continueButton').onclick = function () { + resultMarkers.real.setMap(null); + resultMarkers.real = null; + resultMarkers.guess.setMap(null); + resultMarkers.guess = null; + + document.getElementById('guess').style.visibility = 'visible'; + document.getElementById('result').style.visibility = 'hidden'; + guessMap.fitBounds(guessMapBounds); getNewPosition(); diff --git a/views/game.php b/views/game.php index 2c96a51..4246a94 100644 --- a/views/game.php +++ b/views/game.php @@ -3,7 +3,8 @@ MapGuesser - + +
@@ -13,6 +14,13 @@ +
+
+
+

You were close.

+ +
+
From e15421af8bcd93eb1cb59867943f0cb5bae572d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Tue, 19 May 2020 19:01:35 +0200 Subject: [PATCH 17/32] MAPG-6 introduce and unify geo calculations --- public/static/js/mapguesser.js | 15 +++++++-------- src/Util/Geo/Bounds.php | 16 +++++++++++++++- src/Util/Geo/Position.php | 20 ++++++++++++++++++++ 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/public/static/js/mapguesser.js b/public/static/js/mapguesser.js index 2b8090a..20c5368 100644 --- a/public/static/js/mapguesser.js +++ b/public/static/js/mapguesser.js @@ -11,15 +11,14 @@ var Util = { var lat2 = Math.deg2rad(position2.lat); var lng2 = Math.deg2rad(position2.lng); - var latDelta = lat2 - lat1; - var lonDelta = lng2 - lng1; + var angleCos = Math.cos(lat1) * Math.cos(lat2) * Math.cos(lng2 - lng1) + + Math.sin(lat1) * Math.sin(lat2); - var angle = 2 * Math.asin( - Math.sqrt( - Math.pow(Math.sin(latDelta / 2), 2) + - Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(lonDelta / 2), 2) - ) - ); + if (angleCos > 1.0) { + angleCos = 1.0; + } + + var angle = Math.acos(angleCos); return angle * Util.EARTH_RADIUS_IN_METER; }, diff --git a/src/Util/Geo/Bounds.php b/src/Util/Geo/Bounds.php index b34d583..c77d2b1 100644 --- a/src/Util/Geo/Bounds.php +++ b/src/Util/Geo/Bounds.php @@ -2,6 +2,8 @@ class Bounds { + const ONE_DEGREE_OF_LATITUDE_IN_METER = 111132.954; + private float $southLat; private float $westLng; @@ -19,7 +21,7 @@ class Bounds return $instance; } - public static function createDirectly(float $southLat, $westLng, $northLat, $eastLng): Bounds + public static function createDirectly(float $southLat, float $westLng, float $northLat, float $eastLng): Bounds { $instance = new static(); @@ -61,6 +63,18 @@ class Bounds } } + public function calculateApproximateArea(): float + { + $dLat = $this->northLat - $this->southLat; + $dLng = $this->eastLng - $this->westLng; + + $m = $dLat * static::ONE_DEGREE_OF_LATITUDE_IN_METER; + $a = $dLng * static::ONE_DEGREE_OF_LATITUDE_IN_METER * cos(deg2rad($this->northLat)); + $c = $dLng * static::ONE_DEGREE_OF_LATITUDE_IN_METER * cos(deg2rad($this->southLat)); + + return $m * ($a + $c) / 2; + } + public function toJson(): string { if (!$this->initialized) { diff --git a/src/Util/Geo/Position.php b/src/Util/Geo/Position.php index 2ac9fc2..33eb0cd 100644 --- a/src/Util/Geo/Position.php +++ b/src/Util/Geo/Position.php @@ -2,6 +2,8 @@ class Position { + const EARTH_RADIUS_IN_METER = 6371000; + private float $lat; private float $lng; @@ -21,6 +23,24 @@ class Position return $this->lng; } + public function calculateDistanceTo(Position $otherPosition): float + { + $lat1 = deg2rad($this->lat); + $lng1 = deg2rad($this->lng); + $lat2 = deg2rad($otherPosition->lat); + $lng2 = deg2rad($otherPosition->lng); + + $angleCos = cos($lat1) * cos($lat2) * cos($lng2 - $lng1) + sin($lat1) * sin($lat2); + + if ($angleCos > 1.0) { + $angleCos = 1.0; + } + + $angle = acos($angleCos); + + return $angle * static::EARTH_RADIUS_IN_METER; + } + public function toArray(): array { return [ From 916995161f46e84b8e9c66e65ef6d28e20bf495c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Wed, 20 May 2020 01:05:46 +0200 Subject: [PATCH 18/32] MAPG-6 add score element to game HTML --- views/game.php | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/views/game.php b/views/game.php index 4246a94..370aa66 100644 --- a/views/game.php +++ b/views/game.php @@ -17,11 +17,22 @@
-

You were close.

- +
+

You were close.

+
+
+

You earned points.

+
+
+
+
+
+ +
From 2991dfbe27bcd2e4e3147221aa90e4809cb52562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Wed, 20 May 2020 01:07:01 +0200 Subject: [PATCH 19/32] MAPG-6 add styles for score and result --- public/static/css/mapguesser.css | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/public/static/css/mapguesser.css b/public/static/css/mapguesser.css index d271969..0c8b591 100644 --- a/public/static/css/mapguesser.css +++ b/public/static/css/mapguesser.css @@ -75,7 +75,7 @@ button:disabled { transition-delay: 0s; } -#guess #guessMap { +#guess > #guessMap { height: 115px; width: 100%; transition-property: height; @@ -84,7 +84,7 @@ button:disabled { border-radius: 3px; } -#guess:hover #guessMap { +#guess:hover > #guessMap { height: 315px; transition-delay: 0s; } @@ -122,12 +122,33 @@ button:disabled { #resultInfo { height: 30%; width: 100%; - padding: 20px; + padding: 10px 20px; text-align: center; box-sizing: border-box; } +#resultInfo > div { + height: 25%; + width: 100%; + display: flex; + justify-content: center; + align-items: center; +} + #resultInfo p { - margin-bottom: 20px; font-size: 24px; } + +#scoreBarBase { + height: 20px; + width: 60%; + margin: 0 auto; + background-color: #eeeeee; + border-radius: 3px; +} + +#scoreBar { + height: 100%; + transition-property: width; + transition-duration: 1.0s; +} From 00ac5e842f2e42b3a596f507d7417bfd09bcd96f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Wed, 20 May 2020 01:08:01 +0200 Subject: [PATCH 20/32] MAPG-6 add score calculation to JS --- public/static/js/mapguesser.js | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/public/static/js/mapguesser.js b/public/static/js/mapguesser.js index 20c5368..4f5dc35 100644 --- a/public/static/js/mapguesser.js +++ b/public/static/js/mapguesser.js @@ -5,6 +5,8 @@ Math.deg2rad = function (deg) { var Util = { EARTH_RADIUS_IN_METER: 6371000, + MAX_SCORE: 1000, + calculateDistance: function (position1, position2) { var lat1 = Math.deg2rad(position1.lat); var lng1 = Math.deg2rad(position1.lng); @@ -33,6 +35,27 @@ var Util = { } else { return Number.parseFloat(distance / 1000).toFixed(0) + ' km'; } + }, + + calculateScore: function (distance) { + var goodness = 1.0 - distance / Math.sqrt(mapArea); + + return Math.pow(this.MAX_SCORE, goodness); + }, + + calculateScoreBarProperties: function (score) { + var percent = Math.round((score / this.MAX_SCORE) * 100); + + var color; + if (percent >= 90) { + color = '#11ca00'; + } else if (percent >= 10) { + color = '#ea9000'; + } else { + color = '#ca1100'; + } + + return { width: percent + '%', backgroundColor: color }; } }; @@ -189,9 +212,20 @@ document.getElementById('guessButton').onclick = function () { }); document.getElementById('distance').innerHTML = Util.printDistanceForHuman(distance); + + var score = Util.calculateScore(distance); + var scoreBarProperties = Util.calculateScoreBarProperties(score); + + document.getElementById('score').innerHTML = Number.parseFloat(score).toFixed(0); + + var scoreBar = document.getElementById('scoreBar'); + scoreBar.style.backgroundColor = scoreBarProperties.backgroundColor; + scoreBar.style.width = scoreBarProperties.width; } document.getElementById('continueButton').onclick = function () { + document.getElementById('scoreBar').style.width = '0'; + resultMarkers.real.setMap(null); resultMarkers.real = null; resultMarkers.guess.setMap(null); From 066afa6e2e39d98b97f9c400be94bbddcb6e99aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Wed, 20 May 2020 01:54:14 +0200 Subject: [PATCH 21/32] MAPG-26 remove "async defer" from - - + + From bccd02ce30d7d9a44ec989037fa9dab4cd1a3b7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Wed, 20 May 2020 02:10:47 +0200 Subject: [PATCH 22/32] MAPG-27 reset pov before loading the next pano --- public/static/js/mapguesser.js | 1 + 1 file changed, 1 insertion(+) diff --git a/public/static/js/mapguesser.js b/public/static/js/mapguesser.js index 4f5dc35..3e29009 100644 --- a/public/static/js/mapguesser.js +++ b/public/static/js/mapguesser.js @@ -162,6 +162,7 @@ function loadPano(data, status) { return; } + panorama.setPov({heading: 0, pitch: 0, zoom: 1}); panorama.setPano(data.location.pano); } From c0ad64cbe546aebd3a25846323154e242b79b2b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Wed, 20 May 2020 03:19:53 +0200 Subject: [PATCH 23/32] MAPG-7 refactor JS to be more readable load Google Maps API async --- public/static/js/mapguesser.js | 360 ++++++++++++++++----------------- views/game.php | 2 +- 2 files changed, 181 insertions(+), 181 deletions(-) diff --git a/public/static/js/mapguesser.js b/public/static/js/mapguesser.js index 3e29009..759f25c 100644 --- a/public/static/js/mapguesser.js +++ b/public/static/js/mapguesser.js @@ -1,109 +1,134 @@ -Math.deg2rad = function (deg) { - return deg * (this.PI / 180.0); -}; +(function () { + var Core = { + MAX_SCORE: 1000, -var Util = { - EARTH_RADIUS_IN_METER: 6371000, + realPosition: null, + panorama: null, + guessMap: null, + guessMarker: null, + resultMap: null, + resultMarkers: { guess: null, real: null }, + googleLink: null, - MAX_SCORE: 1000, + getNewPosition: function () { + var xhr = new XMLHttpRequest(); + xhr.responseType = 'json'; + xhr.onreadystatechange = function () { + if (this.readyState == 4 && this.status == 200) { + Core.realPosition = this.response.position; - calculateDistance: function (position1, position2) { - var lat1 = Math.deg2rad(position1.lat); - var lng1 = Math.deg2rad(position1.lng); - var lat2 = Math.deg2rad(position2.lat); - var lng2 = Math.deg2rad(position2.lng); + var sv = new google.maps.StreetViewService(); + sv.getPanorama({ location: this.response.position, preference: google.maps.StreetViewPreference.NEAREST }, Core.loadPano); + } + }; + xhr.open('GET', 'getNewPosition.json', true); + xhr.send(); + }, - var angleCos = Math.cos(lat1) * Math.cos(lat2) * Math.cos(lng2 - lng1) + - Math.sin(lat1) * Math.sin(lat2); + loadPano: function (data, status) { + if (status !== google.maps.StreetViewStatus.OK) { + Core.getNewPosition(); + return; + } - if (angleCos > 1.0) { - angleCos = 1.0; - } + Core.panorama.setPov({ heading: 0, pitch: 0, zoom: 1 }); + Core.panorama.setPano(data.location.pano); + }, - var angle = Math.acos(angleCos); + calculateScore: function (distance) { + var goodness = 1.0 - distance / Math.sqrt(mapArea); - return angle * Util.EARTH_RADIUS_IN_METER; - }, + return Math.pow(Core.MAX_SCORE, goodness); + }, - printDistanceForHuman: function (distance) { - if (distance < 1000) { - return Number.parseFloat(distance).toFixed(0) + ' m'; - } else if (distance < 10000) { - return Number.parseFloat(distance / 1000).toFixed(2) + ' km'; - } else if (distance < 100000) { - return Number.parseFloat(distance / 1000).toFixed(1) + ' km'; - } else { - return Number.parseFloat(distance / 1000).toFixed(0) + ' km'; - } - }, + calculateScoreBarProperties: function (score) { + var percent = Math.round((score / Core.MAX_SCORE) * 100); - calculateScore: function (distance) { - var goodness = 1.0 - distance / Math.sqrt(mapArea); + var color; + if (percent >= 90) { + color = '#11ca00'; + } else if (percent >= 10) { + color = '#ea9000'; + } else { + color = '#ca1100'; + } - return Math.pow(this.MAX_SCORE, goodness); - }, + return { width: percent + '%', backgroundColor: color }; + }, - calculateScoreBarProperties: function (score) { - var percent = Math.round((score / this.MAX_SCORE) * 100); - - var color; - if (percent >= 90) { - color = '#11ca00'; - } else if (percent >= 10) { - color = '#ea9000'; - } else { - color = '#ca1100'; - } - - return { width: percent + '%', backgroundColor: color }; - } -}; - -var MapManipulator = { - rewriteGoogleLink: function () { - if (!googleLink) { - var anchors = document.getElementById('panorama').getElementsByTagName('a'); - for (var i = 0; i < anchors.length; i++) { - var a = anchors[i]; - if (a.href.indexOf('maps.google.com/maps') !== -1) { - googleLink = a; - break; + rewriteGoogleLink: function () { + if (!Core.googleLink) { + var anchors = document.getElementById('panorama').getElementsByTagName('a'); + for (var i = 0; i < anchors.length; i++) { + var a = anchors[i]; + if (a.href.indexOf('maps.google.com/maps') !== -1) { + Core.googleLink = a; + break; + } } } + + setTimeout(function () { + Core.googleLink.title = 'Google Maps' + Core.googleLink.href = 'https://maps.google.com/maps' + }, 1); } + }; - setTimeout(function () { - googleLink.title = 'Google Maps' - googleLink.href = 'https://maps.google.com/maps' - }, 1); - } -}; + var Util = { + EARTH_RADIUS_IN_METER: 6371000, -var realPosition; -var panorama; -var guessMap; -var guessMarker; -var resultMap; -var resultMarkers = { guess: null, real: null }; -var googleLink; + deg2rad: function (deg) { + return deg * (Math.PI / 180.0); + }, -function initialize() { - guessMap = new google.maps.Map(document.getElementById('guessMap'), { + 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'; + } else if (distance < 10000) { + return Number.parseFloat(distance / 1000).toFixed(2) + ' km'; + } else if (distance < 100000) { + return Number.parseFloat(distance / 1000).toFixed(1) + ' km'; + } else { + return Number.parseFloat(distance / 1000).toFixed(0) + ' km'; + } + } + }; + + Core.guessMap = new google.maps.Map(document.getElementById('guessMap'), { disableDefaultUI: true, clickableIcons: false, draggableCursor: 'crosshair' }); - guessMap.fitBounds(guessMapBounds); + Core.guessMap.fitBounds(guessMapBounds); - guessMap.addListener('click', function (e) { - if (guessMarker) { - guessMarker.setPosition(e.latLng); + Core.guessMap.addListener('click', function (e) { + if (Core.guessMarker) { + Core.guessMarker.setPosition(e.latLng); return; } - guessMarker = new google.maps.Marker({ - map: guessMap, + Core.guessMarker = new google.maps.Marker({ + map: Core.guessMap, position: e.latLng, clickable: false, draggable: true, @@ -119,123 +144,98 @@ function initialize() { document.getElementById('guessButton').disabled = false; }); - panorama = new google.maps.StreetViewPanorama(document.getElementById('panorama'), { + Core.panorama = new google.maps.StreetViewPanorama(document.getElementById('panorama'), { disableDefaultUI: true, linksControl: true, showRoadLabels: false }); - panorama.addListener('position_changed', function () { - MapManipulator.rewriteGoogleLink(); + Core.panorama.addListener('position_changed', function () { + Core.rewriteGoogleLink(); }); - panorama.addListener('pov_changed', function () { - MapManipulator.rewriteGoogleLink(); + Core.panorama.addListener('pov_changed', function () { + Core.rewriteGoogleLink(); }); - resultMap = new google.maps.Map(document.getElementById('resultMap'), { + Core.resultMap = new google.maps.Map(document.getElementById('resultMap'), { disableDefaultUI: true, clickableIcons: false, }); - getNewPosition(); -} + Core.getNewPosition(); -function getNewPosition() { - var xhr = new XMLHttpRequest(); - xhr.responseType = 'json'; - xhr.onreadystatechange = function () { - if (this.readyState == 4 && this.status == 200) { - realPosition = this.response.position; - - var sv = new google.maps.StreetViewService(); - sv.getPanorama({ location: this.response.position, preference: google.maps.StreetViewPreference.NEAREST }, loadPano); + document.getElementById('guessButton').onclick = function () { + if (!Core.guessMarker) { + return; } - }; - xhr.open('GET', 'getNewPosition.json', true); - xhr.send(); -} -function loadPano(data, status) { - if (status !== google.maps.StreetViewStatus.OK) { - getNewPosition(); - return; + var guessedPosition = Core.guessMarker.getPosition(); + + this.disabled = true; + Core.guessMarker.setMap(null); + Core.guessMarker = null; + + var distance = Util.calculateDistance(Core.realPosition, { lat: guessedPosition.lat(), lng: guessedPosition.lng() }); + + document.getElementById('guess').style.visibility = 'hidden'; + document.getElementById('result').style.visibility = 'visible'; + + var resultBounds = new google.maps.LatLngBounds(); + resultBounds.extend(Core.realPosition); + resultBounds.extend(guessedPosition); + + Core.resultMap.fitBounds(resultBounds); + + Core.resultMarkers.real = new google.maps.Marker({ + map: Core.resultMap, + position: Core.realPosition, + clickable: true, + draggable: false + }); + Core.resultMarkers.guess = new google.maps.Marker({ + map: Core.resultMap, + position: guessedPosition, + clickable: false, + draggable: false, + label: { + color: '#ffffff', + fontFamily: 'Roboto', + fontSize: '18px', + fontWeight: '500', + text: '?' + } + }); + + Core.resultMarkers.real.addListener('click', function () { + window.open('https://www.google.com/maps/search/?api=1&query=' + Core.realPosition.lat + ',' + Core.realPosition.lng, '_blank'); + }); + + document.getElementById('distance').innerHTML = Util.printDistanceForHuman(distance); + + var score = Core.calculateScore(distance); + var scoreBarProperties = Core.calculateScoreBarProperties(score); + + document.getElementById('score').innerHTML = Number.parseFloat(score).toFixed(0); + + var scoreBar = document.getElementById('scoreBar'); + scoreBar.style.backgroundColor = scoreBarProperties.backgroundColor; + scoreBar.style.width = scoreBarProperties.width; } - panorama.setPov({heading: 0, pitch: 0, zoom: 1}); - panorama.setPano(data.location.pano); -} + document.getElementById('continueButton').onclick = function () { + document.getElementById('scoreBar').style.width = '0'; -document.getElementById('guessButton').onclick = function () { - if (!guessMarker) { - return; + Core.resultMarkers.real.setMap(null); + Core.resultMarkers.real = null; + Core.resultMarkers.guess.setMap(null); + Core.resultMarkers.guess = null; + + document.getElementById('guess').style.visibility = 'visible'; + document.getElementById('result').style.visibility = 'hidden'; + + Core.guessMap.fitBounds(guessMapBounds); + + Core.getNewPosition(); } - - var guessedPosition = guessMarker.getPosition(); - - this.disabled = true; - guessMarker.setMap(null); - guessMarker = null; - - var distance = Util.calculateDistance(realPosition, { lat: guessedPosition.lat(), lng: guessedPosition.lng() }); - - document.getElementById('guess').style.visibility = 'hidden'; - document.getElementById('result').style.visibility = 'visible'; - - var resultBounds = new google.maps.LatLngBounds(); - resultBounds.extend(realPosition); - resultBounds.extend(guessedPosition); - - resultMap.fitBounds(resultBounds); - - resultMarkers.real = new google.maps.Marker({ - map: resultMap, - position: realPosition, - clickable: true, - draggable: false - }); - resultMarkers.guess = new google.maps.Marker({ - map: resultMap, - position: guessedPosition, - clickable: false, - draggable: false, - label: { - color: '#ffffff', - fontFamily: 'Roboto', - fontSize: '18px', - fontWeight: '500', - text: '?' - } - }); - - resultMarkers.real.addListener('click', function () { - window.open('https://www.google.com/maps/search/?api=1&query=' + realPosition.lat + ',' + realPosition.lng, '_blank'); - }); - - document.getElementById('distance').innerHTML = Util.printDistanceForHuman(distance); - - var score = Util.calculateScore(distance); - var scoreBarProperties = Util.calculateScoreBarProperties(score); - - document.getElementById('score').innerHTML = Number.parseFloat(score).toFixed(0); - - var scoreBar = document.getElementById('scoreBar'); - scoreBar.style.backgroundColor = scoreBarProperties.backgroundColor; - scoreBar.style.width = scoreBarProperties.width; -} - -document.getElementById('continueButton').onclick = function () { - document.getElementById('scoreBar').style.width = '0'; - - resultMarkers.real.setMap(null); - resultMarkers.real = null; - resultMarkers.guess.setMap(null); - resultMarkers.guess = null; - - document.getElementById('guess').style.visibility = 'visible'; - document.getElementById('result').style.visibility = 'hidden'; - - guessMap.fitBounds(guessMapBounds); - - getNewPosition(); -} +})(); diff --git a/views/game.php b/views/game.php index 333ce16..ee1fb8b 100644 --- a/views/game.php +++ b/views/game.php @@ -35,7 +35,7 @@ var mapArea = calculateApproximateArea() ?>; var guessMapBounds = toJson() ?>; + - From 2c9a3ec23c554e7dabfaa6e39b9401f3b3461ddd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Wed, 20 May 2020 03:26:51 +0200 Subject: [PATCH 24/32] MAPG-7 install nodejs, uglify-js and clean-css into the Docker image cleanup Docker files --- docker-compose.yml | 1 - docker/Dockerfile | 10 ++++++---- docker/scripts/install-composer.sh | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index e00f9aa..3effcfe 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,7 +13,6 @@ services: volumes: - mysql:/var/lib/mysql environment: - #TZ: Europe/Budapest MYSQL_ROOT_PASSWORD: 'root' MYSQL_DATABASE: 'mapguesser' MYSQL_USER: 'mapguesser' diff --git a/docker/Dockerfile b/docker/Dockerfile index 16f7048..739994b 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -7,10 +7,6 @@ RUN apt update RUN apt install -y curl git apache2 \ php-apcu php-xdebug php7.4-cli php7.4-fpm php7.4-mbstring php7.4-mysql php7.4-zip -# Configure tzdata -#RUN ln -fs /usr/share/zoneinfo/Europe/Budapest /etc/localtime -#RUN dpkg-reconfigure --frontend noninteractive tzdata - # Configure Apache with PHP RUN mkdir -p /run/php RUN a2enmod proxy_fcgi rewrite @@ -24,6 +20,12 @@ RUN echo "xdebug.remote_connect_back = 1" >> /etc/php/7.4/mods-available/xdebug. COPY scripts/install-composer.sh install-composer.sh RUN ./install-composer.sh +# Install Node.js and required packages +RUN curl -sL https://deb.nodesource.com/setup_14.x | bash - +RUN apt install -y nodejs +RUN npm install -g uglify-js +RUN npm install -g clean-css-cli + EXPOSE 80 VOLUME /var/www/mapguesser WORKDIR /var/www/mapguesser diff --git a/docker/scripts/install-composer.sh b/docker/scripts/install-composer.sh index f55d62e..65dbc88 100755 --- a/docker/scripts/install-composer.sh +++ b/docker/scripts/install-composer.sh @@ -1,6 +1,6 @@ #!/bin/sh -EXPECTED_CHECKSUM="$(curl -s https://composer.github.io/installer.sig)" +EXPECTED_CHECKSUM="$(curl -sL https://composer.github.io/installer.sig)" php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" ACTUAL_CHECKSUM="$(php -r "echo hash_file('sha384', 'composer-setup.php');")" From 0bf93f7509c3795fe8458dc2b6643a69800b0997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Wed, 20 May 2020 13:06:40 +0200 Subject: [PATCH 25/32] MAPG-7 redirect / to /game --- public/index.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/public/index.php b/public/index.php index 6f246a3..ee4c162 100644 --- a/public/index.php +++ b/public/index.php @@ -3,6 +3,7 @@ require '../main.php'; // very basic routing +$host = $_SERVER["REQUEST_SCHEME"] . '://' . $_SERVER["SERVER_NAME"]; $url = $_SERVER['REQUEST_URI']; switch($url) { case '/game': @@ -11,6 +12,9 @@ switch($url) { case '/getNewPosition.json': $controller = new MapGuesser\Controller\GetNewPosition(); break; + case '/': + header('Location: ' . $host . '/game', true, 302); + die; default: echo 'Error 404'; die; From ba01767c6c20bbcc6206f78667f7b92bbedc21f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Wed, 20 May 2020 13:42:38 +0200 Subject: [PATCH 26/32] MAPG-7 implement install and update scripts minor adaptations for paths, environment variables and Dockerfile --- .env.example | 2 +- .gitignore | 1 + install/db.sql => db/mapguesser.sql | 0 docker/Dockerfile | 2 +- scripts/install.sh | 23 +++++++++++++++++++++++ scripts/update.sh | 12 ++++++++++++ 6 files changed, 38 insertions(+), 2 deletions(-) rename install/db.sql => db/mapguesser.sql (100%) create mode 100755 scripts/install.sh create mode 100755 scripts/update.sh diff --git a/.env.example b/.env.example index 4cd7025..097e309 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,4 @@ -DEV=true +DEV=1 DB_HOST=mariadb DB_USER=mapguesser DB_PASSWORD=mapguesser diff --git a/.gitignore b/.gitignore index 226ca36..9bdf82a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .env +installed vendor diff --git a/install/db.sql b/db/mapguesser.sql similarity index 100% rename from install/db.sql rename to db/mapguesser.sql diff --git a/docker/Dockerfile b/docker/Dockerfile index 739994b..1d55371 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -4,7 +4,7 @@ ENV DEBIAN_FRONTEND noninteractive # Install Apache, PHP and further necessary packages RUN apt update -RUN apt install -y curl git apache2 \ +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 # Configure Apache with PHP diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 0000000..ce97ff9 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,23 @@ +#/bin/bash + +ROOT_DIR=$(dirname $(readlink -f "$0"))/.. + +. ${ROOT_DIR}/.env + +if [ -f ${ROOT_DIR}/installed ]; then + echo "Mapguesser is already installed! To force reinstall, delete file 'installed' from the root directory!" + exit 1 +fi + +echo "Installing MapGuesser DB..." + +mysql --host=${DB_HOST} --user=${DB_USER} --password=${DB_PASSWORD} ${DB_NAME} < ${ROOT_DIR}/db/mapguesser.sql + +if [ -z "${DEV}" ] || [ "${DEV}" -eq "0" ]; then + echo "Uglifying JS and CSS files..." + + uglifyjs ${ROOT_DIR}/public/static/js/mapguesser.js -c -m -o ${ROOT_DIR}/public/static/js/mapguesser.js + cleancss ${ROOT_DIR}/public/static/css/mapguesser.css -o ${ROOT_DIR}/public/static/css/mapguesser.css +fi + +touch ${ROOT_DIR}/installed diff --git a/scripts/update.sh b/scripts/update.sh new file mode 100755 index 0000000..56e2c1d --- /dev/null +++ b/scripts/update.sh @@ -0,0 +1,12 @@ +#/bin/bash + +ROOT_DIR=$(dirname $(readlink -f "$0"))/.. + +. ${ROOT_DIR}/.env + +if [ -z "${DEV}" ] || [ "${DEV}" -eq "0" ]; then + echo "Uglifying JS and CSS files..." + + uglifyjs ${ROOT_DIR}/public/static/js/mapguesser.js -c -m -o ${ROOT_DIR}/public/static/js/mapguesser.js + cleancss ${ROOT_DIR}/public/static/css/mapguesser.css -o ${ROOT_DIR}/public/static/css/mapguesser.css +fi From 82f69664c1b3f331912942d100a7dd794d2829ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Wed, 20 May 2020 14:59:48 +0200 Subject: [PATCH 27/32] MAPG-32 use Math.ceil instead of Math.round for scorebar --- public/static/js/mapguesser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/static/js/mapguesser.js b/public/static/js/mapguesser.js index 759f25c..10043ae 100644 --- a/public/static/js/mapguesser.js +++ b/public/static/js/mapguesser.js @@ -42,7 +42,7 @@ }, calculateScoreBarProperties: function (score) { - var percent = Math.round((score / Core.MAX_SCORE) * 100); + var percent = Math.ceil((score / Core.MAX_SCORE) * 100); var color; if (percent >= 90) { From 961e6e23802198668c6b56b78d1517b3ca0376d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Wed, 20 May 2020 15:00:11 +0200 Subject: [PATCH 28/32] MAPG-32 use default width (0) for scorebar --- public/static/css/mapguesser.css | 1 + 1 file changed, 1 insertion(+) diff --git a/public/static/css/mapguesser.css b/public/static/css/mapguesser.css index 0c8b591..19f7fae 100644 --- a/public/static/css/mapguesser.css +++ b/public/static/css/mapguesser.css @@ -149,6 +149,7 @@ button:disabled { #scoreBar { height: 100%; + width: 0; transition-property: width; transition-duration: 1.0s; } From f87ca60122106f1e3f0edc921508ee89aa917d5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Wed, 20 May 2020 15:10:55 +0200 Subject: [PATCH 29/32] MAPG-33 fix shebang in deployment scripts --- scripts/install.sh | 2 +- scripts/update.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/install.sh b/scripts/install.sh index ce97ff9..317b5f5 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -1,4 +1,4 @@ -#/bin/bash +#!/bin/bash ROOT_DIR=$(dirname $(readlink -f "$0"))/.. diff --git a/scripts/update.sh b/scripts/update.sh index 56e2c1d..2c435a5 100755 --- a/scripts/update.sh +++ b/scripts/update.sh @@ -1,4 +1,4 @@ -#/bin/bash +#!/bin/bash ROOT_DIR=$(dirname $(readlink -f "$0"))/.. From 310f36e4423ce31fabb9e34495878ff8a48e50b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Wed, 20 May 2020 15:52:55 +0200 Subject: [PATCH 30/32] MAPG-19 store gif files in LFS --- .gitattributes | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..fe357d6 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +*.gif filter=lfs diff=lfs merge=lfs -text + From 51be16395e716567717e17adc62e1a5f37cdf766 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Wed, 20 May 2020 15:53:14 +0200 Subject: [PATCH 31/32] MAPG-19 add loading.gif --- public/static/img/loading.gif | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 public/static/img/loading.gif diff --git a/public/static/img/loading.gif b/public/static/img/loading.gif new file mode 100644 index 0000000..6752946 --- /dev/null +++ b/public/static/img/loading.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d4400fe604ac37f8a9f7ce77f645e89645166347976f8233a0852c2ccb0a24f0 +size 27593 From f647e773663cdee96f4c0f49d4490cd0b0a2ce39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Wed, 20 May 2020 15:54:09 +0200 Subject: [PATCH 32/32] MAPG-19 show loading gif during loading --- public/static/css/mapguesser.css | 14 +++++++++++++- public/static/js/mapguesser.js | 7 +++++++ views/game.php | 3 +++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/public/static/css/mapguesser.css b/public/static/css/mapguesser.css index 19f7fae..d22455b 100644 --- a/public/static/css/mapguesser.css +++ b/public/static/css/mapguesser.css @@ -48,6 +48,18 @@ button:disabled { opacity: 0.7; } +#loading { + position: absolute; + width: 40px; + height: 40px; + top: 50%; + left: 50%; + margin-top: -20px; + margin-left: -20px; + z-index: 2; + visibility: visible; +} + #panorama { height: 100%; width: 100%; @@ -151,5 +163,5 @@ button:disabled { height: 100%; width: 0; transition-property: width; - transition-duration: 1.0s; + transition-duration: 2.0s; } diff --git a/public/static/js/mapguesser.js b/public/static/js/mapguesser.js index 10043ae..a405aee 100644 --- a/public/static/js/mapguesser.js +++ b/public/static/js/mapguesser.js @@ -11,6 +11,10 @@ googleLink: null, getNewPosition: function () { + Core.panorama.setVisible(false); + + document.getElementById('loading').style.visibility = 'visible'; + var xhr = new XMLHttpRequest(); xhr.responseType = 'json'; xhr.onreadystatechange = function () { @@ -31,6 +35,9 @@ return; } + document.getElementById('loading').style.visibility = 'hidden'; + + Core.panorama.setVisible(true); Core.panorama.setPov({ heading: 0, pitch: 0, zoom: 1 }); Core.panorama.setPano(data.location.pano); }, diff --git a/views/game.php b/views/game.php index ee1fb8b..1aa5b7a 100644 --- a/views/game.php +++ b/views/game.php @@ -7,6 +7,9 @@ +
+ +