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-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..1d55371 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -4,13 +4,9 @@ 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 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');")" 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; 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/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 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() ?>; + -