1<?php 2 3/** 4 * webtrees: online genealogy 5 * Copyright (C) 2021 webtrees development team 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18declare(strict_types=1); 19 20namespace Fisharebest\Webtrees\Http\RequestHandlers; 21 22use Fisharebest\Webtrees\Gedcom; 23use Fisharebest\Webtrees\PlaceLocation; 24use Fisharebest\Webtrees\Services\MapDataService; 25use Illuminate\Database\Capsule\Manager as DB; 26use Psr\Http\Message\ResponseInterface; 27use Psr\Http\Message\ServerRequestInterface; 28use Psr\Http\Server\RequestHandlerInterface; 29 30use function addcslashes; 31use function array_reverse; 32use function array_unshift; 33use function count; 34use function implode; 35use function preg_replace; 36use function response; 37 38/** 39 * Export geographic data. 40 */ 41class MapDataExportGeoJson implements RequestHandlerInterface 42{ 43 /** @var MapDataService */ 44 private $map_data_service; 45 46 /** 47 * Dependency injection. 48 * 49 * @param MapDataService $map_data_service 50 */ 51 public function __construct(MapDataService $map_data_service) 52 { 53 $this->map_data_service = $map_data_service; 54 } 55 56 /** 57 * @param ServerRequestInterface $request 58 * 59 * @return ResponseInterface 60 */ 61 public function handle(ServerRequestInterface $request): ResponseInterface 62 { 63 $parent_id = $request->getAttribute('parent_id'); 64 65 if ($parent_id === null) { 66 $parent = new PlaceLocation(''); 67 } else { 68 $parent = $this->map_data_service->findById((int) $parent_id); 69 } 70 71 for ($tmp = $parent, $hierarchy = []; $tmp->id() !== null; $tmp = $tmp->parent()) { 72 $hierarchy[] = $tmp->locationName(); 73 } 74 75 // Create the file name 76 $filename = preg_replace('/[^\p{L}]+/u', '-', $hierarchy[0] ?? 'Global') . '.geojson'; 77 78 // Recursively search for child places 79 $features = []; 80 $queue = [[ 81 $parent->id(), array_reverse($hierarchy), $parent->latitude(), $parent->longitude() 82 ]]; 83 84 while ($queue !== []) { 85 [$id, $hierarchy, $latitude, $longitude] = array_shift($queue); 86 87 if ($latitude !== null && !$longitude !== null) { 88 $features[] = [ 89 'type' => 'Feature', 90 'geometry' => [ 91 'type' => 'Point', 92 'coordinates' => [ 93 (float) $longitude, 94 (float) $latitude, 95 ], 96 ], 97 'properties' => [ 98 'name' => implode(Gedcom::PLACE_SEPARATOR, array_reverse($hierarchy)), 99 ], 100 ]; 101 } 102 103 $query = DB::table('place_location'); 104 // Data for the next level. 105 106 if ($id === null) { 107 $query->whereNull('parent_id'); 108 } else { 109 $query->where('parent_id', '=', $id); 110 } 111 112 $rows = $query 113 ->orderBy('place', 'DESC') 114 ->select(['id', 'place', 'latitude', 'longitude']) 115 ->get(); 116 117 $next_level = count($hierarchy); 118 119 foreach ($rows as $row) { 120 $hierarchy[$next_level] = $row->place; 121 array_unshift($queue, [$row->id, $hierarchy, $row->latitude, $row->longitude]); 122 } 123 } 124 125 $geojson = [ 126 'type' => 'FeatureCollection', 127 'features' => $features, 128 ]; 129 130 $filename = addcslashes($filename, '"'); 131 132 return response($geojson) 133 ->withHeader('Content-Type', 'application/vnd.geo+json') 134 ->withHeader('Content-Disposition', 'attachment; filename="' . $filename . '"'); 135 } 136} 137