Public
Edited
May 7, 2024
Insert cell
Insert cell
Insert cell
<svg width="2200" height="1350" style="background-color: black;">
<style>
/* edit these styles as you need */
rect {stroke: white; stroke-width: 2px;}
text {fill: white; font-family: 'Rajdhani', sans-serif;}
.maintitle {font-size: 39px; fill: #BDBDBD;}
.mainsubtitle {font-size: 18px; fill: #BDBDBD;}
.title {text-anchor: end; font-size: 32px;}
.subtext1 {text-anchor: end; font-size: 17px;}
.subtext2, .subtext3 {}
.footprint {font-size: 17px;}
line {stroke: white; stroke-width: 2px;}
</style>

<text x="5" y="35" class="maintitle">Crimes in Charlottesville and the Nurse-Commuters at Risk</text>
<text x="5" y="55" class="mainsubtitle">Frequent & Extreme crimes committed between 05/04/2019 and 01/04/2024 </text>

</svg>
Insert cell
{
let landguzzlersViz = d3.select(svgContainer); // select the cell called svgContainer and make it a d3 object.

// Define the scale for x-axis (longitude)
const xScale = d3.scaleLinear()
.domain([-78.53, -78.44]) // Adjusted domain for longitude to zoom out slightly
.range([-100, 2200]);

// Define the scale for y-axis (latitude)
const yScale = d3.scaleLinear()
.domain([38.00, 38.07]) // Adjusted domain for latitude to zoom in slightly
.range([1350, 0]); // Reverse the range to flip over the x-axis


// Define color scale based on a specific column in your dataset
const colorScale = d3.scaleOrdinal()
.domain(crimedata.map(d => d.Offense)) // Assuming columnToReflect is the column you want to use for coloring
.range(d3.schemeCategory10); // Or any other color scheme you prefer

landguzzlersViz.selectAll("circle")
.data(crimedata)
.enter()
.append("circle")
.attr("cx", d => xScale(d.longitude)) // Position based on longitude
.attr("cy", d => yScale(d.latitude)) // Position based on latitude, flipped over x-axis
.attr("r", 5) // Radius of the circle
.style("fill", d => colorScale(d.Offense)); // Color of the circle based on the specified column

//////////////////// LEGEND FOR OFFENSE /////////////////////////////////////////////////////

// Append a group element for the legend
const legend = landguzzlersViz.append("g")
.attr("class", "legend")
.attr("transform", "translate(15, 100)"); // Adjust position as needed

// Define unique offenses
const uniqueOffenses = Array.from(new Set(crimedata.map(d => d.Offense)));

// Append rectangles and labels for each offense
legend.selectAll(".legend-item")
.data(uniqueOffenses)
.enter().append("g")
.attr("class", "legend-item")
.attr("transform", (d, i) => "translate(0," + (i * 20) + ")") // Adjust spacing between items
.each(function (d) {
const g = d3.select(this);
g.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 10) // Adjust size of the rectangle
.attr("height", 10) // Adjust size of the rectangle
.style("fill", colorScale(d));

g.append("text")
.attr("x", 15) // Adjust position of the label
.attr("y", 5) // Adjust position of the label
.attr("dy", "0.7em")
.style("fill", "white") // Color of the label
.text(d);
});

/////////////////// LABELING CVILLE NEIGHBORHOODS //////////////////////////////////////////

// Append label for "downtown"
landguzzlersViz.append("text")
.attr("x", xScale(-78.4767)) // Longitude of Rotunda
.attr("y", yScale(38.0293) - 15) // Latitude of Rotunda with slight offset for label
.text("Downtown") // Text label
.style("fill", "white") // Color of the label
.style("font-weight", "bold") // Bold font-weight
.style("font-size", "25px") // Font size of the label
.style("text-anchor", "middle"); // Anchor the label in the middle

// Append a circle for "hospital"
landguzzlersViz.append("circle")
.attr("cx", xScale(-78.4989)) // Longitude of hospital
.attr("cy", yScale(38.0312)) // Latitude of hospital
.attr("r", 7) // Larger radius for hospital
.style("fill", "#E57373") // Color of the circle for hospital
.style("stroke", "red"); // Border color of the circle for hospital
// Append a surrounding circle for "hospital"
landguzzlersViz.append("circle")
.attr("cx", xScale(-78.4989)) // Longitude of hospital
.attr("cy", yScale(38.0312)) // Latitude of hospital
.attr("r", 80) // Larger radius for hospital
.style("fill", "none") // Color of the circle for hospital
.style("stroke", "#E57373"); // Border color of the circle for hospital

// Append label for "hospital"
landguzzlersViz.append("text")
.attr("x", xScale(-78.4989)) // Longitude of hospital
.attr("y", yScale(38.0312) - 15) // Latitude of hospital with slight offset for label
.text("The Hospital") // Text label
.style("fill", "#E57373") // Color of the label
.style("font-size", "25px") // Font size of the label
.style("font-weight", "bold") // Bold font-weight
.style("text-anchor", "middle"); // Anchor the label in the middle

// Append a circle for "Home 1"
landguzzlersViz.append("circle")
.attr("cx", xScale(-78.4944033)) // Longitude of hospital
.attr("cy", yScale(38.0430252)) // Latitude of hospital
.attr("r", 7) // Larger radius for hospital
.style("fill", "#E57373") // Color of the circle for hospital
.style("stroke", "#E57373"); // Border color of the circle for hospital

// Append label for "Home 1"
landguzzlersViz.append("text")
.attr("x", xScale(-78.4944033)) // Longitude of hospital
.attr("y", yScale(38.0430252) - 15) // Latitude of hospital with slight offset for label
.text("Apartment 2022") // Text label
.style("fill", "#E57373") // Color of the label
.style("font-size", "25px") // Font size of the label
.style("font-weight", "bold") // Bold font-weight
.style("text-anchor", "middle"); // Anchor the label in the middle

// Append a circle for "Home 2"
landguzzlersViz.append("circle")
.attr("cx", xScale(-78.512216)) // Longitude of hospital
.attr("cy", yScale(38.0284118)) // Latitude of hospital
.attr("r", 7) // Larger radius for hospital
.style("fill", "#E57373") // Color of the circle for hospital
.style("stroke", "#E57373"); // Border color of the circle for hospital

// Append label for "Home 2"
landguzzlersViz.append("text")
.attr("x", xScale(-78.512216)) // Longitude of hospital
.attr("y", yScale(38.0284118) - 15) // Latitude of hospital with slight offset for label
.text("Apartment 2020") // Text label
.style("fill", "#E57373") // Color of the label
.style("font-size", "25px") // Font size of the label
.style("font-weight", "bold") // Bold font-weight
.style("text-anchor", "middle"); // Anchor the label in the middle

// Append a circle for "Home 3"
landguzzlersViz.append("circle")
.attr("cx", xScale(-78.4933017)) // Longitude of hospital
.attr("cy", yScale(38.0320438)) // Latitude of hospital
.attr("r", 7) // Larger radius for hospital
.style("fill", "#E57373") // Color of the circle for hospital
.style("stroke", "#E57373"); // Border color of the circle for hospital

// Append label for "Home 3"
landguzzlersViz.append("text")
.attr("x", xScale(-78.4933017)) // Longitude of hospital
.attr("y", yScale(38.0320438) - 15) // Latitude of hospital with slight offset for label
.text("Apartment 2018") // Text label
.style("fill", "#E57373") // Color of the label
.style("font-size", "25px") // Font size of the label
.style("font-weight", "bold") // Bold font-weight
.style("text-anchor", "middle"); // Anchor the label in the middle

// Append label for "barracks"
landguzzlersViz.append("text")
.attr("x", xScale(-78.5050)) // Longitude of hospital
.attr("y", yScale(38.0528) - 15) // Latitude of hospital with slight offset for label
.text("Barracks Road") // Text label
.style("fill", "white") // Color of the label
.style("font-weight", "bold") // Bold font-weight
.style("font-size", "25px") // Font size of the label
.style("text-anchor", "middle"); // Anchor the label in the middle

// Append label for "fry spring"
landguzzlersViz.append("text")
.attr("x", xScale(-78.510640)) // Longitude of hospital
.attr("y", yScale(38.014700) - 15) // Latitude of hospital with slight offset for label
.text("Fry's Spring") // Text label
.style("fill", "white") // Color of the label
.style("font-weight", "bold") // Bold font-weight
.style("font-size", "25px") // Font size of the label
.style("text-anchor", "middle"); // Anchor the label in the middle

// Append label for "fifeville"
landguzzlersViz.append("text")
.attr("x", xScale(-78.495830)) // Longitude of hospital
.attr("y", yScale(38.026180) - 15) // Latitude of hospital with slight offset for label
.text("Fifeville") // Text label
.style("fill", "white") // Color of the label
.style("font-weight", "bold") // Bold font-weight
.style("font-size", "25px") // Font size of the label
.style("text-anchor", "middle"); // Anchor the label in the middle

// Append label for "Belmont"
landguzzlersViz.append("text")
.attr("x", xScale(-78.473340)) // Longitude of hospital
.attr("y", yScale(38.022920) - 15) // Latitude of hospital with slight offset for label
.text("Belmont") // Text label
.style("fill", "white") // Color of the label
.style("font-weight", "bold") // Bold font-weight
.style("font-size", "25px") // Font size of the label
.style("text-anchor", "middle"); // Anchor the label in the middle

// Append label for "JPA"
landguzzlersViz.append("text")
.attr("x", xScale(-78.513837)) // Longitude of hospital
.attr("y", yScale(38.026459) - 15) // Latitude of hospital with slight offset for label
.text("JPA") // Text label
.style("fill", "white") // Color of the label
.style("font-weight", "bold") // Bold font-weight
.style("font-size", "25px") // Font size of the label
.style("text-anchor", "middle"); // Anchor the label in the middle

// Append label for "10th and Page"
landguzzlersViz.append("text")
.attr("x", xScale(-78.4908)) // Longitude of hospital
.attr("y", yScale(38.0357) - 15) // Latitude of hospital with slight offset for label
.text("10th and Page") // Text label
.style("fill", "white") // Color of the label
.style("font-weight", "bold") // Bold font-weight
.style("font-size", "25px") // Font size of the label
.style("text-anchor", "middle"); // Anchor the label in the middle

//////////////////////////////////// ADDING LINES BETWEEN HOSPITAL AND HOME //////////////////////////////////

// Define the line generator
const lineGenerator = d3.line()
.x(d => d.x)
.y(d => d.y);

// Data for lines (hospital to home)
const linesData = [
{ start: { x: xScale(-78.4989), y: yScale(38.0312) }, end: { x: xScale(-78.4944033), y: yScale(38.0430252) } }, // hospital to home 1
{ start: { x: xScale(-78.4989), y: yScale(38.0312) }, end: { x: xScale(-78.512216), y: yScale(38.0284118) } }, // hospital to home 2
{ start: { x: xScale(-78.4989), y: yScale(38.0312) }, end: { x: xScale(-78.4933017), y: yScale(38.0320438) } } // hospital to home 3
];

// Append lines
landguzzlersViz.selectAll(".line")
.data(linesData)
.enter().append("path")
.attr("class", "line")
.attr("d", d => lineGenerator([d.start, d.end]))
.attr("stroke", "#E57373") // Color of the lines
.attr("stroke-width", 2) // Width of the lines
.attr("fill", "none"); // No fill for the lines

/////////////////////////////////// FILTER BY OFFENSE DROPDOWN ///////////////////////////////////////////////

// Append title above foreign object for offense filter controls
landguzzlersViz.append("text")
.attr("x", 15) // X position of the title
.attr("y", 1060) // Y position of the title
.style("fill", "#BDBDBD")
.text("Offense Filter Controls"); // Text of the title

// Append foreign object for offense filter controls
const offenseFilterForeignObject = landguzzlersViz.append("foreignObject")
.attr("x", 15) // X position of the foreign object
.attr("y", 1075) // Y position of the foreign object
.attr("width", 200) // Width of the foreign object
.attr("height", 100); // Height of the foreign object

// Append div inside the foreign object for styling
const offenseFilterDiv = offenseFilterForeignObject.append("xhtml:div");

// Append select dropdown for offense filter
const offenseFilterSelect = offenseFilterDiv.append("xhtml:select")
.attr("id", "offense-filter-select")
.attr("class", "input-field");

// Append options to the select dropdown
offenseFilterSelect.selectAll("option")
.data([...new Set(crimedata.map(d => d.Offense))]) // Unique offenses from the dataset
.enter().append("xhtml:option")
.attr("value", d => d)
.text(d => d);

// Append Filter button inside the div
offenseFilterDiv.append("xhtml:button")
.attr("id", "offense-filter-button")
.text("Filter");

// Filter function for offenses
const filterByOffense = () => {
// Get the selected offense from the dropdown
const selectedOffense = document.getElementById("offense-filter-select").value;

// Filter the dataset based on the selected offense
const filteredData = crimedata.filter(d => d.Offense === selectedOffense);

// Remove existing circles
landguzzlersViz.selectAll("circle").remove();

// Re-draw circles with filtered data
landguzzlersViz.selectAll("circle")
.data(filteredData)
.enter()
.append("circle")
.attr("cx", d => xScale(d.longitude)) // Position based on longitude
.attr("cy", d => yScale(d.latitude)) // Position based on latitude, flipped over x-axis
.attr("r", 5) // Radius of the circle
.style("fill", colorScale(selectedOffense)); // Color of the circle based on the selected offense

// Append a circle for "hospital"
landguzzlersViz.append("circle")
.attr("cx", xScale(-78.4989)) // Longitude of hospital
.attr("cy", yScale(38.0312)) // Latitude of hospital
.attr("r", 7) // Larger radius for hospital
.style("fill", "#E57373") // Color of the circle for hospital
.style("stroke", "red"); // Border color of the circle for hospital
// Append a surrounding circle for "hospital"
landguzzlersViz.append("circle")
.attr("cx", xScale(-78.4989)) // Longitude of hospital
.attr("cy", yScale(38.0312)) // Latitude of hospital
.attr("r", 80) // Larger radius for hospital
.style("fill", "none") // Color of the circle for hospital
.style("stroke", "#E57373"); // Border color of the circle for hospital

// Append label for "hospital"
landguzzlersViz.append("text")
.attr("x", xScale(-78.4989)) // Longitude of hospital
.attr("y", yScale(38.0312) - 15) // Latitude of hospital with slight offset for label
.text("The Hospital") // Text label
.style("fill", "#E57373") // Color of the label
.style("font-size", "25px") // Font size of the label
.style("font-weight", "bold") // Bold font-weight
.style("text-anchor", "middle"); // Anchor the label in the middle

// Append a circle for "Home 1"
landguzzlersViz.append("circle")
.attr("cx", xScale(-78.4944033)) // Longitude of hospital
.attr("cy", yScale(38.0430252)) // Latitude of hospital
.attr("r", 7) // Larger radius for hospital
.style("fill", "#E57373") // Color of the circle for hospital
.style("stroke", "#E57373"); // Border color of the circle for hospital

// Append label for "Home 1"
landguzzlersViz.append("text")
.attr("x", xScale(-78.4944033)) // Longitude of hospital
.attr("y", yScale(38.0430252) - 15) // Latitude of hospital with slight offset for label
.text("Apartment 2022") // Text label
.style("fill", "#E57373") // Color of the label
.style("font-size", "25px") // Font size of the label
.style("font-weight", "bold") // Bold font-weight
.style("text-anchor", "middle"); // Anchor the label in the middle

// Append a circle for "Home 2"
landguzzlersViz.append("circle")
.attr("cx", xScale(-78.512216)) // Longitude of hospital
.attr("cy", yScale(38.0284118)) // Latitude of hospital
.attr("r", 7) // Larger radius for hospital
.style("fill", "#E57373") // Color of the circle for hospital
.style("stroke", "#E57373"); // Border color of the circle for hospital

// Append label for "Home 2"
landguzzlersViz.append("text")
.attr("x", xScale(-78.512216)) // Longitude of hospital
.attr("y", yScale(38.0284118) - 15) // Latitude of hospital with slight offset for label
.text("Apartment 2020") // Text label
.style("fill", "#E57373") // Color of the label
.style("font-size", "25px") // Font size of the label
.style("font-weight", "bold") // Bold font-weight
.style("text-anchor", "middle"); // Anchor the label in the middle

// Append a circle for "Home 3"
landguzzlersViz.append("circle")
.attr("cx", xScale(-78.4933017)) // Longitude of hospital
.attr("cy", yScale(38.0320438)) // Latitude of hospital
.attr("r", 7) // Larger radius for hospital
.style("fill", "#E57373") // Color of the circle for hospital
.style("stroke", "#E57373"); // Border color of the circle for hospital

// Append label for "Home 3"
landguzzlersViz.append("text")
.attr("x", xScale(-78.4933017)) // Longitude of hospital
.attr("y", yScale(38.0320438) - 15) // Latitude of hospital with slight offset for label
.text("Apartment 2018") // Text label
.style("fill", "#E57373") // Color of the label
.style("font-size", "25px") // Font size of the label
.style("font-weight", "bold") // Bold font-weight
.style("text-anchor", "middle"); // Anchor the label in the middle

};

// Event listener for the offense filter button
const offenseFilterButton = document.getElementById("offense-filter-button");
offenseFilterButton.addEventListener("click", filterByOffense);

/////////////////////////////////// HOUR FILTER BUTTON ///////////////////////////////////////////////////////

// Append label above foreign object for hour filter controls
landguzzlersViz.append("text")
.attr("x", 15) // X position of the label
.attr("y", 1140) // Y position of the label
.style("fill", "#BDBDBD")
.text("Hour Filter Controls"); // Text of the label

// Append foreign object for hour filter controls
const hourFilterForeignObject = landguzzlersViz.append("foreignObject")
.attr("x", 15) // X position of the foreign object
.attr("y", 1150) // Y position of the foreign object
.attr("width", 200) // Width of the foreign object
.attr("height", 100); // Height of the foreign object

// Append div inside the foreign object for styling
const hourFilterDiv = hourFilterForeignObject.append("xhtml:div");

// Append input box for start hour
hourFilterDiv.append("xhtml:input")
.attr("type", "number")
.attr("id", "start-hour-input")
.attr("class", "input-field")
.attr("placeholder", "Start Hour");

// Append input box for end hour
hourFilterDiv.append("xhtml:input")
.attr("type", "number")
.attr("id", "end-hour-input")
.attr("class", "input-field")
.attr("placeholder", "End Hour");

// Append Filter button inside the div
hourFilterDiv.append("xhtml:button")
.attr("id", "hour-filter-button")
.text("Filter");

const hourFilterButton = document.getElementById("hour-filter-button");
hourFilterButton.addEventListener("click", () => {
// Get the user input hour range
const startHour = parseInt(document.getElementById("start-hour-input").value);
const endHour = parseInt(document.getElementById("end-hour-input").value);

// Check if the input is valid
if (isNaN(startHour) || isNaN(endHour) || startHour > endHour) {
alert("Please enter a valid hour range.");
return;
}

// Filter the dataset based on the hour range
const filteredData = crimedata.filter(d => d['HourReported'] >= startHour && d['HourReported'] <= endHour);

// Remove existing circles
landguzzlersViz.selectAll("circle").remove();

// Re-draw circles with filtered data
landguzzlersViz.selectAll("circle")
.data(filteredData)
.enter()
.append("circle")
.attr("cx", d => xScale(d.longitude)) // Position based on longitude
.attr("cy", d => yScale(d.latitude)) // Position based on latitude, flipped over x-axis
.attr("r", 5) // Radius of the circle
.style("fill", d => colorScale(d.Offense)); // Color of the circle based on the specified column

// Append a circle for "hospital"
landguzzlersViz.append("circle")
.attr("cx", xScale(-78.4989)) // Longitude of hospital
.attr("cy", yScale(38.0312)) // Latitude of hospital
.attr("r", 7) // Larger radius for hospital
.style("fill", "#E57373") // Color of the circle for hospital
.style("stroke", "red"); // Border color of the circle for hospital
// Append a surrounding circle for "hospital"
landguzzlersViz.append("circle")
.attr("cx", xScale(-78.4989)) // Longitude of hospital
.attr("cy", yScale(38.0312)) // Latitude of hospital
.attr("r", 80) // Larger radius for hospital
.style("fill", "none") // Color of the circle for hospital
.style("stroke", "#E57373"); // Border color of the circle for hospital

// Append label for "hospital"
landguzzlersViz.append("text")
.attr("x", xScale(-78.4989)) // Longitude of hospital
.attr("y", yScale(38.0312) - 15) // Latitude of hospital with slight offset for label
.text("The Hospital") // Text label
.style("fill", "#E57373") // Color of the label
.style("font-size", "25px") // Font size of the label
.style("font-weight", "bold") // Bold font-weight
.style("text-anchor", "middle"); // Anchor the label in the middle

// Append a circle for "Home 1"
landguzzlersViz.append("circle")
.attr("cx", xScale(-78.4944033)) // Longitude of hospital
.attr("cy", yScale(38.0430252)) // Latitude of hospital
.attr("r", 7) // Larger radius for hospital
.style("fill", "#E57373") // Color of the circle for hospital
.style("stroke", "#E57373"); // Border color of the circle for hospital

// Append label for "Home 1"
landguzzlersViz.append("text")
.attr("x", xScale(-78.4944033)) // Longitude of hospital
.attr("y", yScale(38.0430252) - 15) // Latitude of hospital with slight offset for label
.text("Apartment 2022") // Text label
.style("fill", "#E57373") // Color of the label
.style("font-size", "25px") // Font size of the label
.style("font-weight", "bold") // Bold font-weight
.style("text-anchor", "middle"); // Anchor the label in the middle

// Append a circle for "Home 2"
landguzzlersViz.append("circle")
.attr("cx", xScale(-78.512216)) // Longitude of hospital
.attr("cy", yScale(38.0284118)) // Latitude of hospital
.attr("r", 7) // Larger radius for hospital
.style("fill", "#E57373") // Color of the circle for hospital
.style("stroke", "#E57373"); // Border color of the circle for hospital

// Append label for "Home 2"
landguzzlersViz.append("text")
.attr("x", xScale(-78.512216)) // Longitude of hospital
.attr("y", yScale(38.0284118) - 15) // Latitude of hospital with slight offset for label
.text("Apartment 2020") // Text label
.style("fill", "#E57373") // Color of the label
.style("font-size", "25px") // Font size of the label
.style("font-weight", "bold") // Bold font-weight
.style("text-anchor", "middle"); // Anchor the label in the middle

// Append a circle for "Home 3"
landguzzlersViz.append("circle")
.attr("cx", xScale(-78.4933017)) // Longitude of hospital
.attr("cy", yScale(38.0320438)) // Latitude of hospital
.attr("r", 7) // Larger radius for hospital
.style("fill", "#E57373") // Color of the circle for hospital
.style("stroke", "#E57373"); // Border color of the circle for hospital

// Append label for "Home 3"
landguzzlersViz.append("text")
.attr("x", xScale(-78.4933017)) // Longitude of hospital
.attr("y", yScale(38.0320438) - 15) // Latitude of hospital with slight offset for label
.text("Apartment 2018") // Text label
.style("fill", "#E57373") // Color of the label
.style("font-size", "25px") // Font size of the label
.style("font-weight", "bold") // Bold font-weight
.style("text-anchor", "middle"); // Anchor the label in the middle

});


///////////////////////////////////// ADDING CIRCLE FOR HOME LONG AND LAT ////////////////////////////////////////

// Append label above foreign object for longitude and latitude filter content
landguzzlersViz.append("text")
.attr("x", 15) // X position of the label
.attr("y", 1240) // Y position of the label
.style("fill", "#BDBDBD")
.text("Longitude and Latitude Filter Content"); // Text of the label
// Append foreign object for long and lat filter content
const foreignObject = landguzzlersViz.append("foreignObject")
.attr("x", 15) // X position of the foreign object
.attr("y", 1250) // Y position of the foreign object
.attr("width", 200) // Width of the foreign object
.attr("height", 100); // Height of the foreign object

// Append div inside the foreign object
const div = foreignObject.append("xhtml:div");

// Append Latitude input box
div.append("xhtml:input")
.attr("type", "number")
.attr("id", "latitude-input")
.attr("class", "input-field")
.attr("placeholder", "Latitude");

// Append Longitude input box
div.append("xhtml:input")
.attr("type", "number")
.attr("id", "longitude-input")
.attr("class", "input-field")
.attr("placeholder", "Longitude");

// Append Update button
div.append("xhtml:button")
.attr("id", "update-button")
.text("Update");

const latitudeInput = document.getElementById("latitude-input");
const longitudeInput = document.getElementById("longitude-input");
const updateButton = document.getElementById("update-button");

updateButton.addEventListener("click", () => {
// Get the user input coordinates
const latitude = parseFloat(latitudeInput.value);
const longitude = parseFloat(longitudeInput.value);

// Check if the input is valid
if (isNaN(latitude) || isNaN(longitude)) {
alert("Please enter valid latitude and longitude values.");
return;
}

// Append a new circle based on user input
landguzzlersViz.append("circle")
.attr("id", "home-circle") // Assign a unique identifier
.attr("cx", xScale(longitude)) // Longitude of the new circle
.attr("cy", yScale(latitude)) // Latitude of the new circle
.attr("r", 7) // Radius of the new circle
.style("fill", "red"); // Color of the new circle

// Append a larger unfilled circle around the dot representing "home"
landguzzlersViz.append("circle")
.attr("id", "home-area") // Assign a unique identifier
.attr("cx", xScale(longitude)) // Longitude of the larger circle
.attr("cy", yScale(latitude)) // Latitude of the larger circle
.attr("r", 60) // Radius of the larger circle
.style("fill", "none") // No fill for the larger circle
.style("stroke", "red"); // Color of the border for the larger circle

// Append label for "home"
landguzzlersViz.append("text")
.attr("id", "home-label") // Assign a unique identifier
.attr("x", xScale(longitude)) // Longitude of home
.attr("y", yScale(latitude) - 15) // Latitude of home with slight offset for label
.text("home") // Text label
.style("fill", "white") // Color of the label
.style("font-size", "12px") // Font size of the label
.style("text-anchor", "middle"); // Anchor the label in the middle
});


/////////////////////////////////// NARRATIVE TEXT BOX ///////////////////////////////////////////

// Define narrative text content
const narrativeText = `
To provide context to this visual, I would like to share my own personal narrative working at the hospital as a student and registered nurse. While the data highlights the apartments I resided in when I worked as a nurse at the hospital, this narrative resonates with many new graduate nurses embarking on their journey at UVA Medical Center. My journey as a nurse began in 2020 when I joined the hospital's medical intensive care unit. At the onset, newly graduated nurses like myself were compensated at a rate of $25 per hour to navigate the complexities of caring for critically ill patients reliant on various forms of life support. My schedule entailed rotating between day and night shifts, commencing or concluding at either 0700 or 1930. Much like my peers, I often found myself commuting to work during the early hours of 0600-0700 or returning home between 1930-2030.

<br><br>

One notable aspect of this commute was the transportation dilemma faced by nurses. While we had the option to obtain a parking pass, the designated parking lot for nurses was situated at JPJ, necessitating a bus commute to the hospital. This not only extended the duration and cost of an already demanding 12.5 hour shift but also underscored a concerning trend: UVA requires nurses to pay for parking permits and requires them to use distant lots despite closer options being available. This trend incentivizes nurses to find free street parking elsewhere to avoid this cost, sometimes exposing them them to unsafe circumstances. Such circumstances accentuate safety risks during the early morning and late evening hours and exemplify yet another instance of UVA's undervaluation of nurses' contributions at the hospital.

<br><br>

Turning to the visual representation, this abstract map illustrates crime data in Charlottesville, Virginia, spanning from 05/04/2019 to 01/04/2024. By filtering the data to identify the twelve most extreme/most frequent crimes and pinpointing those closest to my walking route to and from the hospital, we can better understand the risks faced by nurses during their daily commute. Additionally, you can input the longitude and latitude of your own residence to identify prevalent crimes in your area, thereby gaining insight into the broader safety landscape. <span style="color:#E57373;">This comprehensive approach underscores the urgent need to address transportation options for nurses, recognizing their pivotal role as frontline healthcare professionals within the hospital and advocating for their safety and well-being during their daily commute.</span>

`;

// Append foreign object for narrative text box
const narrativeForeignObject = landguzzlersViz.append("foreignObject")
.attr("x", 1680) // X position of the foreign object
.attr("y", 15) // Y position of the foreign object
.attr("width", 500) // Width of the foreign object
.attr("height", 1350); // Height of the foreign object

// Append div inside the foreign object for styling
const narrativeDiv = narrativeForeignObject.append("xhtml:div")
.style("font-size", "18px") // Font size of the narrative text
.style("padding", "10px") // Padding around the text content
.style("color", "#BDBDBD") // Set the text color to white
.style("background-color", "black") // Background color of the text box
.style("font-family", "'Rajdhani', sans-serif") // Apply the font
.style("text-align", "right"); // Align text to the right


// Set the narrative text content inside the div
narrativeDiv.html(narrativeText);

}
Insert cell
Insert cell
Insert cell
colors = ["#a5620b","#c29657","#cc1f5e","#a71949","#f7991d","#231f20"]; // in order Large Dog to Hamster. Reference as colors[i]
Insert cell
combined_crime_data3@1.csv
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more