From 71312742412f69d1276e5a62adedf25f20f59dd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20A?= Date: Mon, 6 May 2024 14:16:04 +0200 Subject: [PATCH] Calculate Bounds when not in original GPX file (#71) * added a BoundsCalculator to compute it when not in the original GPX. Plus tests. * reverted some useless code related to PHP 8 and minor reformats. --- src/phpGPX/Helpers/BoundsCalculator.php | 52 ++++++ src/phpGPX/Models/Bounds.php | 29 +-- src/phpGPX/Models/Collection.php | 2 +- src/phpGPX/Models/Route.php | 5 +- src/phpGPX/Models/Segment.php | 4 + src/phpGPX/Models/Stats.php | 12 +- src/phpGPX/Models/Track.php | 8 +- src/phpGPX/Parsers/BoundsParser.php | 12 +- tests/LoadFileTest.php | 46 ++++- .../phpGPX/Parsers/BoundsParserTest.php | 9 +- .../phpGPX/Parsers/PointParserTest.json | 25 +++ .../phpGPX/Parsers/PointParserTest.php | 81 +++++++++ .../phpGPX/Parsers/PointParserTest.xml | 7 + .../phpGPX/Parsers/SegmentParserTest.json | 169 ++++++++++++++++++ .../phpGPX/Parsers/SegmentParserTest.php | 81 +++++++++ .../phpGPX/Parsers/SegmentParserTest.xml | 25 +++ 16 files changed, 533 insertions(+), 34 deletions(-) create mode 100644 src/phpGPX/Helpers/BoundsCalculator.php create mode 100644 tests/UnitTests/phpGPX/Parsers/PointParserTest.json create mode 100644 tests/UnitTests/phpGPX/Parsers/PointParserTest.php create mode 100644 tests/UnitTests/phpGPX/Parsers/PointParserTest.xml create mode 100644 tests/UnitTests/phpGPX/Parsers/SegmentParserTest.json create mode 100644 tests/UnitTests/phpGPX/Parsers/SegmentParserTest.php create mode 100644 tests/UnitTests/phpGPX/Parsers/SegmentParserTest.xml diff --git a/src/phpGPX/Helpers/BoundsCalculator.php b/src/phpGPX/Helpers/BoundsCalculator.php new file mode 100644 index 0000000..3c70eb9 --- /dev/null +++ b/src/phpGPX/Helpers/BoundsCalculator.php @@ -0,0 +1,52 @@ +longitude; + $lat = $curPoint->latitude; + + // Update northWest and southEast points if needed + if ($lat > $north) {$north = $lat;} + if ($lng > $east) {$east = $lng;} + if ($lat < $south) {$south = $lat;} + if ($lng < $west) {$west = $lng;} + } + + return [ + ["lat" => $north, "lng" => $west], + ["lat" => $south, "lng" => $east] + ]; + } +} diff --git a/src/phpGPX/Models/Bounds.php b/src/phpGPX/Models/Bounds.php index e2d41c3..e022f02 100644 --- a/src/phpGPX/Models/Bounds.php +++ b/src/phpGPX/Models/Bounds.php @@ -33,24 +33,27 @@ class Bounds implements Summarizable */ public $maxLongitude; - /** - * Bounds constructor. - */ - public function __construct() - { - $this->minLatitude = null; - $this->minLongitude = null; - $this->maxLongitude = null; - $this->maxLatitude = null; - } + /** + * @param ?float $minLatitude + * @param ?float $minLongitude + * @param ?float $maxLatitude + * @param ?float $maxLongitude + */ + public function __construct(?float $minLatitude, ?float $minLongitude, ?float $maxLatitude, ?float $maxLongitude) + { + $this->minLatitude = $minLatitude; + $this->minLongitude = $minLongitude; + $this->maxLatitude = $maxLatitude; + $this->maxLongitude = $maxLongitude; + } - /** + /** * Serialize object to array * @return array */ - public function toArray() - { + public function toArray(): array + { return [ 'minlat' => $this->minLatitude, 'minlon' => $this->minLongitude, diff --git a/src/phpGPX/Models/Collection.php b/src/phpGPX/Models/Collection.php index 8fabf7a..9ee3dcc 100644 --- a/src/phpGPX/Models/Collection.php +++ b/src/phpGPX/Models/Collection.php @@ -95,5 +95,5 @@ public function __construct() * Return all points in collection. * @return Point[] */ - abstract public function getPoints(); + abstract public function getPoints(): array; } diff --git a/src/phpGPX/Models/Route.php b/src/phpGPX/Models/Route.php index 599e666..e546dec 100644 --- a/src/phpGPX/Models/Route.php +++ b/src/phpGPX/Models/Route.php @@ -6,6 +6,7 @@ namespace phpGPX\Models; +use phpGPX\Helpers\BoundsCalculator; use phpGPX\Helpers\DistanceCalculator; use phpGPX\Helpers\ElevationGainLossCalculator; use phpGPX\Helpers\GeoHelper; @@ -40,8 +41,8 @@ public function __construct() * Return all points in collection. * @return Point[] */ - public function getPoints() - { + public function getPoints(): array + { /** @var Point[] $points */ $points = []; diff --git a/src/phpGPX/Models/Segment.php b/src/phpGPX/Models/Segment.php index 0e34faf..c66e5b0 100644 --- a/src/phpGPX/Models/Segment.php +++ b/src/phpGPX/Models/Segment.php @@ -6,6 +6,7 @@ namespace phpGPX\Models; +use phpGPX\Helpers\BoundsCalculator; use phpGPX\Helpers\DistanceCalculator; use phpGPX\Helpers\ElevationGainLossCalculator; use phpGPX\Helpers\GeoHelper; @@ -127,5 +128,8 @@ public function recalculateStats() $this->stats->averagePace = $this->stats->duration / ($this->stats->distance / 1000); } } + + list($northWest, $southEast) = BoundsCalculator::calculate($this->getPoints()); + $this->stats->bounds = [$northWest, $southEast]; } } diff --git a/src/phpGPX/Models/Stats.php b/src/phpGPX/Models/Stats.php index 89ae6ac..5a4c126 100644 --- a/src/phpGPX/Models/Stats.php +++ b/src/phpGPX/Models/Stats.php @@ -106,6 +106,14 @@ class Stats implements Summarizable */ public $duration = null; + /** + * An array of two points representing + * the most northwestern and the most + * southeastern points of a segment + * @var array + */ + public $bounds = array(); + /** * Reset all stats */ @@ -125,6 +133,7 @@ public function reset() $this->startedAtCoords = null; $this->finishedAt = null; $this->finishedAtCoords = null; + $this->bounds = null; } /** @@ -148,7 +157,8 @@ public function toArray() 'startedAtCoords' => $this->startedAtCoords, 'finishedAt' => DateTimeHelper::formatDateTime($this->finishedAt, phpGPX::$DATETIME_FORMAT, phpGPX::$DATETIME_TIMEZONE_OUTPUT), 'finishedAtCoords' => $this->finishedAtCoords, - 'duration' => (float)$this->duration + 'duration' => (float)$this->duration, + 'bounds' => $this->bounds ]; } } diff --git a/src/phpGPX/Models/Track.php b/src/phpGPX/Models/Track.php index 271817a..39d7ac2 100644 --- a/src/phpGPX/Models/Track.php +++ b/src/phpGPX/Models/Track.php @@ -6,6 +6,7 @@ namespace phpGPX\Models; +use phpGPX\Helpers\BoundsCalculator; use phpGPX\Helpers\GeoHelper; use phpGPX\Helpers\SerializationHelper; use phpGPX\phpGPX; @@ -37,8 +38,8 @@ public function __construct() * Return all points in collection. * @return Point[] */ - public function getPoints() - { + public function getPoints(): array + { /** @var Point[] $points */ $points = []; @@ -154,5 +155,8 @@ public function recalculateStats() $this->stats->averagePace = $this->stats->duration / ($this->stats->distance / 1000); } } + + list($northWest, $southEast) = BoundsCalculator::calculate($this->getPoints()); + $this->stats->bounds = [$northWest, $southEast]; } } diff --git a/src/phpGPX/Parsers/BoundsParser.php b/src/phpGPX/Parsers/BoundsParser.php index ada5ebf..ead0638 100644 --- a/src/phpGPX/Parsers/BoundsParser.php +++ b/src/phpGPX/Parsers/BoundsParser.php @@ -27,12 +27,12 @@ public static function parse(\SimpleXMLElement $node) return null; } - $bounds = new Bounds(); - - $bounds->minLatitude = isset($node['minlat']) ? (float) $node['minlat'] : null; - $bounds->minLongitude = isset($node['minlon']) ? (float) $node['minlon'] : null; - $bounds->maxLatitude = isset($node['maxlat']) ? (float) $node['maxlat'] : null; - $bounds->maxLongitude = isset($node['maxlon']) ? (float) $node['maxlon'] : null; + $bounds = new Bounds( + isset($node['minlat']) ? (float) $node['minlat'] : null, + isset($node['minlon']) ? (float) $node['minlon'] : null, + isset($node['maxlat']) ? (float) $node['maxlat'] : null, + isset($node['maxlon']) ? (float) $node['maxlon'] : null + ); return $bounds; } diff --git a/tests/LoadFileTest.php b/tests/LoadFileTest.php index c419664..352839d 100644 --- a/tests/LoadFileTest.php +++ b/tests/LoadFileTest.php @@ -7,7 +7,11 @@ class LoadFileTest extends TestCase { - public function testLoadXmlFileGeneratedByTimezero() + /** + * @coversNothing + * @return void + */ + public function testLoadXmlFileGeneratedByTimezero() { $file = __DIR__ . '/fixtures/timezero.gpx'; @@ -106,6 +110,16 @@ private function createExpectedArray() 'lng' => 0.0801333333365323 ], 'duration' => 9.0, + 'bounds' => [ + [ + 'lat' => 49.3635449998312, + 'lng' => 0.0801333333365323 + ], + [ + 'lat' => 49.3635266991555, + 'lng' => 0.0801483333364938 + ], + ] ], ], ], @@ -133,6 +147,16 @@ private function createExpectedArray() 'lng' => 0.0801333333365323 ], 'duration' => 9.0, + 'bounds' => [ + [ + 'lat' => 49.4574117319429, + 'lng' => 0.0342948235267376 + ], + [ + 'lat' => 49.4573700325059, + 'lng' => 0.0343682156842231 + ], + ] ], ], [ @@ -189,6 +213,16 @@ private function createExpectedArray() 'lng' => 0.0342948235267376 ], 'duration' => 3.0, + 'bounds' => [ + [ + 'lat' => 49.4574117319429, + 'lng' => 0.0342948235267376 + ], + [ + 'lat' => 49.4573700325059, + 'lng' => 0.0343682156842231 + ], + ] ], ], ], @@ -216,6 +250,16 @@ private function createExpectedArray() 'lng' => 0.0342948235267376 ], 'duration' => 3.0, + 'bounds' => [ + [ + 'lat' => 49.4574117319429, + 'lng' => 0.0342948235267376 + ], + [ + 'lat' => 49.4573700325059, + 'lng' => 0.0343682156842231 + ], + ] ], ], ], diff --git a/tests/UnitTests/phpGPX/Parsers/BoundsParserTest.php b/tests/UnitTests/phpGPX/Parsers/BoundsParserTest.php index 05f5654..0e84758 100644 --- a/tests/UnitTests/phpGPX/Parsers/BoundsParserTest.php +++ b/tests/UnitTests/phpGPX/Parsers/BoundsParserTest.php @@ -20,14 +20,7 @@ class BoundsParserTest extends AbstractParserTest public static function createTestInstance() { - $bounds = new Bounds(); - - $bounds->maxLatitude = 49.090543; - $bounds->maxLongitude = 18.886939; - $bounds->minLatitude = 49.072489; - $bounds->minLongitude = 18.814543; - - return $bounds; + return new Bounds(49.072489, 18.814543, 49.090543, 18.886939); } protected function setUp(): void diff --git a/tests/UnitTests/phpGPX/Parsers/PointParserTest.json b/tests/UnitTests/phpGPX/Parsers/PointParserTest.json new file mode 100644 index 0000000..30dc7eb --- /dev/null +++ b/tests/UnitTests/phpGPX/Parsers/PointParserTest.json @@ -0,0 +1,25 @@ +{ + "ageofdgpsdata": null, + "cmt": null, + "desc": null, + "dgpsid": null, + "difference": null, + "distance": null, + "ele": 2419, + "extensions": null, + "fix": null, + "geoidheight": null, + "hdop": null, + "lat": 46.571948, + "link": [], + "lon": 8.414757, + "magvar": null, + "name": null, + "pdop": null, + "sat": null, + "src": null, + "sym": null, + "time": "2017-08-13T07:10:41+00:00", + "type": null, + "vdop": null +} \ No newline at end of file diff --git a/tests/UnitTests/phpGPX/Parsers/PointParserTest.php b/tests/UnitTests/phpGPX/Parsers/PointParserTest.php new file mode 100644 index 0000000..16afe0b --- /dev/null +++ b/tests/UnitTests/phpGPX/Parsers/PointParserTest.php @@ -0,0 +1,81 @@ +latitude = 46.571948; + $point->longitude = 8.414757; + $point->elevation = 2419; + $point->time = DateTimeHelper::parseDateTime("2017-08-13T07:10:41.000Z"); + + return $point; + } + + public static function createTestInstanceWithValues( + float $latitude, + float $longitude, + float $elevation, + string $timeAsString) : Point + { + $point = new Point(Point::TRACKPOINT); + $point->latitude = $latitude; + $point->longitude = $longitude; + $point->elevation = $elevation; + $point->time = DateTimeHelper::parseDateTime($timeAsString); + + return $point; + } + + protected function setUp(): void + { + parent::setUp(); + + $this->testModelInstance = self::createTestInstance(); + } + + public function testParse() + { + $point = PointParser::parse($this->testXmlFile->trkpt); + + $this->assertNotEmpty($point); + + // Primitive attributes + $this->assertEquals($this->testModelInstance->latitude, $point->latitude); + $this->assertEquals($this->testModelInstance->longitude, $point->longitude); + $this->assertEquals($this->testModelInstance->elevation, $point->elevation); + $this->assertEquals($this->testModelInstance->time, $point->time); + } + + /** + * Returns output of ::toXML method of tested parser. + * @depends testParse + * @param \DOMDocument $document + * @return \DOMElement + */ + protected function convertToXML(\DOMDocument $document) + { + return PointParser::toXML($this->testModelInstance, $document); + } +} diff --git a/tests/UnitTests/phpGPX/Parsers/PointParserTest.xml b/tests/UnitTests/phpGPX/Parsers/PointParserTest.xml new file mode 100644 index 0000000..2b62779 --- /dev/null +++ b/tests/UnitTests/phpGPX/Parsers/PointParserTest.xml @@ -0,0 +1,7 @@ + + + + 2419 + + + \ No newline at end of file diff --git a/tests/UnitTests/phpGPX/Parsers/SegmentParserTest.json b/tests/UnitTests/phpGPX/Parsers/SegmentParserTest.json new file mode 100644 index 0000000..9e88c66 --- /dev/null +++ b/tests/UnitTests/phpGPX/Parsers/SegmentParserTest.json @@ -0,0 +1,169 @@ +{ + "extensions": null, + "points": [ + { + "ageofdgpsdata": null, + "cmt": null, + "desc": null, + "dgpsid": null, + "difference": null, + "distance": null, + "ele": 2419, + "extensions": null, + "fix": null, + "geoidheight": null, + "hdop": null, + "lat": 46.571948, + "link": [], + "lon": 8.414757, + "magvar": null, + "name": null, + "pdop": null, + "sat": null, + "src": null, + "sym": null, + "time": "2017-08-13T07:10:41+00:00", + "type": null, + "vdop": null + }, + { + "ageofdgpsdata": null, + "cmt": null, + "desc": null, + "dgpsid": null, + "difference": 11.252021780368882, + "distance": 11.252021780368882, + "ele": 2418.8833883882, + "extensions": null, + "fix": null, + "geoidheight": null, + "hdop": null, + "lat": 46.572016, + "link": [], + "lon": 8.414866, + "magvar": null, + "name": null, + "pdop": null, + "sat": null, + "src": null, + "sym": null, + "time": "2017-08-13T07:10:54+00:00", + "type": null, + "vdop": null + }, + { + "ageofdgpsdata": null, + "cmt": null, + "desc": null, + "dgpsid": null, + "difference": 8.772816467963736, + "distance": 20.024838248332617, + "ele": 2419.8999900064, + "extensions": null, + "fix": null, + "geoidheight": null, + "hdop": null, + "lat": 46.572088, + "link": [], + "lon": 8.414911, + "magvar": null, + "name": null, + "pdop": null, + "sat": null, + "src": null, + "sym": null, + "time": "2017-08-13T07:11:56+00:00", + "type": null, + "vdop": null + }, + { + "ageofdgpsdata": null, + "cmt": null, + "desc": null, + "dgpsid": null, + "difference": 2.979832475456357, + "distance": 23.004670723788976, + "ele": 2422, + "extensions": null, + "fix": null, + "geoidheight": null, + "hdop": null, + "lat": 46.572069, + "link": [], + "lon": 8.414912, + "magvar": null, + "name": null, + "pdop": null, + "sat": null, + "src": null, + "sym": null, + "time": "2017-08-13T07:12:15+00:00", + "type": null, + "vdop": null + }, + { + "ageofdgpsdata": null, + "cmt": null, + "desc": null, + "dgpsid": null, + "difference": 3.8919896350020755, + "distance": 26.896660358791053, + "ele": 2425, + "extensions": null, + "fix": null, + "geoidheight": null, + "hdop": null, + "lat": 46.572054, + "link": [], + "lon": 8.414888, + "magvar": null, + "name": null, + "pdop": null, + "sat": null, + "src": null, + "sym": null, + "time": "2017-08-13T07:12:18+00:00", + "type": null, + "vdop": null + } + ], + "stats": { + "avgPace": 3949.728409446995, + "avgSpeed": 0.2531819650202255, + "bounds": [ + { + "lat": 46.572088, + "lng": 8.414757 + }, + { + "lat": 46.571948, + "lng": 8.414912 + } + ], + "cumulativeElevationGain": 6.116611611800181, + "cumulativeElevationLoss": 0.11661161180018098, + "distance": 24.558650606961873, + "duration": 97, + "finishedAt": "2017-08-13T07:12:18+00:00", + "finishedAtCoords": { + "lat": 46.572054, + "lng": 8.414888 + }, + "maxAltitude": 2425, + "maxAltitudeCoords": { + "lat": 46.572054, + "lng": 8.414888 + }, + "minAltitude": 2418.8833883882, + "minAltitudeCoords": { + "lat": 46.572016, + "lng": 8.414866 + }, + "realDistance": 26.896660358791053, + "startedAt": "2017-08-13T07:10:41+00:00", + "startedAtCoords": { + "lat": 46.571948, + "lng": 8.414757 + } + } +} \ No newline at end of file diff --git a/tests/UnitTests/phpGPX/Parsers/SegmentParserTest.php b/tests/UnitTests/phpGPX/Parsers/SegmentParserTest.php new file mode 100644 index 0000000..ead9287 --- /dev/null +++ b/tests/UnitTests/phpGPX/Parsers/SegmentParserTest.php @@ -0,0 +1,81 @@ +points = [ + PointParserTest::createTestInstanceWithValues(46.571948, 8.414757, 2419, "2017-08-13T07:10:41.000Z"), + PointParserTest::createTestInstanceWithValues(46.572016, 8.414866, 2418.8833883882, "2017-08-13T07:10:54.000Z"), + PointParserTest::createTestInstanceWithValues(46.572088, 8.414911, 2419.8999900064, "2017-08-13T07:11:56.000Z"), + PointParserTest::createTestInstanceWithValues(46.572069, 8.414912, 2422, "2017-08-13T07:12:15.000Z"), + PointParserTest::createTestInstanceWithValues(46.572054, 8.414888, 2425, "2017-08-13T07:12:18.000Z") + ]; + $segment->recalculateStats(); + + return $segment; + } + + protected function setUp(): void + { + parent::setUp(); + + $this->testModelInstance = self::createTestInstance(); + } + + public function testParse() + { + $segment = SegmentParser::parse($this->testXmlFile->trkseg); + + $this->assertNotEmpty($segment); + + // Test second point + $point = $segment[0]->points[1]; + $this->assertEquals($this->testModelInstance->points[1]->latitude, $point->latitude); + $this->assertEquals($this->testModelInstance->points[1]->longitude, $point->longitude); + $this->assertEquals($this->testModelInstance->points[1]->elevation, $point->elevation); + $this->assertEquals($this->testModelInstance->points[1]->time, $point->time); + + // Stats + $this->assertNotEmpty($this->testModelInstance->stats); + + // Check the boundaries + $nw = $this->testModelInstance->stats->bounds[0]; + $se = $this->testModelInstance->stats->bounds[1]; + $this->assertEquals(46.572088, $nw["lat"]); + $this->assertEquals(8.414757, $nw["lng"]); + $this->assertEquals(46.571948, $se["lat"]); + $this->assertEquals(8.414912, $se["lng"]); + } + + /** + * Returns output of ::toXML method of tested parser. + * @depends testParse + * @param \DOMDocument $document + * @return \DOMElement + */ + protected function convertToXML(\DOMDocument $document) + { + return SegmentParser::toXML($this->testModelInstance, $document); + } +} diff --git a/tests/UnitTests/phpGPX/Parsers/SegmentParserTest.xml b/tests/UnitTests/phpGPX/Parsers/SegmentParserTest.xml new file mode 100644 index 0000000..fbc3dfc --- /dev/null +++ b/tests/UnitTests/phpGPX/Parsers/SegmentParserTest.xml @@ -0,0 +1,25 @@ + + + + + 2419 + + + + 2418.8833883882 + + + + 2419.8999900064 + + + + 2422 + + + + 2425 + + + + \ No newline at end of file