maxm-amaranthsquirrel.web.val.run
Readme

ASCII NYC Traffic Cameras

All of NYC's traffic cameras available as streaming ASCII images: https://maxm-asciinyccameras.web.val.run/

NYC has a bunch of traffic cameras and makes them available through static images like this one. If you refresh the page you'll see the image update every 2 seconds or so. I thought it might be fun to make these cameras viewable as ASCII images. I made a small library that takes most of its logic from this repo. You can see a basic example of how to convert any image to ASCII here.

I pull in NYC GeoJSON from here and then hook up a Server-Sent Events endpoint to stream the ASCII updates to the browser. (Polling would work just as well, I've just been on a bit of a SSE kick lately.)

Hilariously (and expectedly) The ASCII representation is about 4x the size of the the source jpeg and harder to see, but it has a retro-nostalgia look to it that is cool to me :)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** @jsxImportSource https://esm.sh/react */
import { renderToString } from "npm:react-dom/server";
import valTownBadge from "https://esm.town/v/jxnblk/valTownBadge?v=16";
import { imageToAscii } from "https://esm.town/v/maxm/imageToAscii";
import { Hono } from "npm:hono@3";
const app = new Hono();
app.get("/", async (c) => {
const frontendJS = () => {
const width = 600;
const height = 600;
// Create SVG container
const svg = d3.select("#map")
.attr("width", width)
.attr("height", height);
// Define projection and path generator
const projection = d3.geoMercator()
.scale(50000)
.center([-74.036242, 40.700610]) // Center on NYC
.translate([width / 2, height / 2]);
const path = d3.geoPath().projection(projection);
document.getElementById("close-btn").style.display = "none";
if (document.location.hash) {
document.getElementById("info-frame").src = "/camera/" + document.location.hash.slice(1);
document.getElementById("message").style.display = "none";
document.getElementById("close-btn").style.display = "flex";
}
document.getElementById("close-btn").addEventListener("click", () => {
document.getElementById("message").style.display = "flex";
document.getElementById("close-btn").style.display = "none";
document.getElementById("info-frame").src = "";
document.location.hash = "";
});
// Load and render GeoJSON data
d3.json("/nyc.geojson").then(function(data) {
svg.selectAll("path")
.data(data.features)
.enter().append("path")
.attr("d", path)
.attr("fill", "white")
.attr("stroke", "black");
// Add points
svg.selectAll("circle")
.data(points)
.enter().append("circle")
.attr("cx", d => projection([d.lng, d.lat])[0])
.attr("cy", d => projection([d.lng, d.lat])[1])
.attr("r", 5)
.attr("class", "link")
.on("click", function(event, d) {
document.getElementById("message").style.display = "none";
document.getElementById("close-btn").style.display = "flex";
document.location.hash = d.id;
document.getElementById("info-frame").src = "/camera/" + d.id;
})
.on("mouseover", function(event, d) {
d3.select(this).transition()
.duration(200)
.attr("r", 10)
.attr("opacity", 1);
d3.select(this).raise();
})
.on("mouseout", function(event, d) {
d3.select(this).transition()
.duration(200)
.attr("r", 5)
.attr("opacity", 0.6);
});
});
};
let resp = await fetch("https://webcams.nyctmc.org/cameras/graphql", {
"headers": {
"content-type": "application/json",
},
"body": JSON.stringify({
query: `query { cameras {
id
lat: latitude
lng: longitude
isOnline
}}`,
}),
"method": "POST",
});
const cameras = await resp.json();
return new Response(
renderToString(
(<>
<head>
<title>NYC Map with Points</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://d3js.org/d3.v6.min.js"></script>
Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
Comments
Nobody has commented on this val yet: be the first!
v7
June 10, 2024