Requiem for Northwest Indiana, Part 1 (Gary–Miller)

For those not aware, the South Shore Line is currently undertaking a major project to double track the line from Gary to Michigan City (currently mostly a single track) and make a number of other improvements to the line, including improving access and parking to stations, making most stations accessible to passengers with disabilities, increasing speeds, and other things. However, as is often the case with major public works projects, there are property impacts. Specifically, buildings near the Miller station (in Miller Beach, Gary), Portage/Ogden Dunes station, and all along 10th and 11th Streets in Michigan City need to be demolished to allow for the construction.

I have been undertaking a major project to catalog the construction, and as part of that I have been photographing as many buildings as I can before and after demolition. New construction hasn’t started yet, but demolition is mostly complete, giving a strange intermediate state with a lot of empty land full of what once was. This is a grim reminder of that progress always comes at a cost.

I thus present the Requiem for Northwest Indiana. This is part 1, specifically focused on the area around the Miller station.

For this photo set, I took the train out to Miller and arrived just before noon. This was my first time traveling out there in the snow (and thanks to the snow last week there was quite a lot). I got off the train at a snowy station:

Miller headhouse and platform on a snowy day
Miller station in the snow

And now, I present the buildings that were lost.

Warehouse

The largest structure demolished as part of this process was a warehouse. This barn had been seized by eminent domain before I photographed it and judging by the condition was probably already abandoned well before then.

Condemned warehouse at 5701 US-12 on September 11, 2021
Warehouse before demolition from the west
Condemned warehouse at 5701 US-12 on September 11, 2021
Another view of the warehouse before demolition from the northwest

The demolition was still in progress, but most of the walls had been demolished by this point.

Partially demolished warehouse at 5701 US-12 on February 6, 2022
Warehouse during demolition from the north
Partially demolished warehouse at 5701 US-12 on February 6, 2022
Warehouse during demolition from the west

Barn

There was a barn nearby that also had been slated for demolition. I have no idea what the barn was used for or who owned it. I also imagine this had been abandoned for quite a while before I photographed it.

Condemned barn at 5501 US-12 on September 11, 2021
Barn before demolition
Demolished empty lot at 5501 US-12 on February 6, 2022
Empty lot after demolition from the north (the fence is no longer there either)
Demolished empty lot at 5501 US-12 on February 6, 2022
Empty lot from the south

Roxxy’s

Roxxy’s was a bar along the Dunes Highway. According to Google Maps it celebrated its 75th birthday relatively recently (the picture was uploaded in April 2019). I actually had to submit an update to Google to explain that the business was now gone (for my “proof” I gave one of the pictures below).

Condemned Roxxy's at 5705 US-12 on September 11, 2021
Roxxy’s before demolition
Demolished empty lot at 5705 US-12 (formerly Roxxy's) on February 6, 2022
Same view of Roxxy’s after demolition
Demolished empty lot at 5705 US-12 (formerly Roxxy's) on February 6, 2022
View from the side of the former site of Roxxy’s

M&M Beauty Supply

Just south of Roxxy’s was the M&M Beauty Supply. According to Google Maps, they have a few other locations in/around Gary. The building itself was demolished, but the sign remained, at least for the time being. As I did with Roxxy’s, I had to submit an update to Google Maps explaining that this location no longer existed.

Condemned M&M Beauty Supply at 5702 US-20 on September 11, 2021
M&M Beauty Supply before demolition
Condemned M&M Beauty Supply at 5702 US-20 on September 11, 2021
Another view of M&M Beauty Supply before demolition

In this case, I was able to pretty closely mirror the viewpoints of the “before” pictures (I did not have the pictures with me when doing this photo set).

Demolished empty lot at 5704 US-20 (formerly M&M Beauty Supply) on February 6, 2022
M&M Beauty Supply lot after demolition
Demolished empty lot at 5702 US-20 (formerly M&M Beauty Supply) on February 6, 2022
M&M Beauty Supply lot after demolition

Garage

At about 5811 US-12, there was some sort of garage building that also looked like it had been abandoned well before I got there. I do not know what used to be there, unfortunately.

Condemned building at 5811 US-12 on September 11, 2021
Garage at 5811 US-12 from the north before demolition
Condemned building at 5811 US-12 on September 11, 2021
Garage at 5811 US-12 from the southwest before demolition
Demolished empty lot at 5811 US-12
Empty lot where the garage used to stand from the north
Demolished empty lot at 5811 US-12
Empty lot where the garage used to stand from the south

Porky’s Pit

Next to the garage was Porky’s Pit, a barbecue place which also appeared to be abandoned before I started my “before” pictures.

Condemned Porky's Pit BBQ at 5813 US-12 on September 11, 2021
Porky’s Pit prior to demolition from the north
Demolished empty lot at 5813 US-12 (formerly Porky's Pit) on February 6, 2022
Empty lot where Porky’s Pit used to stand after demolition from the north
Demolished empty lot at 5813 US-12 (formerly Porky's Pit) on February 6, 2022
Empty lot where Porky’s Pit used to stand after demolition from the south

Empty Lots West of Lake Street

Next to Porky’s were two empty lots that were still empty before I started. They were overgrown at the time but it looks like they’ve been cleared.

Demolished empty lots at 5825-27 US-12 on September 11, 2021
Empty lots west of Lake Street from the north before land clearing
Demolished empty lot at 5825 US-12 on February 6, 2022
Empty lots west of Lake Street from the north after land clearing
Demolished empty lot at 5825 US-12 on February 6, 2022
Empty lots west of Lake Street from the south after land clearing

With all of this, demolition in Miller Beach is mostly complete. New construction will start soon, and it will be interesting to see what develops. However we cannot lost sight of what was lost in the process.

In March or April (once all this snow melts), I’ll continue this project in photographing all the demolished buildings in Michigan City.

Creating custom Optimizely Forms elements, the simple way

There are several guides out there on how to create an Optimizely Forms element, including a set of samples provided by the people at Optimizely themselves. However, these examples are often pretty hard to follow, so I figured it would be helpful to write a quickstart guide that covers just what you need.

Before anything else, you can find the code for this on GitHub.

For this example, we’re going to build out a very simple form element. For the purposes of this demonstration, we will create a form element that allows you to enter a minimum and maximum range. This is of course a very simple example, but serves to get the point across. The steps will be: 1. Create the backend model for the form element, 2. Create the backend validators, 3. Create the frontend JavaScript validation and value binding logic, and 4. Create the view.

0. Prerequisites
For this to work, you need an existing Optimizely website with the Optimizely Forms and Newtonsoft.Json packages already installed.

1. Create the backend model
Like any Optimizely content type, we need to define a model. Form elements are pretty similar to blocks thankfully, so you can use the same type of thinking. We will first start out with the skeleton class for the model:

[ContentType(DisplayName = "Min-Max Range", GUID = "80ABAC02-ACE3-4828-81FF-F7F58D322ACD", Description = "A mininum and maximum number")]
public class MinMaxRangeFormElementBlock : InputElementBlockBase, IElementCustomFormatValue, IElementRequireClientResources
{
    public override ElementInfo GetElementInfo()
    {
        var baseInfo = base.GetElementInfo();
        baseInfo.CustomBinding = true;
        return baseInfo;
    }
}

Let’s break down each part. The ContentType notation is the same as used for pages and blocks, so I won’t bother explaining that. InputElementBlockBase is the base class for form elements, similiar to PageData or BlockData. IElementCustomFormatValue means that we will be applying some sort of custom logic to extract the value. IElementRequireClientResources means there will be frontend JavaScript. We also in GetElementInfo indicate that custom binding logic will be applied.

Now, we need to attach data to the model. Like any page or block type, we should define what types of properties we want. For this one, we can set a lower and upper bound (i.e. min must be above the lower bound and max must be below the upper bound).

[ContentType(DisplayName = "Min-Max Range", GUID = "80ABAC02-ACE3-4828-81FF-F7F58D322ACD", Description = "A mininum and maximum number")]
public class MinMaxRangeFormElementBlock : InputElementBlockBase, IElementCustomFormatValue, IElementRequireClientResources
{
    [Display(Name = "Lower Bound", Order = 100)]
    public int? LowerBound { get; set; }
    
    [Display(Name = "Upper Bound", Order = 200)]
    public int? UpperBound { get; set; }

    public override ElementInfo GetElementInfo()
    {
        var baseInfo = base.GetElementInfo();
        baseInfo.CustomBinding = true;
        return baseInfo;
    }
}

Now that we have the model, we need to implement the interface. First, we need to add the value binding by overridding GetSubmittedValue. We will use a tuple of two integers (Tuple<int, int>) as our backend storage format. Interestingly, because we already invoke frontend JS, we can serialize it to JSON on the frontend and just deserialize that. However, if the user’s browser does not support JavaScript, we do need to apply binding logic. Additionally, we have to have a way to get the value as a string for display to users and such, so for that we need to override GetFormattedValue which will just return “Min to Max” (for example, “1 to 4”.

public override object GetSubmittedValue()
{
	var rawSubmittedData = HttpContext.Current.Request.Form;

	var strValue = base.GetSubmittedValue() as string ?? string.Empty;

	var isJavaScriptSupport = rawSubmittedData.Get(EPiServer.Forms.Constants.FormWithJavaScriptSupport);
	if (isJavaScriptSupport == "true") //if the user's browser support JS, then deserialize the value provided by the frontend
	{
		var values = JsonConvert.DeserializeObject<List<int>>(strValue);
		if ((values?.Count ?? 0) != 2)
			return null;

		return Tuple.Create(values[0], values[1]);
	}

	//if the user's browser does not support JS, we need to extract the value ourselves from the HTML raw form fields
	var minName = $"{FormElement.ElementName}_min";
	var maxName = $"{FormElement.ElementName}_max";

	if (!int.TryParse(rawSubmittedData[minName], out var min) || !int.TryParse(rawSubmittedData[maxName], out var max))
		return null;

	return Tuple.Create(min, max);
}

public object GetFormattedValue()
{
	var submittedVal = GetSubmittedValue() as Tuple<int, int>;

	if (submittedVal is null)
		return string.Empty;

	return $"{submittedVal.Item1} to {submittedVal.Item2}";
}

Finally, we need to add the client resources for the JavaScript, which we will create in a subsequent step for all the frontend logic:

public IEnumerable<Tuple<string, string>> GetExtraResources()
{
	return new List<Tuple<string, string>>
	{
		new Tuple<string, string>("script", "/Static/js/rangeForm.js")
	};
}

With that, we have a complete class:

[ContentType(DisplayName = "Min-Max Range", GUID = "80ABAC02-ACE3-4828-81FF-F7F58D322ACD", Description = "A mininum and maximum number")]
public class MinMaxRangeFormElementBlock : InputElementBlockBase, IElementCustomFormatValue, IElementRequireClientResources
{
	[Display(Name = "Lower Bound", Order = 100)]
        public virtual int? LowerBound { get; set; }
        
        [Display(Name = "Upper Bound", Order = 200)]
        public virtual int? UpperBound { get; set; }

        public override ElementInfo GetElementInfo()
        {
            var baseInfo = base.GetElementInfo();
            baseInfo.CustomBinding = true;
            return baseInfo;
        }

        public IEnumerable<Tuple<string, string>> GetExtraResources()
        {
            return new List<Tuple<string, string>>
            {
                new Tuple<string, string>("script", "/Static/js/rangeForm.js")
            };
        }

        public override object GetSubmittedValue()
        {
            var rawSubmittedData = HttpContext.Current.Request.Form;

            var strValue = base.GetSubmittedValue() as string ?? string.Empty;

            var isJavaScriptSupport = rawSubmittedData.Get(EPiServer.Forms.Constants.FormWithJavaScriptSupport);
            if (isJavaScriptSupport == "true") //if the user's browser support JS, then deserialize the value provided by the frontend
            {
                var values = JsonConvert.DeserializeObject<List<int>>(strValue);
                if ((values?.Count ?? 0) != 2)
                    return null;

                return Tuple.Create(values[0], values[1]);
            }

            //if the user's browser does not support JS, we need to extract the value ourselves from the HTML raw form fields
            var minName = $"{FormElement.ElementName}_min";
            var maxName = $"{FormElement.ElementName}_max";

            if (!int.TryParse(rawSubmittedData[minName], out var min) || !int.TryParse(rawSubmittedData[maxName], out var max))
                return null;

            return Tuple.Create(min, max);
        }

        public object GetFormattedValue()
        {
            var submittedVal = GetSubmittedValue() as Tuple<int, int>;

            if (submittedVal is null)
                return string.Empty;

            return $"{submittedVal.Item1} to {submittedVal.Item2}";
        }
}

2. Create backend validators
We need to validate that users submit valid data to our form. We will validate in two places: frontend and backend. Backend validators are very important to ensure that a user doesn’t bypass frontend validation and also for users without JavaScript support. First, we need to add a validator class:

public class MinMaxRangeValidator : ElementValidatorBase
{
	public override bool? Validate(IElementValidatable targetElement)
	{
		var submittedValue = targetElement.GetSubmittedValue() as Tuple<int, int>;

		return submittedValue is null || submittedValue.Item1 < submittedValue.Item2;
	}

	public override bool AvailableInEditView
	{
		get {
			return false;
		}
	}

	public override IValidationModel BuildValidationModel(IElementValidatable targetElement)
	{
		var model = base.BuildValidationModel(targetElement);
		if (model != null)
		{
			model.Message = LocalizationService.Current.GetString("Form.Error.MinMaxRangeError");
		}
		return model;
	}
}

Let’s break down what each part does. The Validate function is simple enough: it checks the submitted value and determines whether or not it is valid. In this case, we can accept either a null submitted value or a value where the low bound of the range is less than the high bound of the range. Next, we override AvailableInEditView to be false. If it’s true, this validator can optionally be applied to a form element, but by making it false we don’t even give the choice to the editor. Finally, in BuildValidationModel we apply an error message if there is an error.

The next step is to link this validator to our form element. We first need to decorate the form element class:

[AvailableValidatorTypes(Include = new[] { typeof(MinMaxRangeValidator) })]

Then we need to force the validator to always be present. This is a bit tricky due to how the model works. What we have to do is override the list of applied validators and tack on the one we just created. We can do this by adding this property to our form element class:

public override string Validators
{
	get {
		var customValidator = string.Concat(typeof(MinMaxRangeValidator).FullName);

		var validators = this.GetPropertyValue(content => content.Validators);

		return string.IsNullOrEmpty(validators) ? customValidator : string.Concat(validators, EPiServer.Forms.Constants.RecordSeparator, customValidator);
	}

	set {
		this.SetPropertyValue(content => content.Validators, value);
	}
}

In this, we look for any existing validators specified by the editor and then concatenate our own validator to the end of the list, and make sure to include the record separator.

3. Create the frontend JavaScript validation and frontend binding logic
As stated earlier, we perform validation both on the frontend and backend. Now we need to create a JS file to do all this logic. We need to create the file “Static/js/rangeForm.js” (or whatever name/location you chose earlier in step 1). In that, we need to build out a skeleton file:

(function ($) {
    const originalGetCustomElementValue = epi.EPiServer.Forms.Extension.getCustomElementValue;
    const originalBindCustomElementValue = epi.EPiServer.Forms.Extension.bindCustomElementValue;

    $.extend(true, epi.EPiServer.Forms, {
        Extension: {
            getCustomElementValue: function ($element) {
                //TODO: implement
            },
            bindCustomElementValue: function ($element, val) {
                return originalBindCustomElementValue.apply(this, [$element, val]);
            },
        },
        Validators: {
            'FormsTest.Models.FormElements.MinMaxRangeValidator': function (fieldName, fieldValue, validatorMetaData) {
                // TODO: implement
            }
        },
    });
})($$epiforms);

Now, let’s get into what each of these functions does. First, we need to write the custom binding in getCustomElementValue. The basic idea is it will attempt to call this function for every custom form element (not just ours), so we need to do two things: 1. check if it’s a min-max range, and 2. if it is, apply our own value binding (to a two-element JSON-formatted array).

getCustomElementValue: function ($element) {
	if ($element.hasClass('Form__Element__MinMaxRange')) {
		return JSON.stringify([$element.find('[data-rangepart=min]').val(), $element.find('[data-rangepart=max]').val()])
	}

	return originalGetCustomElementValue.apply(this, [$element]);
},

In this case, we check if it’s our custom element by checking the CSS class, and if it is, we find the min and max inputs within the element using data-rangepart attributes (the CSS class and dataset attributes will be fleshed out when we build the view in the next step).

Next, we need to build out the validator. In it, we are already passed the value (formatted with our JS custom binding we just made as a JSON array), so we just need to see if it’s valid.

Validators: {
	'FormsTest.Models.FormElements.MinMaxRangeValidator': function (fieldName, fieldValue, validatorMetaData) {
		const value = JSON.parse(fieldValue);

		return { isValid: value[0] < value[1] };
	}
},

Notice that the object key has to be the fully qualified name of the validator class, and we return an object in the format { isValid: true }. This code will be run for any element with this validator class (in our case, all instances of the min-max range element because we force-applied this validator in step 2).

4. Build the view
This is the last step, thankfully. We need to build out the frontend view for how this element actually renders. Step 1 is to create a view in your block viewss folder called “MinMaxRangeFormElementBlock.cshtml” (make sure the filename matches the class you just created, of course). In it, we start with the outer wrapping.

@model FormsTest.Models.FormElements.MinMaxRangeFormElementBlock

<fieldset class="Form__Element Form__CustomElement Form__Element__MinMaxRange" data-epiforms-element-name="@formElement.ElementName">
</fieldset>

This is critically important to get right. All three CSS classes are important. Form__Element indicates this is a form element, Form__CustomElement indicates we are performing custom binding logic on it (so it knows to invoke the JS from the previous step), and Form__Element__MinMaxRange is so we can actually tell what type of form element this is. We also need data-epiforms-element-name to properly perform binding.

With that out of the way, let’s build out the view.

@model FormsTest.Models.FormElements.MinMaxRangeFormElementBlock

@{
    var formElement = Model.FormElement;
    var errorMessage = Model.GetErrorMessage();
    var submittedValue = Model.GetSubmittedValue() as Tuple<int, int>;
}

<fieldset class="Form__Element Form__CustomElement Form__Element__MinMaxRange" data-epiforms-element-name="@formElement.ElementName">
    <legend class="Form__Element__Caption">@Model.Label</legend>

    <table border="0">
        <tbody>
            <tr>
                <td><label for="@(formElement.Guid)_min">@Html.Translate("Form.Element.MinMaxRange.Min")</label> <input id="@(formElement.Guid)_min" type="number" data-rangepart="min" name="@(formElement.ElementName)_min" value="@(submittedValue == null ? string.Empty : submittedValue.Item1.ToString())" /></td>
                <td><label for="@(formElement.Guid)_max">@Html.Translate("Form.Element.MinMaxRange.Min")</label> <input id="@(formElement.Guid)_max" type="number" data-rangepart="max" name="@(formElement.ElementName)_max" value="@(submittedValue == null ? string.Empty : submittedValue.Item2.ToString())" /></td>
            </tr>
        </tbody>
    </table>

    <div role="alert" aria-live="polite" data-epiforms-linked-name="@formElement.ElementName" class="Form__Element__ValidationError" style="@(string.IsNullOrEmpty(errorMessage) ? " display:none" : "")">@errorMessage</div>
</fieldset>

In this, we have a number of components. We have the label, we have the min and max inputs (notice how the name attribute lines up with what we look for when doing model binding for non-JS clients and the data-rangepart lines up with what we look for when doing JS model binding), and we have an area to show the error message.

With all these components, we now have a fully working custom form element.

If you want to put this all together, you can see it on GitHub.

Saving modified DOM state in a form with the back button

A common problem encountered with building forms in JS is that when leaving a page and coming back, form state is preserved… sort of. If you have a fixed form with no dynamic behavior, everything will be fine. However, if you have something like this (please excuse my barebones JS and markup, this is to simplify the demo):

<script type="text/javascript">
function addRow() {	
	const newRow = document.createElement('ul');
	const newInput = document.createElement('input');
	newInput.type = 'text';
	newInput.name = 'input' + document.getElementById('inputs').childNodes.length;
	newRow.appendChild(newInput);
	inputs.appendChild(newRow);
}
</script>

<input type="text" name="static" />

<ul id="inputs">
</ul>

<button onclick="addRow();">Add Row</button>

…then if you add some rows, leave the page, and then come back, the static input will still have the value you typed in, but the rows you added are gone.

Why do these rows disappear? The reason is because they were added with JS after the page was created. Any DOM manipulation that happens after a page is created will be lost when you leave the page and then come back with the back button.

So, now, what can we do about it? The answer is simple. We can use a simplified version of the memento design pattern. The fundamental idea of the memento pattern is that we store the state in some form that can be retrieved later. There are three important questions to use the memento design pattern effectively, which will all be taken care of in the next paragraphs: 1. How do we convert the state into a storeable object?, 2. Where do we store the state?, and 3. How do we restore the state?

Let’s take these one at a time:

1. How do we convert the state into a storeable object? To store the state effectively, we need a format to store it in and a way to trigger storing it. JavaScript conveniently provides JSON, which is all we need in this case. Since all we have is a list of strings, in this example we can just store it as a JSON-encoded array of strings. For example, if we have two inputs with “Value 1” and “Value 2”, we can just store ["Value 1", "Value 2"].
Now that we have a format to store it in, we need a way to trigger storing it. Thankfully there is a window event, onbeforeunload, that we can leverage. Basically the event fires when the user leaves the page but before actually leaving. In this case, we can add the following JS to generate JS of the state:

window.onbeforeunload = function() {
	const inputWrapper = document.getElementById('inputs');
	const values = [...inputWrapper.querySelectorAll('input')].map(input => input.value)
	const inputListMemento = JSON.stringify(values);
	console.log(inputListMemento);
	
	// TODO: save this somewhere we can see again when reloading the page
}

Now, if we leave the page after entering some data, we will see an array with all the input values logged to the console (make sure to set console logging to persist between pages). Now, this brings us to the next step to solve that TODO:

2. Where do we store the state? At the beginning of the post I explained that input values in inputs present when the page was first loaded will persist. Thankfully, this also includes input values dynamically populated, even to hidden inputs. We can leverage this to store the state. All we have to do is add a hidden input and store the value there.

<script type="text/javascript">
function addRow() {	
	const newRow = document.createElement('ul');
	const newInput = document.createElement('input');
	newInput.type = 'text';
	newInput.name = 'input' + document.getElementById('inputs').childNodes.length;
	newRow.appendChild(newInput);
	inputs.appendChild(newRow);
}

window.onbeforeunload = function() {
	const inputWrapper = document.getElementById('inputs');
	const values = [...inputWrapper.querySelectorAll('input')].map(input => input.value)
	const inputListMemento = JSON.stringify(values);
	
	document.getElementById('inputListMementoHolder').value = inputListMemento;
}
</script>

<input type="text" name="static" />
<input type="hidden" id="inputListMementoHolder" />

<ul id="inputs">
</ul>

<button onclick="addRow();">Add Row</button>

Then if we leave the page and come back, we can verify in your console that the hidden input you just created does indeed store the previous value of the inputs in the list by running the following code:
document.getElementById('inputListMementoHolder').value
Now that we have the value stored in the memento, we finally need to restore it, which brings us to the final part.

3. How do we restore the state? This we can do at page load by reading the value of the input. JS thankfully provides the window.onload event we can use. First, however, we need to modify our addRow function to take an argument for the value:

function addRow(value = '') {	
	const newRow = document.createElement('ul');
	const newInput = document.createElement('input');
	newInput.type = 'text';
	newInput.name = 'input' + document.getElementById('inputs').childNodes.length;
	newInput.value = value;
	newRow.appendChild(newInput);
	inputs.appendChild(newRow);
}

Now, we can reload the state when reloading the page by adding the event to read the memento:

window.onload = function() {
	const inputListMemento = document.getElementById('inputListMementoHolder').value;
	if (inputListMemento) {
		inputListItems = JSON.parse(inputListMemento);
		inputListItems.forEach(item => addRow(item));
	}
}

With this, you should see that when leaving the page and coming back via the back button, we do not lose any data from our list input. For a recap, here’s the final piece of code:

<script type="text/javascript">
function addRow(value = '') {	
	const newRow = document.createElement('ul');
	const newInput = document.createElement('input');
	newInput.type = 'text';
	newInput.name = 'input' + document.getElementById('inputs').childNodes.length;
	newInput.value = value;
	newRow.appendChild(newInput);
	inputs.appendChild(newRow);
}

window.onbeforeunload = function() {
	const inputWrapper = document.getElementById('inputs');
	const values = [...inputWrapper.querySelectorAll('input')].map(input => input.value)
	const inputListMemento = JSON.stringify(values);
	
	document.getElementById('inputListMementoHolder').value = inputListMemento;
}

window.onload = function() {
	const inputListMemento = document.getElementById('inputListMementoHolder').value;
	if (inputListMemento) {
		inputListItems = JSON.parse(inputListMemento);
		inputListItems.forEach(item => addRow(item));
	}
}
</script>

<input type="text" name="static" />
<input type="hidden" id="inputListMementoHolder" />

<ul id="inputs">
</ul>

<button onclick="addRow();">Add Row</button>

Now we can preserve the state of a dynamic form even when leaving the page and returning later.

I have photographed the entire Chicago L!

At long last, I have photographed all 145 stations on the Chicago L! If you just want to see the pictures and not read the rest of this post, check them out here.

My first published photo was taken on March 9, 2019 at Adams/Wabash:

Adams/Wabash station after some rain
Photo at Adams/Wabash

…and my last published photo to complete the set was taken at Morgan on November 6, 2021:

Outbound platform at Morgan, looking west
Photo at Morgan

This was a really fun project, even if exhausting at times, especially near the end where I was trying to finish before it started snowing and we were less likely to get clear skies so I had to take advantage of the chances I got. This involved a lot of days getting up, eating breakfast, loading a day pass onto my Ventra card, and heading out the door to the Red Line then spending most of the day out on the train in parts of the city far from home and ending the day with an hour or two of sorting photos and choosing which ones to upload.

In the course of this project I really came to appreciate the diverse nature of the system. It ranges from utilitarian like Bryn Mawr (for now) to very old-fashioned like Ashland (Green Line) and Quincy to modern like Washington/Wabash and Wilson and everything in between. Meanwhile, the track structures range from elevated to embankment to ground-level to freeway median to open-cut to tunnels. Adding to that, the scenery ranges from industrial to suburban to medium density to ultra-high density as well. Really a big mix of everything.

This project also was a really cool way to experience the city, since I didn’t just go to the stations and take some photos and leave, but instead often walked between adjacent stations and on occasion got lunch on the go (best one was Italian Beef at Nicky’s near 35th/Archer). Walking through the neighborhoods really helped me get a good feel for the area. This also helped me improve my photography skills and figure out more about what conditions are/aren’t good and which techniques work and which ones don’t.

Going forward, I do have a few gaps to fill, between certain parts of stations I couldn’t/forgot to get to for various reasons or stations that received significant changes since I originally photographed them (for example, the three Blue Line stations under Milwaukee Avenue received new flooring). I’ll hit those as I have time, but am not in any hurry. I also of course will continue photographing the ongoing construction in my part of the city. I want to photograph Metra stations (I already have photographed all the stations on the South Shore Line), but am in less of a hurry to do that. After all, Metra doesn’t run anywhere near as frequently as the L does (and some lines don’t run on weekends at all).

Touring an abandoned prison

Today I took a trip down the Joliet Correctional Center in, you guessed it, Joliet. It was an active maximum security Illinois state prison from 1858 to 2002 and held a number of well-known inmates, including Nathan Leopold and Richard Loeb, Baby Face Nelson, John Wayne Gacy, and most famous of all, Jake Blues. It’s interesting that the most famous inmate is fictional.

To get there, I took the Metra Rock Island District to Joliet, then a Pace bus. Of course I got some pictures of the Metra station on the way:

Front of a waiting Rock Island District train at Joliet
The train that took me to Joliet
Rock Island District waiting room at Joliet
Waiting area
Rock Island District train at Joliet from the Amtrak/Heritage Corridor platform
Amtrak/Heritage Corridor platform
Joliet station building from across the street
Station building

Then, I went into the prison on a tour. It was definitely very eerie being in there. The building is a little worse for wear (it was closed due in part to being in poor condition and has seen virtually no maintenance in the intervening two decades). You can see all of the photos I deemed worthy of publication in my Flickr album, but keep reading to see a selection of them with more detailed descriptions.

We entered via the eastern gate where Jake Blues famously walked out. According to the tour guide the gate was actually welded shut, they only opened it after the film company bribed the warden to let them use it and have a crew break the welding. Even then, they only got one shot and five minutes.

Along the path from that gate were the industry buildings to the north and a few other buildings like inmate intake processing to the south.

Industry buildings at the Joliet Prison
Industry buildings (or what’s left of them after severe fire damage)
Industries building at the Joliet Prison
Another burned out industry building
Auto shop building at the Joliet Prison
Burned out auto shop building
Joliet Prison inmate processing building
Inmate intake processing building (also burned out)
Joliet Prison water cistern
Water cistern

We then headed to the solitary confinement building, which had solitary confinement cells on the first floor and death row cells on the second.

Joliet Prison solitary confinement building
Solitary confinement building
Joliet Prison solitary confinement building side view
Solitary confinement building – the windows here were the cell windows at the top of the cells
Joliet Prison original cell interior
Original cell (preserved), apparently three people were held at a time in these cells
"It's never too late to mend" at the Joliet Prison in the solitary confinement building
“It’s never too late! To mend.” (I wonder if that was always there or added because of the Blues Brothers)
Joliet Prison solitary confinement building lower level
Solitary confinement cellblock
Solitary confinement cell at the Joliet Prison
Solitary confinement cell (there was also a toilet-sink unit to the right)
Death row cells at the Joliet Prison
Death row cellblock
Death row cell at the Joliet Prison
Death row cell

Then we headed into the cafeteria building, which was segregated by race into north and south cafeterias (I don’t know/remember which one was which). The north cafeteria in particular included some interesting Simpsons-based graffiti.

Joliet Prison north cafeteria
North cafeteria
Chief Wiggum graffiti at the north cafeteria at the Joliet Prison
Chief Wiggum graffiti in the north cafeteria
Joliet Prison south cafeteria
South cafeteria
Kitchen at the Joliet Prison cafeteria
Kitchen

Then we left to head towards the cell house, passing by (but not entering) the gymnasium.

Joliet Prison gymnasium entrance
Gymnasium entrance

The tour guide then let one of the people on the tour open the door to the east cell house.

Man opening the east cell house door at the Joliet Prison
Man opening the cell house door
Cell block at the Joliet Prison east cell house
Eastern cell house cell block
Cell in the west cell house at the Joliet Prison
Eastern cell house cell

Apparently the eastern cell house cells had beds removed post-closing for maintenance reasons. Also a sobering fact that the left portion of the ceiling in the cell block was added to prevent inmates from trying to jump to their deaths, with apparently as many as three suicides per day.

We then left the cell block and walked by the hospital but couldn’t enter it.

Front of the hospital at the Joliet Prison
Hospital
Front hallway of the Joliet Prison hospital
Entrance to the hospital

We then entered the western cell house, which apparently housed inmates that were disliked even by the other inmates (use your imagination).

Cell in the west cell house at the Joliet Prison
Cell in the western cell house (including a bed this time)
Cell block in the west cell house of the Joliet Prison
Cell block in the western cell house

Here, note the closed doors instead of bars. Apparently this was to avoid inmates throwing stuff at the guards. The inmates here were so disliked that they even had their own yard to avoid contact with other inmates.

West cell house yard at the Joliet Prison
Western cell block yard

We then passed the school and headed towards the chapel.

School building entrance at the Joliet Prison
Entrance to the school building
Chapel stage at the Joliet Prison
Chapel stage (it’s hard to see in this photo but many of the glass panes were missing)
Confession booths at the Joliet Prison chapel
Confession booths
Joliet Prison chapel seating area
Seating area
Joliet Prison chapel
Chapel entrance

It’s hard to see in the photos but the ceiling was in pretty bad shape due to apparently the roof being struck by lightning.

We then walked by a few other buildings to conclude the tour.

Joliet Prison library building
Library building
Joliet Prison laundry facility
Laundry machines
Joliet Prison sally port
Sally port

We then left via the eastern gate via which we entered.

Joliet Prison eastern gate
Eastern gate

I don’t really have much to say, I think the pictures speak for themselves. It was a very interesting tour.

Five lines done!

I have now photographed every station on five of the eight lines of the L: Blue, Brown, Orange, Purple, and Yellow! Of the three remaining lines I have eleven stations left if I’m counting correctly.

My most recent expedition was to photograph the rest of the Blue Line, and in keeping with the blue theme the sky also was a very deep blue. I photographed the rest of the Congress Branch:

Inbound track at Pulaski (Blue), looking east
Pulaski
Inbound track at Kedzie-Homan, looking west
Kedzie-Homan
Platform at Western (Blue - Forest Park), looking west
Western (Congress Branch)
Inbound platform at Illinois Medical District, looking west
Illinois Medical District
Platform at Racine, looking outbound
Racine
Inbound platform at UIC-Halsted, looking east
UIC-Halsted

In particular I got one very nice photo at UIC-Halsted, juxtaposing the station with the downtown skyline:

UIC-Halsted from Morgan Street
UIC-Halsted from Morgan Street

I also photographed Washington, which somehow I had never photographed all this time despite its central location:

Platform at Washington, looking north
Washington

I also photographed two abandoned stations along the Congress Branch in the process:

California (Blue) from California Avenue
California
Kostner (Blue), looking west from pedestrian bridge
Kostner

Then I headed up to Logan Square to get lunch, and on the way home photographed the new flooring at Chicago:

Platform at Chicago (Blue), looking northwest
New flooring at Chicago

Compare this to the flooring in 2019:

Blue Line platform at Chicago
Flooring at Chicago in 2019

Definitely an improvement, looked like they were doing the same thing at Grand and Division too.

Honestly not much more to say, this was just a status update and some more pictures. Eleven stations to go if I’m counting correct, and I’ll hopefully get them done in short order.

Bringing it full circle

Two weeks ago, I returned to Cleveland for the first time since being kicked off campus in March 2020. As part of this trip, I did a bit more photography on the RTA. My main focus was on things that had been renovated since I had left (specifically East 79th on the Red Line, the improved tracks at Tower City, and Farnsleigh). However, I also got new and improved photographs of Cedar-University and Little Italy-University Circle as part of the adventure.

I arrived mid morning at Little Italy-University Circle, one of the first stations I photographed during my time at CWRU by virtue of it being right by campus. Most of my photos of that station were from freshman year, when my photography skills were nowhere near what they are now. Here’s an example:

Little Italy-University Circle Platform looking Outbound
Photo of the Little Italy-University Circle station I took in 2016

Compare with a photo from this time:

Outbound track at Little Italy-University Circle, looking north
Photo of Little Italy-University Circle from 2021

Definitely an improvement: better lighting (I got lucky with the weather admittedly), better angling of the camera, etc.

From there I went to Cedar-University, one stop down the line, and serving the southern portion of the CWRU campus. The story was similar to Little Italy for the most part: my photos from 2016 weren’t the best quality due to my inexperience. However, also important was that Cedar-University had a major bus loop attached which I never photographed.

First, see a typical photo of that station from 2016:

Cedar-University platform looking outbound 4
Photo of the Cedar-University station from 2016

Now compare with a photo taken in 2021:

Outbound track at Cedar-University
Photo of the platform at Cedar-University taken in 2021

Much better lighting (once again lucked out with the weather, but also knew to photograph in the middle of the day instead of in the evening as I did with the first photo), better angles, all that.

Then, I photographed the bus loop which I somehow never did in my four years at CWRU:

Northern bus loop platform at Cedar-University
Bus loop at Cedar-University

Having finished there, I started the main focus of my expedition, photographing the stations renovated since I had left Cleveland. First, I went to East 79th. For reference, here’s what the station used to look like:

Looking westbound from East 79th
Platform at East 79th in 2016

The station at the time was a pretty simple affair: a staircase (behind where I’m standing with the camera) and a wooden platform with a basic bus-like shelter and a roof. The renovations, on the other hand, significantly improved it:

Outbound track at East 79th (Red), looking east
Renovated East 79th station platform in 2021
Platform at East 79th (Red) from the entrance ramp
Renovated East 79th platform in 2021 from across the track
Street entrance to East 79th (Red) from the north
New entrance to East 79th in 2021

The new station has a concrete platform, a ramp for ADA accessibility, new signage, a significantly improved roof, and a much better-looking entrance. Overall it is a significantly improved passenger experience from the original. It did add one interesting twist though, a grade crossing. For a while the only grade crossing on the Red Line was at Brookpark, where passengers had to cross a track to reach the platform. Renovations at the station in 2016-2017 removed that grade crossing and replaced it with a tunnel under the track, but later on one was added at East 34th which saw a similar renovation to East 79th, including a set of ramps on the adjacent hillside.

Grade crossing at East 79th (Red) from the entrance
Grade crossing to access the platform at East 79th

From there I headed to Tower City. When I was a freshman at CWRU in 2016, they replaced the northern track, which resulted in westbound trains going to a temporary station on a normally non-revenue track. They did the same thing again to replace the southern track, and the work was completed prior to my arrival. Here is what the track looked like prior to renovation:

Eastbound platform at Tower City looking west 1
Old eastbound track at Tower City in 2016

After several years with a new northern track but retaining the old southern and stub tracks, they were all replaced. Here are the new and improved tracks:

New westbound Red Line track at Tower City in 2021
New eastbound track at Tower City in 2021
New stub Red Line track at Tower City in 2021
Red Line stub track at Tower City in 2021

Meanwhile, the Blue and Green Lines were not operating due to an eight week construction project shutting down the lines entirely, with them being replaced by shuttle buses in the meantime. However, I did notice that the ceiling had been removed:

Waterfront Line platform at Tower City
Old platform ceiling at Tower City in 2018
Westbound Red Line platform at Tower City in 2021
Platform with ceiling removed at Tower City in 2021

I don’t know the reasoning for removing the ceiling, and whether it’s temporary or permanent, but it definitely takes away some of the character of the station. I hope it will be added back. Given that a lot of the mall above was temporarily closed for construction, it wouldn’t be surprising if that were another part of the construction.

From there I took the shuttle bus out to Farnsleigh. Here’s what it looked like prior to renovation:

Both platforms at Farnsleigh
Farnsleigh station in 2016

It’s a pretty basic median station on the Blue Line with two platforms and a shelter. Now see it post-renovation from approximately the same viewpoint:

Tracks at Farnsleigh, looking west
Farnsleigh station in 2021

Notice the mini-high platforms for wheelchair accessibility and the new shelters. Definitely an improvement, and a big win for the ADA. I wonder if the manufacturer of those shelters will steal my photos again

Heading back to Cleveland was a nice experience, and this photo expedition brought things full circle. I got to see the stations I first photographed when I was still fairly inexperienced and bring to it a new camera and better skills. It really shows how far I’ve come in photography and also brings some closure to my time in Cleveland which was sadly cut short by the pandemic. I’ll definitely be back there another day, and it’ll be nice to see what’s changed and what’s stayed the same then. According to the RTA website, no new station renovations are planned, though they do intend to replace their railcar fleet.

On the home stretch for a multiyear project

My ongoing project to photograph the Chicago L unofficially started on March 9, 2019, when I took a spring break trip to the city, unaware I’d live there a year and a half later. The first photo taken in Chicago (at least that I deemed worth publishing) is this one, of the entrance to Millennium Station:

Entrance to Millennium Station
Entrance to Millennium Station at Randolph and Michigan

Interestingly, the first four train stations I photographed in Chicago were all Metra, on March 9, 2019: Millennium Station, McCormick Place, 55th-56th-57th Street, and Museum Campus/11th Street. The situation there was I was going to the Museum of Science and Industry but stopped at McCormick Place along the way to photograph it, and then headed back to 11th Street to visit the Field Museum. The first photo I took and deemed worthy of publication on the L was at Adams/Wabash:

Adams/Wabash station after some rain
Adams/Wabash platform

The date on Flickr is listed as March 10, but it was actually taken very late on March 9 since my camera was still set on Eastern Time due to coming from Cleveland. Little did I know that this would start my most ambitious photography project so far. I photographed a number of stations in/near downtown as part of that trip, and then returned to Cleveland.

In December 2020 I flew out to Chicago to interview for the company that ended up hiring me, and photographed two stations: O’Hare (where I flew in) and Chicago (Red) (near my hotel).

In March 2020, I had been hired for my current job in Chicago and came back to visit apartments. At that point I knew I would be moving here and had in the back of my mind that I eventually wanted to photograph all the L stations. I stayed in Rogers Park (near Morse) and toured apartments all over the north side. I photographed a number of stations in the process, but knew I would come back.

Since then, I have photographed all but about 25 stations. I have photographed all stations on the Loop, North Side Main, Ravenswood Branch, State Subway, Kennedy/O’Hare Branch, Evanston Branch, Skokie Branch, Englewood/Ashland Branch, and Jackson Park/East 63rd Branch. I also have photographed all but one station on the Milwaukee-Dearborn Subway and at least half of the stations on the Dan Ryan Branch, Cermak Branch, South Side Elevated, and Congress Branch. The big ones remaining are the Lake Street Elevated (just under half done) and Midway Branch. I am taking advantage of every good weather opportunity I get, so with any luck I will have this in the bag soon. If not, I’m not in any hurry. Once this is done, it will be without a doubt my most ambitious photography project so far.

I’m not sure what will come next – maybe photographing as much of Metra as I can? I’m not sure how realistic photographing every Metra station is given that there are over 200 of them and Metra doesn’t run anywhere near as frequently as the L (I don’t have a car) and some lines don’t run at at all on weekends.

Chicago Auto Show!

The Chicago Auto Show is usually in February, but got postponed until in 2021 due to the COVID situation. I went and checked it out, and it was a pretty cool experience.

They had cars from most major car brands, and a few brands even had live demonstrations. I was able to ride in demonstrations for RAM Trucks and Jeep, which were pretty cool:

"Camp Jeep" incline at the 2021 Chicago Auto Show
Jeep incline demonstration
"Camp Jeep" ground clearance demonstration at the 2021 Chicago Auto Show
Jeep ground clearance demosntration
"Camp Jeep" unevenness demonstration at the 2021 Chicago Auto Show
Jeep uneven surface demonstration
Ram truck banked turn at the 2021 Chicago Auto Show
RAM Truck banked turn demonstration

Other than that, I don’t really have much to say, just some pictures, of which a selection is below. You can view them all here.

Bentley Flying Spur at the 2021 Chicago Auto Show
Bentley Flying Spur
Honda CR-V at the 2021 Chicago Auto Show
Honda CR-V
Volkswagen ID.4 at the 2021 Chicago Auto Show
Electric Volkswagen ID.4
Bentley Continental GT at the 2021 Chicago Auto Show
Bentley Continental GT
Subaru Outback at the 2021 Chicago Auto Show
Subaru Outback
Toyota Prius Prime at the 2021 Chicago Auto Show
Toyota Prius Prime

I did get a pretty cool souvenir at the gift shop though, a replica US-66 sign:

Replica US-66 sign

When I was waiting in line to experience the RAM truck, one of the brand reps saw me holding the sign and asked me if I’ve ever been on US-66. I must have made him feel old when I pointed out that it was decommissioned before I was born. I have been on “Historic Route 66” in Arizona though.

Fireworks and a trip to Milwaukee

I have been to Milwaukee a number of times in my life, as I have family that has lived there in the past. However, this was my first time traveling there for non-family reasons. I just wanted to get out of dodge for a bit and also experience the Hiawatha.

It was a bit tough finding a time for this to work, since I wanted to be in Chicago for the Fourth of July (more on that in a bit). I also needed a time when it was going to be sunny, and I didn’t want to take more than two days off work. I ended up choosing the Monday-Wednesday after July 4 (July 5-7), since I already got the Monday off anyway, and the weather looked favorable.

In the meantime, it looked like Chicago cancelled the fireworks, so I looked at options in the suburbs. It ended up looking like Wilmette was my best option since I could easily get there on the Purple Line. However, when I was downtown on July 3, I saw a service alert indicating that there would be restrictions on carrying bikes and strollers on the L in the evening, leading me to check again the firework situation. It turned out that much like when they announced they cancelled the annual dyeing of the Chicago River for St. Patrick’s Day then did it anyway, they did the same for the fireworks and uncancelled them. My guess is that they did so to avoid people traveling from out of the area and also that gave them leeway to make a decision based on case numbers at the time. They did somewhat modify the firework setup, not having a singular viewing area but instead having a seven mile long stretch along the lakeshore where they could be viewed (using bigger and higher fireworks to accomplish this). I thus changed my plans and went downtown, and watched from just north of Museum Campus since that seemed to be approximately in the middle of the viewing area. The fireworks were pretty awesome, just a bit odd having the sound lag behind the visual due to the distance.

Fourth of July fireworks in Chicago

Then, on Monday, I hopped on the 1:05pm Hiawatha to Milwaukee from Chicago Union Station. Union Station was a lot more chaotic than I expected, I guess people are starting to travel again.

Hiawatha Cabbage car at Chicago Union Station
“Cabbage” (cab+baggage) unit at the back of the Hiawatha at Chicago Union Station

From there everything seemed to go smoothly until somewhere just north of the Wisconsin border, we stopped. After a few minutes, the conductor announced that there was a “vehicle on the tracks” and that they were being “inspected for damage.” My first thought was that we hit a car. However, I didn’t hear any crashes (I was two cars from the front of the train), and I checked Google Maps and didn’t see any traffic advisories in the area.

View out the window from near where we stopped

After about ten minutes they announced that they had moved the vehicle so it only blocked the northbound track, so we just needed to wait for a southbound train to clear the area so we could go around it on the southbound track. After about five more minutes, the southbound Hiawatha passed us and we were moving again. We ended up only being about fifteen minutes late, nothing terrible. From there, I photographed the train in Milwaukee:

Hiawatha at Milwaukee Intermodal Station
My train at Milwaukee Intermodal Center

I also photographed the Marquette Interchange, a famously obnoxious traffic interchange:

Marquette Interchange from the southeast
Marquette Interchange

A friend of mine from middle school met me at the station, and we walked around for a bit and got an early dinner at the Brat House, where he got a burger and I got the “southsider,” a bratwurst with melted cheddar, jalapenos, and bacon. It was pretty delicious and a good introduction to Milwaukee. I also got a picture of the Milwaukee river from the park where we ate our dinner:

Milwaukee River looking south from State Street bridge
Milwaukee River from State Street

The Milwaukee River and all the drawbridges was definitely a reminder of Chicago, giving a nice familiar vibe. After that, we parted ways and I continued exploring the city. I scoped out a skyline photography spot, Veterans Park, and determined it would probably be good after the sun set. While waiting for the sun to go down, I got some ice cream and then went back to the hotel to rest my legs for a bit. Then, as dusk approached, I headed back out and caught the “The Hop,” the Milwaukee downtown streetcar, at Wisconsin Avenue and took it to Burns Commons.

The Hop waiting at Burns Commons
The Hop streetcar at Burns Commons

After getting off the streetcar, I walked over to Veterans Park again. This time I did not forget my tripod, so I was able to get some good long-exposure shots. This was probably my best one:

Milwaukee skyline at night from Veterans Park
Downtown Milwaukee skyline at night from Veterans Park

Then, pretty exhausted from all the walking, I headed back to the hotel and called it a night.

The next day I hit the ground running, so to speak. Due to COVID measures, the hotel wasn’t serving breakfast, so I went to the Canary Coffee Bar and got a muffin and chai tea, both of which were great. The weather indicated it was going to be sunny in the morning but cloud over later in the day, so I took the morning for some more photography. Importantly, all my photos from the lakeshore needed to be in the morning anyway so the sun would be behind me. Naturally, I started with photographing the streetcar:

Train at Wisconsin Avenue Southbound
The Hop streetcar at Wisconsin Avenue southbound

I took it to Burns Commons again and got a daytime photo from Veterans Park again.

Milwaukee skyline from Veterans Park
Milwaukee skyline from Veterans Park during the day

I then walked down to Discovery World and got some photos from there.

Milwaukee skyline from Lakeshore State Park
Milwaukee skyline from Discovery World

I explored Downtown Milwaukee some more for a while, then figured I’d head up to Glendale to get frozen custard at Kopp’s. I took the bus up to Glendale and from there got Kopp’s. I’m finally old enough that if I say I’m having frozen custard for lunch, I’m having frozen custard for lunch. Kopp’s only has four flavors: chocolate, vanilla, and two rotating flavors (apparently there was only one rotating flavor at a time in the past). One of the rotating flavors when I was there was “grasshopper fudge,” which is a mint base with fudge chunks. I got a cup of that, which was also delicious.

Sign in front of Kopp’s

After having enjoyed my Kopp’s frozen custard, I caught the bus back downtown. It was starting to cloud over, so I figured that would be a good chance to check out the Grohmann museum, a museum about art related to labor and industry (and one of the only museums open on a Tuesday). The museum was pretty cool, featuring a rooftop sculpture garden and a massive amount of paintings. The paintings dated from the 17th century (that’s the earliest I remember anyway) to 2020 and featured all sorts of industries. Pretty cool museum, worth a visit.

After leaving the museum, I walked around downtown a bit. One particularly interesting detail is that The Hop isn’t electrified for its entire length, operating on battery power for one stretch. I caught a video of a streetcar raising its pantograph:

The Hop streetcar raising its pantograph at City Hall
Eastbound train at Historic Third Ward
The Hop streetcar at Historic Third Ward

Eventually, for dinner, I headed to the Milwaukee Public Market for dinner and got dinner at the Foltz Family Market, a BBQ cheddar burger with fries. Also amazing; I definitely ate well on this trip. After dinner, I walked around downtown for a while longer and photographed some more stuff.

Wells Street Bridge raised
Raised bridge at Wells Street
Milwaukee City Hall tower
Milwaukee City Hall

For my third and final day, I headed to Discovery World in the morning. I first visited it within a year of it opening, in the winter of 2006. It was interesting seeing it from the perspective of an adult vs. a kid and also what’s changed vs. what hasn’t changed. It’s still definitely a cool museum, featuring a bunch of different exhibits about science, technology, and engineering as well as an aquarium and an old restored ship. Also definitely a worthwhile trip for anyone traveling to Milwaukee.

After leaving Discovery World, I headed south to photograph the Allen-Bradley clock tower.

Allen-Bradley clock tower from the east
Allen-Bradley clock tower

It was cloudy so the colors were a bit dull, but I can only ask for so much. With that photo in the bag I started heading back downtown, only to be interrupted by a sudden onset of rain. Of course of all things I forgot to pack, I forgot my umbrella. I stood under a bridge for a while hoping to wait out the rain.

The underside of a bridge where I tried to wait out the rain

After about an hour under the bridge and the rain giving no signs of letting up, I figured I had to leave somehow. I ended up timing it so I was able to get to a nearby bus stop only about two minutes before the bus arrived, so I avoided being totally drenched. Once I got back downtown, I got dinner at Smoke Shack.

Smoke Shack

Being originally from North Carolina, I wanted to see what sort of barbecue game they had in Milwaukee. The building was clearly supposed to mimic the shacks you see down south, but unlike those this was definitely a well-constructed modern building, as opposed to an actual shack. This was definitely a more upscale place trying to pull off a “casual southern” vibe. All that aside, the food there (I got a pulled pork sandwich) was also excellent (as a former North Carolinian, I approve). I also got a “pecan pie in a glass” cocktail to go with my food:

“Pecan pie in a glass” cocktail

After finishing dinner, I waited out the rain a bit longer in the public market seating area, then headed back to the Amtrak station to catch the train home. Unlike the journey north, the journey south was pretty quiet. Very few people on the train. I guess that’s what happens with an evening train on a Wednesday.

Almost empty railcar

We pulled into Chicago Union Station right on time, but interestingly came in on one of the run-through tracks and unloaded on the southern side of the station.

Hiawatha and another Amtrak train at Chicago Union Station
Hiawatha (left) on the southern tracks of Chicago Union Station

My guess is that since this was the last run of the night, they were going to take it to the Amtrak yard, which is located south of Union Station. Anyway, I headed back home. Walking through downtown Chicago was a bit of a shock though, since I headed through several blocks surrounded by skyscrapers. While Milwaukee definitely had tall buildings, they are nowhere near as tall and there are nowhere near as many as in Chicago. Anyway, I made it to Monroe and caught the Red Line home.

Overall, a fun trip! This was my first major vacation in the past year, and it was nice to get out for a bit. Milwaukee is also a pretty cool city and while this wasn’t my first time there, it was my first time really experiencing the city itself rather than the city just being a meeting place.