Maps
+TODO
+diff --git a/public/index.php b/public/index.php index 681a0d9..a740c2e 100644 --- a/public/index.php +++ b/public/index.php @@ -19,6 +19,10 @@ Container::$routeCollection->group('game', function (MapGuesser\Routing\RouteCol $routeCollection->get('position-json', '{mapId}/position.json', [MapGuesser\Controller\PositionController::class, 'getPosition']); $routeCollection->post('guess-json', '{mapId}/guess.json', [MapGuesser\Controller\PositionController::class, 'evaluateGuess']); }); +Container::$routeCollection->group('admin', function (MapGuesser\Routing\RouteCollection $routeCollection) { + $routeCollection->get('admin.maps', 'maps', [MapGuesser\Controller\MapAdminController::class, 'getMaps']); + $routeCollection->get('admin.mapEditor', 'mapEditor/{mapId}', [MapGuesser\Controller\MapAdminController::class, 'getMapEditor']); +}); $match = Container::$routeCollection->match($method, explode('/', $url)); diff --git a/public/static/css/map_editor.css b/public/static/css/map_editor.css new file mode 100644 index 0000000..eab914c --- /dev/null +++ b/public/static/css/map_editor.css @@ -0,0 +1,49 @@ +#map { + width: 100%; + height: calc(100% - 50px); + z-index: 1; +} + +#map.selected { + height: calc(50% - 25px); +} + +#panorama { + width: 100%; + height: calc(50% - 25px); + display: none; + z-index: 1; +} + +#noPano { + display: flex; + position: absolute; + left: 0; + bottom: 0; + right: 0; + height: calc(50% - 25px); + z-index: 2; + visibility: hidden; + background: #cccccc; +} + +#noPano>p { + margin: auto; +} + +#control { + position: absolute; + right: 10px; + top: 60px; + width: 125px; + z-index: 3; +} + +#placeControl { + position: absolute; + right: 10px; + top: calc(50% + 35px); + width: 100px; + z-index: 3; + visibility: hidden; +} diff --git a/public/static/js/map_editor.js b/public/static/js/map_editor.js new file mode 100644 index 0000000..1b9df4d --- /dev/null +++ b/public/static/js/map_editor.js @@ -0,0 +1,108 @@ +(function () { + var MapEditor = { + map: null, + panorama: null, + selectedMarker: null, + + loadPano: function (data, status) { + document.getElementById('loading').style.visibility = 'hidden'; + + if (status !== google.maps.StreetViewStatus.OK) { + document.getElementById('noPano').style.visibility = 'visible'; + return; + } + + MapEditor.panorama.setVisible(true); + MapEditor.panorama.setPov({ heading: 0, pitch: 0 }); + MapEditor.panorama.setZoom(0); + MapEditor.panorama.setPano(data.location.pano); + }, + + select: function (marker) { + document.getElementById('loading').style.visibility = 'visible'; + + document.getElementById('map').classList.add('selected'); + document.getElementById('noPano').style.visibility = 'hidden'; + document.getElementById('panorama').style.display = 'block'; + document.getElementById('placeControl').style.visibility = 'visible'; + + MapEditor.resetSelected(); + MapEditor.selectedMarker = marker; + + marker.setIcon(L.icon({ + iconUrl: '/static/img/markers/marker-blue.svg', + iconSize: [24, 32], + iconAnchor: [12, 32] + })); + marker.setZIndexOffset(2000); + + MapEditor.map.invalidateSize(true); + MapEditor.map.panTo(marker.getLatLng()); + + MapEditor.panorama.setVisible(false); + + var sv = new google.maps.StreetViewService(); + sv.getPanorama({ location: marker.getLatLng(), preference: google.maps.StreetViewPreference.NEAREST, source: google.maps.StreetViewSource.OUTDOOR }, MapEditor.loadPano); + }, + + resetSelected: function () { + if (!MapEditor.selectedMarker) { + return; + } + + MapEditor.selectedMarker.setIcon(L.icon({ + iconUrl: '/static/img/markers/marker-green.svg', + iconSize: [24, 32], + iconAnchor: [12, 32] + })); + MapEditor.selectedMarker.setZIndexOffset(1000); + } + }; + + MapEditor.map = L.map('map', { + attributionControl: false, + zoomControl: false + }); + + L.tileLayer(tileUrl, { + minZoom: 0, + maxZoom: 20 + }).addTo(MapEditor.map); + + MapEditor.map.fitBounds(L.latLngBounds({ lat: mapBounds.south, lng: mapBounds.west }, { lat: mapBounds.north, lng: mapBounds.east })); + + for (var i = 0; i < places.length; ++i) { + var marker = L.marker({ lat: places[i].lat, lng: places[i].lng }, { + icon: L.icon({ + iconUrl: '/static/img/markers/marker-green.svg', + iconSize: [24, 32], + iconAnchor: [12, 32] + }), + zIndexOffset: 1000 + }) + .addTo(MapEditor.map) + .on('click', function () { + MapEditor.select(this); + }); + } + + MapEditor.panorama = new google.maps.StreetViewPanorama(document.getElementById('panorama'), { + // switch off fullscreenControl because positioning doesn't work + fullscreenControl: false, + fullscreenControlOptions: { + position: google.maps.ControlPosition.LEFT_TOP + } + }); + + document.getElementById('cancelButton').onclick = function () { + document.getElementById('map').classList.remove('selected'); + document.getElementById('noPano').style.visibility = 'hidden'; + document.getElementById('panorama').style.display = 'none'; + document.getElementById('placeControl').style.visibility = 'hidden'; + + MapEditor.resetSelected(); + MapEditor.selectedMarker = null; + + MapEditor.map.invalidateSize(true); + }; +})(); diff --git a/src/Controller/MapAdminController.php b/src/Controller/MapAdminController.php new file mode 100644 index 0000000..39c8bd5 --- /dev/null +++ b/src/Controller/MapAdminController.php @@ -0,0 +1,53 @@ +getMapBounds($mapId); + + $places = $this->getPlaces($mapId); + + $data = ['mapId' => $mapId, 'bounds' => $bounds->toArray(), 'places' => &$places]; + return new HtmlContent('admin/map_editor', $data); + } + + private function getMapBounds(int $mapId): Bounds + { + $select = new Select(\Container::$dbConnection, 'maps'); + $select->columns(['bound_south_lat', 'bound_west_lng', 'bound_north_lat', 'bound_east_lng']); + $select->whereId($mapId); + + $map = $select->execute()->fetch(IResultSet::FETCH_ASSOC); + + $bounds = Bounds::createDirectly($map['bound_south_lat'], $map['bound_west_lng'], $map['bound_north_lat'], $map['bound_east_lng']); + + return $bounds; + } + + private function &getPlaces(int $mapId): array + { + $select = new Select(\Container::$dbConnection, 'places'); + $select->columns(['id', 'lat', 'lng']); + $select->where('map_id', '=', $mapId); + $select->orderBy('lng'); + //$select->limit(100); + + $places = $select->execute()->fetchAll(IResultSet::FETCH_ASSOC); + + return $places; + } +} diff --git a/views/admin/map_editor.php b/views/admin/map_editor.php new file mode 100644 index 0000000..e3887f9 --- /dev/null +++ b/views/admin/map_editor.php @@ -0,0 +1,27 @@ + + + +
+No panorama is available for this location.
+TODO
+