Colors & Classification

How iXMaps maps data values to colors: continuous classification methods, categorical color binding, colorscheme formats, diverging scales and data-driven palettes.

A CHOROPLETH or CHART layer maps a value field to a color. This guide covers the two halves of that mapping: classification (how values are grouped into classes) and color schemes (which colors those classes get).


Continuous vs. discrete classification

How the value→color mapping is done depends on whether the data is continuous numeric or discrete categorical.

Continuous numeric values — a number that can take any value in a range (population, temperature, GDP). Classification groups the range into classes and assigns a colour to each class:

Method How classes are built
EQUIDISTANT Equal-width intervals across the data range
QUANTILE Equal-count — same number of features per class
HEADTAIL Iterative mean split — best for heavy-tailed distributions
NATURAL Jenks natural breaks — minimises within-class variance
LOG Logarithmic intervals — for data spanning orders of magnitude
// Continuous: 5 quantile classes, yellow → dark blue
.type("CHOROPLETH|QUANTILE")
.style({ colorscheme: ["#ffffcc","#41b6c4","#253494"], showdata: "true" })

Discrete categorical values — a label or code that belongs to a fixed set ("forest", "urban", "water"; or species names, party names, land-use codes). Use CATEGORICAL and pair it with a values array that maps each category to a colour slot:

// Discrete: three categories, each pinned to a colour
.type("CHART|BUBBLE|CATEGORICAL")
.style({
    colorscheme: ["#4fc3f7", "#ffb300", "#ef5350"],
    values:      ["low",     "medium",  "high"],   // strings — not numbers!
    showdata:    "true"
})
Warningvalues must be strings

iXMaps matches category values as strings. Numeric categories like 1, 2, 3 are silently ignored — always pass ["1","2","3"].

The classification methods work with both CHOROPLETH (polygon fill) and CHART (symbol colour).

In all cases — continuous or discrete — the colour scheme maps to classes in order: first colour → first class, second colour → second class, and so on. With continuous methods the classes are numeric ranges computed automatically; with CATEGORICAL the classes are the entries in the values array. The colour assignment mechanism is the same.

To set class breaks explicitly instead of computing them, use the ranges style property (n+1 values for n classes) — see Style Properties.


Color schemes

The colorscheme property accepts several formats.

Single color

colorscheme: ["#0066cc"]          // single hex color
colorscheme: ["blue"]             // named color

Gradient (two colors, auto-interpolated)

colorscheme: ["5", "#ffffcc", "#253494"]   // 5 classes, yellow → dark blue
colorscheme: ["#fee5d9", "#a50f15"]        // implied 2-class gradient

The first element can be a number (class count) or omitted; iXMaps infers it from the array length.

Multi-stop palette

colorscheme: ["#ffffcc","#c7e9b4","#41b6c4","#2c7fb8","#253494"]  // 5 explicit colors

Named palettes

colorscheme: ["5", "tableau10"]   // 5 classes from Tableau 10
colorscheme: ["7", "viridis"]     // 7 classes viridis
colorscheme: ["7", "plasma"]      // 7 classes plasma
colorscheme: ["100", "tableau"]   // auto-palette, 100 classes

CATEGORICAL color binding

For categorical data, the values array pins specific categories to specific colors (in the same order as colorscheme):

.type("CHART|BUBBLE|CATEGORICAL")
.style({
    colorscheme: ["#4fc3f7", "#ffb300", "#ef5350"],
    values:      ["low",     "medium",  "high"],    // strings — not numbers!
    showdata:    "true"
})
WarningAlways use values with CATEGORICAL

Without values, iXMaps assigns colors by order of first occurrence — unpredictable and data-order dependent.

Always cast values to strings: ["1","2","3"] not [1,2,3].

Diverging scales

Use an even number of colors and set rangecentervalue:

.style({
    colorscheme:      ["#d73027","#f46d43","#fdae61","#abd9e9","#74add1","#4575b4"],
    rangecentervalue: 0,
    showdata:         "true"
})

Flow line gradients

For CHART|VECTOR|BEZIER|GRADIENT, use linecolor as a two-element array — not colorscheme:

.type("CHART|VECTOR|BEZIER|POINTER|GRADIENT")
.style({
    linecolor:  ["#00aabb", "#ff4400"],  // origin color → destination color
    showdata:   "true"
})

Data-driven colorschemes

Every format above is a static palette, fixed when you write .style({...}). For a palette computed from the layer’s own data — e.g. color assigned by matching each class’s label against a pattern, once the classes are actually known — pass a function reference instead of colors:

.style({ colorscheme: "ixmaps.colorScheme_speedmap" })

Define the function on the ixmaps namespace (same convention as userdraw — see Custom Charts). This is a real, shipping example — a CATEGORICAL speed-limit layer where the color depends on the text of each class label, not a fixed palette:

ixmaps.colorScheme_speedmap = function (theme) {
    // theme.szLabelA     — one label per class, in class order
    // theme.colorScheme  — mutate in place: one hex string per class

    for (var i = 0; i < theme.szLabelA.length; i++) {
        if (theme.szLabelA[i].match(/^(45|50|55|60|65|70|75) mph/i)) {
            theme.colorScheme[i] = "#CE517F";
        } else if (theme.szLabelA[i].match(/^40 mph/i)) {
            theme.colorScheme[i] = "#CC6166";
        } else if (theme.szLabelA[i].match(/^30/i)) {
            theme.colorScheme[i] = "#D0A148";
        }
        // ... remaining bands, then a fallback for unmatched labels
    }
};

Load the required helper script and reference the layer as usual:

ixmaps.Map("map_div", { /* ... */ })
    .require("../../ui/js/tools/colorscheme.js")   // ColorScheme.createColorScheme(), used below
    .layer(ixmaps.layer("osm", layer => layer
        .data({ type: "ext", name: "OSM_dataquery_stressmap" })
        .binding({ position: "geometry", id: "id", value: "maxspeed" })
        .type("CHART|FEATURES|CATEGORICAL|LINES")
        .style({ colorscheme: "ixmaps.colorScheme_speedmap", linewidth: "3" })
        .meta({ title: "Speed limits" })
    ));

iXMaps calls the function once the theme’s classes are resolved (theme.szLabelA populated), then reads theme.colorScheme[i] to paint each class — so this is the way to base color on values the layer only knows after loading (its actual set of categories, quantile breaks, etc.). To start from a named palette and only override specific entries, ColorScheme.createColorScheme(...) (from the same colorscheme.js tool) returns a color array you can index into before selectively overwriting entries — see ixmaps.colorScheme_surfacemap alongside colorScheme_speedmap in the source above for that combined pattern.

NoteReference by name, not by array, not inline

colorscheme: "ixmaps.myFunc" works as a bare string (["ixmaps.myFunc"] also works — both are normalized the same way). Never pass an inline function body as the string — the property parser splits on , and |, which corrupts multi-statement source.

A plain JS array built dynamically before the layer is defined works too — colorscheme: buildPalette(n) is just a normal array by the time .style() sees it — but it can’t reflect anything about data this same layer hasn’t loaded yet. Use the function form for anything that depends on the layer’s own resolved classes.


Next steps