Emergencies can strike at any moment, underscoring the critical importance of effective emergency planning and response. In Vermont, as in any other region, having access to accurate and up-to-date information about emergency resources is paramount. Imagine being able to visualize the distribution of fire hydrants, fire stations, and police stations across the state with just a few clicks. This is precisely what my interactive map aims to achieve.
Using R programming and spatial analysis, my goal is to visualize Vermont’s emergency water supply. I prepare the dataset meticulously, ensuring each hydrant includes key attributes like addresses and hydrant types.
The main part of the document describes the mapping of the emergency water supply, starting with the conversion of hydrant data into spatial objects. It then creates an interactive map that displays hydrants with color-coded markers to differentiate types and provides pop-ups and hover labels for detailed information. Further customization allows for layer control, base tile options and a legend for easier interpretation.
To increase the usefulness of the map, the document also includes emergency services by mapping fire and police stations with custom icons and hover popups, making the map even more comprehensive. The base map is labeled with icons for each service type, and layer controls allow you to toggle the visibility of markers, providing a broader view of emergency preparedness resources.
We begin by loading the required R libraries:
# Step 1: Load Required Libraries
library(leaflet) # For interactive maps
library(sf) # For spatial data
library(readr) # For reading CSV files
library(here) # For file path management
library(tidyverse) # For data manipulation
library(RColorBrewer) # For color palettes
library(leaflet.extras)# Additional functionalities for Leaflet maps
library(osmdata) # Access OpenStreetMap data
library(magrittr) # For pipe operator
These libraries facilitate everything from interactive map creation to spatial data manipulation.
In the realm of spatial data analysis, I’ve learned that accurately representing geographic locations is absolutely crucial. I’ve faced a common challenge when latitude and longitude coordinates are mistakenly swapped, leading to incorrect data visualization on maps. Such errors can have a significant impact on the outcomes of spatial analyses and the decision-making processes that rely on these analyses.
This part of my analysis is dedicated to fixing a critical issue I encountered with our hydrants dataset: the coordinates of latitude and longitude were misplaced. Making sure these coordinates are correctly assigned is essential for accurate mapping and analysis. I start by loading the dataset, which has already been pre-processed to have all its column names in lowercase. However, the most critical step is correctly swapping the latitude and longitude values to reflect their true geographic positions.
I’ve outlined the following steps to systematically address this challenge. By the end of this process, I aim to have a dataset ready for precise spatial representation on a map. This effort is not just about correcting errors; it’s about establishing a solid foundation for robust spatial analysis, paving the way for better decisions and insights
In this step, we’re focusing on loading and preparing the dataset containing information about hydrants for spatial visualization. The dataset has undergone previous transformations regarding the coordinates, and our objective is to ensure the latitude and longitude are correctly assigned for accurate mapping.
# Step 2: Generate a Random Sample Dataset for Demonstration
# Load the dataset
hydrants_df <- read_csv(here("data", "transformed_coordinates_hydrants_vt_lowercase.csv")) %>%
rename_all(tolower) %>% # Ensure column names are in lowercase
mutate(
temp = latitude, # Create a temporary column to hold the current latitude values
latitude = longitude, # Assign the values of longitude to latitude
longitude = temp # Assign the values of the temporary column (original latitude) to longitude
) %>%
select(-temp) # Remove the temporary column
Loading the Dataset: Utilizes read_csv() from the readr package and here() to construct the file path, loading the hydrants dataset for processing.
Renaming Columns to Lowercase: Ensures that all column names are in lowercase to avoid case-sensitivity issues in R, enhancing consistency across the dataset.
Swapping Latitude and Longitude Values: Corrects the initial misplacement of latitude and longitude values by swapping them. This step is crucial for the accurate spatial representation of hydrants’ locations on a map.
Cleaning Up: Removes the temporary column created during the swapping process, leaving a clean dataset ready for spatial analysis or visualization.
By ensuring accurate assignment of geographic coordinates, this step lays the groundwork for successful spatial representation and analysis of the hydrants’ locations in subsequent mapping efforts.
The hydrant data is then converted to a spatial object, preserving all original columns:
When I first turned my data frame into an sf (simple features) object in R with st_as_sf(), I didn’t realize it would combine my “longitude” and “latitude” columns into a special geometry column. As a newbie to R, I was puzzled to see my original columns disappear—they were absorbed into this geometry column that R uses for all the spatial stuff, like mapping and spatial analysis.
To keep those “longitude” and “latitude” values accessible for other tasks, like adding details to maps, I figured out I needed to add them back into the sf object from my original data. It was a bit of a workaround, but it let me keep the spatial info in the geometry column for any mapping I wanted to do, while also having my original “longitude” and “latitude” data available for everything else.
# Step 3: Convert Data to sf Object While Preserving Columns
# Convert the DataFrame to a spatial sf object, specifying latitude and longitude for spatial data
hydrants_sf <- hydrants_df %>%
st_as_sf(coords = c("longitude", "latitude"), crs = 4326) %>%
mutate(
longitude = hydrants_df$longitude, # Add longitude column
latitude = hydrants_df$latitude # Add latitude column
)
# Step 3a: Define Hydrant Types and Create Popup Content
# Adding popup content for each hydrant marker on the map
hydrants_sf <- hydrants_sf %>%
mutate(
popup_content = paste("<strong>Hydrant ID:</strong>", hydrantid,
"<br><strong>Address:</strong>", primaryaddress,
"<br><strong>Town:</strong>", townname,
"<br><strong>Type:</strong>", hydranttype,
"<br><strong>Age:</strong>", hydrantage,
"<br><strong>Flow Rates:</strong>", flowratemin, "-", flowratemax, "gpm")
)
I initialize the map and add base tiles, followed by hydrant markers with popups and hover labels:
# Step 4: Define Hydrant Types and Colors
hydrant_colors <- c("Municipal Hydrant" = "#0047AB",
"Dry Hydrant" = "#3E92CC",
"Private Hydrant" = "#85C1E9",
"Pressurized Hydrant" = "#AED6F1",
"Drafting Site" = "#D6EAF8")
pal <- colorFactor(palette = hydrant_colors, domain = hydrants_sf$hydranttype)
# Extract coordinates from the sf object
coords <- st_coordinates(hydrants_sf)
# Calculate mean longitude and latitude
mean_lon <- mean(coords[, 'X'], na.rm = TRUE)
mean_lat <- mean(coords[, 'Y'], na.rm = TRUE)
# Step 5: Initialize Map and Add Base Tiles with Vermont Centering
map <- leaflet() %>%
setView(lng = -72.5754, lat = 44.2601, zoom = 12)
# Add base tiles
map <- map %>%
addProviderTiles(providers$OpenStreetMap) %>%
addProviderTiles(providers$CartoDB.Positron, group = "CartoDB") %>%
addProviderTiles(providers$Esri.WorldImagery, group = "ESRI")
# Step 6: Add Marker Clusters with Colored Markers and Hover Labels
map <- map %>%
addCircleMarkers(
data = hydrants_sf,
popup = ~popup_content,
label = ~primaryaddress,
labelOptions = labelOptions(noHide = TRUE, direction = 'auto'),
radius = 8,
color = ~pal(hydranttype),
clusterOptions = markerClusterOptions()
)
This section demonstrates how to display hydrants on the map, using color-coding to differentiate between types.
I enhance the map with layers control and a legend:
# Step 7: Add Layers Control
map <- map %>%
addLayersControl(
baseGroups = c("OpenStreetMap", "CartoDB", "ESRI"),
overlayGroups = c("Hydrant Markers"),
options = layersControlOptions(collapsed = FALSE)
)
# Step 8: Add Legend
map <- map %>%
addLegend(position = "bottomright",
pal = pal,
values = hydrants_sf$hydranttype,
title = "Hydrant Type",
opacity = 1)
# Display the final map
map
As the “Arcitech” of this interactive hydrant map, I used the power of R to convert hydrant data into a spatial sf (simple features) object that simplifies spatial analysis and mapping. This conversion seamlessly integrates the latitude and longitude information into a special geometry column that is critical for spatial operations. I categorized the hydrants into different types and assigned them unique colors to make them easy to identify on the map. Each hydrant marker displays an informative popup content, including the main address. Further details such as hydrant ID, type, age and flow rates can be added if the project requires it.
To increase interactivity, you can choose between different base tile providers such as OpenStreetMap, CartoDB and ESRI and customize the map background to your liking. Hydrant locations are marked with colored circles that correspond to specific hydrant types, and important addresses are displayed with hover labels. The clustering of markers provides a clear map in densely populated areas. A legend helps the user to interpret the colors of the hydrant markers and understand the distribution of hydrant types on the map.
In summary, this interactive hydrant map serves various purposes, including emergency planning, infrastructure management and spatial analysis of water supply systems. The user-friendly interface and comprehensive data presentation make it an invaluable resource for stakeholders seeking detailed insights into hydrant locations and characteristics.
For a broader view of emergency preparedness, we include fire and police stations on the map:
In this part of my project, I took the initiative to manually input data for fire and police stations to make our spatial dataset more comprehensive. This effort was crucial for ensuring we have a detailed picture of emergency services across the area of interest. Unfortunately, I couldn’t find a ready-made CSV file or an accessible URL/HTTP source that would allow me to fetch the geographical coordinates needed, compelling me to craft a workaround. By detailing the R code used to compile data frames for these critical services, including their names, addresses, and locations, I aimed to fill this gap and enhance our analysis.
I began by manually compiling data for both fire and police stations due to the absence of readily available datasets or URLs for direct data fetching. This meticulous process involved gathering names, addresses, and coordinates, crucial for mapping emergency services.
For the detailed compilation of emergency services, I focused on gathering comprehensive data for both fire and police stations across Vermont. For fire stations, the search led me to a valuable resource on Bing Maps, providing exact locations and coordinates necessary for accurate mapping. The fire station listings can be accessed here.
With the additional link provided, my project now includes a similarly detailed resource for police stations in Vermont, enhancing the completeness and accuracy of my emergency services mapping. This inclusion marks a significant step forward in ensuring that both fire and police stations are accurately represented, facilitating better emergency response planning and community safety initiatives. The police station listings, resulting from an exhaustive search on Bing, can be explored here.
This effort to collect and organize data on emergency services is not just about ensuring accuracy in mapping; it’s about laying the groundwork for more efficient emergency response and enhancing the safety of communities throughout Vermont.
# Step 9: Sample data for fire stations
# Sample data for fire stations
fire_stations <- data.frame(
name = c("Randolph Village Fire Department", "Northfield Fire Department", "Barre City Fire Department", "Waterbury Fire Department", "Roxbury Volunteer Fire Department", "Waitsfield Fayston Fire Department", "Burlington Fire Department", "Berlin Fire Department", "Pittsford (VT) Fire Department", "Brookfield Volunteer Fire Department", "East Montpelier Fire Department", "Warren Volunteer Fire Department", "Bradford Fire Department", "Washington Volunteer Fire Department", "Salisbury Fire Department", "Richmond Fire Department", "Department of Public Safety Division-Fire", "Vershire Volunteer Fire Department"),
address = c("2 Central St, Randolph 05060", "128 Wall St, Northfield 05663", "15 4th St Ste 1, Barre 05641", "43 S Main St, Waterbury 05676", "1726 Roxbury Rd, Roxbury 05669", "4103 Main St, Waitsfield 05673", "136 S Winooski Ave, Burlington 05401", "338 Paine Tpke N, Berlin 05602", "152 Pleasant St, Pittsford 05763", "56 Fire Department Lane, Brookfield 05036", "54 Village Acrs, East Montpelier 05651", "331 Main St, Warren 05674", "135 Carson Lane, Bradford 05033", "51 Firehouse Ln, Washington 05675", "PO Box 22, Salisbury 05769", "357 E Main St, Richmond 05477", "5 Perry St, Barre 05641", "9111 Vt Route 113, Vershire 05079"),
lat = c(44.1131, 44.1447, 44.1991, 44.3352, 44.1374, 44.1845, 44.4779, 44.2762, 43.7068, 44.0116, 44.2782, 44.1019, 43.9877, 44.1575, 43.9399, 44.4074, 44.2032, 44.2027),
lon = c(-72.6605, -72.6601, -72.5051, -72.7562, -72.7168, -72.8311, -73.2144, -72.5977, -72.8619)
)
# Sample data for police stations
police_stations <- data.frame(
name = c("Northfield Police Department", "Barre Town Police Department", "Barre City Police Department", "Randolph Police Department", "Hartford Town Police Department", "Vermont State Police", "Brandon Town Police Department", "Berlin Police Department", "Vermont State Police", "Department of Public Safety", "Vermont State Police", "Thetford Police Department"),
address = c("110 Wall St, Northfield 05663", "149 Websterville Rd, Websterville 05678", "15 4th St, Barre 05641", "6 Salisbury St ~ Drawer B, Randolph 05060", "812 Va Cutoff Rd, White River Junction 05001", "2490 Ethan Allen Hwy, New Haven 05472", "301 Foretdale Road, Brandon 05733", "108 Shed Rd, Berlin 05602", "45 State Dr Ste 1300, Waterbury 05671", "45 State Dr, Waterbury 05676", "35 Crawford Rd, Newport 05855", "3910 Rte 113, Thetford Center 05075"),
lat = c(44.1495, 44.1719, 44.1998, 43.9235, 43.6428, 44.1411, 43.8029, 44.2255, 44.3694, 44.3525, 44.9496, 43.7891),
lon = c(-72.6566, -72.4469, -72.5077, -72.6677, -72.3737, -72.9393, -73.0589, -72.6033, -72.7787, -72.7529, -72.1983, -72.2327)
)
Utilizing the leaflet package, I initialized a base map with OpenStreetMap tiles, setting the view based on the mean coordinates of the locations in our dataset, ensuring a comprehensive view of the region.
# Step 10: Initialize base map with OpenStreetMap tiles
base_map <- leaflet() %>%
addTiles() %>%
setView(lng = mean(c(-73.454162, -71.465281)), lat = mean(c(42.722789, 45.018361)), zoom = 7)
To distinguish between the types of emergency services, I customized icons for both fire stations and police stations using the makeAwesomeIcon function from the leaflet package, assigning distinct colors and symbols to each.
# Step 11: Customize icons for fire stations and police stations
fire_station_icon <- makeAwesomeIcon(icon = 'fire', markerColor = 'red', iconColor = 'white', library = 'fa')
police_station_icon <- makeAwesomeIcon(icon = 'shield', markerColor = 'blue', iconColor = 'white', library = 'fa')
I added markers for each fire and police station to the base map. This involved using the addAwesomeMarkers function, ensuring each marker was placed correctly according to its coordinates and was accompanied by a popup displaying the name of the station.
# Step 12: Adding Markers to the Map:
# Add markers for fire stations with hover popups
fire_layer <- addAwesomeMarkers(base_map, lng = ~lon, lat = ~lat, data = fire_stations, icon = fire_station_icon,
popup = ~name, options = popupOptions(closeButton = FALSE), group = "Fire Stations")
# Add markers for police stations with hover popups
police_layer <- addAwesomeMarkers(fire_layer, lng = ~lon, lat = ~lat, data = police_stations, icon = police_station_icon,
popup = ~name, options = popupOptions(closeButton = FALSE), group = "Police Stations")
For clarity and user interaction, I included a legend to differentiate between fire and police stations and added a layers control to the map. This allows users to toggle the visibility of markers based on the type of emergency service.
# Add a legend for fire and police stations
emergency_service_map <- addLegend(police_layer, position = "bottomright",
colors = c("red", "blue"),
labels = c("Fire Station", "Police Station"),
title = "Emergency Services")
# Add layers control to toggle the visibility of fire and police station markers
emergency_service_map <- addLayersControl(
emergency_service_map,
overlayGroups = c("Fire Stations", "Police Stations"),
options = layersControlOptions(collapsed = FALSE)
)
# Display the base map
emergency_service_map
This map visually depicts the distribution of fire stations and police departments across Vermont. Fire stations are marked with red icons, while police stations are denoted with blue icons. Users have the flexibility to toggle the visibility of these markers through the layers control, providing a clear overview of emergency service locations. By integrating these crucial resources into our spatial analysis, the project bridges gaps in available data and enhances our understanding for more effective emergency response planning and community safety measures.
This project leveraged R’s spatial analysis capabilities to construct an interactive map detailing emergency resources across Vermont, including hydrants, fire, and police stations. It filled informational gaps with manually compiled data, underlining the importance of accessible emergency data for effective planning and response. Custom markers and layers provided a detailed emergency preparedness overview, emphasizing spatial analysis’s critical role in enhancing public safety and response strategies. This initiative showcases how geospatial technologies can be instrumental in improving emergency preparedness and public safety measures.
Working through the Leaflet Map part B assignment felt like climbing a mountain with flip-flops—challenging, sometimes frustrating, but definitely unforgettable… all because I wanted to use my own data. I started off following the examples to a T, hoping for smooth sailing. Instead, I hit a storm of data cleaning and coding issues. Initially, when I saw that there were no latitude and longitude columns, I thought I could just go to the metadata section and get the CRS, but all that was there were bounding boxes.
In my quest to resolve this, I needed to identify the bounding box coordinates provided for Vermont. These typically represent the minimum and maximum longitudes and latitudes of the area. Vermont is part of the United States, so common CRS used here include State Plane Coordinate Systems (SPCS) or Universal Transverse Mercator (UTM) projections. Vermont specifically often employs the Vermont State Plane Coordinate System (SPCS).
From that point, I realized that the missing coordinate reference system (CRS) was the real culprit. Realizing this was like finding out I’d been hiking in the wrong direction. I had to backtrack and re-project everything into the Vermont State Plane Coordinate System (SPCS) to make it work with Leaflet.
Transforming the data frame to play nice with spatial formats was another puzzle. I kept accidentally deleting or duplicating the latitude and longitude columns, which meant hitting the reset button more times than I’d like to admit.
And don’t get me started on adding markers and basemaps to the map. Trying to get those to show up together was like trying to mix oil and water—lots of trial and error and a fair bit of head-scratching. But I didn’t throw in the towel. Instead, I buckled down, decided to sketch out a game plan, and wrote myself a step-by-step guide to make sense of the chaos.
Tackling these challenges was no walk in the park. I dug into debugging, poured over documentation, and scoured the internet for answers. Each bump in the road was a lesson in disguise, pushing me to level up my GIS and spatial data analysis skills. This whole ordeal turned out to be a testament to sticking it out and finding ways through the mess, relying on both my own stubbornness and the occasional lifeline from the online community.
After what felt like endless hours and maybe too much coffee, I finally got to see my map come to life. Sure, the final product might look simple to an outsider, but the sweat equity that went into it was real—talking marathon coding sessions that stretched well into the night. The sheer mental gymnastics of it all was draining, but man, was it worth it. This project really put me through my paces, testing not just my technical chops but my grit and gumption too. And you know what? I’m pretty darn proud of pulling through. It’s one of those experiences that really shows you what you’re made of.
Microsoft. (2024, March 22). Bing Vermont Police Stations. Retrieved from https://www.bing.com/search?q=bing+vermont+police+stations
Microsoft. (2024, March 22). Fire Departments in Vermont. Retrieved from https://www.bing.com/search?q=fire+departments+in+vermont
CRAN. (n.d.). osmdata: Import ‘OpenStreetMap’ Data as Simple Features or Spatial Objects. Retrieved from https://cran.r-project.org/web/packages/osmdata/index.html
CRAN. (n.d.). readr: Read Rectangular Text Data. Retrieved from https://cran.r-project.org/web/packages/readr/index.html
Leaflet. (n.d.). Leaflet - a JavaScript library for interactive maps. Retrieved from https://leafletjs.com/
R-lib. (n.d.). here: A Simpler Way to Find Your Files. Retrieved from https://here.r-lib.org/
R-Spatial. (n.d.). leafpop. Retrieved from https://github.com/r-spatial/leafpop
Earth Data Science. (n.d.). Make Interactive Maps with Leaflet in R. Retrieved from https://www.earthdatascience.org/courses/earth-analytics/spatial-data-r/make-interactive-maps-with-leaflet-r/
Johnson, M. (n.d.). Leaflet Hospitals. Retrieved from https://mikejohnson51.github.io/leaflet-intro/hospitals.html
Johnson, M. (n.d.). Leaflet Intro. Retrieved from https://mikejohnson51.github.io/leaflet-intro/index.html
Moraga, P. (n.d.). Chapter 3: The sf package for spatial vector data. Retrieved from https://www.paulamoraga.com/book-spatial/the-sf-package-for-spatial-vector-data.html
Pennsylvania State University. (n.d.). Canvas Assignment: Leaflet Tutorial. Retrieved from https://psu.instructure.com/courses/2292951/assignments/15938370?module_item_id=41079710
R-charts. (n.d.). Interactive maps with leaflet in R guide. Retrieved from https://r-charts.com/spatial/interactive-maps-leaflet/
SitePoint. (n.d.). Leaflet: Create a Map - A Beginner’s Guide. Retrieved from https://www.sitepoint.com/leaflet-create-map-beginner-guide/
Smith, A. (2021, October 15). Getting Started with Leaflet in R [Video]. YouTube. Retrieved from https://www.youtube.com/watch?v=vewnx2QRflQ