var ambulanceSymbol; var warningSymbol; var stationSymbol; var map; var directionsService; var directionsRenderer; // This example adds an animated symbol to a polyline. function initMap() { map = new google.maps.Map(document.getElementById('map'), { center : {lat : 40.4384511, lng : -3.6959337}, zoom : 11, mapTypeId : 'terrain' }); directionsService = new google.maps.DirectionsService(); directionsRenderer = new google.maps.DirectionsRenderer(); directionsRenderer.setMap(map); // https://material.io/resources/icons/?icon=airport_shuttle&style=baseline ambulanceSymbol = { path : 'M4 16c0 .88.39 1.67 1 2.22V20c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-1h8v1c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-1.78c.61-.55 1-1.34 1-2.22V6c0-3.5-3.58-4-8-4s-8 .5-8 4v10zm3.5 1c-.83 0-1.5-.67-1.5-1.5S6.67 14 7.5 14s1.5.67 1.5 1.5S8.33 17 7.5 17zm9 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm1.5-6H6V6h12v5z', fillColor : 'orange', fillOpacity : 1, scale : 2, strokeColor : 'red', strokeWeight : 1, rotation : 0, anchor : new google.maps.Point(20, 10) } // https://material.io/resources/icons/?icon=warning&style=baseline warningSymbol = { path : 'M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z', fillColor : 'orange', fillOpacity : 0, scale : 2, strokeColor : 'red', strokeWeight : 3, rotation : 0, } // https://material.io/resources/icons/?icon=home_work&style=baseline stationSymbol = { path : 'M8.17 5.7L1 10.48V21h5v-8h4v8h5V10.25z', fillColor : 'orange', fillOpacity : 0, scale : 2, strokeColor : 'green', strokeWeight : 3, rotation : 0, } } function loadSimulation(simData) { var ambs = simData['Ambulances']; for (var i = 0; i < ambs.length; i++) { createAmbulanceRoute(ambs[i]); } } function calculateRoute(route, startPlace, endPlace) { var request = { origin : startPlace, destination : endPlace, travelMode : 'DRIVING' }; var timeoutMillisecs = Math.floor((Math.random() * 10000) + 2000); setTimeout(directionsService.route, timeoutMillisecs, request, function(response, status) { if (status == 'OK') { var linePath = []; var steps = response.routes[0].legs[0].steps; for (var s = 0; s < steps.length; s++) { var path = steps[s].path; for (var p = 0; p < path.length; p++) { linePath.push(path[p]); } } route.setPath(linePath); nPendingRequests--; console.log(nPendingRequests); } else if (status == 'OVER_QUERY_LIMIT') { calculateRoute(route, startPlace, endPlace); } }); } function createAmbulanceRoute(ambData) { var emgs = ambData['Emergencies']; var linePath = []; linePath.push(ambData['Station']); var stationMarker = new google.maps.Marker( {position : ambData['Station'], icon : stationSymbol, map : map}); for (var i = 0; i < emgs.length; i++) { linePath.push(emgs[i]); var emergencyMarker = new google.maps.Marker( {position : emgs[i], icon : warningSymbol, map : map}); } // Create the polyline and add the symbol to it via the 'icons' property. var line = new google.maps.Polyline({ path : linePath, icons : [ {icon : ambulanceSymbol, offset : '0%', fixedRotation : true} ], map : map }); animatePolyline(line); } // Use the DOM setInterval() function to change the offset of the symbol // at fixed intervals. function animatePolyline(line) { var count = 0; var animInterval; animInterval = window.setInterval(function() { count = (count + 1); if (count >= 200) { window.clearInterval(animInterval); } var icons = line.get('icons'); icons[0].offset = (count / 2) + '%'; line.set('icons', icons); }, 20); } // getJSONObject reads the data of a JSON file on the server and passes the // parsed object to callback function getJSONObject(filename, callback) { var request = new XMLHttpRequest(); request.open('GET', filename); request.responseType = 'json'; request.send(); request.onload = function() { var jsonObj = request.response; callback(jsonObj); } } function getCustomFile(callback) { var x = document.getElementById("customFile"); x.disabled = true; var customFile = x.files[0]; customFile.text().then(function(customText) { customJSON = JSON.parse(customText); callback(customJSON); }); } var timer; var timerIntvl; var simDate; // date and time of the simulation var slider; var speedSlider; function setTimer(date) { var hours = date.getHours(); var mins = date.getMinutes(); var secs = date.getSeconds(); var dispStr = ""; if (hours < 10) { dispStr += "0"; } dispStr += hours + ":"; if (mins < 10) { dispStr += "0"; } dispStr += mins + ":"; if (secs < 10) { dispStr += "0"; } dispStr += secs + "
" + date.getDate() + "." + date.getMonth() + "." + date.getFullYear(); timer.innerHTML = dispStr; simDate = date; } // loadState loads the state at the time state function loadState(state) { var time = state.getTime(); for (var i = 0; i < emergencies.length; i++) { var emg = emergencies[i]; if (time >= emg.start.getTime() && time <= emg.end.getTime()) { emg.marker.setMap(map); } else { emg.marker.setMap(null); } } for (var i = 0; i < ambulances.length; i++) { var amb = ambulances[i]; var action = null; var acStart; var acEnd; if (time >= amb.actions[0].start.getTime()) { for (var j = 1; j < amb.actions.length; j++) { if (time < amb.actions[j].start.getTime()) { action = j - 1; acStart = amb.actions[j - 1].start.getTime(); acEnd = amb.actions[j].start.getTime(); break; } } if (action == null && time < amb.end.getTime()) { action = amb.actions.length - 1; acStart = amb.actions[action].start.getTime(); acEnd = amb.end.getTime(); } for (var j = 1; j < amb.actions.length; j++) { if (j == action) { amb.actions[j].route.setMap(map); } else { amb.actions[j].route.setMap(null); } } if (action != null) { var activeAction = amb.actions[action]; var icons = activeAction.route.get('icons'); var percent; if (activeAction.Type == "Wait") { percent = 0; } else if (activeAction.Type == "GoTo") { percent = Math.floor(((time - acStart) / (acEnd - acStart)) * 100); } icons[0].offset = percent + '%'; activeAction.route.set('icons', icons); } } } setTimer(state); } function advanceTimer() { simDate.setTime(simDate.getTime() + 1000 * simSpeed); slider.stepUp(simSpeed); loadState(simDate); } function startTimer() { loadState(simDate); timerIntvl = window.setInterval(advanceTimer, 1000); } function stopTimer() { window.clearInterval(timerIntvl); } function sliderChange() { pauseClick(); simDate.setTime(startTimeDate.getTime() + 1000 * this.value); loadState(simDate); } var simSpeed; function speedSliderChange() { simSpeed = this.value; } function onLoad() { timer = document.getElementById("timer"); playPauseBtn = document.getElementById("playPauseBtn"); slider = document.getElementById("simSlider"); slider.addEventListener('input', sliderChange); speedSlider = document.getElementById("speedSlider"); speedSlider.addEventListener('input', speedSliderChange); disablePlayPauseBtn(); } function drawStations(stations) { for (var i = 0; i < stations.length; i++) { var stationMarker = new google.maps.Marker( {position : stations[i], icon : stationSymbol, map : map}); } } var sim; var startTimeDate; var endTimeDate; var emergencies; var ambulances; function getPos(place) { if (place['Type'] == 'Station') { return sim['Stations'][place['Index']]; } else if (place['Type'] == 'Emergency') { return sim['Emergencies'][place['Index']]['pos']; } else { console.log('unknown Place type'); } } var nPendingRequests; // loadSim loads the simulation stored in simJSON function loadSim(simJSON) { pauseClick(); enablePlayPauseBtn(); sim = simJSON; console.log(sim); startTimeDate = new Date(sim['StartDateTime']); endTimeDate = new Date(sim['EndDateTime']); simDate = new Date(startTimeDate); var nSeconds = Math.floor((endTimeDate.getTime() - startTimeDate.getTime()) / 1000); slider.setAttribute("min", "0"); slider.setAttribute("max", nSeconds); slider.setAttribute("step", "1"); simSpeed = 1; initMap(); drawStations(sim['Stations']); emergencies = []; var emgs = sim['Emergencies']; for (var i = 0; i < emgs.length; i++) { var emergencyMarker = new google.maps.Marker( {position : emgs[i]['pos'], icon : warningSymbol, map : map}); emergencyMarker.setMap(null); emergencies.push({ "marker" : emergencyMarker, "start" : new Date(emgs[i]["StartDateTime"]), "end" : new Date(emgs[i]["EndDateTime"]) }); } nPendingRequests = 0; ambulances = []; var ambs = sim['Ambulances']; for (var i = 0; i < ambs.length; i++) { var actions = ambs[i]['Actions']; var amb = {"actions" : [], "end" : new Date(ambs[i]['EndDateTime'])}; ambulances.push(amb); for (var j = 0; j < actions.length; j++) { var startLine; if (j == 0) { startLine = getPos(actions[j]['Place']); } else { startLine = getPos(actions[j - 1]['Place']); } var endLine = getPos(actions[j]['Place']); var linePath = [ startLine, endLine ]; // Create the polyline and add the symbol to it via the 'icons' property. var line = new google.maps.Polyline({ path : linePath, icons : [ {icon : ambulanceSymbol, offset : '0%', fixedRotation : true } ] }); amb.actions.push({ route : line, start : new Date(actions[j].StartDateTime), Place : actions[j].Place, Type : actions[j].Type }); setTimeout(calculateRoute, i * j * 1000, amb.actions[amb.actions.length - 1].route, startLine, endLine); nPendingRequests++; } } loadState(simDate); } var playPauseBtn; var playPauseFunc; function playClick() { startTimer(); playPauseBtn.setAttribute("src", "pause.png"); playPauseBtn.setAttribute("title", "Pause"); playPauseFunc = pauseClick; } function pauseClick() { stopTimer(); playPauseBtn.setAttribute("src", "play.png"); playPauseBtn.setAttribute("title", "Play"); playPauseFunc = playClick; } function disablePlayPauseBtn() { playPauseBtn.opacity = 0.3; playPauseBtn.setAttribute("disabled", "true"); } function enablePlayPauseBtn() { playPauseBtn.opacity = 1; playPauseBtn.setAttribute("disabled", "false"); } function playPauseClick() { playPauseFunc(); }