Skip to content

Gtfs plus directions #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 156 additions & 0 deletions src/Trafiklab/Gtfs/Model/Entities/Directions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
<?php

namespace Trafiklab\Gtfs\Model\Entities;

use Trafiklab\Gtfs\Model\GtfsArchive;

/**
* Directions.txt is a GTFS+ Experimental dataset.
* GTFS+ is an extension to GTFS in use by the San Francisco (MTC) 511.org, Trillium, etc.
* Many transit agencies in the SF Bay Area publish GTFS+.
* @link https://www.transitwiki.org/TransitWiki/index.php/GTFS+
*
* There are many Agencies that use Trillium to generate GTFS feeds, and directions can be useful when
* relating back to GTFS-RT datasets.
* @link https://trilliumtransit.com/gtfs/reference/#directions
*/
class Directions
{
/**
* Per the GTFS+ Specification, all fields are required.
*/
/** @var string $route_id */
private $route_id;
/** @var int $direction_id */
private $direction_id;
/**
*
* @var string $direction
* Possible Values:
* North, South, East, West, Northeast, Northwest, Southeast, Southwest
* Clockwise, Counterclockwise
* Inbound, Outbound
* Loop, A Loop, B Loop
*/
private $direction;
private $archive;

/**
* Directions constructor.
* @param GtfsArchive $archive The archive in which this data originates, used to link between files.
* @param array $data An associative array containing the variable values.
*
* @internal Not to be used outside of the Trafiklab\Gtfs\Model package.
*/
function __construct(GtfsArchive $archive, array $data)
{
foreach ($data as $variable => $value) {
$this->$variable = $value;
}
$this->archive = $archive;
}

/**
* @return string
*/
public function getRouteId(): string
{
return $this->route_id;
}

/**
* @param string $route_id
*/
public function setRouteId(string $route_id): void
{
$this->route_id = $route_id;
}

/**
* @return int
*/
public function getDirectionId(): int
{
return $this->direction_id;
}

/**
* @param int $direction_id
*/
public function setDirectionId(int $direction_id): void
{
$this->direction_id = $direction_id;
}

/**
* @return string
*/
public function getDirection(): string
{
return $this->direction;
}

/**
* @param string $direction
*/
public function setDirection(string $direction): void
{
$this->direction = $direction;
}

/**
* @return GtfsArchive
*/
public function getArchive(): GtfsArchive
{
return $this->archive;
}

/**
* @param GtfsArchive $archive
*/
public function setArchive(GtfsArchive $archive): void
{
$this->archive = $archive;
}

/**
* Get the Abbreviation of the Cardinal Direction.
* - Ex: North = N, West = W, South = S, Southeast = SE.
* @param bool $adjective - Whether or not to use NorthBound (NB)
* - Ex: North = NB, NorthEast = NB, South = SB, SouthWest = SB.
* @return string
* * Possible Values:
* - North, South, East, West, Northeast, Northwest, Southeast, Southwest
*/
public function getCardinalDirectionAbrv(bool $adjective = false): string
{
switch (strtolower($this->getDirection())) {
case 'north':
case 'northeast':
case 'northwest':
return $adjective ? 'NB' : 'N';
case 'south':
case 'southeast':
case 'southwest':
return $adjective ? 'SB' : 'S';
case 'west':
return $adjective ? 'WB' : 'W';
case 'east':
return $adjective ? 'EB' : 'E';
default:
return '';
}
}

/**
* Return the value of Inbound(1) or Outbound(0) depending on the value of direction_id.
* Inbound assumes that the vehicle is arriving at said stop or location.
* Outbound assumes that the vehicle is leaving said stop or location.
* @return string
*/
public function getBoundDirection(): string
{
return $this->getDirectionId() === 0 ? 'Outbound' : 'Inbound';
}
}
31 changes: 31 additions & 0 deletions src/Trafiklab/Gtfs/Model/Files/GtfsDirectionsFile.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

namespace Trafiklab\Gtfs\Model\Files;

use Trafiklab\Gtfs\Model\Entities\Directions;
use Trafiklab\Gtfs\Model\GtfsArchive;
use Trafiklab\Gtfs\Util\Internal\GtfsParserUtil;

class GtfsDirectionsFile
{
private $dataRows;

public function __construct(GtfsArchive $parent, string $filePath)
{
$this->dataRows = GtfsParserUtil::deserializeCSV(
$parent,
$filePath,
Directions::class
);
}

/**
* Get the file data as an array of its rows.
*
* @return Directions[]
*/
public function getDirections(): array
{
return $this->dataRows;
}
}
9 changes: 9 additions & 0 deletions src/Trafiklab/Gtfs/Model/GtfsArchive.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Trafiklab\Gtfs\Model\Files\GtfsAgencyFile;
use Trafiklab\Gtfs\Model\Files\GtfsCalendarDatesFile;
use Trafiklab\Gtfs\Model\Files\GtfsCalendarFile;
use Trafiklab\Gtfs\Model\Files\GtfsDirectionsFile;
use Trafiklab\Gtfs\Model\Files\GtfsFeedInfoFile;
use Trafiklab\Gtfs\Model\Files\GtfsFrequenciesFile;
use Trafiklab\Gtfs\Model\Files\GtfsRoutesFile;
Expand Down Expand Up @@ -38,6 +39,7 @@ class GtfsArchive
private const PATHWAYS_TXT = "pathways.txt"; // Unsupported at this moment
private const LEVELS_TXT = "levels.txt"; // Unsupported at this moment
private const FEED_INFO_TXT = "feed_info.txt";
private const DIRECTIONS_TXT = "directions.txt"; // Experimental GTFS+ feed.

private const TEMP_ROOT = "/tmp/gtfs/";

Expand Down Expand Up @@ -320,6 +322,13 @@ public function getFrequenciesFile(): GtfsFrequenciesFile
{
return $this->loadGtfsFileThroughCache(__METHOD__, self::FREQUENCIES_TXT, GtfsFrequenciesFile::class);
}
/**
* @return GtfsDirectionsFile|null
*/
public function getDirectionsFile(): ?GtfsDirectionsFile
{
return $this->loadGtfsFileThroughCache(__METHOD__, self::DIRECTIONS_TXT, GtfsDirectionsFile::class);
}

/**
* Delete the uncompressed files. This should be done as a cleanup when you're ready.
Expand Down
Binary file modified tests/Resources/Gtfs/minified-test-feed.zip
Binary file not shown.
29 changes: 29 additions & 0 deletions tests/Trafiklab/Gtfs/GtfsArchiveIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -224,4 +224,33 @@ public function testGetAgency()
self::assertNull($agencies[1]->getAgencyEmail(), 'Agency Email is not null!');
self::assertEquals('000-000-0000', $agencies[1]->getAgencyPhone(), 'Agency Phone is not equal to 000-000-0000');
}

/**
* Test the Directions GTFSArchive.
* @return void
*/
public function testGetDirections()
{
/** Directions file should be retrieved. */
$directions = $this->gtfsArchive->getDirectionsFile()->getDirections();
/** Test correct number of directions parsed. */
self::assertEquals(17, count($directions), 'Parsed incorrect number of Directions.');
/** Test Types */
self::assertIsString($directions[0]->getRouteId(), 'route_id should be a string.');
self::assertIsString($directions[0]->getDirection(), 'direction should be a string.');
self::assertIsNumeric($directions[0]->getDirectionId(), 'direction_id should be numeric (0,1).');
/** Test Getters/Validity */
self::assertEquals(0, $directions[16]->getDirectionId(), 'direction_id should be 0.');
self::assertEquals('North', $directions[0]->getDirection(), 'Should Equal North.');
self::assertEquals('1', $directions[0]->getRouteId(), 'Should equal 1');
self::assertEquals('Northeast', $directions[4]->getDirection(), 'Should Equal Northeast.');
self::assertEquals('A Loop', $directions[15]->getDirection(), 'Should Equal A Loop.');
/** Test Abbreviations method. */
self::assertEquals('N', $directions[0]->getCardinalDirectionAbrv(), 'Should Equal N');
self::assertEquals('NB', $directions[0]->getCardinalDirectionAbrv(true), 'Should Equal NB');
self::assertEquals('SB', $directions[6]->getCardinalDirectionAbrv(true), 'Should Equal SB');
/** Test DirectionBound method (Inbound/Outbound) */
self::assertEquals("Inbound", $directions[0]->getBoundDirection(), 'Should be equal to Inbound.');
self::assertEquals("Outbound", $directions[16]->getBoundDirection(), 'Should be equal to Outbound.');
}
}