api/src/PublicTransport/Service/PTDataProvider.php
<?php
/**
* Copyright (c) 2020, MOBICOOP. All rights reserved.
* This project is dual licensed under AGPL and proprietary licence.
***************************
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <gnu.org/licenses>.
***************************
* Licence MOBICOOP described in the file
* LICENSE
*/
namespace App\PublicTransport\Service;
use App\DataProvider\Entity\CitywayProvider;
use App\DataProvider\Entity\ConduentPTProvider;
use App\DataProvider\Entity\NavitiaProvider;
use App\Geography\Repository\TerritoryRepository;
use App\Geography\Service\GeoTools;
use App\PublicTransport\Entity\PTJourney;
use App\PublicTransport\Entity\PTLineStop;
use App\PublicTransport\Entity\PTTripPoint;
/**
* Public transport DataProvider.
*
* To add a provider :
* - write the custom Provider class in src/DataProvider/Entity/
* - complete the PROVIDERS array with the new provider
*
* @author Sylvain Briat <sylvain.briat@mobicoop.org>
* @author Maxime Bardot <maxime.bardot@mobicoop.org>
*/
class PTDataProvider
{
public const PROVIDERS = [
'cityway' => CitywayProvider::class,
'conduentPT' => ConduentPTProvider::class,
'navitia' => NavitiaProvider::class,
];
public const DATETIME_FORMAT = \DateTime::RFC3339;
public const DATETYPE_DEPARTURE = 'departure';
public const DATETYPE_ARRIVAL = 'arrival';
public const ALGORITHM_FASTEST = 'fastest';
public const ALGORITHM_SHORTEST = 'shortest';
public const ALGORITHM_MINCHANGES = 'minchanges';
private $geoTools;
private $PTProviders;
private $territoryRepository;
public function __construct(GeoTools $geoTools, TerritoryRepository $territoryRepository, array $params)
{
$this->geoTools = $geoTools;
$this->territoryRepository = $territoryRepository;
$this->PTProviders = $params['ptProviders'];
}
/**
* Get journeys from an external Public Transport data provider.
*
* @param string $provider The name of the provider (obsolete : not used anymore)
* @param string $origin_latitude The latitude of the origin point
* @param string $origin_longitude The longitude of the origin point
* @param string $destination_latitude The latitude of the destination point
* @param string $destination_longitude The longitude of the destination point
* @param \Datetime $date The datetime of the trip
* @param string $dateCriteria (optional) Date criteria like "arrival" or "departure"
* @param string $mode (optional) Mode criteria
*
* @return null|array The journeys found or null if no journey is found
*/
public function getJourneys(
?string $provider,
string $origin_latitude,
string $origin_longitude,
string $destination_latitude,
string $destination_longitude,
\DateTime $date,
string $dateType = null,
string $modes = null
): ?array {
$providerUri = null;
$territoryId = 'default';
if (count($this->PTProviders) > 1) {
// If there is a territory, we look for the right provider. If there is no, we take the default.
// Get the territory of the request
$territories = $this->territoryRepository->findPointTerritories($origin_latitude, $origin_longitude);
foreach ($territories as $territory) {
// If the territoryId is in the providers.json for PT, we use this one
if (isset($this->PTProviders[$territory['id']])) {
$territoryId = $territory['id'];
break;
}
}
}
// Values
$provider = $this->PTProviders[$territoryId]['dataprovider'];
// Authorized Providers
if (!array_key_exists($provider, self::PROVIDERS)) {
// echo "Unauthorized Providers";die;
return null;
}
$providerUri = $this->PTProviders[$territoryId]['url'];
$apikey = $this->PTProviders[$territoryId]['apikey'];
$username = $this->PTProviders[$territoryId]['username'];
$customParams = $this->PTProviders[$territoryId]['params'];
$providerClass = self::PROVIDERS[$provider];
$providerInstance = new $providerClass($providerUri);
$params = [
'origin_latitude' => $origin_latitude,
'origin_longitude' => $origin_longitude,
'destination_latitude' => $destination_latitude,
'destination_longitude' => $destination_longitude,
'date' => $date,
'username' => $username,
];
// $mode and $dateCriteria are forced if they're send in parameters
if (!is_null($dateType)) {
$params['dateType'] = $dateType;
}
if (!is_null($modes)) {
$params['modes'] = $modes;
}
foreach ($customParams as $key => $value) {
// We don't override previously set parameters
if (!isset($params[$key])) {
$params[$key] = $value;
}
}
$journeys = call_user_func_array([$providerInstance, 'getCollection'], [PTJourney::class, $apikey, $params]);
// Set the display label of the departure and arrival
foreach ($journeys as $journey) {
/**
* @var PTJourney $journey
*/
$departureAddress = $journey->getPTDeparture()->getAddress();
$departureAddress->setDisplayLabel($this->geoTools->getDisplayLabel($departureAddress));
$arrivalAddress = $journey->getPTArrival()->getAddress();
$arrivalAddress->setDisplayLabel($this->geoTools->getDisplayLabel($arrivalAddress));
foreach ($journey->getPTLegs() as $leg) {
$departureAddress = $leg->getPTDeparture()->getAddress();
$departureAddress->setDisplayLabel($this->geoTools->getDisplayLabel($departureAddress));
$arrivalAddress = $leg->getPTArrival()->getAddress();
$arrivalAddress->setDisplayLabel($this->geoTools->getDisplayLabel($arrivalAddress));
}
$journey->setPtProviderName(isset($this->PTProviders[$territoryId]['ptProviderName']) ? $this->PTProviders[$territoryId]['ptProviderName'] : null);
$journey->setPtProviderUrl(isset($this->PTProviders[$territoryId]['ptProviderUrl']) ? $this->PTProviders[$territoryId]['ptProviderUrl'] : null);
$journey->setProvider($provider);
}
return $journeys;
}
/**
* Get trip points from an external Public Transport data provider.
*
* @param string $provider The name of the provider
* @param float $latitude The latitude of the origin point
* @param float $longitude The longitude of the origin point
* @param int $perimeter Radius of the perimeter (in meters)
* @param string $transportModes The trip modes accepted (PT, BIKE, CAR, PT+BIKE, PT+CAR)
* @param string $keywords Keywords to search in trip point name
*
* @return null|array The trip points or null if no trip points is found
*/
public function getTripPoints(
string $provider,
float $latitude,
float $longitude,
int $perimeter,
string $transportModes,
string $keywords
): ?array {
if (!array_key_exists($provider, self::PROVIDERS)) {
return null;
}
$providerClass = self::PROVIDERS[$provider];
$providerInstance = new $providerClass();
return call_user_func_array([$providerInstance, 'getCollection'], [PTTripPoint::class, '', [
'latitude' => $latitude,
'longitude' => $longitude,
'perimeter' => $perimeter,
'transportModes' => $transportModes,
'keywords' => $keywords,
]]);
}
/**
* Get line stop from an external Public Transport data provider.
*
* @param string $provider The name of the provider
* @param int $logicalId The logicalId of the line stop
* @param string $transportModes|null The transport modes to search
*
* @return null|array The line stop found or null if no line stop is found
*/
public function getLineStop(
string $provider,
int $logicalId,
string $transportModes = ''
): ?array {
if (!array_key_exists($provider, self::PROVIDERS)) {
return null;
}
$providerClass = self::PROVIDERS[$provider];
$providerInstance = new $providerClass();
return call_user_func_array([$providerInstance, 'getCollection'], [PTLineStop::class, '', [
'provider' => $provider,
'logicalId' => $logicalId,
'transportModes' => $transportModes,
]]);
}
}