# ---- 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("