GeometryPattern is another way to specify a Fill Attribute. Normally, fills are specified with an image. However, with GeometryPattern, specify the fill by drawing CarnacJS shapes. This allows more flexibility and control than an image fill. A GeometryPattern can be scalable or non-scalable with respect to the shape being filled.
# Creating
The following example illustrates how to create geometry pattern
import { Rect } from "@int/geotoolkit/util/Rect.ts";
import { SymbolShape } from "@int/geotoolkit/scene/shapes/SymbolShape.ts";
import { AnchorType } from "@int/geotoolkit/util/AnchorType.ts";
import { FontPainter } from "@int/geotoolkit/scene/shapes/painters/FontPainter.ts";
import { PatternFactory } from "@int/geotoolkit/attributes/PatternFactory.ts";
import { Rectangle } from "@int/geotoolkit/scene/shapes/Rectangle.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 font from "/src/assets/fonts/Noto Sans Bold.ttf?import";
function createScene(canvas) {
const createGeometry = function() {
const root = new Group().setBounds(new Rect(0, 0, 20, 20));
const shape2 = new SymbolShape({
ax: 10,
ay: 10,
width: 20,
height: 20,
alignment: AnchorType.Center,
painter: new FontPainter(font, '"Noto Sans Bold"', "\u2605"),
fillstyle: "orange",
linestyle: "blue"
});
root.addChild(shape2);
const uSpaceRect = new Rect(0, 0, 20, 20);
return new Group().setBounds(uSpaceRect).setModelLimits(uSpaceRect).addChild(root);
};
const genPattern = PatternFactory.getInstance().createPattern(createGeometry(), {
"scalable": false
});
const shape = new Rectangle(25, 25, 300, 200).setFillStyle({
"pattern": genPattern,
"color": "white"
}).setLineStyle({
"color": KnownColors.Black,
"width": 2
});
const group = new Group().setFillStyle(KnownColors.LightGray).addChild(shape).setModelLimits(shape.getBounds());
return new Plot({
"canvaselement": canvas,
"root": group
});
}
export { createScene };
createScene(document.querySelector('[ref="plot"]'));
# Scalable
Pattern limits are calculated by the position of the filled shape in absolute coordinates. The pattern shapes are drawn in a coordinate system that is relative to the filled shape's bounding rectangle. So for the pattern shapes, the point (0,0) is the upper left corner of the bounding rectangle. If the GeometryPattern scalable parameter is true, the pattern shapes scale when the filled shape scales. If false, the pattern shapes do not scale. The pattern is repeated enough to fill the shape.
import { Rect } from "@int/geotoolkit/util/Rect.ts";
import { SymbolShape } from "@int/geotoolkit/scene/shapes/SymbolShape.ts";
import { AnchorType } from "@int/geotoolkit/util/AnchorType.ts";
import { FontPainter } from "@int/geotoolkit/scene/shapes/painters/FontPainter.ts";
import { PatternFactory } from "@int/geotoolkit/attributes/PatternFactory.ts";
import { Rectangle } from "@int/geotoolkit/scene/shapes/Rectangle.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 { CompositeNode } from "@int/geotoolkit/scene/CompositeNode.ts";
import font from "/src/assets/fonts/Noto Sans Bold.ttf?import";
function hasBounds(source) {
return source && typeof source.getBounds === "function";
}
function centerShape(shape) {
const tr = shape.getLocalTransform();
if (hasBounds(shape)) {
const transformed = tr != null ? tr.transformRect(shape.getBounds()) : shape.getBounds();
const parent = shape.getParent();
const parentCenter = parent.getModelLimits().getCenter();
const shapeCenter = transformed.getCenter();
shape.translate(parentCenter.getX() - shapeCenter.getX(), parentCenter.getY() - shapeCenter.getY());
}
}
const SCALE_FACTOR = 1.1;
let scale = 1;
function zoomIn(arr) {
for (let i = 0; i < arr.length; i++) {
const root = arr[i].getRoot() instanceof CompositeNode ? arr[i].getRoot() : null;
const shape = root?.getChild(0);
shape.scale(SCALE_FACTOR, SCALE_FACTOR);
centerShape(shape);
}
scale /= SCALE_FACTOR;
}
function zoomOut(arr) {
for (let i = 0; i < arr.length; i++) {
const root = arr[i].getRoot() instanceof CompositeNode ? arr[i].getRoot() : null;
const shape = root?.getChild(0);
shape.scale(1 / SCALE_FACTOR, 1 / SCALE_FACTOR);
centerShape(shape);
}
scale /= 1 / SCALE_FACTOR;
}
function fitToBounds(arr) {
for (let i = 0; i < arr.length; i++) {
const root = arr[i].getRoot() instanceof CompositeNode ? arr[i].getRoot() : null;
const shape = root?.getChild(0);
shape.scale(scale, scale);
centerShape(shape);
}
scale = 1;
}
function createPlot(canvas, isScalable) {
const createGeometry = function() {
const root = new Group().setBounds(new Rect(0, 0, 20, 20));
const shape2 = new SymbolShape({
ax: 10,
ay: 10,
width: 20,
height: 20,
alignment: AnchorType.Center,
painter: new FontPainter(font, '"Noto Sans Bold"', "\u2605"),
fillstyle: "orange",
linestyle: "blue"
});
root.addChild(shape2);
const uSpaceRect = new Rect(0, 0, 20, 20);
return new Group().setBounds(uSpaceRect).setModelLimits(uSpaceRect).addChild(root);
};
const geometry = createGeometry();
const genPattern = PatternFactory.getInstance().createPattern(geometry, {
"scalable": isScalable
});
const shape = new Rectangle(25, 25, 300, 200).setFillStyle({
"pattern": genPattern,
"color": "white"
}).setLineStyle({
"color": KnownColors.Black,
"width": 2
});
const group = new Group().setFillStyle(KnownColors.LightGray).addChild(shape);
const plot = new Plot({
"canvaselement": canvas,
"root": group
});
centerShape(shape);
return plot;
}
function createScene(canvas1, canvas2) {
return [createPlot(canvas1, true), createPlot(canvas2, false)];
}
export { createScene, fitToBounds, zoomIn, zoomOut };
createScene(document.querySelector('[ref="plot1"]'), document.querySelector('[ref="plot2"]'));
# SVG Pattern
The following example illustrates how to use SVG based pattern. It uses SVGParser and Geometry Patterns
import { PatternFactory } from "@int/geotoolkit/attributes/PatternFactory.ts";
import { Group } from "@int/geotoolkit/scene/Group.ts";
import { Rectangle } from "@int/geotoolkit/scene/shapes/Rectangle.ts";
import { Plot } from "@int/geotoolkit/plot/Plot.ts";
import { KnownColors } from "@int/geotoolkit/util/ColorUtil.ts";
import { Rect } from "@int/geotoolkit/util/Rect.ts";
import { SVGParser } from "@int/geotoolkit/svg/SVGParser.ts";
import elementSVG from "/src/assets/svg/element.svg?import&raw";
function createScene(canvas) {
const geometryGroup = new Group();
const parser = new SVGParser();
parser.parse(geometryGroup, elementSVG);
geometryGroup.setBounds(new Rect(0, 0, 30, 30));
const shape = new Rectangle(25, 25, 300, 200).setLineStyle({
"color": KnownColors.Black,
"width": 2
});
const genPattern = PatternFactory.getInstance().createPattern(geometryGroup, {
"scalable": false
});
shape.setFillStyle({
"pattern": genPattern,
"color": "white"
});
const group = new Group().setFillStyle(KnownColors.LightGray).addChild(shape).setModelLimits(shape.getBounds());
return new Plot({
"canvaselement": canvas,
"root": group
});
}
export { createScene };
createScene(document.querySelector('[ref="plot"]'));