diff --git a/lib/ActivityShortcode.class.php b/lib/ActivityShortcode.class.php index 92289db..a43382d 100644 --- a/lib/ActivityShortcode.class.php +++ b/lib/ActivityShortcode.class.php @@ -10,7 +10,7 @@ public static function init() { } // Shortcode handler function - // [ride id=id som=metric map_width="100%" map_height="400px"] + // [ride id=id som=metric map_width="100%" map_height="400px" markers=false] public static function handler( $atts ) { self::$add_script = true; @@ -20,6 +20,7 @@ public static function handler( $atts ) { 'map_width' => '480', 'map_height' => '320', 'athlete_token' => WPStrava::get_instance()->settings->get_default_token(), + 'markers' => false, ); extract( shortcode_atts( $defaults, $atts ) ); @@ -67,7 +68,7 @@ public static function handler( $atts ) { ' . - WPStrava_StaticMap::get_image_tag( $ride_details, $map_height, $map_width ) . + WPStrava_StaticMap::get_image_tag( $ride_details, $map_height, $map_width, $markers ) . ''; } // End if( $ride_details ). } // handler diff --git a/lib/Polyline.php b/lib/Polyline.php new file mode 100755 index 0000000..692e128 --- /dev/null +++ b/lib/Polyline.php @@ -0,0 +1,152 @@ +. + * + * @category Mapping + * @package Polyline + * @author E. McConville + * @copyright 2009-2015 E. McConville + * @license http://www.gnu.org/licenses/lgpl.html LGPL v3 + * @version GIT: $Id: db01b3fea5d96533da928252135ac8f247c1b250 $ + * @link https://github.com/emcconville/google-map-polyline-encoding-tool + */ + +/** + * Polyline encoding & decoding class + * + * Convert list of points to encoded string following Google's Polyline + * Algorithm. + * + * @category Mapping + * @package Polyline + * @author E. McConville + * @license http://www.gnu.org/licenses/lgpl.html LGPL v3 + * @link https://github.com/emcconville/google-map-polyline-encoding-tool + */ +class Polyline +{ + /** + * Default precision level of 1e-5. + * + * Overwrite this property in extended class to adjust precision of numbers. + * !!!CAUTION!!! + * 1) Adjusting this value will not guarantee that third party + * libraries will understand the change. + * 2) Float point arithmetic IS NOT real number arithmetic. PHP's internal + * float precision may contribute to undesired rounding. + * + * @var int $precision + */ + protected static $precision = 5; + + /** + * Apply Google Polyline algorithm to list of points. + * + * @param array $points List of points to encode. Can be a list of tuples, + * or a flat on dimensional array. + * + * @return string encoded string + */ + final public static function encode( $points ) + { + $points = self::flatten($points); + $encodedString = ''; + $index = 0; + $previous = array(0,0); + foreach ( $points as $number ) { + $number = (float)($number); + $number = (int)round($number * pow(10, static::$precision)); + $diff = $number - $previous[$index % 2]; + $previous[$index % 2] = $number; + $number = $diff; + $index++; + $number = ($number < 0) ? ~($number << 1) : ($number << 1); + $chunk = ''; + while ( $number >= 0x20 ) { + $chunk .= chr((0x20 | ($number & 0x1f)) + 63); + $number >>= 5; + } + $chunk .= chr($number + 63); + $encodedString .= $chunk; + } + return $encodedString; + } + + /** + * Reverse Google Polyline algorithm on encoded string. + * + * @param string $string Encoded string to extract points from. + * + * @return array points + */ + final public static function decode( $string ) + { + $points = array(); + $index = $i = 0; + $previous = array(0,0); + while ($i < strlen($string)) { + $shift = $result = 0x00; + do { + $bit = ord(substr($string, $i++)) - 63; + $result |= ($bit & 0x1f) << $shift; + $shift += 5; + } while ($bit >= 0x20); + + $diff = ($result & 1) ? ~($result >> 1) : ($result >> 1); + $number = $previous[$index % 2] + $diff; + $previous[$index % 2] = $number; + $index++; + $points[] = $number * 1 / pow(10, static::$precision); + } + return $points; + } + + /** + * Reduce multi-dimensional to single list + * + * @param array $array Subject array to flatten. + * + * @return array flattened + */ + final public static function flatten( $array ) + { + $flatten = array(); + array_walk_recursive( + $array, // @codeCoverageIgnore + function ($current) use (&$flatten) { + $flatten[] = $current; + } + ); + return $flatten; + } + + /** + * Concat list into pairs of points + * + * @param array $list One-dimensional array to segment into list of tuples. + * + * @return array pairs + */ + final public static function pair( $list ) + { + return is_array($list) ? array_chunk($list, 2) : array(); + } +} diff --git a/lib/RouteShortcode.class.php b/lib/RouteShortcode.class.php index b8a3399..cd5667c 100644 --- a/lib/RouteShortcode.class.php +++ b/lib/RouteShortcode.class.php @@ -9,7 +9,7 @@ public static function init() { } // Shortcode handler function - // [route id=id som=metric map_width="100%" map_height="400px"] + // [route id=id som=metric map_width="100%" map_height="400px" markers=false] public static function handler( $atts ) { self::$add_script = true; @@ -19,6 +19,7 @@ public static function handler( $atts ) { 'map_width' => '480', 'map_height' => '320', 'athlete_token' => WPStrava::get_instance()->settings->get_default_token(), + 'markers' => false, ); extract( shortcode_atts( $defaults, $atts ) ); @@ -57,7 +58,7 @@ public static function handler( $atts ) { ' . - WPStrava_StaticMap::get_image_tag( $route_details, $map_height, $map_width ) . + WPStrava_StaticMap::get_image_tag( $route_details, $map_height, $map_width, $markers ) . ''; } // End if( $route_details ). } // handler diff --git a/lib/StaticMap.class.php b/lib/StaticMap.class.php index 38a9e29..b213f15 100644 --- a/lib/StaticMap.class.php +++ b/lib/StaticMap.class.php @@ -1,5 +1,7 @@ settings->gmaps_key; // Short circuit if missing key or ride object doesn't have the data we need. @@ -27,11 +30,28 @@ public static function get_image_tag( $ride, $height = 320, $width = 480 ) { if ( ! empty( $ride->map->polyline ) && ( $url_len + strlen( $ride->map->polyline ) < $max_chars ) ) { $url .= $ride->map->polyline; + $points = self::decode_polyline($ride->map->polyline); } elseif ( ! empty( $ride->map->summary_polyline ) ) { $url .= $ride->map->summary_polyline; + $points = self::decode_polyline($ride->map->summary_polyline); } + if ($markers) { + $markers = '&markers=color:green|' . $points['start'][0] . ',' . $points['start'][1] . + '&markers=color:red|' . $points['finish'][0] . ',' . $points['finish'][1]; + $url .= $markers; + } + return ""; } + private static function decode_polyline($enc) { + $points = Polyline::decode($enc); + $points = Polyline::pair($points); + $start = $points[0]; + $finish = $points[count($points)-1]; + + return array('start' => $start, 'finish' => $finish); + } + } diff --git a/readme.txt b/readme.txt index 72dfbf7..b58a9c8 100755 --- a/readme.txt +++ b/readme.txt @@ -22,6 +22,7 @@ Also takes the following optional parameters: * map_width - width (width of image in pixels). * map_height - height (height of image in pixels). * athlete_token - specify a different athlete (you can copy this value from https://www.strava.com/settings/api or the wp-strava settings page at /wp-admin/options-general.php?page=wp-strava-options). +* markers - Display markers at the start/finish point (true/false, defaults to false). [ride] is an alias for [activity] and will accept the same parameters (kept for backwards compatibility).