v1 Explained
Migration to v1 has started. This page documents the new schema as it exists today and will grow as additional v1 changes are implemented.
Compatibility during the migration
The existing API and v0 catalogue remain available and can continue to be used as usual. New spectral indices can also still be submitted through the normal contribution process. The v1 files are being developed in parallel so that the public API does not have to change all at once.
Table of Contents
Current status
At the present stage, the main v1 schema changes are:
short_nameis renamed toacronym;long_nameis renamed toname;referenceis replaced by a structuredsourceobject with link health, identifier coverage, publication metadata, citations, citation formats, and source companions;- the generated
platformsproperty is no longer included; - spectral bands, radar polarizations, and constants are stored separately, with contributor-defined metadata for constants;
- formula inputs supplied outside spectral data use a dedicated
external_variablesproperty with contributor-defined descriptions; source.source_companionsidentifies other catalogue keys generated from the same scientific source link;source.source_link_semantic_scholaroptionally records contributor-provided Semantic Scholar paper and corpus identifiers;- the formula language introduces calls to
min(),max(), and the unarytanh()andlog()functions; - contextual spatial reductions use
spatial_max(),spatial_min(), andspatial_mean(), with their shared scope declared underreductions; - kernel evaluations use the two-input
kernel()function instead of synthetic operands such askNNandkNR; - application, generated sensing modalities, and optional index families are grouped in
classification; and - wavelength-specific and selectable-range hyperspectral reflectance inputs use dynamic standards such as
R720andR750_800and generate thehyperspectralsensing modality.
Both renamed properties remain required. Neither an acronym nor a name is required to be unique, and neither one serves as the catalogue lookup key.
The current generated catalogues have:
- 280 catalogue keys in v0 and 328 in v1. The versions share 279 keys: the 49 v1-only entries comprise WCI3, CWI, the six Thematic Mapper Tasseled Cap features, 36 hyperspectral indices (including CCRI), MSAVI2, GreenDVI, SUI, NDVISR, and SAVISR, while SR3 remains v0-only;
- identity values generally carried from
short_nametoacronymand fromlong_nametonameacross the 279 shared keys, except for deliberate v1 metadata corrections such as the PSRI, CRI550, and CRI700 names and the distinction between MSAVI and MSAVI2; - the former
referencevalue retained assource.source_link, with generated link status, link type, source-companion metadata, and optional contributed Semantic Scholar identifiers; - formula inputs divided between ordered
bandsandpolarizationsarrays, aconstantsobject, and anexternal_variablesobject containing per-index definitions; - contributor-provided application domains and families grouped under
classification, alongside sensing modalities generated from formula standards; - contextual reduction operations retained directly in
formula, with their dimension-level execution context stored inreductions; - the five kernel-index formulas expressed through
kernel()calls over their underlying bands and constants instead of precomputed kernel operands; - identical
bands.jsonmetadata, plus v1constants.jsonandexternal_variables.jsonfiles that group contributor-provided definitions by variable and index; and - a v1 CSV using
acronym,name,sourcein place ofshort_name,long_name,reference, incorporating the deliberate v1 formula changes and v1-only catalogue additions described above.
The two versions are generated independently and written to different locations:
| Version | Catalogue JSON | Catalogue CSV | Band metadata | Constant metadata | External-variable metadata |
|---|---|---|---|---|---|
| v0 | output/spectral-indices-dict.json | output/spectral-indices-table.csv | output/bands.json | output/constants.json | — |
| v1 | output/v1/spectral-indices-dict.json | output/v1/spectral-indices-table.csv | output/v1/bands.json | output/v1/constants.json | output/v1/external_variables.json |
V1 also generates two source-oriented artifacts that do not exist in v0:
output/v1/spectral-indices-citations.jsonstores the dated citation-count history for every catalogue key; andoutput/v1/spectral-indices-references.bibstores one reusable BibTeX entry per unique source with sufficient bibliographic metadata.
This separation allows v1 to evolve without changing the files currently used by v0 consumers.
Catalogue structure
The top-level JSON structure has not changed. The catalogue is an object with a single SpectralIndices property. Its value is an object whose keys identify the individual spectral indices:
{
"SpectralIndices": {
"SAVI": {
...
},
"NDVI": {
...
}
}
}The outer key, such as SAVI, is the catalogue lookup key. The acronym inside the record is a separate schema property. They normally have the same value in the current catalogue, but the schema does not require them to match. Both catalogue keys and acronyms are case-sensitive.
Schema comparison: SAVI
The Soil-Adjusted Vegetation Index (SAVI) illustrates the current difference between the schemas and the full constant-definition structure.
v1: New schema.v0: Old schema.diff: Difference between schemas (red highlighting means deletion, green highlighting means addition).
{
"SpectralIndices": {
...,
"SAVI": {
"acronym": "SAVI",
"bands": [
"N",
"R"
],
"classification": {
"application_domain": "vegetation",
"family": null,
"sensing_modalities": [
"multispectral"
]
},
"constants": {
"L": {
"default_value": 0.5,
"description": "Canopy background adjustment",
"suggested_range": [
0.25,
1
],
"suggested_values": {
"High vegetation densities": 0.25,
"Intermediate vegetation densities": 0.5,
"Low vegetation densities": 1.0
}
}
},
"contributor": "https://github.com/davemlz",
"date_of_addition": "2021-04-07",
"external_variables": {},
"formula": "(1.0 + L) * (N - R) / (N + R + L)",
"name": "Soil-Adjusted Vegetation Index",
"polarizations": [],
"reductions": {},
"source": {
"source_companions": [],
"source_link": "https://doi.org/10.1016/0034-4257(88)90106-X",
"source_link_semantic_scholar": null,
"source_link_status": "operational",
"source_link_type": "doi",
"source_metadata": {
"authors": [
"A.R Huete"
],
"citations_metrics": {
"citation_count": 6293,
"date": "2026-08-18",
"overall": {
"percentile": 98.4,
"percentile_similar_age": 100.0,
"rank": 6,
"rank_similar_age": 1,
"similar_age_count": 8
},
"within_application_domain": {
"percentile": 98.1,
"percentile_similar_age": 100.0,
"rank": 5,
"rank_similar_age": 1,
"similar_age_count": 8
}
},
"how_to_cite": {
"apa": "A.R Huete (1988). A soil-adjusted vegetation index (SAVI). Remote Sensing of Environment, 25(3). https://doi.org/10.1016/0034-4257(88)90106-x",
"bibtex": "ASI_SAVI"
},
"issue": "3",
"journal": "Remote Sensing of Environment",
"source": "crossref",
"title": "A soil-adjusted vegetation index (SAVI)",
"type": "article",
"volume": "25",
"year": 1988
}
}
},
...
}
}{
"SpectralIndices": {
...,
"SAVI": {
"application_domain": "vegetation",
"bands": [
"L",
"N",
"R"
],
"contributor": "https://github.com/davemlz",
"date_of_addition": "2021-04-07",
"formula": "(1.0 + L) * (N - R) / (N + R + L)",
"long_name": "Soil-Adjusted Vegetation Index",
"platforms": [
"Sentinel-2",
"Landsat-OLI",
"Landsat-TM",
"Landsat-ETM+",
"MODIS",
"Planet-Fusion"
],
"reference": "https://doi.org/10.1016/0034-4257(88)90106-X",
"short_name": "SAVI"
},
...
}
}{
"SpectralIndices": {
...,
"SAVI": {
"acronym": "SAVI",
"application_domain": "vegetation",
"bands": [
"L",
"N",
"R"
],
"classification": {
"application_domain": "vegetation",
"family": null,
"sensing_modalities": ["multispectral"]
},
"constants": {
"L": {
"default_value": 0.5,
"description": "Canopy background adjustment",
"suggested_range": [0.25, 1],
"suggested_values": {
"High vegetation densities": 0.25,
"Intermediate vegetation densities": 0.5,
"Low vegetation densities": 1.0
}
}
},
"contributor": "https://github.com/davemlz",
"date_of_addition": "2021-04-07",
"external_variables": {},
"formula": "(1.0 + L) * (N - R) / (N + R + L)",
"long_name": "Soil-Adjusted Vegetation Index",
"name": "Soil-Adjusted Vegetation Index",
"polarizations": [],
"platforms": [
"Sentinel-2",
"Landsat-OLI",
"Landsat-TM",
"Landsat-ETM+",
"MODIS",
"Planet-Fusion"
],
"reference": "https://doi.org/10.1016/0034-4257(88)90106-X",
"reductions": {},
"source": {
"source_companions": [],
"source_link": "https://doi.org/10.1016/0034-4257(88)90106-X",
"source_link_semantic_scholar": null,
"source_link_status": "operational",
"source_link_type": "doi",
"source_metadata": {
"authors": ["A.R Huete"],
"citations_metrics": {
"citation_count": 6293,
"date": "2026-08-18",
"overall": {
"percentile": 98.4,
"percentile_similar_age": 100.0,
"rank": 6,
"rank_similar_age": 1,
"similar_age_count": 8
},
"within_application_domain": {
"percentile": 98.1,
"percentile_similar_age": 100.0,
"rank": 5,
"rank_similar_age": 1,
"similar_age_count": 8
}
},
"how_to_cite": {
"apa": "A.R Huete (1988). A soil-adjusted vegetation index (SAVI). Remote Sensing of Environment, 25(3). https://doi.org/10.1016/0034-4257(88)90106-x",
"bibtex": "ASI_SAVI"
},
"issue": "3",
"journal": "Remote Sensing of Environment",
"source": "crossref",
"title": "A soil-adjusted vegetation index (SAVI)",
"type": "article",
"volume": "25",
"year": 1988
}
},
"short_name": "SAVI"
},
...
}
}Design decisions
Why the identity properties were renamed
The v0 names describe the two fields relative to one another: short_name is the compact label and long_name is its expanded form. In v1, the properties state their roles directly. acronym is the abbreviation used for display and communication, while name is the human-readable name of the index.
These properties are descriptive metadata rather than catalogue identifiers. Two indices may use the same acronym, the same name, or both, including when they belong to different application domains. The surrounding key in the SpectralIndices mapping remains the exact identifier used to retrieve a record.
Both acronym and name are still required when a source definition is validated. The rename does not make either property optional and does not add any uniqueness constraint.
Why bands and constants were separated
In v0, every named formula variable is stored in a single bands array. That means physical inputs such as N, R, and B appear beside coefficients such as g, C1, C2, and L, even though they play different roles when an index is evaluated.
V1 uses separate Bands, Hyperspectral, Polarizations, Constants, and External standards. Formula variables are classified against those standards: spectral, thermal, and wavelength-specific hyperspectral inputs are generated into bands; radar inputs are generated into polarizations; the contributor supplies metadata for every constant; and separately supplied contextual inputs are defined in external_variables. For SAVI, this produces two required bands and one configurable constant instead of one mixed three-item list.
Constant values are no longer taken from one universal metadata dictionary. Each index provides its own description and may provide its own default, so the same standard constant can be documented or initialized differently for different indices.
Why classification is structured
The v0 application_domain vocabulary mixes scientific applications with other ways of describing an index. vegetation, water, and burn describe applications, while kernel describes an index family and radar describes a sensing modality as well as the current radar family.
V1 moves the required, contributor-provided application domain into classification.application_domain. The optional contributor-provided classification.family list currently accepts kernel, tasseled_cap, and radar. All five kernel indices are classified under the vegetation application with the kernel family. All radar indices use the vegetation application except NDPolI, which uses the new geology application; all thirteen use the radar family.
classification.sensing_modalities is not submitted by contributors. The generator derives it from the formula standards and may include multispectral, hyperspectral, thermal, or radar. A formula combining different input types receives every applicable value. The current catalogue contains 256 multispectral indices, 20 multispectral-plus-thermal indices, 13 radar indices, and 39 hyperspectral indices.
Why hyperspectral wavelength standards are dynamic
Hyperspectral formulas often identify reflectance by a specific wavelength rather than by a broad sensor band. Enumerating every possible wavelength as a separate enum member would make the operand registry unnecessarily large, so v1 defines the dynamic Hyperspectral standard.
The canonical form is an uppercase R followed immediately by an integer wavelength in nanometres. The supported interval is inclusive from R300 to R2500. Thus R300, R542, and R2487 are valid, while R299, R2501, R0300, r542, and R542nm are not. The existing bare R standard continues to mean the broad multispectral red band, so it does not conflict with R720. Possible future numbered broad-band standards below 300 are outside this rule and are not introduced by the hyperspectral range.
Some sources specify an admissible wavelength interval instead of one exact wavelength. V1 represents that input with R<lower>_<upper>, such as R750_800. HyperspectralRange validates both integer bounds on demand; it does not enumerate every possible interval. Both bounds must be within 300–2500 nm and the lower bound must be smaller than the upper bound. The interval is inclusive.
A range operand means that one wavelength within the interval is selected for an evaluation or dataset. It is not an average, minimum, maximum, or other aggregation over the interval. Repeated occurrences of the same operand must resolve to the same selected wavelength.
Wavelength operands are written to the generated bands list in first-seen order and cause classification.sensing_modalities to contain hyperspectral. A formula can later combine hyperspectral operands with other input standards, in which case generation will retain all applicable sensing modalities.
Why polarizations are separate from bands
HH, HV, VH, and VV describe radar transmit/receive polarizations, not spectral bands. V1 therefore registers them in Polarizations and writes them to the generated polarizations array. Radar-only indices have an empty bands array, while mixed formulas could populate both arrays. This separation also provides the input information used to generate the radar sensing modality.
Why external variables are separate
Some formulas require a value that is neither an observed spectral band nor a configurable mathematical coefficient. NIRvP, for example, uses photosynthetically active radiation (PAR) in addition to near-infrared and red reflectance. PAR must be supplied when evaluating the formula, but it does not have a universal catalogue default and should not be presented as a tunable constant.
V1 therefore registers PAR in the dedicated External registry. NIRvP describes it under external_variables, while bands contains only N and R and constants remains empty. This makes the source of each input clear without prescribing how consumers obtain, scale, or align the external data.
Why contextual reductions are separate
Ordinary spectral-index arithmetic is evaluated independently at every pixel. Some published indices additionally need a statistic calculated from a larger data context. CWI, for example, divides values using the maximum of S2 and the maximum of B across the area of interest. Each maximum is a scalar that is then reused for every output pixel; it is not the pointwise maximum of multiple bands.
V1 keeps S2 and B as the only operands and expresses the operations directly as spatial_max(S2) and spatial_max(B). The separate reductions property supplies the spatial context shared by those operations. This avoids creating synthetic operands such as S2_max and keeps context configuration out of the mathematical expression.
Why kernel evaluations are functions
The v0 kernel indices encode evaluated kernel terms as operands. For example, kNN means the kernel evaluated between N and N, while kNR means the kernel evaluated between N and R. Treating every pair as another band expands the supported operand registry even though these values are derived operations rather than observed inputs.
V1 expresses the operation directly as kernel(X, Y). The kNDVI formula now uses kernel(N, N) and kernel(N, R), and its generated bands array contains only the underlying N and R inputs. The same change applies to kEVI, kRVI, kVARI, and kIPVI. The former kNN, kNR, kNB, kNL, kGG, kGR, kGB, kBB, kBR, kBL, kRR, kRB, kRL, and kLL values are no longer v1 band standards.
Why platforms is absent
In v0, platforms is not authored as part of an index definition. The v0 generator derives it by comparing every variable in the formula with a hard-coded set of platform band profiles. It then attaches the resulting list to the serialized index record.
The v1 generator deliberately skips that inference step. It still parses the formula and creates bands, polarizations, constants, external_variables, and sensing modalities, but it neither calculates nor serializes platforms. Platform compatibility will be handled separately as the v1 migration develops; sensing modality should not be interpreted as a replacement list of compatible platforms.
The omission applies only to the v1 index records. v0 continues to generate platforms, so existing consumers are not required to migrate now. The absence of platforms in v1 should also not be interpreted as meaning that an index is compatible with every sensor.
Property reference
Every serialized v1 index currently contains the following twelve properties:
| Property | JSON type | Purpose |
|---|---|---|
acronym | string | Concise, case-sensitive acronym |
bands | array of strings | Ordered spectral, thermal, and hyperspectral inputs extracted from the formula |
classification | object | Application, generated sensing modalities, and optional families |
constants | object of objects | Per-index metadata and recommendations for formula constants |
contributor | string | Contributor's GitHub profile or email address |
date_of_addition | string | Valid calendar date in YYYY-MM-DD form |
external_variables | object of objects | Descriptions of formula inputs supplied outside spectral data |
formula | string | Validated mathematical expression |
name | string | Human-readable name of the index |
polarizations | array of strings | Ordered radar-polarization inputs extracted from the formula |
reductions | object | Shared execution contexts for contextual formula reductions |
source | object | Scientific source and generated link metadata |
Unknown properties are rejected when source definitions are validated. bands, polarizations, and classification.sensing_modalities are generated from formula. constants, external_variables, and reductions may be omitted only when the formula does not require them; when it does, the contributor must provide the corresponding definitions. Generated records use empty arrays or objects where an index has no corresponding inputs, definitions, or reduction context.
acronym
"SAVI": {
...,
"acronym": "SAVI",
...
}The concise abbreviation commonly used for the index in publications, code, and catalogue interfaces. It is case-sensitive and must not contain whitespace.
The current schema does not require acronyms to be unique and does not require acronym to equal the surrounding catalogue key. Multiple indices may share an acronym, including indices from different application domains. Consumers should therefore use the outer catalogue key when they need the exact key under which an entry is stored, and use acronym as display metadata.
name
"SAVI": {
...,
"name": "Soil-Adjusted Vegetation Index",
...
}The human-readable name of the spectral index. It is intended for documentation, search results, tables, and interfaces where the acronym alone would be ambiguous.
The current schema requires a string but does not impose a naming convention or require the value to be unique. Multiple indices may therefore have the same name, including indices used in different application domains.
classification
"kNDVI": {
...,
"classification": {
"application_domain": "vegetation",
"family": ["kernel"],
"sensing_modalities": ["multispectral"]
},
...
}The object has three classification facets:
| Property | Contributor-provided | Required in source | Generated |
|---|---|---|---|
application_domain | Yes | Yes | No |
family | Yes | No | No |
sensing_modalities | No | No | Yes |
classification.application_domain
The broad scientific application used to organize and style the catalogue. The lowercase, case-sensitive value must be one of:
vegetationwaterburnsnowsoilurbangeologyclouds
An index has exactly one application domain in the current schema. It is required from the contributor and does not describe a sensing modality, platform, or computational method.
classification.sensing_modalities
A generated list containing one or more of multispectral, hyperspectral, thermal, and radar. Registered fixed non-thermal bands produce multispectral; exact wavelength operands from R300 through R2500 and selectable wavelength ranges such as R750_800 produce hyperspectral; T, T1, and T2 produce thermal; and registered radar polarizations produce radar. The generator uses the canonical order multispectral, hyperspectral, thermal, then radar.
The list describes required measurement types, not compatible platforms. For example, NBRT1 is generated with ["multispectral", "thermal"] because its formula combines N and S2 with T.
classification.family
An optional contributor-provided list containing one or more of kernel, tasseled_cap, and radar. Empty lists, duplicates, and unsupported values are rejected. When no family has been assigned, generated records serialize the property as null.
The family is independent of application and modality. kNDVI is in the kernel family but has vegetation as its application and multispectral as its generated sensing modality.
bands
"SAVI": {
...,
"bands": [
"N",
"R"
],
...
}An ordered list of the registered spectral, thermal, and hyperspectral inputs used by formula. Variables are recorded in the order in which they first appear, and repeated variables appear only once.
Formula constants, coefficients, and wavelength parameters are written to constants; radar inputs are written to polarizations; and inputs from the External registry are written to external_variables. A function result is not added as a band: the variables inside kernel(N, R) are extracted as N and R.
For SAVI, N and R refer to near-infrared and red inputs. Descriptions of fixed registered bands are available in bands.json. Hyperspectral operands are self-describing wavelength standards and are not expanded into 2,201 entries—or every possible wavelength interval—in that file. R720 means reflectance at 720 nm, while R750_800 means reflectance at one selected wavelength from 750 through 800 nm, inclusive.
bands describes the inputs needed to evaluate the expression. In v1 it is not a list of supported platforms and does not, by itself, guarantee that a particular sensor provides every required input.
polarizations
"NDPolI": {
...,
"polarizations": [
"VV",
"VH"
],
...
}An ordered, generated list of radar-polarization variables used by formula. The supported standards are HH, HV, VH, and VV. They are recorded in first-seen order and deduplicated, using the same extraction behavior as bands.
NDPolI therefore has polarizations equal to ["VV", "VH"] and an empty bands array. Indices with no radar inputs are serialized with an empty polarizations array ([]).
constants
"SAVI": {
...,
"constants": {
"L": {
"default_value": 0.5,
"description": "Canopy background adjustment",
"suggested_range": [0.25, 1],
"suggested_values": {
"High vegetation densities": 0.25,
"Intermediate vegetation densities": 0.5,
"Low vegetation densities": 1.0
}
}
},
...
}An object mapping every registered constant used by formula to that index's definition. Each definition requires a string description and may include a numeric default_value. Integers and floating-point values are accepted; numeric strings and booleans are rejected. If no default is appropriate, the property is omitted rather than written as null.
Two further recommendation properties are optional:
| Property | Type | Meaning |
|---|---|---|
suggested_values | object | Conditions mapped to a numeric value or a two-number range |
suggested_range | two-number array | General recommended interval for the constant |
The keys of suggested_values are free-form condition descriptions supplied by the contributor. Each value must be either one number or an array containing exactly two numbers. suggested_range must likewise contain exactly two numbers. These fields document recommendations from the scientific source; they do not override default_value automatically.
The property is conditionally required. A formula that uses constants cannot be submitted without constants, and every formula constant must have exactly one definition. Missing constants, extra constants, and band names incorrectly placed in this object all fail validation. An index whose formula uses no constants may omit the constants property and is serialized with an empty object ({}).
Defaults are index-specific starting points rather than fixed mathematical requirements. Consumers may override them when an implementation or data source requires another value. Parameters such as lambdaN and lambdaR can omit default_value when the appropriate wavelength depends on the sensor.
The generated constants.json transposes these definitions for discovery. Its top-level keys are the standard constants, and each value maps catalogue keys to their submitted definition:
"L": {
"SAVI": {
"default_value": 0.5,
"description": "Canopy background adjustment",
"suggested_range": [0.25, 1],
"suggested_values": {
"High vegetation densities": 0.25,
"Intermediate vegetation densities": 0.5,
"Low vegetation densities": 1.0
}
},
"SAVIT": {
"default_value": 1.0,
"description": "Canopy background adjustment"
}
}The old short_name metadata is not included. A registered constant with no current users is retained as an empty object so the file continues to expose the complete standard registry.
external_variables
"NIRvP": {
...,
"external_variables": {
"PAR": {
"description": "Photosynthetically Active Radiation"
}
},
...
}An object mapping every registered external input used by formula to its index-specific definition. Each definition contains exactly one required property, description, whose value must be a string. Constant-oriented properties such as default_value, suggested_values, and suggested_range are rejected, as are any other unknown properties.
The property is conditionally required. A formula using an external variable cannot be submitted without its definition, and definitions not used by the formula are rejected. Indices without external inputs may omit the property in source code and are serialized with an empty object ({}).
NIRvP currently uses PAR, or photosynthetically active radiation. The catalogue describes the required external input but does not assign a default value or specify how it must be measured, resampled, or aligned with the spectral inputs.
The generated external_variables.json transposes these definitions in the same way as constants.json: top-level keys are registered external-variable standards, and each value maps catalogue keys to their definitions.
"PAR": {
"NIRvP": {
"description": "Photosynthetically Active Radiation"
}
}A registered external variable with no current users is retained as an empty object, so the file exposes the complete External registry.
contributor
"SAVI": {
...,
"contributor": "https://github.com/davemlz",
...
}Identifies the person who contributed the catalogue entry. The current validator accepts either a GitHub profile URL or an email address. A GitHub profile is preferred in most existing entries because it provides a durable, public attribution link.
This property identifies the catalogue contributor, who is not necessarily an author of the scientific publication referenced by the index.
date_of_addition
"SAVI": {
...,
"date_of_addition": "2021-04-07",
...
}The calendar date on which the index was added to Awesome Spectral Indices. It is validated as a real date and serialized in ISO 8601 date form: YYYY-MM-DD.
This is catalogue provenance metadata. It is not the publication date of the index, the acquisition date of an image, or the release date of the catalogue.
formula
"SAVI": {
...,
"formula": "(1.0 + L) * (N - R) / (N + R + L)",
...
}The mathematical expression used to calculate the index. Variable names are case-sensitive and correspond to standards later written to bands, polarizations, constants, or external_variables.
Formulas use a deliberately small expression language. The current validator allows:
- addition, subtraction, multiplication, division, and exponentiation;
- unary positive and negative values;
- integer and floating-point numeric literals;
- supported variable names;
- calls to the explicitly allowed
min()andmax()functions; - calls to the explicitly allowed unary
tanh()andlog()functions; - calls to the explicitly allowed two-input
kernel()function; and - calls to the contextual
spatial_max(),spatial_min(), andspatial_mean()reduction functions.
Attribute access, indexing, keyword arguments, arbitrary function calls, strings, booleans, and other Python syntax are rejected. The expression is parsed and inspected without being evaluated. Every variable must match a fixed registered standard, an exact hyperspectral wavelength, or a validated hyperspectral wavelength-range operand before an index can be generated.
Hyperspectral operands: CARI
CARI is the first v1 catalogue entry to use wavelength-specific hyperspectral reflectance operands:
"CARI": {
...,
"bands": ["R720", "R521"],
"classification": {
"application_domain": "vegetation",
"family": null,
"sensing_modalities": ["hyperspectral"]
},
"formula": "(R720 / R521) - 1",
...
}The contributor writes only the formula and the required application domain. Validation recognizes both variables through the Hyperspectral range standard. Generation preserves their first-seen order in bands and derives the hyperspectral modality; no contributor-provided band declarations or modality value are needed. The numbers represent wavelengths in nanometres, not sensor band numbers.
Selectable hyperspectral ranges: NDISI
NDISI is the first v1 catalogue entry whose source permits selecting one wavelength from each of two spectral intervals:
"NDISI": {
...,
"bands": ["R1080_1120", "R1760_1800"],
"classification": {
"application_domain": "snow",
"family": null,
"sensing_modalities": ["hyperspectral"]
},
"formula": "(R1080_1120 - R1760_1800)/(R1080_1120 + R1760_1800)",
...
}R1080_1120 means one reflectance wavelength selected from the inclusive 1,080–1,120 nm interval; R1760_1800 has the equivalent meaning for 1,760–1,800 nm. Each is a single formula operand and a single generated band. The endpoints are not extracted separately, and the notation does not imply aggregation across all wavelengths in either interval. An implementation must use each selected wavelength consistently throughout one evaluation.
Functions in formulas: WCI3
WCI3 is the first catalogue entry that exercises nested allowed functions in a formula:
"WCI3": {
...,
"bands": ["B", "R", "G", "RE1", "N"],
"constants": {
"epsilon": {
"default_value": 1e-10,
"description": "Adjustment constant for numerical stability"
}
},
"formula": "((B - R)/(B + R + epsilon)) * tanh(R - max(B, G, RE1, N))",
...
}Here, max(B, G, RE1, N) selects the greatest of four spectral inputs and the unary tanh() call transforms the resulting difference. The parser visits the arguments recursively, so the function names themselves do not appear in generated metadata: the five spectral variables become bands, while epsilon is validated and serialized under constants.
Function calls remain deliberately constrained. min() and max() accept positional arguments, while tanh() and log() accept exactly one positional argument. log() denotes the natural logarithm. Keywords, qualified calls such as math.log(), and arbitrary functions are rejected.
Function-call syntax is a v1 formula-language feature and is not part of the v0 catalogue schema. V1 formally introduces min(), max(), tanh(), and log(), with WCI3 as the first catalogue entry to use the first three. WCI3 is therefore a v1-only index and cannot be added unchanged to the v0 catalogue.
NDNI and NDLI are the first catalogue entries to use log(). For example, NDNI represents its source equation directly as:
"NDNI": {
...,
"bands": ["R1510", "R1680"],
"formula": "(log(1.0 / R1510) - log(1.0 / R1680))/(log(1.0 / R1510) + log(1.0 / R1680))",
...
}Each log() call is evaluated per pixel. Its wavelength arguments are still extracted as hyperspectral bands, while log itself does not become an operand or generated catalogue property.
Kernel evaluations: kNDVI
kNDVI shows how v1 represents kernel evaluations without introducing derived kernel operands:
"kNDVI": {
...,
"bands": ["N", "R"],
"classification": {
"application_domain": "vegetation",
"family": ["kernel"],
"sensing_modalities": ["multispectral"]
},
"formula": "(kernel(N, N) - kernel(N, R)) / (kernel(N, N) + kernel(N, R))",
...
}The correspondence with the previous notation is direct:
| Previous operand | V1 expression |
|---|---|
kNN | kernel(N, N) |
kNR | kernel(N, R) |
kernel() requires exactly two positional expressions and accepts no keyword arguments. Its arguments are visited recursively by the formula parser, so function names and evaluated kernel results never enter the operand registry. Repeated inputs are deduplicated in generated band metadata, which is why the four calls in kNDVI still produce only ["N", "R"].
The migrated kEVI formula also makes a previously implicit dependency explicit: kNL becomes kernel(N, L). Consequently, kEVI now lists N, R, and B as bands and defines L as its canopy-background constant alongside g, C1, and C2.
At this stage, kernel() represents the kernel evaluation required by the index formula and classification.family identifies the index as kernel. The catalogue does not yet prescribe a specific kernel function or its runtime implementation. Consumers must use a compatible kernel definition when evaluating these indices.
Contextual reduction functions: CWI
CWI is the first catalogue entry to require values reduced across a spatial context:
"CWI": {
...,
"bands": ["S2", "B"],
"formula": "(spatial_max(S2) * B) / (spatial_max(B) * S2)",
"reductions": {
"space": {
"scope": "aoi"
}
},
...
}Both spatial_max() calls calculate one scalar maximum across all valid pixels in the supplied area of interest. Those scalars are broadcast when the remaining formula is evaluated per pixel. This differs from max(B, G, RE1, N), which compares several inputs at each individual pixel.
The three initial contextual functions are:
| Function | Result |
|---|---|
spatial_max(X) | Maximum valid value of X within the configured spatial scope |
spatial_min(X) | Minimum valid value of X within the configured spatial scope |
spatial_mean(X) | Arithmetic mean of the valid values of X within the configured spatial scope |
Each function requires exactly one positional expression and does not accept keyword arguments. Its input variables are still extracted normally: CWI therefore generates bands as ["S2", "B"], without adding the function names or synthetic reduction results to the operand metadata.
The formula string preserves the published implementation of the index. Consumers are responsible for supplying the variables with compatible units, scaling, and preprocessing.
reductions
"CWI": {
...,
"reductions": {
"space": {
"scope": "aoi"
}
},
...
}An object describing the execution context shared by contextual reductions in the formula. The formula identifies the reducer and dimension: for example, spatial_mean() identifies a mean over space. reductions.space.scope then identifies which spatial extent supplies the values.
The two currently supported spatial scopes are:
| Scope | Meaning |
|---|---|
aoi | All valid pixels within the area of interest supplied for evaluation |
scene | All valid pixels within the complete input scene supplied for evaluation |
The property is conditionally required and validated in both directions. An index using any spatial_*() function must provide reductions.space, while an index with no spatial reduction cannot provide an unused space definition. Every spatial reduction in one formula currently shares the same scope. Indices without contextual reductions may omit reductions in their source definition and are serialized with an empty object ({}).
reductions configures operations already present in the formula; it does not declare additional operands or repeat each function call. No separate reductions.json file is generated. Implementations must evaluate the reduction over the configured context before broadcasting its scalar result through the pixelwise portion of the formula.
source
"SAVI": {
...,
"source": {
"source_companions": [],
"source_link": "https://doi.org/10.1016/0034-4257(88)90106-X",
"source_link_semantic_scholar": null,
"source_link_status": "operational",
"source_link_type": "doi",
"source_metadata": {
"authors": ["A.R Huete"],
"citations_metrics": {
"citation_count": 6293,
"date": "2026-08-18",
"overall": {
"percentile": 98.4,
"percentile_similar_age": 100.0,
"rank": 6,
"rank_similar_age": 1,
"similar_age_count": 8
},
"within_application_domain": {
"percentile": 98.1,
"percentile_similar_age": 100.0,
"rank": 5,
"rank_similar_age": 1,
"similar_age_count": 8
}
},
"how_to_cite": {
"apa": "A.R Huete (1988). A soil-adjusted vegetation index (SAVI). Remote Sensing of Environment, 25(3). https://doi.org/10.1016/0034-4257(88)90106-x",
"bibtex": "ASI_SAVI"
},
"issue": "3",
"journal": "Remote Sensing of Environment",
"source": "crossref",
"title": "A soil-adjusted vegetation index (SAVI)",
"type": "article",
"volume": "25",
"year": 1988
}
},
...
}The structured scientific source used to support the index definition. It preserves the link previously stored in reference, accepts optional publication metadata from the contributor, and adds generated information about the link and publication.
The object has six properties:
| Property | Required from contributor | Generated | Allowed values |
|---|---|---|---|
source_companions | No | Yes | Array of catalogue keys |
source_link | Yes | No | HTTP or HTTPS URL |
source_link_semantic_scholar | No | No | Object or null; see below |
source_link_status | No | Yes | operational, down |
source_link_type | No | Yes | doi, other |
source_metadata | No | Enriched and extended during generation | Object; see below |
source_companions
A generated list of the case-sensitive catalogue keys of every other index whose source_link is exactly the same. The current index is never included in its own list, duplicate keys are not produced, and an index with a unique source link receives an empty list ([]).
For example, NDVI and TVI are part of the same source. NDVI therefore has the following generated value:
"source_companions": ["TVI"]This property describes shared catalogue provenance. It does not imply that the companion indices use the same formula, bands, constants, application domain, or interpretation. Generated VitePress pages link each companion key to its corresponding index page.
source_link
The original source URL supplied by the contributor. It normally points to a DOI resolver or another stable page containing the publication in which the formula is described. It is required and must be an HTTP or HTTPS URL.
This is the v1 location of the value previously stored directly in reference. The link provides provenance for reviewing the formula and its intended use; it does not by itself establish scientific validity.
source_link_semantic_scholar
An optional contributor-provided object containing identifiers for the same source in Semantic Scholar. It is especially useful, and therefore suggested, when source_link is not a DOI resolver URL. It remains optional for all sources and may also be supplied alongside a DOI.
The object contains two independently optional properties:
| Property | Meaning | Validation |
|---|---|---|
paper_id | Semantic Scholar paper identifier | Non-empty alphanumeric string containing only A-Z, a-z, and 0-9 |
corpus_id | Semantic Scholar corpus identifier | Non-negative integer |
For example, NDVI supplies both identifiers because its canonical source link points to NASA NTRS rather than a DOI resolver:
"source_link_semantic_scholar": {
"paper_id": "fb2f60fe0fe2874e5cbf927a2556d719c32eac29",
"corpus_id": 133358670
}The identifiers supplement source_link; they do not replace it, alter source_link_type, or participate in source-companion grouping. Unknown nested properties, non-alphanumeric paper IDs, and non-integer corpus IDs are rejected during validation.
During generation, these identifiers enable metadata retrieval through the Semantic Scholar Academic Graph when the source is not a DOI or when neither a live nor cached Crossref record is available for its DOI. The generator tries paper_id first and uses corpus_id only when that lookup returns no paper. All indices sharing the exact source_link receive the successfully retrieved metadata, even when the identifiers were contributed on only one of those indices.
source_link_status
A generated snapshot of whether source_link can be reached. operational means the generator received a successful response or a response showing that the resource exists but restricts automated access. down means the link returned a not-found, gone, or server-error response, or remained unreachable after repeated attempts.
The generator checks each unique URL once per run and shares the result among indices that use the same source. The value can change between catalogue generations as websites become available or unavailable. It reports link availability only; it does not assess the content or scientific quality of the source.
source_link_type
A generated classification of the URL. It is doi when the link uses the doi.org or dx.doi.org resolver and other for every other valid HTTP or HTTPS source. This property describes the submitted link form, not whether a publication reachable through another website also has a DOI.
source_metadata
An optional object containing the publication metadata available for the source. Contributors may submit any of its descriptive publication fields; when they do, they must set source to contributor. DOI resolver links are then looked up by DOI through Crossref. A successful Crossref lookup replaces the whole contributor-provided metadata object, including type, and records crossref as the provider. This makes the registry record authoritative while preserving contributed metadata as a fallback when Crossref is unavailable.
For a non-DOI link, or a DOI not available from Crossref, supplied Semantic Scholar identifiers trigger the fallback Academic Graph lookup. A successful lookup replaces contributor metadata and records semantic_scholar as the provider. Contributor metadata is retained when neither registry supplies a record. Fields without a value are simply omitted, so the object may contain only a subset of the available properties or remain empty when no metadata source supplies information.
Every nested property is optional:
| Property | Meaning |
|---|---|
type | Optional source classification; contributed or retrieved |
title | Optional publication or source title; contributed or retrieved |
journal | Optional journal, proceedings, or other containing publication; contributed or retrieved |
volume | Optional journal or proceedings volume; contributed or retrieved |
issue | Optional journal or proceedings issue; contributed or retrieved |
authors | Optional ordered list of author display names; contributed or retrieved |
year | Optional publication year; contributed or retrieved |
citations_metrics | Generated latest dated citation-count snapshot and comparative metrics |
how_to_cite | Generated BibTeX key and APA citation |
source | Metadata provider: contributor, crossref, semantic_scholar, or other |
type
An optional classification of the scientific source. Contributors may supply it along with the other descriptive metadata. Registry enrichment replaces a contributed value with the normalized Crossref or Semantic Scholar publication type when that type has a clear equivalent in this catalogue. Any value must be exactly one of:
articlebookbook_chapterconference_paperposterreportpreprint
The property remains absent when it has not been classified and the selected registry does not provide a supported equivalent. SAVI is classified by Crossref as an article. Semantic Scholar currently supplies bibliographic metadata and citations for NDVI and TVI but no publication type for that record, so their generated type is omitted rather than inferred.
Contributor-provided metadata
Contributors may provide type, title, journal, volume, issue, authors, and year. Every field is optional, but any non-empty contributed object must identify its provenance with source: "contributor". SWM, whose poster source is not currently enriched by Crossref or Semantic Scholar, provides this metadata directly:
"source_metadata": {
"authors": [
"Marta Milczarek",
"Anna Robak",
"Alicja Gadawska"
],
"journal": "7th Advanced Training Course on Land Remote Sensing, Szent István University, Gödöllő, Hungary 4-9 September 2017",
"source": "contributor",
"title": "Sentinel Water Mask (SWM) - new index for water detection on Sentinel-2 images",
"type": "poster",
"year": 2017
}Contributors must not submit citations_metrics or how_to_cite; these are generated outputs. If Crossref has a record for a DOI, its metadata replaces every contributed descriptive field rather than merging the two records. Otherwise, an available Semantic Scholar record does the same. The contributor record is kept unchanged only when neither applicable registry supplies metadata.
citations_metrics
The generated citations_metrics object combines the latest citation snapshot with comparative catalogue statistics. citation_count is a non-negative integer and date is its retrieval date in YYYY-MM-DD format. Crossref records use is-referenced-by-count; Semantic Scholar records use citationCount. Citation coverage differs between providers, so the count is a dated snapshot from the provider named by source, not a universal count across all citation databases.
The remaining properties are regenerated from the complete current catalogue:
| Property | Meaning |
|---|---|
overall | Rank and percentile among every index with citation data |
within_application_domain | The same metrics restricted to the index's application domain |
Both comparison objects contain rank, percentile, rank_similar_age, percentile_similar_age, and similar_age_count. The count is nested because the overall age cohort and the application-domain age cohort can have different sizes. Ranks are one-based and ordered from the greatest citation count to the smallest. When counts are equal, catalogue keys are ordered alphabetically, so every index still receives a deterministic unique rank. The similar-age values repeat the calculation within an inclusive five-year window: publication year minus two through publication year plus two. within_application_domain applies the domain restriction before both its full and age-window comparisons.
A percentile is 100 × (comparison indices with a citation count less than or equal to this index's count) / comparison population size, rounded to two decimal places. Equal citation counts therefore share a percentile even though alphabetical ordering gives them different ranks. The greatest count is in the 100th percentile. When an index has citation data but no publication year, similar_age_count is 0 in both comparison objects and both similar-age rank and percentile values are null.
The separate spectral-indices-citations.json file retains only the chronological citation_count and date snapshots for each catalogue key, including an empty array where no count is available. Comparative metrics are not historical snapshots: they are recalculated whenever the catalogue is generated, because they depend on the current set of indices and counts.
how_to_cite
The generated how_to_cite object contains:
bibtex: the citation key of the shared entry inspectral-indices-references.bib; andapa: an APA-style citation assembled from the retrieved publication metadata.
For SAVI, the generated value is:
"how_to_cite": {
"apa": "A.R Huete (1988). A soil-adjusted vegetation index (SAVI). Remote Sensing of Environment, 25(3). https://doi.org/10.1016/0034-4257(88)90106-x",
"bibtex": "ASI_SAVI"
}bibtex is the entry key, not the complete BibTeX record. Consumers can use it to locate @article{ASI_SAVI, ...} in spectral-indices-references.bib. The complete APA citation is embedded directly as apa.
Indices whose DOI resolves to the same publication receive the same BibTeX key and APA citation. Contributed metadata can also generate these values when it contains at least one bibliographic field such as a title, author, venue, or year; non-DOI records sharing the same source URL then reuse one entry. The .bib file therefore contains one item per unique usable source rather than one duplicate item per spectral index.
Provider lookup and caching
Crossref responses are cached for seven days. One request is made per unique DOI rather than per index, so indices sharing a publication reuse the same response. Requests are sequential and rate-limited, identify the project with a user agent, use the configured CROSSREF_EMAIL for Crossref's polite pool when available, and retry temporary failures with bounded backoff. Cached metadata is preserved if a refresh temporarily fails.
Semantic Scholar responses are also cached for seven days and grouped by exact source link. The generator uses the Academic Graph paper batch endpoint, submitting at most 500 identifiers per request. It batches all preferred paper IDs first and then batches corpus IDs only for unresolved sources. Requests send SEMANTIC_SCHOLAR_API_KEY in the x-api-key header when configured and are spaced by at least 1.1 seconds, keeping the client below the one-request- per-second cumulative limit. Temporary failures use bounded retry backoff and previously generated metadata remains available as a cache fallback.
Unknown source properties and unsupported source_metadata.type values are rejected during validation. Contributor metadata without source: "contributor" is also rejected, as are contributor-supplied citations_metrics and how_to_cite. source_companions, source_link_status, and source_link_type are always derived during generation.
How the v1 files are generated
The current v1 generation process is:
- Source definitions are loaded into validated Pydantic models.
- Each formula is parsed and validated. Its variables are classified as bands, polarizations, constants, or external variables. Constants and external variables are checked against the definitions submitted with that index, and contextual reduction dimensions are checked against
reductions. - Sensing modalities are generated from the extracted band and polarization standards and added to
classification. - Each source link is classified as
doiorother. - Each unique source link is checked and marked
operationalordown. - Unique DOI sources are enriched from Crossref. Cached records younger than seven days are reused. A successful or cached Crossref record replaces the complete contributor metadata block and its work type is normalized where a supported catalogue value exists. Contributor metadata is retained when no Crossref record is available.
- Non-DOI sources and DOI sources without Crossref metadata are enriched from Semantic Scholar when contributed identifiers are available. Paper IDs are batched first; unresolved sources are retried by corpus ID. Successful metadata is shared across indices using the same exact source link.
- Indices are grouped by exact source link and receive the keys of every other index in that group as
source_companions. - Citation ranks and percentiles are generated overall, within application domains, and within inclusive publication-year windows of plus or minus two years. The latest count and date are then merged into each index's history in
spectral-indices-citations.json. A run on the same date replaces that date's snapshot rather than duplicating it. - Unique sources with usable retrieved or contributed metadata are rendered once in
spectral-indices-references.bib; associated indices receive their shared BibTeX key and APA citation insource_metadata.how_to_cite. - The catalogue is serialized to JSON and flattened into the public CSV.
- Band metadata is written alongside the catalogue. Per-index constant and external-variable definitions are grouped by their respective standards in
constants.jsonandexternal_variables.json.
Unlike v0, there is no platform-inference step between formula parsing and serialization. The generated JSON preserves the nested SpectralIndices mapping, while the CSV contains one row per index with these columns:
acronym, name, classification, formula, bands, polarizations, constants,
external_variables, reductions, source, contributor, date_of_additionThe CSV does not contain the outer catalogue key as a separate column, and its classification, bands, polarizations, constants, external_variables, reductions, and source values are textual representations of their nested data. Applications that need keyed records, native JSON arrays, or native JSON objects should use spectral-indices-dict.json.
spectral-indices-citations.json maps every current catalogue key to an array of dated citation snapshots. Indices without an available count have an empty array. New retrieval dates are appended in chronological order, allowing the file to preserve citation-count history over time.
spectral-indices-references.bib is a deduplicated bibliography of retrieved or contributor-described scientific sources. DOI links are grouped by their normalized DOI; other links are grouped by their exact URL. A new citation key uses the first case-sensitive catalogue key in that source group with an ASI_ prefix. Once generated, the key is reused from the cache so adding another index for the same source does not rename the existing bibliography item.
More changes will be documented here
This page describes only what has already been implemented. As the v1 schema evolves, new properties, changed behavior, and migration guidance will be added to this page.