Reading time: 17 minutes and 45 seconds.
Snippets
How to use a snippet
Each snippet listed below contains three code blocks. The first block shows the command to run to add the script with an ID and a name. For example npx @spotfire/mods-sdk add-script my-snippet-id --name my-snippet-name
.
The second block shows the expected entry in the scripts
array of the mod manifest. The third block shows the source code of the script.
Test the snippet by adding the entry to your mod manifest, then copy and paste the source code into the script file.
Some snippets use APIs which require your mod to declare additional capabilities.
Snippets
Add a hierarchy column
Adds a hierarchy column created from the column names.
npx @spotfire/mods-sdk add-script add-hierarchy-column --name "Add a hierarchy column"
mod-manifest.json:
{
"id": "add-hierarchy-column",
"name": "Add a hierarchy column",
"file": "build/add-hierarchy-column.js",
"entryPoint": "addHierarchyColumn",
"description": "Adds a hierarchy column created from the column names.",
"parameters": [
{
"name": "table",
"type": "DataTable",
"description": "Table which contains the columns and to which the hierarchy column should be added."
},
{ "name": "hierarchyColumnName", "type": "String", "description": "The name of the hierarchy column." },
{
"name": "columnNames",
"type": "String",
"description": "A comma separated list of column names from which to create a hierarchy column, e.g: 'column1,column2,column3'."
}
]
}
src/scripts/add-hierarchy-column.ts:
const { HierarchyDefinition, HierarchyNestingMode } = Spotfire.Dxp.Data;
export function addHierarchyColumn({ table, columnNames, hierarchyColumnName }: AddHierarchyColumnParameters) {
// Split the input string to get the names of the columns.
const columns = columnNames.split(",");
// Create column expressions from the names, e.g. [column1].
const columnExpressions = columns.map(columnName => `[${columnName}]`);
// Create a managed array required by the HierarchyDefinition constructor.
const expressions = TypedArray.create(System.String, columnExpressions);
// Create the definition from the hierarchy column.
const definition = new HierarchyDefinition(HierarchyNestingMode.Nested, expressions);
try {
// Add the column to the table.
console.log(
`Creating a hierarchy column called '${hierarchyColumnName}' with the expressions '${columnExpressions}'.`
);
table.Columns.AddHierarchyColumn(hierarchyColumnName, definition);
} catch (e) {
console.log(`Creating column failed: ${e}`);
}
}
RegisterEntryPoint(addHierarchyColumn);
Add a horizontal bar chart
Adds a bar chart visualization and configures its orientation to be horizontal.
npx @spotfire/mods-sdk add-script add-horizontal-bar-chart --name "Add a horizontal bar chart"
mod-manifest.json:
{
"id": "add-horizontal-bar-chart",
"name": "Add a horizontal bar chart",
"entryPoint": "addHorizontalBarChart",
"file": "build/add-horizontal-bar-chart.js",
"description": "Adds a bar chart visualization and configures its orientation to be horizontal."
}
src/scripts/add-horizontal-bar-chart.ts:
const { BarChart, BarChartOrientation } = Spotfire.Dxp.Application.Visuals;
export function addHorizontalBarChart({ document }: AddHorizontalBarChartParameters) {
const page = document.ActivePageReference ?? document.Pages.AddNew();
const barChart = page.Visuals.AddNew(BarChart);
// Auto configure to select axis.
barChart.AutoConfigure();
// Proceed with custom configuration.
barChart.Title = "Horizontal Bar Chart";
barChart.Orientation = BarChartOrientation.Horizontal;
}
RegisterEntryPoint(addHorizontalBarChart);
Add a shapefile to the active map chart
Adds a feature layer to the active map chart visualization using a shapefile from the library. Useful when configured as an action trigger on a map chart visualization.
Requires the following capabilities: LibraryRead
npx @spotfire/mods-sdk add-script add-shapefile-to-active-map-chart --name "Add a shapefile to the active map chart"
mod-manifest.json:
{
"id": "add-shapefile-to-active-map-chart",
"name": "Add a shapefile to the active map chart",
"entryPoint": "addShapefileToActiveMapChart",
"file": "build/add-shapefile-to-active-map-chart.js",
"description": "Adds a feature layer to the active map chart visualization using a shapefile from the library. Useful when configured as an action trigger on a map chart visualization.",
"parameters": [
{ "name": "shapeFileLibraryName", "type": "String", "description": "The name of the shapefile in the library." }
]
}
src/scripts/add-shapefile-to-active-map-chart.ts:
const { LibraryManager } = Spotfire.Dxp.Framework.Library;
const { SbdfLibraryDataSource } = Spotfire.Dxp.Data.Import;
const { VisualTypeIdentifiers } = Spotfire.Dxp.Application.Visuals;
const { MapChart, FeatureLayerVisualization } = Spotfire.Dxp.Application.Visuals.Maps;
export function addShapefileToActiveMapChart({
document,
application,
shapeFileLibraryName
}: AddShapefileToActiveMapChartParameters) {
const activeVisual = document.ActiveVisualReference;
if (activeVisual?.TypeId !== VisualTypeIdentifiers.MapChart2) {
throw new Error("The active visualization is not a map chart.");
}
const mapChart = activeVisual.As(MapChart)!;
const libraryManager = application.GetService(LibraryManager)!;
const shapeFiles = Array.from(libraryManager.Search(`type::sbdf ${shapeFileLibraryName}`));
if (shapeFiles.length === 0) {
throw new Error(`Cannot find shapefile with name '${shapeFileLibraryName}' in the library.`);
}
const shapeFileItem = shapeFiles[0];
const table = document.Data.Tables.Add(shapeFileLibraryName, new SbdfLibraryDataSource(shapeFileItem));
const featureLayer = OutParam.create(FeatureLayerVisualization);
mapChart.Layers.AddNewFeatureLayer(table, featureLayer.out);
// Configure the layer
featureLayer.Title = `Feature Layer from '${shapeFileLibraryName}'`;
featureLayer.AutoConfigure();
}
RegisterEntryPoint(addShapefileToActiveMapChart);
Embed all data tables in the analysis
Updates the save settings of all tables in the analysis to be embedded in the analysis.
npx @spotfire/mods-sdk add-script embed-data-tables --name "Embed all data tables in the analysis"
mod-manifest.json:
{
"id": "embed-data-tables",
"name": "Embed all data tables in the analysis",
"file": "build/embed-data-tables.js",
"entryPoint": "embedDataTables",
"description": "Updates the save settings of all tables in the analysis to be embedded in the analysis."
}
src/scripts/embed-data-tables.ts:
const { DataTableSaveSettings } = Spotfire.Dxp.Data;
export function embedDataTables({ document }: EmbedDataTablesParameters) {
// Loop through all data tables current in the analysis.
for (const table of document.Data.Tables) {
// Create save settins with 'useLinkedData' disabled.
const settings = new DataTableSaveSettings(table, false, true);
// Add the save settings to the active settings of the document.
document.Data.SaveSettings.DataTableSettings.Add(settings);
}
}
RegisterEntryPoint(embedDataTables);
Find all columns that match a predicate
This snippet shows how to use a predicate callback to find all time columns of a table and prints them to the debug output.
npx @spotfire/mods-sdk add-script find-columns-with-predicate --name "Find all columns that match a predicate"
mod-manifest.json:
{
"id": "find-columns-with-predicate",
"name": "Find all columns that match a predicate",
"entryPoint": "findColumnsWithPredicate",
"file": "build/find-columns-with-predicate.js",
"description": "This snippet shows how to use a predicate callback to find all time columns of a table and prints them to the debug output.",
"parameters": [
{ "name": "table", "type": "DataTable", "description": "The data table in which to search for columns." }
]
}
src/scripts/find-columns-with-predicate.ts:
const { DataColumn } = Spotfire.Dxp.Data;
export function findColumnsWithPredicate({ table }: FindColumnsWithPredicateParameters) {
console.log(`Searching for all time columns in table '${table.Name}'.`);
const predicate = new System.Predicate(DataColumn, dc => dc.DataType.IsTime);
const columns = table.Columns.FindAll(predicate);
for (const column of columns) {
console.log(`Found time column '${column.Name}'.`);
}
}
RegisterEntryPoint(findColumnsWithPredicate);
Insert columns to an existing data table with custom column match
Inserts columns from one table to another using a custom column match.
npx @spotfire/mods-sdk add-script insert-columns-with-column-match --name "Insert columns to an existing data table with custom column match"
mod-manifest.json:
{
"id": "insert-columns-with-column-match",
"name": "Insert columns to an existing data table with custom column match",
"entryPoint": "insertColumnsWithColumnMatch",
"file": "build/insert-columns-with-column-match.js",
"description": "Inserts columns from one table to another using a custom column match.",
"parameters": [
{ "name": "srcTable", "type": "DataTable", "description": "The table which holds the rows to be added." },
{ "name": "destTable", "type": "DataTable", "description": "The table to which the rows should be added." },
{
"name": "srcColumn",
"type": "String",
"description": "The name of the column in 'srcTable' which matches 'destColumn' in 'destTable'."
},
{
"name": "destColumn",
"type": "String",
"description": "The name of the column 'destTable' which matches 'srcColumn' in 'srcTable'."
},
{
"name": "ignoreColumns",
"type": "String",
"description": "Comma separated list of columns in 'srcTable' to ignore, e.g. col1,col2,col3."
}
]
}
src/scripts/insert-columns-with-column-match.ts:
const { Dictionary, List } = System.Collections.Generic;
const { DataColumnSignature, AddColumnsSettings, JoinType, DataColumn } = Spotfire.Dxp.Data;
const { DataTableDataSource } = Spotfire.Dxp.Data.Import;
export function insertColumnsWithColumnMatch({
srcTable,
srcColumn,
destTable,
destColumn,
ignoreColumns
}: InsertColumnsWithColumnMatchParameters) {
const destCol = getColumnOrThrow(destTable, destColumn);
const srcCol = getColumnOrThrow(srcTable, srcColumn);
// The matching column.
const map = new Dictionary(DataColumnSignature, DataColumnSignature);
map.Add(new DataColumnSignature(destCol), new DataColumnSignature(srcCol));
// Columns to ignore.
const ignoredColumns = new List(DataColumnSignature);
for (const ignoreColumn of ignoreColumns.split(",")) {
ignoredColumns.Add(new DataColumnSignature(getColumnOrThrow(srcTable, ignoreColumn)));
}
const settings = new AddColumnsSettings(map, JoinType.InnerJoin, ignoredColumns);
const dataSource = new DataTableDataSource(srcTable);
destTable.AddColumns(dataSource, settings);
}
function getColumnOrThrow(table: Spotfire.Dxp.Data.DataTable, columnName: string) {
const col = OutParam.create(DataColumn);
if (!table.Columns.TryGetValue(columnName, col.out)) {
throw new Error(`Cannot find column '${columnName}' in table '${table.Name}'.`);
}
return col.value;
}
RegisterEntryPoint(insertColumnsWithColumnMatch);
Read the values of a column
Shows how to read the values of data column.
npx @spotfire/mods-sdk add-script read-column-values --name "Read the values of a column"
mod-manifest.json:
{
"id": "read-column-values",
"name": "Read the values of a column",
"entryPoint": "readColumnValues",
"file": "build/read-column-values.js",
"description": "Shows how to read the values of data column.",
"parameters": [
{ "name": "table", "type": "DataTable", "description": "The data table to read from." },
{ "name": "column", "type": "String", "description": "The name of the column to read from." }
]
}
src/scripts/read-column-values.ts:
const { DataValueCursor, IndexSet, DataColumn } = Spotfire.Dxp.Data;
export function readColumnValues({ document, table, column }: ReadColumnValuesParameters) {
const dataColumn = OutParam.create(DataColumn);
if (!table.Columns.TryGetValue(column, dataColumn.out)) {
throw new Error(`Data table '${table.Name}' contains no column with name '${column}'.`);
}
const rowsToInclude = new IndexSet(table.RowCount, true);
const cursor = DataValueCursor.CreateFormatted(dataColumn);
for (const row of table.GetRows(rowsToInclude, cursor)) {
const rowIndex = row.Index;
const value = cursor.CurrentValue;
if (rowIndex < 10) {
console.log(`Row ${rowIndex} = ${value}`);
} else if (rowIndex === 10) {
// Logging all data would be very performance intensive for large data tables.
console.log("Skipping logging of further rows.");
}
}
}
RegisterEntryPoint(readColumnValues);
Remove all bookmarks from the document
Removes all bookmarks from the document.
npx @spotfire/mods-sdk add-script remove-all-bookmarks --name "Remove all bookmarks from the document"
mod-manifest.json:
{
"id": "remove-all-bookmarks",
"name": "Remove all bookmarks from the document",
"entryPoint": "removeAllBookmarks",
"file": "build/remove-all-bookmarks.js",
"description": "Removes all bookmarks from the document."
}
src/scripts/remove-all-bookmarks.ts:
const { BookmarkManager } = Spotfire.Dxp.Application.AnalyticItems;
export function removeAllBookmarks({ application }: RemoveAllBookmarksParameters) {
const bookmarkManager = application.GetService(BookmarkManager);
if (bookmarkManager == null) {
throw new Error("BookmarkManager is not available.");
}
for (const bookmark of bookmarkManager.GetBookmarks()) {
console.log(`Removing bookmark with name '${bookmark.Name}'.`);
bookmarkManager.Remove(bookmark);
}
}
RegisterEntryPoint(removeAllBookmarks);
Remove unreferenced filtering schemes
Removes all filtering schemes which are not referenced by a visualization.
npx @spotfire/mods-sdk add-script remove-unreferenced-filtering-schemes --name "Remove unreferenced filtering schemes"
mod-manifest.json:
{
"id": "remove-unreferenced-filtering-schemes",
"name": "Remove unreferenced filtering schemes",
"entryPoint": "removeUnreferencedFilteringSchemes",
"file": "build/remove-unreferenced-filtering-schemes.js",
"description": "Removes all filtering schemes which are not referenced by a visualization."
}
src/scripts/remove-unreferenced-filtering-schemes.ts:
const { DataFilteringSelection } = Spotfire.Dxp.Data;
const { Visualization } = Spotfire.Dxp.Application.Visuals;
export function removeUnreferencedFilteringSchemes({ document }: RemoveUnreferencedFilteringSchemesParameters) {
const referencedFilterings: Spotfire.Dxp.Data.DataFilteringSelection[] = [];
for (const page of document.Pages) {
for (const visual of page.Visuals) {
const visualization = visual.As(Visualization);
if (visualization?.Data == null) {
continue;
}
if (visualization.Data.UseActiveFiltering) {
referencedFilterings.push(page.ActiveFilteringSelectionReference);
}
for (const selection of visualization.Data.Filterings) {
const filtering = selection.TryCast(DataFilteringSelection);
if (filtering != null) {
referencedFilterings.push(filtering);
}
}
}
}
const unreferencedFilterings = Array.from(document.FilteringSchemes).filter(
filtering => !referencedFilterings.includes(filtering.FilteringSelectionReference)
);
for (const filtering of unreferencedFilterings) {
document.Data.Filterings.Remove(filtering.FilteringSelectionReference);
}
}
RegisterEntryPoint(removeUnreferencedFilteringSchemes);
Remove unreferenced markings
Removes all markings which are not referenced by a visualization.
npx @spotfire/mods-sdk add-script remove-unreferenced-markings --name "Remove unreferenced markings"
mod-manifest.json:
{
"id": "remove-unreferenced-markings",
"name": "Remove unreferenced markings",
"entryPoint": "removeUnreferencedMarkings",
"file": "build/remove-unreferenced-markings.js",
"description": "Removes all markings which are not referenced by a visualization."
}
src/scripts/remove-unreferenced-markings.ts:
const { DataMarkingSelection } = Spotfire.Dxp.Data;
const { Visualization } = Spotfire.Dxp.Application.Visuals;
export function removeUnreferencedMarkings({ document }: RemoveUnreferencedMarkingsParameters) {
const referencedMarkings: Spotfire.Dxp.Data.DataMarkingSelection[] = [];
for (const page of document.Pages) {
for (const visual of page.Visuals) {
const visualization = visual.As(Visualization);
if (visualization?.Data == null) {
continue;
}
if (visualization.Data.MarkingReference != null) {
referencedMarkings.push(visualization.Data.MarkingReference);
}
for (const selection of visualization.Data.Filterings) {
const marking = selection.TryCast(DataMarkingSelection);
if (marking != null) {
referencedMarkings.push(marking);
}
}
}
}
const unreferencedMarkings = Array.from(document.Data.Markings).filter(
marking => !referencedMarkings.includes(marking)
);
for (const marking of unreferencedMarkings) {
document.Data.Markings.Remove(marking);
}
}
RegisterEntryPoint(removeUnreferencedMarkings);
Replace a data table with an SBDF file from the library
Replaces a table in the analysis with data from an SBDF file saved in the library.
Requires the following capabilities: LibraryRead
npx @spotfire/mods-sdk add-script replace-table-with-library-data --name "Replace a data table with an SBDF file from the library"
mod-manifest.json:
{
"id": "replace-table-with-library-data",
"name": "Replace a data table with an SBDF file from the library",
"entryPoint": "replaceTableWithLibraryData",
"file": "build/replace-table-with-library-data.js",
"description": "Replaces a table in the analysis with data from an SBDF file saved in the library.",
"parameters": [
{ "name": "libraryGuid", "type": "String", "description": "The library ID of the SBDF." },
{ "name": "table", "type": "DataTable", "description": "The data table which should be replaced." }
]
}
src/scripts/replace-table-with-library-data.ts:
const { Guid } = System;
const { SbdfLibraryDataSource } = Spotfire.Dxp.Data.Import;
const { LibraryManager, LibraryItem, LibraryItemType } = Spotfire.Dxp.Framework.Library;
export function replaceTableWithLibraryData({
application,
libraryGuid,
table
}: ReplaceTableWithLibraryDataParameters) {
const manager = application.GetService(LibraryManager);
if (manager == null) {
throw new Error("LibraryManager is unavailable.");
}
const guid = OutParam.create(Guid);
if (!Guid.TryParse(libraryGuid, guid.out)) {
throw new Error(`The provided library ID '${libraryGuid}' is not a valid GUID.`);
}
const item = OutParam.create(LibraryItem);
if (!manager.TryGetItem(guid, item.out)) {
throw new Error(`A library item with the id '${libraryGuid}' could not be found.`);
}
if (item.ItemType !== LibraryItemType.SbdfDataFile) {
throw new Error("The library item is not an SBDF file.");
}
const ds = new SbdfLibraryDataSource(item);
try {
table.ReplaceData(ds);
} catch (e) {
throw new Error(`Failed to replace table with library data, exception: ${e}`);
}
}
RegisterEntryPoint(replaceTableWithLibraryData);
Replace empty values of a column
Replaces all the empty values of a column with another value.
npx @spotfire/mods-sdk add-script replace-empty-values --name "Replace empty values of a column"
mod-manifest.json:
{
"id": "replace-empty-values",
"name": "Replace empty values of a column",
"entryPoint": "replaceEmptyValues",
"file": "build/replace-empty-values.js",
"description": "Replaces all the empty values of a column with another value.",
"parameters": [
{ "name": "table", "type": "DataTable", "description": "The table in which the column can be found." },
{ "name": "columnName", "type": "String", "description": "The name of the column." },
{ "name": "value", "type": "String", "description": "The value which should replace the (Empty) values." }
]
}
src/scripts/replace-empty-values.ts:
const { DataColumn, DataColumnSignature, DataType } = Spotfire.Dxp.Data;
const { ReplaceValuesTransformation } = Spotfire.Dxp.Data.Transformations;
export function replaceEmptyValues({ table, columnName, value }: ReplaceEmptyValuesParameters) {
const column = OutParam.create(DataColumn);
if (!table.Columns.TryGetValue(columnName, column.out)) {
throw new Error(`Cannot find column with name '${columnName}' in table '${table.Name}'.`);
}
const dataOperations = Array.from(table.GenerateSourceView().OperationsSupportingTransformations);
if (dataOperations.length === 0) {
throw new Error(`There are no data operations for table '${table.Name}' which support transformations.`);
}
const dataOperation = dataOperations[0];
const transformations = dataOperation.GetTransformations();
// Clear existing transformations if necessary.
transformations.Clear();
const typedValue = OutParam.create(System.Object);
if (!column.DataType.Formatter.TryParse(value, typedValue.out)) {
throw new Error(`Failed to convert '${value}' to '${column.DataType.Name}'.`);
}
transformations.Add(
new ReplaceValuesTransformation(new DataColumnSignature(column.Name, column.DataType), null, typedValue)
);
dataOperation.ReplaceTransformations(transformations);
}
RegisterEntryPoint(replaceEmptyValues);
Save the analysis to the library
Saves the current analysis to the library.
Requires the following capabilities: LibraryRead, LibraryWrite
Requires wrapInTransaction
to be false
.
npx @spotfire/mods-sdk add-script save-analysis-to-library --name "Save the analysis to the library"
mod-manifest.json:
{
"id": "save-analysis-to-library",
"name": "Save the analysis to the library",
"entryPoint": "saveAnalysisToLibrary",
"file": "build/save-analysis-to-library.js",
"description": "Saves the current analysis to the library.",
"wrapInTransaction": false,
"parameters": [
{
"name": "libraryFolder",
"type": "String",
"description": "The path to the folder in the library where the analysis should be saved."
},
{ "name": "title", "type": "String", "description": "The title of the analysis." }
]
}
src/scripts/save-analysis-to-library.ts:
const { LibraryManager, LibraryItem, LibraryItemType, LibraryItemRetrievalOption, LibraryItemMetadataSettings } =
Spotfire.Dxp.Framework.Library;
const { DocumentSaveSettings } = Spotfire.Dxp.Application;
export function saveAnalysisToLibrary({ application, libraryFolder, title }: SaveAnalysisToLibraryParameters) {
const libraryManager = application.GetService(LibraryManager)!;
const folderItem = getOrCreateLibraryFolder(libraryManager, libraryFolder);
console.log(`Saving analysis to '${joinPath(folderItem.Path, title)}'`);
application.SaveAs(folderItem, title, new LibraryItemMetadataSettings(), new DocumentSaveSettings());
}
/**
* Gets or creates a library folder with the given path. If it does not exist then the folder structure
* is recursively created by splitting the path on "/".
*/
function getOrCreateLibraryFolder(libraryManager: Spotfire.Dxp.Framework.Library.LibraryManager, folderPath: string) {
const folderItem = OutParam.create(LibraryItem);
if (
!libraryManager.TryGetItem(
folderPath,
LibraryItemType.Folder,
folderItem.out,
LibraryItemRetrievalOption.IncludePath
)
) {
const parentFolder = OutParam.create(LibraryItem);
if (!libraryManager.TryGetItem("/", LibraryItemType.Folder, parentFolder.out)) {
throw new Error("Cannot retrieve library root.");
}
let parent = parentFolder.value;
const pathParts = folderPath.split("/");
for (const part of pathParts) {
// Skip initial "/" root if it exists.
if (part === "") {
continue;
}
const path = joinPath(parent.Path, part);
if (
!libraryManager.TryGetItem(
path,
LibraryItemType.Folder,
parentFolder.out,
LibraryItemRetrievalOption.IncludePath
)
) {
console.log(`Creating library folder with path '${path}'`);
parent = libraryManager.CreateFolder(parent, part, new LibraryItemMetadataSettings());
}
}
folderItem.value = parent;
}
return folderItem.value;
}
function joinPath(left: string, right: string) {
return left.split("/").concat(right.split("/")).join("/").replace("//", "/");
}
RegisterEntryPoint(saveAnalysisToLibrary);
Set a custom date format for a column
Sets a custom date format for a specific column.
npx @spotfire/mods-sdk add-script set-custom-date-format --name "Set a custom date format for a column"
mod-manifest.json:
{
"id": "set-custom-date-format",
"name": "Set a custom date format for a column",
"entryPoint": "setCustomDateFormat",
"file": "build/set-custom-date-format.js",
"description": "Sets a custom date format for a specific column.",
"parameters": [
{ "name": "format", "type": "String", "description": "The date format, e.g. 'dd-MM-yyyy' for day/month-year." },
{
"name": "columnName",
"type": "String",
"description": "The name of the column whose date format should be set."
},
{ "name": "table", "type": "DataTable", "description": "The data table in which the column can be found." }
]
}
src/scripts/set-custom-date-format.ts:
const { DataType, DataColumn } = Spotfire.Dxp.Data;
const { DateTimeFormatter } = Spotfire.Dxp.Data.Formatters;
export function setCustomDateFormat({ format, columnName, table }: SetCustomDateFormatParameters) {
const column = OutParam.create(DataColumn);
if (!table.Columns.TryGetValue(columnName, column.out)) {
throw new Error(`Table '${table.Name}' does not contain a column with the name '${columnName}'.`);
}
if (column.Properties.DataType !== DataType.DateTime) {
throw new Error(`The column '${columnName}' in table '${table.Name}' is not a DateTime column.`);
}
// We have to cast here since CreateLocalizedFormatter returns a type which is not specific enough.
const formatter = DataType.DateTime.CreateLocalizedFormatter().Cast(DateTimeFormatter);
formatter.FormatString = format;
column.Properties.Formatter = formatter;
}
RegisterEntryPoint(setCustomDateFormat);
Set a document property
Sets a document property to a specific value, creating the property if it does not already exist.
npx @spotfire/mods-sdk add-script set-document-property --name "Set a document property"
mod-manifest.json:
{
"id": "set-document-property",
"name": "Set a document property",
"entryPoint": "setDocumentProperty",
"file": "build/set-document-property.js",
"description": "Sets a document property to a specific value, creating the property if it does not already exist.",
"parameters": [
{ "name": "propertyName", "type": "String", "description": "The name of the property." },
{ "name": "type", "type": "String", "description": "The type of the property." },
{ "name": "value", "type": "String", "description": "The value to set the document property to." }
]
}
src/scripts/set-document-property.ts:
const { DataType, DataProperty, DataPropertyClass } = Spotfire.Dxp.Data;
export function setDocumentProperty({ document, propertyName, type, value }: SetDocumentPropertyParameters) {
const dataType = DataType.FromName(type);
const typedValue = OutParam.create(System.Object);
if (dataType.Formatter.TryParse(value, typedValue.out)) {
const properties = document.Data.Properties;
const property = OutParam.create(DataProperty);
if (!properties.TryGetProperty(DataPropertyClass.Document, propertyName, property.out)) {
property.value = DataProperty.CreateCustomPrototype(propertyName, dataType, DataProperty.DefaultAttributes);
properties.AddProperty(DataPropertyClass.Document, property);
console.log(
`Document property with name '${propertyName}' does not exist. Creating it with type '${dataType.Name}'.`
);
}
console.log(`Setting value of '${propertyName}' to '${dataType.Formatter.Format(typedValue)}'.`);
property.Value = typedValue;
} else {
throw new Error(`Failed to parse '${value}' as type '${dataType.Name}'.`);
}
}
RegisterEntryPoint(setDocumentProperty);
Set a projection for all layers in a mapchart
Sets a projection for all layers in a mapchart.
npx @spotfire/mods-sdk add-script set-projection-for-mapchart-layer --name "Set a projection for all layers in a mapchart"
mod-manifest.json:
{
"id": "set-projection-for-mapchart-layer",
"name": "Set a projection for all layers in a mapchart",
"entryPoint": "setProjectionForMapchartLayer",
"file": "build/set-projection-for-mapchart-layer.js",
"description": "Sets a projection for all layers in a mapchart.",
"parameters": [
{ "name": "visual", "type": "Visualization", "description": "The map chart visualization." },
{
"name": "useCustomProjection",
"type": "Boolean",
"description": "If a custom projection should be used instead of an EPSG one."
}
]
}
src/scripts/set-projection-for-mapchart-layer.ts:
const { MapChart, Projection } = Spotfire.Dxp.Application.Visuals.Maps;
export function setProjectionForMapchartLayer({
visual,
useCustomProjection
}: SetProjectionForMapchartLayerParameters) {
const mapchart = visual.As(MapChart);
if (mapchart == null) {
throw new Error(`Visualization '${visual.Title}' is not a map chart.`);
}
// Creating a projection using an EPSG code.
const projection = new Projection("EPSG:3857");
// Creating a projection using an proj4 definition.
const customProjection = new Projection(
"CustomIdentifierPrefix:1",
"+proj=aeqd +lat_0=0 +lon_0=0 +x_0=0 +y_0=0 +units=m +datum=WGS84 +no_defs",
"Generated CRS"
);
for (const layer of mapchart.Layers) {
if (useCustomProjection) {
layer.Projection = customProjection;
} else {
layer.Projection = projection;
}
}
}
RegisterEntryPoint(setProjectionForMapchartLayer);
Set the colors of a bar chart
Updates the categorical coloring of a bar chart visualization.
npx @spotfire/mods-sdk add-script bar-chart-coloring --name "Set the colors of a bar chart"
mod-manifest.json:
{
"id": "bar-chart-coloring",
"name": "Set the colors of a bar chart",
"entryPoint": "barChartColoring",
"file": "build/bar-chart-coloring.js",
"description": "Updates the categorical coloring of a bar chart visualization.",
"parameters": [
{ "name": "visual", "type": "Visualization", "description": "The bar chart visualization." },
{
"name": "colorMap",
"type": "String",
"description": "A comma separated list of key=color combinators, e.g. \"Apple=red,Banana=yellow\"."
}
]
}
src/scripts/bar-chart-coloring.ts:
const { BarChart, CategoryKey, AxisMode } = Spotfire.Dxp.Application.Visuals;
const { Color } = System.Drawing;
const red = Color.FromArgb(255, 0, 0);
const blue = Color.FromArgb(0, 0, 255);
const green = Color.FromArgb(0, 255, 0);
const yellow = Color.FromArgb(255, 255, 0);
function toColor(name: string) {
switch (name.toLowerCase()) {
case "red":
return red;
case "blue":
return blue;
case "green":
return green;
case "yellow":
return yellow;
default:
throw new Error(`Unknown color name '${name}'`);
}
}
export function barChartColoring({ visual, colorMap }: BarChartColoringParameters) {
const barChart = visual.As(BarChart);
if (!barChart) {
throw new Error(`The visual provided is not a bar chart visualization.`);
}
barChart.ColorAxis.Coloring.Clear();
for (const keyColor of colorMap.split(",")) {
if (!keyColor.includes("=")) {
throw new Error(`Broken key=color pair: '${keyColor}'.`);
}
const [key, color] = keyColor.split("=");
barChart.ColorAxis.Coloring.SetColorForCategory(new CategoryKey(key), toColor(color));
}
}
RegisterEntryPoint(barChartColoring);
Set up a page layout and rotate the visualizations on consecutive runs
Creates a layout where one visualization is enlarged and all others are stacked to its the right. On consecutive runs the enlarged visualization is rotated with one of the smaller ones.
npx @spotfire/mods-sdk add-script layout-and-rotate --name "Set up a page layout and rotate the visualizations on consecutive runs"
mod-manifest.json:
{
"id": "layout-and-rotate",
"name": "Set up a page layout and rotate the visualizations on consecutive runs",
"entryPoint": "layoutAndRotate",
"file": "build/layout-and-rotate.js",
"description": "Creates a layout where one visualization is enlarged and all others are stacked to its the right. On consecutive runs the enlarged visualization is rotated with one of the smaller ones."
}
src/scripts/layout-and-rotate.ts:
const { DataProperty, DataType, DataPropertyClass, DataPropertyAttributes } = Spotfire.Dxp.Data;
const { LayoutDefinition } = Spotfire.Dxp.Application.Layout;
export function layoutAndRotate({ document }: LayoutAndRotateParameters) {
if (document.ActivePageReference == null) {
throw new Error("No active page open.");
}
const page = document.ActivePageReference;
const visuals = Array.from(page.Visuals);
if (visuals.length < 2) {
throw new Error("No or too few visualizations on the current page to properly layout.");
}
// Find out what index was used last rotation and increment it.
const indexProperty = getIndexProperty();
const nextIndex = (indexProperty.Value + 1) % visuals.length;
const largeVisual = visuals[nextIndex];
// Store the index for next run.
indexProperty.Value = nextIndex;
const layout = new LayoutDefinition();
layout.BeginSideBySideSection();
// Stack with a single visualization to be able to define proportion.
layout.BeginStackedSection(70);
layout.Add(largeVisual);
layout.EndSection();
// Second section containing all other visualizations stacked.
layout.BeginStackedSection(30);
for (const visual of visuals.filter(v => v !== largeVisual)) {
layout.Add(visual);
}
layout.EndSection();
layout.EndSection();
// Apply the layout
page.ApplyLayout(layout);
/**
* Gets the index of the visualization which was enlarged the last time this script ran.
* @returns A property containing the index of the visualization which is currently enlarged.
*/
function getIndexProperty(): Spotfire.Dxp.Data.DataProperty & { get Value(): number } {
const propertyName = "script.layout.index";
const property = OutParam.create(DataProperty);
if (!document.Data.Properties.TryGetProperty(DataPropertyClass.Document, propertyName, property.out)) {
// Hide the document property in the UI.
const attributes = DataProperty.DefaultAttributes.Xor(DataPropertyAttributes.IsVisible);
property.value = DataProperty.CreateCustomPrototype(propertyName, 0, DataType.Integer, attributes);
document.Data.Properties.AddProperty(DataPropertyClass.Document, property);
}
const propertyValue = property.value;
// Validate that the property is of the correct type.
if (typeof propertyValue !== "number") {
throw new Error(`Property ${propertyName} has unexpected type.`);
}
return propertyValue;
}
}
RegisterEntryPoint(layoutAndRotate);
Switch cross table axis
Switches the axis expressions of a cross table visualization.
npx @spotfire/mods-sdk add-script switch-cross-table-axis --name "Switch cross table axis"
mod-manifest.json:
{
"id": "switch-cross-table-axis",
"name": "Switch cross table axis",
"entryPoint": "switchCrossTableAxis",
"file": "build/switch-cross-table-axis.js",
"description": "Switches the axis expressions of a cross table visualization.",
"parameters": [{ "name": "visual", "type": "Visualization", "description": "The cross table visualization." }]
}
src/scripts/switch-cross-table-axis.ts:
const { CrossTablePlot } = Spotfire.Dxp.Application.Visuals;
export function switchCrossTableAxis({ visual }: SwitchCrossTableAxisParameters) {
const crossTable = visual.As(CrossTablePlot);
// Throw an informative error if the user hasn't provided a visualization which is a cross table.
if (crossTable == null) {
throw new Error(
`The visual provided is not a cross table, visualization type is: ${visual.TypeId.DisplayName}.`
);
}
// Store the current expressions in local variables.
const columnAxisExpression = crossTable.ColumnAxis.Expression;
const rowAxisExpression = crossTable.RowAxis.Expression;
// Swap them.
crossTable.ColumnAxis.Expression = rowAxisExpression;
crossTable.RowAxis.Expression = columnAxisExpression;
}
RegisterEntryPoint(switchCrossTableAxis);