Composition in JS - javascript

I am learning the concepts of Composition in JS. Below is my demo code.
The moveBy function assigns the values correctly to x and y.
However, the setFillColor function does not assign the passed value to fillColor.
What exactly is happening when the setFillColor function is called?
const withMoveBy = (shape) => ({
moveBy: (diffX, diffY) => {
shape.dimensions.x += diffX;
shape.dimensions.y += diffY;
},
});
const withSetFillColor = (shape) => ({
setFillColor: (color) => {
console.log(shape.fillColor); // 1
shape.fillColor = color;
shape.dimensions.fillColor = color;
console.log(shape.fillColor); // 2
},
});
const shapeRectangle = (dimensions) => ({
type: 'rectangle',
fillColor: 'white',
dimensions,
});
const shapeCircle = (dimensions) => ({
type: 'circle',
fillColor: 'white',
dimensions,
});
const createShape = (type, dimensions) => {
let shape = null;
switch (type) {
case 'rectangle': {
shape = shapeRectangle(dimensions);
break;
}
case 'circle': {
shape = shapeCircle(dimensions);
break;
}
}
if (shape) {
shape = {
...shape,
...withSetFillColor(shape),
...withMoveBy(shape),
};
}
return shape;
};
let r = createShape('rectangle', {
x: 1,
y: 1,
width: 10,
height: 10,
});
let c = createShape('circle', { x: 10, y: 10, diameter: 10 });
r.moveBy(2, 3);
c.moveBy(1, 2);
r.setFillColor('red');
c.setFillColor('blue');
console.log(r);
console.log(c);
OUTPUT:
Line marked as // 1 prints white in case of rectangle as well as circle.
Line marked as // 2 prints red for rectangle and blue for circle.
The final output is:
{
"type": "rectangle",
"fillColor": "white",
"dimensions": {
"x": 3,
"y": 4,
"width": 10,
"height": 10,
"fillColor": "red"
}
}
{
"type": "circle",
"fillColor": "white",
"dimensions": {
"x": 11,
"y": 12,
"diameter": 10,
"fillColor": "blue"
}
}
The fillColor as the property of object is still white.
However, the one inside of dimensions has taken the correct value.

The problem stems from this assignment in createShape - annotations by me:
// creating the "new object"
shape = {
...shape, // shallow copying of the "old object"
...withSetFillColor(shape),
...withMoveBy(shape),
};
Here, you create a new object that is composed of:
shallow-copied properties of the existing ...shape (type, fillcolor, dimensions which is an object)
setFillColor, a closure that is bound to shape (the old object)
moveBy, a closure that is bound to shape (the old object)
After this statement is executed, you have created two shapes:
The "old object", which the methods operate on
The "new object", which you return
Out of the properties copied from the old object, only dimensions is a non-primitive value, so it is shared between the instances.
Then, when you call:
r.moveBy(2, 3);
it changes oldShape.dimensions, but it's the same object as newShape.dimensions, so it's visible in the output.
However, this call:
r.setFillColor('red');
modifies the fillColor property of the oldShape, which you are not seeing. It also writes to oldShape.dimensions.fillColor, which, again, is shared between the objects, so that change is visible in both.

Let me illustrate the problem by re-writing your code. I have removed some of the details to focus on the issue only. Added annotations and logging to the code to show more clearly what happens:
const withSetFillColor = (shape) => ({
setFillColor: (color) => {
console.log(`now changing shape with id [${shape.id}]`);
shape.fillColor = color;
shape.dimensions.fillColor = color;
},
});
const shapeRectangle = (dimensions) => ({
id: 1, //add an ID of the created object for illustrative purpose
type: 'rectangle',
fillColor: 'white',
dimensions,
});
const createShape = (type, dimensions) => {
//variable is now named 1 to showcase what happens
let shape1 = null;
switch (type) {
case 'rectangle': {
shape1 = shapeRectangle(dimensions);
break;
}
}
//this is effectively what happens when you clone and reassign an object:
//a *second one* is created but the first one persists
let shape2 = null;
if (shape1) {
shape2 = {
...shape1,
...withSetFillColor(shape1),
id: 2, //make it a different ID for illustrative purpose
};
}
console.log(`Created shape1 and shape2 and they are the same: ${shape1 === shape2}`);
console.log(`The dimensions object is the same: ${shape1.dimensions === shape2.dimensions}`);
return shape2;
};
let r = createShape('rectangle', {
x: 1,
y: 1,
width: 10,
height: 10,
});
r.setFillColor('red');
console.log(r);
You create and manipulate two different objects. This is the reason why the code assigns a property to the object but it appears as if it is not changed.
There are several way to deal with this.
Only create one object and assign to it
If you use Object.assign() you can directly change one object instead of having two competing ones. Thus, passing the object to the withX() functions will work as intended.
const withMoveBy = (shape) => ({
moveBy: (diffX, diffY) => {
shape.dimensions.x += diffX;
shape.dimensions.y += diffY;
},
});
const withSetFillColor = (shape) => ({
setFillColor: (color) => {
shape.fillColor = color;
shape.dimensions.fillColor = color;
},
});
const shapeRectangle = (dimensions) => ({
type: 'rectangle',
fillColor: 'white',
dimensions,
});
const shapeCircle = (dimensions) => ({
type: 'circle',
fillColor: 'white',
dimensions,
});
const createShape = (type, dimensions) => {
let shape = null;
switch (type) {
case 'rectangle': {
shape = shapeRectangle(dimensions);
break;
}
case 'circle': {
shape = shapeCircle(dimensions);
break;
}
}
if (shape) {
//use Object assign to only manipulate one `shape` object
Object.assign(
shape,
withSetFillColor(shape),
withMoveBy(shape)
);
}
return shape;
};
let r = createShape('rectangle', {
x: 1,
y: 1,
width: 10,
height: 10,
});
let c = createShape('circle', { x: 10, y: 10, diameter: 10 });
r.moveBy(2, 3);
c.moveBy(1, 2);
r.setFillColor('red');
c.setFillColor('blue');
console.log(r);
console.log(c);
Don't use arrow functions, use this instead
Alternatively, use regular functions or the shorthand method definition syntax which lets you use this. You can then add these methods to your object and use this to refer to the object, instead of having to pass it in.
const withMoveBy = { //no need for a function to produce the object
moveBy(diffX, diffY) { //shorthand method syntax
this.dimensions.x += diffX;
this.dimensions.y += diffY;
},
};
const withSetFillColor = { //no need for a function to produce the object
setFillColor(color) { //shorthand method syntax
this.fillColor = color;
this.dimensions.fillColor = color;
},
};
const shapeRectangle = (dimensions) => ({
type: 'rectangle',
fillColor: 'white',
dimensions,
});
const shapeCircle = (dimensions) => ({
type: 'circle',
fillColor: 'white',
dimensions,
});
const createShape = (type, dimensions) => {
let shape = null;
switch (type) {
case 'rectangle': {
shape = shapeRectangle(dimensions);
break;
}
case 'circle': {
shape = shapeCircle(dimensions);
break;
}
}
if (shape) {
shape = {
...shape,
...withSetFillColor,
...withMoveBy,
};
}
return shape;
};
let r = createShape('rectangle', {
x: 1,
y: 1,
width: 10,
height: 10,
});
let c = createShape('circle', { x: 10, y: 10, diameter: 10 });
r.moveBy(2, 3);
c.moveBy(1, 2);
r.setFillColor('red');
c.setFillColor('blue');
console.log(r);
console.log(c);
A mixed approach
This is more of an explanation of what's happening than an actual new approach.
Both of the above both work but show two sides of the same coin. Combining objects together is called mixin*. Mixins are similar to object composition because you build up more complex objects from simpler ones but also a separate category of its own since you do it via concatenation.
Traditionally, you would use Object.assign(obj, mixinA, mixinB) for adding things to obj. Which makes it similar to the first approach. However, mixinA and mixinB would be actual objects like in the second approach.
Using class syntax, there is an interesting alternative to add mixins to a class. I'm adding it here just to show it - it's totally OK to not use classes and use regular objects instead.
const withMoveBy = Base => class extends Base { //mixin
moveBy(diffX, diffY) {
this.dimensions.x += diffX;
this.dimensions.y += diffY;
}
};
const withSetFillColor = Base => class extends Base { //mixin
setFillColor(color) {
this.fillColor = color;
this.dimensions.fillColor = color;
}
};
class Shape {
constructor({type, fillColor, dimensions}) {
this.type = type;
this.fillColor = fillColor;
this.dimensions = dimensions;
}
}
const shapeRectangle = (dimensions) => ({
type: 'rectangle',
fillColor: 'white',
dimensions,
});
const shapeCircle = (dimensions) => ({
type: 'circle',
fillColor: 'white',
dimensions,
});
const createShape = (type, dimensions) => {
let shapeArgs = null;
switch (type) {
case 'rectangle': {
shapeArgs = shapeRectangle(dimensions);
break;
}
case 'circle': {
shapeArgs = shapeCircle(dimensions);
break;
}
}
let shape = null;
if (shapeArgs) {
//add mixins to the Shape class
const mixedInConstructor = withMoveBy(withSetFillColor(Shape));
//create the enhanced class
shape = new mixedInConstructor(shapeArgs);
}
return shape;
};
let r = createShape('rectangle', {
x: 1,
y: 1,
width: 10,
height: 10,
});
let c = createShape('circle', { x: 10, y: 10, diameter: 10 });
r.moveBy(2, 3);
c.moveBy(1, 2);
r.setFillColor('red');
c.setFillColor('blue');
console.log(r);
console.log(c);
* Yes, the title was a pun. You can laugh now.

Related

Reinitialise the coordinate by using setter in Javascripts

I am new to OOP and just learning now. I want to reinitialise the default location of a circle by using the codes below:
function Circle(radius) {
this.radius = radius;
let defaultLocation = {
x: 0,
y: 0
};
this.getDefaultLocation = function(a, b) {
return defaultLocation
};
Object.defineProperty(this, 'defaultLocation', {
get: function(a, b) {
return defaultLocation;
},
set: function(a, b) {
defaultLocation = {
x: a,
y: b
};
}
});
}
const circle = new Circle(10);
circle.defaultLocation = {
x: 5,
y: 6
};
However, i check in the chrome browser console, the result is:
x: {x: 5, y: 6}
y: undefined
Could you tell me where i done wrong and how to correct it?
Thanks.
You can't pass two variables to set, but you can pass an object (or an array).
class Circle {
get defaultLocation() {
return this._defaultLocation
}
set defaultLocation(loc) {
this._defaultLocation = loc
}
constructor(radius) {
this.radius = radius;
this._defaultLocation = {
x: 0,
y: 0
};
}
}
const circle = new Circle(10);
circle.defaultLocation = {
x: 5,
y: 6
};

Can I access this of an object in JavaScript?

var b = {
state: 'initial',
initial: {
onClick: function() {
console.log(this);
},
x: 0,
},
}
Hi, I want to know if its possible to access b object inside onClick function in the code above?
the output is {x: 0, onClick: ƒ} instead of {state: 'initial', ... }
Changing it to arrow func will output window object instead.
Im making an escape room game and kind of have a chicken & egg situation.
var spriteObject =
{
img: null,
INITIAL: {
sourceX: 0,
sourceY: 0,
sourceWidth: 64,
sourceHeight: 64,
x: 0,
y: 0,
width: 64,
height: 64,
isWithinBounds: function(x, y) {
return x > this.x && x < this.x + this.width && y > this.y && y < this.y + this.height;
},
},
state: 'INITIAL',
};
const inventoryItems = [];
let currentRoom = "kitchen";
const knife = {
...spriteObject,
INITIAL: {
...spriteObject.INITIAL,
x: 200,
y: 162,
sourceX: 1330,
sourceY: 8,
sourceWidth: 803,
sourceHeight: 514,
width: 803,
height: 514,
onClick: () => {
inventoryItems.push(knife);
layers.kitchen.sprites = layers.kitchen.sprites.filter(sprite => sprite !== knife);
},
},
img: kitchenLayout,
};
const layers = {
kitchen: {
sprites: [knife],
},
};
window.addEventListener("click", function(e) {
const x = e.pageX - canvas.offsetLeft;
const y = e.pageY - canvas.offsetTop;
layers[currentRoom].sprites.forEach(sprite => {
const currSprite = sprite[sprite.state];
if (currSprite.onClick && currSprite.isWithinBounds(x, y)) {
currSprite.onClick();
render();
}
})
})
Let's say I have a kitchen room and a knife on the counter. I can pick the knife and put it in my inventory. The knife will have 3 different conditions: on the counter (initial), in inventory, and dull (after used). I am not sure if I want to model in inventory as a state, but I have trouble figuring out how to remove the knife from list of sprites in kitchen. It is doable in the code above but it seems to rely on the fact that I declare knife as a variable. I don't want to do this, in case I want to declare my items directly on the sprites array. I appreciate any hints, thanks
You should be able to use javascript classes for this. That way you can reference your current class as this.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes

What are flexible approaches for creating different kinds or types of shape objects like rect- or triangles?

I have this code snippet that creates an array of boxes, I want to make it generic so it can also, for example, store a triangle. I'm not quite sure what parameters I need to use or how I need to modify this so it will allow a triangle. It almost seems like it would be better to create an array of triangles then position them to form a box if I want triangles and boxes, but then I'd loose the flexibility of creating easy rectangles. Context: this is a snippet of a program that implements a z-buffer.
class Box {
/** #member {Object} position of the box storing x,y,z coordinates */
position;
/** #member {Object} size of the box storing width and height */
size;
/** #member {Object} color of the box given in RGB */
color;
constructor (props) {
this.position = props.position;
this.size = props.size;
this.color = props.color;
}
/**
* Check if given point is in box
* #param {Number} px coordinate of the point
* #param {Number} py coordinate of the point
* #return {Boolean} point in box
*/
pointInBox (px,py) {
return this.position.x < px && this.position.x + this.size.width > px
&& this.position.y < py && this.position.y + this.size.height > py;
}
}
const boxes = [
new Box({
position: { x: 50, y: 50, z: 10 },
size: { width: 150, height: 50 },
color: { r: 255, g: 0, b:0 }
}),
new Box({
position: { x: 80, y: 30, z: 5 },
size: { width: 10, height: 150 },
color: { r: 0, g: 255, b:0 }
}),
new Box({
position: { x: 70, y: 70, z: 8 },
size: { width: 50, height: 40 },
color: { r: 0, g: 0, b: 255 }
})
];
console.log({ boxes });
.as-console-wrapper { min-height: 100%!important; top: 0; }
With vanilla JS you would have to use inheritance such as below. I would recommend using Typescript, a super set of Javascript, that makes using types a lot easier.
class Shape {
constructor({ color, position}){
this.color = color;
this.positon = position;
}
}
class Cube extends Shape {
constructor({color, position, height, width, length}){
super({ color, position });
this.height = height;
this.width = width;
this.length = length;
}
}
const myCube = new Cube({
color: "#555555",
position: {x: 12, y: 5, z: 9},
height: 12,
width: 12,
length: 12
});
console.log(myCube)
In Typescript, it would look something like this instead:
interface Coordinate {
x: number;
y: number;
z: number;
}
interface Shape {
color: string;
position: Coordinate;
}
interface Box extends Shape {
height: number;
width: number;
length: number;
}
And now, If I want a function that will work for both boxes and shapes, you can do it like this:
function getPosition(shape:Shape){
return shape.position;
}
const myShape: Box = {
color: "red",
position: {
x: 1,
y: 4,
z: 7,
},
height: 12,
length: 12,
width: 12,
};
getPosition(myShape);
Because Box extends shape, the function works for both of them, and any other interface that extends shape.
That's just scratching the surface of what you can do with Typescript.
With Vanilla-JS one of cause is not limited to just one option like choosing an inheritance based approach. Composition, based on tailored mixins, does provide/support the flexibility the OP is looking for.
One also is free of where (and when and even how) to compose, for instance at plain object level within e.g. a factory function or at instantiation time within a class constructor.
// function based "position" mixin.
function withPosition({ x = 0, y = 0, z = 0 }) {
// "position" specifc (default) assignment.
Object.assign(this, { position: { x, y, z } });
}
// function based "shape" mixin.
function asShape({ color='#000', ...position }) {
// "shape" specifc (default) assignment.
Object.assign(this, { color });
// delegate "position" specific assignement
// and default handling to the mixin.
withPosition.call(this, (position || {}));
}
// factory function.
function createRectangle({ width=10, height=10, ...options }) {
const type = {};
// delegate "shape" specific assignements
// and default handling to the mixin.
asShape.call(type, options);
// "rectangle" specifc assignments including defaults.
return Object.assign(type, { width, height });
}
// factory function.
function createCube({ length=10, ...options }) {
// composition via ...
// ... "rectangle" specifc forwarding ...
// ............. and "cube" specific `length` enrichment.
return { ...createRectangle(options), length }
}
class Cube {
// `Cube` type instantiation.
constructor({ width=10, height=10, length=10, ...options }) {;
// delegate "shape" specific assignements
// and default handling to the mixin.
asShape.call(this, options);
// "cube" specifc assignments including defaults.
Object.assign(this, { width, height, length });
}/*
constructor({ length=10, ...options }) {
Object.assign(this, { ...createRectangle(options), length });
}*/
}
const my1stRectangle = createRectangle({
x: 50,
y: 50,
z: 10,
width: 150,
height: 50,
color: '#fc0',
});
const my2ndRectangle = createRectangle({});
const myCubeComposite = createCube({
x: 50,
y: 50,
z: 50,
color: '#c0f',
});
const myComposedCubeType = new Cube({});
console.log({
my1stRectangle,
my2ndRectangle,
myCubeComposite,
myComposedCubeType,
});
console.log(
'(myCubeComposite instanceof Cube) ?',
(myCubeComposite instanceof Cube)
);
console.log(
'(myComposedCubeType instanceof Cube) ?',
(myComposedCubeType instanceof Cube)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

Running ForceAtlas2 from a set position in Sigma.js

I am trying to render a complex network using the React-Sigma wrapper. I know the base structure of this network to be many nodes of degree one and some of a very high degree. To this end, I have preformatted my graph data with x and y coordinates that represent an approximate layout, with degree one nodes clustered around the nodes they are connected to. I then want to run the ForceAtlas2 simulation from this point. However, when I try to do this, the simulation appears to just randomise and change the initial positions of the nodes to be in the centre, as seen in this gif:
Is there any way to stop this from happening and run the simulation from the initial positions? My code is below:
const App = () => {
const myGraph = getExampleGraph();
return (
<Sigma
graph={myGraph}
renderer="webgl"
settings={{
clone: false,
batchEdgesDrawing: true,
}}
style={{
height: "100vh"
}}
>
<ForceAtlas2
iterationsPerRender={1}
barnesHutOptimize
barnesHutTheta={1}
timeout={50000}
worker
/>
</Sigma>
)
}
Code to get a random example graph like the one I described:
export const getRandomInt = (max: number) => Math.floor(Math.random() * Math.floor(max));
export const getRandomPosition = () => ({
x: Math.random(),
y: Math.random()
});
export const randomisePosition = (position) => ({
x: position.x + (Math.random() - .5)/10,
y: position.y + (Math.random() - .5)/10
});
export const getExampleGraph = () => {
const positions: any = {
"1": getRandomPosition(),
"2": getRandomPosition(),
"3": getRandomPosition()
};
const highDegNodes = [
{ id: "1", size: 20, label: "1", ...positions["1"]},
{ id: "2", size: 20, label: "2", ...positions["2"] },
{ id: "3", size: 20, label: "3", ...positions["3"] }
];
const nodes = Object.assign([], highDegNodes);
const edges = [];
for(let i = 0; i < 50; i += 1) {
const id = (i + 4) + '';
const node = { id, size: 1, label: id, x: 0, y: 0 };
const target = (getRandomInt(3) + 1) + '';
edges.push({
id: `${id}:${target}:${i}`,
source: id,
target,
});
const nodePos = randomisePosition(positions[target]);
node.x = nodePos.x;
node.y = nodePos.y;
nodes.push(node);
if (Math.random() < .1) {
const target2 = (getRandomInt(3) + 1) + '';
edges.push({
id: `${id}:${target2}:${i}:2`,
source: id,
target: target2,
});
}
}
return {
nodes,
edges
}
};
The problem there is with implementation of ForceAtlas2 used in Sigma.js. It expects a higher scale of positions and is highly unstable at scale under 1.
The easiest to stabilize is you can multiply your positions by 100:
const getRandomPosition = () => ({
x: Math.random()*100,
y: Math.random()*100
});
const randomisePosition = (position) => ({
x: position.x + (Math.random() - .5)*10,
y: position.y + (Math.random() - .5)*10
});
you could also apply slowDown={10} to forceAtlas to make it softer and remove batchEdgesDrawing if your graph is not too big:
<Sigma
graph={myGraph}
renderer="webgl"
settings={{
clone: false,
}}
style={{
height: "100vh"
}}
>
<ForceAtlas2
iterationsPerRender={1}
barnesHutOptimize
barnesHutTheta={1}
slowDown={10}
timeout={2000}
worker
/>
</Sigma>

How to refactor reduce with array value

I'm trying to clean up code.
selectedGroup.items looks like this (simplified):
[
{ szenarien: [{extent: {xmin: 1, xmax: 2, ymin: 3, ymax: 4}}, ...] },
{ extent: {...}] } // Extent has same properties as above
]
Below is my mapping code. Goal: Get the outermost points of a collection of polygons
const coordinateValues = selectedGroup.items.reduce(
//TODO: Make this a bit more readable
(acc, item) => {
// item is a group
if (item.szenarios) {
item.szenarios.map((szenario) => {
acc.xmin.push(szenario.extent.xmin);
acc.xmax.push(szenario.extent.xmax);
acc.ymin.push(szenario.extent.ymin);
acc.ymax.push(szenario.extent.ymax);
});
}
// item is a szenario
else {
acc.xmin.push(item.extent.xmin);
acc.xmax.push(item.extent.xmax);
acc.ymin.push(item.extent.ymin);
acc.ymax.push(item.extent.ymax);
}
return acc;
},
{ xmin: [], xmax: [], ymin: [], ymax: [] }
);
// Prepare an extent with the smallest xmin, ymin and the biggest xmax, ymax to have all szenarios in it
const calculatedGroupExtent: __esri.Extent = new EsriExtent({
xmax: Math.max(...coordinateValues.xmax) + 200,
xmin: Math.min(...coordinateValues.xmin) - 200,
ymax: Math.max(...coordinateValues.ymax) + 200,
ymin: Math.min(...coordinateValues.ymin) - 200,
spatialReference: { wkid: 2056 },
});
Obviously it's not really readable. I can't find a beautiful way to simplify it without adding many "readability constants" (and thus having no improvement)
You could make a helper function that takes a szenario object and pushes each of the ['xmin', 'xmax', 'ymin', 'ymax'] properties to the appropriate array. Then, while iterating over items, you just need to test whether the item is a group (in which case you'd do item.szenarios.forEach(addSzenario)), otherwise add the single szenario: addSzenario(item.extent)
const props = ['xmin', 'xmax', 'ymin', 'ymax'];
const coordinateValues = Object.fromEntries(
props.map(prop => [prop, []])
);
const addSzenario = (szenario) => {
for (const prop of props) {
coordinateValues[prop].push(szenario[prop]);
}
};
for (const item of selectedGroup.items) {
// item is a group
if (item.szenarios) {
item.szenarios.forEach(addSzenario);
} else {
addSzenario(item.extent);
}
}
you can use reduce to compute the max and min.
const getVal = (item) => {
return item.szenarios ? szenario.extent[key] : item.extent[key];
}
const getMin = (group, key) => {
return group.items.reduce((acc, item) => {
return Math.min(acc, getVal(item));
}, Number.MAX_SAFE_INTEGER);
}
const getMax = (group, key) => {
return group.items.reduce((acc, item) => {
return Math.max(acc, getVal(item));
}, Number.MIN_SAFE_INTEGER);
}
const xmin = getMin(selectedGroup, "xmin");
const xmax = getMin(selectedGroup, "xmax");
const ymin = getMin(selectedGroup, "ymin");
const ymax = getMin(selectedGroup, "ymax");
I came up with a mix of both answers:
const pointIdentifiers = ['xmin', 'xmax', 'ymin', 'ymax'];
// Get all coordinates of the group's szenarios
const coordinateValues = selectedGroup.items.reduce((acc, item) => {
// Differenciate if item is a szenario itself or a group (with szenarios)
const szenarios = item.szenarios ? item.szenarios : [item];
// Add every point for every szenario
szenarios.forEach((valueItem) => {
pointIdentifiers.forEach((identifier) => {
acc[identifier].push(valueItem.extent[identifier]);
});
});
return acc;
}, Object.fromEntries(pointIdentifiers.map((prop) => [prop, []])));
// Prepare an extent with the smallest xmin, ymin and the biggest xmax, ymax to have all szenarios in it
const calculatedGroupExtent: __esri.Extent = new EsriExtent({
xmax: Math.max(...coordinateValues.xmax) + 200,
xmin: Math.min(...coordinateValues.xmin) - 200,
ymax: Math.max(...coordinateValues.ymax) + 200,
ymin: Math.min(...coordinateValues.ymin) - 200,
spatialReference: { wkid: 2056 },
});

Categories