Skip to content

Commit

Permalink
Utilise ST_ApproximateMedialAxis pour le calcul des linéaires Littera…
Browse files Browse the repository at this point in the history
…lis (#942)

* Utilise ST_ApproximateMedialAxis pour le calcul des linéaires Litteralis

* Ajoute une migration

* Ajoute un test
  • Loading branch information
florimondmanca authored Sep 17, 2024
1 parent b7c25ce commit 4398158
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 23 deletions.
1 change: 1 addition & 0 deletions config/packages/jsor_doctrine_postgis.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ doctrine:
dql:
string_functions:
ST_AsGeoJSON: 'Jsor\Doctrine\PostGIS\Functions\ST_AsGeoJSON'
ST_ApproximateMedialAxis: 'App\Infrastructure\Persistence\Doctrine\PostGIS\Functions\ST_ApproximateMedialAxis'
16 changes: 4 additions & 12 deletions src/Infrastructure/Adapter/BdTopoRoadGeocoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -367,19 +367,11 @@ public function findSectionsInArea(string $areaGeometry, array $excludeTypes = [
public function convertPolygonRoadToLines(string $geometry): string
{
try {
// Une solution basée sur ST_ApproximateMedialAxis serait meilleure, mais postgis_sfcgal n'est pas encore dispo sur Scalingo.
// https://postgis.net/docs/ST_ApproximateMedialAxis.html
// Comme on a les linéaires des tronçons dans la BDTOPO, on peut se contenter d'une intersection avec ceux-ci.
// À noter, cette approche génère des tronçons parasites au niveau des intersections qui donnent une forme de "peigne" au résultat.
$row = $this->bdtopoConnection->fetchAssociative(
'WITH sections AS (
SELECT ST_Force2D(ST_Collect(t.geometrie)) AS geom
FROM troncon_de_route AS t
WHERE ST_Intersects(t.geometrie, :geom)
)
SELECT ST_AsGeoJSON(ST_Intersection(:geom, s.geom)) AS geom
FROM sections AS s
',
// ST_ApproximateMedialAxis permet de calculer la "ligne centrale" d'un polygone
// https://postgis.net/docs/ST_ApproximateMedialAxis.html
// Ici on l'utilise pour approximer le linéaire de voie à partir d'un polygone qui définit l'enveloppe de cette voie.
'SELECT ST_AsGeoJSON(ST_ApproximateMedialAxis(ST_MakeValid(:geom))) AS geom',
[
'geom' => $geometry,
],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace App\Infrastructure\Persistence\Doctrine\BdTopoMigrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

final class Version20240917103338 extends AbstractMigration
{
public function getDescription(): string
{
return 'Install postgis_sfcgal';
}

public function up(Schema $schema): void
{
$this->addSql('CREATE EXTENSION IF NOT EXISTS postgis_sfcgal');
}

public function down(Schema $schema): void
{
$this->addSql('DROP EXTENSION IF EXISTS postgis_sfcgal');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace App\Infrastructure\Persistence\Doctrine\PostGIS\Functions;

use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;

class ST_ApproximateMedialAxis extends FunctionNode
{
protected array $expressions = [];

public function parse(Parser $parser): void
{
// Signature: `geometry ST_ApproximateMedialAxis(geometry geom);`
// https://postgis.net/docs/ST_ApproximateMedialAxis.html
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->expressions[] = $parser->ArithmeticFactor();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}

public function getSql(SqlWalker $sqlWalker): string
{
$arguments = [];

/** @var Node $expression */
foreach ($this->expressions as $expression) {
$arguments[] = $expression->dispatch($sqlWalker);
}

return 'ST_ApproximateMedialAxis(' . implode(', ', $arguments) . ')';
}
}
42 changes: 31 additions & 11 deletions tests/Integration/Infrastructure/Adapter/BdTopoRoadGeocoderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -205,17 +205,25 @@ private function provideTestFindRoadNames(): array
return [
'no-result' => ['does not exist', '59606', []],
'success' => ['famars', '59606', ['Rue De Famars']],
'compound-word' => ['rue sainte-catherine', '59606', [
'Rue Sainte-Catherine',
'Rue Du Faubourg Sainte-Catherine',
'Lotissement Sainte-Catherine',
'Rue Sainte-Barbe',
'Rue Catherine Samie',
'Résidence Catherine',
]],
'quote_accent' => ['l\'église', '68066', [
'Rue De L\'Église',
]],
'compound-word' => [
'rue sainte-catherine',
'59606',
[
'Rue Sainte-Catherine',
'Rue Du Faubourg Sainte-Catherine',
'Lotissement Sainte-Catherine',
'Rue Sainte-Barbe',
'Rue Catherine Samie',
'Résidence Catherine',
],
],
'quote_accent' => [
'l\'église',
'68066',
[
'Rue De L\'Église',
],
],
];
}

Expand Down Expand Up @@ -315,4 +323,16 @@ public function testFindSectionsInArea(): void
$geometry,
);
}

public function testConvertPolygonToRoadLines(): void
{
$polygonGeometry = '{"type":"Polygon","coordinates":[[[3.0739553997,50.6424619948],[3.0739665207,50.6424529935],[3.074522087,50.6418609607],[3.0777787224,50.63850051],[3.0776585827,50.6384534293],[3.0744016773,50.6418141505],[3.0744011534,50.6418146998],[3.0738499776,50.6424020525],[3.0733516646,50.6427136294],[3.0734506159,50.6427776181],[3.0739553997,50.6424619948]]]}';

$geometry = $this->roadGeocoder->convertPolygonRoadToLines($polygonGeometry);

$this->assertSame(
'{"type":"MultiLineString","coordinates":[[[3.073884143,50.64244362],[3.073451098,50.642714387]],[[3.073884143,50.64244362],[3.0739208,50.64241349]],[[3.0739208,50.64241349],[3.073925633,50.642408998]],[[3.077673753,50.638523301],[3.07447901,50.641819881]],[[3.07447901,50.641819881],[3.074444873,50.641855679]],[[3.074444873,50.641855679],[3.074444682,50.64185588]],[[3.074444682,50.64185588],[3.073925633,50.642408998]]]}',
$geometry,
);
}
}

0 comments on commit 4398158

Please sign in to comment.