block by burritojustice 15971aa1d3be6f23877733f74d866264

Travel by Map - work in progress

Full Screen

index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Travel by Map Example (WIP)</title>
    <link rel="stylesheet" href="https://mapzen.com/js/mapzen.css"/>
    <link rel="stylesheet" href="https://unpkg.com/leaflet-easybutton@2.0.0/src/easy-button.css">
    <link rel="stylesheet" href="https://unpkg.com/lrm-mapzen/dist/lrm-mapzen.css" />
    <style>
        .icon {
          font-size: 20px;
        }
        .icon-mode {
          font-size: 40px;
          position: absolute;
          z-index: 1000;
          top: 48%;
          left: 49%;
          visibility: hidden;
        }
        .leaflet-routing-container {
          display: none;
        }
    </style>
    <script src="https://mapzen.com/js/mapzen.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet-routing-machine/3.2.4/leaflet-routing-machine.min.js"></script>
    <script src="https://unpkg.com/leaflet-easybutton@2.0.0/src/easy-button.js"></script>
    <script src="https://unpkg.com/lrm-mapzen/dist/lrm-mapzen.js"></script>
    <script src="https://npmcdn.com/@turf/turf@3.5.1/turf.js"></script>
</head>
<body>
    <div id="map"></div>
    <div class="icon-mode" id="pedestrian">🏃</div>
    <div class="icon-mode" id="bird">✈️️</div>
    <div class="icon-mode" id="auto">🚗</div>
    <div class="icon-mode" id="bicycle">🚲</div>
    <div class="icon-mode" id="multimodal">🚌</div>

    <script>
        // TODO:
        // - Hide line (optionally)
        // - Add hash
        // - Base zoom level on total distance (and mode)
        // - Re-run route based on new costing level for each mode
        L.Mapzen.apiKey = 'mapzen-UJDfMvY';

        var map = L.Mapzen.map('map', {
            tangramOptions: {
                scene: L.Mapzen.BasemapStyles.RefillMoreLabels,
                debug: true
            }
        });

        var la = L.latLng(33.8128,-117.9259),
            chicago =  L.latLng(41.8758,-87.6189),
            vistapoint = L.latLng(37.839682,-122.485284),
            fairyland = L.latLng(37.809270,-122.259810);

        var currentRoute = [],
            currentTimeouts = [],
            currentRouteBounds,
            firstAndLast,
            currentIcon,
            zoom, speed,
            tangramLayer;

        var modeConfig = {
          auto: {
            zoom: 18,
            speed: 120
          },
          multimodal: {
            zoom: 17,
            speed: 60
          },
          bicycle: {
            zoom: 18,
            speed: 40
          },
          pedestrian: {
            zoom: 20,
            speed: 10
          },
          bird: {
            zoom: 12,
            speed: 1200
          }
        };

        var demo = {
          costing: 'auto' // auto, bicycle, pedestrian, multimodal
        };
        var control = L.Routing.control({
          routeLine: function (route, options) {
            // Update currentRoute
            currentRoute = route;
            console.log('This is where you can get info about the current route')

            // Update first and last route points
            var lastIndex = currentRoute.coordinates.length - 1;
            firstAndLast = [currentRoute.coordinates[0], currentRoute.coordinates[lastIndex]];

            return L.Routing.mapzenLine(route, options);
          },
          lineOptions: {
            styles: [{ color: '#f66', opacity: 0.7, weight: 5 }]
          },
          waypoints: [
            vistapoint,
            fairyland
          ],
          geocoder: L.Mapzen.geocoder('mapzen-UJDfMvY'),
          router: L.Routing.mapzen('mapzen-UJDfMvY', demo),
          formatter: new L.Routing.mapzenFormatter(),
          summaryTemplate:'<div class="start">{name}</div><div class="info {costing}">{distance}, {time}</div>'
        }).addTo(map);

        // Not showing control right now
        control.hide();

        L.Routing.errorControl(control).addTo(map);

        // Update route bounds anytime new route is selected
        control.on('routeselected', function(r) {
            var line = L.Routing.line(r.route)
            currentRouteBounds = line.getBounds();
        });

        map.on('tangramloaded', function (e) {
          tangramLayer = e.tangramLayer;
          addButtons();
        });

        function addButtons() {
          L.easyButton('<span class="icon pedestrian" title="Take a stroll">🏃‍♀️</span>', function(){
            
            console.log("******** TO DO  **********")
            // TODO:  rerun route with different costing mode

            beginAdventure('pedestrian');
          }).addTo(map);

          L.easyButton('<span class="icon bicycle" title="Let\'s ride bikes">🚲</span>', function(){
            beginAdventure('bicycle');
          }).addTo(map);
            
          L.easyButton('<span class="icon multimodal" title="Hop on the bus, Gus">🚌</span>', function(){
            beginAdventure('multimodal');
          }).addTo(map);
          L.easyButton('<span class="icon auto" title="We be driving">🚗</span>', function(){
            beginAdventure('auto');
          }).addTo(map);

          L.easyButton('<span class="icon bird" title="Fly!">✈</span>', function(){
            beginAdventure('bird');
          }).addTo(map);

          L.easyButton( '<span class="icon stop" title="Stop the madness!">🙀</span>', function(){
            endAdventure();
          }).addTo(map);
        }

        function beginAdventure(mode) {
          if (currentTimeouts.length > 0) {
            endAdventure();
          }

          zoom = modeConfig[mode].zoom;
          speed = modeConfig[mode].speed;
          currentIcon = mode;
          var route = currentRoute;

          if (mode == 'bird') {
            // Use current zoom if that's closer
            // TODO: link zoom level to total distance of route
            var currentZoom = map.getZoom();
            if (currentZoom > zoom) zoom = currentZoom;

            route = {coordinates: firstAndLast};
          }

          travelByMap(route);
        }

        function endAdventure() {
          // Clear pending timeouts
          for (var x = 0; x < currentTimeouts.length; x++) {
            clearTimeout(currentTimeouts[x]);
          }
          currentTimeouts = [];
          
          // Hide emoji
          hideEmojis();
          
          // Back to original view
          if (currentRouteBounds) map.fitBounds(currentRouteBounds);
        }

        function travelByMap(route) {
          var routeCoords = route.coordinates;

          // Zoom in on first coordinate
          map.setView(routeCoords[0], zoom);

          // Display appropriate emoji
          document.getElementById(currentIcon).style.visibility = "visible";

          // Pause 2 seconds before initiating to let people get their bearings
          var routeCoords = route.coordinates;
          setTimeout(initiateTravel, 2000, routeCoords);
        }

        function initiateTravel(routeCoords) {
          var loopTime = 0;
          for (var x = 0; x < routeCoords.length; x++) {

            var thisCoord = routeCoords[x],
                nextCoord = routeCoords[x + 1] ? routeCoords[x + 1] : null,
                units = 'miles';

            var distance = nextCoord ? turf.distance(getGeoJSONPoint(thisCoord), getGeoJSONPoint(nextCoord), units) : 0.001;
            var durationTime = distance * 3600 / speed; // in seconds  // distance (mi) * (seconds to hours conv) / speed (mph)

            // Rounding to avoid extra micro times in loopTime
            //durationTime = Math.round(durationTime * 100) / 100;

            if (nextCoord && distance > 0) {
              var timeout = setTimeout(function(y) { 
                map.panTo(y.coord, {animate: true, duration: y.time, easeLinearity: 1, noMoveStart: true});
              }, loopTime, {coord: nextCoord, time: durationTime});

              currentTimeouts.push(timeout);
            }

            // Set loopTime for next point
            loopTime += (durationTime * 1000); // in miliseconds
          }
        }

        // Intercept keypress to change basemap
        document.onkeydown = function(evt) {
            if (!tangramLayer) return;
            evt = evt || window.event;
            var basemap;

            switch(evt.key) {
              case 'r':
                basemap = L.Mapzen.BasemapStyles.RefillMoreLabels;
                break;
              case 't':
                basemap = L.Mapzen.BasemapStyles.TronMoreLabels;
                break;
              case 'w':
                basemap = L.Mapzen.BasemapStyles.WalkaboutMoreLabels;
                break;
              case 'b':
                basemap = L.Mapzen.BasemapStyles.BubbleWrap;
                break;
              default:
                // do nothing
                return;
            }

            // Update basemap
            tangramLayer.scene.load(basemap);
        };



        /*  HELPERS */

        function hideEmojis() {
          var elements = document.getElementsByClassName('icon-mode');
          for (var x = 0; x < elements.length; x ++) {
            elements[x].style.visibility = "hidden";
          }
        }

        function getGeoJSONPoint(coord) {
          return {
            'type': 'Feature',
            'properties': {},
            'geometry': {
              'type': 'Point',
              'coordinates': [coord.lat, coord.lng]
            }
          };
        }

    </script>
</body>
</html>