Merged in feature/MAPG-119-new-map-creation (pull request #97)
Feature/MAPG-119 new map creation
This commit is contained in:
commit
2fc0ab557f
@ -23,7 +23,7 @@ Container::$routeCollection->group('game', function (MapGuesser\Routing\RouteCol
|
|||||||
$routeCollection->post('guess-json', '{mapId}/guess.json', [MapGuesser\Controller\GameFlowController::class, 'evaluateGuess']);
|
$routeCollection->post('guess-json', '{mapId}/guess.json', [MapGuesser\Controller\GameFlowController::class, 'evaluateGuess']);
|
||||||
});
|
});
|
||||||
Container::$routeCollection->group('admin', function (MapGuesser\Routing\RouteCollection $routeCollection) {
|
Container::$routeCollection->group('admin', function (MapGuesser\Routing\RouteCollection $routeCollection) {
|
||||||
$routeCollection->get('admin.mapEditor', 'mapEditor/{mapId}', [MapGuesser\Controller\MapAdminController::class, 'getMapEditor']);
|
$routeCollection->get('admin.mapEditor', 'mapEditor/{mapId?}', [MapGuesser\Controller\MapAdminController::class, 'getMapEditor']);
|
||||||
$routeCollection->get('admin.place', 'place.json/{placeId}', [MapGuesser\Controller\MapAdminController::class, 'getPlace']);
|
$routeCollection->get('admin.place', 'place.json/{placeId}', [MapGuesser\Controller\MapAdminController::class, 'getPlace']);
|
||||||
$routeCollection->post('admin.saveMap', 'saveMap/{mapId}/json', [MapGuesser\Controller\MapAdminController::class, 'saveMap']);
|
$routeCollection->post('admin.saveMap', 'saveMap/{mapId}/json', [MapGuesser\Controller\MapAdminController::class, 'saveMap']);
|
||||||
});
|
});
|
||||||
|
@ -146,6 +146,16 @@ button.fullWidth, a.button.fullWidth {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.noLeftRadius, a.button.noLeftRadius {
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.noRightRadius, a.button.noRightRadius {
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
button.gray, a.button.gray {
|
button.gray, a.button.gray {
|
||||||
background-color: #808080;
|
background-color: #808080;
|
||||||
}
|
}
|
||||||
@ -170,6 +180,14 @@ button.yellow:hover, button.yellow:focus, a.button.yellow:hover, a.button.yellow
|
|||||||
background-color: #c37713;
|
background-color: #c37713;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.green, a.button.green {
|
||||||
|
background-color: #28a745;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.green:hover, button.green:focus, a.button.green:hover, a.button.green:focus {
|
||||||
|
background-color: #1b7d31;
|
||||||
|
}
|
||||||
|
|
||||||
input, select, textarea {
|
input, select, textarea {
|
||||||
background-color: #f9fafb;
|
background-color: #f9fafb;
|
||||||
border: solid #c8d2e1 1px;
|
border: solid #c8d2e1 1px;
|
||||||
@ -291,7 +309,7 @@ div.box {
|
|||||||
div.header.small h1 span {
|
div.header.small h1 span {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
button {
|
button, a.button {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,15 @@
|
|||||||
|
|
||||||
div.mapItem {
|
div.mapItem {
|
||||||
width: 350px;
|
width: 350px;
|
||||||
background-color: #eeeeee;
|
|
||||||
border-radius: 3px;
|
|
||||||
margin: 10px auto;
|
margin: 10px auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.mapItem.new {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
div.mapItem>div.title {
|
div.mapItem>div.title {
|
||||||
background-color: #28a745;
|
background-color: #28a745;
|
||||||
color: white;
|
color: white;
|
||||||
@ -27,6 +31,7 @@ div.mapItem>img {
|
|||||||
}
|
}
|
||||||
|
|
||||||
div.mapItem>div.inner {
|
div.mapItem>div.inner {
|
||||||
|
background-color: #eeeeee;
|
||||||
padding: 10px 8px;
|
padding: 10px 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,6 +48,12 @@ div.mapItem>div.inner>div.info>p:nth-child(2) {
|
|||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.mapItem>div.buttonContainer {
|
||||||
|
display: grid;
|
||||||
|
grid-auto-columns: 1fr;
|
||||||
|
grid-auto-flow: column;
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 1504px) {
|
@media screen and (min-width: 1504px) {
|
||||||
#mapContainer {
|
#mapContainer {
|
||||||
grid-template-columns: auto auto auto auto;
|
grid-template-columns: auto auto auto auto;
|
||||||
@ -69,6 +80,6 @@ div.mapItem>div.inner>div.info>p:nth-child(2) {
|
|||||||
|
|
||||||
@media screen and (max-width: 374px) {
|
@media screen and (max-width: 374px) {
|
||||||
div.mapItem {
|
div.mapItem {
|
||||||
width: initial;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -291,6 +291,11 @@
|
|||||||
|
|
||||||
MapEditor.replacePlaceIdsToReal(this.response.added);
|
MapEditor.replacePlaceIdsToReal(this.response.added);
|
||||||
|
|
||||||
|
if (mapId === 0) {
|
||||||
|
mapId = this.response.mapId;
|
||||||
|
window.history.replaceState(null, '', '/admin/mapEditor/' + mapId);
|
||||||
|
}
|
||||||
|
|
||||||
MapEditor.added = {};
|
MapEditor.added = {};
|
||||||
MapEditor.edited = {};
|
MapEditor.edited = {};
|
||||||
MapEditor.deleted = {};
|
MapEditor.deleted = {};
|
||||||
@ -370,7 +375,7 @@
|
|||||||
ppi: highResData.ppi,
|
ppi: highResData.ppi,
|
||||||
tileSize: highResData.tileSize,
|
tileSize: highResData.tileSize,
|
||||||
zoomOffset: highResData.zoomOffset,
|
zoomOffset: highResData.zoomOffset,
|
||||||
minZoom: 0,
|
minZoom: 2,
|
||||||
maxZoom: 20
|
maxZoom: 20
|
||||||
}).addTo(MapEditor.map);
|
}).addTo(MapEditor.map);
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
<?php namespace MapGuesser\Controller;
|
<?php namespace MapGuesser\Controller;
|
||||||
|
|
||||||
use MapGuesser\Database\Query\Select;
|
|
||||||
use MapGuesser\Interfaces\Database\IResultSet;
|
|
||||||
use MapGuesser\Interfaces\Request\IRequest;
|
use MapGuesser\Interfaces\Request\IRequest;
|
||||||
use MapGuesser\Util\Geo\Bounds;
|
use MapGuesser\Util\Geo\Bounds;
|
||||||
use MapGuesser\Response\HtmlContent;
|
use MapGuesser\Response\HtmlContent;
|
||||||
|
@ -17,6 +17,8 @@ use MapGuesser\Util\Geo\Position;
|
|||||||
|
|
||||||
class MapAdminController implements ISecured
|
class MapAdminController implements ISecured
|
||||||
{
|
{
|
||||||
|
private static string $unnamedMapName = '[unnamed map]';
|
||||||
|
|
||||||
private IRequest $request;
|
private IRequest $request;
|
||||||
|
|
||||||
private MapRepository $mapRepository;
|
private MapRepository $mapRepository;
|
||||||
@ -41,9 +43,18 @@ class MapAdminController implements ISecured
|
|||||||
{
|
{
|
||||||
$mapId = (int) $this->request->query('mapId');
|
$mapId = (int) $this->request->query('mapId');
|
||||||
|
|
||||||
$map = $this->mapRepository->getById($mapId);
|
if ($mapId) {
|
||||||
$bounds = Bounds::createDirectly($map['bound_south_lat'], $map['bound_west_lng'], $map['bound_north_lat'], $map['bound_east_lng']);
|
$map = $this->mapRepository->getById($mapId);
|
||||||
$places = $this->getPlaces($mapId);
|
$bounds = Bounds::createDirectly($map['bound_south_lat'], $map['bound_west_lng'], $map['bound_north_lat'], $map['bound_east_lng']);
|
||||||
|
$places = $this->getPlaces($mapId);
|
||||||
|
} else {
|
||||||
|
$map = [
|
||||||
|
'name' => self::$unnamedMapName,
|
||||||
|
'description' => ''
|
||||||
|
];
|
||||||
|
$bounds = Bounds::createDirectly(-90.0, -180.0, 90.0, 180.0);
|
||||||
|
$places = [];
|
||||||
|
}
|
||||||
|
|
||||||
$data = ['mapId' => $mapId, 'mapName' => $map['name'], 'mapDescription' => str_replace('<br>', "\n", $map['description']), '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);
|
return new HtmlContent('admin/map_editor', $data);
|
||||||
@ -63,12 +74,16 @@ class MapAdminController implements ISecured
|
|||||||
{
|
{
|
||||||
$mapId = (int) $this->request->query('mapId');
|
$mapId = (int) $this->request->query('mapId');
|
||||||
|
|
||||||
|
if (!$mapId) {
|
||||||
|
$mapId = $this->addNewMap();
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($_POST['added'])) {
|
if (isset($_POST['added'])) {
|
||||||
$addedIds = [];
|
$addedIds = [];
|
||||||
foreach ($_POST['added'] as $placeRaw) {
|
foreach ($_POST['added'] as $placeRaw) {
|
||||||
$placeRaw = json_decode($placeRaw, true);
|
$placeRaw = json_decode($placeRaw, true);
|
||||||
|
|
||||||
$addedIds[] = ['tempId' => $placeRaw['id'], $this->placeRepository->addToMap($mapId, [
|
$addedIds[] = ['tempId' => $placeRaw['id'], 'id' => $this->placeRepository->addToMap($mapId, [
|
||||||
'lat' => (float) $placeRaw['lat'],
|
'lat' => (float) $placeRaw['lat'],
|
||||||
'lng' => (float) $placeRaw['lng'],
|
'lng' => (float) $placeRaw['lng'],
|
||||||
'pano_id_cached_timestamp' => $placeRaw['panoId'] === -1 ? (new DateTime('-1 day'))->format('Y-m-d H:i:s') : null
|
'pano_id_cached_timestamp' => $placeRaw['panoId'] === -1 ? (new DateTime('-1 day'))->format('Y-m-d H:i:s') : null
|
||||||
@ -107,7 +122,7 @@ class MapAdminController implements ISecured
|
|||||||
];
|
];
|
||||||
|
|
||||||
if (isset($_POST['name'])) {
|
if (isset($_POST['name'])) {
|
||||||
$map['name'] = $_POST['name'] ? $_POST['name'] : '[unnamed map]';
|
$map['name'] = $_POST['name'] ? $_POST['name'] : self::$unnamedMapName;
|
||||||
}
|
}
|
||||||
if (isset($_POST['description'])) {
|
if (isset($_POST['description'])) {
|
||||||
$map['description'] = str_replace(["\n", "\r\n"], '<br>', $_POST['description']);
|
$map['description'] = str_replace(["\n", "\r\n"], '<br>', $_POST['description']);
|
||||||
@ -115,7 +130,7 @@ class MapAdminController implements ISecured
|
|||||||
|
|
||||||
$this->saveMapData($mapId, $map);
|
$this->saveMapData($mapId, $map);
|
||||||
|
|
||||||
$data = ['added' => $addedIds];
|
$data = ['mapId' => $mapId, 'added' => $addedIds];
|
||||||
return new JsonContent($data);
|
return new JsonContent($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,6 +150,18 @@ class MapAdminController implements ISecured
|
|||||||
return $bounds;
|
return $bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function addNewMap(): int
|
||||||
|
{
|
||||||
|
$modify = new Modify(\Container::$dbConnection, 'maps');
|
||||||
|
$modify->fill([
|
||||||
|
'name' => self::$unnamedMapName,
|
||||||
|
'description' => ''
|
||||||
|
]);
|
||||||
|
$modify->save();
|
||||||
|
|
||||||
|
return $modify->getId();
|
||||||
|
}
|
||||||
|
|
||||||
private function saveMapData(int $mapId, array $map): void
|
private function saveMapData(int $mapId, array $map): void
|
||||||
{
|
{
|
||||||
$modify = new Modify(\Container::$dbConnection, 'maps');
|
$modify = new Modify(\Container::$dbConnection, 'maps');
|
||||||
|
@ -32,6 +32,7 @@ class MapsController
|
|||||||
new RawExpression('COUNT(places.id) AS num_places')
|
new RawExpression('COUNT(places.id) AS num_places')
|
||||||
]);
|
]);
|
||||||
$select->leftJoin('places', ['places', 'map_id'], '=', ['maps', 'id']);
|
$select->leftJoin('places', ['places', 'map_id'], '=', ['maps', 'id']);
|
||||||
|
$select->groupBy(['maps', 'id']);
|
||||||
$select->orderBy('name');
|
$select->orderBy('name');
|
||||||
|
|
||||||
$result = $select->execute();
|
$result = $select->execute();
|
||||||
@ -52,10 +53,18 @@ class MapsController
|
|||||||
|
|
||||||
private function formatMapAreaForHuman(float $area): array
|
private function formatMapAreaForHuman(float $area): array
|
||||||
{
|
{
|
||||||
if ($area < 100000.0) {
|
if ($area < 100.0) {
|
||||||
$digits = 0;
|
$digits = 0;
|
||||||
$rounded = round($area, 0);
|
$rounded = round($area, 0);
|
||||||
$unit = 'm';
|
$unit = 'm';
|
||||||
|
} elseif ($area < 100000.0) {
|
||||||
|
$digits = 0;
|
||||||
|
$rounded = round($area, -2);
|
||||||
|
$unit = 'm';
|
||||||
|
} elseif ($area < 1000000.0) {
|
||||||
|
$digits = 2;
|
||||||
|
$rounded = round($area / 1000000.0, 2);
|
||||||
|
$unit = 'km';
|
||||||
} elseif ($area < 100000000.0) {
|
} elseif ($area < 100000000.0) {
|
||||||
$digits = 0;
|
$digits = 0;
|
||||||
$rounded = round($area / 1000000.0, 0);
|
$rounded = round($area / 1000000.0, 0);
|
||||||
|
@ -34,7 +34,7 @@ class PlaceRepository
|
|||||||
$panoId = $this->requestPanoId($place);
|
$panoId = $this->requestPanoId($place);
|
||||||
|
|
||||||
if ($panoId === null) {
|
if ($panoId === null) {
|
||||||
$placesWithoutPano[] = $place['id'];
|
$placesWithoutPano[] = $exclude[] = $place['id'];
|
||||||
}
|
}
|
||||||
} while ($panoId === null);
|
} while ($panoId === null);
|
||||||
|
|
||||||
|
@ -62,9 +62,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
var tileUrl = '<?= $_ENV['LEAFLET_TILESERVER_URL'] ?>';
|
var tileUrl = '<?= $_ENV['LEAFLET_TILESERVER_URL'] ?>';
|
||||||
var mapId = '<?= $mapId ?>';
|
var mapId = <?= $mapId ?>;
|
||||||
var mapBounds = <?= json_encode($bounds) ?>;
|
var mapBounds = <?= json_encode($bounds) ?>;
|
||||||
var places = <?= json_encode($places) ?>;
|
var places = <?= json_encode($places, JSON_FORCE_OBJECT) ?>;
|
||||||
</script>
|
</script>
|
||||||
<script src="/static/node_modules/leaflet/dist/leaflet.js"></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="https://maps.googleapis.com/maps/api/js?key=<?= $_ENV['GOOGLE_MAPS_JS_API_KEY'] ?>"></script>
|
||||||
|
@ -50,7 +50,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
var mapId = '<?= $mapId ?>';
|
var mapId = <?= $mapId ?>;
|
||||||
var mapBounds = <?= json_encode($bounds) ?>;
|
var mapBounds = <?= json_encode($bounds) ?>;
|
||||||
</script>
|
</script>
|
||||||
<script src="https://maps.googleapis.com/maps/api/js?key=<?= $_ENV['GOOGLE_MAPS_JS_API_KEY'] ?>"></script>
|
<script src="https://maps.googleapis.com/maps/api/js?key=<?= $_ENV['GOOGLE_MAPS_JS_API_KEY'] ?>"></script>
|
||||||
|
@ -30,14 +30,28 @@
|
|||||||
</div>
|
</div>
|
||||||
<p class="small justify marginTop"><?= $map['description'] ?></p>
|
<p class="small justify marginTop"><?= $map['description'] ?></p>
|
||||||
</div>
|
</div>
|
||||||
<a class="button fullWidth" href="game/<?= $map['id']; ?>" title="Play map '<?= $map['name'] ?>'">Play this map</a>
|
|
||||||
<?php if ($isAdmin): ?>
|
<?php if ($isAdmin): ?>
|
||||||
<a class="button yellow fullWidth marginTop" href="admin/mapEditor/<?= $map['id']; ?>" title="Edit map '<?= $map['name'] ?>'">Edit this map</a>
|
<div class="buttonContainer">
|
||||||
|
<a class="button fullWidth noRightRadius" href="game/<?= $map['id']; ?>" title="Play map '<?= $map['name'] ?>'">Play this map</a>
|
||||||
|
<a class="button yellow fullWidth noLeftRadius noRightRadius" href="admin/mapEditor/<?= $map['id']; ?>" title="Edit map '<?= $map['name'] ?>'">Edit</a>
|
||||||
|
<button class="button red fullWidth noLeftRadius" title="Delete map '<?= $map['name'] ?>'">Delete</button>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<a class="button fullWidth" href="game/<?= $map['id']; ?>" title="Play map '<?= $map['name'] ?>'">Play this map</a>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
<?php if (count($maps) < 4): ?>
|
<?php if ($isAdmin): ?>
|
||||||
<?php for ($i = 0; $i < 4 - count($maps); ++$i): ?>
|
<div class="mapItem new">
|
||||||
|
<a class="button green fullWidth" href="admin/mapEditor" title="Add new map">
|
||||||
|
Add new map
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="mapItem"></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (count($maps) < 3): ?>
|
||||||
|
<?php for ($i = 0; $i < 3 - count($maps); ++$i): ?>
|
||||||
<div class="mapItem"></div>
|
<div class="mapItem"></div>
|
||||||
<?php endfor; ?>
|
<?php endfor; ?>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
Loading…
Reference in New Issue
Block a user