From 1fb4eb9cf7d88a047aa849daaebf736c8fd2c3ba Mon Sep 17 00:00:00 2001 From: vincent Date: Sun, 22 Jun 2025 22:31:54 +0200 Subject: [PATCH] =?UTF-8?q?Ajoute=20l=E2=80=99outil=20Osmose?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/app.js | 1 + assets/controllers/map_controller.js | 3 +- config/packages/twig.yaml | 2 + importmap.php | 7 ++ lib/OSM/Element/Element.php | 19 ++++++ lib/OSM/Element/Node.php | 25 ++++++++ lib/OSM/OSM.php | 20 ++++++ php.ini | 1 - src/Controller/TaskController.php | 2 +- src/Controller/ToolsController.php | 121 +++++++++++++++++++++++++++++++++++ src/Form/OsmoseToolType.php | 25 ++++++++ src/Service/OsmoseClient.php | 16 +++++ templates/tools/osmose.html.twig | 5 ++ 13 files changed, 244 insertions(+), 3 deletions(-) create mode 100644 lib/OSM/Element/Node.php create mode 100644 src/Form/OsmoseToolType.php create mode 100644 templates/tools/osmose.html.twig diff --git a/assets/app.js b/assets/app.js index 5a41d3e..6f8974d 100644 --- a/assets/app.js +++ b/assets/app.js @@ -1,6 +1,7 @@ import './bootstrap.js'; // Stimulus import './vendor/bootstrap/dist/css/bootstrap.min.css'; // Bootstrap +import './vendor/leaflet.markercluster/dist/MarkerCluster.min.css'; // Leaflet clusters import './vendor/leaflet/dist/leaflet.min.css'; // Leaflet import './styles/app.css'; // Nos personnalisations diff --git a/assets/controllers/map_controller.js b/assets/controllers/map_controller.js index ef7f9d1..075a10f 100644 --- a/assets/controllers/map_controller.js +++ b/assets/controllers/map_controller.js @@ -9,6 +9,7 @@ Cf **/ import { Controller } from '@hotwired/stimulus'; import 'leaflet'; +import 'leaflet.markercluster'; export default class extends Controller { static targets = [ 'openLink' ]; @@ -57,7 +58,7 @@ export default class extends Controller { var layer = L.featureGroup(); // Crée la couche dédiée à Overpass - var overpassLayer = L.featureGroup(); + var overpassLayer = L.markerClusterGroup(); if (this.overpassResultValue !== '') { geojsons = JSON.parse(this.overpassResultValue); if (geojsons.elements.length > 0) { diff --git a/config/packages/twig.yaml b/config/packages/twig.yaml index 085abac..b1dd948 100644 --- a/config/packages/twig.yaml +++ b/config/packages/twig.yaml @@ -19,6 +19,8 @@ twig: tools: - label: 'Communes' route: 'app_tools_city' + - label: 'Osmose' + route: 'app_tools_osmose' when@test: twig: diff --git a/importmap.php b/importmap.php index 506dd51..faab011 100644 --- a/importmap.php +++ b/importmap.php @@ -39,4 +39,11 @@ return [ '@symfony/stimulus-bundle' => [ 'path' => './vendor/symfony/stimulus-bundle/assets/dist/loader.js', ], + 'leaflet.markercluster' => [ + 'version' => '1.5.3', + ], + 'leaflet.markercluster/dist/MarkerCluster.min.css' => [ + 'version' => '1.5.3', + 'type' => 'css', + ], ]; diff --git a/lib/OSM/Element/Element.php b/lib/OSM/Element/Element.php index a3c8cbe..7a886c4 100644 --- a/lib/OSM/Element/Element.php +++ b/lib/OSM/Element/Element.php @@ -69,4 +69,23 @@ class Element return $tag->value; } + + public function asDOMElement(\DOMDocument $document): \DOMElement { + $reflect = new \ReflectionClass(get_called_class()); + $name = strtolower($reflect->getShortName()); + + $xml = $document->createElement($name); + + $xml->setAttribute('id', $this->id); + $xml->setAttribute('visible', 'true'); + + foreach ($this->tags as $tag) { + $tagElement = $document->createElement('tag'); + $tagElement->setAttribute('k', $tag->key); + $tagElement->setAttribute('v', $tag->value); + $xml->appendChild($tagElement); + } + + return $xml; + } } diff --git a/lib/OSM/Element/Node.php b/lib/OSM/Element/Node.php new file mode 100644 index 0000000..f49f063 --- /dev/null +++ b/lib/OSM/Element/Node.php @@ -0,0 +1,25 @@ +point = Point::createFromArray($array); + + return $this; + } + public function asDOMElement(\DOMDocument $document): \DOMElement { + $xml = parent::asDOMElement($document); + + $xml->setAttribute('lat', $this->point->latitude); + $xml->setAttribute('lon', $this->point->longitude); + + return $xml; + } +} diff --git a/lib/OSM/OSM.php b/lib/OSM/OSM.php index 1841b7e..60109b9 100644 --- a/lib/OSM/OSM.php +++ b/lib/OSM/OSM.php @@ -12,6 +12,11 @@ class OSM { $array = json_decode($json, true); + return self::createFromArray($array); + } + + public static function createFromArray($array) + { $instance = new self(); $items = $array['elements']; @@ -22,4 +27,19 @@ class OSM return $instance; } + + public function __toString() { + $document = new \DOMDocument('1.0', 'UTF-8'); + + $osm = $document->createElement('osm'); + $osm->setAttribute('version', '0.6'); + + foreach ($this->elements as $element) { + $osm->appendChild($element->asDOMElement($document)); + } + + $document->appendChild($osm); + + return $document->saveXML(); + } } diff --git a/php.ini b/php.ini index e9fa75b..f048845 100644 --- a/php.ini +++ b/php.ini @@ -1,4 +1,3 @@ upload_max_filesize = 40M post_max_size = 40M memory_limit = 512M - diff --git a/src/Controller/TaskController.php b/src/Controller/TaskController.php index 4515499..8ee41af 100644 --- a/src/Controller/TaskController.php +++ b/src/Controller/TaskController.php @@ -389,7 +389,7 @@ class TaskController extends AbstractController // Renvoie le XML OSM associé à la tâche #[Route('/download/{slug}.osm', name: 'app_task_osm')] - #[IsGranted('ROLE_USER')] + #[IsGranted('PUBLIC_ACCESS')] public function osm(Request $request, EntityManagerInterface $entityManager, $slug): Response { $repository = $entityManager->getRepository(Task::class); diff --git a/src/Controller/ToolsController.php b/src/Controller/ToolsController.php index 4eb7bb8..4fd1eab 100644 --- a/src/Controller/ToolsController.php +++ b/src/Controller/ToolsController.php @@ -3,6 +3,8 @@ namespace App\Controller; use App\Form\CityToolType; +use App\Form\OsmoseToolType; +use App\Service\OsmoseClient; use App\Service\OverpassClient; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Form\Extension\Core\Type\SubmitType; @@ -89,4 +91,123 @@ class ToolsController extends AbstractController 'form' => $form, ]); } + + #[Route('/osmose', name: 'app_tools_osmose')] + public function osmose( + Request $request, + OsmoseClient $osmose, + ): Response { + $form = $this->createForm(OsmoseToolType::class, $request->query->all()); + $form->add('submit', SubmitType::class, ['label' => 'Générer']); + + $form->handleRequest($request); + if ($form->isSubmitted() and $form->isValid()) { + $issues = $osmose->issues([ + 'item' => $form->get('item')->getData(), + 'source' => $form->get('source')->getData(), + 'class' => $form->get('class')->getData(), + 'username' => '', + 'bbox' => '', + 'full' => 'true', + 'limit' => 500, + ]); + + $response = new StreamedResponse(); + + $response->headers->set('Content-Type', 'text/csv'); + $response->headers->set( + 'Content-Disposition', + HeaderUtils::makeDisposition( + HeaderUtils::DISPOSITION_ATTACHMENT, + 'issues.csv' + ) + ); + + $features = json_decode($issues, true)['features']; + + $response->setCallback(function () use ($features): void { + $headings = [ + 'name', + 'description', + 'osm', + 'geojson', + 'status', + ]; + + $csv = fopen('php://output', 'a'); + + fputcsv($csv, $headings); + + $total = count($features); + foreach ($features as $index => $feature) { + $name = sprintf('%s n°%d/%d', $feature['properties']['title'], $index + 1, $total); + $description = << $v) { + $tags[$k] = $v; + } + } + } + + $osm = \OSM\OSM::createFromArray([ + 'elements' => [[ + 'type' => 'node', + 'id' => -1, + 'lat' => (float) $feature['geometry']['coordinates'][1], + 'lon' => (float) $feature['geometry']['coordinates'][0], + 'tags' => $tags, + ]], + ]); + } else { + $osm = ''; + } + + $geojson = json_encode([ + 'type' => 'FeatureCollection', + 'features' => [ + $feature, + ], + ]); + + fputcsv($csv, [ + $name, + $description, + $osm, + $geojson, + 'todo', + ]); + } + + fclose($csv); + }); + + return $response; + } + + return $this->render('tools/osmose.html.twig', [ + 'form' => $form, + ]); + } } diff --git a/src/Form/OsmoseToolType.php b/src/Form/OsmoseToolType.php new file mode 100644 index 0000000..7789dae --- /dev/null +++ b/src/Form/OsmoseToolType.php @@ -0,0 +1,25 @@ +add('item', IntegerType::class, [ + 'label' => 'Item', + ]) + ->add('source', IntegerType::class, [ + 'label' => 'Identifiant source', + ]) + ->add('class', IntegerType::class, [ + 'label' => 'Identifiant de la classe', + ]) + ; + } +} diff --git a/src/Service/OsmoseClient.php b/src/Service/OsmoseClient.php index 636d6f5..ae46487 100644 --- a/src/Service/OsmoseClient.php +++ b/src/Service/OsmoseClient.php @@ -28,4 +28,20 @@ class OsmoseClient private HttpClientInterface $client, ) { } + + public function issues($params) + { + $response = $this->client->request('GET', 'https://osmose.openstreetmap.fr/api/0.3/issues.geojson', [ + 'query' => $params, + ]); + + $isStatusCodeOk = (200 === $response->getStatusCode()); + $isContentTypeJson = ('application/vnd.geo+json' === $response->getHeaders()['content-type'][0]); + + if (!$isStatusCodeOk or !$isContentTypeJson) { + throw new \RuntimeException('Erreur de communication avec Osmose'); + } + + return $response->getContent(); + } } diff --git a/templates/tools/osmose.html.twig b/templates/tools/osmose.html.twig new file mode 100644 index 0000000..c5df176 --- /dev/null +++ b/templates/tools/osmose.html.twig @@ -0,0 +1,5 @@ +{% extends 'tools/base.html.twig' %} + +{% block tools_content %} +{{ form(form) }} +{% endblock %}