From b823af1f7d75ddbcdcec038cb10f115909d6e154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Mon, 1 Jun 2020 20:39:23 +0200 Subject: [PATCH 1/4] MAPG-97 make better split up of JSs, CSSs, SVGs, templates --- public/static/css/game.css | 190 ++++++++++++ public/static/css/mapguesser.css | 276 +----------------- public/static/css/maps.css | 74 +++++ public/static/img/favicon/SOURCE | 2 + .../img/{favicon/favicon.svg => icon.svg} | 1 - scripts/minify.sh | 8 +- views/game.php | 3 +- views/maps.php | 3 +- views/templates/header.php | 2 +- views/templates/icon.php | 4 - views/templates/main_header.php | 5 + 11 files changed, 293 insertions(+), 275 deletions(-) create mode 100644 public/static/css/game.css create mode 100644 public/static/css/maps.css create mode 100644 public/static/img/favicon/SOURCE rename public/static/img/{favicon/favicon.svg => icon.svg} (89%) delete mode 100644 views/templates/icon.php diff --git a/public/static/css/game.css b/public/static/css/game.css new file mode 100644 index 0000000..63d1357 --- /dev/null +++ b/public/static/css/game.css @@ -0,0 +1,190 @@ +#roundInfo { + line-height: inherit; + text-align: right; +} + +#roundInfo p { + font-size: 16px; + line-height: inherit; +} + +#panorama { + width: 100%; + height: calc(100% - 40px); + z-index: 1; +} + +#cover { + position: absolute; + top: 40px; + left: 0; + bottom: 0; + right: 0; + background-color: #000000; + opacity: 0.5; + z-index: 3; +} + +#guess { + position: absolute; + bottom: 30px; + right: 20px; + z-index: 2; +} + +#guess.result { + z-index: 4; +} + +#guess>#continueButtonContainer { + display: none; +} + +#guess.result>#closeGuessButtonContainer, #guess.result>#guessButtonContainer { + display: none; +} + +#guess.result>#continueButtonContainer { + display: block; +} + +#map { + width: 100%; + border-radius: 3px; +} + +#guess.result>#map { + height: calc(100% - 170px); +} + +#resultInfo { + margin-top: 5px; + width: 100%; + height: 120px; + padding: 5px 20px; + text-align: center; + box-sizing: border-box; + background-color: #ffffff; + border-radius: 3px; + display: none; +} + +#guess.result>#resultInfo { + display: block; +} + +#resultInfo>div { + width: 100%; + height: 33.33%; + display: flex; + justify-content: center; + align-items: center; +} + +#resultInfo p { + font-size: 24px; + line-height: 1; +} + +#distanceInfo>p:nth-child(2), #scoreInfo>p:nth-child(2) { + display: none; +} + +#scoreBarBase { + height: 24px; + margin: 0 auto; + background-color: #eeeeee; + border-radius: 3px; +} + +#scoreBar { + width: 0; + height: 100%; + border-radius: 3px; + transition-property: width; + transition-duration: 2.0s; +} + +#showSummaryButton, #startNewGameButton { + display: none; +} + +@media screen and (max-width: 599px) { + #showGuessButtonContainer { + position: absolute; + left: 20px; + bottom: 30px; + right: 20px; + z-index: 2; + } + #guess { + top: 50px; + left: 20px; + opacity: 0.95; + visibility: hidden; + } + #map { + height: calc(100% - 90px); + } + #scoreBarBase { + width: 100%; + } +} + +@media screen and (min-width: 600px) { + #showGuessButtonContainer { + display: none; + } + #guess { + width: 500px; + height: 375px; + opacity: 0.95; + } + #guess.adapt { + top: initial; + width: 250px; + height: 200px; + opacity: 0.5; + transition-property: width, height, opacity; + transition-duration: 0.1s; + transition-delay: 0.8s; + } + #guess.adapt:hover { + width: 500px; + height: 375px; + opacity: 0.95; + transition-delay: 0s; + } + #closeGuessButtonContainer { + display: none; + } + #map { + height: calc(100% - 45px); + } + #guess.result { + width: initial; + height: initial; + top: 50px; + left: 50px; + right: 50px; + bottom: 50px; + } + #scoreBarBase { + width: 60%; + } + @media screen and (max-height: 424px) { + #guess { + top: 50px; + height: initial; + } + #guess.adapt:hover { + top: 50px; + height: initial; + } + #guess.result { + left: 20px; + right: 20px; + bottom: 30px; + } + } +} diff --git a/public/static/css/mapguesser.css b/public/static/css/mapguesser.css index 83f717e..d96bc8a 100644 --- a/public/static/css/mapguesser.css +++ b/public/static/css/mapguesser.css @@ -93,7 +93,10 @@ sub { margin-bottom: 10px; } -svg.inline { +svg.inline, img.inline { + display: inline-block; + width: 1em; + height: 1em; vertical-align: -0.15em; } @@ -147,6 +150,14 @@ button.gray:hover, button.gray:focus, a.button.gray:hover, a.button.gray:focus { background-color: #555555; } +button.red, a.button.red { + background-color: #aa5e5e; +} + +button.red:hover, button.red:focus, a.button.red:hover, a.button.red:focus { + background-color: #7f2929; +} + div.header { background-color: #333333; height: 50px; @@ -177,51 +188,6 @@ div.buttonContainer>button { margin: 0 auto; } -div.mapContainer { - display: grid; -} - -div.mapItem { - width: 350px; - background-color: #eeeeee; - border-radius: 3px; - margin: 10px auto; -} - -div.mapItem>div.title { - background-color: #28a745; - color: white; - border-top-left-radius: 3px; - border-top-right-radius: 3px; - padding: 4px 8px; -} - -div.mapItem>div.title>p.title { - font-weight: 500; - font-size: 18px; -} - -div.mapItem>img { - width: 100%; -} - -div.mapItem>div.inner { - padding: 8px; -} - -div.mapItem>div.inner>div.info { - display: grid; - grid-template-columns: auto auto; -} - -div.mapItem>div.inner>div.info>p:nth-child(1) { - text-align: left; -} - -div.mapItem>div.inner>div.info>p:nth-child(2) { - text-align: right; -} - #loading { position: absolute; width: 64px; @@ -234,147 +200,6 @@ div.mapItem>div.inner>div.info>p:nth-child(2) { visibility: hidden; } -#roundInfo { - line-height: inherit; - text-align: right; -} - -#roundInfo p { - font-size: 16px; - line-height: inherit; -} - -#panorama { - width: 100%; - height: calc(100% - 40px); - z-index: 1; -} - -#cover { - position: absolute; - top: 40px; - left: 0; - bottom: 0; - right: 0; - background-color: #000000; - opacity: 0.5; - z-index: 3; -} - -#guess { - position: absolute; - bottom: 30px; - right: 20px; - z-index: 2; -} - -#guess.result { - z-index: 4; -} - -#guess>#continueButtonContainer { - display: none; -} - -#guess.result>#closeGuessButtonContainer, #guess.result>#guessButtonContainer { - display: none; -} - -#guess.result>#continueButtonContainer { - display: block; -} - -#map { - width: 100%; - border-radius: 3px; -} - -#guess.result>#map { - height: calc(100% - 170px); -} - -#resultInfo { - margin-top: 5px; - width: 100%; - height: 120px; - padding: 5px 20px; - text-align: center; - box-sizing: border-box; - background-color: #ffffff; - border-radius: 3px; - display: none; -} - -#guess.result>#resultInfo { - display: block; -} - -#resultInfo>div { - width: 100%; - height: 33.33%; - display: flex; - justify-content: center; - align-items: center; -} - -#resultInfo p { - font-size: 24px; - line-height: 1; -} - -#distanceInfo>p:nth-child(2), #scoreInfo>p:nth-child(2) { - display: none; -} - -#scoreBarBase { - height: 24px; - margin: 0 auto; - background-color: #eeeeee; - border-radius: 3px; -} - -#scoreBar { - width: 0; - height: 100%; - border-radius: 3px; - transition-property: width; - transition-duration: 2.0s; -} - -#showSummaryButton, #startNewGameButton { - display: none; -} - -@media screen and (min-width: 1504px) { - div.mapContainer { - grid-template-columns: auto auto auto auto; - } -} - -@media screen and (min-width: 1134px) and (max-width: 1503px) { - div.mapContainer { - grid-template-columns: auto auto auto; - } -} - -@media screen and (min-width: 764px) and (max-width: 1133px) { - div.mapContainer { - grid-template-columns: auto auto; - } -} - -@media screen and (max-width: 763px) { - div.mapContainer { - grid-template-columns: auto; - } -} - -@media screen and (max-width: 374px) { - div.mapItem { - width: initial; - } -} - @media screen and (max-width: 599px) { div.header.small h1 span { display: none; @@ -383,81 +208,4 @@ div.mapItem>div.inner>div.info>p:nth-child(2) { padding: 0; width: 100%; } - #showGuessButtonContainer { - position: absolute; - left: 20px; - bottom: 30px; - right: 20px; - z-index: 2; - } - #guess { - top: 50px; - left: 20px; - opacity: 0.95; - visibility: hidden; - } - #map { - height: calc(100% - 90px); - } - #scoreBarBase { - width: 100%; - } -} - -@media screen and (min-width: 600px) { - #showGuessButtonContainer { - display: none; - } - #guess { - width: 500px; - height: 375px; - opacity: 0.95; - } - #guess.adapt { - top: initial; - width: 250px; - height: 200px; - opacity: 0.5; - transition-property: width, height, opacity; - transition-duration: 0.1s; - transition-delay: 0.8s; - } - #guess.adapt:hover { - width: 500px; - height: 375px; - opacity: 0.95; - transition-delay: 0s; - } - #closeGuessButtonContainer { - display: none; - } - #map { - height: calc(100% - 45px); - } - #guess.result { - width: initial; - height: initial; - top: 50px; - left: 50px; - right: 50px; - bottom: 50px; - } - #scoreBarBase { - width: 60%; - } - @media screen and (max-height: 424px) { - #guess { - top: 50px; - height: initial; - } - #guess.adapt:hover { - top: 50px; - height: initial; - } - #guess.result { - left: 20px; - right: 20px; - bottom: 30px; - } - } } diff --git a/public/static/css/maps.css b/public/static/css/maps.css new file mode 100644 index 0000000..69da1db --- /dev/null +++ b/public/static/css/maps.css @@ -0,0 +1,74 @@ +#mapContainer { + display: grid; +} + +div.mapItem { + width: 350px; + background-color: #eeeeee; + border-radius: 3px; + margin: 10px auto; +} + +div.mapItem>div.title { + background-color: #28a745; + color: white; + border-top-left-radius: 3px; + border-top-right-radius: 3px; + padding: 4px 8px; +} + +div.mapItem>div.title>p.title { + font-weight: 500; + font-size: 18px; +} + +div.mapItem>img { + width: 100%; +} + +div.mapItem>div.inner { + padding: 8px; +} + +div.mapItem>div.inner>div.info { + display: grid; + grid-template-columns: auto auto; +} + +div.mapItem>div.inner>div.info>p:nth-child(1) { + text-align: left; +} + +div.mapItem>div.inner>div.info>p:nth-child(2) { + text-align: right; +} + +@media screen and (min-width: 1504px) { + #mapContainer { + grid-template-columns: auto auto auto auto; + } +} + +@media screen and (min-width: 1134px) and (max-width: 1503px) { + #mapContainer { + grid-template-columns: auto auto auto; + } +} + +@media screen and (min-width: 764px) and (max-width: 1133px) { + #mapContainer { + grid-template-columns: auto auto; + } +} + +@media screen and (max-width: 763px) { + #mapContainer { + grid-template-columns: auto; + } +} + +@media screen and (max-width: 374px) { + div.mapItem { + width: initial; + } +} diff --git a/public/static/img/favicon/SOURCE b/public/static/img/favicon/SOURCE new file mode 100644 index 0000000..f750f52 --- /dev/null +++ b/public/static/img/favicon/SOURCE @@ -0,0 +1,2 @@ +The PNGs in this folder are generated from '../icon.svg'. +Copyright (c) 2019 The Bootstrap Authors. License can be found in 'USED_SOFTWARE' in section 'Bootstrap Icons'. \ No newline at end of file diff --git a/public/static/img/favicon/favicon.svg b/public/static/img/icon.svg similarity index 89% rename from public/static/img/favicon/favicon.svg rename to public/static/img/icon.svg index b08c37a..ce182d9 100644 --- a/public/static/img/favicon/favicon.svg +++ b/public/static/img/icon.svg @@ -1,4 +1,3 @@ - diff --git a/scripts/minify.sh b/scripts/minify.sh index 3a57b71..bcf6a6a 100755 --- a/scripts/minify.sh +++ b/scripts/minify.sh @@ -4,6 +4,8 @@ ROOT_DIR=$(dirname $(readlink -f "$0"))/.. . ${ROOT_DIR}/.env -uglifyjs ${ROOT_DIR}/public/static/js/game.js -c -m -o ${ROOT_DIR}/public/static/js/game.js -cleancss ${ROOT_DIR}/public/static/css/mapguesser.css -o ${ROOT_DIR}/public/static/css/mapguesser.css -svgo ${ROOT_DIR}/public/static/img/loading.svg -o ${ROOT_DIR}/public/static/img/loading.svg +find ${ROOT_DIR}/public/static/js -type f -iname '*.js' -exec uglifyjs {} -c -m -o {} \; + +find ${ROOT_DIR}/public/static/css -type f -iname '*.css' -exec cleancss {} -o {} \; + +find ${ROOT_DIR}/public/static/img -type f -iname '*.svg' -exec svgo {} -o {} \; diff --git a/views/game.php b/views/game.php index 05f6157..5ea5f5c 100644 --- a/views/game.php +++ b/views/game.php @@ -1,9 +1,10 @@ +

- + MapGuesser

diff --git a/views/maps.php b/views/maps.php index 1871506..8ca8bce 100644 --- a/views/maps.php +++ b/views/maps.php @@ -1,8 +1,9 @@ +

Playable maps

-
+
diff --git a/views/templates/header.php b/views/templates/header.php index 9e80e6f..033c791 100644 --- a/views/templates/header.php +++ b/views/templates/header.php @@ -1,7 +1,7 @@

- + MapGuesser

diff --git a/views/templates/icon.php b/views/templates/icon.php deleted file mode 100644 index 85fba48..0000000 --- a/views/templates/icon.php +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/views/templates/main_header.php b/views/templates/main_header.php index 1615c46..cd488cd 100644 --- a/views/templates/main_header.php +++ b/views/templates/main_header.php @@ -5,6 +5,11 @@ MapGuesser + + + + + From f7d37b85138ff4bb32d4c6d0645abc4b20a85ddb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Mon, 1 Jun 2020 20:41:46 +0200 Subject: [PATCH 2/4] MAPG-97 add and use new markers --- public/static/img/markers/marker-blue.svg | 16 +++++++++ .../static/img/markers/marker-gray-empty.svg | 10 ++++++ public/static/img/markers/marker-gray.svg | 16 +++++++++ .../static/img/markers/marker-green-empty.svg | 10 ++++++ public/static/img/markers/marker-green.svg | 16 +++++++++ public/static/img/markers/marker-red.svg | 16 +++++++++ public/static/js/game.js | 35 ++++++++++++++++--- 7 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 public/static/img/markers/marker-blue.svg create mode 100644 public/static/img/markers/marker-gray-empty.svg create mode 100644 public/static/img/markers/marker-gray.svg create mode 100644 public/static/img/markers/marker-green-empty.svg create mode 100644 public/static/img/markers/marker-green.svg create mode 100644 public/static/img/markers/marker-red.svg diff --git a/public/static/img/markers/marker-blue.svg b/public/static/img/markers/marker-blue.svg new file mode 100644 index 0000000..d49029e --- /dev/null +++ b/public/static/img/markers/marker-blue.svg @@ -0,0 +1,16 @@ + + + + + diff --git a/public/static/img/markers/marker-gray-empty.svg b/public/static/img/markers/marker-gray-empty.svg new file mode 100644 index 0000000..8a3d446 --- /dev/null +++ b/public/static/img/markers/marker-gray-empty.svg @@ -0,0 +1,10 @@ + + + + diff --git a/public/static/img/markers/marker-gray.svg b/public/static/img/markers/marker-gray.svg new file mode 100644 index 0000000..780715f --- /dev/null +++ b/public/static/img/markers/marker-gray.svg @@ -0,0 +1,16 @@ + + + + + diff --git a/public/static/img/markers/marker-green-empty.svg b/public/static/img/markers/marker-green-empty.svg new file mode 100644 index 0000000..ed948fd --- /dev/null +++ b/public/static/img/markers/marker-green-empty.svg @@ -0,0 +1,10 @@ + + + + diff --git a/public/static/img/markers/marker-green.svg b/public/static/img/markers/marker-green.svg new file mode 100644 index 0000000..bb90e28 --- /dev/null +++ b/public/static/img/markers/marker-green.svg @@ -0,0 +1,16 @@ + + + + + diff --git a/public/static/img/markers/marker-red.svg b/public/static/img/markers/marker-red.svg new file mode 100644 index 0000000..fb20dbf --- /dev/null +++ b/public/static/img/markers/marker-red.svg @@ -0,0 +1,16 @@ + + + + + diff --git a/public/static/js/game.js b/public/static/js/game.js index 00f0489..e302a86 100644 --- a/public/static/js/game.js +++ b/public/static/js/game.js @@ -222,7 +222,13 @@ title: 'Open in Google Maps', zIndex: Core.rounds.length * 2, clickable: true, - draggable: false + draggable: false, + icon: { + url: '/static/img/markers/marker-green.svg', + size: new google.maps.Size(24, 32), + scaledSize: new google.maps.Size(24, 32), + anchor: new google.maps.Point(12, 32) + }, }); round.realMarker.addListener('click', function () { @@ -236,10 +242,17 @@ zIndex: Core.rounds.length, clickable: false, draggable: false, + icon: { + url: '/static/img/markers/marker-gray-empty.svg', + size: new google.maps.Size(24, 32), + scaledSize: new google.maps.Size(24, 32), + anchor: new google.maps.Point(12, 32), + labelOrigin: new google.maps.Point(12, 14) + }, label: { color: '#ffffff', fontFamily: 'Roboto', - fontSize: '18px', + fontSize: '16px', fontWeight: '500', text: '?' } @@ -300,8 +313,15 @@ for (var i = 0; i < Core.rounds.length; ++i) { var round = Core.rounds[i]; + round.realMarker.setIcon({ + url: '/static/img/markers/marker-green-empty.svg', + size: new google.maps.Size(24, 32), + scaledSize: new google.maps.Size(24, 32), + anchor: new google.maps.Point(12, 32), + labelOrigin: new google.maps.Point(12, 14) + }); round.realMarker.setLabel({ - color: '#812519', + color: '#285624', fontFamily: 'Roboto', fontSize: '16px', fontWeight: '500', @@ -385,10 +405,17 @@ position: e.latLng, clickable: false, draggable: true, + icon: { + url: '/static/img/markers/marker-gray-empty.svg', + size: new google.maps.Size(24, 32), + scaledSize: new google.maps.Size(24, 32), + anchor: new google.maps.Point(12, 32), + labelOrigin: new google.maps.Point(12, 14) + }, label: { color: '#ffffff', fontFamily: 'Roboto', - fontSize: '18px', + fontSize: '16px', fontWeight: '500', text: '?' } From a82b27cb909be9720346319ccb38eec20719b9dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Mon, 1 Jun 2020 20:42:37 +0200 Subject: [PATCH 3/4] MAPG-97 add Leaflet --- .env.example | 1 + public/static/.gitignore | 1 + public/static/package.json | 5 +++++ public/static/yarn.lock | 8 ++++++++ scripts/install.sh | 5 +++-- scripts/update.sh | 4 +++- 6 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 public/static/.gitignore create mode 100644 public/static/package.json create mode 100644 public/static/yarn.lock diff --git a/.env.example b/.env.example index 6d5a0de..e27e8dd 100644 --- a/.env.example +++ b/.env.example @@ -5,3 +5,4 @@ DB_PASSWORD=mapguesser DB_NAME=mapguesser GOOGLE_MAPS_SERVER_API_KEY=your_google_maps_server_api_key GOOGLE_MAPS_JS_API_KEY=your_google_maps_js_api_key +LEAFLET_TILESERVER_URL=a_leaflet_compatible_tileserver_url diff --git a/public/static/.gitignore b/public/static/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/public/static/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/public/static/package.json b/public/static/package.json new file mode 100644 index 0000000..4647f50 --- /dev/null +++ b/public/static/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "leaflet": "^1.6.0" + } +} diff --git a/public/static/yarn.lock b/public/static/yarn.lock new file mode 100644 index 0000000..f324630 --- /dev/null +++ b/public/static/yarn.lock @@ -0,0 +1,8 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +leaflet@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.6.0.tgz#aecbb044b949ec29469eeb31c77a88e2f448f308" + integrity sha512-CPkhyqWUKZKFJ6K8umN5/D2wrJ2+/8UIpXppY7QDnUZW5bZL5+SEI2J7GBpwh4LIupOKqbNSQXgqmrEJopHVNQ== diff --git a/scripts/install.sh b/scripts/install.sh index d7aaa17..890a9f5 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -9,13 +9,14 @@ if [ -f ${ROOT_DIR}/installed ]; then exit 1 fi -echo "Installing MapGuesser DB..." +echo "Installing Yarn packages..." +(cd ${ROOT_DIR}/public/static && yarn install) +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 "Minifying JS, CSS and SVG files..." - ${ROOT_DIR}/scripts/minify.sh fi diff --git a/scripts/update.sh b/scripts/update.sh index d54aab2..a61300a 100755 --- a/scripts/update.sh +++ b/scripts/update.sh @@ -7,8 +7,10 @@ ROOT_DIR=$(dirname $(readlink -f "$0"))/.. echo "Installing Composer packages..." (cd ${ROOT_DIR} && composer install) +echo "Installing Yarn packages..." +(cd ${ROOT_DIR}/public/static && yarn install) + if [ -z "${DEV}" ] || [ "${DEV}" -eq "0" ]; then echo "Minifying JS, CSS and SVG files..." - ${ROOT_DIR}/scripts/minify.sh fi From 9085793c9f4955c855b3b37fabb5cfa72e1c2c10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Mon, 1 Jun 2020 21:13:02 +0200 Subject: [PATCH 4/4] MAPG-97 add initial map editor with Leaflet (works as map viewer now) --- public/index.php | 4 + public/static/css/map_editor.css | 49 ++++++++++++ public/static/js/map_editor.js | 108 ++++++++++++++++++++++++++ src/Controller/MapAdminController.php | 53 +++++++++++++ views/admin/map_editor.php | 27 +++++++ views/admin/maps.php | 7 ++ 6 files changed, 248 insertions(+) create mode 100644 public/static/css/map_editor.css create mode 100644 public/static/js/map_editor.js create mode 100644 src/Controller/MapAdminController.php create mode 100644 views/admin/map_editor.php create mode 100644 views/admin/maps.php diff --git a/public/index.php b/public/index.php index 681a0d9..a740c2e 100644 --- a/public/index.php +++ b/public/index.php @@ -19,6 +19,10 @@ Container::$routeCollection->group('game', function (MapGuesser\Routing\RouteCol $routeCollection->get('position-json', '{mapId}/position.json', [MapGuesser\Controller\PositionController::class, 'getPosition']); $routeCollection->post('guess-json', '{mapId}/guess.json', [MapGuesser\Controller\PositionController::class, 'evaluateGuess']); }); +Container::$routeCollection->group('admin', function (MapGuesser\Routing\RouteCollection $routeCollection) { + $routeCollection->get('admin.maps', 'maps', [MapGuesser\Controller\MapAdminController::class, 'getMaps']); + $routeCollection->get('admin.mapEditor', 'mapEditor/{mapId}', [MapGuesser\Controller\MapAdminController::class, 'getMapEditor']); +}); $match = Container::$routeCollection->match($method, explode('/', $url)); diff --git a/public/static/css/map_editor.css b/public/static/css/map_editor.css new file mode 100644 index 0000000..eab914c --- /dev/null +++ b/public/static/css/map_editor.css @@ -0,0 +1,49 @@ +#map { + width: 100%; + height: calc(100% - 50px); + z-index: 1; +} + +#map.selected { + height: calc(50% - 25px); +} + +#panorama { + width: 100%; + height: calc(50% - 25px); + display: none; + z-index: 1; +} + +#noPano { + display: flex; + position: absolute; + left: 0; + bottom: 0; + right: 0; + height: calc(50% - 25px); + z-index: 2; + visibility: hidden; + background: #cccccc; +} + +#noPano>p { + margin: auto; +} + +#control { + position: absolute; + right: 10px; + top: 60px; + width: 125px; + z-index: 3; +} + +#placeControl { + position: absolute; + right: 10px; + top: calc(50% + 35px); + width: 100px; + z-index: 3; + visibility: hidden; +} diff --git a/public/static/js/map_editor.js b/public/static/js/map_editor.js new file mode 100644 index 0000000..1b9df4d --- /dev/null +++ b/public/static/js/map_editor.js @@ -0,0 +1,108 @@ +(function () { + var MapEditor = { + map: null, + panorama: null, + selectedMarker: null, + + loadPano: function (data, status) { + document.getElementById('loading').style.visibility = 'hidden'; + + if (status !== google.maps.StreetViewStatus.OK) { + document.getElementById('noPano').style.visibility = 'visible'; + return; + } + + MapEditor.panorama.setVisible(true); + MapEditor.panorama.setPov({ heading: 0, pitch: 0 }); + MapEditor.panorama.setZoom(0); + MapEditor.panorama.setPano(data.location.pano); + }, + + select: function (marker) { + document.getElementById('loading').style.visibility = 'visible'; + + document.getElementById('map').classList.add('selected'); + document.getElementById('noPano').style.visibility = 'hidden'; + document.getElementById('panorama').style.display = 'block'; + document.getElementById('placeControl').style.visibility = 'visible'; + + MapEditor.resetSelected(); + MapEditor.selectedMarker = marker; + + marker.setIcon(L.icon({ + iconUrl: '/static/img/markers/marker-blue.svg', + iconSize: [24, 32], + iconAnchor: [12, 32] + })); + marker.setZIndexOffset(2000); + + MapEditor.map.invalidateSize(true); + MapEditor.map.panTo(marker.getLatLng()); + + MapEditor.panorama.setVisible(false); + + var sv = new google.maps.StreetViewService(); + sv.getPanorama({ location: marker.getLatLng(), preference: google.maps.StreetViewPreference.NEAREST, source: google.maps.StreetViewSource.OUTDOOR }, MapEditor.loadPano); + }, + + resetSelected: function () { + if (!MapEditor.selectedMarker) { + return; + } + + MapEditor.selectedMarker.setIcon(L.icon({ + iconUrl: '/static/img/markers/marker-green.svg', + iconSize: [24, 32], + iconAnchor: [12, 32] + })); + MapEditor.selectedMarker.setZIndexOffset(1000); + } + }; + + MapEditor.map = L.map('map', { + attributionControl: false, + zoomControl: false + }); + + L.tileLayer(tileUrl, { + minZoom: 0, + maxZoom: 20 + }).addTo(MapEditor.map); + + MapEditor.map.fitBounds(L.latLngBounds({ lat: mapBounds.south, lng: mapBounds.west }, { lat: mapBounds.north, lng: mapBounds.east })); + + for (var i = 0; i < places.length; ++i) { + var marker = L.marker({ lat: places[i].lat, lng: places[i].lng }, { + icon: L.icon({ + iconUrl: '/static/img/markers/marker-green.svg', + iconSize: [24, 32], + iconAnchor: [12, 32] + }), + zIndexOffset: 1000 + }) + .addTo(MapEditor.map) + .on('click', function () { + MapEditor.select(this); + }); + } + + MapEditor.panorama = new google.maps.StreetViewPanorama(document.getElementById('panorama'), { + // switch off fullscreenControl because positioning doesn't work + fullscreenControl: false, + fullscreenControlOptions: { + position: google.maps.ControlPosition.LEFT_TOP + } + }); + + document.getElementById('cancelButton').onclick = function () { + document.getElementById('map').classList.remove('selected'); + document.getElementById('noPano').style.visibility = 'hidden'; + document.getElementById('panorama').style.display = 'none'; + document.getElementById('placeControl').style.visibility = 'hidden'; + + MapEditor.resetSelected(); + MapEditor.selectedMarker = null; + + MapEditor.map.invalidateSize(true); + }; +})(); diff --git a/src/Controller/MapAdminController.php b/src/Controller/MapAdminController.php new file mode 100644 index 0000000..39c8bd5 --- /dev/null +++ b/src/Controller/MapAdminController.php @@ -0,0 +1,53 @@ +getMapBounds($mapId); + + $places = $this->getPlaces($mapId); + + $data = ['mapId' => $mapId, 'bounds' => $bounds->toArray(), 'places' => &$places]; + return new HtmlContent('admin/map_editor', $data); + } + + private function getMapBounds(int $mapId): Bounds + { + $select = new Select(\Container::$dbConnection, 'maps'); + $select->columns(['bound_south_lat', 'bound_west_lng', 'bound_north_lat', 'bound_east_lng']); + $select->whereId($mapId); + + $map = $select->execute()->fetch(IResultSet::FETCH_ASSOC); + + $bounds = Bounds::createDirectly($map['bound_south_lat'], $map['bound_west_lng'], $map['bound_north_lat'], $map['bound_east_lng']); + + return $bounds; + } + + private function &getPlaces(int $mapId): array + { + $select = new Select(\Container::$dbConnection, 'places'); + $select->columns(['id', 'lat', 'lng']); + $select->where('map_id', '=', $mapId); + $select->orderBy('lng'); + //$select->limit(100); + + $places = $select->execute()->fetchAll(IResultSet::FETCH_ASSOC); + + return $places; + } +} diff --git a/views/admin/map_editor.php b/views/admin/map_editor.php new file mode 100644 index 0000000..e3887f9 --- /dev/null +++ b/views/admin/map_editor.php @@ -0,0 +1,27 @@ + + + +
+
+ + Back to maps +
+
+
+

No panorama is available for this location.

+
+
+ + + +
+ + + + + \ No newline at end of file diff --git a/views/admin/maps.php b/views/admin/maps.php new file mode 100644 index 0000000..bc6ad73 --- /dev/null +++ b/views/admin/maps.php @@ -0,0 +1,7 @@ + + +
+

Maps

+

TODO

+
+ \ No newline at end of file