Skip to main content

Data in

Real data arrives as a spreadsheet, not a GeoJSON FeatureCollection. fromCSV handles the gap with no geocoding service and no API key.

import { fromCSV } from "canvas-globe";

const markers = fromCSV(`city,count
Ahmedabad,12
London,8
Tokyo,4
Nairobi,2`);

globe.setMarkers(markers).fitToMarkers();
Loading globe…

How rows are resolved​

For each row, in order:

  1. Explicit coordinates: a lat/latitude/y column plus lon/lng/long/longitude/x
  2. A city name: city, place, town or location
  3. A country: country, iso, country_code or nation

Anything that resolves to none of those is collected on markers.skipped so you can report it rather than silently dropping rows.

const markers = fromCSV(text);

markers.length; // placed
markers.skipped; // the original rows that failed
markers.skipped.length; // show this in your UI

Recognised columns​

Column namesBecomes
lat, latitude, ymarker.lat
lon, lng, long, longitude, xmarker.lon
city, place, town, locationlooked up, and used as label
country, iso, country_code, nationlooked up
count, value, total, users, visits, amount, nmarker.count
label, name, titlemarker.label
emoji, image, color, datepassed through
anything elsecopied onto the marker

City coverage​

City lookup uses the representative cities in the bundled time-zone table: roughly 300 major cities, at no extra payload. For anything beyond that, supply your own:

fromCSV(text, {
gazetteer: {
Ahmedabad: [72.58, 23.03],
Surat: [72.83, 21.17],
},
});

Keys are matched case-insensitively; values are [lon, lat] or { lat, lon }.

:::tip Ship coordinates when you can If your backend can emit lat/lon columns, do that. Name lookup is a convenience for pasted spreadsheets, not a substitute for real geocoding. :::

Parsing without placing​

parseCSV handles quoted fields, embedded commas, escaped quotes and CRLF, and lowercases headers.

import { parseCSV, fromRows } from "canvas-globe";

const rows = parseCSV(text); // → [{ city: "London", count: "8" }, …]
const markers = fromRows(rows); // → markers

Use fromRows directly when your data comes from JSON or an API rather than CSV text:

const markers = fromRows(await res.json());

Resolving single places​

import { geocode, countryPoint, placeLocation } from "canvas-globe";

geocode("Tokyo"); // city table, then countries
geocode("Ahmedabad", { gazetteer });
countryPoint("IN"); // ISO, numeric id or name
placeLocation("New York"); // time-zone city table only

From your own API​

Nothing about the marker format is special: it is a plain object.

const res = await fetch("/api/signups");
const rows = await res.json();

globe.setMarkers(
rows.map((r) => ({
lat: r.latitude,
lon: r.longitude,
count: r.total,
label: r.city,
date: r.created_at,
plan: r.plan, // comes back in onClick untouched
})),
);

Live updates​

setMarkers replaces the whole set and recomputes the weight ceiling. For a live feed, keep an array and re-set it, or use pings for transient events that should not persist as markers.

setInterval(async () => {
const markers = fromRows(await load());
globe.setMarkers(markers);
}, 30_000);

Talk to a real person

Live human support. Choose what you need help with.