# ---- Load Packages ---- if (!require("pacman")) install.packages("pacman") pacman::p_load(tidyverse, sf, leaflet, htmltools, htmlwidgets, viridis, geosphere) # ---- Set File Paths ---- base_dir <- "E:/All Project/R Project" bangladesh_shp_path <- file.path(base_dir, "Shapefile/Bangladesh/bgd_admbnda_adm1_bbs_20201113.shp") airport_file <- file.path(base_dir, "Data/airports.dat") routes_file <- file.path(base_dir, "Data/routes.dat") output_html <- file.path(base_dir, "Shapefile/Output/bangladesh_flight_network_hover_final.html") # ---- Load and Prepare Data ---- bangladesh <- st_read(bangladesh_shp_path) %>% st_transform(4326) airport <- read.csv(airport_file, header = FALSE) colnames(airport)[1:14] <- c("ID", "Name", "City", "Country", "IATA", "ICAO", "Latitude", "Longitude", "Altitude", "Timezone", "DST", "Tz", "Type", "Source") bd_airports <- airport %>% filter(Country == "Bangladesh", IATA != "\\N") %>% distinct(IATA, .keep_all = TRUE) routes <- read.csv(routes_file, header = FALSE) colnames(routes) <- c("Airline", "Airline_ID", "Source_Airport", "Source_ID", "Destination_Airport", "Destination_ID", "Codeshare", "Stops", "Equipment") intl_routes <- routes %>% filter(Source_Airport %in% bd_airports$IATA, !Destination_Airport %in% bd_airports$IATA, Stops == "0") %>% distinct(Source_Airport, Destination_Airport, .keep_all = TRUE) dest_airports <- airport %>% filter(IATA %in% intl_routes$Destination_Airport) bd_airports_with_flights <- bd_airports %>% filter(IATA %in% intl_routes$Source_Airport) # ---- Create Curved Lines ---- create_curve_safe <- function(origin_lon, origin_lat, dest_lon, dest_lat) { tryCatch({ coords <- gcIntermediate( c(origin_lon, origin_lat), c(dest_lon, dest_lat), n = 50, addStartEnd = TRUE, breakAtDateLine = TRUE ) if (!is.null(coords)) st_linestring(coords) else NULL }, error = function(e) NULL) } flight_lines <- intl_routes %>% left_join(bd_airports, by = c("Source_Airport" = "IATA")) %>% left_join(dest_airports, by = c("Destination_Airport" = "IATA")) %>% mutate( flight_geom = pmap(list(Longitude.x, Latitude.x, Longitude.y, Latitude.y), create_curve_safe), geom_valid = map_lgl(flight_geom, Negate(is.null)) ) %>% filter(geom_valid) flight_lines_sf <- st_sf(flight_lines, geometry = flight_lines$flight_geom, crs = 4326) flight_lines_sf$id <- paste0("line", seq_len(nrow(flight_lines_sf))) # ---- Color Palette ---- bd_pal <- colorFactor(viridis(6), domain = bd_airports_with_flights$IATA) # ---- Build Leaflet Map ---- m <- leaflet(options = leafletOptions(minZoom = 3, maxZoom = 8)) %>% addProviderTiles(providers$CartoDB.DarkMatter, group = "Dark") %>% addProviderTiles(providers$Esri.WorldImagery, group = "Satellite") %>% addPolygons( data = bangladesh, color = "#4ecdc4", weight = 1, fillColor = "#1a1a1a", fillOpacity = 0.3, group = "Bangladesh", popup = "Bangladesh" ) %>% # Invisible wider lines under flight lines to increase hover area addPolylines( data = flight_lines_sf, color = "transparent", weight = 15, opacity = 0, group = "Flights Hover", layerId = ~paste0("hover_", id) ) %>% # Flight lines visible, dashed style and layerId addPolylines( data = flight_lines_sf, color = ~bd_pal(Source_Airport), weight = 2, opacity = 0.8, dashArray = "5, 3", group = "Flights", layerId = ~id, popup = ~paste0( "Route: ", Source_Airport, " → ", Destination_Airport, "
", "From: ", Name.x, " (", City.x, ")
", "To: ", Name.y, " (", City.y, ", ", Country.y, ")" ) ) %>% # Bangladesh airports with radius increased, but use bindTooltip for hover labels (sticky + no flicker) addCircleMarkers( data = bd_airports_with_flights, lng = ~Longitude, lat = ~Latitude, radius = 8, color = ~bd_pal(IATA), fillColor = ~bd_pal(IATA), fillOpacity = 1, popup = ~paste0("", Name, "
", City), group = "Bangladesh Airports", layerId = ~IATA ) %>% addCircleMarkers( data = bd_airports_with_flights, lng = ~Longitude, lat = ~Latitude, radius = 8, color = "transparent", fillOpacity = 0, # invisible layer for hover tooltip label = ~paste0(Name, " (", City, ")"), labelOptions = labelOptions( direction = "top", style = list("color" = "white", "background" = "black", "padding" = "3px"), textsize = "12px", sticky = TRUE, opacity = 0.9 ), group = "Bangladesh Airports Hover" ) %>% # Destination airports same approach addCircleMarkers( data = dest_airports, lng = ~Longitude, lat = ~Latitude, radius = 7, color = "#ff7f00", fillColor = "#ff7f00", fillOpacity = 0.8, popup = ~paste0("", Name, "
", City, ", ", Country), group = "Destinations", layerId = ~IATA ) %>% addCircleMarkers( data = dest_airports, lng = ~Longitude, lat = ~Latitude, radius = 7, color = "transparent", fillOpacity = 0, label = ~paste0(Name, " (", City, ")"), labelOptions = labelOptions( direction = "top", style = list("color" = "white", "background" = "black", "padding" = "3px"), textsize = "12px", sticky = TRUE, opacity = 0.9 ), group = "Destinations Hover" ) %>% # Layers and controls (include hover label groups) addLayersControl( baseGroups = c("Dark", "Satellite"), overlayGroups = c("Bangladesh", "Flights", "Flights Hover", "Bangladesh Airports", "Bangladesh Airports Hover", "Destinations", "Destinations Hover"), options = layersControlOptions(collapsed = FALSE) ) %>% addScaleBar(position = "bottomleft") %>% # Title at top center - bold single line addControl( html = HTML("
Bangladesh International Flight Network
"), position = 'topleft' ) %>% # Legend for BD airports with full names addLegend( position = "bottomright", colors = bd_pal(bd_airports_with_flights$IATA), labels = bd_airports_with_flights$Name, title = "Bangladeshi Airports (Source)", opacity = 1 ) %>% # Add your embedded Flight Radar iframe at bottom left as a control addControl( html = HTML('
'), position = "bottomleft" ) %>% setView(lng = 90, lat = 23.5, zoom = 4) # ---- Inject JavaScript for hover effects on flight lines ---- m <- htmlwidgets::onRender(m, " function(el, x) { var map = this; // Store original styles for visible lines map.eachLayer(function(layer) { if(layer.options && layer.options.layerId && layer.options.layerId.startsWith('line')) { layer._originalStyle = { color: layer.options.color, weight: layer.options.weight, dashArray: layer.options.dashArray, opacity: layer.options.opacity }; } }); // Hover handlers for invisible wide lines map.eachLayer(function(layer) { if(layer.options && layer.options.layerId && layer.options.layerId.startsWith('hover_line')) { var lineId = layer.options.layerId.replace('hover_', ''); var visibleLine = null; map.eachLayer(function(l) { if(l.options && l.options.layerId === lineId) { visibleLine = l; } }); if(visibleLine) { layer.on('mouseover', function(e) { visibleLine.setStyle({ color: 'deepskyblue', weight: 4, dashArray: '5,3', opacity: 1 }); visibleLine.openPopup(); }); layer.on('mouseout', function(e) { var orig = visibleLine._originalStyle; visibleLine.setStyle({ color: orig.color, weight: orig.weight, dashArray: orig.dashArray, opacity: orig.opacity }); visibleLine.closePopup(); }); } } }); } ") # ---- Save & View Map ---- htmlwidgets::saveWidget( widget = m, file = output_html, selfcontained = TRUE, title = "Bangladesh International Flights", background = "black" ) browseURL(output_html)