Skip to content

Commit

Permalink
RD/RN : Détecte le côté à partir du sens concerné
Browse files Browse the repository at this point in the history
  • Loading branch information
florimondmanca committed Dec 17, 2024
1 parent cafbb7f commit 99ecb30
Show file tree
Hide file tree
Showing 11 changed files with 203 additions and 52 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace App\Application\Exception;

class BothDirectionsNotSupportedAtPointNumbers extends GeocodingFailureException
{
}
18 changes: 18 additions & 0 deletions src/Application/PointNumberSideDetectorInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace App\Application;

interface PointNumberSideDetectorInterface
{
public function detect(
string $direction,
string $administrator,
string $roadNumber,
string $fromPointNumber,
int $fromAbscissa,
string $toPointNumber,
int $toAbscissa,
): array;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace App\Application\Regulation\Query\Location;

use App\Application\PointNumberSideDetectorInterface;
use App\Application\QueryInterface;
use App\Application\RoadGeocoderInterface;
use App\Application\RoadSectionMakerInterface;
Expand All @@ -13,11 +14,24 @@ final class GetNumberedRoadGeometryQueryHandler implements QueryInterface
public function __construct(
private RoadSectionMakerInterface $roadSectionMaker,
private RoadGeocoderInterface $roadGeocoder,
private PointNumberSideDetectorInterface $pointNumberSideDetector,
) {
}

public function __invoke(GetNumberedRoadGeometryQuery $query): string
{
$command = $query->command;

[$command->fromSide, $command->toSide] = $this->pointNumberSideDetector->detect(
$command->direction,
$command->administrator,
$command->roadNumber,
$command->fromPointNumber,
$command->fromAbscissa ?? 0,
$command->toPointNumber,
$command->toAbscissa ?? 0,
);

if ($query->geometry) {
return $query->geometry;
}
Expand Down
6 changes: 6 additions & 0 deletions src/Application/RoadGeocoderInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,10 @@ public function findRoadNames(string $search, string $cityCode): array;
public function findSectionsInArea(string $areaGeometry, array $excludeTypes = [], ?bool $clipToArea = false): string;

public function convertPolygonRoadToLines(string $geometry): string;

public function getAvailableSidesAtPointNumber(
string $administrator,
string $roadNumber,
string $pointNumber,
): array;
}
13 changes: 13 additions & 0 deletions src/Domain/Regulation/Location/NumberedRoad.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,17 @@ public function update(
$this->toSide = $toSide;
$this->direction = $direction;
}

/**
* Compare two PR+abs.
* Return -1 if A < B, 0 if A === B, or 1 if A > B
*/
public static function comparePointNumber(
string $pointNumberA,
int $abscissaA,
string $pointNumberB,
int $abscissaB,
): int {
return 0;
}
}
30 changes: 30 additions & 0 deletions src/Infrastructure/Adapter/BdTopoRoadGeocoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -414,4 +414,34 @@ public function convertPolygonRoadToLines(string $geometry): string

return $row['geom'];
}

public function getAvailableSidesAtPointNumber(
string $administrator,
string $roadNumber,
string $pointNumber,
): array {
$rows = $this->bdtopoConnection->fetchAllAssociative(
\sprintf(
'SELECT p.cote AS side
FROM point_de_repere AS p
WHERE p.gestionnaire = :administrator
AND p.route = :roadNumber
AND p.numero = :pointNumber
',
),
[
'administrator' => $administrator,
'roadNumber' => $roadNumber,
'pointNumber' => $pointNumber,
],
);

$sides = [];

foreach ($rows as $row) {
$sides[] = $row['side'];
}

return $sides;
}
}
80 changes: 80 additions & 0 deletions src/Infrastructure/Adapter/PointNumberSideDetector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

declare(strict_types=1);

namespace App\Infrastructure\Adapter;

use App\Application\Exception\BothDirectionsNotSupportedAtPointNumbers;
use App\Application\PointNumberSideDetectorInterface;
use App\Application\RoadGeocoderInterface;
use App\Domain\Regulation\Enum\DirectionEnum;
use App\Domain\Regulation\Enum\RoadSideEnum;
use App\Domain\Regulation\Location\NumberedRoad;

final class PointNumberSideDetector implements PointNumberSideDetectorInterface
{
public function __construct(
private readonly RoadGeocoderInterface $roadGeocoder,
) {
}

public function detect(
string $direction,
string $administrator,
string $roadNumber,
string $fromPointNumber,
int $fromAbscissa,
string $toPointNumber,
int $toAbscissa,
): array {
$sidesAtFromPoint = $this->roadGeocoder->getAvailableSidesAtPointNumber(
$administrator,
$roadNumber,
$fromPointNumber,
);

$sidesAtToPoint = $this->roadGeocoder->getAvailableSidesAtPointNumber(
$administrator,
$roadNumber,
$toPointNumber,
);

$isSingleWayAtFromPoint = \in_array(RoadSideEnum::U->value, $sidesAtFromPoint);
$isSingleWayAtToPoint = \in_array(RoadSideEnum::U->value, $sidesAtToPoint);

if ($isSingleWayAtFromPoint || $isSingleWayAtToPoint) {
// L'un des deux PR au moins est sur une chaussée unique
// On doit forcément utiliser des PR de type U.
return [RoadSideEnum::U->value, RoadSideEnum::U->value];
}

// Les deux PR se trouvent sur une section à chaussée séparée.
// A priori, les deux côtés G ou D sont possibles au niveau de chaque PR.
// On choisit le côté adéquat en fonction de la direction demandée et de l'ordre
// des PR dans le sens des PR croissants.
// Le "Double sens" n'est pas supporté pour l'instant, il faut saisir deux localisations,
// une dans chaque sens.

if ($direction === DirectionEnum::BOTH->value) {
// TODO: handle in controller
throw new BothDirectionsNotSupportedAtPointNumbers();
}

if (NumberedRoad::comparePointNumber($fromPointNumber, $fromAbscissa, $toPointNumber, $toAbscissa) <= 0) {
// Le PR A est situé avant le PR B, dans l'ordre des PR croissants.
// On doit choisir le côté D si le sens A -> B est demandé, et le côté G sinon.
$side = $direction === DirectionEnum::A_TO_B->value
? RoadSideEnum::D->value
: RoadSideEnum::G->value;

return [$side, $side];
}

// Le PR A est situé après le PR B, donc on choisit le côté G si A->B est demandé, et le côté D sinon.
$side = $direction === DirectionEnum::A_TO_B->value
? RoadSideEnum::G->value
: RoadSideEnum::D->value;

return [$side, $side];
}
}
28 changes: 0 additions & 28 deletions src/Infrastructure/Form/Regulation/NumberedRoadFormType.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

use App\Application\Regulation\Command\Location\SaveNumberedRoadCommand;
use App\Domain\Regulation\Enum\DirectionEnum;
use App\Domain\Regulation\Enum\RoadSideEnum;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
Expand Down Expand Up @@ -43,11 +42,6 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
'help' => 'regulation.location.referencePoint.pointNumber.help',
],
)
->add(
'fromSide',
ChoiceType::class,
options: $this->getRoadSideOptions(),
)
->add(
'toPointNumber',
TextType::class,
Expand All @@ -56,11 +50,6 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
'help' => 'regulation.location.referencePoint.pointNumber.help',
],
)
->add(
'toSide',
ChoiceType::class,
options: $this->getRoadSideOptions(),
)
->add(
'fromAbscissa',
IntegerType::class,
Expand Down Expand Up @@ -92,23 +81,6 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
});
}

private function getRoadSideOptions(): array
{
$choices = [];

foreach (RoadSideEnum::cases() as $case) {
$choices[\sprintf('regulation.location.road.side.%s', $case->value)] = $case->value;
}

return [
'choices' => array_merge(
$choices,
),
'label' => 'regulation.location.road.side',
'help' => 'regulation.location.road.side.help',
];
}

private function getAdministratorOptions(array $administrators, string $roadType): array
{
$choices = [];
Expand Down
2 changes: 0 additions & 2 deletions templates/regulation/fragments/_measure_form.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,6 @@
</legend>
<div class="fr-grid-row fr-grid-row--gutters">
{{ form_row(form[roadType].fromPointNumber, { row_attr: {class:'fr-col-12 fr-col-md-4'}, attr: {class: 'fr-input'}}) }}
{{ form_row(form[roadType].fromSide, { row_attr: {class:'fr-col-12 fr-col-md-4'}, attr: {class: 'fr-select'}}) }}
{{ form_row(form[roadType].fromAbscissa, { row_attr: {class:'fr-col-12 fr-col-md-4'}, attr: {class: 'fr-input'}}) }}
</div>
</fieldset>
Expand All @@ -251,7 +250,6 @@
</legend>
<div class="fr-grid-row fr-grid-row--gutters">
{{ form_row(form[roadType].toPointNumber, { row_attr: {class:'fr-col-12 fr-col-md-4'}, attr: {class: 'fr-input'}}) }}
{{ form_row(form[roadType].toSide, { row_attr: {class:'fr-col-12 fr-col-md-4'}, attr: {class: 'fr-select'}}) }}
{{ form_row(form[roadType].toAbscissa, { row_attr: {class:'fr-col-12 fr-col-md-4'}, attr: {class: 'fr-input'}}) }}
</div>
</fieldset>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace App\Tests\Unit\Application\Regulation\Command\Location;

use App\Application\IdFactoryInterface;
use App\Application\PointNumberSideDetectorInterface;
use App\Application\Regulation\Command\Location\SaveNumberedRoadCommand;
use App\Application\Regulation\Command\Location\SaveNumberedRoadCommandHandler;
use App\Domain\Geography\Coordinates;
Expand All @@ -31,11 +32,13 @@ final class SaveNumberedRoadCommandHandlerTest extends TestCase

private MockObject $idFactory;
private MockObject $numberedRoadRepository;
private $pointNumberSideDetector;

public function setUp(): void
{
$this->idFactory = $this->createMock(IdFactoryInterface::class);
$this->numberedRoadRepository = $this->createMock(NumberedRoadRepositoryInterface::class);
$this->pointNumberSideDetector = $this->createMock(PointNumberSideDetectorInterface::class);

$this->administrator = 'Département de Loire-Atlantique';
$this->roadNumber = 'D12';
Expand Down Expand Up @@ -90,9 +93,24 @@ public function testCreate(): void
)
->willReturn($createdNumberedRoad);

$this->pointNumberSideDetector
->expects(self::once())
->method('detect')
->with(
$this->direction,
$this->administrator,
$this->roadNumber,
$this->fromPointNumber,
$this->fromAbscissa,
$this->toPointNumber,
$this->toAbscissa,
)
->willReturn([$this->fromSide, $this->toSide]);

$handler = new SaveNumberedRoadCommandHandler(
$this->idFactory,
$this->numberedRoadRepository,
$this->pointNumberSideDetector,
);

$command = new SaveNumberedRoadCommand();
Expand All @@ -101,10 +119,8 @@ public function testCreate(): void
$command->administrator = $this->administrator;
$command->roadNumber = $this->roadNumber;
$command->fromPointNumber = $this->fromPointNumber;
$command->fromSide = $this->fromSide;
$command->fromAbscissa = $this->fromAbscissa;
$command->toPointNumber = $this->toPointNumber;
$command->toSide = $this->toSide;
$command->toAbscissa = $this->toAbscissa;

$result = $handler($command);
Expand Down Expand Up @@ -138,9 +154,24 @@ public function testUpdate(): void
->expects(self::never())
->method('add');

$this->pointNumberSideDetector
->expects(self::once())
->method('detect')
->with(
$this->direction,
$this->administrator,
$this->roadNumber,
$this->fromPointNumber,
$this->fromAbscissa,
$this->toPointNumber,
$this->toAbscissa,
)
->willReturn([$this->fromSide, $this->toSide]);

$handler = new SaveNumberedRoadCommandHandler(
$this->idFactory,
$this->numberedRoadRepository,
$this->pointNumberSideDetector,
);

$command = new SaveNumberedRoadCommand($numberedRoad);
Expand Down
20 changes: 0 additions & 20 deletions translations/messages.fr.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -553,14 +553,6 @@
<source>regulation.location.referencePoint.to</source>
<target>Point de fin</target>
</trans-unit>
<trans-unit id="regulation.location.road.side">
<source>regulation.location.road.side</source>
<target>Côté</target>
</trans-unit>
<trans-unit id="regulation.location.road.side.help">
<source>regulation.location.road.side.help</source>
<target>Côté de la route où se situe le PR</target>
</trans-unit>
<trans-unit id="regulation.location.referencePoint">
<source>regulation.location.referencePoint</source>
<target>Section concernée</target>
Expand All @@ -569,18 +561,6 @@
<source>regulation.location.referencePoint.help</source>
<target>Indiquez la section de route concernée par la mesure. La restriction s'applique à toutes les voies de la chaussée</target>
</trans-unit>
<trans-unit id="regulation.location.road.side.D">
<source>regulation.location.road.side.D</source>
<target>Chaussée droite (D)</target>
</trans-unit>
<trans-unit id="regulation.location.road.side.G">
<source>regulation.location.road.side.G</source>
<target>Chaussée gauche (G)</target>
</trans-unit>
<trans-unit id="regulation.location.road.side.U">
<source>regulation.location.road.side.U</source>
<target>Chaussée unique (U)</target>
</trans-unit>
<trans-unit id="regulation.location.referencePoint.pointNumber">
<source>regulation.location.referencePoint.pointNumber</source>
<target>PR</target>
Expand Down

0 comments on commit 99ecb30

Please sign in to comment.