This repository has been archived on 2026-03-21. You can view files and clone it, but cannot push or open issues or pull requests.
payfrit-biz/api/admin/geocode.cfm
John Mizerek c62895e464 Fix prefixed column names in admin, beacon, task, assignment, chat, rating APIs
Updated all remaining SQL queries to use correct prefixed column names for
ServicePoints, Users, Businesses, Addresses, tt_States, tt_Days, and Hours
tables across 23 admin/infrastructure API files.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 17:55:16 -08:00

204 lines
7.2 KiB
Text

<cfsetting showdebugoutput="false">
<!DOCTYPE html>
<html>
<head>
<title>Address Geocoding</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, sans-serif; max-width: 1000px; margin: 40px auto; padding: 20px; background: ##1a1a1a; color: ##fff; }
h1 { color: ##4CAF50; }
table { width: 100%; border-collapse: collapse; margin-top: 20px; }
th, td { padding: 10px; text-align: left; border-bottom: 1px solid ##333; }
th { background: ##2a2a2a; }
tr:hover { background: ##2a2a2a; }
.address { color: ##aaa; font-size: 13px; }
.success { color: ##4CAF50; padding: 10px; background: ##1b3d1b; border-radius: 4px; margin: 10px 0; }
.error { color: ##f44336; padding: 10px; background: ##3d1b1b; border-radius: 4px; margin: 10px 0; }
.has-coords { color: ##4CAF50; }
.no-coords { color: ##ff9800; }
button { padding: 6px 12px; background: ##4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; }
button:hover { background: ##45a049; }
.btn-lookup { background: ##2196F3; }
.btn-lookup:hover { background: ##1976D2; }
.coords { font-family: monospace; font-size: 12px; color: ##888; }
</style>
</head>
<body>
<h1>Address Geocoding</h1>
<p>Auto-geocode addresses using OpenStreetMap Nominatim (free, no API key required)</p>
<cfscript>
function geocodeAddress(addressString) {
var result = { "success": false, "error": "" };
if (len(trim(addressString)) EQ 0) {
result.error = "Empty address";
return result;
}
try {
var httpService = new http();
httpService.setMethod("GET");
httpService.setUrl("https://nominatim.openstreetmap.org/search?q=" & urlEncodedFormat(addressString) & "&format=json&limit=1");
httpService.addParam(type="header", name="User-Agent", value="Payfrit/1.0");
httpService.setTimeout(10);
var httpResult = httpService.send().getPrefix();
if (httpResult.statusCode CONTAINS "200") {
var data = deserializeJSON(httpResult.fileContent);
if (arrayLen(data) GT 0) {
result.success = true;
result.lat = data[1].lat;
result.lng = data[1].lon;
return result;
}
result.error = "No results found";
return result;
}
result.error = "HTTP " & httpResult.statusCode;
return result;
} catch (any e) {
result.error = e.message;
return result;
}
}
function buildAddressString(line1, line2, city, zipCode) {
var parts = [];
if (len(trim(line1))) arrayAppend(parts, trim(line1));
if (len(trim(line2))) arrayAppend(parts, trim(line2));
if (len(trim(city))) arrayAppend(parts, trim(city));
if (len(trim(zipCode))) arrayAppend(parts, trim(zipCode));
return arrayToList(parts, ", ");
}
</cfscript>
<cfif structKeyExists(url, "geocode") AND structKeyExists(url, "addressId")>
<cfset addressId = val(url.addressId)>
<cfquery name="addr" datasource="payfrit">
SELECT AddressLine1 AS Line1, AddressLine2 AS Line2, AddressCity AS City, AddressZIPCode AS ZIPCode
FROM Addresses WHERE AddressID = <cfqueryparam value="#addressId#" cfsqltype="cf_sql_integer">
</cfquery>
<cfif addr.recordCount GT 0>
<cfset fullAddress = buildAddressString(addr.Line1, addr.Line2, addr.City, addr.ZIPCode)>
<cfset geo = geocodeAddress(fullAddress)>
<cfif geo.success>
<cfquery datasource="payfrit">
UPDATE Addresses SET AddressLat = <cfqueryparam value="#geo.lat#" cfsqltype="cf_sql_decimal">,
AddressLng = <cfqueryparam value="#geo.lng#" cfsqltype="cf_sql_decimal">
WHERE AddressID = <cfqueryparam value="#addressId#" cfsqltype="cf_sql_integer">
</cfquery>
<cfoutput><div class="success">Geocoded Address ID #addressId#: #geo.lat#, #geo.lng#</div></cfoutput>
<cfelse>
<cfoutput><div class="error">Failed: #geo.error#</div></cfoutput>
</cfif>
</cfif>
</cfif>
<cfif structKeyExists(url, "geocodeAll")>
<cfquery name="missing" datasource="payfrit">
SELECT AddressID AS ID, AddressLine1 AS Line1, AddressLine2 AS Line2, AddressCity AS City, AddressZIPCode AS ZIPCode
FROM Addresses
WHERE (AddressLat IS NULL OR AddressLat = 0)
AND AddressLine1 IS NOT NULL AND AddressLine1 != ''
</cfquery>
<cfset successCount = 0>
<cfset failCount = 0>
<cfloop query="missing">
<cfset fullAddress = buildAddressString(missing.Line1, missing.Line2, missing.City, missing.ZIPCode)>
<cfset geo = geocodeAddress(fullAddress)>
<cfif geo.success>
<cfquery datasource="payfrit">
UPDATE Addresses SET AddressLat = <cfqueryparam value="#geo.lat#" cfsqltype="cf_sql_decimal">,
AddressLng = <cfqueryparam value="#geo.lng#" cfsqltype="cf_sql_decimal">
WHERE AddressID = <cfqueryparam value="#missing.ID#" cfsqltype="cf_sql_integer">
</cfquery>
<cfset successCount = successCount + 1>
<cfelse>
<cfset failCount = failCount + 1>
</cfif>
<cfset sleep(1100)>
</cfloop>
<cfoutput><div class="success">Geocoded #successCount# addresses. #failCount# failed.</div></cfoutput>
</cfif>
<cfquery name="addresses" datasource="payfrit">
SELECT
b.BusinessID,
b.BusinessName AS Name,
a.AddressID,
a.AddressLine1 AS Line1,
a.AddressLine2 AS Line2,
a.AddressCity AS City,
a.AddressZIPCode AS ZIPCode,
a.AddressLat AS Latitude,
a.AddressLng AS Longitude
FROM Businesses b
LEFT JOIN Addresses a ON b.BusinessAddressID = a.AddressID
ORDER BY b.BusinessName
</cfquery>
<cfset missingCount = 0>
<cfloop query="addresses">
<cfif (NOT len(addresses.Latitude) OR val(addresses.Latitude) EQ 0) AND len(addresses.Line1)>
<cfset missingCount = missingCount + 1>
</cfif>
</cfloop>
<cfoutput>
<p>
<strong>#missingCount#</strong> addresses missing coordinates.
<cfif missingCount GT 0>
<a href="?geocodeAll=1"><button class="btn-lookup">Geocode All Missing (#missingCount#)</button></a>
<small style="color:##888;">(~#missingCount# seconds due to rate limiting)</small>
</cfif>
</p>
<table>
<thead>
<tr>
<th>Business</th>
<th>Address</th>
<th>Coordinates</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<cfloop query="addresses">
<tr>
<td>
#addresses.Name#
<cfif len(addresses.Latitude) AND val(addresses.Latitude) NEQ 0>
<span class="has-coords">#chr(10003)#</span>
<cfelseif len(addresses.Line1)>
<span class="no-coords">#chr(9679)#</span>
</cfif>
</td>
<td class="address">
<cfif len(addresses.Line1)>
#addresses.Line1#<cfif len(addresses.Line2)>, #addresses.Line2#</cfif><br>
#addresses.City# #addresses.ZIPCode#
<cfelse>
<em style="color:##666;">No address</em>
</cfif>
</td>
<td class="coords">
<cfif len(addresses.Latitude) AND val(addresses.Latitude) NEQ 0>
#numberFormat(addresses.Latitude, "_.______")#<br>
#numberFormat(addresses.Longitude, "_.______")#
<cfelse>
-
</cfif>
</td>
<td>
<cfif len(addresses.Line1) AND len(addresses.AddressID)>
<a href="?geocode=1&addressId=#addresses.AddressID#"><button class="btn-lookup">Lookup</button></a>
</cfif>
</td>
</tr>
</cfloop>
</tbody>
</table>
</cfoutput>
</body>
</html>