The data comes from a text file that originally came from the US Census Bureau in 2002.
It has 154,568 named cities, with their latitudes, longitudes, and associated quadrangles.
The file is 9,530,947 bytes in size.
#!/usr/bin/env -S awk -f
# trip - road trip planner.
# trip is Copyright Daniel K. Allen, 2000-2026.
# All rights reserved.
#
# 29 Apr 2000 - Created by Dan Allen. (In Zanesville, OH while on Lower 48 states trip.)
# 1 May 2000 - Empirical constant changed from 7/5 to 1.33.
# 16 May 2000 - Writes a map track file.
# 3 Aug 2000 - Renamed trip.awk from plan.awk.
# 2 Apr 2001 - Boot variable used on Mac; duplicate cities bug fixed.
# 9 May 2001 - Statute miles, shows time, empirical constant gone, multiline comments allowed.
# 21 May 2002 - Ported to Mac OS X.
# 10 Jun 2002 - Cleaner output.
# 15 Jun 2002 - Even cleaner output. (Peter Tobin's, Rathdrum, ID, while on family x-country trip.)
# 18 Jun 2002 - Refueling rounded. (Spearfish,SD)
# 15 Nov 2002 - Added to bin.
# 13 Jan 2009 - Updated Mac OS X path.
# 24 Jul 2014 - Added total time. (Fairhaven,MA,41.643,70.883)
# 13 Oct 2016 - Uses env.
# 18 Oct 2016 - No longer uses env due to -S being non-portable.
# 23 Apr 2020 - Uses env -S.
# 31 May 2021 - Fixed USCities name.
# 14 Jul 2021 - Added isdir(); redid USCities file determination.
# 15 Jul 2021 - Added another file location.
# 29 Oct 2021 - AwkTrue -> awk.
# 16 Oct 2022 - Find extreme points; detabbed. (Davenport,IA - ETA 64th!)
# 17 Oct 2022 - Exit immediately if a place is not found, returning error code 2.
# 19 Apr 2023 - Allow "stay" as well as "overnight"; print tilde lines.
# 22 Apr 2023 - Prints comments; fixed bug with last line being a tilde comment.
# 6 Dec 2024 - ASCIIifed.
# 23 Jan 2026 - Nicer final output.
# 19 Jul 2026 - If no space after # then do not print.
#
# Usage: trip trip.txt
#
# Expects to parse tab-delimited USCities.txt file in this order:
#
# City County State Latitude Longitude Elevation Quadrangles
#
# Expects trip.txt lines that start with # and ~ to be in certain format.
#
BEGIN {
a["AL"] = "Alabama"
a["AK"] = "Alaska"
a["AZ"] = "Arizona"
a["AR"] = "Arkansas"
a["CA"] = "California"
a["CO"] = "Colorado"
a["CT"] = "Connecticut"
a["DE"] = "Delaware"
a["FL"] = "Florida"
a["GA"] = "Georgia"
a["HI"] = "Hawaii"
a["ID"] = "Idaho"
a["IL"] = "Illinois"
a["IN"] = "Indiana"
a["IA"] = "Iowa"
a["KS"] = "Kansas"
a["KY"] = "Kentucky"
a["LA"] = "Louisiana"
a["ME"] = "Maine"
a["MD"] = "Maryland"
a["MA"] = "Massachusetts"
a["MI"] = "Michigan"
a["MN"] = "Minnesota"
a["MS"] = "Mississippi"
a["MO"] = "Missouri"
a["MT"] = "Montana"
a["NE"] = "Nebraska"
a["NV"] = "Nevada"
a["NH"] = "New Hampshire"
a["NJ"] = "New Jersey"
a["NM"] = "New Mexico"
a["NY"] = "New York"
a["NC"] = "North Carolina"
a["ND"] = "North Dakota"
a["OH"] = "Ohio"
a["OK"] = "Oklahoma"
a["OR"] = "Oregon"
a["PA"] = "Pennsylvania"
a["RI"] = "Rhode Island"
a["SC"] = "South Carolina"
a["SD"] = "South Dakota"
a["TN"] = "Tennessee"
a["TX"] = "Texas"
a["UT"] = "Utah"
a["VT"] = "Vermont"
a["VA"] = "Virginia"
a["WA"] = "Washington"
a["WV"] = "West Virginia"
a["WI"] = "Wisconsin"
a["WY"] = "Wyoming"
a["DC"] = "District of Columbia"
PI = atan2(1, 1)*4
RTOD = 180/PI
DTOR = PI/180
if (!speed) speed = 60 # MPH
if (!outputLabels) outputLabels = 0 # to write lat/lon to L48Labels.txt file
northernMost = westernMost = -999
southernMost = easternMost = 999
}
function Abs(x) {
return x < 0 ? -x : x
}
function Cos(x) { # Cosine, argument in degrees
return cos(x*DTOR)
}
function AGD(x, y, z) { # Inverse Gudermannian, argument in degrees
y = PI/4+x/2*DTOR
z = sin(y) /cos(y)
return z <= 0 ? "infinity" : log(z)
}
function NextDay(dmy, d, m, y) { # Expects date as "dd mmm yyyy", i.e., "30 Apr 2000"
d = substr(dmy, 1, index(dmy, " ") -1)
m = substr(dmy, index(dmy, " ")+1, 3)
y = substr(dmy, length(dmy) -3)
d++
if (d == 32 && m == "Jan") return "1 Feb " y
if (d == 29 && m == "Feb" && y % 4 || d == 30 && m == "Feb") return "1 Mar " y
if (d == 32 && m == "Mar") return "1 Apr " y
if (d == 31 && m == "Apr") return "1 May " y
if (d == 32 && m == "May") return "1 Jun " y
if (d == 31 && m == "Jun") return "1 Jul " y
if (d == 32 && m == "Jul") return "1 Aug " y
if (d == 32 && m == "Aug") return "1 Sep " y
if (d == 31 && m == "Sep") return "1 Oct " y
if (d == 32 && m == "Oct") return "1 Nov " y
if (d == 31 && m == "Nov") return "1 Dec " y
if (d == 32 && m == "Dec") return "1 Jan "++ y
return d " " m " " y
}
function DMS2Dec(dms, neg) { # ddmmssx--> dd.ddddd
CONVFMT = "%.5f"
if (length(dms) == 7) {
neg =(substr(dms, 7, 1) == "S") ? -1 : 1
return neg *(substr(dms, 1, 2)+substr(dms, 3, 2) /60+substr(dms, 5, 2) /3600)
}
else if (length(dms) == 8) {
neg =(substr(dms, 7, 1) == "E") ? -1 : 1
return neg *(substr(dms, 1, 3)+substr(dms, 4, 2) /60+substr(dms, 6, 2) /3600)
}
}
function HR2HMS(hr, h, m) { # hr -> hh:mm
h = int(hr)
m = int(( hr-h)*60+0.5)
if (m == 60) { m = 0; h++ }
if (length(m) == 1) m = "0" m
return h ":" m
}
function RhumbDist(lat1, lon1, lat2, lon2, dist) { # arguments in degrees, result in statute mi
course = RTOD*atan2(DTOR *(lon1-lon2), AGD(lat2)-AGD(lat1))
if (course < 0) course+= 360
dist = lat1-lat2 == 0 ? Abs(lon2-lon1)*Cos(lat2) :(lat2-lat1) / Cos(course);
CONVFMT = "%.1f"
return dist*69.046766881413
}
{ # parse stdin with a list of places to visit, one per line
if (index($0, "#") == 1) { # comments are ignored
if (