Skip to content

Commit

Permalink
New Structural uncertainty layer in Intersection module and other…
Browse files Browse the repository at this point in the history
… fixes (equinor#656)

Co-authored-by: HansKallekleiv <[email protected]>
Co-authored-by: Hans Kallekleiv <[email protected]>
  • Loading branch information
3 people authored Jun 25, 2024
1 parent fdee465 commit d7b5437
Show file tree
Hide file tree
Showing 41 changed files with 1,647 additions and 697 deletions.
9 changes: 2 additions & 7 deletions backend_py/primary/primary/routers/well/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,11 @@
async def get_drilled_wellbore_headers(
# fmt:off
authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
case_uuid: str = Query(description="Sumo case uuid"),
field_identifier: str = Query(description="Sumo field identifier"),
# Should be field identifier
# fmt:on
) -> List[schemas.WellboreHeader]:
"""Get wellbore headers for all wells in the field"""

case_inspector = CaseInspector.from_case_uuid(authenticated_user.get_sumo_access_token(), case_uuid)
field_identifier = (await case_inspector.get_field_identifiers_async())[0]
well_access: Union[SmdaWellAccess, MockedSmdaWellAccess]
if field_identifier == "DROGON":
# Handle DROGON
Expand All @@ -51,13 +48,11 @@ async def get_drilled_wellbore_headers(
async def get_field_well_trajectories(
# fmt:off
authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
case_uuid: str = Query(description="Sumo case uuid"), # Should be field identifier?
field_identifier: str = Query(description="Sumo field identifier"),
unique_wellbore_identifiers:List[str] = Query(None, description="Optional subset of well names")
# fmt:on
) -> List[schemas.WellboreTrajectory]:
"""Get well trajectories for field"""
case_inspector = CaseInspector.from_case_uuid(authenticated_user.get_sumo_access_token(), case_uuid)
field_identifier = (await case_inspector.get_field_identifiers_async())[0]
well_access: Union[SmdaWellAccess, MockedSmdaWellAccess]
if field_identifier == "DROGON":
# Handle DROGON
Expand Down
12 changes: 6 additions & 6 deletions frontend/src/api/services/WellService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,18 @@ export class WellService {
/**
* Get Drilled Wellbore Headers
* Get wellbore headers for all wells in the field
* @param caseUuid Sumo case uuid
* @param fieldIdentifier Sumo field identifier
* @returns WellboreHeader Successful Response
* @throws ApiError
*/
public getDrilledWellboreHeaders(
caseUuid: string,
fieldIdentifier: string,
): CancelablePromise<Array<WellboreHeader>> {
return this.httpRequest.request({
method: 'GET',
url: '/well/drilled_wellbore_headers/',
query: {
'case_uuid': caseUuid,
'field_identifier': fieldIdentifier,
},
errors: {
422: `Validation Error`,
Expand All @@ -38,20 +38,20 @@ export class WellService {
/**
* Get Field Well Trajectories
* Get well trajectories for field
* @param caseUuid Sumo case uuid
* @param fieldIdentifier Sumo field identifier
* @param uniqueWellboreIdentifiers Optional subset of well names
* @returns WellboreTrajectory Successful Response
* @throws ApiError
*/
public getFieldWellTrajectories(
caseUuid: string,
fieldIdentifier: string,
uniqueWellboreIdentifiers?: Array<string>,
): CancelablePromise<Array<WellboreTrajectory>> {
return this.httpRequest.request({
method: 'GET',
url: '/well/field_well_trajectories/',
query: {
'case_uuid': caseUuid,
'field_identifier': fieldIdentifier,
'unique_wellbore_identifiers': uniqueWellboreIdentifiers,
},
errors: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,6 @@ function isPixiLayer(layer: Layer<unknown>): boolean {
}

export function EsvIntersection(props: EsvIntersectionProps): React.ReactNode {
console.debug("esv intersection render");
const { onReadout, onViewportChange } = props;

const [prevAxesOptions, setPrevAxesOptions] = React.useState<AxisOptions | undefined>(undefined);
Expand Down
46 changes: 18 additions & 28 deletions frontend/src/framework/components/EsvIntersection/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ export type LayerDataItem = {
intersectionItem: IntersectionItem;
};

export enum AdditionalInformationKey {
export enum AdditionalInformationType {
GLOBAL_POLYGON_INDEX = "polygon-index",
IJK = "ijk",
PROP_VALUE = "prop-value",
Expand All @@ -147,36 +147,26 @@ export enum AdditionalInformationKey {
R = "r",
G = "g",
B = "b",
LABEL = "label",
POI = "poi",
}

export type PropValue = {
name: string;
unit: string;
value: number;
export type LineStyle = {
color: string;
alpha?: number;
dashSegments?: number[];
};

export type SchematicInfo = {
label: string;
value: string | number;
}[];
export type AreaStyle = {
fillColor: string;
alpha?: number;
strokeStyle?: LineStyle;
};

export type AdditionalInformation = {
[AdditionalInformationKey.B]?: number;
[AdditionalInformationKey.G]?: number;
[AdditionalInformationKey.R]?: number;
[AdditionalInformationKey.X]?: number;
[AdditionalInformationKey.Y]?: number;
[AdditionalInformationKey.MEAN]?: number;
[AdditionalInformationKey.MIN]?: number;
[AdditionalInformationKey.MAX]?: number;
[AdditionalInformationKey.P10]?: number;
[AdditionalInformationKey.P90]?: number;
[AdditionalInformationKey.P50]?: number;
[AdditionalInformationKey.GLOBAL_POLYGON_INDEX]?: number;
[AdditionalInformationKey.IJK]?: [number, number, number];
[AdditionalInformationKey.PROP_VALUE]?: PropValue;
[AdditionalInformationKey.MD]?: number;
[AdditionalInformationKey.LABEL]?: string;
[AdditionalInformationKey.SCHEMATIC_INFO]?: SchematicInfo;
export type AdditionalInformationItem = {
type: AdditionalInformationType;
label: string;
value: number | string | [number, number] | [number, number, number] | boolean;
lineStyle?: LineStyle;
areaStyle?: AreaStyle;
unit?: string;
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,140 +2,126 @@ import React from "react";

import { Layer } from "@equinor/esv-intersection";

import { AdditionalInformationKey, PropValue, ReadoutItem, SchematicInfo } from "../types";
import { AdditionalInformationItem, AdditionalInformationType, ReadoutItem } from "../types";
import { getColorFromLayerData } from "../utils/intersectionConversion";
import { getAdditionalInformationFromReadoutItem, getLabelFromLayerData } from "../utils/readoutItemUtils";
import { getAdditionalInformationItemsFromReadoutItem, getLabelFromLayerData } from "../utils/readoutItemUtils";

export type ReadoutBoxProps = {
readoutItems: ReadoutItem[];
maxNumItems?: number;
makeLabelFromLayer?: (layer: Layer<any>) => string | null;
};

function additionalInformationItemToReadableString(
key: string,
value: unknown
): { label: string; value: string } | { label: string; value: string }[] | null {
if (key === AdditionalInformationKey.IJK) {
return {
label: "IJK",
value: `${(value as [number, number, number])[0].toFixed(0)}, ${(
value as [number, number, number]
)[1].toFixed(0)}, ${(value as [number, number, number])[2].toFixed(0)}`,
};
}
if (key === AdditionalInformationKey.PROP_VALUE) {
const propValue = value as PropValue;
return {
label: propValue.name,
value: `${propValue.value.toFixed(2)} ${propValue.unit}`,
};
}
if (key === AdditionalInformationKey.MD) {
return {
label: "MD",
value: `${(value as number).toFixed(2)} m`,
};
}
if (key === AdditionalInformationKey.MAX) {
return {
label: "Max",
value: `${(value as number).toFixed(2)}`,
};
}
if (key === AdditionalInformationKey.MIN) {
return {
label: "Min",
value: `${(value as number).toFixed(2)}`,
};
}
if (key === AdditionalInformationKey.P10) {
return {
label: "P10",
value: `${(value as number).toFixed(2)}`,
};
}
if (key === AdditionalInformationKey.P90) {
return {
label: "P90",
value: `${(value as number).toFixed(2)}`,
};
type InfoItem = {
adornment: React.ReactNode;
label: React.ReactNode;
value: string;
unit?: string;
};

function formatValue(value: number | string): string {
if (typeof value === "number") {
return (+value.toFixed(2)).toString();
}
if (key === AdditionalInformationKey.P50) {
return {
label: "P50",
value: `${(value as number).toFixed(2)}`,
};
return value.toString();
}

function makeAdornment(item: AdditionalInformationItem): React.ReactNode {
if (item.lineStyle) {
return (
<svg
style={{
height: 8,
width: 8,
}}
version="1.1"
xmlns="http://www.w3.org/2000/svg"
>
<line
x1="0"
y1="4"
x2="8"
y2="4"
style={{
stroke: item.lineStyle.color,
opacity: item.lineStyle.alpha,
strokeWidth: 1,
strokeDasharray: item.lineStyle.dashSegments?.join(","),
}}
/>
</svg>
);
}
if (key === AdditionalInformationKey.MEAN) {
return {
label: "Mean",
value: `${(value as number).toFixed(2)}`,
};

if (item.areaStyle) {
return (
<svg
style={{
height: 8,
width: 8,
}}
version="1.1"
xmlns="http://www.w3.org/2000/svg"
>
<rect
x="0"
y="0"
width="8"
height="8"
style={{
fill: item.areaStyle.fillColor,
opacity: item.areaStyle.alpha,
stroke: item.areaStyle.strokeStyle?.color,
strokeWidth: 1,
strokeDasharray: item.areaStyle.strokeStyle?.dashSegments?.join(","),
}}
/>
</svg>
);
}
if (key === AdditionalInformationKey.SCHEMATIC_INFO) {
const schematicInfo = value as SchematicInfo;
const info: { label: string; value: string }[] = [];

for (const el of schematicInfo) {
let val = el.value;
if (typeof val === "number") {
val = val.toFixed(2);
}
info.push({
label: el.label,
value: val as string,
});

return null;
}

function convertAdditionalInformationItemToInfoItem(item: AdditionalInformationItem): InfoItem {
let formattedValue: string = "";
if (item.value instanceof Array) {
if (item.value.length === 3) {
formattedValue = item.value.map((el) => formatValue(el)).join(", ");
} else {
formattedValue = item.value.map((el) => formatValue(el)).join(" - ");
}
return info;
}
if (key === AdditionalInformationKey.X) {
return {
label: "X",
value: `${(value as number).toFixed(2)} m`,
};
}
if (key === AdditionalInformationKey.Y) {
return {
label: "Y",
value: `${(value as number).toFixed(2)} m`,
};
if (typeof item.value === "number" || typeof item.value === "string") {
formattedValue = formatValue(item.value);
}
return null;
}

function makeAdditionalInformation(item: ReadoutItem): { label: string; value: string }[] {
const additionalInformation = getAdditionalInformationFromReadoutItem(item);
return Object.entries(additionalInformation)
.map(([key, value]) => {
return additionalInformationItemToReadableString(key, value);
})
.filter((el): el is { label: string; value: string } => el !== null);
return {
label: item.label,
value: formattedValue,
unit: item.unit,
adornment: makeAdornment(item),
};
}

function convertAdditionalInformationToHtml(items: { label: string; value: string }[]): React.ReactNode {
function formatValue(value: number | string): string {
if (typeof value === "number") {
return value.toFixed(2);
}
return value.toString();
}
function makeAdditionalInformation(item: ReadoutItem): InfoItem[] {
const additionalInformation = getAdditionalInformationItemsFromReadoutItem(item);

return additionalInformation
.filter((el) => !(el.type === AdditionalInformationType.SCHEMATIC_INFO && el.label === "ID"))
.map((el) => {
return convertAdditionalInformationItemToInfoItem(el);
});
}

function convertAdditionalInformationToHtml(items: InfoItem[]): React.ReactNode {
return items.map((el, index) => {
if (Array.isArray(el)) {
return el.map((subEl, subIndex) => {
return (
<div className="table-row" key={subIndex}>
<div className="table-cell w-32">{subEl.label}:</div>
<div className="table-cell">{formatValue(subEl.value)}</div>
</div>
);
});
}
return (
<div className="table-row" key={index}>
<div className="table-cell w-32">{el.label}:</div>
<div className="table-cell">{formatValue(el.value)}</div>
<div className="table-cell w-4 align-middle">{el.adornment}</div>
<div className="table-cell w-32 align-middle">{el.label}:</div>
<div className="table-cell align-middle">{el.value}</div>
{el.unit && <div className="table-cell text-right align-middle">{el.unit}</div>}
</div>
);
});
Expand Down
Loading

0 comments on commit d7b5437

Please sign in to comment.