This tutorial demonstrates how to create an axis and grid and set their attributes. To make an axis, first a tickGenerator must be constructed and passed into an axis. This axis must be added to a group which is then added to the root group.
# Axis Basics
The example below shows the default axis. The left axis uses default model origin (0) and the right one starts generating ticks from the specified model origin (-25) with the same data range [0, 200] and hidden edge labels.
import { AdaptiveTickGenerator } from "@int/geotoolkit/axis/AdaptiveTickGenerator.ts";
import { Rect } from "@int/geotoolkit/util/Rect.ts";
import { Axis } from "@int/geotoolkit/axis/Axis.ts";
import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { Group } from "@int/geotoolkit/scene/Group.ts";
function createAxis(x1, y1, x2, y2, modelOrigin) {
const minValue = 0;
const maxValue = 200;
const bounds = new Rect(x1, y1, x2, y2);
const tg = new AdaptiveTickGenerator();
if (modelOrigin) {
tg.setModelOrigin(modelOrigin);
tg.setVisibleTickGrade("edge", false);
tg.setVisibleLabelGrade("edge", false);
}
return new Axis(tg).setBounds(bounds).setModelLimits(new Rect(minValue, minValue, maxValue, maxValue));
}
function createScene(canvas) {
return new Plot({
"canvaselement": canvas,
"root": new Group().addChild(createAxis(10, 10, 110, 340)).addChild(createAxis(150, 10, 250, 340, -25))
});
}
export { createScene };
createScene(document.querySelector('[ref="plot"]'));
# Auto Label Rotation
The axis label will be rotated when the axis is too small.
import { AdaptiveTickGenerator } from "@int/geotoolkit/axis/AdaptiveTickGenerator.ts";
import { Rect } from "@int/geotoolkit/util/Rect.ts";
import { Axis } from "@int/geotoolkit/axis/Axis.ts";
import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { Group } from "@int/geotoolkit/scene/Group.ts";
function createAxis(bounds) {
const minValue = 4e4;
const maxValue = 15e6;
return new Axis(new AdaptiveTickGenerator()).setBounds(bounds).setModelLimits(new Rect(minValue, minValue, maxValue, maxValue));
}
function createScene(canvas) {
return new Plot({
"canvaselement": canvas,
"root": new Group().addChild([
createAxis(new Rect(10, 10, 100, 340)),
createAxis(new Rect(120, 10, 150, 340)).setAutoLabelRotation(true)
])
});
}
export { createScene };
createScene(document.querySelector('[ref="plot"]'));
# Tick Generator Types
GeoToolkit provides several tick generators to be used to generate numeric labels; a few are explained below:
1. AdaptiveTickGenerator: Generates ticks and labels considering the minimum distance between ticks in the pixels and it automatically configures itself to create ticks at a reasonable interval.
2. AdaptiveLogTickGenerator: Logarithmic tick generator with automatic spacing of ticks and labels.
3. AdaptiveTangentialTickGenerator: Tangential tick generator with automatic spacing of ticks and labels based on the tangent function.
4. NumericTickGenerator: Generates ticks and labels with fixed major and minor steps from start model position. The amount of labels are fixed and doesn't depend on the resolution.
The example below shows these ticks generators side by side.
import { AdaptiveTickGenerator } from "@int/geotoolkit/axis/AdaptiveTickGenerator.ts";
import { AdaptiveLogTickGenerator } from "@int/geotoolkit/axis/AdaptiveLogTickGenerator.ts";
import { NumericLinearTickGenerator } from "@int/geotoolkit/axis/NumericLinearTickGenerator.ts";
import { AdaptiveTangentialTickGenerator } from "@int/geotoolkit/axis/AdaptiveTangentialTickGenerator.ts";
import { Rect } from "@int/geotoolkit/util/Rect.ts";
import { Axis } from "@int/geotoolkit/axis/Axis.ts";
import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { Group } from "@int/geotoolkit/scene/Group.ts";
function createAxis(tickGenerator, bounds) {
const minValue = 0;
const maxValue = 3;
return new Axis(tickGenerator).setBounds(bounds).setModelLimits(new Rect(minValue, minValue, maxValue, maxValue));
}
function createScene(canvas) {
return new Plot({
"canvaselement": canvas,
"root": new Group().addChild([
createAxis(
new AdaptiveTickGenerator(),
new Rect(10, 10, 100, 340)
),
createAxis(
new AdaptiveLogTickGenerator().setVisibleTickGrade("minor", true),
new Rect(105, 10, 195, 340)
),
createAxis(
new AdaptiveTangentialTickGenerator({ "minorticksamount": 3, "majorticksamount": 2 }),
new Rect(200, 10, 290, 340)
),
createAxis(
new NumericLinearTickGenerator().setTickStep("major", 0.5),
new Rect(295, 10, 385, 340)
)
])
});
}
export { createScene };
createScene(document.querySelector('[ref="plot"]'));
# Modifying Axes
Axes tick positions and orientations can also be changed.
import { AdaptiveTickGenerator } from "@int/geotoolkit/axis/AdaptiveTickGenerator.ts";
import { Rect } from "@int/geotoolkit/util/Rect.ts";
import { Axis } from "@int/geotoolkit/axis/Axis.ts";
import { TickPosition } from "@int/geotoolkit/axis/TickInfo.ts";
import { Orientation } from "@int/geotoolkit/util/Orientation.ts";
import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { Group } from "@int/geotoolkit/scene/Group.ts";
function createAxis(bounds, tickPosition) {
const minValue = 0;
const maxValue = 500;
return new Axis(new AdaptiveTickGenerator()).setBounds(bounds).setModelLimits(new Rect(minValue, minValue, maxValue, maxValue)).setTickPosition(tickPosition).setOrientation(Orientation.Horizontal);
}
function createScene(canvas) {
return new Plot({
"canvaselement": canvas,
"root": new Group().addChild([
createAxis(new Rect(10, 0, 290, 40), TickPosition.Bottom),
createAxis(new Rect(10, 60, 290, 100), TickPosition.Top),
createAxis(new Rect(10, 120, 290, 160), TickPosition.TopAndBottom)
])
});
}
export { createScene };
createScene(document.querySelector('[ref="plot"]'));
# Modifying Labels
Furthermore, label position can be changed. By default, all labels will appear in the center of the axis. They can also be snapped to the right or left (or top or bottom) tick marks, and can be offset towards the center from any direction.
import { AdaptiveTickGenerator } from "@int/geotoolkit/axis/AdaptiveTickGenerator.ts";
import { Rect } from "@int/geotoolkit/util/Rect.ts";
import { Axis } from "@int/geotoolkit/axis/Axis.ts";
import { LabelPosition, TickPosition } from "@int/geotoolkit/axis/TickInfo.ts";
import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { Group } from "@int/geotoolkit/scene/Group.ts";
function createAxis(bounds, labelPosition, tickPosition) {
const minValue = 0;
const maxValue = 400;
return new Axis(new AdaptiveTickGenerator()).setBounds(bounds).setModelLimits(new Rect(minValue, minValue, maxValue, maxValue)).setLabelPosition(labelPosition).setTickPosition(tickPosition);
}
function createScene(canvas) {
return new Plot({
"canvaselement": canvas,
"root": new Group().addChild([
createAxis(new Rect(5, 10, 55, 220), LabelPosition.Right, TickPosition.Right),
createAxis(new Rect(70, 10, 120, 220), LabelPosition.Left, TickPosition.Left),
createAxis(new Rect(140, 10, 190, 220), LabelPosition.Left, TickPosition.Left).setLabelOffset(10)
])
});
}
export { createScene };
createScene(document.querySelector('[ref="plot"]'));
# Tick Generator Options
Styles of ticks and the axis itself can be changed.
.cg-tooltip-holder {
position: relative;
}
.cg-tooltip {
position: absolute;
display: block;
padding: 2px 12px 3px 7px;
overflow: visible !important;
font-family: Roboto, Helvetica, Arial, sans-serif;
font-size: 13px;
background: white !important;
opacity: 0.9;
color: #333333;
border: solid 1px gray;
border-radius: 5px;
text-align: left;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
border-radius: 5px;
margin: 0 !important;
z-index: 10000;
max-width: 400px;
text-wrap: normal !important;
white-space: normal !important;
}
/* Default setting for tooltip */
.cg-tooltip-container {
position: absolute;
display: block;
overflow: visible !important;
font-family: Roboto, Helvetica, Arial, sans-serif;
font-size: 12px;
padding: 3px 7px;
background: #f7f7f7;
color: #333333;
border: 1px solid #938e8e;
opacity: 0.8;
text-align: left;
box-shadow: 3px 3px 10px #888;
margin: 0 !important;
z-index: 10000;
max-width: 400px;
text-wrap: normal !important;
white-space: normal !important;
user-select: none;
}
@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
.cg-tooltip-container {
border-radius: 0;
}
}
/* Default left arrow for tooltip */
.cg-tooltip-arrow-left::before {
content: '';
position: absolute;
display: block;
width: 0px;
left: 0;
top: 50%;
border: 5px solid transparent;
border-left: 0;
border-right: 5px solid #938e8e;
transform: translate(calc(-100%), -50%);
}
.cg-tooltip-arrow-left::after {
content: '';
position: absolute;
display: block;
width: 0px;
left: 0;
top: 50%;
border: 4px solid transparent;
border-left: 0;
border-right: 4px solid #f7f7f7;
transform: translate(calc(-100%), -50%);
}
/* Default top arrow for tooltip */
.cg-tooltip-arrow-top::before {
content: '';
position: absolute;
display: block;
width: 0px;
left: 50%;
top: 0;
border: 5px solid transparent;
border-top: 0;
border-bottom: 5px solid #938e8e;
transform: translate(-50%, -100%);
}
.cg-tooltip-arrow-top::after {
content: '';
position: absolute;
display: block;
width: 0px;
left: 50%;
top: 0;
border: 4px solid transparent;
border-top: 0;
border-bottom: 4px solid #f7f7f7;
transform: translate(-50%, -100%);
}
/* Default right arrow for tooltip */
.cg-tooltip-arrow-right::before {
content: '';
position: absolute;
display: block;
width: 0px;
right: 0;
top: 50%;
border: 5px solid transparent;
border-right: 0;
border-left: 5px solid #938e8e;
transform: translate(100%, -50%);
}
.cg-tooltip-arrow-right::after {
content: '';
position: absolute;
display: block;
width: 0px;
right: 0;
top: 50%;
border: 4px solid transparent;
border-right: 0;
border-left: 4px solid #f7f7f7;
transform: translate(100%, -50%);
}
/* Default bottom arrow for tooltip */
.cg-tooltip-arrow-bottom::before {
content: '';
position: absolute;
display: block;
width: 0px;
left: 50%;
bottom: 0px;
border: 5px solid transparent;
border-bottom: 0;
border-top: 5px solid #938e8e;
transform: translate(-50%, 100%);
z-index: 10000;
}
.cg-tooltip-arrow-bottom::after {
content: '';
position: absolute;
display: block;
width: 0px;
left: 50%;
bottom: 0;
border: 4px solid transparent;
border-bottom: 0;
border-top: 4px solid #f7f7f7;
transform: translate(-50%, 100%);
z-index: 10000;
}
/* Tooltip item name */
/* Tooltip item value */
/* .cg-tooltip-item-value */
/* Tooltip item value */
.cg-tooltip-item-unit {
text-transform: none;
}
.cg-tooltip-item-name {
text-transform: capitalize;
white-space: nowrap;
vertical-align: middle;
font-size: 13px;
}
.cg-tooltip-row {
display: flex;
flex-direction: row;
align-items: center;
white-space: pre-wrap;
font-size: 12px;
line-height: 100%;
margin: 1px 0;
}
.cg-tooltip-title {
font-size: 13px;
height: 14px;
text-transform: capitalize;
}
.cg-tooltip-title .cg-tooltip-symbol {
margin-right: 0 !important;
}
.cg-tooltip-title-name {
vertical-align: middle;
}
.cg-tooltip-row + .cg-tooltip-row {
margin-top: 4px;
}
.cg-tooltip-row.cg-tooltip-title + .cg-tooltip-row {
margin-top: 5px;
}
.cg-tooltip-item-value + .cg-tooltip-item-unit {
margin-left: 1px;
}
/* Tooltip symbol */
.cg-tooltip-symbol-cell {
display: inline-flex;
min-width: 13px; /* 10px size + 3px margin */
}
.cg-tooltip-symbol {
margin-right: 3px;
background-color: transparent;
display: block;
}
.cg-tooltip-symbol > img {
display: block;
}
.cg-tooltip-list-cell {
display: inline-flex;
}
.cg-tooltip-list-symbol {
display: block;
margin-right: 3px;
width: 6px;
height: 6px;
vertical-align: middle;
border-radius: 50%;
border: 1px solid rgba(0,0,0,.4);
}
.cg-tooltip-symbol-legacy {
border-radius: 4px;
margin-right: 5px;
height: 8px;
width: 8px;
display: inline-block;
}
.cg-tooltip-title-legacy {
font-weight: 900;
}
/* Tooltip symbol circle */
.cg-tooltip-symbol.circle {
height: 10px;
width: 10px;
display: inline-block;
border-radius: 50%;
border: 1px solid rgba(0,0,0,.4);
}
/* Tooltip symbol line */
.cg-tooltip-symbol.line {
height: 10px;
width: 10px;
display: inline-block;
transform: scale(1.2, 0.2);
}
/* Tooltip symbol diamond */
.cg-tooltip-symbol.diamond {
height: 10px;
width: 10px;
display: inline-block;
transform: rotate(45deg);
border-radius: 0px;
}
/* Tooltip symbol square */
.cg-tooltip-symbol.square {
height: 10px;
width: 10px;
display: inline-block;
border-radius: 0px;
border: 1px solid rgba(0,0,0,.4);
}
# Tick Customization
Using a TickGenerators setFormatLabelHandler allows you full control of what is displayed by a label.
import { NumericLinearTickGenerator } from "@int/geotoolkit/axis/NumericLinearTickGenerator.ts";
import { Rect } from "@int/geotoolkit/util/Rect.ts";
import { Axis } from "@int/geotoolkit/axis/Axis.ts";
import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { Group } from "@int/geotoolkit/scene/Group.ts";
import { KnownColors } from "@int/geotoolkit/util/ColorUtil.ts";
function createAxis() {
const minValue = 0;
const maxValue = 3;
const bounds = new Rect(20, 10, 250, 340);
const colors = [
KnownColors.Blue,
KnownColors.Green,
KnownColors.Red
];
const tickGenerator = new NumericLinearTickGenerator().setTickStep("major", 1).setTickStep("minor", 0.4).setFormatLabelHandler((ref, parent, orient, tickInfo, tickIndex, value) => {
if (tickInfo.getGrade() === "edge") {
tickInfo.getTextStyle().setColor(colors[2]);
} else {
tickInfo.getTextStyle().setColor(colors[tickIndex]);
}
return tickInfo.getGrade() + " tick at " + tickIndex + " Value = " + value;
});
return new Axis(tickGenerator).setBounds(bounds).setModelLimits(new Rect(minValue, minValue, maxValue, maxValue));
}
function createScene(canvas) {
return new Plot({
"canvaselement": canvas,
"root": new Group().addChild(createAxis())
});
}
export { createScene };
createScene(document.querySelector('[ref="plot"]'));
# CSS Customization
It is easy to modify all axis settings using CSS.
import { AdaptiveTickGenerator } from "@int/geotoolkit/axis/AdaptiveTickGenerator.ts";
import { Rect } from "@int/geotoolkit/util/Rect.ts";
import { Axis } from "@int/geotoolkit/axis/Axis.ts";
import { AnchorType } from "@int/geotoolkit/util/AnchorType.ts";
import { KnownColors } from "@int/geotoolkit/util/ColorUtil.ts";
import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { Group } from "@int/geotoolkit/scene/Group.ts";
function createAxis(bounds) {
const minValue = 0;
const maxValue = 200;
return new Axis(new AdaptiveTickGenerator()).setBounds(bounds).setModelLimits(new Rect(minValue, minValue, maxValue, maxValue));
}
function setCss(axis) {
const orange = KnownColors.Orange, blue = KnownColors.Blue, gray = KnownColors.Gray;
const css = [
"",
"geotoolkit.axis.Axis {",
" tickgenerator-edge-labelstyle-color: " + orange + ";",
" tickgenerator-edge-labelstyle-font: 12px Roboto;",
" tickgenerator-edge-tickstyle-color:" + orange + ";",
" tickgenerator-edge-tickstyle-width: 2;",
" tickgenerator-major-tickvisible: true;",
" tickgenerator-major-labelstyle-color:" + blue + ";",
" tickgenerator-major-labelstyle-font: 12px Roboto;",
" tickgenerator-major-tickstyle-color:" + blue + ";",
" tickgenerator-major-tickstyle-width: 1;",
" tickgenerator-major-ticksize: 10;",
" tickgenerator-intermediate-tickvisible: true;",
" tickgenerator-minor-labelvisible: false;",
" tickgenerator-intermediate-tickstyle-color:" + gray + ";",
" tickgenerator-intermediate-ticksize: 7;",
" tickgenerator-minor-tickvisible: true;",
" tickgenerator-minor-labelvisible: false;",
" tickgenerator-minor-ticksize: 5;",
" title-text: Css Customization ;",
" title-visible: true;",
" title-alignment: " + AnchorType.RightCenter + ";",
" title-textstyle-color: " + orange + ";",
" title-textstyle-font: 12px Roboto;",
" tickgenerator-minorticksamount: 10;",
" tickgenerator-minspan: 50;",
" tickgenerator-minspangrade: major;",
" tickposition: left;",
" labelposition: left;",
" labeloffset: 2;",
" linestyle-color: red;",
"}"
].join("");
axis.setCss(css);
return axis;
}
function createScene(canvas) {
return new Plot({
"canvaselement": canvas,
"root": new Group().addChild([
createAxis(new Rect(10, 10, 110, 340)),
setCss(createAxis(new Rect(120, 10, 180, 340)))
])
});
}
export { createScene };
createScene(document.querySelector('[ref="plot"]'));
# Smart Limits
A function that can calculate "smart" limits (axis on the right).
import { AdaptiveTickGenerator } from "@int/geotoolkit/axis/AdaptiveTickGenerator.ts";
import { Rect } from "@int/geotoolkit/util/Rect.ts";
import { Axis } from "@int/geotoolkit/axis/Axis.ts";
import { MathUtil } from "@int/geotoolkit/util/MathUtil.ts";
import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { Group } from "@int/geotoolkit/scene/Group.ts";
function createAxisNoNeatLimits() {
const minValue = 5.25;
const maxValue = 1487;
const bounds = new Rect(10, 0, 90, 290);
return new Axis(new AdaptiveTickGenerator()).setBounds(bounds).setModelLimits(new Rect(minValue, minValue, maxValue, maxValue));
}
function createAxisNeatLimits() {
const range = MathUtil.calculateNeatLimits(5.25, 1487, false, false);
const minValue = range.getLow();
const maxValue = range.getHigh();
const bounds = new Rect(100, 0, 190, 290);
return new Axis(new AdaptiveTickGenerator()).setBounds(bounds).setModelLimits(new Rect(minValue, minValue, maxValue, maxValue));
}
function createScene(canvas) {
return new Plot({
"canvaselement": canvas,
"root": new Group().addChild([
createAxisNoNeatLimits(),
createAxisNeatLimits()
])
});
}
export { createScene };
createScene(document.querySelector('[ref="plot"]'));
# Axis Dimension
AxisMappingDimension can be used for setting neat limits of axis.
In this example from left to right:
1. AdaptiveTickGenerator without AxisMappingDimension.
2. AdaptiveTickGenerator with AxisMappingDimension.
3. AdaptiveLogTickGenerator without AxisMappingDimension.
4. AdaptiveLogTickGenerator without AxisMappingDimension.
import { Axis } from "@int/geotoolkit/axis/Axis.ts";
import { AdaptiveLogTickGenerator } from "@int/geotoolkit/axis/AdaptiveLogTickGenerator.ts";
import { AdaptiveTickGenerator } from "@int/geotoolkit/axis/AdaptiveTickGenerator.ts";
import { AxisMappingDimension } from "@int/geotoolkit/axis/AxisMappingDimension.ts";
import { Rect } from "@int/geotoolkit/util/Rect.ts";
import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { Group } from "@int/geotoolkit/scene/Group.ts";
import { HorizontalBoxLayout } from "@int/geotoolkit/layout/HorizontalBoxLayout.ts";
function createAxis(minValue, maxValue, logarithmic, neatLimits) {
const axis = new Axis(logarithmic ? new AdaptiveLogTickGenerator({ intermediateticks: true }) : new AdaptiveTickGenerator()).setLayoutStyle({
"minheight": 300,
"minwidth": 75
});
if (neatLimits) {
const dimension = new AxisMappingDimension();
dimension.setOptions({
"min": minValue,
"max": maxValue,
"neatlimits": true
});
axis.setAxisDimension(dimension);
dimension.updateLimits(minValue, maxValue, {
"span": logarithmic ? 1 : 5
});
axis.setModelLimits(new Rect(dimension.getMin(), dimension.getMin(), dimension.getMax(), dimension.getMax()));
} else {
axis.setModelLimits(new Rect(minValue, minValue, maxValue, maxValue));
}
return axis;
}
function createScene(canvas) {
return new Plot({
"canvaselement": canvas,
"root": new Group().setLayout(new HorizontalBoxLayout()).addChild([
createAxis(1.7, 13.84, false, false),
createAxis(1.7, 13.84, false, true),
createAxis(0.23, 1.14, true, false),
createAxis(0.23, 1.14, true, true)
])
});
}
export { createScene };
createScene(document.querySelector('[ref="plot"]'));
# Grids
Grids can use any of the previously outlined tick generators. Color and tick settings are adjusted on the tick generator object. Grids should be in their own group, just like axes.
import { AdaptiveTickGenerator } from "@int/geotoolkit/axis/AdaptiveTickGenerator.ts";
import { CssLayout } from "@int/geotoolkit/layout/CssLayout.ts";
import { Rect } from "@int/geotoolkit/util/Rect.ts";
import { Grid } from "@int/geotoolkit/axis/Grid.ts";
import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { Group } from "@int/geotoolkit/scene/Group.ts";
import { KnownColors } from "@int/geotoolkit/util/ColorUtil.ts";
import { AdaptiveLogTickGenerator } from "@int/geotoolkit/axis/AdaptiveLogTickGenerator.ts";
import { ViewCache } from "@int/geotoolkit/scene/ViewCache.ts";
function createScene(canvas) {
const hTickGenerator = new AdaptiveTickGenerator().setVisibleTickGrade("minor", true);
hTickGenerator.getTickStyle("major").setColor(KnownColors.Blue);
hTickGenerator.getTickStyle("edge").setColor(KnownColors.DarkGray);
hTickGenerator.getTickStyle("minor").setColor(KnownColors.LightOrange);
const vTickGenerator = new AdaptiveLogTickGenerator().setVisibleTickGrade("minor", true);
vTickGenerator.getTickStyle("major").setColor(KnownColors.Blue);
vTickGenerator.getTickStyle("edge").setColor(KnownColors.DarkGray);
vTickGenerator.getTickStyle("minor").setColor(KnownColors.Gray);
return new Plot({
"canvaselement": canvas,
"root": new Group().setLayout(new CssLayout()).addChild([
new Group().setCache(new ViewCache()).setLayoutStyle({ left: 10, top: 10, right: 10, bottom: 10 }).setModelLimits(new Rect(Math.log10(1), 0, Math.log10(100), 200)).addChild([
new Grid(hTickGenerator, vTickGenerator)
])
])
});
}
export { createScene };
createScene(document.querySelector('[ref="plot"]'));