Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

A Functional Guide to Cat Herding with PHP Generators

725 views

Published on

Using the Filter/Map/Reduce pattern in PHP to analyse geospatial data read from GPX files using Generators

Published in: Data & Analytics
  • Be the first to comment

  • Be the first to like this

A Functional Guide to Cat Herding with PHP Generators

  1. 1. A Functional Guide to Cat Herding with PHP Generators The Filter/Map/Reduce Pattern for PHP Generators
  2. 2. A Functional Guide to Cat Herding with PHP Generators • Blog Post http://markbakeruk.net/2016/01/19/a-functional-guide-to-cat-herding-with- php-generators/ • Code Examples https://github.com/MarkBaker/GeneratorFunctionExamples
  3. 3. A Functional Guide to Cat Herding with PHP Generators
  4. 4. A Functional Guide to Cat Herding with PHP Generators
  5. 5. A Functional Guide to Cat Herding with PHP Generators <?xml version="1.0" encoding="UTF-8" standalone="no" ?> <gpx xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.topografix.com/GPX/1/1" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"> <trk> <name>Roman 2015-11-23</name> <trkseg> <trkpt lat="53.54398800" lon="-2.7403680000"> <ele>146.100</ele><time>2015-11-23T11:05:07Z</time> </trkpt> <trkpt lat="53.54401600" lon="-2.7402620000"> <ele>141.300</ele><time>2015-11-23T11:05:15Z</time> </trkpt> ... <trkpt lat="53.54384500" lon="-2.7426130000"> <ele>110.500</ele><time>2015-11-23T14:58:57Z</time> </trkpt> </trkseg> </trk> </gpx>
  6. 6. A Functional Guide to Cat Herding with PHP Generators namespace GpxReader; class GpxHandler { protected $gpxReader; public function __construct($gpxFilename) { $this->gpxReader = new XMLReader(); $this->gpxReader->open($gpxFilename); } public function getElements($elementType) { while ($this->gpxReader->read()) { if ($this->gpxReader->nodeType == XMLREADER::ELEMENT && $this->gpxReader->name == $elementType) { $doc = new DOMDocument('1.0', 'UTF-8'); $xml = simplexml_import_dom($doc->importNode($this->gpxReader->expand(), true)); $gpxAttributes = $this->readAttributes($this->gpxReader); $gpxElement = $this->readChildren($xml); $gpxElement->position = $gpxAttributes; yield $gpxElement->timestamp => $gpxElement; } } } }
  7. 7. A Functional Guide to Cat Herding with PHP Generators // Create our initial Generator to read the gpx file $gpxReader = new GpxReaderGpxHandler($gpxFilename); // Iterate over the trackpoint set from the gpx file, // displaying each point detail in turn foreach ($gpxReader->getElements('trkpt') as $time => $element) { printf( '%s' . PHP_EOL . ' latitude: %7.4f longitude: %7.4f elevation: %d' . PHP_EOL, $time->format('Y-m-d H:i:s'), $element->position->latitude, $element->position->longitude, $element->elevation ); }
  8. 8. A Functional Guide to Cat Herding with PHP Generators 2015-11-23 11:05:07 latitude: 53.5440 longitude: -2.7404 elevation: 146 2015-11-23 11:05:15 latitude: 53.5440 longitude: -2.7403 elevation: 141 2015-11-23 11:05:25 latitude: 53.5440 longitude: -2.7402 elevation: 140 ... 2015-11-23 14:58:47 latitude: 53.5439 longitude: -2.7426 elevation: 103 2015-11-23 14:58:57 latitude: 53.5438 longitude: -2.7426 elevation: 110
  9. 9. A Functional Guide to Cat Herding with PHP Generators
  10. 10. Cat Herding with PHP Generators – Filter • A filter selects only a subset of values from the Traversable. • The rules for filtering are defined in a callback function. • If no callback is provided, then only non-empty values are returned.
  11. 11. Cat Herding with PHP Generators – Filter array_filter() Filters elements of an array using a callback function. array array_filter ( array $array [, callable $callback] ) Iterates over each value in the array passing them to the callback function. If the callback function returns true, the current value from array is returned into the result array, otherwise it is discarded. Array keys are preserved.
  12. 12. Cat Herding with PHP Generators – Filter function notEmpty($value) { return !empty($value); } /** * Version of filter to use with versions of PHP prior to 5.6.0, * without the `$flag` option * **/ function filter(Traversable $filter, Callable $callback = null) { if ($callback === null) { $callback = 'notEmpty'; } foreach ($filter as $key => $value) { if ($callback($value)) { yield $key => $value; } } }
  13. 13. Cat Herding with PHP Generators – Filter array_filter() Filters elements of an array using a callback function. array array_filter ( array $array [, callable $callback] ) array array_filter ( array $array [, callable $callback [, int $flag = 0 ]] ) Iterates over each value in the array passing them to the callback function. If the callback function returns true, the current value from array is returned into the result array, otherwise it is discarded. Array keys are preserved. [PHP < 5.6.0] [PHP >= 5.6.0]
  14. 14. Cat Herding with PHP Generators – Filter /** * The `$flag` option (and the constants ARRAY_FILTER_USE_KEY and ARRAY_FILTER_USE_BOTH) * were introduced in PHP 5.6.0 * **/ function filter(Traversable $filter, Callable $callback = null, $flag = 0) { if ($callback === null) { $callback = 'notEmpty'; } foreach ($filter as $key => $value) { switch($flag) { case ARRAY_FILTER_USE_KEY: ... case ARRAY_FILTER_USE_BOTH: ... default: ... } } }
  15. 15. Cat Herding with PHP Generators – Filter • Time Range Where was Roman between 11:30 and 12:00? • Geo-Fencing (“Bounding Box”) • Inside Did Osiris go anywhere near the main road? • Outside Has Lexie left the house at all?
  16. 16. Cat Herding with PHP Generators – Filter // Create our initial Generator to read the gpx file $gpxReader = new GpxReaderGpxHandler($gpxFilename); // Define the date/time filter parameters $startTime = new DateTime('2015-03-02 13:20:00Z'); $endTime = new DateTime('2015-03-02 13:30:00Z'); // Create the filter callback with the date/time parameters we've just defined $timeFilter = function($timestamp) use ($startTime, $endTime) { return $timestamp >= $startTime && $timestamp <= $endTime; };
  17. 17. Cat Herding with PHP Generators – Filter // Iterate over the trackpoint set from the gpx file, // displaying each point detail in turn foreach (filter($gpxReader->getElements('trkpt'), $timeFilter, ARRAY_FILTER_USE_KEY) as $time => $element) { printf( '%s' . PHP_EOL . ' latitude: %7.4f longitude: %7.4f elevation: %d' . PHP_EOL, $time->format('Y-m-d H:i:s'), $element->position->latitude, $element->position->longitude, $element->elevation ); }
  18. 18. Cat Herding with PHP Generators – Filter
  19. 19. Cat Herding with PHP Generators – Filter namespace GpxReaderHelpers; class BoundingBox { /** * Identify whether a trackpoint falls inside the defined bounding box * * @param GpxReaderGpxElement The trackpoint * @return boolean If the trackpoint falls outside (false) * or inside (true) the bounding box **/ public function inside(GpxReaderGpxElement $point) { return (($point->position->longitude >= $this->left) && ($point->position->longitude <= $this->right) && ($point->position->latitude >= $this->bottom) && ($point->position->latitude <= $this->top)); } }
  20. 20. Cat Herding with PHP Generators – Filter // Create our initial Generator to read the gpx file $gpxReader = new GpxReaderGpxHandler($gpxFilename); // Create a bounding box defining the coordinates we want to test each point against // This bounding box is for inside the house/garden $boundaries = new GpxReaderHelpersBoundingBox(); $boundaries->setLatitudes(53.54382, 53.54340); $boundaries->setLongitudes(-2.74059, -2.74005); // We want to set the filter to include only points inside the bounding box $boundingBoxFilter = [$boundaries, 'inside'];
  21. 21. Cat Herding with PHP Generators – Filter // Iterate over the trackpoint set from the gpx file, // displaying each point detail in turn foreach (filter($gpxReader->getElements('trkpt'), $boundingBoxFilter) as $time => $element) { printf( '%s' . PHP_EOL . ' latitude: %7.4f longitude: %7.4f elevation: %d' . PHP_EOL, $time->format('Y-m-d H:i:s'), $element->position->latitude, $element->position->longitude, $element->elevation ); }
  22. 22. Cat Herding with PHP Generators – Filter // Iterate over the trackpoint set from the gpx file, // displaying each point detail in turn // applying both a time filter (12:00:00-12:20:00 on 2015-11-23) // and a bounding box filter for inside the house/garden foreach (filter( filter( $gpxReader->getElements('trkpt'), $timeFilter, ARRAY_FILTER_USE_KEY ), $boundingBoxFilter ) as $time => $element) { printf( ... ); }
  23. 23. Cat Herding with PHP Generators Osiris Located Achievement Unlocked
  24. 24. Cat Herding with PHP Generators – Map • A map is like a foreach loop that transforms each value in the Traversable. • Each input value is transformed into a new output value. • The rules for the transformation are defined in a callback function.
  25. 25. Cat Herding with PHP Generators – Map array_map() Applies the callback to the elements of the given arrays. array array_map ( callable $callback , array $array1 [, array $... ] ) array_map() returns an array containing all the elements of array1 after applying the callback function to each one. The number of parameters that the callback function accepts should match the number of arrays passed to the array_map().
  26. 26. Cat Herding with PHP Generators – Map function map(Callable $callback, Traversable $iterator) { foreach ($iterator as $key => $value) { yield $key => $callback($value); } }
  27. 27. Cat Herding with PHP Generators – Map namespace GpxReaderHelpers; class DistanceCalculator { public function setDistance(GpxReaderGpxElement $point) { $point->distance = $this->calculateDistance($point); return $point; } }
  28. 28. Cat Herding with PHP Generators – Map // Create our initial Generator to read the gpx file $gpxReader = new GpxReaderGpxHandler($gpxFilename); // Set the mapper to calculate the distance between a trackpoint // and the previous trackpoint $distanceCalculator = new GpxReaderHelpersDistanceCalculator();
  29. 29. Cat Herding with PHP Generators – Map // Iterate over the trackpoint set from the gpx file, mapping the distances as we go, // displaying each point detail in turn foreach (map([$distanceCalculator, 'setDistance'], $gpxReader->getElements('trkpt')) as $time => $element) { printf( '%s' . PHP_EOL . ' latitude: %7.4f longitude: %7.4f elevation: %d' . PHP_EOL . ' distance from previous point: %5.2f m' . PHP_EOL, $time->format('Y-m-d H:i:s'), $element->position->latitude, $element->position->longitude, $element->elevation, $element->distance ); }
  30. 30. Cat Herding with PHP Generators – Map function mmap(Callable $callback, Traversable ...$iterators) { $mi = new MultipleIterator(MultipleIterator::MIT_NEED_ANY); foreach($iterators as $iterator) { $mi->attachIterator($iterator); } foreach($mi as $values) { yield $callback(...$values); } } http://nl3.php.net/manual/en/function.array-slice.php ... Splat Operator PHP >= 5.6.0 Argument Packing/Unpacking
  31. 31. Cat Herding with PHP Generators Lexie Located Achievement Unlocked
  32. 32. Cat Herding with PHP Generators – Reduce • A reduce aggregates all the values in the Traversable to a single value. • A callback function determines the process for the aggregation.
  33. 33. Cat Herding with PHP Generators – Reduce array_reduce() Iteratively reduces the array to a single value using a callback function. mixed array_reduce ( array $array , callable $callback [, mixed $initial = NULL ] ) array_reduce() applies the callback function iteratively to the elements of the array, so as to reduce the array to a single value.
  34. 34. Cat Herding with PHP Generators – Reduce function reduce(Traversable $iterator, Callable $callback, $initial = null) { $result = $initial; foreach($iterator as $value) { $result = $callback($result, $value); } return $result; }
  35. 35. Cat Herding with PHP Generators – Reduce • Bounding Box What Coordinates should I use for the bounding box when displaying the track on a map? • Distance How far has Roman walked while he’s been out? What is the furthest distance that Osiris has travelled from home?
  36. 36. Cat Herding with PHP Generators – Reduce // Iterate over our trackpoint set from the gpx file (mapping the distance as we go) // and reducing the results to calculate the total distance travelled $totalDistance = reduce( map([$distanceCalculator, 'setDistance'], $gpxReader->getElements('trkpt')), function($runningTotal, $value) { $runningTotal += $value->distance; return $runningTotal; }, 0.0 ); // Display the results of our reduce printf( 'Total distance travelled is %5.2f km' . PHP_EOL, $totalDistance / 1000 );
  37. 37. Cat Herding with PHP Generators – Reduce Total distance travelled is 4.33 km
  38. 38. Cat Herding with PHP Generators – Reduce namespace GpxReaderHelpers; class BoundingBox { function calculate($discard, GpxReaderGpxElement $point) { $this->top = max($point->position->latitude, $this->top); $this->bottom = min($point->position->latitude, $this->bottom); $this->left = min($point->position->longitude, $this->left); $this->right = max($point->position->longitude, $this->right); return $this; } }
  39. 39. Cat Herding with PHP Generators – Reduce // Create our initial Generator to read the gpx file $gpxReader = new GpxReaderGpxHandler($gpxFilename); // Set our bounding box callback $boundaries = new GpxReaderHelpersBoundingBox(); // Reduce our trackpoint set from the gpx file against the bounding box callback $boundingBox = reduce( $gpxReader->getElements('trkpt'), [$boundaries, 'calculate'] ); // Display the results of our reduce printf( 'Top: %7.4f Bottom: %7.4f' . PHP_EOL . 'Left: %7.4f Right: %7.4f' . PHP_EOL, $boundingBox->top, $boundingBox->bottom, $boundingBox->left, $boundingBox->right );
  40. 40. Cat Herding with PHP Generators – Reduce Top: 53.5445 Bottom: 53.5426 Left: -2.7433 Right: -2.7393
  41. 41. Cat Herding with PHP Generators Roman Located Achievement Unlocked
  42. 42. Cat Herding with PHP Generators iterator_apply() Call a function for every element in an iterator. int iterator_apply ( Traversable $iterator , callable $function [, array $args ] )
  43. 43. Cat Herding with PHP Generators iterator_count() Count the elements in an iterator. int iterator_count ( Traversable $iterator )
  44. 44. Cat Herding with PHP Generators iterator_to_array() Copy all values from the iterator into an array. array iterator_to_array ( Traversable $iterator [, bool $use_keys = true ] )
  45. 45. Cat Herding with PHP Generators // Create an instance of the fluent Generator Helper and set our filters, // mapper offset and limits // and the display callback to "do" (or execute) // In this case, the "do" is a display callback, // but it could also be a "reduce" callback withGenerator($trackPoints) ->filteredBy($boundingBoxFilter) ->filteredBy($timeFilter, ARRAY_FILTER_USE_KEY) ->map([$distanceCalculator, 'setDistance']) ->offset(1) ->limit(1) ->do($display);
  46. 46. A Functional Guide to Cat Herding with PHP Generators https://github.com/lstrojny/functional-php
  47. 47. A Functional Guide to Cat Herding with PHP Generators No cats were forced to walk anywhere that they didn't want to go during the writing of this presentation.
  48. 48. A Functional Guide to Cat Herding with PHP Generators ? Questions
  49. 49. Who am I? Mark Baker Design and Development Manager InnovEd (Innovative Solutions for Education) Ltd Coordinator and Developer of: Open Source PHPOffice library PHPExcel, PHPWord, PHPPresentation (formerly PHPPowerPoint), PHPProject, PHPVisio Minor contributor to PHP core @Mark_Baker https://github.com/MarkBaker http://uk.linkedin.com/pub/mark-baker/b/572/171 http://markbakeruk.net

×