Store Finder
Store finder with Geo location services, Python code sorts the JSON neareast to the users location.
Integration Properties
Note that we selected Python, using a Carousel
Geo Settings
LLM Location Parameter Location
A string representing location to look for property This can be a full address (e.g., "123 Main St, Boston, MA") or geographic coordinates in latitude and longitude format (e.g., "42.3601,-71.0589"). Used to determine proximity or directions.
Handlebars List Template
<div class="atlas-store-list grid grid-cols-1 gap-6">
{{#each items}}
<div
class="atlas-store-card bg-white rounded-2xl shadow-lg border border-gray-200 flex flex-col md:flex-row overflow-hidden mb-4 transition hover:shadow-2xl hover:-translate-y-1 duration-200"
data-lat="{{latitude}}"
data-lng="{{longitude}}"
>
<div class="w-full md:w-1/3 flex-shrink-0 bg-gray-100">
<img
src="{{image}}"
alt="{{title}}"
class="atlas-store-img w-full h-40 object-cover md:h-full"
loading="lazy"
/>
</div>
<div class="flex-1 p-4 flex flex-col justify-between">
<div>
<h2 class="text-2xl font-bold text-sky-800 mb-1 flex items-center">
{{title}}
<span
class="inline-block ml-2 rounded-full bg-gradient-to-br from-sky-300 to-blue-200 px-3 py-1 text-xs font-semibold text-sky-900 shadow"
>Open</span
>
</h2>
<p class="text-gray-700 text-sm mb-2 font-medium">
{{short_description}}
</p>
<div class="flex flex-col gap-1 mb-3">
<span class="font-semibold text-gray-600 flex items-center">
<svg
class="inline mr-2 w-4 h-4 text-sky-700"
fill="none"
stroke="currentColor"
stroke-width="2"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M17.657 16.657L13.414 12.414a2 2 0 010-2.828l4.243-4.243m-5.657 5.657a4 4 0 110-5.657 4 4 0 010 5.657z"
/>
</svg>
{{address}}
</span>
<span class="text-xs text-sky-700 flex items-center">
<svg
class="inline mr-1 w-4 h-4 text-sky-600"
fill="none"
stroke="currentColor"
stroke-width="2"
viewBox="0 0 24 24"
>
<circle cx="12" cy="12" r="10" />
<path d="M12 6v6l4 2" />
</svg>
{{hours}} - Distance {{distance_km}} km
</span>
</div>
<div class="flex flex-wrap gap-2 mb-2">
{{#each services}}
<span
class="atlas-badge bg-gradient-to-br from-blue-100 to-sky-100 text-sky-900 text-xs font-semibold px-3 py-1 rounded-xl border border-sky-200 shadow-sm"
>{{this}}</span
>
{{/each}}
</div>
</div>
<div
class="flex flex-row flex-wrap items-center mt-3 gap-2 overflow-visible"
>
<button
class="atlas-copy-btn text-sky-600 border border-sky-200 rounded px-3 py-2 text-xs hover:bg-sky-50 transition flex-shrink-0 min-w-[100px]"
onclick="ai12zBot.sendMessage('Google Map from your location to destination {{address}}','Google Map from your location to destination {{address}}')"
type="button"
>
Google Map
</button>
</div>
</div>
</div>
{{/each}}
</div>
Python
Python code for GEO ordering
def find_nearest_stores(address, source, max_distance_km=10000):
try:
stores = source["items"]
geolocator = Nominatim(user_agent="ai12z")
try:
location = geolocator.geocode(address, timeout=10)
except Exception as geo_err:
raise Exception(f"Network error during geocode: {geo_err}")
if not location:
raise Exception(f"Could not geocode address: {address}")
user_coords = (location.latitude, location.longitude)
log_message(f"User address geocoded to: {user_coords}")
result = []
for store in stores:
store_coords = (store['latitude'], store['longitude'])
distance_km = geodesic(user_coords, store_coords).kilometers
if distance_km <= max_distance_km:
store_with_distance = {}
for k in store:
store_with_distance[k] = store[k]
store_with_distance['distance_km'] = round(distance_km, 2)
result.append(store_with_distance)
# log_message(f"Store {store['title']} is {store_with_distance['distance_km']} km away.")
result.sort(key=lambda x: x['distance_km'])
items = result
source["items"] = items
return {"success": True, "source": source}
except Exception as e:
log_message(f"ERROR in find_nearest_stores: {str(e)}")
return {"success": False, "error": str(e)}
# Main entry point: expects llm["location"] and source["items"]
address = llm.get("location", "Boston, MA")
result = find_nearest_stores(address, source)
Javascript for a slider in the AI Handlebar
ai12zBot.addEventListener("messageSent", (event) => {
const userMessage = String(event.detail).toLowerCase()
if (userMessage.includes("current location")) {
if (!navigator.geolocation) {
alert("Geolocation is not supported by your browser.")
return
}
navigator.geolocation.getCurrentPosition(
handleLocationSuccess,
handleLocationError
)
}
})
if (typeof ai12zBot.lastLocationSendTime === "undefined") {
ai12zBot.lastLocationSendTime = 0
}
function handleLocationSuccess(position) {
const now = Date.now()
// 20 seconds = 20000 ms
if (now - ai12zBot.lastLocationSendTime < 20000) return
// Set time immediately to block re-entry
ai12zBot.lastLocationSendTime = now
const { latitude, longitude } = position.coords
setTimeout(() => {
ai12zBot.sendJSON(
{ latitude: latitude, longitude: longitude },
"Using location data"
)
// Optionally: update the time here if you want the block to begin AFTER send (uncommon)
// ai12zBot.lastLocationSendTime = Date.now();
}, 5000)
}
function handleLocationError(err) {
const messages = {
1: "Location access was denied. Unable to retrieve your location.",
2: "Location information is unavailable. Please try again later.",
3: "Location request timed out. Please try again.",
}
alert(
messages[err.code] ||
"Unable to retrieve location. Please check your settings."
)
}
Style for a slider in the AI Handlebar
.atlas-store-list {
padding: 1rem 0;
}
.atlas-store-card {
box-shadow:
0 2px 10px 0 rgba(34, 71, 107, 0.05),
0 1.5px 6px 0 rgba(36, 63, 106, 0.07);
transition:
box-shadow 0.2s,
transform 0.2s;
}
.atlas-store-card:hover {
box-shadow:
0 8px 32px rgba(36, 71, 107, 0.2),
0 2.5px 12px rgba(36, 63, 106, 0.11);
transform: translateY(-3px) scale(1.01);
}
.atlas-store-img {
border-radius: 0 0 0 1rem;
}
.atlas-badge {
background: linear-gradient(90deg, #f0f6ff 0%, #d2ebff 100%);
}
.atlas-directions-btn {
cursor: pointer;
}
.atlas-copy-btn {
cursor: pointer;
}
JSON
{
"items": [
{
"address": "1200 18th St NW, Washington, DC 20036",
"hours": "Mon-Sat 10am-7pm, Sun 12pm-5pm",
"image": "https://cdn.ai12z.net/assets/web/atlas-dc.jpg",
"latitude": 38.9065,
"longitude": -77.0427,
"services": ["Fitting", "Tuning", "Rentals"],
"short_description": "DC’s best source for ski, snowboard, and winter sports gear.",
"title": "Atlas Washington DC"
},
{
"address": "1201 S Hayes St, Arlington, VA 22202",
"hours": "Mon-Fri 10am-8pm, Sat 10am-7pm, Sun 12pm-5pm",
"image": "https://cdn.ai12z.net/assets/web/atlas-arlington.jpg",
"latitude": 38.8621,
"longitude": -77.0596,
"services": ["Fitting", "Tuning", "Boot Customization"],
"short_description": "Northern Virginia’s premier ski and snowboard outfitter.",
"title": "Atlas Arlington VA"
}
]
}