Last updated

Annotated Widget

AnnotatedWidget is a simple widget which consists of five special instances ofgeotoolkit/scene/Group. The first one is the model to place shapes to be displayed in the main central part of the AnnotatedWidget. The remaining four groups are north, south, east and west annotations. Each of these is a geotoolkit/scene/Group. The annotations are convenient locations to place axes, titles, legends and such. AnnotatedWidget also has a layout that sizes these components and provides basic tools to work with central part, such as Zoom, Panning and others.
In this tutorial we are going to focus on widget layout and style customization.

# Create Annotated Widget

Create simple annotated widget with deactivated tools.

import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { AnnotatedWidget } from "@int/geotoolkit/widgets/AnnotatedWidget.ts";
import { AxisFactory } from "@int/geotoolkit/axis/AxisFactory.ts";
import { Text } from "@int/geotoolkit/scene/shapes/Text.ts";
import { AnnotationLocation } from "@int/geotoolkit/layout/AnnotationLocation.ts";
import { createModel } from "/src/code/Carnac/Widgets/AnnotatedWidget/createModel.ts";
function createScene(canvas) {
  const axisFactory = AxisFactory.getInstance();
  const annotatedWidget = new AnnotatedWidget({
    "model": createModel(),
    "annotationssizes": {
      "north": 50,
      "south": 40,
      "west": 40,
      "east": 40
    },
    "border": null,
    "tools": {
      "verticalscroll": {
        "visible": false
      },
      "horizontalscroll": {
        "visible": false
      },
      "crosshair": {
        "enabled": false
      }
    },
    "north": [
      new Text({
        "text": "Annotated Widget Title",
        "ax": 0.5,
        "ay": 0.5,
        "textstyle": {
          "color": "#757575",
          "font": "18px Roboto"
        }
      }),
      axisFactory.create({ "location": AnnotationLocation.North })
    ],
    "east": [
      axisFactory.create({ "location": AnnotationLocation.East })
    ],
    "west": [
      axisFactory.create({ "location": AnnotationLocation.West })
    ],
    "south": [
      axisFactory.create({ "location": AnnotationLocation.South })
    ]
  });
  return new Plot({
    "canvaselement": canvas,
    "root": annotatedWidget
  });
}
export { createScene };

createScene(document.querySelector('[ref="plot"]'));

# Customize Annotations

Customize Annotations

import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { AnnotatedWidget } from "@int/geotoolkit/widgets/AnnotatedWidget.ts";
import { AnnotationLocation } from "@int/geotoolkit/layout/AnnotationLocation.ts";
import { AxisFactory } from "@int/geotoolkit/axis/AxisFactory.ts";
import { Group } from "@int/geotoolkit/scene/Group.ts";
import { Border } from "@int/geotoolkit/scene/shapes/Border.ts";
import { Rectangle } from "@int/geotoolkit/scene/shapes/Rectangle.ts";
import { Spline } from "@int/geotoolkit/scene/shapes/Spline.ts";
import { Text } from "@int/geotoolkit/scene/shapes/Text.ts";
import { Rect } from "@int/geotoolkit/util/Rect.ts";
import { KnownColors } from "@int/geotoolkit/util/ColorUtil.ts";
import { KnownScales as ColorProviderKnownScales } from "@int/geotoolkit/util/ColorProvider.ts";
import { DiscreteGradientColorProvider } from "@int/geotoolkit/util/DiscreteGradientColorProvider.ts";
import { ColorBar } from "@int/geotoolkit/controls/shapes/ColorBar.ts";
import { ColorBarLocation } from "@int/geotoolkit/controls/shapes/ColorBarLocation.ts";
import { AxisHelper } from "/src/helpers/axis.ts";
import { createModel } from "/src/code/Carnac/Widgets/AnnotatedWidget/createModel.ts";
function createColorBar(location) {
  const colorProvider = new DiscreteGradientColorProvider({
    "bins": 256
  }).setScale(ColorProviderKnownScales.Rainbow, 0, 1);
  const isVerical = location === ColorBarLocation.West || location === ColorBarLocation.East;
  const COLORBOX_WIDTH = 15;
  const AXIS_WIDTH = 35;
  const TITLE_WIDTH = 25;
  const COLORBAR_SIZE = COLORBOX_WIDTH + AXIS_WIDTH + TITLE_WIDTH;
  return new ColorBar({
    "location": location,
    "componentsizes": {
      "colorbox": 15,
      "axis": 35,
      "title": 25
    },
    "colorprovider": colorProvider,
    "flip": true,
    "title": {
      "text": "elevation",
      "textstyle": "black",
      "sizeisindevicespace": true
    }
  }).setModelLimits(
    isVerical ? new Rect(0, 0, COLORBAR_SIZE, 1) : new Rect(0, 0, 1, COLORBAR_SIZE)
  );
}
function createLegend() {
  return new Group().setBounds(new Rect(0, 0, 1, 1)).setModelLimits(new Rect(0, 0, 1, 1)).addChild(
    [
      new Border({
        "linestyle": {
          color: KnownColors.Gray,
          pixelsnapmode: true
        },
        "fillstyle": KnownColors.LightGray
      }),
      new Rectangle({
        "left": 0.1,
        "top": 0.1,
        "width": 0.1,
        "height": 0.8,
        "fillstyle": KnownColors.Blue
      }),
      new Text({
        "text": "rectangle",
        "ax": 0.35,
        "ay": 0.5
      }),
      new Group().setBounds(new Rect(0.55, 0.1, 0.75, 0.9)).setModelLimits(new Rect(0, 0, 400, 400)).addChild([
        new Spline({
          "x": [100, 50, 150, 200, 300, 350],
          "y": [350, 300, 200, 150, 50, 100],
          "linestyle": KnownColors.Orange
        })
      ]),
      new Text({
        "text": "spline",
        "ax": 0.85,
        "ay": 0.5,
        "sizeisindevicespace": true
      })
    ]
  );
}
function createScene(canvas) {
  const axisFactory = AxisFactory.getInstance();
  const annotatedWidget = new AnnotatedWidget({
    "border": "LightGray",
    "annotationssizes": {
      "north": 30,
      "south": 70,
      "east": 50,
      "west": 75
    },
    "tools": {
      "verticalscroll": {
        "visible": false
      },
      "horizontalscroll": {
        "visible": false
      },
      "crosshair": {
        "enabled": false
      },
      "panning": {
        "enabled": false
      }
    },
    "model": createModel(),
    "north": [
      new Text({
        "text": "Custom Layout",
        "ax": 0.5,
        "ay": 0.5,
        "textstyle": {
          "color": "#757575",
          "font": "18px Roboto"
        }
      })
    ],
    "south": [
      axisFactory.create({ "location": AnnotationLocation.South }).execute(function() {
        this.getTickGenerator().setMinSpan(50);
      }),
      createLegend().setPaddingStyle("5px 1px")
    ],
    "west": [
      createColorBar(ColorBarLocation.West).setPaddingStyle({
        "right": "2px"
      })
    ],
    "east": [
      axisFactory.create({ "location": AnnotationLocation.East }).execute(function() {
        const tgEast = this.getTickGenerator();
        tgEast.setMinSpan(50);
        AxisHelper.initializeAxisTickGeneratorStyle(tgEast);
      }),
      new Text({
        "text": "East Title",
        "ax": 0.5,
        "ay": 0.5,
        "textstyle": {
          "color": KnownColors.Green,
          "font": "20px Arial"
        }
      }).rotate(Math.PI / 2)
    ]
  });
  return new Plot({
    "canvaselement": canvas,
    "root": annotatedWidget
  });
}
export { createScene };

createScene(document.querySelector('[ref="plot"]'));

# Using CSS Style

Сustomize look and fill width using CSS styles.

import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { AnnotatedWidget } from "@int/geotoolkit/widgets/AnnotatedWidget.ts";
import { AxisFactory } from "@int/geotoolkit/axis/AxisFactory.ts";
import { AnnotationLocation } from "@int/geotoolkit/layout/AnnotationLocation.ts";
import { createModel } from "/src/code/Carnac/Widgets/AnnotatedWidget/createModel.ts";
function createScene(canvas) {
  const axisFactory = AxisFactory.getInstance();
  const annotatedWidget = new AnnotatedWidget({
    "model": createModel(),
    "annotationssizes": {
      "north": 40,
      "south": 40,
      "west": 40,
      "east": 40
    },
    "border": null,
    "tools": {
      "verticalscroll": {
        "visible": false
      },
      "horizontalscroll": {
        "visible": false
      },
      "crosshair": {
        "enabled": false
      },
      "panning": {
        "enabled": false
      }
    },
    "north": [
      axisFactory.create({ "location": AnnotationLocation.North })
    ],
    "south": [
      axisFactory.create({ "location": AnnotationLocation.South })
    ],
    "west": [
      axisFactory.create({ "location": AnnotationLocation.West })
    ],
    "east": [
      axisFactory.create({ "location": AnnotationLocation.East })
    ]
  });
  const css = `
        *[cssclass~="vertical-middle"] {
            fillstyle: #303030;
        }

        *[cssclass~="left"] {
            fillstyle: #303030;
        }

        *[cssclass~="right"] {
            fillstyle: #303030;
        }

        .Axis {
           tickgenerator-major-tickstyle-color: #FFD180;
           tickgenerator-major-tickstyle-width: 2;
           tickgenerator-major-labelstyle-color: #EFEFEF;
           tickgenerator-major-labelstyle-font: bold 12px Roboto;

           tickgenerator-minor-tickstyle-color: #EFEFEF;

           tickgenerator-edge-tickstyle-color: #FFD180;
           tickgenerator-edge-labelstyle-color: #EFEFEF;
           tickgenerator-edge-labelstyle-font: bold 12px Roboto;
        }

        .Rectangle{
           fillstyle: #81D4FA;
        }

        .Spline{
           linestyle: 3 solid #FFF176;
        }
    `;
  annotatedWidget.setCss({ "css": css });
  return new Plot({
    "canvaselement": canvas,
    "root": annotatedWidget
  });
}
export { createScene };

createScene(document.querySelector('[ref="plot"]'));

# Change Visible Limits

Customize the layout by modifying visible limits of the model.

The Annotated node uses its own model coordinates system in order to display North, East, West, South annotations. To work directly with model and it limits you can use AnnotatedVidget``::getModel() to get the center model.
With using model you can call CompositeNode``::getModelLimits() to get model limits,
::getVisibleModelLimits() returns the visible model limits in Model space
and the function ::getVisibleDeviceLimits() returns the visible model limits in the plot coordinates;
You can also change model orientation, for example with using ::setVerticalFlip(true)

You can alse use methods on AnnotatedWidget``::getVisibleModelLimits() to get visible model limits from center model or ::setVisibleModelLimits() to set it.

import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { AnnotatedWidget } from "@int/geotoolkit/widgets/AnnotatedWidget.ts";
import { AxisFactory } from "@int/geotoolkit/axis/AxisFactory.ts";
import { AnnotationLocation } from "@int/geotoolkit/layout/AnnotationLocation.ts";
import { Rect } from "@int/geotoolkit/util/Rect.ts";
import { Text } from "@int/geotoolkit/scene/shapes/Text.ts";
import { Group } from "@int/geotoolkit/scene/Group.ts";
import { Spline } from "@int/geotoolkit/scene/shapes/Spline.ts";
import { KnownColors } from "@int/geotoolkit/util/ColorUtil.ts";
import { AnchorType } from "@int/geotoolkit/util/AnchorType.ts";
import { Line } from "@int/geotoolkit/scene/shapes/Line.ts";
import { Grid } from "@int/geotoolkit/axis/Grid.ts";
import { AdaptiveTickGenerator } from "@int/geotoolkit/axis/AdaptiveTickGenerator.ts";
function createModel() {
  const splineX = [];
  const splineY = [];
  const offsetX = 100;
  const offsetY = 50;
  for (let x = 0; x < 400; x++) {
    splineX.push(x);
    splineY.push((x - offsetX) * (x - offsetX) / 100 + offsetY);
  }
  const hTickGenerator = new AdaptiveTickGenerator().setVisibleTickGrade("minor", true);
  hTickGenerator.getTickStyle("major").setColor("rgba(100, 100, 100, 0.2)");
  hTickGenerator.getTickStyle("edge").setColor("rgba(100, 100, 100, 0.5)");
  hTickGenerator.getTickStyle("minor").setColor("rgba(100, 100, 100, 0.1)").setPattern([4, 4]);
  const vTickGenerator = new AdaptiveTickGenerator().setVisibleTickGrade("minor", true);
  vTickGenerator.getTickStyle("major").setColor("rgba(100, 100, 100, 0.2)");
  vTickGenerator.getTickStyle("edge").setColor("rgba(100, 100, 100, 0.5)");
  vTickGenerator.getTickStyle("minor").setColor("rgba(100, 100, 100, 0.1)").setPattern([4, 4]);
  return new Group().setModelLimits(new Rect(0, 0, 400, 400)).addChild([
    new Grid(hTickGenerator, vTickGenerator),
    new Spline({
      "x": splineX,
      "y": splineY,
      "linestyle": {
        "color": KnownColors.Orange,
        "width": 2
      }
    }),
    new Line({
      "from": { x: offsetX, y: 0 },
      "to": { x: offsetX, y: 400 },
      "linestyle": {
        "color": KnownColors.Orange,
        "pixelsnapmode": true
      }
    }),
    new Line({
      "from": { x: 0, y: offsetY },
      "to": { x: 400, y: offsetY },
      "linestyle": {
        "color": KnownColors.Orange,
        "pixelsnapmode": true
      }
    })
  ]);
}
function createScene(canvas) {
  const axisFactory = AxisFactory.getInstance();
  const annotatedWidget = new AnnotatedWidget({
    "model": createModel().setVerticalFlip(true),
    "annotationssizes": {
      "north": 30,
      "south": 40,
      "west": 50,
      "east": 20
    },
    "border": "rgba(100, 100, 100, 0.2)",
    "tools": {
      "verticalscroll": {
        "floating": true
      },
      "horizontalscroll": {
        "floating": true
      },
      "crosshair": {
        "enabled": false
      },
      "panning": {
        "enabled": true
      }
    },
    "north": [
      new Text({
        "text": "Simple graph",
        "ax": 0.5,
        "ay": 0.5,
        "textstyle": {
          "color": "#757575",
          "font": "18px Roboto"
        }
      })
    ],
    "south": [
      axisFactory.create({ "location": AnnotationLocation.South }),
      new Text({
        "text": "x-axis",
        "ax": 0,
        "ay": 1,
        "alignment": AnchorType.LeftTop,
        "textstyle": {
          "color": "#757575"
        }
      })
    ],
    "west": [
      new Text({
        "text": "y-axis",
        "ax": 1,
        "ay": 1,
        "alignment": AnchorType.LeftBottom,
        "textstyle": {
          "color": "#757575"
        }
      }).rotate(-Math.PI / 2),
      axisFactory.create({ "location": AnnotationLocation.West })
    ]
  });
  annotatedWidget.setVisibleModelLimits(new Rect(0, 0, 350, 350));
  return new Plot({
    "canvaselement": canvas,
    "root": annotatedWidget
  });
}
export { createScene };

createScene(document.querySelector('[ref="plot"]'));

# Internal Locked Axes

Illusion of a locked axes can be created by putting a group in the same place as the model and then connect the axis to the model.

import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { AnnotatedWidget } from "@int/geotoolkit/widgets/AnnotatedWidget.ts";
import { AnnotationLocation } from "@int/geotoolkit/layout/AnnotationLocation.ts";
import { AxisFactory } from "@int/geotoolkit/axis/AxisFactory.ts";
import { Axis } from "@int/geotoolkit/axis/Axis.ts";
import { AdaptiveLogTickGenerator } from "@int/geotoolkit/axis/AdaptiveLogTickGenerator.ts";
import { Events as NodeEvents } from "@int/geotoolkit/scene/Node.ts";
import { Polyline } from "@int/geotoolkit/scene/shapes/Polyline.ts";
import { Group } from "@int/geotoolkit/scene/Group.ts";
import { Orientation } from "@int/geotoolkit/util/Orientation.ts";
import { LabelPosition, TickPosition } from "@int/geotoolkit/axis/TickInfo.ts";
import { KnownColors } from "@int/geotoolkit/util/ColorUtil.ts";
import { AxisHelper } from "/src/helpers/axis.ts";
import { CssLayout } from "@int/geotoolkit/layout/CssLayout.ts";
function createModel() {
  const x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30], y = [
    0,
    0.2942140125669539,
    0.03376712603494525,
    0.8403971537481993,
    0.511907295556739,
    0.21692502405494452,
    0.9026303198188543,
    0.6066542339976877,
    0.4515766170807183,
    0.6822457714006305,
    0.6191746226977557,
    0.304504499072209,
    0.3814454732928425,
    0.12356831086799502,
    0.021915478399023414,
    0.42551541421562433,
    0.288056978257373,
    0.7553384704515338,
    0.13983638840727508,
    0.352386727463454,
    0.19704886013641953,
    0.5506594027392566,
    0.2501351598184556,
    0.6341288497205824,
    0.43139789439737797,
    0.08476797654293478,
    0.9867885017301887,
    0.562166113872081,
    0.7035165049601346,
    0.3304662255104631,
    1
  ];
  const polyLine = new Polyline({
    "x": x,
    "y": y,
    "linestyle": KnownColors.Orange
  });
  return new Group().setModelLimits(polyLine.getBounds()).addChild(polyLine);
}
function createScene(canvas) {
  const axisFactory = AxisFactory.getInstance();
  const annotatedWidget = new AnnotatedWidget({
    "annotationssizes": {
      "north": 0,
      "south": 0,
      "west": 0,
      "east": 0
    },
    "model": createModel()
  });
  const axisContainer = new Group().setModelLimits(
    annotatedWidget.getModel().getModelLimits()
  ).enableClipping(true);
  axisContainer.addChild([
    axisFactory.create({ "location": AnnotationLocation.North }),
    axisFactory.create({ "location": AnnotationLocation.South }),
    new Axis(AxisHelper.initializeAxisTickGeneratorStyle(new AdaptiveLogTickGenerator()).setLabelStyle("minor", AxisHelper.axisColor()["style"]).setVisibleLabelGrade("minor", true).setIntermediateTicks(false)).setOrientation(Orientation.Vertical).setTickPosition(TickPosition.Left).setLabelPosition(LabelPosition.Left),
    new Axis(AxisHelper.initializeAxisTickGeneratorStyle(new AdaptiveLogTickGenerator()).setLabelStyle("minor", AxisHelper.axisColor()["style"]).setVisibleLabelGrade("minor", true).setIntermediateTicks(false)).setOrientation(Orientation.Vertical).setTickPosition(TickPosition.Right).setLabelPosition(LabelPosition.Right)
  ]);
  annotatedWidget.getModel().on(NodeEvents.SceneTransformationChanged, () => {
    const modelLimits = annotatedWidget.getVisibleCenterModelLimits();
    axisContainer.setModelLimits(modelLimits).getChildren().forEach((axis) => axis.setBounds(modelLimits));
  });
  return new Plot({
    "canvaselement": canvas,
    "root": new Group().setLayout(new CssLayout()).addChild([
      annotatedWidget.setLayoutStyle({
        left: "20px",
        top: "20px",
        right: "20px",
        bottom: "20px"
      }),
      axisContainer.setLayoutStyle({
        left: "20px",
        top: "20px",
        right: "20px",
        bottom: "20px"
      })
    ])
  });
}
export { createScene };

createScene(document.querySelector('[ref="plot"]'));