Merged in feature/MAPG-97-integrate-leaflet (pull request #75)

Feature/MAPG-97 integrate leaflet
This commit is contained in:
Bence Pőcze 2020-06-01 20:01:33 +00:00
commit 150bc5be20
30 changed files with 677 additions and 282 deletions

View File

@ -5,3 +5,4 @@ DB_PASSWORD=mapguesser
DB_NAME=mapguesser DB_NAME=mapguesser
GOOGLE_MAPS_SERVER_API_KEY=your_google_maps_server_api_key GOOGLE_MAPS_SERVER_API_KEY=your_google_maps_server_api_key
GOOGLE_MAPS_JS_API_KEY=your_google_maps_js_api_key GOOGLE_MAPS_JS_API_KEY=your_google_maps_js_api_key
LEAFLET_TILESERVER_URL=a_leaflet_compatible_tileserver_url

View File

@ -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->get('position-json', '{mapId}/position.json', [MapGuesser\Controller\PositionController::class, 'getPosition']);
$routeCollection->post('guess-json', '{mapId}/guess.json', [MapGuesser\Controller\PositionController::class, 'evaluateGuess']); $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)); $match = Container::$routeCollection->match($method, explode('/', $url));

1
public/static/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules

190
public/static/css/game.css Normal file
View File

@ -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;
}
}
}

View File

@ -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;
}

View File

@ -93,7 +93,10 @@ sub {
margin-bottom: 10px; margin-bottom: 10px;
} }
svg.inline { svg.inline, img.inline {
display: inline-block;
width: 1em;
height: 1em;
vertical-align: -0.15em; 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; 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 { div.header {
background-color: #333333; background-color: #333333;
height: 50px; height: 50px;
@ -177,51 +188,6 @@ div.buttonContainer>button {
margin: 0 auto; 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 { #loading {
position: absolute; position: absolute;
width: 64px; width: 64px;
@ -234,147 +200,6 @@ div.mapItem>div.inner>div.info>p:nth-child(2) {
visibility: hidden; 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) { @media screen and (max-width: 599px) {
div.header.small h1 span { div.header.small h1 span {
display: none; display: none;
@ -383,81 +208,4 @@ div.mapItem>div.inner>div.info>p:nth-child(2) {
padding: 0; padding: 0;
width: 100%; 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;
}
}
} }

View File

@ -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;
}
}

View File

@ -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'.

View File

@ -1,4 +1,3 @@
<!-- The PNGs in this folder are generated from this SVG. -->
<!-- Copyright (c) 2019 The Bootstrap Authors. License can be found in 'USED_SOFTWARE' in section 'Bootstrap Icons'. --> <!-- Copyright (c) 2019 The Bootstrap Authors. License can be found in 'USED_SOFTWARE' in section 'Bootstrap Icons'. -->
<svg viewBox="0 0 16 16" fill="#28a745" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 16 16" fill="#28a745" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M15.817.613A.5.5 0 0 1 16 1v13a.5.5 0 0 1-.402.49l-5 1a.502.502 0 0 1-.196 0L5.5 14.51l-4.902.98A.5.5 0 0 1 0 15V2a.5.5 0 0 1 .402-.49l5-1a.5.5 0 0 1 .196 0l4.902.98 4.902-.98a.5.5 0 0 1 .415.103zM10 2.41l-4-.8v11.98l4 .8V2.41zm1 11.98l4-.8V1.61l-4 .8v11.98zm-6-.8V1.61l-4 .8v11.98l4-.8z" /> <path fill-rule="evenodd" d="M15.817.613A.5.5 0 0 1 16 1v13a.5.5 0 0 1-.402.49l-5 1a.502.502 0 0 1-.196 0L5.5 14.51l-4.902.98A.5.5 0 0 1 0 15V2a.5.5 0 0 1 .402-.49l5-1a.5.5 0 0 1 .196 0l4.902.98 4.902-.98a.5.5 0 0 1 .415.103zM10 2.41l-4-.8v11.98l4 .8V2.41zm1 11.98l4-.8V1.61l-4 .8v11.98zm-6-.8V1.61l-4 .8v11.98l4-.8z" />

Before

Width:  |  Height:  |  Size: 591 B

After

Width:  |  Height:  |  Size: 529 B

View File

@ -0,0 +1,16 @@
<!-- Original image: Copyright (c) 2019 The Bootstrap Authors. License can be found in 'USED_SOFTWARE' in section 'Bootstrap Icons'. -->
<svg viewBox="0 0 12 16" xmlns="http://www.w3.org/2000/svg">
<path
fill="#3183ce"
fill-rule="evenodd"
stroke="#19456d"
stroke-width="0.3"
stroke-linecap="round"
d="m 5.9999998,15.849652 c 0,0 5.8511182,-5.579947 5.8511182,-9.8134832 a 5.8511179,5.8880898 0 0 0 -11.7022358,0 c 0,4.2335362 5.8511176,9.8134832 5.8511176,9.8134832" />
<circle
fill="#19456d"
fill-rule="evenodd"
cx="6"
cy="6"
r="3" />
</svg>

After

Width:  |  Height:  |  Size: 639 B

View File

@ -0,0 +1,10 @@
<!-- Original image: Copyright (c) 2019 The Bootstrap Authors. License can be found in 'USED_SOFTWARE' in section 'Bootstrap Icons'. -->
<svg viewBox="0 0 12 16" xmlns="http://www.w3.org/2000/svg">
<path
fill="#828282"
fill-rule="evenodd"
stroke="#383838"
stroke-width="0.3"
stroke-linecap="round"
d="m 5.9999998,15.849652 c 0,0 5.8511182,-5.579947 5.8511182,-9.8134832 a 5.8511179,5.8880898 0 0 0 -11.7022358,0 c 0,4.2335362 5.8511176,9.8134832 5.8511176,9.8134832" />
</svg>

After

Width:  |  Height:  |  Size: 529 B

View File

@ -0,0 +1,16 @@
<!-- Original image: Copyright (c) 2019 The Bootstrap Authors. License can be found in 'USED_SOFTWARE' in section 'Bootstrap Icons'. -->
<svg viewBox="0 0 12 16" xmlns="http://www.w3.org/2000/svg">
<path
fill="#828282"
fill-rule="evenodd"
stroke="#383838"
stroke-width="0.3"
stroke-linecap="round"
d="m 5.9999998,15.849652 c 0,0 5.8511182,-5.579947 5.8511182,-9.8134832 a 5.8511179,5.8880898 0 0 0 -11.7022358,0 c 0,4.2335362 5.8511176,9.8134832 5.8511176,9.8134832" />
<circle
fill="#383838"
fill-rule="evenodd"
cx="6"
cy="6"
r="3" />
</svg>

After

Width:  |  Height:  |  Size: 639 B

View File

@ -0,0 +1,10 @@
<!-- Original image: Copyright (c) 2019 The Bootstrap Authors. License can be found in 'USED_SOFTWARE' in section 'Bootstrap Icons'. -->
<svg viewBox="0 0 12 16" xmlns="http://www.w3.org/2000/svg">
<path
fill="#28a745"
fill-rule="evenodd"
stroke="#285624"
stroke-width="0.3"
stroke-linecap="round"
d="m 5.9999998,15.849652 c 0,0 5.8511182,-5.579947 5.8511182,-9.8134832 a 5.8511179,5.8880898 0 0 0 -11.7022358,0 c 0,4.2335362 5.8511176,9.8134832 5.8511176,9.8134832" />
</svg>

After

Width:  |  Height:  |  Size: 529 B

View File

@ -0,0 +1,16 @@
<!-- Original image: Copyright (c) 2019 The Bootstrap Authors. License can be found in 'USED_SOFTWARE' in section 'Bootstrap Icons'. -->
<svg viewBox="0 0 12 16" xmlns="http://www.w3.org/2000/svg">
<path
fill="#28a745"
fill-rule="evenodd"
stroke="#285624"
stroke-width="0.3"
stroke-linecap="round"
d="m 5.9999998,15.849652 c 0,0 5.8511182,-5.579947 5.8511182,-9.8134832 a 5.8511179,5.8880898 0 0 0 -11.7022358,0 c 0,4.2335362 5.8511176,9.8134832 5.8511176,9.8134832" />
<circle
fill="#285624"
fill-rule="evenodd"
cx="6"
cy="6"
r="3" />
</svg>

After

Width:  |  Height:  |  Size: 639 B

View File

@ -0,0 +1,16 @@
<!-- Original image: Copyright (c) 2019 The Bootstrap Authors. License can be found in 'USED_SOFTWARE' in section 'Bootstrap Icons'. -->
<svg viewBox="0 0 12 16" xmlns="http://www.w3.org/2000/svg">
<path
fill="#d24d4d"
fill-rule="evenodd"
stroke="#752929"
stroke-width="0.3"
stroke-linecap="round"
d="m 5.9999998,15.849652 c 0,0 5.8511182,-5.579947 5.8511182,-9.8134832 a 5.8511179,5.8880898 0 0 0 -11.7022358,0 c 0,4.2335362 5.8511176,9.8134832 5.8511176,9.8134832" />
<circle
fill="#752929"
fill-rule="evenodd"
cx="6"
cy="6"
r="3" />
</svg>

After

Width:  |  Height:  |  Size: 639 B

View File

@ -222,7 +222,13 @@
title: 'Open in Google Maps', title: 'Open in Google Maps',
zIndex: Core.rounds.length * 2, zIndex: Core.rounds.length * 2,
clickable: true, 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 () { round.realMarker.addListener('click', function () {
@ -236,10 +242,17 @@
zIndex: Core.rounds.length, zIndex: Core.rounds.length,
clickable: false, clickable: false,
draggable: 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: { label: {
color: '#ffffff', color: '#ffffff',
fontFamily: 'Roboto', fontFamily: 'Roboto',
fontSize: '18px', fontSize: '16px',
fontWeight: '500', fontWeight: '500',
text: '?' text: '?'
} }
@ -300,8 +313,15 @@
for (var i = 0; i < Core.rounds.length; ++i) { for (var i = 0; i < Core.rounds.length; ++i) {
var round = Core.rounds[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({ round.realMarker.setLabel({
color: '#812519', color: '#285624',
fontFamily: 'Roboto', fontFamily: 'Roboto',
fontSize: '16px', fontSize: '16px',
fontWeight: '500', fontWeight: '500',
@ -385,10 +405,17 @@
position: e.latLng, position: e.latLng,
clickable: false, clickable: false,
draggable: true, 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: { label: {
color: '#ffffff', color: '#ffffff',
fontFamily: 'Roboto', fontFamily: 'Roboto',
fontSize: '18px', fontSize: '16px',
fontWeight: '500', fontWeight: '500',
text: '?' text: '?'
} }

View File

@ -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);
};
})();

View File

@ -0,0 +1,5 @@
{
"dependencies": {
"leaflet": "^1.6.0"
}
}

8
public/static/yarn.lock Normal file
View File

@ -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==

View File

@ -9,13 +9,14 @@ if [ -f ${ROOT_DIR}/installed ]; then
exit 1 exit 1
fi 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 mysql --host=${DB_HOST} --user=${DB_USER} --password=${DB_PASSWORD} ${DB_NAME} < ${ROOT_DIR}/db/mapguesser.sql
if [ -z "${DEV}" ] || [ "${DEV}" -eq "0" ]; then if [ -z "${DEV}" ] || [ "${DEV}" -eq "0" ]; then
echo "Minifying JS, CSS and SVG files..." echo "Minifying JS, CSS and SVG files..."
${ROOT_DIR}/scripts/minify.sh ${ROOT_DIR}/scripts/minify.sh
fi fi

View File

@ -4,6 +4,8 @@ ROOT_DIR=$(dirname $(readlink -f "$0"))/..
. ${ROOT_DIR}/.env . ${ROOT_DIR}/.env
uglifyjs ${ROOT_DIR}/public/static/js/game.js -c -m -o ${ROOT_DIR}/public/static/js/game.js find ${ROOT_DIR}/public/static/js -type f -iname '*.js' -exec uglifyjs {} -c -m -o {} \;
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/css -type f -iname '*.css' -exec cleancss {} -o {} \;
find ${ROOT_DIR}/public/static/img -type f -iname '*.svg' -exec svgo {} -o {} \;

View File

@ -7,8 +7,10 @@ ROOT_DIR=$(dirname $(readlink -f "$0"))/..
echo "Installing Composer packages..." echo "Installing Composer packages..."
(cd ${ROOT_DIR} && composer install) (cd ${ROOT_DIR} && composer install)
echo "Installing Yarn packages..."
(cd ${ROOT_DIR}/public/static && yarn install)
if [ -z "${DEV}" ] || [ "${DEV}" -eq "0" ]; then if [ -z "${DEV}" ] || [ "${DEV}" -eq "0" ]; then
echo "Minifying JS, CSS and SVG files..." echo "Minifying JS, CSS and SVG files..."
${ROOT_DIR}/scripts/minify.sh ${ROOT_DIR}/scripts/minify.sh
fi fi

View File

@ -0,0 +1,53 @@
<?php namespace MapGuesser\Controller;
use MapGuesser\Database\Query\Select;
use MapGuesser\Interfaces\Database\IResultSet;
use MapGuesser\Interfaces\Response\IContent;
use MapGuesser\Response\HtmlContent;
use MapGuesser\Util\Geo\Bounds;
class MapAdminController
{
public function getMaps(): IContent {
//TODO
return new HtmlContent('admin/maps');
}
public function getMapEditor(array $parameters): IContent {
$mapId = (int) $parameters['mapId'];
$bounds = $this->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;
}
}

View File

@ -0,0 +1,27 @@
<?php $cssFiles = ['/static/node_modules/leaflet/dist/leaflet.css', '/static/css/map_editor.css']; ?>
<?php require ROOT . '/views/templates/main_header.php'; ?>
<?php require ROOT . '/views/templates/header.php'; ?>
<div id="map"></div>
<div id="control">
<button id="saveButton" class="fullWidth">Save</button>
<a class="button gray fullWidth marginTop" href="/admin/maps" title="Back to maps">Back to maps</a>
</div>
<div id="panorama"></div>
<div id="noPano">
<p class="bold">No panorama is available for this location.</p>
</div>
<div id="placeControl">
<button id="applyButton" class="fullWidth">Apply</button>
<button id="cancelButton" class="gray fullWidth marginTop">Cancel</button>
<button id="deleteButton" class="red fullWidth marginTop">Delete</button>
</div>
<script>
var tileUrl = '<?= $_ENV['LEAFLET_TILESERVER_URL'] ?>';
var mapId = '<?= $mapId ?>';
var mapBounds = <?= json_encode($bounds) ?>;
var places = <?= json_encode($places) ?>;
</script>
<script src="/static/node_modules/leaflet/dist/leaflet.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?key=<?= $_ENV['GOOGLE_MAPS_JS_API_KEY'] ?>"></script>
<script src="/static/js/map_editor.js"></script>
<?php require ROOT . '/views/templates/main_footer.php'; ?>

7
views/admin/maps.php Normal file
View File

@ -0,0 +1,7 @@
<?php require ROOT . '/views/templates/main_header.php'; ?>
<?php require ROOT . '/views/templates/header.php'; ?>
<div class="main">
<h2>Maps</h2>
<p>TODO</p>
</div>
<?php require ROOT . '/views/templates/main_footer.php'; ?>

View File

@ -1,9 +1,10 @@
<?php $cssFiles = ['/static/css/game.css']; ?>
<?php require ROOT . '/views/templates/main_header.php'; ?> <?php require ROOT . '/views/templates/main_header.php'; ?>
<div class="header small"> <div class="header small">
<div class="grid"> <div class="grid">
<h1> <h1>
<a href="/maps" title="Back to playable maps"> <a href="/maps" title="Back to playable maps">
<?php require ROOT . '/views/templates/icon.php'; ?> <img class="inline" src="/static/img/icon.svg">
<span>MapGuesser</span> <span>MapGuesser</span>
</a> </a>
</h1> </h1>

View File

@ -1,8 +1,9 @@
<?php $cssFiles = ['/static/css/maps.css']; ?>
<?php require ROOT . '/views/templates/main_header.php'; ?> <?php require ROOT . '/views/templates/main_header.php'; ?>
<?php require ROOT . '/views/templates/header.php'; ?> <?php require ROOT . '/views/templates/header.php'; ?>
<div class="main"> <div class="main">
<h2>Playable maps</h2> <h2>Playable maps</h2>
<div class="mapContainer"> <div id="mapContainer">
<?php foreach ($maps as $map) : ?> <?php foreach ($maps as $map) : ?>
<div class="mapItem"> <div class="mapItem">
<div class="title"> <div class="title">

View File

@ -1,7 +1,7 @@
<div class="header"> <div class="header">
<h1> <h1>
<a href="/" title="MapGuesser"> <a href="/" title="MapGuesser">
<?php require ROOT . '/views/templates/icon.php'; ?> <img class="inline" src="/static/img/icon.svg">
MapGuesser MapGuesser
</a> </a>
</h1> </h1>

View File

@ -1,4 +0,0 @@
<?php /* Copyright (c) 2019 The Bootstrap Authors. License can be found in 'USED_SOFTWARE' in section 'Bootstrap Icons'. */ ?>
<svg class="inline" width="1em" height="1em" viewBox="0 0 16 16" fill="#28a745" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M15.817.613A.5.5 0 0 1 16 1v13a.5.5 0 0 1-.402.49l-5 1a.502.502 0 0 1-.196 0L5.5 14.51l-4.902.98A.5.5 0 0 1 0 15V2a.5.5 0 0 1 .402-.49l5-1a.5.5 0 0 1 .196 0l4.902.98 4.902-.98a.5.5 0 0 1 .415.103zM10 2.41l-4-.8v11.98l4 .8V2.41zm1 11.98l4-.8V1.61l-4 .8v11.98zm-6-.8V1.61l-4 .8v11.98l4-.8z" />
</svg>

View File

@ -5,6 +5,11 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>MapGuesser</title> <title>MapGuesser</title>
<link href="/static/css/mapguesser.css" rel="stylesheet"> <link href="/static/css/mapguesser.css" rel="stylesheet">
<?php if (isset($cssFiles)) : ?>
<?php foreach ($cssFiles as $cssFile) : ?>
<link href="<?= $cssFile ?>" rel="stylesheet">
<?php endforeach; ?>
<?php endif; ?>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;500&family=Roboto+Mono:wght@300;500&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;500&family=Roboto+Mono:wght@300;500&display=swap" rel="stylesheet">
<link rel="icon" type="image/png" sizes="192x192" href="/static/img/favicon/192x192.png"> <link rel="icon" type="image/png" sizes="192x192" href="/static/img/favicon/192x192.png">
<link rel="icon" type="image/png" sizes="96x96" href="/static/img/favicon/96x96.png"> <link rel="icon" type="image/png" sizes="96x96" href="/static/img/favicon/96x96.png">