Skip to content
Snippets Groups Projects
Commit 42bfbca0 authored by Tadeusz Miesiąc's avatar Tadeusz Miesiąc
Browse files

Merge branch 'fix/overlays-color-alpha' into 'development'

fix(overlays): fixed opacity for overlays generated by entity color

See merge request !96
parents 33a6d7f2 006f25a3
No related branches found
No related tags found
2 merge requests!223reset the pin numbers before search results are fetch (so the results will be...,!96fix(overlays): fixed opacity for overlays generated by entity color
Pipeline #84491 passed
Showing
with 388 additions and 174 deletions
......@@ -15,8 +15,8 @@
"prepare": "husky install",
"postinstall": "husky install",
"test": "jest --config ./jest.config.ts --transformIgnorePatterns 'node_modules/(?!(ol|geotiff|quick-lru|color-space|color-rgba|color-parse|.*\\.mjs$))'",
"test:ci": "jest --config ./jest.config.ts --collectCoverage --coverageDirectory=\"./coverage\" --ci --reporters=default --reporters=jest-junit --watchAll=false --passWithNoTests --transformIgnorePatterns 'node_modules/(?!(ol|geotiff|quick-lru|color-space|color-rgba|color-parse|.*\\.mjs$))'",
"test:watch": "jest --watch --config ./jest.config.ts --transformIgnorePatterns 'node_modules/(?!(ol|geotiff|quick-lru|color-space|color-rgba|color-parse|.*\\.mjs$))'",
"test:ci": "jest --config ./jest.config.ts --collectCoverage --coverageDirectory=\"./coverage\" --ci --reporters=default --reporters=jest-junit --watchAll=false --passWithNoTests --transformIgnorePatterns 'node_modules/(?!(ol|geotiff|quick-lru|color-space|color-rgba|color-parse|.*\\.mjs$))'",
"test:coverage": "jest --watchAll --coverage --config ./jest.config.ts --transformIgnorePatterns 'node_modules/(?!(ol|geotiff|quick-lru|.*\\.mjs$))'",
"test:coveragee": "jest --coverage --transformIgnorePatterns 'node_modules/(?!(ol|geotiff|quick-lru|.*\\.mjs$))'",
"coverage": "open ./coverage/lcov-report/index.html",
......
import { OverlayBioEntityRender } from '@/types/OLrendering';
import { getColorByAvailableProperties } from './getColorByAvailableProperties';
describe('getColorByAvailableProperties', () => {
const ENTITY: OverlayBioEntityRender = {
id: 0,
modelId: 0,
x1: 0,
y1: 0,
x2: 0,
y2: 0,
width: 0,
height: 0,
value: null,
overlayId: 0,
color: null,
};
const getHexTricolorGradientColorWithAlpha = jest.fn().mockReturnValue('#FFFFFF');
const defaultColor = '#000000';
beforeEach(() => {
jest.clearAllMocks();
});
it('should return the result of getHexTricolorGradientColorWithAlpha if entity has a value equal to 0', () => {
const entity = { ...ENTITY, value: 0 };
const result = getColorByAvailableProperties(
entity,
getHexTricolorGradientColorWithAlpha,
defaultColor,
);
expect(result).toEqual('#FFFFFF');
expect(getHexTricolorGradientColorWithAlpha).toHaveBeenCalledWith(entity.value);
});
it('should return the result of getHexTricolorGradientColorWithAlpha if entity has a value', () => {
const entity = { ...ENTITY, value: -0.2137 };
const result = getColorByAvailableProperties(
entity,
getHexTricolorGradientColorWithAlpha,
defaultColor,
);
expect(result).toEqual('#FFFFFF');
expect(getHexTricolorGradientColorWithAlpha).toHaveBeenCalledWith(entity.value);
});
it('should return the result of convertDecimalToHex if entity has a color', () => {
const entity = { ...ENTITY, color: { rgb: -65536, alpha: 0 } }; // red color
const result = getColorByAvailableProperties(
entity,
getHexTricolorGradientColorWithAlpha,
defaultColor,
);
expect(result).toEqual('#ff0000');
expect(getHexTricolorGradientColorWithAlpha).not.toHaveBeenCalled();
});
it('should return the default color if entity has neither a value nor a color', () => {
const result = getColorByAvailableProperties(
ENTITY,
getHexTricolorGradientColorWithAlpha,
defaultColor,
);
expect(result).toEqual('#000000');
expect(getHexTricolorGradientColorWithAlpha).not.toHaveBeenCalled();
});
});
import { ZERO } from '@/constants/common';
import type { GetHex3ColorGradientColorWithAlpha } from '@/hooks/useTriColorLerp';
import { OverlayBioEntityRender } from '@/types/OLrendering';
import { convertDecimalToHexColor } from '@/utils/convert/convertDecimalToHex';
export const getColorByAvailableProperties = (
entity: OverlayBioEntityRender,
getHexTricolorGradientColorWithAlpha: GetHex3ColorGradientColorWithAlpha,
defaultColor: string,
): string => {
if (typeof entity.value === 'number') {
return getHexTricolorGradientColorWithAlpha(entity.value || ZERO);
}
if (entity.color) {
return convertDecimalToHexColor(entity.color.rgb);
}
return defaultColor;
};
import type { GetHex3ColorGradientColorWithAlpha } from '@/hooks/useTriColorLerp';
import { OverlayBioEntityRender } from '@/types/OLrendering';
import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
import type Feature from 'ol/Feature';
import type Polygon from 'ol/geom/Polygon';
import { OverlayOrder } from '@/redux/overlayBioEntity/overlayBioEntity.utils';
import { ZERO } from '@/constants/common';
import { createOverlayGeometryFeature } from './createOverlayGeometryFeature';
import { getColorByAvailableProperties } from './getColorByAvailableProperties';
import { getPolygonLatitudeCoordinates } from './getPolygonLatitudeCoordinates';
type GetOverlayFeaturesProps = {
bioEntities: OverlayBioEntityRender[];
pointToProjection: UsePointToProjectionResult;
getHex3ColorGradientColorWithAlpha: GetHex3ColorGradientColorWithAlpha;
defaultColor: string;
overlaysOrder: OverlayOrder[];
};
export const getOverlayFeatures = ({
bioEntities,
pointToProjection,
getHex3ColorGradientColorWithAlpha,
defaultColor,
overlaysOrder,
}: GetOverlayFeaturesProps): Feature<Polygon>[] =>
bioEntities.map(entity => {
/**
* Depending on number of active overlays
* it's required to calculate xMin and xMax coordinates of the polygon
* so "entity" might be devided equali between active overlays
*/
const { xMin, xMax } = getPolygonLatitudeCoordinates({
width: entity.width,
nOverlays: overlaysOrder.length,
xMin: entity.x1,
overlayIndexBasedOnOrder:
overlaysOrder.find(({ id }) => id === entity.overlayId)?.index || ZERO,
});
return createOverlayGeometryFeature(
[
...pointToProjection({ x: xMin, y: entity.y1 }),
...pointToProjection({ x: xMax, y: entity.y2 }),
],
getColorByAvailableProperties(entity, getHex3ColorGradientColorWithAlpha, defaultColor),
);
});
import { renderHook } from '@testing-library/react';
import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
import { CONFIGURATION_INITIAL_STORE_MOCKS } from '@/redux/configuration/configuration.mock';
import { OverlayBioEntityRender } from '@/types/OLrendering';
import { useGetOverlayColor } from './useGetOverlayColor';
describe('useOverlayFeatures - hook', () => {
const { Wrapper } = getReduxWrapperWithStore({
configuration: {
...CONFIGURATION_INITIAL_STORE_MOCKS,
},
});
const {
result: {
current: { getOverlayBioEntityColorByAvailableProperties },
},
} = renderHook(() => useGetOverlayColor(), {
wrapper: Wrapper,
});
describe('getOverlayBioEntityColorByAvailableProperties - function', () => {
const ENTITY: OverlayBioEntityRender = {
id: 0,
modelId: 0,
x1: 0,
y1: 0,
x2: 0,
y2: 0,
width: 0,
height: 0,
value: null,
overlayId: 0,
color: null,
};
it('should return color based on value', () => {});
it('should return the correct result if entity has a value equal to 0', () => {
const entity = { ...ENTITY, value: 0 };
expect(getOverlayBioEntityColorByAvailableProperties(entity)).toEqual('#FFFFFFcc');
});
it('should return the result if entity has a value', () => {
const entity = { ...ENTITY, value: -0.2137 };
expect(getOverlayBioEntityColorByAvailableProperties(entity)).toEqual('#FFC9C9cc');
});
it('should return the correct result if entity has a color but no value', () => {
const entity = { ...ENTITY, color: { rgb: -65536, alpha: 0 } }; // red color
expect(getOverlayBioEntityColorByAvailableProperties(entity)).toEqual('#ff0000cc');
});
it('should return the default color if entity has neither a value nor a color', () => {
const entity = { ...ENTITY };
expect(getOverlayBioEntityColorByAvailableProperties(entity)).toEqual('#00FF00cc');
});
});
});
import { useCallback } from 'react';
import { useCallback, useMemo } from 'react';
import { WHITE_HEX_OPACITY_0 } from '@/constants/hexColors';
import {
maxColorValSelector,
......@@ -9,17 +9,18 @@ import {
} from '@/redux/configuration/configuration.selectors';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { getHexTricolorGradientColorWithAlpha } from '@/utils/convert/getHexTricolorGradientColorWithAlpha';
import { ONE } from '@/constants/common';
import { addAlphaToHexString } from '../utils/convert/addAlphaToHexString';
import { ONE, ZERO } from '@/constants/common';
import { OverlayBioEntityRender } from '@/types/OLrendering';
import { addAlphaToHexString } from '@/utils/convert/addAlphaToHexString';
import { getHexStringColorFromRGBIntWithAlpha } from '@/utils/convert/getHexStringColorFromRGBIntWithAlpha';
export type GetHex3ColorGradientColorWithAlpha = (position: number) => string;
type GetOverlayBioEntityColorByAvailableProperties = (entity: OverlayBioEntityRender) => string;
type UseTriColorLerpReturn = {
getHex3ColorGradientColorWithAlpha: GetHex3ColorGradientColorWithAlpha;
defaultColorHex: string;
getOverlayBioEntityColorByAvailableProperties: GetOverlayBioEntityColorByAvailableProperties;
};
export const useTriColorLerp = (): UseTriColorLerpReturn => {
export const useGetOverlayColor = (): UseTriColorLerpReturn => {
const minColorValHexString = useAppSelector(minColorValSelector) || '';
const maxColorValHexString = useAppSelector(maxColorValSelector) || '';
const neutralColorValHexString = useAppSelector(neutralColorValSelector) || '';
......@@ -38,7 +39,35 @@ export const useTriColorLerp = (): UseTriColorLerpReturn => {
[minColorValHexString, neutralColorValHexString, maxColorValHexString, overlayOpacityValue],
);
const defaultColorHex = addAlphaToHexString(simpleColorValue, Number(overlayOpacityValue));
const getHexColorFromRGBIntWithAlpha = useCallback(
(rgbInt: number) =>
getHexStringColorFromRGBIntWithAlpha({ rgbInt, alpha: Number(overlayOpacityValue) }),
[overlayOpacityValue],
);
const defaultColorHex = useMemo(
() => addAlphaToHexString(simpleColorValue, Number(overlayOpacityValue)),
[simpleColorValue, overlayOpacityValue],
);
/**
* Entity might have 3 properties that indicates color:
* value - which value from [-1,1] range and needs to interpolate between minColorVal, neutralColorVal and maxColorVal
* color - which is integer representation of color and needs to be converted to hex string
* defaultColor - which is hex string representation of color. It occurs when color and value are not available
*/
const getOverlayBioEntityColorByAvailableProperties = useCallback(
(entity: OverlayBioEntityRender) => {
if (typeof entity.value === 'number') {
return getHex3ColorGradientColorWithAlpha(entity.value || ZERO);
}
if (entity.color) {
return getHexColorFromRGBIntWithAlpha(entity.color.rgb);
}
return defaultColorHex;
},
[getHex3ColorGradientColorWithAlpha, getHexColorFromRGBIntWithAlpha, defaultColorHex],
);
return { getHex3ColorGradientColorWithAlpha, defaultColorHex };
return { getOverlayBioEntityColorByAvailableProperties };
};
import { useTriColorLerp } from '@/hooks/useTriColorLerp';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import {
getOverlayOrderSelector,
overlayBioEntitiesForCurrentModelSelector,
} from '@/redux/overlayBioEntity/overlayBioEntity.selector';
import { usePointToProjection } from '@/utils/map/usePointToProjection';
import { Feature } from 'ol';
import { Geometry } from 'ol/geom';
import Geometry from 'ol/geom/Geometry';
import VectorLayer from 'ol/layer/Vector';
import VectorSource from 'ol/source/Vector';
import { Feature } from 'ol';
import { useMemo } from 'react';
import { getOverlayFeatures } from './getOverlayFeatures';
import { useOverlayFeatures } from './useOverlayFeatures';
/**
* Prerequisites: "view" button triggers opening overlays -> it triggers downloading overlayBioEntityData for given overlay for ALL available submaps(models)
......@@ -27,34 +20,13 @@ import { getOverlayFeatures } from './getOverlayFeatures';
*/
export const useOlMapOverlaysLayer = (): VectorLayer<VectorSource<Feature<Geometry>>> => {
const pointToProjection = usePointToProjection();
const { getHex3ColorGradientColorWithAlpha, defaultColorHex } = useTriColorLerp();
const bioEntities = useAppSelector(overlayBioEntitiesForCurrentModelSelector);
const overlaysOrder = useAppSelector(getOverlayOrderSelector);
const features = useMemo(
() =>
getOverlayFeatures({
bioEntities,
pointToProjection,
getHex3ColorGradientColorWithAlpha,
defaultColor: defaultColorHex,
overlaysOrder,
}),
[
bioEntities,
getHex3ColorGradientColorWithAlpha,
pointToProjection,
defaultColorHex,
overlaysOrder,
],
);
const overlaysFeatures = useOverlayFeatures();
const vectorSource = useMemo(() => {
return new VectorSource({
features,
features: overlaysFeatures,
});
}, [features]);
}, [overlaysFeatures]);
const overlaysLayer = useMemo(
() =>
......
/* eslint-disable no-magic-numbers */
import { renderHook } from '@testing-library/react';
import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
import { CONFIGURATION_INITIAL_STORE_MOCKS } from '@/redux/configuration/configuration.mock';
import { OVERLAYS_PUBLIC_FETCHED_STATE_MOCK } from '@/redux/overlays/overlays.mock';
import { mapStateWithCurrentlySelectedMainMapFixture } from '@/redux/map/map.fixtures';
import { MODELS_DATA_MOCK_WITH_MAIN_MAP } from '@/redux/models/models.mock';
import { MOCKED_OVERLAY_BIO_ENTITY_RENDER } from '@/redux/overlayBioEntity/overlayBioEntity.mock';
import { useOverlayFeatures } from './useOverlayFeatures';
/**
* mocks for useOverlayFeatures
* are taken from helpers functions that are used inside useOverlayFeatures
* point of the test is to test if all helper functions work correctly when combined together
*/
describe('useOverlayFeatures', () => {
const { Wrapper } = getReduxWrapperWithStore({
configuration: {
...CONFIGURATION_INITIAL_STORE_MOCKS,
},
overlayBioEntity: {
overlaysId: [11, 12],
data: {
// overlayId
11: {
// modelId
52: MOCKED_OVERLAY_BIO_ENTITY_RENDER,
53: MOCKED_OVERLAY_BIO_ENTITY_RENDER,
},
12: {
52: MOCKED_OVERLAY_BIO_ENTITY_RENDER,
53: MOCKED_OVERLAY_BIO_ENTITY_RENDER,
},
},
},
overlays: {
...OVERLAYS_PUBLIC_FETCHED_STATE_MOCK,
},
map: {
...mapStateWithCurrentlySelectedMainMapFixture,
},
models: {
...MODELS_DATA_MOCK_WITH_MAIN_MAP,
},
});
it('should return an array of features', () => {
const {
result: { current: features },
} = renderHook(() => useOverlayFeatures(), {
wrapper: Wrapper,
});
expect(features).toHaveLength(6);
expect(features[0].getGeometry()?.getCoordinates()).toEqual([
[
[-13149141, 18867005],
[-13149141, 18881970],
[-13143529, 18881970],
[-13143529, 18867005],
[-13149141, 18867005],
],
]);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
expect(features[0].getStyle().getFill().getColor()).toBe('#FFFFFFcc');
});
});
import { useMemo } from 'react';
import { usePointToProjection } from '@/utils/map/usePointToProjection';
import type Feature from 'ol/Feature';
import type Polygon from 'ol/geom/Polygon';
import { ZERO } from '@/constants/common';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import {
getOverlayOrderSelector,
overlayBioEntitiesForCurrentModelSelector,
} from '@/redux/overlayBioEntity/overlayBioEntity.selector';
import { createOverlayGeometryFeature } from './createOverlayGeometryFeature';
import { getPolygonLatitudeCoordinates } from './getPolygonLatitudeCoordinates';
import { useGetOverlayColor } from './useGetOverlayColor';
export const useOverlayFeatures = (): Feature<Polygon>[] => {
const pointToProjection = usePointToProjection();
const { getOverlayBioEntityColorByAvailableProperties } = useGetOverlayColor();
const overlaysOrder = useAppSelector(getOverlayOrderSelector);
const bioEntities = useAppSelector(overlayBioEntitiesForCurrentModelSelector);
const features = useMemo(
() =>
bioEntities.map(entity => {
/**
* Depending on number of active overlays
* it's required to calculate xMin and xMax coordinates of the polygon
* so "entity" might be devided equality between active overlays
*/
const { xMin, xMax } = getPolygonLatitudeCoordinates({
width: entity.width,
nOverlays: overlaysOrder.length,
xMin: entity.x1,
overlayIndexBasedOnOrder:
overlaysOrder.find(({ id }) => id === entity.overlayId)?.index || ZERO,
});
return createOverlayGeometryFeature(
[
...pointToProjection({ x: xMin, y: entity.y1 }),
...pointToProjection({ x: xMax, y: entity.y2 }),
],
getOverlayBioEntityColorByAvailableProperties(entity),
);
}),
[overlaysOrder, bioEntities, pointToProjection, getOverlayBioEntityColorByAvailableProperties],
);
return features;
};
......@@ -5,6 +5,7 @@ export const CONFIGURATION_OPTIONS_TYPES_MOCK: string[] = [
'MAX_COLOR_VAL',
'SIMPLE_COLOR_VAL',
'NEUTRAL_COLOR_VAL',
'OVERLAY_OPACITY',
];
export const CONFIGURATION_OPTIONS_COLOURS_MOCK: ConfigurationOption[] = [
......@@ -44,4 +45,13 @@ export const CONFIGURATION_OPTIONS_COLOURS_MOCK: ConfigurationOption[] = [
value: 'FFFFFF',
group: 'Overlays',
},
{
idObject: 33,
type: 'OVERLAY_OPACITY',
valueType: 'DOUBLE',
commonName: 'Opacity used when drawing data overlays (value between 0.0-1.0)',
isServerSide: false,
value: '0.8',
group: 'Overlays',
},
];
......@@ -29,6 +29,7 @@ export const CONFIGURATION_INITIAL_STORE_MOCKS: ConfigurationState = {
[CONFIGURATION_OPTIONS_TYPES_MOCK[1]]: CONFIGURATION_OPTIONS_COLOURS_MOCK[1],
[CONFIGURATION_OPTIONS_TYPES_MOCK[2]]: CONFIGURATION_OPTIONS_COLOURS_MOCK[2],
[CONFIGURATION_OPTIONS_TYPES_MOCK[3]]: CONFIGURATION_OPTIONS_COLOURS_MOCK[3],
[CONFIGURATION_OPTIONS_TYPES_MOCK[4]]: CONFIGURATION_OPTIONS_COLOURS_MOCK[4],
},
loading: 'idle',
error: DEFAULT_ERROR,
......
......@@ -40,3 +40,20 @@ export const initialMapStateFixture: MapState = {
error: DEFAULT_ERROR,
openedMaps: openedMapsInitialValueFixture,
};
export const mapStateWithCurrentlySelectedMainMapFixture: MapState = {
data: {
...initialMapDataFixture,
modelId: 52,
size: {
width: 26779.25,
height: 13503,
tileSize: 256,
minZoom: 2,
maxZoom: 9,
},
},
loading: 'idle',
error: DEFAULT_ERROR,
openedMaps: openedMapsInitialValueFixture,
};
......@@ -6,3 +6,27 @@ export const MODELS_INITIAL_STATE_MOCK: ModelsState = {
loading: 'idle',
error: DEFAULT_ERROR,
};
export const MODELS_DATA_MOCK_WITH_MAIN_MAP: ModelsState = {
data: [
{
idObject: 52,
width: 26779.25,
height: 13503,
defaultCenterX: null,
defaultCenterY: null,
description: '',
name: 'Core PD map',
defaultZoomLevel: null,
tileSize: 256,
references: [],
authors: [],
creationDate: null,
modificationDates: [],
minZoom: 2,
maxZoom: 9,
},
],
loading: 'idle',
error: DEFAULT_ERROR,
};
import { OverlayBioEntityRender } from '@/types/OLrendering';
import { OverlaysBioEntityState } from './overlayBioEntity.types';
export const OVERLAY_BIO_ENTITY_INITIAL_STATE_MOCK: OverlaysBioEntityState = {
overlaysId: [],
data: [],
};
export const MOCKED_OVERLAY_BIO_ENTITY_RENDER: OverlayBioEntityRender[] = [
{
id: 1,
modelId: 52,
width: 30,
x1: 18412,
x2: 18492,
y1: 3128.653195488725,
y2: 3088.653195488725,
overlayId: 11,
height: 10,
value: 0,
color: null,
},
{
id: 2,
modelId: 52,
width: 30,
x1: 18412,
x2: 18492,
y1: 3128.653195488725,
y2: 3088.653195488725,
overlayId: 11,
height: 10,
value: -0.2137,
color: null,
},
{
id: 3,
modelId: 52,
width: 40,
x1: 18412,
x2: 18492,
y1: 3128.653195488725,
y2: 3088.653195488725,
overlayId: 11,
height: 10,
value: null,
color: { rgb: -65536, alpha: 0 },
},
];
import { getHexStringColorFromRGBIntWithAlpha } from './getHexStringColorFromRGBIntWithAlpha';
const OPACITY_80 = 0.8;
describe('getHexTricolorGradientColorWithAlpha ', () => {
it('should return the correct result with input with negative rgb integer', () => {
expect(getHexStringColorFromRGBIntWithAlpha({ rgbInt: -3342388, alpha: OPACITY_80 })).toEqual(
'#ccffcccc',
);
});
it('should return the correct result with input with positive rgb integer', () => {
expect(getHexStringColorFromRGBIntWithAlpha({ rgbInt: 57, alpha: OPACITY_80 })).toEqual(
'#000039cc',
);
});
});
import { convertDecimalToHexColor } from './convertDecimalToHex';
import { addAlphaToHexString } from './addAlphaToHexString';
type GetHexStringColorFromRGBIntWithAlphaProps = {
rgbInt: number;
alpha: number;
};
export const getHexStringColorFromRGBIntWithAlpha = ({
rgbInt,
alpha,
}: GetHexStringColorFromRGBIntWithAlphaProps): string => {
const hexStringColor = convertDecimalToHexColor(rgbInt);
const hexStringColorWithAlpha = addAlphaToHexString(hexStringColor, alpha);
return hexStringColorWithAlpha;
};
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