Third day at the new job, really liking it so far! Feels good to be getting back into a regular 9-5 with some consistency (and a paycheck lol).
Currently reading: Children of Time by Adrian Tchaikovsky đ
Mapillary Timelapse - East Lakeview
Went out and recorded some footage for Mapillary today. Rode around some side streets in Lakeview that donât have 360 imagery on them and are pretty old. You can watch the timelapse here.
Iâve been using the new Mapillary uploader that supports the GoPro .360 files for a little while and am pretty impressed. Instead of having individual photos that are taken every 2 seconds on the GoPro, you take a timelapse while biking or a video while driving. Mapillary will then process the video, taking individual frames from it every so often and matching it up with the GPS data.
The advantage of this approach is that itâs a loooot simpler for people recording. Timelapse videos take up much less space than hundreds upon hundreds of photos and impact the battery a lot less. They also offer higher interval recording - the GoPro Max only offers 360 timelapse photos at a 2s interval, while video timelapses can have photos taken every half second. You can also level out the footage after youâve taken it by exporting to a HEVC MP4 with the GPS data encoded. By far my largest complaint with Mapillary is that they donât do horizon leveling on photos, which makes for an awful experience for mappers. This export approach fixes that, at the expense of lower quality.
The downside of this approach is the lower quality. The photos produced by it tend to appear more compressed, have lower dynamic range, and donât look that great in shade or preserve details well. If you donât upload the raw .360 files, the compression Mapillary uses to serve lower-res segments while zoomed out doesnât play nice with the compression added to the HEVC MP4, leading to 360 photos that look even messier. But if you do import the raw 360 file, Mapillary doesnât horizon level things, and it still doesnât look as good as a regular photo.
I recently bought an Insta360 X3, and I have a GPS module coming for it in the mail soon. The X3 has a photo interval mode and Iâm interested to see how well it works as an alternative to the Max. The X3 has horizon leveling built in at the camera level (and itâs fantastic) so Iâm hoping the photos made by it will look better.
GoPro Max as a cycling dashcam
Iâve been using the GoPro Max for the past six months as a cycling dashcam, and I think I can now recommend it as a daily driver for all cyclists. The Max is a 360 camera by GoPro that has a variety of mounts and two killer features that make it an ideal fit for recording and storing lots of high-quality 360 video: a) quick capture, and b) auto-upload.
Quick capture is a feature that allows you to press and hold the record button on top of the GoPro and instantly start a video or timelampse. If the camera is off, itâll turn on and instantly start recording. You can customize what video mode the GoPro will start recording in (quality, 360 or 180, etc.). But this works significantly better than just about any other camera Iâve ever used. Once you set the setting right, you can strap the camera to your helmet, remove the lens covers, and just press and hold the record button to get going. Itâs seamless.
The GoPro subscription (about $50 a year) gives you unlimited cloud backup of all of your 360 videos. When you plug the Max into the wall to charge, it will instantly connect to your home wifi network and start uploading content to the cloud. When itâs done, youâll get a notification in the GoPro app on your phone. GoProâs subscription gives you unlimited cloud backup of all your videos, which is a huge steal for 360 video. An hour of 360 video on the Max can easily be 5-10GB, so having it all backed up in the cloud for a price this cheap allows you to continue recording tons of 360 video daily while deleting all the old content every so often. In addition to storing the video, GoPro will also automatically export your content to MP4 for you and downsample it. Itâll also stitch the GoProâs smaller .360 outputs for one video into a single .360 video.
These two features, combined with the cheaper price tag of the Max, allow me to unequivocally recommend this camera as the choice for cycling commuters. Itâs easy to switch batteries in and out, the helmet mount works fucking perfectly, and the reframing capabilities are just as good as any other app. This, combined with the lightweight design and the great microphone quality, make it perfect for recording lots and lots of 360 video on your rides to and from work or for leisure.
Thoughts on delivery vehicles in cities
Okay, so my friend Mike made a response to a really dumb car-brained take on Twitter. And as I started replying, I realized I had more complicated thoughts on them, so I decided to write them out here. Also, I’m on Micro.Blog now.
Here’s the tweet he quoted:
The entitlement is crazy here as if there are “Delivery Lanes or designated parking spaces. Yet everybody wants their packages to be delivered in a timely manner. You’re on a bike, just ride around, and let the delivery person DO THEIR JOB.
And here’s his response:
Unpopular opinion. My god do I hate that everybody orders everything online.
We live in a city. Find a store and go to the store. Go outside, it’s good for you.
Online shopping and food delivery apps are so gross. ESPECIALLY cuz we know their employees so taken advantage of
These companies put more dangerous massive heavy trucks and vans on the road. These companies are among the largest repeat offenders of bike lane and crosswalk obstructions.
Fuck online shopping, online shopping created this stupid problem
I have mixed opinions on this. On the one hand, Iâm a Chicago cyclist. Iâve had my fair share of close calls with drivers because a delivery driver decided to park their 18-wheelers or FedEx trucks in the bike lane, forcing me out into traffic. On the other hand, I have plenty of friends who are delivery drivers themselves. And on the other other hand, I rely a lot on delivery services to help me function.
First, one of the main reasons why I no longer ride in the bike lane in Chicago is because they are very frequently blocked: sometimes by regular drivers, sometimes by delivery drivers, both equally dangerous. When a bike lane is blocked, I have two choices: I can swerve around the vehicle into traffic, or I can come to an emergency stop. I have to choose between these two decisions in a split second, and both are incredibly risky. When going around the vehicle, I risk being run over by drivers who are completely unaware of my existence as a cyclist (SUVs have atrocious blind spots). When attempting an emergency stop, I risk being run into by cyclists behind me, running into the vehicle, or launching over my handlebars if I pull it off wrong. Neither of these options are safe, so I donât ride in the bike lane anymore.
So I have a lot of empathy for cyclists angry at delivery drivers for parking in bike lanes, because it risks their lives. Every week in Chicago feels like weâre on a âmurder of the weekâ show as we see a new cyclist or bike commuter dead because a driver parked in the bike lane or a driver wasnât looking where they were going. And when the bike infrastructure is as shit as it is in Chicago, it feels reasonable for cyclists to be protective of what little we have, and angry over violations of its use.
But at the same time, I have plenty of friends who work for UPS. Working as a delivery driver for UPS is a stressful job. You have to fucking book it constantly because your route is timed. Thatâs stressful. And due to the lack of proper loading zones in Chicago, and the overabundance of parking, delivery drivers donât have the time to find a proper parking spot. When you have tens or hundreds of packages to deliver in a single shift, parking once or twice in a bike lane just so you can get back on a fast pace seems like a reasonable tradeoff. And UPS is a unionized workforce! For delivery drivers at FedEx or at Amazon, who are not unionized, the pressure to perform at an unreasonably fast pace is even heavier. So I donât place a lot of the blame on the workers here.
I also have a hard time blaming people who use delivery. Plenty of people also rely on delivery for good reasons, like disability. I have a hard time going to the grocery store. The two grocery stores closest to me have cramped aisles, awful yellow lighting that hurts my brain, and crowds of loud people constantly needing to push past each other. Every time I need to go to them, I dread going because of sensory issues, and I constantly forget things I need. It takes me twice as long to find shit and I donât get everything I need to cook.
So I and a lot of other neurodivergent people rely heavily on grocery delivery. I get it once a week or so to keep my kitchen stocked and help make cooking easier. I just do not have the energy to go to the store regularly except for quick visits. And that to me is an accommodation because my brain just does not work like other peopleâs. If I spend too much of my energy trying to cook and get groceries, I end up exhausted and wonât be able to take care of myself in other ways. So over the last few months, Iâve begun to rely increasingly on parcel + grocery delivery to save me trips to stores that overwhelm me sensory-wise (and also risk my life as a cyclist).
So I also donât blame people who use delivery services. I think delivery services are good and necessary for a city to function well. Theyâre one of the many conveniences of living in a denser area: because of the density, itâs cheaper to do local delivery than it would be in a suburban or rural place. And it helps people like me who kinda need it in order to function for my job or just for my own mental health.
So who to blame then? Auto manufacturers. Private vehicles do not belong in cities, period. All of the roadways that delivery drivers and service vehicles could rely on to get their jobs done are instead used inefficiently by car commuters. Instead of having loading zones in front of stores, we have miles and miles of parking, most of which goes unused. We have untrained drivers who are really bad at it and shouldnât be behind a wheel, but they drive to work anyway because itâs âmore convenientâ and âthere isnât any alternativeâ. After years of lobbying local governments and spreading auto propaganda, auto manufacturers and oil companies forced us to become dependent on their inefficient, wasteful motorized carriages to the point where we canât imagine any alternative way to navigate a city.
Get rid of auto companies and cars, and you have the tools to build an enjoyable, thriving city. But any amount of half-measures or trying to be nice to private vehicle owners will put you exactly where you are now.
What is the void keyword in TypeScript?
One of the more confusing types in the TypeScript universe is the void
type. The most common place itâs used is as the return type for a callback function. For example, the type of the callback you pass to the Array forEach
function is this:
type CallbackFn<T> =
(value: T, index: number, array: readonly T[]) => void;
The forEach
callback accepts three arguments: the current value (T
), the current index, and the array (readonly T[]
). Its return type is the void
type.
Most people think that the void
type means âreturns undefinedâ, and that void
and undefined
are interchangeable. For example, TypeScript accepts both of these callback functions as valid:
const logNumbersVoid = (num: number): void => {
console.log(num);
return undefined;
}
const logNumbersUndefined = (num: number): undefined => {
console.log(num);
return undefined;
}
// TypeScript doesn't complain about either of these.
[1, 2, 3].forEach(logNumbersVoid);
[1, 2, 3].forEach(logNumbersUndefined);
These two functions (logNumbersVoid
and logNumbersUndefined
) are the same, except for their return type. Both accept a number
as their parameter, log the number, and return undefined
. The first functionâs return type is void
, and the second one is undefined
.
TypeScript allows us to use both of these as the callback to the forEach
function. So, it seems like void
and undefined
are doing the same thing.
However, there are some cases where void
allows some things that undefined
does not:
const logNumbersVoid = (num: number): void {
console.log(num);
// Note that we're returning `num` in both functions.
return num;
}
const logNumbersUndefined = (num: number): undefined {
console.log(num);
// TypeScript complains about this but not the other one???
return num;
}
Here, we change both functions to return num
instead of undefined
. TypeScript complains about the return
statement in logNumbersUndefined
, but doesnât complain about the return
from the void
function.
Whatâs going on here? Why are we allowed to return a number from a void
function but not from an undefined
function? Letâs dig deeper.
Where does void
come from?
Several constructs in TypeScript have similarly named constructs in JavaScript. For example, TypeScriptâs typeof
operator comes from the typeof
operator in JavaScript, working similarly to it in type definitions. Itâs useful to understand the JavaScript version of these TypeScript constructs. Knowing the behavior in JavaScript can help us predict how theyâll behave in TypeScript.
Just like typeof
, TypeScriptâs void
type has a counterpart in JavaScript: the void
keyword.
The void
keyword in JavaScript is a keyword that can be put before any expression (something evaluating to a value). JavaScript will evaluate the expression and then return the value undefined
for the entire expression. So in the example below, âIt returned undefined!â would be logged to the console:
if (void "helloWorld" === undefined) {
console.log("It returned undefined!");
}
void
works similarly to typeof
in that it evaluates the expression to the right of it. However, void
throws away the result of the expression. In this way, void
sort of acts like a trash can: the value of any expression you give it will not be accessible again. Hold on to that analogy for a minute.
Use of void
It might seem like the void
keyword doesnât have much of a use case now, and youâd be right in thinking that. But before ES5 JavaScript, it had an important use case: getting the value undefined
.
In JavaScript, there are two versions of undefined
: the value and the variable. For whatever goddamn reason, undefined
is actually a global variable in JavaScript, not a reserved word. By default, this variable points to the value undefined
. Tricky, I know.
Before the ES5 standard, any script could modify the contents of the undefined
variable. Running something like window.undefined = "HAHA FUCK YOU"
could potentially screw up a large part of most working programs (e.g., conditions like variableName === undefined
would return false).
Because of this, many JavaScript developers would use the expression void 0
to obtain the undefined
value. Contrary to the undefined
variable, void
is a reserved word that cannot be modified or changed. This meant that variableName === void 0
would always return true if variableName
was undefined
, even if someone re-assigned the undefined
variable.
The ES5 standard changed undefined
so that it was read-only, so thankfully these kinds of bugs / exploits no longer exist. The resolution of this issue removed one of the main use cases for void
. As a result, most JS programmers donât know about it.
void
in TypeScript
The TypeScript Handbook describes void
like this(https://www.typescriptlang.org/docs/handbook/2/functions.html#void):
void
represents the return value of functions which donât return a value. Itâs the inferred type any time a function doesnât have any return statements, or doesnât return any explicit value from thosereturn
statements.
So void
is a type used when our functions donât have any return value. If we donât have any return statements in the function, TypeScript will automatically assume that our function returns void
. This explains the typing of the Array.prototype.forEach
callback: forEach
isn’t concerned with the result of the callback, only the parameters it needs to pass to it.
If void
is inferred by TypeScript only when nothing is returned from the function, why could we return undefined
in the example at the beginning of the post? The handbook goes on:
In JavaScript, a function that doesnât return any value will implicitly return the value undefined. However,
void
andundefined
are not the same thing in TypeScript.
In JavaScript, any function that doesnât return a value automatically returns undefined
. As consumers of a function, we have no way to tell whether the return of undefined
was explicit (return undefined;
) or implicit (no return
statement). Therefore, TypeScript allows us to return undefined
from a void
function. As consumers of that function, we canât tell the difference between the two.
But hold on a second, why does the handbook say that void
and undefined
are different? Scrolling down to the bottom of the page(https://www.typescriptlang.org/docs/handbook/2/functions.html#return-type-void), we read this:
Contextual typing with a return type of void does not force functions to not return something. Another way to say this is a contextual function type with a
void
return type (type vf = () => void
), when implemented, can return any other value, but it will be ignored.
This is saying that when we have an explicit return type of void
(like in both of our functions before), we can return any type from our function. In other words, any type is assignable to void
when weâre returning from a function.
This is not the case with a function with a return type of undefined
: TypeScript will force us to either return nothing, or explicitly return undefined
. Contrary to this, void
doesnât care. We can return implicit undefined
, explicit undefined
, or any explicit value we want.
Remember what we said about JavaScriptâs void being a trash can? TypeScriptâs void
is the same way, but as a type. Once you assign something to void
, TypeScript wonât let you use it again. You can put stuff into it, but you canât get stuff back out. Once you tell TypeScript that something is void
, it throws out whatever type it was before and starts treating it like nothing. Itâs a trash can.
So thatâs how void
works in TypeScript.