Merged in feature/MAPG-103-map-editor-ui-elements (pull request #88)
Feature/MAPG-103 map editor ui elements
This commit is contained in:
commit
16bcbaecbe
@ -19,13 +19,14 @@ Container::$routeCollection->get('maps', 'maps', [MapGuesser\Controller\MapsCont
|
||||
Container::$routeCollection->group('game', function (MapGuesser\Routing\RouteCollection $routeCollection) {
|
||||
$routeCollection->get('game', '{mapId}', [MapGuesser\Controller\GameController::class, 'getGame']);
|
||||
$routeCollection->get('game-json', '{mapId}/json', [MapGuesser\Controller\GameController::class, 'getGameJson']);
|
||||
$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->get('newPlace-json', '{mapId}/newPlace.json', [MapGuesser\Controller\GameFlowController::class, 'getNewPlace']);
|
||||
$routeCollection->post('guess-json', '{mapId}/guess.json', [MapGuesser\Controller\GameFlowController::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']);
|
||||
$routeCollection->get('admin.place', 'place.json/{placeId}', [MapGuesser\Controller\MapAdminController::class, 'getPlace']);
|
||||
$routeCollection->post('admin.saveMap', 'saveMap/{mapId}/json', [MapGuesser\Controller\MapAdminController::class, 'saveMap']);
|
||||
});
|
||||
|
||||
$match = Container::$routeCollection->match($method, explode('/', $url));
|
||||
|
@ -1,13 +1,3 @@
|
||||
#roundInfo {
|
||||
line-height: inherit;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#roundInfo p {
|
||||
font-size: 16px;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
#panorama {
|
||||
width: 100%;
|
||||
height: calc(100% - 40px);
|
||||
@ -110,6 +100,9 @@
|
||||
}
|
||||
|
||||
@media screen and (max-width: 599px) {
|
||||
#mapName {
|
||||
display: none;
|
||||
}
|
||||
#showGuessButtonContainer {
|
||||
position: absolute;
|
||||
left: 20px;
|
||||
|
@ -1,9 +1,27 @@
|
||||
#metadata {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
left: 10px;
|
||||
padding: 10px;
|
||||
background-color: #eeeeee;
|
||||
border: solid 1px #555555;
|
||||
border-radius: 3px;
|
||||
box-sizing: border-box;
|
||||
opacity: 0.95;
|
||||
z-index: 2;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#map {
|
||||
width: 100%;
|
||||
height: calc(100% - 50px);
|
||||
height: calc(100% - 40px);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.leaflet-container {
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
#panorama {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
@ -24,53 +42,65 @@
|
||||
|
||||
#control {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
right: 10px;
|
||||
width: 125px;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
#placeControl {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
z-index: 3;
|
||||
width: 100px;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#deleteButton {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 999px) and (min-height: 600px) {
|
||||
#metadata {
|
||||
width: calc(100% - 155px);
|
||||
}
|
||||
#map.selected {
|
||||
height: calc(50% - 25px);
|
||||
height: calc(50% - 20px);
|
||||
}
|
||||
#panorama, #noPano {
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
height: calc(50% - 25px);
|
||||
}
|
||||
#control {
|
||||
right: 10px;
|
||||
top: 60px;
|
||||
height: calc(50% - 20px);
|
||||
}
|
||||
#placeControl {
|
||||
right: 10px;
|
||||
top: calc(50% + 35px);
|
||||
top: calc(50% + 30px);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1000px), (max-height: 599px) {
|
||||
#metadata {
|
||||
width: calc(50% - 20px);
|
||||
}
|
||||
#metadata.selected {
|
||||
top: 95px;
|
||||
}
|
||||
#map.selected {
|
||||
width: 50%;
|
||||
}
|
||||
#panorama, #noPano {
|
||||
top: 50px;
|
||||
top: 40px;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 50%;
|
||||
}
|
||||
#control, #placeControl {
|
||||
right: 10px;
|
||||
top: 60px;
|
||||
#placeControl {
|
||||
top: 50px;
|
||||
}
|
||||
#modified.selected {
|
||||
right: calc(50% + 10px);
|
||||
}
|
||||
#control.selected {
|
||||
right: calc(50% + 10px);
|
||||
top: 60px;
|
||||
}
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ sub {
|
||||
}
|
||||
|
||||
svg.inline, img.inline {
|
||||
display: inline-block;
|
||||
display: inline;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
@ -228,6 +228,24 @@ div.header.small {
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
div.header>div.grid>:nth-child(2) {
|
||||
line-height: inherit;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
div.header>div.grid>:nth-child(2)>span {
|
||||
padding-left: 6px;
|
||||
}
|
||||
|
||||
div.header>div.grid>:nth-child(2)>span>a:link, div.header>div.grid>:nth-child(2)>span>a:visited {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
div.header>div.grid>:nth-child(2)>span:not(:last-child) {
|
||||
border-right: solid white 1px;
|
||||
padding-right: 6px;
|
||||
}
|
||||
|
||||
div.main {
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
(function () {
|
||||
var Core = {
|
||||
var Game = {
|
||||
NUMBER_OF_ROUNDS: 5,
|
||||
MAX_SCORE: 1000,
|
||||
|
||||
@ -17,54 +17,54 @@
|
||||
initialize: function () {
|
||||
document.getElementById('loading').style.visibility = 'visible';
|
||||
document.getElementById('cover').style.visibility = 'visible';
|
||||
document.getElementById('currentRound').innerHTML = '1/' + String(Core.NUMBER_OF_ROUNDS);
|
||||
document.getElementById('currentRound').innerHTML = '1/' + String(Game.NUMBER_OF_ROUNDS);
|
||||
document.getElementById('currentScoreSum').innerHTML = '0/0';
|
||||
|
||||
Core.map.setOptions({
|
||||
Game.map.setOptions({
|
||||
draggableCursor: 'crosshair'
|
||||
});
|
||||
Core.map.fitBounds(mapBounds);
|
||||
Game.map.fitBounds(mapBounds);
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.responseType = 'json';
|
||||
xhr.onload = function () {
|
||||
document.getElementById('loading').style.visibility = 'hidden';
|
||||
document.getElementById('cover').style.visibility = 'hidden';
|
||||
|
||||
if (this.response.error) {
|
||||
//TODO: handle this error
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById('loading').style.visibility = 'hidden';
|
||||
document.getElementById('cover').style.visibility = 'hidden';
|
||||
|
||||
Core.panoId = this.response.panoId;
|
||||
Game.panoId = this.response.panoId;
|
||||
|
||||
if (this.response.history) {
|
||||
for (var i = 0; i < this.response.history.length; ++i) {
|
||||
var round = this.response.history[i];
|
||||
Core.rounds.push({ position: round.position, guessPosition: round.guessPosition, realMarker: null, guessMarker: null, line: null });
|
||||
Core.addRealGuessPair(round.position, round.guessPosition, true);
|
||||
Core.scoreSum += round.score;
|
||||
Game.rounds.push({ position: round.position, guessPosition: round.guessPosition, realMarker: null, guessMarker: null, line: null });
|
||||
Game.addRealGuessPair(round.position, round.guessPosition, true);
|
||||
Game.scoreSum += round.score;
|
||||
}
|
||||
|
||||
document.getElementById('currentRound').innerHTML = String(Core.rounds.length) + '/' + String(Core.NUMBER_OF_ROUNDS);
|
||||
document.getElementById('currentScoreSum').innerHTML = String(Core.scoreSum) + '/' + String(Core.rounds.length * Core.MAX_SCORE);
|
||||
document.getElementById('currentRound').innerHTML = String(Game.rounds.length) + '/' + String(Game.NUMBER_OF_ROUNDS);
|
||||
document.getElementById('currentScoreSum').innerHTML = String(Game.scoreSum) + '/' + String(Game.rounds.length * Game.MAX_SCORE);
|
||||
}
|
||||
|
||||
Core.startNewRound();
|
||||
Game.startNewRound();
|
||||
};
|
||||
|
||||
xhr.open('GET', '/game/' + mapId + '/position.json', true);
|
||||
xhr.open('GET', '/game/' + mapId + '/newPlace.json', true);
|
||||
xhr.send();
|
||||
},
|
||||
|
||||
resetGame: function () {
|
||||
if (Core.guessMarker) {
|
||||
Core.guessMarker.setMap(null);
|
||||
Core.guessMarker = null;
|
||||
reset: function () {
|
||||
if (Game.guessMarker) {
|
||||
Game.guessMarker.setMap(null);
|
||||
Game.guessMarker = null;
|
||||
}
|
||||
|
||||
for (var i = 0; i < Core.rounds.length; ++i) {
|
||||
var round = Core.rounds[i];
|
||||
for (var i = 0; i < Game.rounds.length; ++i) {
|
||||
var round = Game.rounds[i];
|
||||
|
||||
if (round.realMarker && round.guessMarker && round.line) {
|
||||
round.realMarker.setMap(null);
|
||||
@ -73,8 +73,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
Core.rounds = [];
|
||||
Core.scoreSum = 0;
|
||||
Game.rounds = [];
|
||||
Game.scoreSum = 0;
|
||||
|
||||
var distanceInfo = document.getElementById('distanceInfo');
|
||||
distanceInfo.children[0].style.display = null;
|
||||
@ -89,14 +89,14 @@
|
||||
document.getElementById('guess').style.visibility = null;
|
||||
document.getElementById('guess').classList.remove('result');
|
||||
|
||||
Core.initialize();
|
||||
Game.initialize();
|
||||
},
|
||||
|
||||
resetRound: function () {
|
||||
document.getElementById('scoreBar').style.width = null;
|
||||
|
||||
if (Core.rounds.length > 0) {
|
||||
var lastRound = Core.rounds[Core.rounds.length - 1];
|
||||
if (Game.rounds.length > 0) {
|
||||
var lastRound = Game.rounds[Game.rounds.length - 1];
|
||||
|
||||
lastRound.realMarker.setVisible(false);
|
||||
lastRound.guessMarker.setVisible(false);
|
||||
@ -108,20 +108,20 @@
|
||||
document.getElementById('guess').style.visibility = null;
|
||||
document.getElementById('guess').classList.remove('result')
|
||||
|
||||
Core.map.setOptions({
|
||||
Game.map.setOptions({
|
||||
draggableCursor: 'crosshair'
|
||||
});
|
||||
Core.map.fitBounds(mapBounds);
|
||||
Game.map.fitBounds(mapBounds);
|
||||
|
||||
Core.startNewRound();
|
||||
Game.startNewRound();
|
||||
},
|
||||
|
||||
startNewRound: function () {
|
||||
Core.rounds.push({ position: null, guessPosition: null, realMarker: null, guessMarker: null, line: null });
|
||||
Game.rounds.push({ position: null, guessPosition: null, realMarker: null, guessMarker: null, line: null });
|
||||
|
||||
document.getElementById('currentRound').innerHTML = String(Core.rounds.length) + '/' + String(Core.NUMBER_OF_ROUNDS);
|
||||
document.getElementById('currentRound').innerHTML = String(Game.rounds.length) + '/' + String(Game.NUMBER_OF_ROUNDS);
|
||||
|
||||
Core.loadPano(Core.panoId);
|
||||
Game.loadPano(Game.panoId);
|
||||
},
|
||||
|
||||
handleErrorResponse: function (error) {
|
||||
@ -132,7 +132,7 @@
|
||||
xhr.onload = function () {
|
||||
mapBounds = this.response.bounds;
|
||||
|
||||
Core.resetGame();
|
||||
Game.reset();
|
||||
};
|
||||
|
||||
xhr.open('GET', '/game/' + mapId + '/json', true);
|
||||
@ -140,20 +140,25 @@
|
||||
},
|
||||
|
||||
loadPano: function (panoId) {
|
||||
if (Core.adaptGuess) {
|
||||
if (Game.adaptGuess) {
|
||||
document.getElementById('guess').classList.add('adapt');
|
||||
}
|
||||
|
||||
Core.panorama.setPov({ heading: 0, pitch: 0 });
|
||||
Core.panorama.setZoom(0);
|
||||
Core.panorama.setPano(panoId);
|
||||
Game.panorama.setPov({ heading: 0, pitch: 0 });
|
||||
Game.panorama.setZoom(0);
|
||||
Game.panorama.setPano(panoId);
|
||||
},
|
||||
|
||||
evaluateGuess: function () {
|
||||
var guessPosition = Core.guessMarker.getPosition().toJSON();
|
||||
Core.rounds[Core.rounds.length - 1].guessPosition = guessPosition;
|
||||
if (!Game.guessMarker) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Core.adaptGuess) {
|
||||
var guessPosition = Game.guessMarker.getPosition().toJSON();
|
||||
Game.rounds[Game.rounds.length - 1].guessPosition = guessPosition;
|
||||
|
||||
document.getElementById('guessButton').disabled = true;
|
||||
if (Game.adaptGuess) {
|
||||
document.getElementById('guess').classList.remove('adapt');
|
||||
}
|
||||
document.getElementById('loading').style.visibility = 'visible';
|
||||
@ -167,45 +172,45 @@
|
||||
xhr.responseType = 'json';
|
||||
xhr.onload = function () {
|
||||
if (this.response.error) {
|
||||
Core.handleErrorResponse(this.response.error);
|
||||
Game.handleErrorResponse(this.response.error);
|
||||
return;
|
||||
}
|
||||
|
||||
Core.guessMarker.setMap(null);
|
||||
Core.guessMarker = null;
|
||||
Game.guessMarker.setMap(null);
|
||||
Game.guessMarker = null;
|
||||
|
||||
document.getElementById('loading').style.visibility = 'hidden';
|
||||
document.getElementById('guess').classList.add('result');
|
||||
|
||||
Core.scoreSum += this.response.result.score;
|
||||
document.getElementById('currentScoreSum').innerHTML = String(Core.scoreSum) + '/' + String(Core.rounds.length * Core.MAX_SCORE);
|
||||
Game.scoreSum += this.response.result.score;
|
||||
document.getElementById('currentScoreSum').innerHTML = String(Game.scoreSum) + '/' + String(Game.rounds.length * Game.MAX_SCORE);
|
||||
|
||||
Core.rounds[Core.rounds.length - 1].position = this.response.result.position;
|
||||
Core.addRealGuessPair(this.response.result.position, guessPosition);
|
||||
Game.rounds[Game.rounds.length - 1].position = this.response.result.position;
|
||||
Game.addRealGuessPair(this.response.result.position, guessPosition);
|
||||
|
||||
var resultBounds = new google.maps.LatLngBounds();
|
||||
resultBounds.extend(this.response.result.position);
|
||||
resultBounds.extend(guessPosition);
|
||||
|
||||
Core.map.setOptions({
|
||||
Game.map.setOptions({
|
||||
draggableCursor: 'grab'
|
||||
});
|
||||
Core.map.fitBounds(resultBounds);
|
||||
Game.map.fitBounds(resultBounds);
|
||||
|
||||
document.getElementById('distance').innerHTML = Util.printDistanceForHuman(this.response.result.distance);
|
||||
document.getElementById('score').innerHTML = this.response.result.score;
|
||||
|
||||
var scoreBarProperties = Core.calculateScoreBarProperties(this.response.result.score, Core.MAX_SCORE);
|
||||
var scoreBarProperties = Game.calculateScoreBarProperties(this.response.result.score, Game.MAX_SCORE);
|
||||
var scoreBar = document.getElementById('scoreBar');
|
||||
scoreBar.style.backgroundColor = scoreBarProperties.backgroundColor;
|
||||
scoreBar.style.width = scoreBarProperties.width;
|
||||
|
||||
if (Core.rounds.length === Core.NUMBER_OF_ROUNDS) {
|
||||
if (Game.rounds.length === Game.NUMBER_OF_ROUNDS) {
|
||||
document.getElementById('continueButton').style.display = 'none';
|
||||
document.getElementById('showSummaryButton').style.display = 'block';
|
||||
}
|
||||
|
||||
Core.panoId = this.response.panoId;
|
||||
Game.panoId = this.response.panoId;
|
||||
};
|
||||
|
||||
xhr.open('POST', '/game/' + mapId + '/guess.json', true);
|
||||
@ -213,14 +218,14 @@
|
||||
},
|
||||
|
||||
addRealGuessPair: function (position, guessPosition, hidden) {
|
||||
var round = Core.rounds[Core.rounds.length - 1];
|
||||
var round = Game.rounds[Game.rounds.length - 1];
|
||||
|
||||
round.realMarker = new google.maps.Marker({
|
||||
map: Core.map,
|
||||
map: Game.map,
|
||||
visible: !hidden,
|
||||
position: position,
|
||||
title: 'Open in Google Maps',
|
||||
zIndex: Core.rounds.length * 2,
|
||||
zIndex: Game.rounds.length * 2,
|
||||
clickable: true,
|
||||
draggable: false,
|
||||
icon: {
|
||||
@ -236,10 +241,10 @@
|
||||
});
|
||||
|
||||
round.guessMarker = new google.maps.Marker({
|
||||
map: Core.map,
|
||||
map: Game.map,
|
||||
visible: !hidden,
|
||||
position: guessPosition,
|
||||
zIndex: Core.rounds.length,
|
||||
zIndex: Game.rounds.length,
|
||||
clickable: false,
|
||||
draggable: false,
|
||||
icon: {
|
||||
@ -259,7 +264,7 @@
|
||||
});
|
||||
|
||||
round.line = new google.maps.Polyline({
|
||||
map: Core.map,
|
||||
map: Game.map,
|
||||
visible: !hidden,
|
||||
path: [
|
||||
position,
|
||||
@ -310,8 +315,8 @@
|
||||
|
||||
var resultBounds = new google.maps.LatLngBounds();
|
||||
|
||||
for (var i = 0; i < Core.rounds.length; ++i) {
|
||||
var round = Core.rounds[i];
|
||||
for (var i = 0; i < Game.rounds.length; ++i) {
|
||||
var round = Game.rounds[i];
|
||||
|
||||
round.realMarker.setIcon({
|
||||
url: '/static/img/markers/marker-green-empty.svg',
|
||||
@ -335,32 +340,32 @@
|
||||
resultBounds.extend(round.guessPosition);
|
||||
}
|
||||
|
||||
Core.map.fitBounds(resultBounds);
|
||||
Game.map.fitBounds(resultBounds);
|
||||
|
||||
document.getElementById('scoreSum').innerHTML = String(Core.scoreSum);
|
||||
document.getElementById('scoreSum').innerHTML = String(Game.scoreSum);
|
||||
|
||||
var scoreBarProperties = Core.calculateScoreBarProperties(Core.scoreSum, Core.NUMBER_OF_ROUNDS * Core.MAX_SCORE);
|
||||
var scoreBarProperties = Game.calculateScoreBarProperties(Game.scoreSum, Game.NUMBER_OF_ROUNDS * Game.MAX_SCORE);
|
||||
var scoreBar = document.getElementById('scoreBar');
|
||||
scoreBar.style.backgroundColor = scoreBarProperties.backgroundColor;
|
||||
scoreBar.style.width = scoreBarProperties.width;
|
||||
},
|
||||
|
||||
rewriteGoogleLink: function () {
|
||||
if (!Core.googleLink) {
|
||||
if (!Game.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;
|
||||
Game.googleLink = a;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
if (Core.googleLink) {
|
||||
Core.googleLink.title = 'Google Maps';
|
||||
Core.googleLink.href = 'https://maps.google.com/maps';
|
||||
if (Game.googleLink) {
|
||||
Game.googleLink.title = 'Google Maps';
|
||||
Game.googleLink.href = 'https://maps.google.com/maps';
|
||||
}
|
||||
}, 1);
|
||||
}
|
||||
@ -381,27 +386,27 @@
|
||||
};
|
||||
|
||||
if (!('ontouchstart' in document.documentElement)) {
|
||||
Core.adaptGuess = true;
|
||||
Game.adaptGuess = true;
|
||||
}
|
||||
|
||||
Core.map = new google.maps.Map(document.getElementById('map'), {
|
||||
Game.map = new google.maps.Map(document.getElementById('map'), {
|
||||
disableDefaultUI: true,
|
||||
clickableIcons: false,
|
||||
draggingCursor: 'grabbing'
|
||||
});
|
||||
|
||||
Core.map.addListener('click', function (e) {
|
||||
if (Core.rounds[Core.rounds.length - 1].guessPosition) {
|
||||
Game.map.addListener('click', function (e) {
|
||||
if (Game.rounds[Game.rounds.length - 1].guessPosition) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Core.guessMarker) {
|
||||
Core.guessMarker.setPosition(e.latLng);
|
||||
if (Game.guessMarker) {
|
||||
Game.guessMarker.setPosition(e.latLng);
|
||||
return;
|
||||
}
|
||||
|
||||
Core.guessMarker = new google.maps.Marker({
|
||||
map: Core.map,
|
||||
Game.guessMarker = new google.maps.Marker({
|
||||
map: Game.map,
|
||||
position: e.latLng,
|
||||
clickable: false,
|
||||
draggable: true,
|
||||
@ -424,22 +429,22 @@
|
||||
document.getElementById('guessButton').disabled = false;
|
||||
});
|
||||
|
||||
Core.panorama = new google.maps.StreetViewPanorama(document.getElementById('panorama'), {
|
||||
Game.panorama = new google.maps.StreetViewPanorama(document.getElementById('panorama'), {
|
||||
disableDefaultUI: true,
|
||||
linksControl: true,
|
||||
showRoadLabels: false,
|
||||
motionTracking: false
|
||||
});
|
||||
|
||||
Core.panorama.addListener('position_changed', function () {
|
||||
Core.rewriteGoogleLink();
|
||||
Game.panorama.addListener('position_changed', function () {
|
||||
Game.rewriteGoogleLink();
|
||||
});
|
||||
|
||||
Core.panorama.addListener('pov_changed', function () {
|
||||
Core.rewriteGoogleLink();
|
||||
Game.panorama.addListener('pov_changed', function () {
|
||||
Game.rewriteGoogleLink();
|
||||
});
|
||||
|
||||
Core.initialize();
|
||||
Game.initialize();
|
||||
|
||||
document.getElementById('showGuessButton').onclick = function () {
|
||||
this.style.visibility = 'hidden';
|
||||
@ -452,24 +457,18 @@
|
||||
}
|
||||
|
||||
document.getElementById('guessButton').onclick = function () {
|
||||
if (!Core.guessMarker) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.disabled = true;
|
||||
|
||||
Core.evaluateGuess();
|
||||
Game.evaluateGuess();
|
||||
}
|
||||
|
||||
document.getElementById('continueButton').onclick = function () {
|
||||
Core.resetRound();
|
||||
Game.resetRound();
|
||||
}
|
||||
|
||||
document.getElementById('showSummaryButton').onclick = function () {
|
||||
Core.showSummary();
|
||||
Game.showSummary();
|
||||
}
|
||||
|
||||
document.getElementById('startNewGameButton').onclick = function () {
|
||||
Core.resetGame();
|
||||
Game.reset();
|
||||
}
|
||||
})();
|
||||
|
@ -1,8 +1,30 @@
|
||||
'use strict';
|
||||
|
||||
(function () {
|
||||
var MapEditor = {
|
||||
metadata: {
|
||||
name: null,
|
||||
description: null
|
||||
},
|
||||
map: null,
|
||||
panorama: null,
|
||||
selectedMarker: null,
|
||||
added: {},
|
||||
edited: {},
|
||||
deleted: {},
|
||||
|
||||
editMetadata: function () {
|
||||
var form = document.getElementById('metadataForm');
|
||||
|
||||
MapEditor.metadata.name = form.elements.name.value;
|
||||
MapEditor.metadata.description = form.elements.description.value;
|
||||
|
||||
document.getElementById('mapName').innerHTML = form.elements.name.value ? form.elements.name.value : '[unnamed map]';
|
||||
|
||||
document.getElementById('metadata').style.visibility = 'hidden';
|
||||
|
||||
document.getElementById('saveButton').disabled = false;
|
||||
},
|
||||
|
||||
getPlace: function (placeId, marker) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
@ -13,28 +35,85 @@
|
||||
if (!this.response.panoId) {
|
||||
document.getElementById('noPano').style.visibility = 'visible';
|
||||
|
||||
marker.noPano = true;
|
||||
places[marker.placeId].panoId = -1;
|
||||
places[marker.placeId].noPano = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
MapEditor.loadPano(this.response.panoId);
|
||||
places[marker.placeId].panoId = this.response.panoId;
|
||||
places[marker.placeId].noPano = false;
|
||||
|
||||
MapEditor.loadPano(this.response.panoId, places[marker.placeId].pov);
|
||||
};
|
||||
|
||||
xhr.open('GET', '/admin/place.json/' + placeId, true);
|
||||
xhr.send();
|
||||
},
|
||||
|
||||
loadPano: function (panoId) {
|
||||
loadPano: function (panoId, pov) {
|
||||
MapEditor.panorama.setVisible(true);
|
||||
MapEditor.panorama.setPov({ heading: 0, pitch: 0 });
|
||||
MapEditor.panorama.setZoom(0);
|
||||
MapEditor.panorama.setPov({ heading: pov.heading, pitch: pov.pitch });
|
||||
MapEditor.panorama.setZoom(pov.zoom);
|
||||
MapEditor.panorama.setPano(panoId);
|
||||
},
|
||||
|
||||
select: function (marker) {
|
||||
document.getElementById('loading').style.visibility = 'visible';
|
||||
loadPanoForNewPlace: function (panoLocationData) {
|
||||
var placeId = MapEditor.selectedMarker.placeId;
|
||||
|
||||
if (!panoLocationData) {
|
||||
places[placeId].panoId = -1;
|
||||
places[placeId].noPano = true;
|
||||
|
||||
document.getElementById('noPano').style.visibility = 'visible';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var latLng = panoLocationData.latLng;
|
||||
|
||||
places[placeId].panoId = panoLocationData.pano;
|
||||
places[placeId].lat = latLng.lat();
|
||||
places[placeId].lng = latLng.lng();
|
||||
|
||||
MapEditor.selectedMarker.setLatLng({ lat: places[placeId].lat, lng: places[placeId].lng });
|
||||
MapEditor.map.panTo(MapEditor.selectedMarker.getLatLng());
|
||||
|
||||
MapEditor.panorama.setVisible(true);
|
||||
MapEditor.panorama.setPov({ heading: 0.0, pitch: 0.0 });
|
||||
MapEditor.panorama.setZoom(0.0);
|
||||
MapEditor.panorama.setPano(panoLocationData.pano);
|
||||
},
|
||||
|
||||
requestPanoData: function (location, canBeIndoor) {
|
||||
var sv = new google.maps.StreetViewService();
|
||||
|
||||
sv.getPanorama({
|
||||
location: location,
|
||||
preference: google.maps.StreetViewPreference.NEAREST,
|
||||
radius: 100,
|
||||
source: canBeIndoor ? google.maps.StreetViewSource.DEFAULT : google.maps.StreetViewSource.OUTDOOR
|
||||
}, function (data, status) {
|
||||
var panoLocationData = status === google.maps.StreetViewStatus.OK ? data.location : null;
|
||||
|
||||
if (panoLocationData === null && !canBeIndoor) {
|
||||
MapEditor.requestPanoData(location, true);
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById('loading').style.visibility = 'hidden';
|
||||
|
||||
MapEditor.loadPanoForNewPlace(panoLocationData);
|
||||
});
|
||||
},
|
||||
|
||||
select: function (marker) {
|
||||
if (MapEditor.selectedMarker === marker) {
|
||||
MapEditor.closePlace();
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById('metadata').classList.add('selected');
|
||||
document.getElementById('map').classList.add('selected');
|
||||
document.getElementById('control').classList.add('selected');
|
||||
document.getElementById('noPano').style.visibility = 'hidden';
|
||||
@ -44,24 +123,195 @@
|
||||
MapEditor.resetSelected();
|
||||
MapEditor.selectedMarker = marker;
|
||||
|
||||
marker.setIcon(IconCollection.iconBlue);
|
||||
marker.setZIndexOffset(2000);
|
||||
|
||||
MapEditor.map.invalidateSize(true);
|
||||
MapEditor.map.panTo(marker.getLatLng());
|
||||
|
||||
MapEditor.panorama.setVisible(false);
|
||||
|
||||
if (marker.placeId) {
|
||||
marker.setIcon(IconCollection.iconBlue);
|
||||
marker.setZIndexOffset(2000);
|
||||
|
||||
document.getElementById('deleteButton').style.display = 'block';
|
||||
|
||||
if (places[marker.placeId].panoId) {
|
||||
if (places[marker.placeId].panoId === -1) {
|
||||
document.getElementById('noPano').style.visibility = 'visible';
|
||||
} else {
|
||||
MapEditor.loadPano(places[marker.placeId].panoId, places[marker.placeId].pov);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById('loading').style.visibility = 'visible';
|
||||
|
||||
MapEditor.getPlace(marker.placeId, marker);
|
||||
} else {
|
||||
marker.placeId = 'new_' + new Date().getTime();
|
||||
|
||||
var latLng = marker.getLatLng();
|
||||
|
||||
places[marker.placeId] = { id: null, lat: latLng.lat, lng: latLng.lng, panoId: null, pov: { heading: 0.0, pitch: 0.0, zoom: 0 }, noPano: false };
|
||||
|
||||
document.getElementById('loading').style.visibility = 'visible';
|
||||
|
||||
MapEditor.requestPanoData(latLng);
|
||||
}
|
||||
},
|
||||
|
||||
resetSelected: function () {
|
||||
resetSelected: function (del) {
|
||||
if (!MapEditor.selectedMarker) {
|
||||
return;
|
||||
}
|
||||
|
||||
MapEditor.selectedMarker.setIcon(MapEditor.selectedMarker.noPano ? IconCollection.iconRed : IconCollection.iconGreen);
|
||||
var placeId = MapEditor.selectedMarker.placeId
|
||||
|
||||
if (places[placeId].id && !del) {
|
||||
MapEditor.selectedMarker.setIcon(places[placeId].noPano ? IconCollection.iconRed : IconCollection.iconGreen);
|
||||
MapEditor.selectedMarker.setZIndexOffset(1000);
|
||||
} else {
|
||||
delete places[placeId];
|
||||
MapEditor.map.removeLayer(MapEditor.selectedMarker);
|
||||
}
|
||||
|
||||
document.getElementById('deleteButton').style.display = 'none';
|
||||
},
|
||||
|
||||
applyPlace: function () {
|
||||
var placeId = MapEditor.selectedMarker.placeId;
|
||||
|
||||
if (!places[placeId].noPano) {
|
||||
var latLng = MapEditor.panorama.getPosition();
|
||||
var pov = MapEditor.panorama.getPov();
|
||||
var zoom = MapEditor.panorama.getZoom();
|
||||
|
||||
places[placeId].lat = latLng.lat();
|
||||
places[placeId].lng = latLng.lng();
|
||||
places[placeId].panoId = MapEditor.panorama.getPano();
|
||||
places[placeId].pov = { heading: pov.heading, pitch: pov.pitch, zoom: zoom };
|
||||
}
|
||||
|
||||
if (!places[placeId].id) {
|
||||
places[placeId].id = placeId;
|
||||
MapEditor.added[placeId] = places[placeId];
|
||||
|
||||
document.getElementById('added').innerHTML = String(Object.keys(MapEditor.added).length);
|
||||
|
||||
document.getElementById('deleteButton').style.display = 'block';
|
||||
} else {
|
||||
if (!MapEditor.added[placeId]) {
|
||||
MapEditor.edited[placeId] = places[placeId];
|
||||
|
||||
document.getElementById('edited').innerHTML = String(Object.keys(MapEditor.edited).length);
|
||||
} else {
|
||||
MapEditor.added[placeId] = places[placeId];
|
||||
}
|
||||
}
|
||||
|
||||
MapEditor.selectedMarker.setLatLng({ lat: places[placeId].lat, lng: places[placeId].lng });
|
||||
|
||||
document.getElementById('saveButton').disabled = false;
|
||||
},
|
||||
|
||||
closePlace: function (del) {
|
||||
document.getElementById('metadata').classList.remove('selected')
|
||||
document.getElementById('map').classList.remove('selected');
|
||||
document.getElementById('control').classList.remove('selected');
|
||||
document.getElementById('noPano').style.visibility = 'hidden';
|
||||
document.getElementById('panorama').style.visibility = 'hidden';
|
||||
document.getElementById('placeControl').style.visibility = 'hidden';
|
||||
|
||||
MapEditor.resetSelected(del);
|
||||
MapEditor.selectedMarker = null;
|
||||
|
||||
MapEditor.map.invalidateSize(true);
|
||||
},
|
||||
|
||||
deletePlace: function () {
|
||||
var placeId = MapEditor.selectedMarker.placeId;
|
||||
|
||||
if (places[placeId].id && !MapEditor.added[placeId]) {
|
||||
MapEditor.deleted[placeId] = places[placeId];
|
||||
|
||||
document.getElementById('deleted').innerHTML = String(Object.keys(MapEditor.deleted).length);
|
||||
}
|
||||
|
||||
MapEditor.closePlace(true);
|
||||
|
||||
delete MapEditor.added[placeId];
|
||||
delete MapEditor.edited[placeId];
|
||||
|
||||
document.getElementById('added').innerHTML = String(Object.keys(MapEditor.added).length);
|
||||
document.getElementById('edited').innerHTML = String(Object.keys(MapEditor.edited).length);
|
||||
|
||||
document.getElementById('saveButton').disabled = false;
|
||||
},
|
||||
|
||||
saveMap: function () {
|
||||
document.getElementById('loading').style.visibility = 'visible';
|
||||
|
||||
var data = new FormData();
|
||||
|
||||
if (MapEditor.metadata.name !== null) {
|
||||
data.append('name', MapEditor.metadata.name);
|
||||
}
|
||||
if (MapEditor.metadata.description !== null) {
|
||||
data.append('description', MapEditor.metadata.description);
|
||||
}
|
||||
|
||||
for (var placeId in MapEditor.added) {
|
||||
if (!MapEditor.added.hasOwnProperty(placeId)) {
|
||||
continue;
|
||||
}
|
||||
data.append('added[]', JSON.stringify(MapEditor.added[placeId]));
|
||||
}
|
||||
for (var placeId in MapEditor.edited) {
|
||||
if (!MapEditor.edited.hasOwnProperty(placeId)) {
|
||||
continue;
|
||||
}
|
||||
data.append('edited[]', JSON.stringify(MapEditor.edited[placeId]));
|
||||
}
|
||||
for (var placeId in MapEditor.deleted) {
|
||||
if (!MapEditor.deleted.hasOwnProperty(placeId)) {
|
||||
continue;
|
||||
}
|
||||
data.append('deleted[]', JSON.stringify(MapEditor.deleted[placeId]));
|
||||
}
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.responseType = 'json';
|
||||
xhr.onload = function () {
|
||||
document.getElementById('loading').style.visibility = 'hidden';
|
||||
|
||||
if (this.response.error) {
|
||||
//TODO: handle this error
|
||||
return;
|
||||
}
|
||||
|
||||
MapEditor.replacePlaceIdsToReal(this.response.added);
|
||||
|
||||
MapEditor.added = {};
|
||||
MapEditor.edited = {};
|
||||
MapEditor.deleted = {};
|
||||
|
||||
document.getElementById('added').innerHTML = '0';
|
||||
document.getElementById('edited').innerHTML = '0';
|
||||
document.getElementById('deleted').innerHTML = '0';
|
||||
|
||||
document.getElementById('saveButton').disabled = true;
|
||||
};
|
||||
|
||||
xhr.open('POST', '/admin/saveMap/' + mapId + '/json', true);
|
||||
xhr.send(data);
|
||||
},
|
||||
|
||||
replacePlaceIdsToReal: function (addedPlaces) {
|
||||
for (var i = 0; i < addedPlaces.length; ++i) {
|
||||
var tempId = addedPlaces[i].tempId;
|
||||
var placeId = addedPlaces[i].id;
|
||||
places[tempId].id = placeId;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -100,6 +350,19 @@
|
||||
zoomControl: false
|
||||
});
|
||||
|
||||
MapEditor.map.on('click', function (e) {
|
||||
var marker = L.marker(e.latlng, {
|
||||
icon: IconCollection.iconBlue,
|
||||
zIndexOffset: 2000
|
||||
})
|
||||
.addTo(MapEditor.map)
|
||||
.on('click', function () {
|
||||
MapEditor.select(this);
|
||||
});
|
||||
|
||||
MapEditor.select(marker);
|
||||
});
|
||||
|
||||
var highResData = Util.getHighResData();
|
||||
|
||||
L.tileLayer(tileUrl, {
|
||||
@ -113,22 +376,23 @@
|
||||
|
||||
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: places[i].noPano ? IconCollection.iconRed : IconCollection.iconGreen,
|
||||
for (var placeId in places) {
|
||||
if (!places.hasOwnProperty(placeId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var place = places[placeId];
|
||||
|
||||
var marker = L.marker({ lat: place.lat, lng: place.lng }, {
|
||||
icon: place.noPano ? IconCollection.iconRed : IconCollection.iconGreen,
|
||||
zIndexOffset: 1000
|
||||
})
|
||||
.addTo(MapEditor.map)
|
||||
.on('click', function () {
|
||||
if (MapEditor.selectedMarker === this) {
|
||||
return;
|
||||
}
|
||||
|
||||
MapEditor.select(this);
|
||||
});
|
||||
|
||||
marker.placeId = places[i].id;
|
||||
marker.noPano = places[i].noPano;
|
||||
marker.placeId = place.id;
|
||||
}
|
||||
|
||||
MapEditor.panorama = new google.maps.StreetViewPanorama(document.getElementById('panorama'), {
|
||||
@ -140,16 +404,42 @@
|
||||
motionTracking: false
|
||||
});
|
||||
|
||||
document.getElementById('cancelButton').onclick = function () {
|
||||
document.getElementById('map').classList.remove('selected');
|
||||
document.getElementById('control').classList.remove('selected');
|
||||
document.getElementById('noPano').style.visibility = 'hidden';
|
||||
document.getElementById('panorama').style.visibility = 'hidden';
|
||||
document.getElementById('placeControl').style.visibility = 'hidden';
|
||||
document.getElementById('mapName').onclick = function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
MapEditor.resetSelected();
|
||||
MapEditor.selectedMarker = null;
|
||||
var metadata = document.getElementById('metadata');
|
||||
|
||||
MapEditor.map.invalidateSize(true);
|
||||
if (metadata.style.visibility === 'visible') {
|
||||
metadata.style.visibility = 'hidden';
|
||||
} else {
|
||||
metadata.style.visibility = 'visible';
|
||||
document.getElementById('metadataForm').elements.name.select();
|
||||
}
|
||||
};
|
||||
|
||||
document.getElementById('metadataForm').onsubmit = function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
MapEditor.editMetadata();
|
||||
};
|
||||
|
||||
document.getElementById('closeMetadataButton').onclick = function () {
|
||||
document.getElementById('metadata').style.visibility = 'hidden';
|
||||
};
|
||||
|
||||
document.getElementById('saveButton').onclick = function () {
|
||||
MapEditor.saveMap();
|
||||
};
|
||||
|
||||
document.getElementById('applyButton').onclick = function () {
|
||||
MapEditor.applyPlace();
|
||||
};
|
||||
|
||||
document.getElementById('closeButton').onclick = function () {
|
||||
MapEditor.closePlace();
|
||||
};
|
||||
|
||||
document.getElementById('deleteButton').onclick = function () {
|
||||
MapEditor.deletePlace();
|
||||
};
|
||||
})();
|
||||
|
@ -7,14 +7,18 @@ use MapGuesser\Util\Geo\Bounds;
|
||||
use MapGuesser\Response\HtmlContent;
|
||||
use MapGuesser\Response\JsonContent;
|
||||
use MapGuesser\Interfaces\Response\IContent;
|
||||
use MapGuesser\Repository\MapRepository;
|
||||
|
||||
class GameController
|
||||
{
|
||||
private IRequest $request;
|
||||
|
||||
private MapRepository $mapRepository;
|
||||
|
||||
public function __construct(IRequest $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->mapRepository = new MapRepository();
|
||||
}
|
||||
|
||||
public function getGame(): IContent
|
||||
@ -33,7 +37,9 @@ class GameController
|
||||
|
||||
private function prepareGame(int $mapId)
|
||||
{
|
||||
$bounds = $this->getMapBounds($mapId);
|
||||
$map = $this->mapRepository->getById($mapId);
|
||||
|
||||
$bounds = Bounds::createDirectly($map['bound_south_lat'], $map['bound_west_lng'], $map['bound_north_lat'], $map['bound_east_lng']);
|
||||
|
||||
$session = $this->request->session();
|
||||
|
||||
@ -45,19 +51,6 @@ class GameController
|
||||
]);
|
||||
}
|
||||
|
||||
return ['mapId' => $mapId, 'bounds' => $bounds->toArray()];
|
||||
}
|
||||
|
||||
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;
|
||||
return ['mapId' => $mapId, 'mapName' => $map['name'], 'bounds' => $bounds->toArray()];
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use MapGuesser\Response\JsonContent;
|
||||
use MapGuesser\Interfaces\Response\IContent;
|
||||
use MapGuesser\Repository\PlaceRepository;
|
||||
|
||||
class PositionController
|
||||
class GameFlowController
|
||||
{
|
||||
const NUMBER_OF_ROUNDS = 5;
|
||||
const MAX_SCORE = 1000;
|
||||
@ -21,7 +21,7 @@ class PositionController
|
||||
$this->placeRepository = new PlaceRepository();
|
||||
}
|
||||
|
||||
public function getPosition(): IContent
|
||||
public function getNewPlace(): IContent
|
||||
{
|
||||
$mapId = (int) $this->request->query('mapId');
|
||||
|
||||
@ -33,11 +33,11 @@ class PositionController
|
||||
}
|
||||
|
||||
if (count($state['rounds']) === 0) {
|
||||
$newPosition = $this->placeRepository->getForMapWithValidPano($mapId);
|
||||
$state['rounds'][] = $newPosition;
|
||||
$place = $this->placeRepository->getForMapWithValidPano($mapId);
|
||||
$state['rounds'][] = $place;
|
||||
$session->set('state', $state);
|
||||
|
||||
$data = ['panoId' => $newPosition['panoId']];
|
||||
$data = ['panoId' => $place['panoId']];
|
||||
} else {
|
||||
$rounds = count($state['rounds']);
|
||||
$last = $state['rounds'][$rounds - 1];
|
||||
@ -93,11 +93,11 @@ class PositionController
|
||||
$exclude = array_merge($exclude, $round['placesWithoutPano'], [$round['placeId']]);
|
||||
}
|
||||
|
||||
$newPosition = $this->placeRepository->getForMapWithValidPano($mapId, $exclude);
|
||||
$state['rounds'][] = $newPosition;
|
||||
$place = $this->placeRepository->getForMapWithValidPano($mapId, $exclude);
|
||||
$state['rounds'][] = $place;
|
||||
$session->set('state', $state);
|
||||
|
||||
$panoId = $newPosition['panoId'];
|
||||
$panoId = $place['panoId'];
|
||||
} else {
|
||||
$state['rounds'] = [];
|
||||
$session->set('state', $state);
|
@ -1,25 +1,32 @@
|
||||
<?php namespace MapGuesser\Controller;
|
||||
|
||||
use DateTime;
|
||||
use MapGuesser\Database\Query\Modify;
|
||||
use MapGuesser\Database\Query\Select;
|
||||
use MapGuesser\Interfaces\Authentication\IUser;
|
||||
use MapGuesser\Interfaces\Authorization\ISecured;
|
||||
use MapGuesser\Interfaces\Database\IResultSet;
|
||||
use MapGuesser\Interfaces\Request\IRequest;
|
||||
use MapGuesser\Interfaces\Response\IContent;
|
||||
use MapGuesser\Repository\MapRepository;
|
||||
use MapGuesser\Repository\PlaceRepository;
|
||||
use MapGuesser\Response\HtmlContent;
|
||||
use MapGuesser\Response\JsonContent;
|
||||
use MapGuesser\Util\Geo\Bounds;
|
||||
use MapGuesser\Util\Geo\Position;
|
||||
|
||||
class MapAdminController implements ISecured
|
||||
{
|
||||
private IRequest $request;
|
||||
|
||||
private MapRepository $mapRepository;
|
||||
|
||||
private PlaceRepository $placeRepository;
|
||||
|
||||
public function __construct(IRequest $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->mapRepository = new MapRepository();
|
||||
$this->placeRepository = new PlaceRepository();
|
||||
}
|
||||
|
||||
@ -41,15 +48,15 @@ class MapAdminController implements ISecured
|
||||
{
|
||||
$mapId = (int) $this->request->query('mapId');
|
||||
|
||||
$bounds = $this->getMapBounds($mapId);
|
||||
|
||||
$map = $this->mapRepository->getById($mapId);
|
||||
$bounds = Bounds::createDirectly($map['bound_south_lat'], $map['bound_west_lng'], $map['bound_north_lat'], $map['bound_east_lng']);
|
||||
$places = $this->getPlaces($mapId);
|
||||
|
||||
$data = ['mapId' => $mapId, 'bounds' => $bounds->toArray(), 'places' => &$places];
|
||||
$data = ['mapId' => $mapId, 'mapName' => $map['name'], 'mapDescription' => str_replace('<br>', '\n', $map['description']), 'bounds' => $bounds->toArray(), 'places' => &$places];
|
||||
return new HtmlContent('admin/map_editor', $data);
|
||||
}
|
||||
|
||||
public function getPlace()
|
||||
public function getPlace(): IContent
|
||||
{
|
||||
$placeId = (int) $this->request->query('placeId');
|
||||
|
||||
@ -59,34 +66,113 @@ class MapAdminController implements ISecured
|
||||
return new JsonContent($data);
|
||||
}
|
||||
|
||||
private function getMapBounds(int $mapId): Bounds
|
||||
public function saveMap(): IContent
|
||||
{
|
||||
$select = new Select(\Container::$dbConnection, 'maps');
|
||||
$select->columns(['bound_south_lat', 'bound_west_lng', 'bound_north_lat', 'bound_east_lng']);
|
||||
$select->whereId($mapId);
|
||||
$mapId = (int) $this->request->query('mapId');
|
||||
|
||||
$map = $select->execute()->fetch(IResultSet::FETCH_ASSOC);
|
||||
if (isset($_POST['added'])) {
|
||||
$addedIds = [];
|
||||
foreach ($_POST['added'] as $placeRaw) {
|
||||
$placeRaw = json_decode($placeRaw, true);
|
||||
|
||||
$bounds = Bounds::createDirectly($map['bound_south_lat'], $map['bound_west_lng'], $map['bound_north_lat'], $map['bound_east_lng']);
|
||||
$addedIds[] = ['tempId' => $placeRaw['id'], $this->placeRepository->addToMap($mapId, [
|
||||
'lat' => (float) $placeRaw['lat'],
|
||||
'lng' => (float) $placeRaw['lng'],
|
||||
'pano_id_cached_timestamp' => $placeRaw['panoId'] === -1 ? (new DateTime('-1 day'))->format('Y-m-d H:i:s') : null
|
||||
])];
|
||||
}
|
||||
} else {
|
||||
$addedIds = [];
|
||||
}
|
||||
|
||||
if (isset($_POST['edited'])) {
|
||||
foreach ($_POST['edited'] as $placeRaw) {
|
||||
$placeRaw = json_decode($placeRaw, true);
|
||||
|
||||
$this->placeRepository->modify((int) $placeRaw['id'], [
|
||||
'lat' => (float) $placeRaw['lat'],
|
||||
'lng' => (float) $placeRaw['lng']
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_POST['deleted'])) {
|
||||
foreach ($_POST['deleted'] as $placeRaw) {
|
||||
$placeRaw = json_decode($placeRaw, true);
|
||||
|
||||
$this->placeRepository->delete($placeRaw['id']);
|
||||
}
|
||||
}
|
||||
|
||||
$mapBounds = $this->calculateMapBounds($mapId);
|
||||
|
||||
$map = [
|
||||
'bound_south_lat' => $mapBounds->getSouthLat(),
|
||||
'bound_west_lng' => $mapBounds->getWestLng(),
|
||||
'bound_north_lat' => $mapBounds->getNorthLat(),
|
||||
'bound_east_lng' => $mapBounds->getEastLng()
|
||||
];
|
||||
|
||||
if (isset($_POST['name'])) {
|
||||
$map['name'] = $_POST['name'] ? $_POST['name'] : '[unnamed map]';
|
||||
}
|
||||
if (isset($_POST['description'])) {
|
||||
$map['description'] = str_replace(['\n', '\r\n'], '<br>', $_POST['description']);
|
||||
}
|
||||
|
||||
$this->saveMapData($mapId, $map);
|
||||
|
||||
$data = ['added' => $addedIds];
|
||||
return new JsonContent($data);
|
||||
}
|
||||
|
||||
private function calculateMapBounds(int $mapId): Bounds
|
||||
{
|
||||
$select = new Select(\Container::$dbConnection, 'places');
|
||||
$select->columns(['lat', 'lng']);
|
||||
$select->where('map_id', '=', $mapId);
|
||||
|
||||
$result = $select->execute();
|
||||
|
||||
$bounds = new Bounds();
|
||||
while ($place = $result->fetch(IResultSet::FETCH_ASSOC)) {
|
||||
$bounds->extend(new Position($place['lat'], $place['lng']));
|
||||
}
|
||||
|
||||
return $bounds;
|
||||
}
|
||||
|
||||
private function saveMapData(int $mapId, array $map): void
|
||||
{
|
||||
$modify = new Modify(\Container::$dbConnection, 'maps');
|
||||
$modify->setId($mapId);
|
||||
$modify->fill($map);
|
||||
$modify->save();
|
||||
}
|
||||
|
||||
private function &getPlaces(int $mapId): array
|
||||
{
|
||||
$select = new Select(\Container::$dbConnection, 'places');
|
||||
$select->columns(['id', 'lat', 'lng', 'pano_id_cached', 'pano_id_cached_timestamp']);
|
||||
$select->where('map_id', '=', $mapId);
|
||||
$select->orderBy('lng');
|
||||
|
||||
$result = $select->execute();
|
||||
|
||||
$places = [];
|
||||
|
||||
while ($place = $result->fetch(IResultSet::FETCH_ASSOC)) {
|
||||
//$panoId = ???
|
||||
//$pov = ???
|
||||
$noPano = $place['pano_id_cached_timestamp'] && $place['pano_id_cached'] === null;
|
||||
|
||||
$places[] = ['id' => $place['id'], 'lat' => $place['lat'], 'lng' => $place['lng'], 'noPano' => $noPano];
|
||||
$places[$place['id']] = [
|
||||
'id' => $place['id'],
|
||||
'lat' => $place['lat'],
|
||||
'lng' => $place['lng'],
|
||||
'panoId' => null,
|
||||
'pov' => ['heading' => 0.0, 'pitch' => 0.0, 'zoom' => 0.0],
|
||||
'noPano' => $noPano
|
||||
];
|
||||
}
|
||||
|
||||
return $places;
|
||||
|
@ -59,6 +59,11 @@ class Modify
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->attributes[$this->idName];
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
if (isset($this->attributes[$this->idName])) {
|
||||
|
16
src/Repository/MapRepository.php
Normal file
16
src/Repository/MapRepository.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php namespace MapGuesser\Repository;
|
||||
|
||||
use MapGuesser\Database\Query\Select;
|
||||
use MapGuesser\Interfaces\Database\IResultSet;
|
||||
|
||||
class MapRepository
|
||||
{
|
||||
public function getById(int $mapId)
|
||||
{
|
||||
$select = new Select(\Container::$dbConnection, 'maps');
|
||||
$select->columns(['id', 'name', 'description', 'bound_south_lat', 'bound_west_lng', 'bound_north_lat', 'bound_east_lng']);
|
||||
$select->whereId($mapId);
|
||||
|
||||
return $select->execute()->fetch(IResultSet::FETCH_ASSOC);
|
||||
}
|
||||
}
|
@ -48,6 +48,33 @@ class PlaceRepository
|
||||
];
|
||||
}
|
||||
|
||||
public function addToMap(int $mapId, array $place): int
|
||||
{
|
||||
$modify = new Modify(\Container::$dbConnection, 'places');
|
||||
$modify->set('map_id', $mapId);
|
||||
$modify->fill($place);
|
||||
$modify->save();
|
||||
|
||||
return $modify->getId();
|
||||
}
|
||||
|
||||
public function modify(int $id, array $place): void
|
||||
{
|
||||
$modify = new Modify(\Container::$dbConnection, 'places');
|
||||
$modify->setId($id);
|
||||
$modify->set('pano_id_cached', null);
|
||||
$modify->set('pano_id_cached_timestamp', null);
|
||||
$modify->fill($place);
|
||||
$modify->save();
|
||||
}
|
||||
|
||||
public function delete(int $id): void
|
||||
{
|
||||
$modify = new Modify(\Container::$dbConnection, 'places');
|
||||
$modify->setId($id);
|
||||
$modify->delete();
|
||||
}
|
||||
|
||||
private function selectFromDbById(int $placeId): array
|
||||
{
|
||||
$select = new Select(\Container::$dbConnection, 'places');
|
||||
@ -66,7 +93,7 @@ class PlaceRepository
|
||||
$select->where('id', 'NOT IN', $exclude);
|
||||
$select->where('map_id', '=', $mapId);
|
||||
|
||||
$numberOfPlaces = $select->count();// TODO: what if 0
|
||||
$numberOfPlaces = $select->count(); // TODO: what if 0
|
||||
$randomOffset = random_int(0, $numberOfPlaces - 1);
|
||||
|
||||
$select->orderBy('id');
|
||||
|
@ -59,6 +59,26 @@ class Bounds
|
||||
}
|
||||
}
|
||||
|
||||
public function getSouthLat(): float
|
||||
{
|
||||
return $this->southLat;
|
||||
}
|
||||
|
||||
public function getWestLng(): float
|
||||
{
|
||||
return $this->westLng;
|
||||
}
|
||||
|
||||
public function getNorthLat(): float
|
||||
{
|
||||
return $this->northLat;
|
||||
}
|
||||
|
||||
public function getEastLng(): float
|
||||
{
|
||||
return $this->eastLng;
|
||||
}
|
||||
|
||||
public function calculateApproximateArea(): float
|
||||
{
|
||||
$dLat = $this->northLat - $this->southLat;
|
||||
|
@ -1,10 +1,55 @@
|
||||
<?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 class="header small">
|
||||
<div class="grid">
|
||||
<h1>
|
||||
<a href="/admin/maps" title="Back to maps">
|
||||
<img class="inline" src="/static/img/icon.svg">
|
||||
<span>MapGuesser</span>
|
||||
</a>
|
||||
</h1>
|
||||
<p>
|
||||
<span class="bold"><a href="#" id="mapName"><?= $mapName ?></a></span><!--
|
||||
--><span><!--
|
||||
<?php /* Copyright (c) 2019 The Bootstrap Authors. License can be found in 'USED_SOFTWARE' in section 'Bootstrap Icons'. */ ?>
|
||||
--><svg class="inline" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" d="M8 3.5a.5.5 0 0 1 .5.5v4a.5.5 0 0 1-.5.5H4a.5.5 0 0 1 0-1h3.5V4a.5.5 0 0 1 .5-.5z"/>
|
||||
<path fill-rule="evenodd" d="M7.5 8a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1H8.5V12a.5.5 0 0 1-1 0V8z"/>
|
||||
<path fill-rule="evenodd" d="M14 1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z"/>
|
||||
</svg>
|
||||
<span id="added" class="bold">0</span><!--
|
||||
--></span><!--
|
||||
--><span><!--
|
||||
<?php /* Copyright (c) 2019 The Bootstrap Authors. License can be found in 'USED_SOFTWARE' in section 'Bootstrap Icons'. */ ?>
|
||||
--><svg class="inline" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15.502 1.94a.5.5 0 0 1 0 .706a.5.5 0 0 1 .707 0l1.293 1.293zm-1.75 2.456l-2-2L4.939 9.21a.5.5 0 0 0-.121.196l-.805 2.414a.25.25 0 0 0 .316.316l2.414-.805a.5.5 0 0 0 .196-.12l6.813-6.814z"/> <path fill-rule="evenodd" d="M14 1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z"/>
|
||||
</svg>
|
||||
<span id="edited" class="bold">0</span><!--
|
||||
--></span><!--
|
||||
--><span><!--
|
||||
<?php /* Copyright (c) 2019 The Bootstrap Authors. License can be found in 'USED_SOFTWARE' in section 'Bootstrap Icons'. */ ?>
|
||||
--><svg class="inline" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" d="M3.5 8a.5.5 0 0 1 .5-.5h8a.5.5 0 0 1 0 1H4a.5.5 0 0 1-.5-.5z"/>
|
||||
<path fill-rule="evenodd" d="M14 1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z"/>
|
||||
</svg>
|
||||
<span id="deleted" class="bold">0</span><!--
|
||||
--></span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="metadata">
|
||||
<form id="metadataForm">
|
||||
<input class="fullWidth" type="text" name="name" value="<?= $mapName ?>" placeholder="Name of the map">
|
||||
<textarea class="fullWidth marginTop" name="description" rows="4" placeholder="Description of the map"><?= $mapDescription ?></textarea>
|
||||
<div style="text-align: right;">
|
||||
<button id="closeMetadataButton" class="gray marginTop" type="button">Close</button>
|
||||
<button class="marginTop" type="submit">Apply</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<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>
|
||||
<button id="saveButton" class="fullWidth" disabled>Save</button>
|
||||
</div>
|
||||
<div id="panorama"></div>
|
||||
<div id="noPano">
|
||||
@ -12,7 +57,7 @@
|
||||
</div>
|
||||
<div id="placeControl">
|
||||
<button id="applyButton" class="fullWidth">Apply</button>
|
||||
<button id="cancelButton" class="gray fullWidth marginTop">Cancel</button>
|
||||
<button id="closeButton" class="gray fullWidth marginTop">Close</button>
|
||||
<button id="deleteButton" class="red fullWidth marginTop">Delete</button>
|
||||
</div>
|
||||
<script>
|
||||
|
@ -8,7 +8,11 @@
|
||||
<span>MapGuesser</span>
|
||||
</a>
|
||||
</h1>
|
||||
<p id="roundInfo">Round: <span id="currentRound" class="mono bold"></span> | Score: <span id="currentScoreSum" class="mono bold"></span></p>
|
||||
<p>
|
||||
<span id="mapName" class="bold"><?= $mapName ?></span><!--
|
||||
--><span>Round <span id="currentRound" class="bold"></span></span><!--
|
||||
--><span>Score <span id="currentScoreSum" class="bold"></span></span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="cover"></div>
|
||||
|
@ -10,7 +10,7 @@
|
||||
<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&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="96x96" href="/static/img/favicon/96x96.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/static/img/favicon/32x32.png">
|
||||
|
Loading…
Reference in New Issue
Block a user