Skip to content

Instantly share code, notes, and snippets.

@maxwofford
Created October 13, 2025 03:34
Show Gist options
  • Select an option

  • Save maxwofford/5cd3e642493c447808fe32502c64319b to your computer and use it in GitHub Desktop.

Select an option

Save maxwofford/5cd3e642493c447808fe32502c64319b to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Airtable Locations Map</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<style>
body {
margin: 0;
padding: 20px;
font-family: Arial, sans-serif;
max-width: 1200px;
margin: 0 auto;
}
#map {
height: 500px;
width: 100%;
border: 1px solid #ccc;
margin-bottom: 20px;
}
.locations-list {
max-height: 400px;
overflow-y: auto;
border: 1px solid #ddd;
padding: 10px;
}
.location-item {
margin-bottom: 10px;
padding: 8px;
border-bottom: 1px solid #eee;
}
.location-item:last-child {
border-bottom: none;
}
.location-name {
font-weight: bold;
cursor: pointer;
}
.location-name:hover {
color: #007cba;
}
</style>
</head>
<body>
<div id="map"></div>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
// Initialize the map centered on the world (it will auto-zoom to fit points)
const map = L.map('map').setView([0, 0], 2);
// Add OpenStreetMap tiles
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
// Airtable config - Replace BASE_ID and TABLE_NAME with yours
const AIRTABLE_PAT = 'patkDeTklC7FkfuDl.f4c50991508eff765c7ca4952b363fecca32a31b0b4599aa88001778d55715d1';
const BASE_ID = 'appdOWEJZEtL8lanH';
const TABLE_NAME = 'tblg7SJpuCpj5PNMP';
let markers = L.featureGroup(); // Group to auto-fit bounds and manage markers
let locationsData = []; // Store data for the list
// Fetch data from Airtable API
fetch(`https://api.airtable.com/v0/${BASE_ID}/${TABLE_NAME}`, {
headers: {
'Authorization': `Bearer ${AIRTABLE_PAT}`
}
})
.then(response => response.json())
.then(data => {
console.log({ data })
locationsData = data.records;
locationsData.forEach(record => {
const fields = record.fields;
const lat = fields.lat;
const lng = fields.lng;
// const name = fields.Name || 'Unnamed Location'; // Adjust field name if different
// const description = fields.Description || ''; // Adjust field name if different
if (lat && lng && !isNaN(lat) && !isNaN(lng)) {
const marker = L.marker([lat, lng])
// .bindPopup(`<b>${name}</b><br>${description}`)
.addTo(markers);
// Store marker reference for list clicks
record.marker = marker;
}
});
markers.addTo(map);
if (markers.getLayers().length > 0) {
map.fitBounds(markers.getBounds(), { padding: [20, 20] });
}
})
.catch(error => {
console.error('Error fetching Airtable data:', error);
});
// Function to pan map to a specific marker when list item is clicked
function panToMarker(recordId) {
const record = locationsData.find(r => r.id === recordId);
if (record && record.marker) {
map.panTo(record.marker.getLatLng(), { animate: true, duration: 0.5 });
record.marker.openPopup();
}
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment