diff --git a/airline-web/public/javascripts/airport.js b/airline-web/public/javascripts/airport.js
index 7b50f4fdc..cd083e5d1 100644
--- a/airline-web/public/javascripts/airport.js
+++ b/airline-web/public/javascripts/airport.js
@@ -1148,16 +1148,25 @@ function updateAirportBaseMarkers(newBaseAirports, relatedFlightPaths) {
marker.baseInfo = baseAirport
var originalOpacity = marker.getOpacity()
marker.addListener('mouseover', function(event) {
+ var primaryBaseInfo = this.baseInfo
+ var airlineIdsToHighlight = [primaryBaseInfo.airlineId]
+
+ // if a partner base's info is attached, add its airline ID too
+ if (primaryBaseInfo.alliancePartnerBaseInfo) {
+ airlineIdsToHighlight.push(primaryBaseInfo.alliancePartnerBaseInfo.airlineId)
+ }
+
$.each(relatedFlightPaths, function(linkId, pathEntry) {
var path = pathEntry.path
var link = pathEntry.path.link
if (!$(path).data("originalOpacity")) {
$(path).data("originalOpacity", path.strokeOpacity)
}
- if (link.fromAirportId != baseAirport.airportId || link.airlineId != baseAirport.airlineId) {
- path.setOptions({ strokeOpacity : 0.1 })
- } else {
+
+ if (link.fromAirportId == primaryBaseInfo.airportId && airlineIdsToHighlight.includes(link.airlineId)) {
path.setOptions({ strokeOpacity : 0.8 })
+ } else {
+ path.setOptions({ strokeOpacity : 0.1 })
}
})
})
diff --git a/airline-web/public/javascripts/alliance.js b/airline-web/public/javascripts/alliance.js
index 76e786dc8..0a884fac9 100644
--- a/airline-web/public/javascripts/alliance.js
+++ b/airline-web/public/javascripts/alliance.js
@@ -742,66 +742,133 @@ function showAllianceMap() {
$.ajax({
type: 'GET',
url: "alliances/" + selectedAlliance.id + "/details",
- contentType: 'application/json; charset=utf-8',
- dataType: 'json',
- success: function(result) {
- $.each(result.links, function(index, link) {
- alliancePaths.push(drawAllianceLink(link))
+ contentType: 'application/json; charset=utf-8',
+ dataType: 'json',
+ success: function(result) {
+ //group links by endpoints
+ var groupedLinks = {};
+ $.each(result.links, function(index, link) {
+ var key = [link.fromAirportId, link.toAirportId].sort().join('-');
+
+ if (!groupedLinks[key]) {
+ groupedLinks[key] = {
+ links: [],
+ totalCapacity: 0,
+ fromLatitude: link.fromLatitude,
+ fromLongitude: link.fromLongitude,
+ toLatitude: link.toLatitude,
+ toLongitude: link.toLongitude,
+ fromAirportCity: link.fromAirportCity,
+ fromAirportCode: link.fromAirportCode,
+ fromCountryCode: link.fromCountryCode,
+ toAirportCity: link.toAirportCity,
+ toAirportCode: link.toAirportCode,
+ toCountryCode: link.toCountryCode
+ };
+ }
+ groupedLinks[key].links.push(link);
+ groupedLinks[key].totalCapacity += link.capacity.total;
+ });
+
+ $.each(groupedLinks, function(key, groupedLink) {
+ alliancePaths.push(drawAllianceLink(groupedLink));
+ });
+
+ //consolidate information for airports with multiple alliance bases
+ var rawAllianceMembersBases = []
+ $.each(result.members, function(index, airline) {
+ if (airline.role !== "APPLICANT") {
+ $.merge(rawAllianceMembersBases, airline.bases)
+ }
+ })
+
+ var basesByAirportId = {}
+ $.each(rawAllianceMembersBases, function(index, base) {
+ if (!basesByAirportId[base.airportId]) {
+ basesByAirportId[base.airportId] = []
+ }
+ basesByAirportId[base.airportId].push(base)
+ })
+
+ var allianceBasesForMap = []
+ $.each(basesByAirportId, function(airportId, basesAtThisAirport) {
+ if (basesAtThisAirport.length === 1) {
+ allianceBasesForMap.push(basesAtThisAirport[0])
+ } else if (basesAtThisAirport.length === 2) {
+ var base1 = basesAtThisAirport[0]
+ var base2 = basesAtThisAirport[1]
+ var primaryBase = base1.headquarter ? base1 : base2
+ var partnerBase = base1.headquarter ? base2 : base1
+ primaryBase.alliancePartnerBaseInfo = partnerBase
+ allianceBasesForMap.push(primaryBase)
+ }
+ })
+
+ var airportMarkers = updateAirportBaseMarkers(allianceBasesForMap, alliancePaths)
+
+ $.each(airportMarkers, function(key, marker) {
+ marker.addListener('mouseover', function(event) {
+ closeAlliancePopups()
+ var baseInfo = marker.baseInfo
+ var partnerInfo = baseInfo.alliancePartnerBaseInfo
+
+ var $popup = $("#allianceBasePopup").clone()
+
+ $popup.find(".city").html(getCountryFlagImg(baseInfo.countryCode) + " " + baseInfo.city)
+ $popup.find(".airportName").text(baseInfo.airportName)
+ $popup.find(".iata").html(baseInfo.airportCode)
+ $popup.find(".airlineName").html(getAirlineLogoImg(baseInfo.airlineId) + " " + baseInfo.airlineName + (baseInfo.headquarter ? " (HQ)" : ""))
+ $popup.find(".baseScale").html(baseInfo.scale)
+
+ if (partnerInfo) {
+ var $table = $popup.find(".table")
+ $table.append("
")
+
+ var $airlineRowTemplate = $popup.find(".airlineName").closest(".table-row")
+ var $partnerAirlineRow = $airlineRowTemplate.clone()
+ $partnerAirlineRow.find(".airlineName").html(getAirlineLogoImg(partnerInfo.airlineId) + " " + partnerInfo.airlineName + (partnerInfo.headquarter ? " (HQ)" : ""))
+ $table.append($partnerAirlineRow)
+
+ var $scaleRowTemplate = $popup.find(".baseScale").closest(".table-row")
+ var $partnerScaleRow = $scaleRowTemplate.clone()
+ $partnerScaleRow.find(".baseScale").html(partnerInfo.scale)
+ $table.append($partnerScaleRow)
+ }
+
+ $popup.show()
+
+ var infoWindow = new google.maps.InfoWindow({ maxWidth : 1200 });
+ infoWindow.setContent($popup[0])
+ infoWindow.open(map, marker);
+ map.allianceBasePopup = infoWindow
})
- var allianceBases = []
- $.each(result.members, function(index, airline) {
- if (airline.role != "APPLICANT") {
- $.merge(allianceBases, airline.bases)
- }
- })
-
- var airportMarkers = updateAirportBaseMarkers(allianceBases, alliancePaths)
- //now add extra listener for alliance airports
- $.each(airportMarkers, function(key, marker) {
- marker.addListener('mouseover', function(event) {
- closeAlliancePopups()
- var baseInfo = marker.baseInfo
- $("#allianceBasePopup .city").html(getCountryFlagImg(baseInfo.countryCode) + " " + baseInfo.city)
- $("#allianceBasePopup .airportName").text(baseInfo.airportName)
- $("#allianceBasePopup .iata").html(baseInfo.airportCode)
- $("#allianceBasePopup .airlineName").html(getAirlineLogoImg(baseInfo.airlineId) + " " + baseInfo.airlineName)
- $("#allianceBasePopup .baseScale").html(baseInfo.scale)
-
- var infoWindow = new google.maps.InfoWindow({ maxWidth : 1200});
- var popup = $("#allianceBasePopup").clone()
- popup.show()
- infoWindow.setContent(popup[0])
- //infoWindow.setPosition(event.latLng);
- infoWindow.open(map, marker);
- map.allianceBasePopup = infoWindow
- })
- marker.addListener('mouseout', function(event) {
- closeAlliancePopups()
- })
- })
-
-
- switchMap();
- $("#worldMapCanvas").data("initCallback", function() { //if go back to world map, re-init the map
- map.controls[google.maps.ControlPosition.TOP_CENTER].clear()
- clearAllPaths()
- updateAirportMarkers(activeAirline)
- updateLinksInfo() //redraw all flight paths
- closeAlliancePopups()
- })
-
- window.setTimeout(addExitButton , 1000); //delay otherwise it doesn't push to center
- },
- error: function(jqXHR, textStatus, errorThrown) {
- console.log(JSON.stringify(jqXHR));
- console.log("AJAX error: " + textStatus + ' : ' + errorThrown);
- },
- beforeSend: function() {
- $('body .loadingSpinner').show()
- },
- complete: function(){
- $('body .loadingSpinner').hide()
- }
+ marker.addListener('mouseout', function(event) {
+ closeAlliancePopups()
+ })
+ })
+
+
+ switchMap();
+ $("#worldMapCanvas").data("initCallback", function() { //if go back to world map, re-init the map
+ map.controls[google.maps.ControlPosition.TOP_CENTER].clear()
+ clearAllPaths()
+ updateAirportMarkers(activeAirline)
+ updateLinksInfo() //redraw all flight paths
+ closeAlliancePopups()
+ })
+
+ window.setTimeout(addExitButton , 1000); //delay otherwise it doesn't push to center
+ },
+ error: function(jqXHR, textStatus, errorThrown) {
+ console.log(JSON.stringify(jqXHR));
+ console.log("AJAX error: " + textStatus + ' : ' + errorThrown);
+ },
+ beforeSend: function() {
+ $('body .loadingSpinner').show()
+ },
+ complete: function(){
+ $('body .loadingSpinner').hide()
+ }
});
}
@@ -812,90 +879,114 @@ function addExitButton() {
map.controls[google.maps.ControlPosition.TOP_CENTER].push(createMapButton(map, 'Exit Alliance Flight Map', 'hideAllianceMap()', 'hideAllianceMapButton')[0]);
}
-function drawAllianceLink(link) {
- var from = new google.maps.LatLng({lat: link.fromLatitude, lng: link.fromLongitude})
- var to = new google.maps.LatLng({lat: link.toLatitude, lng: link.toLongitude})
- //var pathKey = link.id
-
- var strokeColor = airlineColors[link.airlineId]
+function drawAllianceLink(groupedLink) {
+ var from = new google.maps.LatLng({lat: groupedLink.fromLatitude, lng: groupedLink.fromLongitude})
+ var to = new google.maps.LatLng({lat: groupedLink.toLatitude, lng: groupedLink.toLongitude})
+
+ //use link with highest capacity for route color
+ var dominantLink = groupedLink.links.reduce(function(prev, current) {
+ return (prev.capacity.total > current.capacity.total) ? prev : current;
+ });
+
+ var strokeColor = airlineColors[dominantLink.airlineId]
if (!strokeColor) {
strokeColor = "#DC83FC"
}
- var maxOpacity = 0.7
- var minOpacity = 0.1
- var standardCapacity = 10000
- var strokeOpacity
- if (link.capacity.total < standardCapacity) {
- strokeOpacity = minOpacity + link.capacity.total / standardCapacity * (maxOpacity - minOpacity)
- } else {
- strokeOpacity = maxOpacity
- }
-
+ var maxOpacity = 0.7
+ var minOpacity = 0.1
+ var standardCapacity = 10000
+ var strokeOpacity
+ if (groupedLink.totalCapacity < standardCapacity) {
+ strokeOpacity = minOpacity + groupedLink.totalCapacity / standardCapacity * (maxOpacity - minOpacity)
+ } else {
+ strokeOpacity = maxOpacity
+ }
+
var linkPath = new google.maps.Polyline({
- geodesic: true,
- strokeColor: strokeColor,
- strokeOpacity: strokeOpacity,
- strokeWeight: 2,
- path: [from, to],
- zIndex : 90,
- link : link
- });
-
- var fromAirport = getAirportText(link.fromAirportCity, link.fromAirportCode)
- var toAirport = getAirportText(link.toAirportCity, link.toAirportCode)
-
-
- shadowPath = new google.maps.Polyline({
- geodesic: true,
- strokeColor: strokeColor,
- strokeOpacity: 0.0001,
- strokeWeight: 25,
- path: [from, to],
- zIndex : 100,
- fromAirport : fromAirport,
- fromCountry : link.fromCountryCode,
- toAirport : toAirport,
- toCountry : link.toCountryCode,
- capacity : link.capacity.total,
- airlineName : link.airlineName,
- airlineId : link.airlineId
+ geodesic: true,
+ strokeColor: strokeColor,
+ strokeOpacity: strokeOpacity,
+ strokeWeight: 2,
+ path: [from, to],
+ zIndex : 90,
+ //for compatibility with other functions, assign the dominant link object
+ link : dominantLink
});
-
+
+ var fromAirport = getAirportText(dominantLink.fromAirportCity, dominantLink.fromAirportCode)
+ var toAirport = getAirportText(dominantLink.toAirportCity, dominantLink.toAirportCode)
+
+ var shadowPath = new google.maps.Polyline({
+ geodesic: true,
+ strokeColor: strokeColor,
+ strokeOpacity: 0.0001,
+ strokeWeight: 25,
+ path: [from, to],
+ zIndex : 100,
+ fromAirport : fromAirport,
+ fromCountry : groupedLink.fromCountryCode,
+ toAirport : toAirport,
+ toCountry : groupedLink.toCountryCode,
+ links: groupedLink.links, //full array of individual links
+ totalCapacity: groupedLink.totalCapacity
+ });
+
linkPath.shadowPath = shadowPath
-
shadowPath.addListener('mouseover', function(event) {
- if (!map.allianceBasePopup) { //only do this if it is not hovered over base icon. This is a workaround as zIndex does not work - hovering over base icon triggers onmouseover event on the link below the icon
- $("#linkPopupFrom").html(getCountryFlagImg(this.fromCountry) + " " + this.fromAirport)
- $("#linkPopupTo").html(getCountryFlagImg(this.toCountry) + " " + this.toAirport)
- $("#linkPopupCapacity").html(this.capacity)
- $("#linkPopupAirline").html(getAirlineLogoImg(this.airlineId) + " " + this.airlineName)
+ if (!map.allianceBasePopup) {
+ var $popupContent = $("#linkPopup").clone();
+
+ $popupContent.find("#linkPopupFrom").html(getCountryFlagImg(this.fromCountry) + " " + this.fromAirport)
+ $popupContent.find("#linkPopupTo").html(getCountryFlagImg(this.toCountry) + " " + this.toAirport)
+ if (this.links.length > 1) {
+ var capacityBreakdown = this.links.map(function (l) {
+ return commaSeparateNumber(l.capacity.total);
+ }).join(' / ');
+ $popupContent.find("#linkPopupCapacity").html(commaSeparateNumber(this.totalCapacity) + " (" + capacityBreakdown + ")")
+ } else {
+ $popupContent.find("#linkPopupCapacity").html(commaSeparateNumber(this.totalCapacity))
+ }
+ var $airlineRowTemplate = $popupContent.find("#linkPopupAirline").closest(".table-row");
+ if (this.links.length > 1) {
+ $airlineRowTemplate.find(".label h5").text("Airlines:");
+ } else {
+ $airlineRowTemplate.find(".label h5").text("Airline:");
+ }
- var infowindow = new google.maps.InfoWindow({
- maxWidth : 1200});
+ var firstLink = this.links[0];
+ $airlineRowTemplate.find("#linkPopupAirline").html(getAirlineLogoImg(firstLink.airlineId) + " " + firstLink.airlineName);
- var popup = $("#linkPopup").clone()
- popup.show()
- infowindow.setContent(popup[0])
+ //add new rows for subsequent airlines
+ for (var i = 1; i < this.links.length; i++) {
+ var link = this.links[i];
+ var $newRow = $airlineRowTemplate.clone();
+ $newRow.find(".label").html("");
+ $newRow.find(".value").removeAttr('id').html(getAirlineLogoImg(link.airlineId) + " " + link.airlineName);
+ $airlineRowTemplate.parent().append($newRow);
+ }
- infowindow.setPosition(event.latLng);
- infowindow.open(map);
- map.allianceLinkPopup = infowindow
- }
- })
+ var infowindow = new google.maps.InfoWindow({ maxWidth : 1200});
+ $popupContent.show()
+ infowindow.setContent($popupContent[0])
+ infowindow.setPosition(event.latLng);
+ infowindow.open(map);
+ map.allianceLinkPopup = infowindow
+ }
+ })
shadowPath.addListener('mouseout', function(event) {
- closeAllianceLinkPopup()
+ closeAllianceLinkPopup()
})
-
+
linkPath.setMap(map)
linkPath.shadowPath.setMap(map)
polylines.push(linkPath)
polylines.push(linkPath.shadowPath)
- var resultPath = { path : linkPath, shadow : shadowPath } //kinda need this so it has consistent data structure as the normal flight paths
- return resultPath
+ var resultPath = { path : linkPath, shadow : shadowPath } //kinda need this so it has consistent data structure as the normal flight paths
+ return resultPath
}
function showAllianceMemberDetails(allianceMember) {