# Load necessary libraries
pacman::p_load(pacman, websocket, jsonlite, later, logging, lubridate, ggplot2, maps, gridExtra)
# Initialise earthquake data
quake_data <- data.frame(
mag = numeric(),
location = character(),
timestamp = character(),
longitude = numeric(),
latitude = numeric(),
depth = numeric(),
stringsAsFactors = FALSE
)
# Function to update the map and table
update_map_and_table <- function(data) {
# Plot world map
world_map <- map_data("world")
p1 <- ggplot() +
geom_polygon(data = world_map, aes(x = long, y = lat, group = group), fill = "#cccccc", color = "#000000") +
geom_point(data = data, aes(x = longitude, y = latitude, color = mag, size = mag), alpha = 0.7) +
scale_color_gradient(low = "#ffff99", high = "#cc0033") +
scale_size(range = c(2, 5)) +
labs(title = "Earthquakes around the World", x = "Longitude", y = "Latitude") +
theme_minimal()
# Create the table of earthquakes with depth information
quake_table <- data[, c("location", "mag", "timestamp", "depth")]
table_grob <- tableGrob(quake_table, rows = NULL)
# Plot both the map and the table side by side
grid.arrange(p1, table_grob, ncol = 2, widths = c(3/5, 2/5))
}
# Set up WebSocket connection
ws <- WebSocket$new("wss://www.seismicportal.eu/standing_order/websocket")
# WebSocket message handler
ws$onMessage(function(event) {
message <- event$data
data <- fromJSON(message)
# Print the structure of incoming data (for debugging purposes)
print_data_structure(data)
# Extract relevant earthquake data, with safety checks
mag <- ifelse(!is.null(data$data$properties$mag), data$data$properties$mag, NA)
location <- ifelse(!is.null(data$data$properties$flynn_region), data$data$properties$flynn_region, "Unknown")
time <- ifelse(!is.null(data$data$properties$time), data$data$properties$time, NA)
timestamp <- if (!is.na(time)) {
as.character(lubridate::ymd_hms(time, tz = "UTC"))
} else {
"NA"
}
longitude <- ifelse(!is.null(data$data$geometry$coordinates) && length(data$data$geometry$coordinates) >= 1, data$data$geometry$coordinates[1], NA)
latitude <- ifelse(!is.null(data$data$geometry$coordinates) && length(data$data$geometry$coordinates) >= 2, data$data$geometry$coordinates[2], NA)
# Extract depth
depth <- ifelse(!is.null(data$data$properties$depth), data$data$properties$depth, NA)
# Create a new data frame for this quake event
new_quake_data <- data.frame(
mag = mag,
location = location,
timestamp = timestamp,
longitude = longitude,
latitude = latitude,
depth = depth,
stringsAsFactors = FALSE
)
# Ensure the column names and types match those of quake_data
if (all(colnames(new_quake_data) == colnames(quake_data))) {
# Add new data to quake_data
quake_data <<- rbind(quake_data, new_quake_data)
# Update map and table
update_map_and_table(quake_data)
} else {
cat("Error: Incoming data does not match the structure of quake_data\n")
}
})
ws$onOpen(function(event) {
cat("WebSocket connection opened\n")
})
ws$onClose(function(event) {
cat("WebSocket connection closed\n")
})
ws$onError(function(event) {
cat("WebSocket error: ", event$message, "\n")
})
# Function to print the structure of incoming data
print_data_structure <- function(data) {
cat("Structure of incoming data:\n")
print(str(data))
}