Index: ow/models/workout.py
===================================================================
--- ow/models/workout.py	(revision ceae1586c5f3212b82536b7b50d6d3728ab9d181)
+++ ow/models/workout.py	(revision d1c4782c094cba6d591977ed76cd7ee9c9d8ae8a)
@@ -1,3 +1,3 @@
-
+import os
 from datetime import datetime, timedelta, timezone
 from decimal import Decimal
@@ -12,5 +12,6 @@
     copy_blob,
     create_blob,
-    mps_to_kmph
+    mps_to_kmph,
+    save_map_screenshot
 )
 
@@ -419,2 +420,26 @@
     def has_fit(self):
         return self.fit_file is not None
+
+    @property
+    def map_screenshot(self):
+        """
+        Return the static path to the screenshot image of the map for
+        this workout (works only for workouts with gps tracking)
+        """
+        if not self.has_gpx:
+            return None
+
+        current_path = os.path.abspath(os.path.dirname(__file__))
+        screenshot_path = os.path.join(
+            current_path, '../static/maps',
+            str(self.owner.uid), str(self.workout_id)) + '.png'
+
+        if not os.path.exists(screenshot_path):
+            # screenshot does not exist, generate it
+            save_map_screenshot(self)
+
+        # the value returned is relative to the static files served
+        # by the app, so we can use request.static_url() with it
+        static_path = os.path.join('static/maps', str(self.owner.uid),
+                                   str(self.workout_id))
+        return 'ow:' + static_path + '.png'
Index: ow/static/css/main.css
===================================================================
--- ow/static/css/main.css	(revision ceae1586c5f3212b82536b7b50d6d3728ab9d181)
+++ ow/static/css/main.css	(revision d1c4782c094cba6d591977ed76cd7ee9c9d8ae8a)
@@ -752,4 +752,8 @@
 }
 
+.workout-map img {
+    width: 100%;
+}
+
 .owo-del a:hover {
     color:red
Index: ow/static/js/ow.js
===================================================================
--- ow/static/js/ow.js	(revision ceae1586c5f3212b82536b7b50d6d3728ab9d181)
+++ ow/static/js/ow.js	(revision d1c4782c094cba6d591977ed76cd7ee9c9d8ae8a)
@@ -25,8 +25,9 @@
     var shadow = spec.shadow;
     var elevation = spec.elevation;
+    var zoom_control = spec.zoom_control;
 
     // OpenStreetMap urls and references
-    var openstreetmap_url = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
-    var openstreetmap_attr = 'Map data &copy; <a href="http://www.osm.org">OpenStreetMap</a>'
+    var openstreetmap_url = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
+    var openstreetmap_attr = 'Map data &copy; <a href="http://www.osm.org">OpenStreetMap</a>';
 
     // Some constants reused through the code
@@ -38,5 +39,5 @@
     var create_map = function create_map(latitude, longitude, zoom) {
         /* Create a Leaflet map, set center point and add tiles */
-        map = L.map(map_id, {preferCanvas: true});
+        map = L.map(map_id, {zoomControl: zoom_control});
         map.setView([latitude, longitude], zoom);
         var tile_layer = L.tileLayer(openstreetmap_url, {
Index: ow/templates/dashboard.pt
===================================================================
--- ow/templates/dashboard.pt	(revision ceae1586c5f3212b82536b7b50d6d3728ab9d181)
+++ ow/templates/dashboard.pt	(revision d1c4782c094cba6d591977ed76cd7ee9c9d8ae8a)
@@ -90,4 +90,11 @@
             <div class="workout-intro" tal:content="workout.notes"></div>
 
+            <div class="workout-map" tal:condition="workout.has_gpx">
+                <a href="" tal:attributes="href request.resource_url(workout)">
+                    <img src="" tal:attributes="src request.static_url(workout.map_screenshot);
+                              alt workout.title; title workout.title">
+                </a>
+            </div>
+
             <ul class="workout-options">
               <li class="owo-edit"><a href="" i18n:translate="" tal:attributes="href request.resource_url(workout, 'edit')"><span>edit</span></a></li>
Index: ow/templates/workout-map.pt
===================================================================
--- ow/templates/workout-map.pt	(revision d1c4782c094cba6d591977ed76cd7ee9c9d8ae8a)
+++ ow/templates/workout-map.pt	(revision d1c4782c094cba6d591977ed76cd7ee9c9d8ae8a)
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xml:lang="en"
+      xmlns:tal="http://xml.zope.org/namespaces/tal"
+      xmlns:metal="http://xml.zope.org/namespaces/metal"
+      xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+      i18n:domain="OpenWorkouts"
+      tal:attributes="lang request.locale_name">
+
+  <head>
+
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <meta name="description"
+          content="OpenWorkouts - tracking your workouts openly">
+    <meta name="author" content="The OpenWorkouts Project">
+    <title>OpenWorkouts - tracking your workouts openly</title>
+
+    <link rel="stylesheet"
+          href="${request.static_url('ow:static/css/main.css')}" />
+    <link rel="stylesheet"
+          href="${request.static_url('ow:static/css/openworkouts.css')}" />
+    <link rel="stylesheet" tal:condition="context.has_gpx"
+          href="${request.static_url('ow:static/components/Leaflet/leaflet.css')}" />
+
+    <style>
+     html, body, #full-map {
+	 height:100%;
+	 width:100%;
+	 padding:0px;
+	 margin:0px;
+     }
+    </style>
+
+  </head>
+
+  <body class="full-map">
+    <!--! Track map -->
+    <div id="full-map" tal:condition="context.has_gpx"></div>
+
+    <script src="${request.static_url('ow:static/components/Leaflet/leaflet.js')}"
+            tal:condition="context.has_gpx"></script>
+
+    <script src="${request.static_url('ow:static/components/leaflet-gpx/gpx.js')}"
+            tal:condition="context.has_gpx"></script>
+
+    <script src="${request.static_url('ow:static/js/ow.js')}"
+            tal:condition="context.has_gpx"></script>
+
+    <script type="text/javascript" tal:condition="context.has_gpx">
+     var workout_map = owjs.map({
+         map_id: 'full-map',
+         latitude: ${start_point['latitude']},
+         longitude: ${start_point['longitude']},
+         zoom: 11,
+         gpx_url: '${request.resource_url(context, 'gpx')}',
+         elevation: false,
+         zoom_control: false
+     });
+     workout_map.render();
+
+    </script>
+  </body>
+
+</html>
Index: ow/templates/workout.pt
===================================================================
--- ow/templates/workout.pt	(revision ceae1586c5f3212b82536b7b50d6d3728ab9d181)
+++ ow/templates/workout.pt	(revision d1c4782c094cba6d591977ed76cd7ee9c9d8ae8a)
@@ -160,5 +160,6 @@
          end_icon: '${request.static_url('ow:static/components/leaflet-gpx/pin-icon-end.png')}',
          shadow: '${request.static_url('ow:static/components/leaflet-gpx/pin-shadow.png')}',
-         elevation: true
+         elevation: true,
+         zoom_control: true
      });
      workout_map.render();
Index: ow/utilities.py
===================================================================
--- ow/utilities.py	(revision ceae1586c5f3212b82536b7b50d6d3728ab9d181)
+++ ow/utilities.py	(revision d1c4782c094cba6d591977ed76cd7ee9c9d8ae8a)
@@ -1,3 +1,6 @@
 import re
+import os
+import logging
+import subprocess
 from datetime import datetime
 from decimal import Decimal
@@ -7,4 +10,6 @@
 from xml.dom import minidom
 from ZODB.blob import Blob
+
+log = logging.getLogger(__name__)
 
 
@@ -184,2 +189,25 @@
         open_blob.write(data)
     return blob
+
+
+def save_map_screenshot(workout):
+    if workout.has_gpx:
+        current_path = os.path.abspath(os.path.dirname(__file__))
+        tool_path = os.path.join(current_path, '../bin/screenshot_map')
+
+        screenshots_path = os.path.join(
+            current_path, 'static/maps', str(workout.owner.uid))
+        if not os.path.exists(screenshots_path):
+            os.makedirs(screenshots_path)
+
+        screenshot_path = os.path.join(
+            screenshots_path, str(workout.workout_id))
+        screenshot_path += '.png'
+
+        subprocess.run(
+            [tool_path, str(workout.owner.uid), str(workout.workout_id),
+             screenshot_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+
+        return True
+
+    return False
Index: ow/views/workout.py
===================================================================
--- ow/views/workout.py	(revision ceae1586c5f3212b82536b7b50d6d3728ab9d181)
+++ ow/views/workout.py	(revision d1c4782c094cba6d591977ed76cd7ee9c9d8ae8a)
@@ -214,2 +214,25 @@
         content_disposition='attachment; filename="%s"' % gpx_slug,
         body_file=context.tracking_file.open())
+
+
+@view_config(
+    context=Workout,
+    name='map',
+    renderer='ow:templates/workout-map.pt')
+def workout_map(context, request):
+    """
+    Render a page that has only a map with tracking info
+    """
+    start_point = {}
+    if context.has_gpx:
+        with context.tracking_file.open() as gpx_file:
+            gpx_contents = gpx_file.read()
+            gpx_contents = gpx_contents.decode('utf-8')
+            gpx = gpxpy.parse(gpx_contents)
+            if gpx.tracks:
+                track = gpx.tracks[0]
+                center_point = track.get_center()
+                start_point = {'latitude': center_point.latitude,
+                               'longitude': center_point.longitude,
+                               'elevation': center_point.elevation}
+    return {'start_point': start_point}
