AirTable to Geojson
Simple API endpoint allows use to export AirTable tables as geojson for use in maps. No-code, of course.
AirTable is a widely loved tool for working with small datasets. When the data represents locations, in particular points, there is not an easy way to view them on maps outside of AirTable.
Clockwork Micro has developed a simple, no-code interface that allows a user to enter the base and table values and an optional view value and create an API endpoint that reformats the data in geojson format. The geojson endpoint calls the AirTable endpoint every time it is called, and so the data is always up-to-date. If, however, the data is large, the user can select to precompute the geojson dataset. This geojson file is then delivered from storage and is recomputed each time the user selects to do so in the Clockwork Micro UI. Of course one can also use the AirTable source within the Clockwork Micro's Map Maker to add to a web map.
As an example, we've used a dataset of hiking huts in France. We enter the table's value in the UI as shown in the photo. Note that one must go to the AirTable Developer Hub, create a token and give that token read access to the base.
![](https://cdn.sanity.io/images/owsmuvnq/production/6fa6b78bb5668c4b51e3d917d4e54deb14d43d05-1986x450.png)
A geojson endpoint is created:
This endpoint can be used to access the data and use it in many mapping applications, such as MapLibre, MapBox, Leaflet or OpenLayers. Below is a simple MapLibre code example and the corresponding map.
To create an account and create a map using your AirTable point data with no-code, try the service at https://www.clockworkmicro.com/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Refuges</title>
<link
rel="stylesheet"
href="https://unpkg.com/maplibre-gl@3.2.1/dist/maplibre-gl.css"
/>
<link
rel="stylesheet"
href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"
/>
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
<script src="https://unpkg.com/maplibre-gl@3.2.1/dist/maplibre-gl.js"></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.0.3/purify.min.js"
integrity="sha512-TBmnYz6kBCpcGbD55K7f4LZ+ykn3owqujFnUiTSHEto6hMA7aV4W7VDPvlqDjQImvZMKxoR0dNY5inyhxfZbmA=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
></script>
<style>
body {
margin: 0;
padding: 0;
}
html,
body,
#map {
height: 100%;
}
#cwm-attribute {
position: absolute;
left: 0;
bottom: 0;
width: "auto";
padding: 0 10px;
font-size: 0.9rem;
display: block;
}
</style>
</head>
<body>
<div id="map"></div>
<div id="cwm-attribute">
<a target="_blank" href="https://www.clockworkmicro.com/">CWM</a>
</div>
<script>
const basemapURL =
"https://maps.clockworkmicro.com/streets/v1/style?x-api-key=VefESQoY8v9kJrmX4hfEO36R1I4lfSSa6ITi7EKS";
const geoJSONURL =
"https://filetiler.clockworkmicro.com/v2/tile/a14c29f8-ea55-49ef-ad9c-07f72de728ae/7b867c0a-4f95-4d82-958d-0a9d0d8a639b.geojson?x-api-key=mMohVDvRxU9SorhLeeKCr4KPtPqz3S4P3d9QfpHB";
const map = new maplibregl.Map({
container: "map",
style: basemapURL,
center: [6.608942, 46.069477],
glyphs:
"https://orangemug.github.io/font-glyphs/glyphs/{fontstack}/{range}.pbf",
zoom: 8,
attributionControl: true,
});
let popup;
map.on("load", () => {
const getgeoJSONData = async () => {
try {
const res = await fetch(geoJSONURL).then((res) => res.json());
console.log("res: ", res);
map.addSource("refuges-source", {
type: "geojson",
data: res,
cluster: false,
});
map.addLayer({
id: "refuges-layer",
type: "circle",
source: "refuges-source",
paint: {
"circle-radius": 10,
"circle-color": "#007cbf",
},
});
} catch (error) {
return false;
}
};
map.on("mouseenter", "refuges-layer", (e) => {
console.log("e: ", e);
const features = map
.queryRenderedFeatures(e.point)
.filter((f) => f.source === "refuges-source");
if (features[0]) {
popup = new maplibregl.Popup()
.setLngLat(e.lngLat)
.setHTML(`<p>${features[0].properties.equip_nom}</p>`);
popup.addTo(map);
}
console.log("features: ", features);
});
map.on("mouseleave", "refuges-layer", (e) => {
popup.remove();
});
getgeoJSONData();
// map.on("click", () => {
// console.log(map.getStyle().layers);
// });
map.addControl(
new maplibregl.AttributionControl({
customAttribution:
'Map data from © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> and <a href="https://www.naturalearthdata.com/">Natural Earth Data</a>',
})
);
});
</script>
</body>
</html>