{"id":1115,"date":"2022-12-20T22:17:20","date_gmt":"2022-12-20T21:17:20","guid":{"rendered":"https:\/\/www.combinant.be\/train-network\/"},"modified":"2023-04-29T15:48:34","modified_gmt":"2023-04-29T13:48:34","slug":"train-network","status":"publish","type":"page","link":"https:\/\/www.combinant.be\/nl\/train-network\/","title":{"rendered":"Trein netwerk"},"content":{"rendered":"\n<div class=\"wp-block-qubely-row alignfull qubely-section qubely-block-cefe78\"><div class=\"qubely-row-overlay\"><\/div><div class=\"qubely-container-fluid\"><div class=\"qubely-row qubely-row-height-window\">\n<div class=\"wp-block-qubely-column qubely-column qubely-column-front qubely-block-7540f3\"><div class=\"qubely-column-inner\">\n<style>\n    #legend {\n      background: #fff;\n      padding: 5px;\n      margin: 5px;\n      border: 2px solid #000;\n    }\n    #legend > p,\n    #mapDisclaimer {\n      font-size: 10px;\n    }\n    #mapDisclaimer {\n      padding: 3px;\n      text-align: center;\n      vertical-align: middle;\n    }\n  <\/style>\n  <input\n    id=\"pac-input\"\n    type=\"text\"\n    placeholder=\"Vind je locatie\"\n    style=\"font-size: 16px; margin-top: 10px; margin-left: 10px;\"\n  \/>\n  <div id=\"map\" style=\"width: 100%\"><\/div>\n  <p id=\"mapDisclaimer\">\n    De op of via deze pagina aangeboden informatie kan onjuistheden bevatten alle soorten. &#8220;Combinant&#8221; is niet verantwoordelijk voor geschiktheid, betrouwbaarheid, gedateerdheid of juistheid van de informatie. De informatie wordt bezorgd en gepubliceerd zonder enige vorm van garantie. U dient altijd de operator te raadplegen.\n  <\/p>\n  <script>\n    var mapUrl = \"https:\/\/trainnetwork-api.combinant.be\/api\/Map\";\n    var combinantIcon = \"https:\/\/maps.google.com\/mapfiles\/ms\/icons\/yellow-dot.png\";\n    var indirectIcon = \"https:\/\/maps.google.com\/mapfiles\/ms\/icons\/red-dot.png\";\n    var directIcon = \"https:\/\/maps.google.com\/mapfiles\/ms\/icons\/blue-dot.png\";\n    var searchedIcon = \"https:\/\/maps.google.com\/mapfiles\/ms\/icons\/green-dot.png\";\n    var legendElement = \"div\";\n    var legendHtml =\n      '<p style=\"width: 100%; text-align:center;\"><b>Legende<\/b><\/p>\\\n  <p><img decoding=\"async\" src=\"https:\/\/maps.google.com\/mapfiles\/ms\/icons\/yellow-dot.png\" \/> Combinant<\/p>\\\n  <p><img decoding=\"async\" src=\"https:\/\/maps.google.com\/mapfiles\/ms\/icons\/blue-dot.png\" \/> directe route<\/p>\\\n  <p><img decoding=\"async\" src=\"https:\/\/maps.google.com\/mapfiles\/ms\/icons\/red-dot.png\" \/> indirecte route<\/p>\\\n  <p><img decoding=\"async\" src=\"https:\/\/maps.google.com\/mapfiles\/ms\/icons\/green-dot.png\" \/> gevonden locatie<\/p>';\n  \n    var map;\n    var routes;\n    var operators;\n    var searchedMarkers = [];\n    var searchButton;\n  \n    var combinantPosition = {\n      lat: 51.37123953903346,\n      lng: 4.288451224565506,\n    };\n    var markers = [];\n  \n    var currentInfoWindow = null;\n    var bounds = {\n      north: combinantPosition.lat,\n      east: combinantPosition.lng,\n      south: combinantPosition.lat,\n      west: combinantPosition.lng,\n    };\n  \n    window.addEventListener(\"resize\", handleResize, false);\n    window.addEventListener(\"orientationchange\", handleResize, false);\n  \n    Array.prototype.find =\n      Array.prototype.find ||\n      function (callback) {\n        if (this === null) {\n          throw new TypeError(\"Array.prototype.find called on null or undefined\");\n        } else if (typeof callback !== \"function\") {\n          throw new TypeError(\"callback must be a function\");\n        }\n        var list = Object(this);\n        var length = list.length >>> 0;\n        var thisArg = arguments[1];\n        for (var i = 0; i < length; i++) {\n          var element = list[i];\n          if (callback.call(thisArg, element, i, list)) {\n            return element;\n          }\n        }\n      };\n  \n    function doResize() {\n      const height =\n        document.getElementsByClassName(\"qubely-row-height-window\")[0]\n          .clientHeight -\n        document.getElementById(\"mapDisclaimer\").clientHeight -\n        document.getElementById(\"site-header\").clientHeight;\n      document.getElementById(\"map\").style.height = height + \"px\";\n      const wrapHeight = document.getElementsByClassName(\n        \"wp-block-qubely-column qubely-column qubely-column-front\"\n      )[0].clientHeight;\n      document.getElementById(\"content-wrap\").style.height = wrapHeight + \"px\";\n    }\n  \n    function handleResize() {\n      doResize();\n      if (map) {\n        map.fitBounds(bounds);\n      }\n    }\n  \n    function initMap() {\n      doInitMap();\n      doInitSearchBox();\n      doInitClearSearchButton();\n    }\n\n    function rad(x) {return x*Math.PI\/180;}\n    \n    function findClosestMarker(position, markersToSearch) {\n        var lat = position.lat();\n        var lng = position.lng();\n        var R = 6371; \/\/ radius of earth in km\n        var distances = [];\n        var closest = -1;\n        for( i=0;i<markersToSearch.length; i++ ) {\n            var mlat = markersToSearch[i].position.lat();\n            var mlng = markersToSearch[i].position.lng();\n            var dLat  = rad(mlat - lat);\n            var dLong = rad(mlng - lng);\n            var a = Math.sin(dLat\/2) * Math.sin(dLat\/2) +\n                Math.cos(rad(lat)) * Math.cos(rad(lat)) * Math.sin(dLong\/2) * Math.sin(dLong\/2);\n            var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));\n            var d = R * c;\n            distances[i] = d;\n            if ( closest == -1 || d < distances[closest] ) {\n                closest = i;\n            }\n        }\n      \n        return markersToSearch[closest];\n    }\n\n    function createSearchControl(map) {\n      const controlButton = document.createElement(\"button\");\n      controlButton.style.cursor = \"pointer\";\n      controlButton.style.fontSize = \"16px\";\n      controlButton.style.marginTop = \"10px\";\n      controlButton.style.visibility = \"hidden\";\n      controlButton.style.textAlign = \"center\";\n      controlButton.textContent = \"Clear search\";\n      controlButton.title = \"Click to clear the search\";\n      controlButton.type = \"button\";\n      controlButton.addEventListener(\"click\", () => {\n        searchButton.style.visibility = \"hidden\";\n        clearSearchMarkers();\n        document.getElementById('pac-input').value = \"\";\n        map.fitBounds(bounds);\n      });\n      return controlButton;\n    }\n\n    function doInitClearSearchButton() {\n      const searchControlDiv = document.createElement(\"div\");\n      searchButton = createSearchControl(map);\n      searchControlDiv.appendChild(searchButton);\n      map.controls[google.maps.ControlPosition.TOP_LEFT].push(searchControlDiv);\n    }\n\n    function clearSearchMarkers() {\n        \/\/ Clear out the old markers.\n        searchedMarkers.forEach((marker) => {\n          marker.setMap(null);\n        });\n        searchedMarkers = [];\n    }\n\n    function doInitSearchBox() {\n        const input = document.getElementById(\"pac-input\");\n        const searchBox = new google.maps.places.SearchBox(input);\n\n        map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);\n        \/\/ Bias the SearchBox results towards current map's viewport.\n        map.addListener(\"bounds_changed\", () => {\n            searchBox.setBounds(map.getBounds());\n        });\n\n        searchBox.addListener(\"places_changed\", () => {\n            const places = searchBox.getPlaces();\n\n            if (places.length == 0) {\n              return;\n            }\n            \n            clearSearchMarkers();\n        \n            \/\/ For each place, get the icon, name and location.\n            const searchedBounds = new google.maps.LatLngBounds();\n        \n            places.forEach((place) => {\n              if (!place.geometry || !place.geometry.location) {\n                console.log(\"Returned place contains no geometry\");\n                return;\n              }\n          \n              \/\/ Create a marker for each place.\n              searchedMarkers.push(\n                new google.maps.Marker({\n                  map,\n                  icon: searchedIcon,\n                  title: place.name,\n                  position: place.geometry.location,\n                })\n              );\n              if (place.geometry.viewport) {\n                \/\/ Only geocodes have viewport.\n                searchedBounds.union(place.geometry.viewport);\n              } else {\n                searchedBounds.extend(place.geometry.location);\n              }\n            });\n            var closests = getClosestMarkers(searchedMarkers[0].position);\n            for (var i = 0; i < closests.length; i++) {\n              searchedBounds.extend(closests[i].position);\n            }\n            map.fitBounds(searchedBounds);\n            searchButton.style.visibility = \"visible\";\n          });\n    }\n\n    function getClosestMarkers(position) {\n      var closests = [];\n      var markersToSearch = [];\n      for (var i = 0; i< markers.length; i++) {\n        markersToSearch.push(markers[i]);\n      }\n      var closest = findClosestMarker(position, markersToSearch);\n      const index = markersToSearch.indexOf(closest);\n      markersToSearch.splice(index, 1);\n      closests.push(closest);\n      closests.push(findClosestMarker(position, markersToSearch));\n      return closests;\n    }\n\n    function doInitMap() {\n      doResize();\n      map = new google.maps.Map(document.getElementById(\"map\"), {\n        center: combinantPosition,\n        streetViewControl: false,\n        mapTypeControl: true,\n        mapTypeControlOptions: {\n          style: google.maps.MapTypeControlStyle.HORIZONTAL_BAR,\n          position: google.maps.ControlPosition.BOTTOM_LEFT,\n        },\n      });\n      var legend = document.createElement(legendElement);\n      legend.id = \"legend\";\n      legend.innerHTML = legendHtml;\n      map.controls[google.maps.ControlPosition.RIGHT_BOTTOM].push(legend);\n      setTimeout(loadTrainnetwork, 0);\n    }\n  \n    function loadTrainnetwork() {\n      var xhttp = new XMLHttpRequest();\n      xhttp.onreadystatechange = function () {\n        if (this.readyState == 4) {\n          if (this.status == 200) {\n            var data = JSON.parse(this.responseText);\n            routes = data.routes;\n            operators = data.operators;\n            setTimeout(showTrainnetwork, 0);\n          }\n        }\n      };\n      xhttp.open(\"GET\", mapUrl, true);\n      xhttp.send();\n    }\n  \n    function showTrainnetwork() {\n      var combinantMarker = new google.maps.Marker({\n        position: combinantPosition,\n        map: map,\n        icon: combinantIcon,\n      });\n      combinantMarker.addListener(\"click\", function () {\n        if (currentInfoWindow !== null) {\n          currentInfoWindow.close();\n        }\n        var infowindow = new google.maps.InfoWindow({\n          content: \"<b>Combinant<\/b>\",\n        });\n        infowindow.open(map, combinantMarker);\n        currentInfoWindow = infowindow;\n      });\n  \n      markers = [];\n      markers.push(combinantMarker);\n  \n      var lines = [];\n      routes.forEach(function (route) {\n        var marker = new google.maps.Marker({\n          position: {\n            lat: route.terminal.coordinates.latitude,\n            lng: route.terminal.coordinates.longitude,\n          },\n          map: map,\n          icon: getIcon(route),\n        });\n        marker.addListener(\"click\", function () {\n          if (currentInfoWindow !== null) {\n            currentInfoWindow.close();\n          }\n          var infowindow = new google.maps.InfoWindow({\n            content: getInfoWindowContent(route),\n          });\n          infowindow.open(map, marker);\n          currentInfoWindow = infowindow;\n        });\n        markers.push(marker);\n        if (route.terminal.coordinates.latitude < bounds.north) {\n          bounds.north = route.terminal.coordinates.latitude;\n        }\n        if (route.terminal.coordinates.latitude > bounds.south) {\n          bounds.south = route.terminal.coordinates.latitude;\n        }\n        if (route.terminal.coordinates.longitude < bounds.west) {\n          bounds.west = route.terminal.coordinates.longitude;\n        }\n        if (route.terminal.coordinates.longitude > bounds.east) {\n          bounds.east = route.terminal.coordinates.longitude;\n        }\n        pushLines(lines, route);\n      }, this);\n  \n      drawLines(lines);\n      map.fitBounds(bounds);\n    }\n  \n    function getInfoWindowContent(route) {\n      var content = \"<b>\" + route.terminal.name + \"<\/b><br\/>\";\n      route.timings.forEach(function (timing) {\n        content += \"<div><hr\/>\";\n  \n        content += \"<span>Operator \";\n        if (hasOperatorUrl(timing)) {\n          content += '<a target=\"_blank\" href=\"' + getOperatorUrl(timing) + '\">';\n        }\n        content += \"<b>\" + getOperatorName(timing) + \"<\/b>\";\n        if (hasOperatorUrl(timing)) {\n          content += \"<\/a>\";\n        }\n        content += \"<\/span>\";\n  \n        if (hasTimingData(timing.timingDataTo)) {\n          content += \"<pre>\";\n          content += getFormattedTimingData(\n            \"<b>from<\/b> Combinant <b>to<\/b> \" + route.terminal.name + \":\",\n            timing.timingDataTo,\n            \"departure\"\n          );\n          content += \"<\/pre>\";\n        }\n  \n        if (hasTimingData(timing.timingDataFrom)) {\n          content += \"<pre>\";\n          content += getFormattedTimingData(\n            \"<b>from<\/b> \" + route.terminal.name + \" <b>to<\/b> Combinant:\",\n            timing.timingDataFrom,\n            \"  arrival\"\n          );\n          content += \"<\/pre>\";\n        }\n  \n        content += \"<\/div>\";\n      });\n      return content;\n    }\n  \n    function getIcon(route) {\n      return route.isDirectRoute ? directIcon : indirectIcon;\n    }\n  \n    function drawLines(lines) {\n      lines.forEach(function (line) {\n        line.setMap(map);\n      });\n    }\n  \n    function pushLines(lines, route) {\n      if (route.isDirectRoute) {\n        lines.push(\n          new google.maps.Polyline({\n            path: [\n              combinantPosition,\n              {\n                lat: route.terminal.coordinates.latitude,\n                lng: route.terminal.coordinates.longitude,\n              },\n            ],\n            geodesic: false,\n            strokeColor: \"#0000FF\",\n            strokeOpacity: 1.0,\n            strokeWeight: 1.7,\n          })\n        );\n      }\n      if (route.viaIds !== null && route.viaIds.length > 0) {\n        var toCoordinates = route.terminal.coordinates;\n        route.viaIds.forEach(function (viaId) {\n          var fromCoordinates = routes.find(function (r) {\n            return r.id === viaId;\n          }).terminal.coordinates;\n          lines.push(\n            new google.maps.Polyline({\n              path: [\n                {\n                  lat: fromCoordinates.latitude,\n                  lng: fromCoordinates.longitude,\n                },\n                {\n                  lat: toCoordinates.latitude,\n                  lng: toCoordinates.longitude,\n                },\n              ],\n              geodesic: false,\n              strokeColor: \"#FF0000\",\n              strokeOpacity: 1.0,\n              strokeWeight: 1.7,\n            })\n          );\n        });\n      }\n    }\n  \n    function getOperator(operatorId) {\n      var operator = operators.find(function (o) {\n        return o.id === operatorId;\n      });\n      if (operator) {\n        return operator;\n      } else {\n        return null;\n      }\n    }\n  \n    function hasOperatorUrl(timing) {\n      return getOperatorUrl(timing) !== null;\n    }\n  \n    function getOperatorUrl(timing) {\n      var operator = getOperator(timing.operatorId);\n      if (operator) {\n        var url = operator.url;\n        if (url && url.trim() !== \"\") {\n          return url;\n        } else {\n          return null;\n        }\n      } else {\n        return null;\n      }\n    }\n  \n    function getOperatorName(timing) {\n      const operator = getOperator(timing.operatorId);\n      if (operator) {\n        return operator.name;\n      } else {\n        return null;\n      }\n    }\n  \n    function hasTimingData(timingData) {\n      if (\n        (timingData.departure === null || timingData.departure.trim() === \"\") &&\n        (timingData.closing === null || timingData.closing.trim() === \"\") &&\n        (timingData.pickup === null || timingData.pickup.trim() === \"\") &&\n        (timingData.profile === null || timingData.profile.trim() === \"\")\n      ) {\n        return false;\n      }\n  \n      return (\n        timingData !== null &&\n        (timingData.departure !== null ||\n          timingData.closing !== null ||\n          timingData.pickup !== null ||\n          timingData.profile !== null)\n      );\n    }\n  \n    function getFormattedTimingData(prefix, timingData, daysWording) {\n      var formatted = prefix + \"\\n\";\n      if (timingData.departure !== null && timingData.departure.trim() !== \"\") {\n        formatted += \" <b>\" + daysWording + \"<\/b> \" + timingData.departure + \"\\n\";\n      }\n      if (timingData.closing !== null && timingData.closing.trim() !== \"\") {\n        formatted += \"   <b>closing<\/b> \" + timingData.closing + \"\\n\";\n      }\n      if (timingData.pickup !== null && timingData.pickup.trim() !== \"\") {\n        formatted += \"    <b>pickup<\/b> \" + timingData.pickup + \"\\n\";\n      }\n      if (timingData.profile !== null && timingData.profile.trim() !== \"\") {\n        formatted += \"   <b>profile<\/b> \" + timingData.profile;\n      }\n      return formatted;\n    }\n  <\/script>\n  <script async=\"\" defer=\"\" src=\"https:\/\/maps.googleapis.com\/maps\/api\/js?key=AIzaSyCI-TrT5xbpbakJqRM2i34WtNSq9RrTHFY&amp;libraries=places&amp;callback=initMap\"><\/script>\n<\/div><\/div>\n<\/div><\/div><\/div>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"qubely_global_settings":"","qubely_interactions":"","_uag_custom_page_level_css":"","ocean_post_layout":"full-screen","ocean_both_sidebars_style":"","ocean_both_sidebars_content_width":0,"ocean_both_sidebars_sidebars_width":0,"ocean_sidebar":"0","ocean_second_sidebar":"0","ocean_disable_margins":"enable","ocean_add_body_class":"","ocean_shortcode_before_top_bar":"","ocean_shortcode_after_top_bar":"","ocean_shortcode_before_header":"","ocean_shortcode_after_header":"","ocean_has_shortcode":"","ocean_shortcode_after_title":"","ocean_shortcode_before_footer_widgets":"","ocean_shortcode_after_footer_widgets":"","ocean_shortcode_before_footer_bottom":"","ocean_shortcode_after_footer_bottom":"","ocean_display_top_bar":"default","ocean_display_header":"default","ocean_header_style":"","ocean_center_header_left_menu":"0","ocean_custom_header_template":"0","ocean_custom_logo":0,"ocean_custom_retina_logo":0,"ocean_custom_logo_max_width":0,"ocean_custom_logo_tablet_max_width":0,"ocean_custom_logo_mobile_max_width":0,"ocean_custom_logo_max_height":0,"ocean_custom_logo_tablet_max_height":0,"ocean_custom_logo_mobile_max_height":0,"ocean_header_custom_menu":"0","ocean_menu_typo_font_family":"0","ocean_menu_typo_font_subset":"","ocean_menu_typo_font_size":0,"ocean_menu_typo_font_size_tablet":0,"ocean_menu_typo_font_size_mobile":0,"ocean_menu_typo_font_size_unit":"px","ocean_menu_typo_font_weight":"","ocean_menu_typo_font_weight_tablet":"","ocean_menu_typo_font_weight_mobile":"","ocean_menu_typo_transform":"","ocean_menu_typo_transform_tablet":"","ocean_menu_typo_transform_mobile":"","ocean_menu_typo_line_height":0,"ocean_menu_typo_line_height_tablet":0,"ocean_menu_typo_line_height_mobile":0,"ocean_menu_typo_line_height_unit":"","ocean_menu_typo_spacing":0,"ocean_menu_typo_spacing_tablet":0,"ocean_menu_typo_spacing_mobile":0,"ocean_menu_typo_spacing_unit":"","ocean_menu_link_color":"","ocean_menu_link_color_hover":"","ocean_menu_link_color_active":"","ocean_menu_link_background":"","ocean_menu_link_hover_background":"","ocean_menu_link_active_background":"","ocean_menu_social_links_bg":"","ocean_menu_social_hover_links_bg":"","ocean_menu_social_links_color":"","ocean_menu_social_hover_links_color":"","ocean_disable_title":"default","ocean_disable_heading":"default","ocean_post_title":"","ocean_post_subheading":"","ocean_post_title_style":"","ocean_post_title_background_color":"","ocean_post_title_background":0,"ocean_post_title_bg_image_position":"","ocean_post_title_bg_image_attachment":"","ocean_post_title_bg_image_repeat":"","ocean_post_title_bg_image_size":"","ocean_post_title_height":0,"ocean_post_title_bg_overlay":0.5,"ocean_post_title_bg_overlay_color":"","ocean_disable_breadcrumbs":"default","ocean_breadcrumbs_color":"","ocean_breadcrumbs_separator_color":"","ocean_breadcrumbs_links_color":"","ocean_breadcrumbs_links_hover_color":"","ocean_display_footer_widgets":"default","ocean_display_footer_bottom":"default","ocean_custom_footer_template":"0","osh_disable_topbar_sticky":"default","osh_disable_header_sticky":"default","osh_sticky_header_style":"fixed","osh_sticky_header_effect":"","osh_custom_sticky_logo":0,"osh_custom_retina_sticky_logo":0,"osh_custom_sticky_logo_height":0,"osh_background_color":"","osh_links_color":"","osh_links_hover_color":"","osh_links_active_color":"","osh_links_bg_color":"","osh_links_hover_bg_color":"","osh_links_active_bg_color":"","osh_menu_social_links_color":"","osh_menu_social_hover_links_color":"","footnotes":""},"class_list":["post-1115","page","type-page","status-publish","hentry","entry"],"uagb_featured_image_src":{"full":false,"thumbnail":false,"medium":false,"medium_large":false,"large":false,"1536x1536":false,"2048x2048":false,"ocean-thumb-m":false,"ocean-thumb-ml":false,"ocean-thumb-l":false,"qubely_landscape":false,"qubely_portrait":false,"qubely_thumbnail":false},"uagb_author_info":{"display_name":"jeankedotcom","author_link":"https:\/\/www.combinant.be\/nl\/author\/jeankedotcom\/"},"uagb_comment_info":0,"uagb_excerpt":null,"_links":{"self":[{"href":"https:\/\/www.combinant.be\/nl\/wp-json\/wp\/v2\/pages\/1115","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.combinant.be\/nl\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.combinant.be\/nl\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.combinant.be\/nl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.combinant.be\/nl\/wp-json\/wp\/v2\/comments?post=1115"}],"version-history":[{"count":6,"href":"https:\/\/www.combinant.be\/nl\/wp-json\/wp\/v2\/pages\/1115\/revisions"}],"predecessor-version":[{"id":1123,"href":"https:\/\/www.combinant.be\/nl\/wp-json\/wp\/v2\/pages\/1115\/revisions\/1123"}],"wp:attachment":[{"href":"https:\/\/www.combinant.be\/nl\/wp-json\/wp\/v2\/media?parent=1115"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}