Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated IfcRelationsIndexer to allow indexations from WerbIfc #381

Merged
merged 1 commit into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/components/src/ifc/IfcRelationsIndexer/example.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ if (json) downloadJSON(json, "small-relations.json");
Now, in case you've loaded several models and want to get all the calculated relations, there is also a handy method to do it.
*/

const allRelationsJSON = indexer.serializeRelations();
const allRelationsJSON = indexer.serializeAllRelations();
downloadJSON(allRelationsJSON, "relations-index-all.json");

/* MD
Expand Down
92 changes: 81 additions & 11 deletions packages/components/src/ifc/IfcRelationsIndexer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { FragmentsGroup } from "@thatopen/fragments";
import { Disposable, Event, Component, Components } from "../../core";
import { FragmentManager } from "../../fragments/FragmentManager";
import { IfcPropertiesUtils } from "../Utils";
import { getRelationMap } from "./src/get-relation-map";

// TODO: Refactor to combine logic from process and processFromWebIfc

interface RelationMap {
[modelID: string]: Map<number, Map<number, number[]>>;
Expand Down Expand Up @@ -195,6 +198,59 @@ export class IfcRelationsIndexer extends Component implements Disposable {
return relationsMap;
}

/**
* Processes a given model from a WebIfc API to index its IFC entities relations based on predefined inverse attributes.
*
* @param ifcApi - The WebIfc API instance from which to retrieve the model's properties.
* @param modelID - The unique identifier of the model within the WebIfc API.
* @returns A promise that resolves to the relations map for the processed model.
* This map is a detailed representation of the relations indexed by entity expressIDs and relation types.
*/
async processFromWebIfc(ifcApi: WEBIFC.IfcAPI, modelID: number) {
const relationsMap = new Map<number, Map<number, number[]>>();

const properties: Record<string, Record<string, any>> = {};
const lines = ifcApi.GetAllLines(modelID);

for (let i = 0; i < lines.size(); i++) {
const line = lines.get(i);
const attrs = await ifcApi.properties.getItemProperties(modelID, line);
properties[line] = attrs;
}

for (const attribute of this.inverseAttributesToProcess) {
const rels = this.getAttributeRels(attribute);
for (const rel of rels) {
getRelationMap(properties, rel, (relatingID, relatedID) => {
const inverseAttributes = this._relToAttributesMap.get(rel);
if (!inverseAttributes) return;
const { forRelated: related, forRelating: relating } =
inverseAttributes;
if (relating && this.inverseAttributesToProcess.includes(relating)) {
const currentMap =
relationsMap.get(relatingID) ?? new Map<number, number[]>();
const index = this.inverseAttributes.indexOf(relating);
currentMap.set(index, relatedID);
relationsMap.set(relatingID, currentMap);
}
if (related && this.inverseAttributesToProcess.includes(related)) {
for (const id of relatedID) {
const currentMap =
relationsMap.get(id) ?? new Map<number, number[]>();
const index = this.inverseAttributes.indexOf(related);
const relations = currentMap.get(index) ?? [];
relations.push(relatingID);
currentMap.set(index, relations);
relationsMap.set(id, currentMap);
}
}
});
}
}

return relationsMap;
}

/**
* Retrieves the relations of a specific entity within a model based on the given relation name.
* This method searches the indexed relation maps for the specified model and entity,
Expand Down Expand Up @@ -222,21 +278,17 @@ export class IfcRelationsIndexer extends Component implements Disposable {
}

/**
* Serializes the relations of a specific model into a JSON string.
* This method iterates through the relations indexed for the given model,
* organizing them into a structured object where each key is an expressID of an entity,
* Serializes the relations of a given relation map into a JSON string.
* This method iterates through the relations in the given map, organizing them into a structured object where each key is an expressID of an entity,
* and its value is another object mapping relation indices to arrays of related entity expressIDs.
* The resulting object is then serialized into a JSON string.
*
* @param model The `FragmentsGroup` model whose relations are to be serialized.
* @returns A JSON string representing the serialized relations of the specified model.
* If the model has no indexed relations, `null` is returned.
* @param relationMap - The map of relations to be serialized. The map keys are expressIDs of entities, and the values are maps where each key is a relation type ID and its value is an array of expressIDs of entities related through that relation type.
* @returns A JSON string representing the serialized relations of the given relation map.
*/
serializeModelRelations(model: FragmentsGroup) {
const indexMap = this.relationMaps[model.uuid];
if (!indexMap) return null;
serializeRelations(relationMap: Map<number, Map<number, number[]>>) {
const object: Record<string, Record<string, number[]>> = {};
for (const [expressID, relations] of indexMap.entries()) {
for (const [expressID, relations] of relationMap.entries()) {
if (!object[expressID]) object[expressID] = {};
for (const [relationID, relationEntities] of relations.entries()) {
object[expressID][relationID] = relationEntities;
Expand All @@ -245,6 +297,24 @@ export class IfcRelationsIndexer extends Component implements Disposable {
return JSON.stringify(object);
}

/**
* Serializes the relations of a specific model into a JSON string.
* This method iterates through the relations indexed for the given model,
* organizing them into a structured object where each key is an expressID of an entity,
* and its value is another object mapping relation indices to arrays of related entity expressIDs.
* The resulting object is then serialized into a JSON string.
*
* @param model The `FragmentsGroup` model whose relations are to be serialized.
* @returns A JSON string representing the serialized relations of the specified model.
* If the model has no indexed relations, `null` is returned.
*/
serializeModelRelations(model: FragmentsGroup) {
const relationsMap = this.relationMaps[model.uuid];
if (!relationsMap) return null;
const json = this.serializeRelations(relationsMap);
return json;
}

/**
* Serializes all relations of every model processed by the indexer into a JSON string.
* This method iterates through each model's relations indexed in `relationMaps`, organizing them
Expand All @@ -255,7 +325,7 @@ export class IfcRelationsIndexer extends Component implements Disposable {
* @returns A JSON string representing the serialized relations of all models processed by the indexer.
* If no relations have been indexed, an empty object is returned as a JSON string.
*/
serializeRelations() {
serializeAllRelations() {
const jsonObject: Record<
string,
Record<string, Record<string, number[]>>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
export const getRelationMap = (
properties: Record<string, Record<string, any>>,
relationType: number,
onElementsFound?: (relatingID: number, relatedIDs: number[]) => void,
) => {
const defaultCallback = () => {};
const _onElementsFound = onElementsFound ?? defaultCallback;
const result: { [relatingID: number]: number[] } = {};
const ids = Object.keys(properties);
for (const expressID of ids) {
const prop = properties[expressID];

if (!prop) continue;

const isRelation = prop.type === relationType;
const relatingKey = Object.keys(prop).find((key) =>
key.startsWith("Relating"),
);

const relatedKey = Object.keys(prop).find((key) =>
key.startsWith("Related"),
);

if (!(isRelation && relatingKey && relatedKey)) continue;
const relating = properties[prop[relatingKey]?.value];
const related = prop[relatedKey];

if (!relating || !related) continue;

if (!(related && Array.isArray(related))) continue;
const elements = related.map((el: any) => el.value);

_onElementsFound(relating.expressID, elements);
result[relating.expressID] = elements;
}
return result;
};