📌 Practical Example: Calculating Distance Between Cities
In this tutorial, we’ll create a set of ACF functions that allow you to calculate the distance between two cities based on their names.

We'll implement the following components:
- A function to look up coordinates for each city using an external API
- A function to calculate the distance between two coordinate pairs
- A final function to present the result in a readable format
🌍 Looking Up City Coordinates via API
To retrieve the geographic coordinates (latitude and longitude) of a city, we use a free public API: Nominatim, based on OpenStreetMap.
We go to the API function itself. It is called GetCityCoordinates
This will return a JSON object, containing the latitude and the longitude coordinates.
Function GetCityCoordinates ( string cityName ) // → float lat, float lon
string baseUrl = "https://nominatim.openstreetmap.org/search";
string url = format("%s?q=%s&format=json&limit=1", baseUrl, Url_Encode (cityName));
string res = http_get(url,"User-Agent: MyACFapp/1.0");
if (res == "" || HTTP_STATUS_CODE != 200) then
throw "No response from Nominatim API";
end if
JSON js;
js["a"] = res;
string latStr = js["a[1].lat"];
string lonStr = js["a[1].lon"];
if (latStr == "?" || lonStr == "?") then
throw "City not found";
end if
float lat = float(latStr);
float lon = float(lonStr);
return json ("lat", lat, "lon", lon);
End
📐 Calculating the Distance
Now that we can request coordinates for two cities, we can calculate the distance between them using the Haversine formula. You could use the Pythagorean theorem, but since we're not flat-earthers, we need a formula that accounts for the curvature of the Earth — and that's exactly what the Haversine formula does.
But before we implement it, we need a helper function: Atan2.
ℹ️ What is atan2?
atan2 is a special arctangent function that returns the angle (in radians) between the x-axis and the point (x, y) on a 2D plane. It’s a smarter version of atan(y / x) that also takes direction into account.
🔍 Why not just use atan(y / x)?
Because atan(y / x) loses quadrant information:
- It only knows the ratio, not the signs of
xandyseparately. - It can’t distinguish between
(1, 1)and(-1, -1)— both produce the same result.
atan2(y, x) fixes this by:
- Taking two arguments (y and x),
- And correctly computing the angle in all four quadrants.
Then we can make a function for the Haversine formula.
Function HaversineDistance:
Function HaversineDistance ( float lat1, float lon1, float lat2, float lon2 )
// Earth's radius in kilometers
float R = 6371;
// Convert degrees to radians
float fi1 = lat1 * pi / 180;
float fi2 = lat2 * pi / 180;
float dtfi = (lat2 - lat1) * pi / 180;
float dtlb = (lon2 - lon1) * pi / 180;
// Haversine formula
float a = sin(dtfi / 2)^2 + cos(fi1) * cos(fi2) * sin(dtlb / 2)^2;
float c = 2 * atan2(sqrt(a), sqrt(1 - a));
return R * c;
End
🧩 Wrapping Up
Now that we’ve built all the pieces, let’s put them together.
We’ll create a function that takes two city names, looks up their coordinates, and returns the distance between them in kilometers.
Function: CityDistance
Function CityDistance ( string cityA, string cityB )
JSON a = GetCityCoordinates(cityA);
JSON b = GetCityCoordinates(cityB);
float lat1 = float(a["lat"]);
float lon1 = float(a["lon"]);
float lat2 = float(b["lat"]);
float lon2 = float(b["lon"]);
return HaversineDistance(lat1, lon1, lat2, lon2);
End
💡 Bonus: Presenting the Result — the Nerdy Way
If we’re feeling a bit nerdy (and why not?), we can enhance the result by adding a descriptive text and calculating how long light would take to travel the same distance — in microseconds.
To do this, we use a value converter called m2lighty to convert meters into light-years. Then we multiply the result by the number of seconds in a year to get the time light would take, and finally multiply by 10^6 to express it in microseconds.
Function DisplayCityDistance:
Function DisplayCityDistance (string cityA, string cityB)
float distkm = CityDistance(cityA, cityB);
float distmiles = distkm*0.621371;
float distlysec = m2lighty(distkm*1000.0)*365*24*60*60;
return format ("The distance between %s and %s is %.1f km, (%.1f miles)\n(%f us at lightspeed)",
cityA, cityB, distkm, distmiles, distlysec*10^6);
end
Testing
In FileMaker, we run this script:
Set Field [test::result; ACF_Run("DisplayCityDistance"; test::FromCity; test::ToCity)]
For the cities of New York and Fredrikstad, it came up with
The distance between New York and Fredrikstad is 5948.5 km, (3696.2 miles)
(19828.351929 us at lightspeed)
Full listing:
This listing is for plugin version 1.7.5.2, which has the URL_Encode and Atan2 as standard functions.
Package CityDistances "Functions to calculate the distance between cities";
Function HaversineDistance ( float lat1, float lon1, float lat2, float lon2 )
print format ("\n\n %f, %f, %f, %f", lat1, lon1, lat2, lon2);
// Earth's radius in kilometers
float R = 6371;
// Convert degrees to radians
float fi1 = lat1 * pi / 180;
float fi2 = lat2 * pi / 180;
float dtfi = (lat2 - lat1) * pi / 180;
float dtlb = (lon2 - lon1) * pi / 180;
// Haversine formula
float a = sin(dtfi / 2)^2 + cos(fi1) * cos(fi2) * sin(dtlb / 2)^2;
float c = 2 * atan2(sqrt(a), sqrt(1 - a));
return R * c;
End
function OsloNYCDist ()
float d = HaversineDistance(59.9139, 10.7522, 40.7128, -74.0060); // Oslo to NYC
Print format("Distance: %.1f km", d);
return d;
end
Function GetCityCoordinates ( string cityName ) // → float lat, float lon
string baseUrl = "https://nominatim.openstreetmap.org/search";
string url = format("%s?q=%s&format=json&limit=1", baseUrl, URL_Encode(cityName));
string res = http_get(url,"User-Agent: MyACFapp/1.0");
if (res == "") then
throw "No response from Nominatim API";
end if
// print res;
JSON js;
js["a"] = res;
print string(js);
string latStr = js["a[1].lat"];
string lonStr = js["a[1].lon"];
if (latStr == "" || lonStr == "") then
throw "City not found";
end if
// print latstr;
float lat = float(latStr);
float lon = float(lonStr);
return json ("lat", lat, "lon", lon);
End
Function CityDistance ( string cityA, string cityB )
JSON a = GetCityCoordinates(cityA);
JSON b = GetCityCoordinates(cityB);
//print string (a);
//print string (b);
float lat1 = float(a["lat"]);
float lon1 = float(a["lon"]);
float lat2 = float(b["lat"]);
float lon2 = float(b["lon"]);
return HaversineDistance(lat1, lon1, lat2, lon2);
End
Function DisplayCityDistance (string cityA, string cityB)
float distkm = CityDistance(cityA, cityB);
float distmiles = distkm*0.621371;
float distlysec = m2lighty(distkm*1000.0)*365*24*60*60;
return format ("The distance between %s and %s is %.1f km, (%.1f miles)\n(%f us at lightspeed)",
cityA, cityB, distkm, distmiles, distlysec*10^6);
end
