You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

335 lines
13 KiB

<?php
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;
use Symfony\Component\HttpFoundation\HeaderUtils;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\Routing\Attribute\Route;
#[Route('/tools')]
class ToolsController extends AbstractController
{
#[Route('/', name: 'app_tools')]
public function index(): Response
{
return $this->render('tools/index.html.twig', [
]);
}
#[Route('/city', name: 'app_tools_city')]
public function city(
Request $request,
OverpassClient $overpass,
): Response {
$form = $this->createForm(CityToolType::class, []);
$form->add('submit', SubmitType::class, ['label' => 'Générer']);
$form->handleRequest($request);
if ($form->isSubmitted() and $form->isValid()) {
$areaId = $form->get('area')->getData();
$query = sprintf('relation["boundary"="administrative"]["admin_level"="8"]["name"](area:%d);', 3600000000 + $areaId); // Cf. <https://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL#By_area_.28area.29>
$json = $overpass->query($query);
$response = new StreamedResponse();
$response->headers->set('Content-Type', 'text/csv');
$response->headers->set(
'Content-Disposition',
HeaderUtils::makeDisposition(
HeaderUtils::DISPOSITION_ATTACHMENT,
sprintf('cities-in-%d.csv', $areaId)
)
);
$response->setCallback(function () use ($json): void {
$headings = [
'name',
'description',
'osm',
'geojson',
'status',
];
$csv = fopen('php://output', 'a');
fputcsv($csv, $headings);
$osm = \OSM\OSM::createFromJson($json);
foreach ($osm->elements as $relation) {
$name = $relation->getTagValue('name');
$feature = new \GeoJson\Feature\Feature(
\OSM\GeoJsonConverter::convertRelationToPolygon($relation),
['name' => $name]
);
fputcsv($csv, [
$name,
$name,
'',
json_encode(new \GeoJson\Feature\FeatureCollection([$feature])),
'todo',
]);
}
fclose($csv);
});
return $response;
}
return $this->render('tools/city.html.twig', [
'form' => $form,
]);
}
#[Route('/osmose', name: 'app_tools_osmose')]
public function osmose(
Request $request,
OsmoseClient $osmose,
OverpassClient $overpass,
): Response {
$form = $this->createForm(OsmoseToolType::class, array_merge(['limit' => 500], $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(),
'limit' => $form->get('limit')->getData(),
'username' => '',
'bbox' => '',
'full' => 'true',
]);
$response = new StreamedResponse();
$response->headers->set('Content-Type', 'text/csv');
$response->headers->set(
'Content-Disposition',
HeaderUtils::makeDisposition(
HeaderUtils::DISPOSITION_ATTACHMENT,
'issues.csv'
)
);
$issuesGeojson = \GeoJson\GeoJson::jsonUnserialize(json_decode($issues, true));
$groupByCity = (bool) $form->get('group_by_city')->getData();
if ($groupByCity) {
$minLat = null;
$maxLat = null;
$minLon = null;
$maxLon = null;
foreach ($issuesGeojson->getFeatures() as $feature) {
$geometry = $feature->getGeometry();
$coordinates = $geometry->getCoordinates();
$lat = (float) $coordinates[1];
$lon = (float) $coordinates[0];
if (is_null($minLat) or ($lat < $minLat)) {
$minLat = $lat;
}
if (is_null($maxLat) or ($lat > $maxLat)) {
$maxLat = $lat;
}
if (is_null($minLon) or ($lon < $minLon)) {
$minLon = $lon;
}
if (is_null($maxLon) or ($lon > $maxLon)) {
$maxLon = $lon;
}
}
$citiesData = $overpass->query(sprintf('relation[admin_level=8][boundary=administrative]["ref:INSEE"](%f,%f,%f,%f);', $minLat, $minLon, $maxLat, $maxLon));
$citiesOsm = \OSM\OSM::createFromJson($citiesData);
$cities = [];
foreach ($citiesOsm->elements as $cityOsm) {
$geojson = \OSM\GeoJsonConverter::convertRelationToPolygon($cityOsm);
$geojsonString = '{"type":"FeatureCollection","features":[{"type": "Feature","properties":{},"geometry":'.json_encode($geojson->jsonSerialize()).'}]}';
$polygon = \geoPHP::load($geojsonString, 'json');
$cities[] = [
'name' => $cityOsm->getTagValue('name'),
'polygon' => $polygon,
'geojson' => $geojsonString,
'features' => [],
];
}
foreach ($issuesGeojson->getFeatures() as $feature) {
$point = \geoPHP::load(json_encode($feature->jsonSerialize()), 'json');
foreach ($cities as $cityIndex => $city) {
$isIn = $city['polygon']->contains($point);
if ($isIn) {
$cities[$cityIndex]['features'][] = $feature;
}
}
}
$cities = array_filter($cities, function ($city) { return !empty($city['features']); });
$response->setCallback(function () use ($cities): void {
$headings = [
'name',
'description',
'osm',
'geojson',
'status',
];
$csv = fopen('php://output', 'a');
fputcsv($csv, $headings);
foreach ($cities as $city) {
$name = $city['name'];
$description = sprintf('**%d** signalements sur la commune **%s** :'.PHP_EOL.PHP_EOL, count($city['features']), $city['name']);
$geojson = $city['geojson'];
$nodes = [];
foreach ($city['features'] as $featureIndex => $feature) {
$properties = $feature->getProperties();
$description .= sprintf('* Signalement %s %s'.PHP_EOL, $properties['uuid'], $properties['title']);
if (isset($properties['fixes'])) {
$tags = [];
foreach ($properties['fixes'] as $fixItems) {
foreach ($fixItems as $fixItem) {
$isExpected = (
('N' === $fixItem['type'])
and array_key_exists('create', $fixItem)
);
if (!$isExpected) {
continue;
} // TODO gérer tous les cas possibles ici
foreach ($fixItem['create'] as $k => $v) {
$tags[$k] = $v;
}
}
}
$geometry = $feature->getGeometry();
$coordinates = $geometry->getCoordinates();
$nodes[] = [
'type' => 'node',
'id' => -1 * ($featureIndex + 1),
'lat' => (float) $coordinates[1],
'lon' => (float) $coordinates[0],
'tags' => $tags,
];
}
}
if (!empty($nodes)) {
$osm = \OSM\OSM::createFromArray([
'elements' => $nodes,
]);
} else {
$osm = '';
}
fputcsv($csv, [
$name,
$description,
$osm,
$geojson,
'todo',
]);
}
fclose($csv);
});
} else {
$response->setCallback(function () use ($issuesGeojson): void {
$headings = [
'name',
'description',
'osm',
'geojson',
'status',
];
$csv = fopen('php://output', 'a');
fputcsv($csv, $headings);
$total = $issuesGeojson->count();
foreach ($issuesGeojson->getFeatures() as $index => $feature) {
$properties = $feature->getProperties();
$name = sprintf('%s n°%d/%d', $properties['title'], $index + 1, $total);
$description = <<<EOT
### Signalement {$properties['uuid']}
* Niveau {$properties['level']}
* Thème {$properties['item']}
* Identifiant de source {$properties['source_id']}
* Identifiant de classe {$properties['class']}
### Osmose
[✔ Fait](https://osmose.openstreetmap.fr/api/0.3/issue/{$properties['uuid']}/done)
[✘ Faux positif](https://osmose.openstreetmap.fr/api/0.3/issue/{$properties['uuid']}/false)
EOT;
if (isset($properties['fixes'])) {
$tags = [];
foreach ($properties['fixes'] as $fixItems) {
foreach ($fixItems as $fixItem) {
$isExpected = (
('N' === $fixItem['type'])
and array_key_exists('create', $fixItem)
);
if (!$isExpected) {
continue;
} // TODO gérer tous les cas possibles ici
foreach ($fixItem['create'] as $k => $v) {
$tags[$k] = $v;
}
}
}
$geometry = $feature->getGeometry();
$coordinates = $geometry->getCoordinates();
$osm = \OSM\OSM::createFromArray([
'elements' => [[
'type' => 'node',
'id' => -1,
'lat' => (float) $coordinates[1],
'lon' => (float) $coordinates[0],
'tags' => $tags,
]],
]);
} else {
$osm = '';
}
fputcsv($csv, [
$name,
$description,
$osm,
json_encode($feature->jsonSerialize()),
'todo',
]);
}
fclose($csv);
});
}
return $response;
}
return $this->render('tools/osmose.html.twig', [
'form' => $form,
]);
}
}