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 @@ class WPStrava_ActivityShortcode { } // 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 @@ class WPStrava_ActivityShortcode { '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 @@ class WPStrava_ActivityShortcode { ' . - 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..3202e84 --- /dev/null +++ b/lib/Polyline.php @@ -0,0 +1,153 @@ +. + * + * @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; + + // To remove PHP 5.3 requirement. + protected static $flatten = array(); + + /** + * 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 ); + $encoded_string = ''; + $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 ); + $encoded_string .= $chunk; + } + return $encoded_string; + } + + /** + * 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 ) { + self::$flatten = array(); + array_walk_recursive( $array, array( 'Polyline', 'flatten_callback' ) ); + return self::$flatten; + } + + final public static function flatten_callback( $value ) { + self::$flatten[] = $value; + } + + /** + * 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 new file mode 100644 index 0000000..23cfc12 --- /dev/null +++ b/lib/RouteShortcode.class.php @@ -0,0 +1,74 @@ + 0, + 'som' => WPStrava::get_instance()->settings->som, + 'map_width' => '480', + 'map_height' => '320', + 'athlete_token' => WPStrava::get_instance()->settings->get_default_token(), + 'markers' => false, + ); + + extract( shortcode_atts( $defaults, $atts ) ); + + $strava_som = WPStrava_SOM::get_som( $som ); + $route = WPStrava::get_instance()->routes; + $route_details = $route->get_route( $id ); + + //sanitize width & height + $map_width = str_replace( '%', '', $map_width ); + $map_height = str_replace( '%', '', $map_height ); + $map_width = str_replace( 'px', '', $map_width ); + $map_height = str_replace( 'px', '', $map_height ); + + if ( $route_details ) { + return ' +
+ + + + + + + + + + + + + + + + + + + + +
' . __( 'Est. Moving Time', 'wp-strava' ) . '' . __( 'Distance', 'wp-strava' ) . '' . __( 'Elevation Gain', 'wp-strava' ) . '
' . $strava_som->time( $route_details->estimated_moving_time ) . '' . $strava_som->distance( $route_details->distance ) . '' . $strava_som->elevation( $route_details->elevation_gain ) . '
' . $strava_som->get_time_label() . '' . $strava_som->get_distance_label() . '' . $strava_som->get_elevation_label() . '
' . + WPStrava_StaticMap::get_image_tag( $route_details, $map_height, $map_width, $markers ) . + '
'; + } // End if( $route_details ). + } // handler + + public static function print_scripts() { + if ( self::$add_script ) { + wp_enqueue_style( 'wp-strava-style' ); + } + } +} + +// Initialize short code +WPStrava_RouteShortcode::init(); diff --git a/lib/Routes.class.php b/lib/Routes.class.php new file mode 100644 index 0000000..573030f --- /dev/null +++ b/lib/Routes.class.php @@ -0,0 +1,19 @@ +get_api()->get( "routes/{$route_id}" ); + } +} diff --git a/lib/StaticMap.class.php b/lib/StaticMap.class.php index 38a9e29..cefe939 100644 --- a/lib/StaticMap.class.php +++ b/lib/StaticMap.class.php @@ -9,11 +9,13 @@ class WPStrava_StaticMap { * * @static * @access public - * @param object $ride Ride object from strava. - * @param int $height Height of map in pixels. - * @param int $width Width of map in pixels. + * @param object $ride Ride object from strava. + * @param int $height Height of map in pixels. + * @param int $width Width of map in pixels. + * @param bool $markers Display start and finish markers. + * @return string HTML img tag with static map image. */ - public static function get_image_tag( $ride, $height = 320, $width = 480 ) { + public static function get_image_tag( $ride, $height = 320, $width = 480, $markers = false ) { $key = WPStrava::get_instance()->settings->gmaps_key; // Short circuit if missing key or ride object doesn't have the data we need. @@ -21,17 +23,49 @@ class WPStrava_StaticMap { return ''; } - $url = "https://maps.googleapis.com/maps/api/staticmap?maptype=terrain&size={$width}x{$height}&sensor=false&key={$key}&path=color:0xFF0000BF|weight:2|enc:"; - $url_len = strlen( $url ); + $url = "https://maps.googleapis.com/maps/api/staticmap?maptype=terrain&size={$width}x{$height}&sensor=false&key={$key}&path=color:0xFF0000BF|weight:2|enc:"; + $url_len = strlen( $url ); $max_chars = 1865; + $polyline = ''; if ( ! empty( $ride->map->polyline ) && ( $url_len + strlen( $ride->map->polyline ) < $max_chars ) ) { - $url .= $ride->map->polyline; + $polyline = $ride->map->polyline; } elseif ( ! empty( $ride->map->summary_polyline ) ) { - $url .= $ride->map->summary_polyline; + $polyline = $ride->map->summary_polyline; + } + $url .= $polyline; + + if ( $markers ) { + $points = self::decode_start_finish( $polyline ); + $markers = '&markers=color:green|' . $points['start'][0] . ',' . $points['start'][1] . + '&markers=color:red|' . $points['finish'][0] . ',' . $points['finish'][1]; + $url .= $markers; } return ""; } + /** + * From an encoded polyline, get the start and finish points for + * the purposes of displaying start and finish markers. + * + * @static + * @see https://developers.google.com/maps/documentation/utilities/polylinealgorithm + * @access private + * @param string $enc Encoded polyline. + * @return array with indexes of start & finish containing lat/lon for each. + */ + private static function decode_start_finish( $enc ) { + require_once WPSTRAVA_PLUGIN_DIR . 'lib/Polyline.php'; + $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/lib/Strava.class.php b/lib/Strava.class.php index 375f751..79b1ff2 100644 --- a/lib/Strava.class.php +++ b/lib/Strava.class.php @@ -5,6 +5,7 @@ require_once WPSTRAVA_PLUGIN_DIR . 'lib/SOM.class.php'; require_once WPSTRAVA_PLUGIN_DIR . 'lib/LatestRidesWidget.class.php'; require_once WPSTRAVA_PLUGIN_DIR . 'lib/LatestMapWidget.class.php'; require_once WPSTRAVA_PLUGIN_DIR . 'lib/ActivityShortcode.class.php'; +require_once WPSTRAVA_PLUGIN_DIR . 'lib/RouteShortcode.class.php'; require_once WPSTRAVA_PLUGIN_DIR . 'lib/StaticMap.class.php'; class WPStrava { @@ -13,6 +14,7 @@ class WPStrava { private $settings = null; private $api = array(); // Holds an array of APIs. private $rides = null; + private $routes = null; private function __construct() { $this->settings = new WPStrava_Settings(); @@ -40,7 +42,9 @@ class WPStrava { // On-demand classes. if ( $name == 'rides' ) { return $this->get_rides(); - } + } elseif ( $name == 'routes' ) { + return $this->get_routes(); + } if ( isset( $this->{$name} ) ) { return $this->{$name}; @@ -71,6 +75,14 @@ class WPStrava { return $this->rides; } + public function get_routes() { + if ( ! $this->routes ) { + require_once WPSTRAVA_PLUGIN_DIR . 'lib/Routes.class.php'; + $this->routes = new WPStrava_Routes(); + } + return $this->routes; + } + public function register_scripts() { // Register a personalized stylesheet wp_register_style( 'wp-strava-style', WPSTRAVA_PLUGIN_URL . 'css/wp-strava.css' ); diff --git a/readme.txt b/readme.txt index 9b553a5..1fd6cb3 100755 --- a/readme.txt +++ b/readme.txt @@ -22,9 +22,14 @@ 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). +[route id=NUMBER] - add to any page or post. Shows a summary of the activity plus a map if a google maps key has been added. + +This also takes the same optional parameters as the activity shortcode above. + = Widgets = Strava Latest Activity List - shows a list of the last few activities. @@ -33,12 +38,16 @@ Strava Latest Map - shows map of latest activity with option to limit latest map == Changelog == += 1.3.0 = +Added [route] shortcode and start/finish https://github.com/cmanon/wp-strava/pull/10/ +Fixed error with /rides link (should be /activities). https://wordpress.org/support/topic/problem-with-link-4/ + = 1.2.0 = -Added multi-athlete configuration. +Added multi-athlete configuration. https://wordpress.org/support/topic/multi-strava-user/ Additional transitions from Ride -> Activity. Updated setup instructions to reflect latest Strava API set up process. -Backwards Compatibility - removed PHP 5.3+ specific operator (should work with PHP 5.2 now - versions 1.1 and 1.1.1 don't). -Reworked error reporting and formatting. +Backwards Compatibility - removed PHP 5.3+ specific operator (should work with PHP 5.2 now - versions 1.1 and 1.1.1 don't). https://wordpress.org/support/topic/version-1-1-broken/ +Reworked error reporting and formatting. https://wordpress.org/support/topic/updating-settings-failure/#post-9764942 = 1.1.1 = Changes to better support translations through https://translate.wordpress.org. @@ -47,15 +56,15 @@ Cleaned up formatting. = 1.1 = Added [activity] shortcode to deprecate [ride] in the future. Fixed static method call error in shortcode. -Added title to Strava Latest Map Widget. +Added title to Strava Latest Map Widget. https://wordpress.org/support/topic/change-widget-title-from-latest-ride-to-latest-run-or-something-else/ Added Lance Willett to contributors. Added target="_blank" to widget hrefs. -Added Google Maps Key to settings (required for map images). +Added Google Maps Key to settings (required for map images). https://wordpress.org/support/topic/the-google-maps-api-server-rejected-your-request-3/ Added cache clear option to remove transient & image data. Cleaned up formatting. = 1.0 = -Change to Strava API V3. +Change to Strava API V3. https://wordpress.org/support/topic/does-not-work-354/ Switch ride shortcode to use static map. = 0.70 = @@ -68,7 +77,7 @@ Fixed several bugs. Added feature to show athlete name/link to the widget if the search option is by club. = 0.61 = -Added option to select unit of measurements on the widget. +Added option to select unit of measurements on the widget. https://wordpress.org/support/topic/feature-request-runs-in-minkm/ = 0.6 = Initial version.