Filter Points of Interest by travel time

In this example we will show you how you can use the TimeService to filter a list of your entities by travel time, starting from a source location. In our case we will use all museums from OpenStreetMaps which have a non-empty name and website.

With this feature users can instantly see which points of interest are closest to them, with respect to the actual travel time and not just beeline. Note that this would not be possible with traditional routing services like Google Maps, since you would have to make one request per place of interest.

Filter Points of Interest by travel time

See which points of interest are closest by bike, with respect to the actual travel time.

GET YOUR FREE API KEY to use this example
reload
open in new tab
copy HTML
hide code
<!DOCTYPE html>
<html>
<head>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
  <!--  Include leaflet javascript and css -->
  <link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" crossorigin="">
  <script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet-src.js" crossorigin=""></script>
  <!--  Include dragdealer slider bar    -->
  <script src="https://cdn.rawgit.com/skidding/dragdealer/master/src/dragdealer.js"></script>
  <!--   Include font awesome   -->
  <script src="https://use.fontawesome.com/d0a009c133.js"></script>
  <!--  Include r360.js -->
  <script src="https://releases.route360.net/r360-js/latest.js"></script>

  <style>
    html, body { width: 100%; height: 100%; margin: 0; font-family: sans-serif; }
    #map { width: 100%; height: 100%; }
    .leaflet-popup-content-wrapper {
      padding: 1px;
      text-align: left;
      border-radius: 0px !important;
      box-shadow: none !important;
      padding: 3% !important;
    }
    .leaflet-popup-content-wrapper a { color: black !important; }
    .controls-panel {
      position: absolute;
      top: 0px;
      left: 0px;
      right: 0px;
      background: rgba(255, 255, 255, 0.8);
      padding: 10px;
      display: flex;
      flex-direction: row;
      align-items: center;
      justify-content: center;
      z-index: 1000;
    }
    .time-value {
      font-size: 16px;
      font-weight: 500;
      margin-right: 10px;
      width: 100px;
      color: rgba(0, 0, 0, .5);
      text-align: right;
    }
    #slider { position: relative; height: 20px; width: 50%; }
    #slider .handle {
      position: absolute;
      top: 0;
      left: 0;
      cursor: pointer;
      width: 20px;
      height: 20px;
      background: #70BC7A;
      border-radius: 10px;
      z-index: 2;
    }
    #slider:after {
      content: "";
      position: absolute;
      margin-top: 8px;
      height: 4px;
      background: rgba(0, 0, 0, .5);
      border-radius: 2px;
      left: 0;
      right: 0;
      z-index: 1;
    }
  </style>
</head>
<body>
  <!--  where the map will live  -->
  <div id="map"></div>
  <div class="controls-panel">
    <div class="time-value">
      <i class="fa fa-clock-o" aria-hidden="true"></i>
      <span id="actual-time">30</span>min
    </div>
    <div id="slider">
      <div class="handle"></div>
    </div>
  </div>
  
  <script>
    // osm museums with valid url field
    var museumsBB = [{"id":15968753,"name":"Altes Museum","url":"http://www.smb.museum/smb/standorte/index.php?lang=de&p=2&objID=24&n=2&r=1","lon":13.3988098410779,"lat":52.5194482697477},{"id":18578139,"name":"Neues Museum","url":"http://www.neues-museum.de","lon":13.3977318647919,"lat":52.5201792598004},{"id":22990151,"name":"Musikinstrumenten-Museum","url":"http://www.sim.spk-berlin.de/mim_3.html","lon":13.370790858961,"lat":52.510457544161},{"id":22990228,"name":"Kunstgewerbemuseum","url":"http://www.smb.museum/smb/standorte/index.php?lang=de&p=2&objID=37&n=8","lon":13.3673485161884,"lat":52.5097509246173},{"id":22990276,"name":"Kupferstichkabinett","url":"http://www.smb.museum/smb/standorte/index.php?p=2&objID=39","lon":13.3666604757285,"lat":52.5084017983238},{"id":23813200,"name":"Nikolaikirche","url":"http://www.stadtmuseum.de/nikolaikirche","lon":13.4074901704182,"lat":52.5168055278862},{"id":26144213,"name":"Gründerzeitmuseum im Gutshaus Mahlsdorf","url":"http://www.gruenderzeitmuseum.de","lon":13.6139832649212,"lat":52.5024636041934},{"id":26247891,"name":"Museum für Fotografie","url":"http://www.smb.museum/smb/standorte/index.php?lang=de&p=2&objID=6124&n=12","lon":13.3320859102401,"lat":52.508140292441},{"id":27120308,"name":"Museum Neukölln","url":"www.museum-neukoelln.de","lon":13.43776755,"lat":52.44596185},{"id":27182821,"name":"Botanisches Museum Berlin-Dahlem","url":"http://www.bgbm.org/bgbm/museum/","lon":13.3051390048542,"lat":52.4583581560775},{"id":28147152,"name":"Museumsdorf Düppel","url":"http://www.dueppel.de/","lon":13.2334496216724,"lat":52.4251563984244},{"id":29490400,"name":"Museum für Asiatische Kunst","url":"http://www.smb.museum/smb/sammlungen/details.php?objID=12904","lon":13.2922617644695,"lat":52.4559466132299},{"id":31283095,"name":"Haus am Waldsee","url":"http://www.hausamwaldsee.de","lon":13.2384135246861,"lat":52.4420693652737},{"id":31415713,"name":"Schöneberg Museum","url":"http://www.jugendmuseum.de/m_schoeneberg/dashaus_fr.html","lon":13.3509769927722,"lat":52.4839053697533},{"id":32541759,"name":"MACHmit! Museum für Kinder","url":"http://www.machmitmuseum.de","lon":13.4227734046356,"lat":52.5405355583121},{"id":33910270,"name":"Schmetterlingshorst","url":"http://www.schmetterlingshorst.de/","lon":13.6096067600917,"lat":52.4125698609369},{"id":43173495,"name":"Tränenpalast","url":"http://www.traenenpalast.de","lon":13.3870976693499,"lat":52.5208751114516},{"id":44038022,"name":"Villa Schöningen","url":"http://www.villa-schoeningen.de/","lon":13.0876676798627,"lat":52.4136114585373},{"id":47423543,"name":"Science Center Medizintechnik","url":"http://www.sciencecenter-medizintechnik.de","lon":13.3773362288055,"lat":52.5112752151801},{"id":48472661,"name":"Käthe-Kollwitz-Museum Berlin","url":"http://www.kaethe-kollwitz.de/","lon":13.3269243807532,"lat":52.5016866584425},{"id":49055962,"name":"Jagdschloss Grunewald","url":"http://www.spsg.de/index.php?id=140","lon":13.2616549407188,"lat":52.4670861383712},{"id":49055979,"name":"Brücke Museum","url":"http://www.bruecke-museum.de/","lon":13.2746692520118,"lat":52.4668808606349},{"id":49900345,"name":"Blindenmuseum","url":"http://www.blindenmuseum-berlin.de","lon":13.3150827580171,"lat":52.455801544996},{"id":79611278,"name":"Museum Charlottenburg-Wilmersdorf in der Villa Oppenheim","url":"http://www.villa-oppenheim-berlin.de/","lon":13.2980018314897,"lat":52.5161363397466},{"id":83811523,"name":"Alliierten-Museum","url":"http://www.alliiertenmuseum.de/","lon":13.2723870025997,"lat":52.4555647352739},{"id":87601076,"name":"Sammlung Scharf-Gerstenberg","url":"http://www.berlin.de/tickets/suche/ort.php?ort=7084;http://www.smb.spk-berlin.de/smb/sammlungen/details.php?lang=de&objID=12807&n=13&r=5","lon":13.297225990395,"lat":52.5191632723136},{"id":87646457,"name":"Museum Berggruen","url":"http://www.smb.museum/smb/sammlungen/details.php?lang=de&objectId=22&n=1&r=12","lon":13.295039405256,"lat":52.5190991466351},{"id":93657835,"name":"Heimatmuseum","url":"http://heimatverein.teltow.de/heimatmuseum-teltow-aeltestes-haus.html","lon":13.2613798970354,"lat":52.4022393513897},{"id":93942770,"name":"Bröhan-Museum","url":"http://www.broehan-museum.de/","lon":13.2953430870309,"lat":52.5186305501298},{"id":104991250,"name":"Bodemuseum","url":"http://www.visitberlin.de/de/ort/bode-museum","lon":13.394323905151,"lat":52.5219076199488},{"id":120447353,"name":"Märkisches Museum","url":"http://www.stadtmuseum.de/index3.php?museum=mm","lon":13.4147756479837,"lat":52.5135257325173},{"id":130433320,"name":"Luftwaffenmuseum der Bundeswehr","url":"http://www.Luftwaffenmuseum.com","lon":13.1404272643009,"lat":52.4727577983473},{"id":131487807,"name":"Max-Liebermann-Haus","url":"http://www.stiftung.brandenburgertor.de/stiftung/liebermann_haus","lon":13.3778290059608,"lat":52.5167035692929},{"id":174285355,"name":"Jüdisches Museum","url":"http://www.jmberlin.de","lon":13.3961442780818,"lat":52.5018237192202},{"id":175149971,"name":"DDR-Museum","url":"http://www.ddr-museum.de","lon":13.40245015,"lat":52.51916135},{"id":182553889,"name":"Schwerbelastungskörper","url":"http://www.schwerbelastungskoerper.de","lon":13.3715497617704,"lat":52.484025190794},{"id":184592746,"name":"Museum für Architekturzeichnung","url":"http://www.tchoban-foundation.de/","lon":13.4100299295074,"lat":52.5320707889862},{"id":199532111,"name":"Industriesalon Schöneweide","url":"www.industriesalon.de","lon":13.5180057,"lat":52.46032445},{"id":277961472,"name":"Jagdschloss Grunewald","url":"http://www.spsg.de/index.php?id=140","lon":13.2619698137898,"lat":52.4674283415068},{"id":285972948,"name":"Ephraim-Palais Stadtmuseum Berlin","url":"http://www.stadtmuseum.de/ephraim-palais","lon":13.4071270316785,"lat":52.5158562236628},{"id":252550890,"name":"The Story of Berlin","url":"http://www.story-of-berlin.de","lon":13.3235264,"lat":52.5011348},{"id":260763484,"name":"Deutsches Historisches Museum","url":"http://www.dhm.de","lon":13.3969544,"lat":52.5181006},{"id":260768839,"name":"Deutsche Kinemathek - Museum für Film und Fernsehen","url":"http://www.deutsche-kinemathek.de/","lon":13.37351,"lat":52.5096032},{"id":269691877,"name":"Bauhaus Archiv","url":"http://www.bauhaus.de","lon":13.353876,"lat":52.5061895},{"id":281391655,"name":"Stasimuseum","url":"http://www.stasimuseum.de/","lon":13.4875353,"lat":52.5145119},{"id":292738889,"name":"Museum für Kommunikation","url":"http://www.mfk-berlin.de/","lon":13.3872552,"lat":52.5099308},{"id":340388625,"name":"Dalí-Museum","url":"http://www.daliberlin.de/","lon":13.378434,"lat":52.5087884},{"id":355500002,"name":"Erinnerungsstätte Notaufnahmelager Marienfelde","url":"www.notaufnahmelager-berlin.de","lon":13.3684502,"lat":52.4204028},{"id":410940703,"name":"S-Bahn-Museum","url":"http://www.s-bahn-museum.de","lon":13.12997,"lat":52.3949992},{"id":411855495,"name":"Deutsches Currywurst Museum Berlin","url":"http://currywurstmuseum.com","lon":13.3914026,"lat":52.508782},{"id":448810258,"name":"Puppentheater-Museum Berlin","url":"http://www.puppentheater-museum.de","lon":13.4389855,"lat":52.4772885},{"id":507083358,"name":"Heimatmuseum Zehlendorf","url":"http://www.heimatmuseum-zehlendorf.de","lon":13.260326,"lat":52.4354436},{"id":530775817,"name":"Deutsch-Russisches Museum Berlin-Karlshorst","url":"http://www.museum-karlshorst.de","lon":13.5396824,"lat":52.4862994},{"id":538692583,"name":"Museum für Naturkunde","url":"http://www.naturkundemuseum-berlin.de","lon":13.3791152,"lat":52.5304903},{"id":540687675,"name":"Hanf Museum Berlin","url":"http://www.hanfmuseum.de","lon":13.408184,"lat":52.5166269},{"id":545060231,"name":"Hugenottenmuseum Berlin","url":"http://www.berlin.de/orte/museum/hugenottenmuseum/","lon":13.3924292,"lat":52.514289},{"id":545074057,"name":"Historische Ausstellung des Dt. Bundestages","url":"http://www.bundestag.de/kulturundgeschichte/ausstellungen/wege/index.html","lon":13.3927272,"lat":52.5126755},{"id":545738013,"name":"Saarländische Galerie","url":"http://www.saarlaendische-galerie.eu","lon":13.395823,"lat":52.518549},{"id":632148988,"name":"Kreuzberg Museum","url":"http://www.kreuzbergmuseum.de","lon":13.4182586,"lat":52.5006123},{"id":634804367,"name":"Energie-Museum","url":"http://www.energie-museum.de/","lon":13.3301968,"lat":52.4433752},{"id":661126964,"name":"Keramik-Museum Berlin","url":"www.keramik-museum-berlin.de","lon":13.3039934,"lat":52.5171467},{"id":663295703,"name":"Labyrinth Kindermuseum","url":"http://www.labyrinth-kindermuseum.de/","lon":13.3859968,"lat":52.556125},{"id":703607134,"name":"Heimatmuseum (Mitte Museum am Gesundbrunnen)","url":"http://www.mittemuseum.de/","lon":13.3800105,"lat":52.5510701},{"id":766728477,"name":"Berliner Unterwelten","url":"http://www.berliner-unterwelten.de/fichtebunker.330.0.html","lon":13.4125151,"lat":52.4903183},{"id":766875174,"name":"Anna-Seghers-Gedenkstätte","url":"www.anna-seghers.de/gedenkstaette.php","lon":13.5407192,"lat":52.4381707},{"id":1257857022,"name":"Stadthaus","url":"http://www.museum-lichtenberg.de","lon":13.4806887,"lat":52.5029062},{"id":1260572076,"name":"Dauerausstellung \"Die Pankower Machthaber\"","url":"http://www.pankower-machthaber.de/","lon":13.4062041,"lat":52.5763714},{"id":1260572083,"name":"Dauerausstellung \"Die Pankower Machthaber\"","url":"http://www.pankower-machthaber.de/","lon":13.4063999,"lat":52.5764024},{"id":1270188822,"name":"Computerspielemuseum Berlin","url":"http://www.computerspielemuseum.de","lon":13.4419395,"lat":52.5175757},{"id":1358325721,"name":"Berliner U-Bahn Museum","url":"www.ag-berliner-u-bahn.de","lon":13.2499733,"lat":52.5173541},{"id":1387498130,"name":"Aedes Gallery","url":"http://www.aedes-arc.de/","lon":13.4108887,"lat":52.5321702},{"id":1390712121,"name":"Das Verborgene Museum","url":"http://www.dasverborgenemuseum.de/","lon":13.3184593,"lat":52.5080398},{"id":1433855703,"name":"1. Berliner DDR Motorrad-Museum","url":"http://www.erstesberliner-ddr-motorradmuseum.de","lon":13.4077402,"lat":52.5229688},{"id":1476011662,"name":"Zille-Museum","url":"http://zillemuseum-berlin.de/","lon":13.4061155,"lat":52.516433},{"id":1831591396,"name":"Martin-Gropius-Bau","url":"http://gropiusbau.de","lon":13.3819927,"lat":52.5067032},{"id":1860480265,"name":"Hamburger Bahnhof - Museum für Gegenwart","url":"http://www.hamburgerbahnhof.de","lon":13.3720175,"lat":52.5284267},{"id":1867566597,"name":"Forum Willy Brandt","url":"http://www.willy-brandt.de/forum-berlin.html","lon":13.3832985,"lat":52.5169366},{"id":1874529831,"name":"Kommunale Galerie Berlin","url":"http://www.kommunalegalerie-berlin.de/","lon":13.3120475,"lat":52.4894924},{"id":1995082505,"name":"Ethnologisches Museum","url":"http://www.smb.museum/em","lon":13.2922302,"lat":52.4567372},{"id":2272746071,"name":"Effizienzhaus Plus","url":"http://www.bmvbs.de","lon":13.3293455,"lat":52.5091155},{"id":2321948745,"name":"Uhrenmuseum","url":"http://juwelier-lorenz.de/uhrenmuseum.html","lon":13.3340061,"lat":52.4699552},{"id":2323962542,"name":"Ausstellung zur Berliner Märzrevolution","url":"http://www.friedhof-der-maerzgefallenen.de/","lon":13.4365732,"lat":52.5241696},{"id":2401869345,"name":"Mauermuseum – Haus am Checkpoint Charlie","url":"http://www.mauermuseum.de/","lon":13.3906746,"lat":52.5075162},{"id":2417322422,"name":"asisi-Panometer Berlin","url":"http://www.asisi.de/index.php?id=7#asisi_index_id_71","lon":13.3893331,"lat":52.507892},{"id":2429461877,"name":"Buchstabenmuseum","url":"www.Buchstabenmuseum.de","lon":13.4195991,"lat":52.5154563},{"id":2437533571,"name":"Aquadom","url":"www.visitsealife.com/berlin/","lon":13.40269,"lat":52.5196002},{"id":2807497298,"name":"Lippenstiftmuseum","url":"http://www.lippenstiftmuseum.de","lon":13.3340545,"lat":52.4883021},{"id":2807527330,"name":"Werkbundarchiv – Museum der Dinge","url":"http://www.museumderdinge.de","lon":13.4207946,"lat":52.5010399},{"id":2807538150,"name":"Museumswohnung","url":"http://www.stadtundland.de/33_museumswohnung.htm","lon":13.5929329,"lat":52.5343019},{"id":2807587974,"name":"Museum der Unerhörten Dinge","url":"http://www.museumderunerhoertendinge.de","lon":13.3586756,"lat":52.4869923},{"id":2818117012,"name":"Schwules Museum","url":"http://www.schwulesmuseum.de","lon":13.3582193,"lat":52.5042512},{"id":2889093537,"name":"Mampemuseum","url":"http://www.mampemuseum.de/index.html","lon":13.447389,"lat":52.4749946}];
    // container for museum markers
    var targets = [];
    var traveltime = 20;
    // define a pair of coordinates, where the map should be centered
    var latlon = [52.51, 13.37];

    // add the map and set the initial center to berlin
    var map = L.map('map', { zoomControl: false }).setView(latlon, 12);
    map.attributionControl.addAttribution("ÖPNV Daten © <a href='https://www.vbb.de/de/index.html' target='_blank'>VBB</a>");

    // create empty layer to put results into
    var targetLayer = L.featureGroup().addTo(map);

    // put zoom controls at the bottom
    L.control.zoom({ position: 'bottomleft' }).addTo(map);
    
    // initialise the base map
    r360.basemap({ style: 'basic', apikey: '__APIPLACEHOLDER__' }).addTo(map);

    // create the filter start marker
    var sourceMarker = L.marker((latlon), { draggable: true, zIndexOffset: 1000 }).addTo(map);

    var filter = function() {
      // we only want to make the request if the travel time
      // does not change for at least 100 ms
      clearTimeout(filterTimeout);

      var filterTimeout = setTimeout(function() {
        // you need to define some options for the polygon service
        // for more travel options check out the other tutorials
        var travelOptions = r360.travelOptions();
        // please contact us and request your own key
        travelOptions.setServiceKey('__APIPLACEHOLDER__');
        // set the service url for your area
        travelOptions.setServiceUrl('https://api.targomo.com/westcentraleurope/');
        // we only have one source which is the marker we just added
        travelOptions.addSource(sourceMarker);
        // add all the museums to the options
        travelOptions.setTargets(targets);
        // set the travel type to transit
        travelOptions.setTravelType('bike');
        // for all museums which are not reachable within <maxRoutingTime>
        // no routing time will be returned
        travelOptions.setMaxRoutingTime(traveltime * 60);

        // call the service
        r360.TimeService.getRouteTime(travelOptions, function(sources) {

          targetLayer.clearLayers();

          // get each target for the first source (only one source was supplied to the service)
          sources[0].targets.forEach(function(target) {

            // find the museum in our "database"
            var museum = museumsBB.filter(function(museum) {
                return museum.id == target.id; // Filter out the appropriate one
            })[0];

            // set the travel time for this museum
            museum.travelTime = target.travelTime;

            if (museum.travelTime > 0 && museum.travelTime <= travelOptions.getMaxRoutingTime()) {
                // travel time to museum < maximum travel width, so we'll symbolize as a green circle
                var poiSymbol = getSymbol(museum, true);
            } else {
                // otherwise we'll keep as red
                var poiSymbol = getSymbol(museum, false);
            }

            // bind the popup
            bindPopup(poiSymbol, museum);

            // add to targetLayer
            poiSymbol.addTo(targetLayer);
          });
        });
      }, 10)
    }

    // symbology definition function
    var getSymbol = function(point, reachable){
      return L.circleMarker([point.lat, point.lon], {
        color: "white",
        fillColor: reachable ? '#6DBD77' : 'red',
        fillOpacity: 1.0,
        stroke: true,
        radius: reachable ? 6 : 3,
      })
    }

    // popup binding function
    var bindPopup = function(poiSymbol, point){
      var popUpContent = "<h2><a href='" + point.url + "'>" + point.name + "</a></h2>";
      if (point.travelTime > 0){
        popUpContent += "<p> Travel time: " + r360.Util.secondsToHoursAndMinutes(point.travelTime) + "</p>"
      };
      poiSymbol.bindPopup(popUpContent);
    }

    // populate layer from museumsBB source
    var loadTargets = function() {
      targets = [];
      museumsBB.forEach(function(museum) {
        var target = getSymbol(museum, true);
        target.id = museum.id;
        targets.push(target);
      });
    }
    // initial load
    loadTargets();
    // initial filter
    filter();
    // re-filter on marker drag
    sourceMarker.on('dragend', filter);

    // set up slider
    var sliderMax = 30;
    var travelSlider = new Dragdealer('slider', {
      steps: 7, // only select at defined intervals
      snap: true, // snap to each step
      x: traveltime / sliderMax, // set start position
      animationCallback: function(x, y) { // on slider move
        // get converted slider value
        var sliderValue = Math.round(x * sliderMax);
        // set display value
        $('#actual-time').text(sliderValue);
        // update travel time to slider position
        traveltime = sliderValue;
        // re-filter points
        filter();
      }
    });
  </script>
</body>
</html>