Skip to content
Snippets Groups Projects
Commit e8a3f71d authored by Miłosz Grocholewski's avatar Miłosz Grocholewski
Browse files

Merge branch 'feat/MIN-27-compartment-pathway' into 'development'

feat(vector-map): implement pathway compartment

Closes MIN-27

See merge request !274
parents 66953e4c 83edf7a7
No related branches found
No related tags found
1 merge request!274feat(vector-map): implement pathway compartment
Pipeline #96834 failed
......@@ -19,6 +19,7 @@ import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import CompartmentSquare from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentSquare';
import CompartmentCircle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentCircle';
import { ModelElement } from '@/types/models';
import CompartmentPathway from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway';
export const useOlMapReactionsLayer = ({
mapInstance,
......@@ -36,16 +37,48 @@ export const useOlMapReactionsLayer = ({
const shapes = useSelector(bioShapesSelector);
const lineTypes = useSelector(lineTypesSelector);
const elements: Array<MapElement | CompartmentCircle | CompartmentSquare> = useMemo(() => {
if (!modelElements || !shapes) return [];
const elements: Array<MapElement | CompartmentCircle | CompartmentSquare | CompartmentPathway> =
useMemo(() => {
if (!modelElements || !shapes) return [];
const validElements: Array<MapElement | CompartmentCircle | CompartmentSquare> = [];
modelElements.content.forEach((element: ModelElement) => {
const shape = shapes.find(bioShape => bioShape.sboTerm === element.sboTerm);
if (shape) {
validElements.push(
new MapElement({
shapes: shape.shapes,
const validElements: Array<
MapElement | CompartmentCircle | CompartmentSquare | CompartmentPathway
> = [];
modelElements.content.forEach((element: ModelElement) => {
const shape = shapes.find(bioShape => bioShape.sboTerm === element.sboTerm);
if (shape) {
validElements.push(
new MapElement({
shapes: shape.shapes,
x: element.x,
y: element.y,
nameX: element.nameX,
nameY: element.nameY,
nameHeight: element.nameHeight,
nameWidth: element.nameWidth,
width: element.width,
height: element.height,
zIndex: element.z,
lineWidth: element.lineWidth,
lineType: element.borderLineType,
fontColor: element.fontColor,
fillColor: element.fillColor,
borderColor: element.borderColor,
nameVerticalAlign: element.nameVerticalAlign as VerticalAlign,
nameHorizontalAlign: element.nameHorizontalAlign as HorizontalAlign,
homodimer: element.homodimer,
activity: element.activity,
text: element.name,
fontSize: element.fontSize,
pointToProjection,
mapInstance,
modifications: element.modificationResidues,
lineTypes,
bioShapes: shapes,
}),
);
} else if (element.sboTerm === 'SBO:0000290') {
const compartmentProps = {
x: element.x,
y: element.y,
nameX: element.nameX,
......@@ -55,57 +88,30 @@ export const useOlMapReactionsLayer = ({
width: element.width,
height: element.height,
zIndex: element.z,
lineWidth: element.lineWidth,
lineType: element.borderLineType,
innerWidth: element.innerWidth,
outerWidth: element.outerWidth,
thickness: element.thickness,
fontColor: element.fontColor,
fillColor: element.fillColor,
borderColor: element.borderColor,
nameVerticalAlign: element.nameVerticalAlign as VerticalAlign,
nameHorizontalAlign: element.nameHorizontalAlign as HorizontalAlign,
homodimer: element.homodimer,
activity: element.activity,
text: element.name,
fontSize: element.fontSize,
pointToProjection,
mapInstance,
modifications: element.modificationResidues,
lineTypes,
bioShapes: shapes,
}),
);
} else if (element.sboTerm === 'SBO:0000290') {
const compartmentProps = {
x: element.x,
y: element.y,
nameX: element.nameX,
nameY: element.nameY,
nameHeight: element.nameHeight,
nameWidth: element.nameWidth,
width: element.width,
height: element.height,
zIndex: element.z,
innerWidth: element.innerWidth,
outerWidth: element.outerWidth,
thickness: element.thickness,
fontColor: element.fontColor,
fillColor: element.fillColor,
borderColor: element.borderColor,
nameVerticalAlign: element.nameVerticalAlign as VerticalAlign,
nameHorizontalAlign: element.nameHorizontalAlign as HorizontalAlign,
text: element.name,
fontSize: element.fontSize,
pointToProjection,
mapInstance,
};
if (element.shape === 'OVAL_COMPARTMENT') {
validElements.push(new CompartmentCircle(compartmentProps));
} else if (element.shape === 'SQUARE_COMPARTMENT') {
validElements.push(new CompartmentSquare(compartmentProps));
};
if (element.shape === 'OVAL_COMPARTMENT') {
validElements.push(new CompartmentCircle(compartmentProps));
} else if (element.shape === 'SQUARE_COMPARTMENT') {
validElements.push(new CompartmentSquare(compartmentProps));
} else if (element.shape === 'PATHWAY') {
validElements.push(new CompartmentPathway(compartmentProps));
}
}
}
});
return validElements;
}, [modelElements, shapes, pointToProjection, mapInstance, lineTypes]);
});
return validElements;
}, [modelElements, shapes, pointToProjection, mapInstance, lineTypes]);
const features = useMemo(() => {
return elements.map(element => element.multiPolygonFeature);
......
/* eslint-disable no-magic-numbers */
import { Feature, Map } from 'ol';
import { Fill, Style, Text } from 'ol/style';
import { Polygon, MultiPolygon } from 'ol/geom';
import getTextStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextStyle';
import getMultiPolygon from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getMultiPolygon';
import getStroke from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStroke';
import getFill from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getFill';
import { rgbToHex } from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/rgbToHex';
import View from 'ol/View';
import {
WHITE_COLOR,
BLACK_COLOR,
} from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants';
import CompartmentPathway, {
CompartmentPathwayProps,
} from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway';
import getEllipseCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getEllipseCoords';
import getTextCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextCoords';
jest.mock('../text/getTextStyle');
jest.mock('../text/getTextCoords');
jest.mock('./getMultiPolygon');
jest.mock('../style/getStroke');
jest.mock('../coords/getEllipseCoords');
jest.mock('../style/getFill');
jest.mock('../style/rgbToHex');
describe('MapElement', () => {
let props: CompartmentPathwayProps;
beforeEach(() => {
const dummyElement = document.createElement('div');
const mapInstance = new Map({
target: dummyElement,
view: new View({
zoom: 5,
minZoom: 3,
maxZoom: 7,
}),
});
props = {
x: 0,
y: 0,
width: 100,
height: 100,
zIndex: 1,
fillColor: WHITE_COLOR,
borderColor: BLACK_COLOR,
fontColor: BLACK_COLOR,
outerWidth: 2,
text: 'Test Text',
fontSize: 12,
nameX: 10,
nameY: 20,
nameHeight: 30,
nameWidth: 40,
nameVerticalAlign: 'MIDDLE',
nameHorizontalAlign: 'CENTER',
pointToProjection: jest.fn(() => [10, 10]),
mapInstance,
};
(getTextStyle as jest.Mock).mockReturnValue(
new Style({
text: new Text({
text: props.text,
font: `bold ${props.fontSize}px Arial`,
fill: new Fill({
color: '#000',
}),
placement: 'point',
textAlign: 'center',
textBaseline: 'middle',
}),
}),
);
(getTextCoords as jest.Mock).mockReturnValue([10, 10]);
(getMultiPolygon as jest.Mock).mockReturnValue([
new Polygon([
[
[0, 0],
[1, 1],
[2, 2],
],
]),
]);
(getStroke as jest.Mock).mockReturnValue(new Style());
(getFill as jest.Mock).mockReturnValue(new Style());
(rgbToHex as jest.Mock).mockReturnValue('#FFFFFF');
(getEllipseCoords as jest.Mock).mockReturnValue([
[10, 10],
[20, 20],
[30, 30],
]);
});
it('should initialize with correct default properties', () => {
const multiPolygon = new CompartmentPathway(props);
expect(multiPolygon.polygons.length).toBe(2);
expect(multiPolygon.multiPolygonFeature).toBeInstanceOf(Feature);
expect(multiPolygon.multiPolygonFeature.getGeometry()).toBeInstanceOf(MultiPolygon);
});
it('should apply correct styles to the feature', () => {
const multiPolygon = new CompartmentPathway(props);
const feature = multiPolygon.multiPolygonFeature;
const style = feature.getStyleFunction()?.call(multiPolygon, feature, 1);
if (Array.isArray(style)) {
expect(style.length).toBeGreaterThan(0);
} else {
expect(style).toBeInstanceOf(Style);
}
});
});
/* eslint-disable no-magic-numbers */
import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
import { MapInstance } from '@/types/map';
import {
ColorObject,
HorizontalAlign,
VerticalAlign,
} from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.types';
import {
BLACK_COLOR,
WHITE_COLOR,
} from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants';
import Polygon from 'ol/geom/Polygon';
import BaseMultiPolygon from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/BaseMultiPolygon';
import getStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStyle';
export type CompartmentPathwayProps = {
x: number;
y: number;
width: number;
height: number;
zIndex: number;
fillColor?: ColorObject;
borderColor?: ColorObject;
fontColor?: ColorObject;
outerWidth?: number;
text?: string;
fontSize?: number;
nameX: number;
nameY: number;
nameHeight: number;
nameWidth: number;
nameVerticalAlign?: VerticalAlign;
nameHorizontalAlign?: HorizontalAlign;
pointToProjection: UsePointToProjectionResult;
mapInstance: MapInstance;
};
export default class CompartmentPathway extends BaseMultiPolygon {
outerWidth: number;
constructor({
x,
y,
width,
height,
zIndex,
fillColor = WHITE_COLOR,
borderColor = BLACK_COLOR,
fontColor = BLACK_COLOR,
outerWidth = 1,
text = '',
fontSize = 12,
nameX,
nameY,
nameHeight,
nameWidth,
nameVerticalAlign = 'MIDDLE',
nameHorizontalAlign = 'CENTER',
pointToProjection,
mapInstance,
}: CompartmentPathwayProps) {
super({
x,
y,
width,
height,
zIndex,
text,
fontSize,
nameX,
nameY,
nameWidth,
nameHeight,
fontColor,
nameVerticalAlign,
nameHorizontalAlign,
fillColor,
borderColor,
pointToProjection,
});
this.outerWidth = outerWidth;
this.createPolygons();
this.drawText();
this.drawMultiPolygonFeature(mapInstance);
}
protected createPolygons(): void {
const compartmentPolygon = new Polygon([
[
this.pointToProjection({ x: this.x, y: this.y }),
this.pointToProjection({ x: this.x + this.width, y: this.y }),
this.pointToProjection({ x: this.x + this.width, y: this.y + this.height }),
this.pointToProjection({ x: this.x, y: this.y + this.height }),
],
]);
this.styles.push(
getStyle({
geometry: compartmentPolygon,
borderColor: this.borderColor,
fillColor: { ...this.fillColor, alpha: 9 },
lineWidth: this.outerWidth,
zIndex: this.zIndex,
}),
);
this.polygons.push(compartmentPolygon);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment