@fusedashlabs/ui9000-workspace
Version:
UI9000 workspace MCP. One tool, show_workspace, draws a chart from your table. Set TYPESAFE_API_KEY to let Jev choose the chart.
79 lines (78 loc) • 2.82 kB
JavaScript
import { inferMapTypeFromKeyNames } from '../../migrate/map-validation/inferMapTypeFromKeyNames.js';
import { GEO_LAT_NAMES, GEO_LNG_NAMES } from '../../profiler/roles.js';
import { normalizeName } from '../../profiler/table.js';
import { countryJoinLabel } from '../country-join-label.js';
import { aggregatePoints } from './rows.js';
/**
* Same default as the widget's buildLayer: an unnamed choropleth key is country.
* The value is written on the layer, because that is the field the widget reads first.
*/
export function choroplethMapType(fieldName) {
return inferMapTypeFromKeyNames(fieldName ? [fieldName] : []) ?? 'country';
}
export function mapChartPayload(chartType, fields, rows) {
const pair = latLngFields(rows[0]);
if (pair) {
const metric = fields.metric &&
fields.metric !== pair.lat &&
fields.metric !== pair.lng
? fields.metric
: undefined;
const markers = [];
for (const row of rows) {
const lat = Number(row[pair.lat]);
const lng = Number(row[pair.lng]);
if (!Number.isFinite(lat) || !Number.isFinite(lng))
continue;
const raw = metric ? Number(row[metric]) : 1;
markers.push({
point: [lng, lat],
value: Number.isFinite(raw) ? raw : 1,
});
}
if (markers.length === 0)
return null;
return {
chartType,
name: 'map-chart',
layers: [
{
name: 'Markers',
visualisationType: 'markers',
geospatialData: ['point'],
arrangeByMetric: ['value'],
data: markers,
},
],
};
}
const geo = fields.geo ?? fields.category;
const metric = fields.metric;
if (!geo || !metric || geo === metric)
return null;
const mapType = choroplethMapType(geo);
const points = aggregatePoints(rows, geo, metric, mapType === 'country' ? countryJoinLabel : (label) => label);
if (!points)
return null;
return {
chartType,
name: 'map-chart',
layers: [
{
name: 'Map',
visualisationType: 'choropleth',
mapType,
geospatialData: ['label'],
arrangeByMetric: ['value'],
aggregationFunction: 'sum',
data: points,
},
],
};
}
function latLngFields(row) {
const keys = Object.keys(row);
const lat = keys.find((key) => GEO_LAT_NAMES.includes(normalizeName(key)));
const lng = keys.find((key) => GEO_LNG_NAMES.includes(normalizeName(key)));
return lat && lng ? { lat, lng } : null;
}