MAPG-2 Init project MapGuesser

* add composer.json and basic environment
 * add basic classes
 * add basic map with JS and CSS (with demo functionality)
 * add files for docker-compose
 * add launch.json for Docker debug
 * add README
This commit is contained in:
Bence Pőcze 2020-05-17 19:29:06 +02:00
commit a61d2951d7
16 changed files with 692 additions and 0 deletions

2
.env.example Normal file
View File

@ -0,0 +1,2 @@
DEV=true
GOOGLE_MAPS_JS_API_KEY=your_google_maps_js_api_key

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.env
vendor

14
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,14 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Listen for XDebug in Docker",
"type": "php",
"request": "launch",
"port": 9000,
"pathMappings": {
"/var/www/mapguesser": "${workspaceRoot}",
}
}
]
}

5
README.md Normal file
View File

@ -0,0 +1,5 @@
# MapGuesser
This is the MapGuesser Application project.
License: GNU GPL 3.0

20
composer.json Normal file
View File

@ -0,0 +1,20 @@
{
"name": "esoko/mapguesser",
"type": "project",
"description": "MapGuesser Application",
"license": "GNU GPL 3.0",
"require": {
"vlucas/phpdotenv": "^4.1"
},
"require-dev": {},
"autoload": {
"psr-4": {
"MapGuesser\\": "src"
}
},
"scripts": {
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
]
}
}

220
composer.lock generated Normal file
View File

@ -0,0 +1,220 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "29431cf83ee884f01ee954b1068c0ccc",
"packages": [
{
"name": "phpoption/phpoption",
"version": "1.7.3",
"source": {
"type": "git",
"url": "https://github.com/schmittjoh/php-option.git",
"reference": "4acfd6a4b33a509d8c88f50e5222f734b6aeebae"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/schmittjoh/php-option/zipball/4acfd6a4b33a509d8c88f50e5222f734b6aeebae",
"reference": "4acfd6a4b33a509d8c88f50e5222f734b6aeebae",
"shasum": ""
},
"require": {
"php": "^5.5.9 || ^7.0 || ^8.0"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.3",
"phpunit/phpunit": "^4.8.35 || ^5.0 || ^6.0 || ^7.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.7-dev"
}
},
"autoload": {
"psr-4": {
"PhpOption\\": "src/PhpOption/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Johannes M. Schmitt",
"email": "schmittjoh@gmail.com"
},
{
"name": "Graham Campbell",
"email": "graham@alt-three.com"
}
],
"description": "Option Type for PHP",
"keywords": [
"language",
"option",
"php",
"type"
],
"time": "2020-03-21T18:07:53+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.17.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e94c8b1bbe2bc77507a1056cdb06451c75b427f9",
"reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"suggest": {
"ext-ctype": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.17-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Ctype\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Gert de Pagter",
"email": "BackEndTea@gmail.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for ctype functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"ctype",
"polyfill",
"portable"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-05-12T16:14:59+00:00"
},
{
"name": "vlucas/phpdotenv",
"version": "v4.1.5",
"source": {
"type": "git",
"url": "https://github.com/vlucas/phpdotenv.git",
"reference": "539bb6927c101a5605d31d11a2d17185a2ce2bf1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/539bb6927c101a5605d31d11a2d17185a2ce2bf1",
"reference": "539bb6927c101a5605d31d11a2d17185a2ce2bf1",
"shasum": ""
},
"require": {
"php": "^5.5.9 || ^7.0 || ^8.0",
"phpoption/phpoption": "^1.7.2",
"symfony/polyfill-ctype": "^1.9"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.3",
"ext-filter": "*",
"ext-pcre": "*",
"phpunit/phpunit": "^4.8.35 || ^5.0 || ^6.0 || ^7.0"
},
"suggest": {
"ext-filter": "Required to use the boolean validator.",
"ext-pcre": "Required to use most of the library."
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.1-dev"
}
},
"autoload": {
"psr-4": {
"Dotenv\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Graham Campbell",
"email": "graham@alt-three.com",
"homepage": "https://gjcampbell.co.uk/"
},
{
"name": "Vance Lucas",
"email": "vance@vancelucas.com",
"homepage": "https://vancelucas.com/"
}
],
"description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
"keywords": [
"dotenv",
"env",
"environment"
],
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv",
"type": "tidelift"
}
],
"time": "2020-05-02T14:08:57+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"plugin-api-version": "1.1.0"
}

22
docker-compose.yml Normal file
View File

@ -0,0 +1,22 @@
version: '3'
services:
app:
build: ./docker
ports:
- 80:80
volumes:
- .:/var/www/mapguesser
links:
- 'mariadb'
mariadb:
image: mariadb:10.1
volumes:
- mysql:/var/lib/mysql
environment:
#TZ: Europe/Budapest
MYSQL_ROOT_PASSWORD: 'root'
MYSQL_DATABASE: 'mapguesser'
MYSQL_USER: 'mapguesser'
MYSQL_PASSWORD: 'mapguesser'
volumes:
mysql:

31
docker/Dockerfile Normal file
View File

@ -0,0 +1,31 @@
FROM ubuntu:focal
ENV DEBIAN_FRONTEND noninteractive
# Install Apache, PHP and further necessary packages
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
RUN a2enconf php7.4-fpm
COPY configs/apache.conf /etc/apache2/sites-available/000-default.conf
RUN echo "xdebug.remote_enable = 1" >> /etc/php/7.4/mods-available/xdebug.ini
RUN echo "xdebug.remote_autostart = 1" >> /etc/php/7.4/mods-available/xdebug.ini
RUN echo "xdebug.remote_connect_back = 1" >> /etc/php/7.4/mods-available/xdebug.ini
# Install Composer
COPY scripts/install-composer.sh install-composer.sh
RUN ./install-composer.sh
EXPOSE 80
VOLUME /var/www/mapguesser
WORKDIR /var/www/mapguesser
ENTRYPOINT /usr/sbin/php-fpm7.4 -F & /usr/sbin/apache2ctl -DFOREGROUND

View File

@ -0,0 +1,15 @@
<VirtualHost *:80>
ServerName mapguesser-dev.ch
ServerAdmin webmaster@localhost
DocumentRoot /var/www/mapguesser/public
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
<Directory /var/www/mapguesser/public>
Options FollowSymLinks
AllowOverride All
Require all granted
</Directory>

View File

@ -0,0 +1,17 @@
#!/bin/sh
EXPECTED_CHECKSUM="$(curl -s 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');")"
if [ "$EXPECTED_CHECKSUM" != "$ACTUAL_CHECKSUM" ]
then
>&2 echo 'ERROR: Invalid installer checksum'
rm composer-setup.php
exit 1
fi
php composer-setup.php --install-dir=/usr/local/bin --filename=composer
RESULT=$?
rm composer-setup.php
exit $RESULT

14
main.php Normal file
View File

@ -0,0 +1,14 @@
<?php
require 'vendor/autoload.php';
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();
if (!empty($_ENV['DEV'])) {
error_reporting(E_ALL);
ini_set('display_errors', '1');
} else {
ini_set('display_errors', '0');
}

40
public/index.php Normal file
View File

@ -0,0 +1,40 @@
<?php
require '../main.php';
// demo position
$realPosition = new MapGuesser\Geo\Position(47.85239, 13.35101);
// demo bounds
$bounds = new MapGuesser\Geo\Bounds($realPosition);
$bounds->extend(new MapGuesser\Geo\Position(48.07683,7.35758));
$bounds->extend(new MapGuesser\Geo\Position(47.57496, 19.08077));
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>MapGuesser</title>
<link rel="stylesheet" type="text/css" href="static/css/mapguesser.css">
</head>
<body>
<div id="panorama"></div>
<div id="guess">
<div id="guessMap"></div>
<div id="guessButtonContainer">
<button id="guessButton" disabled>Guess</button>
</div>
</div>
<script>
var realPosition = <?= $realPosition->toJson() ?>;
var guessMapBounds = <?= $bounds->toJson() ?>;
</script>
<script src="static/js/mapguesser.js" async defer></script>
<script src="https://maps.googleapis.com/maps/api/js?key=<?= $_ENV['GOOGLE_MAPS_JS_API_KEY'] ?>&callback=initialize" async defer></script>
</body>
</html>

View File

@ -0,0 +1,75 @@
html, body {
height: 100%;
margin: 0;
padding: 0;
}
#panorama {
height: 100%;
width: 100%;
z-index: 1;
}
#guess {
position: absolute;
right: 20px;
bottom: 20px;
width: 250px;
height: 150px;
opacity: 0.5;
z-index: 2;
transition-property: width, height, opacity;
transition-duration: 0.1s;
transition-delay: 0.8s;
}
#guess:hover {
width: 500px;
height: 350px;
opacity: 1.0;
transition-delay: 0s;
}
#guess #guessMap {
height: 115px;
width: 100%;
transition-property: height;
transition-duration: 0.1s;
transition-delay: 0.8s;
border-radius: 3px;
}
#guess:hover #guessMap {
height: 315px;
transition-delay: 0s;
}
#guessButtonContainer {
margin-top: 5px;
height: 30px;
}
#guessButton {
cursor: pointer;
font-size: 14px;
font-weight: bold;
color: #ffffff;
background-color: #5e77aa;
border: none;
border-radius: 3px;
width: 100%;
height: 100%;
box-sizing: border-box;
}
#guessButton:hover, #guessButton:focus {
background-color: #29457f;
outline: none;
}
#guessButton:disabled {
cursor: no-drop;
color: #dddddd;
background-color: #808080;
opacity: 0.7;
}

View File

@ -0,0 +1,108 @@
Math.deg2rad = function (deg) {
return deg * (this.PI / 180.0);
};
var Util = {
EARTH_RADIUS_IN_METER: 6371000,
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 latDelta = lat2 - lat1;
var lonDelta = lng2 - lng1;
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)
)
);
return angle * Util.EARTH_RADIUS_IN_METER;
}
};
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;
}
}
}
setTimeout(function () {
googleLink.title = 'Google Maps'
googleLink.href = 'https://maps.google.com/maps'
}, 1);
}
};
var panorama;
var guessMap;
var guessMarker;
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
});
panorama.addListener('position_changed', function () {
MapManipulator.rewriteGoogleLink();
});
panorama.addListener('pov_changed', function () {
MapManipulator.rewriteGoogleLink();
});
guessMap = new google.maps.Map(document.getElementById('guessMap'), {
disableDefaultUI: true,
clickableIcons: false,
draggableCursor: 'crosshair'
});
guessMap.fitBounds(guessMapBounds);
guessMap.addListener('click', function (e) {
if (guessMarker) {
guessMarker.setPosition(e.latLng);
return;
}
guessMarker = new google.maps.Marker({
map: guessMap,
position: e.latLng,
draggable: true
});
document.getElementById('guessButton').disabled = false;
});
}
document.getElementById('guessButton').onclick = function () {
if (!guessMarker) {
return;
}
var guessedPosition = guessMarker.getPosition();
var distance = Util.calculateDistance(realPosition, { lat: guessedPosition.lat(), lng: guessedPosition.lng() });
alert('You were ' + distance + 'm close!');
this.blur();
}

76
src/Geo/Bounds.php Normal file
View File

@ -0,0 +1,76 @@
<?php namespace MapGuesser\Geo;
class Bounds
{
private float $southLat;
private float $westLng;
private float $northLat;
private float $eastLng;
private bool $initialized = false;
public function __construct(Position $position = null)
{
if ($position === null) {
return;
}
$this->initialize($position);
}
public function extend(Position $position): void
{
if (!$this->initialized) {
$this->initialize($position);
return;
}
$lat = $position->getLat();
$lng = $position->getLng();
if ($lat < $this->southLat) {
$this->southLat = $lat;
}
if ($lng < $this->westLng) {
$this->westLng = $lng;
}
if ($lat > $this->northLat) {
$this->northLat = $lat;
}
if ($lng > $this->eastLng) {
$this->eastLng = $lng;
}
}
public function toJson(): string
{
if (!$this->initialized) {
throw new \Exception("Bounds are not initialized!");
}
return json_encode([
'south' => $this->southLat,
'west' => $this->westLng,
'north' => $this->northLat,
'east' => $this->eastLng,
]);
}
private function initialize(Position $position)
{
$lat = $position->getLat();
$lng = $position->getLng();
$this->northLat = $lat;
$this->westLng = $lng;
$this->southLat = $lat;
$this->eastLng = $lng;
$this->initialized = true;
}
}

31
src/Geo/Position.php Normal file
View File

@ -0,0 +1,31 @@
<?php namespace MapGuesser\Geo;
class Position
{
private float $lat;
private float $lng;
public function __construct(float $lat, float $lng)
{
$this->lat = $lat;
$this->lng = $lng;
}
public function getLat(): float
{
return $this->lat;
}
public function getLng(): float
{
return $this->lng;
}
public function toJson(): string
{
return json_encode([
'lat' => $this->lat,
'lng' => $this->lng,
]);
}
}