# Exporting Data

Guide to exporting IFC data in various formats.

## Quick Start: CDN Export (No Build Required)

Export IFC to GLB directly in the browser with zero setup:

```html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>IFC to GLB Export</title>
</head>
<body>
    <input type="file" id="file" accept=".ifc">
    <div id="status"></div>

    <script type="module">
        import { GeometryProcessor } from "https://cdn.jsdelivr.net/npm/@ifc-lite/geometry@1.2.1/+esm";
        import { GLTFExporter } from "https://cdn.jsdelivr.net/npm/@ifc-lite/export@1.2.1/+esm";
        import initWasm from "https://cdn.jsdelivr.net/npm/@ifc-lite/wasm@1.2.1/+esm";

        // Initialize WASM with explicit path for CDN
        const wasmUrl = "https://cdn.jsdelivr.net/npm/@ifc-lite/wasm@1.2.1/pkg/ifc-lite_bg.wasm";
        await initWasm({ module_or_path: wasmUrl });

        document.getElementById("file").addEventListener("change", async (e) => {
            const file = e.target.files[0];
            if (!file) return;

            try {
                const processor = new GeometryProcessor();
                await processor.init();

                const buffer = new Uint8Array(await file.arrayBuffer());
                const result = await processor.process(buffer);

                const exporter = new GLTFExporter(result);
                const glb = exporter.exportGLB();

                // Download the GLB file
                const blob = new Blob([glb], { type: "model/gltf-binary" });
                const url = URL.createObjectURL(blob);
                const a = document.createElement("a");
                a.href = url;
                a.download = file.name.replace(/\.ifc$/i, ".glb");
                a.click();
                URL.revokeObjectURL(url);

                document.getElementById("status").textContent = "Done!";
                processor.dispose();
            } catch (error) {
                document.getElementById("status").textContent = "Error: " + error.message;
            }
        });
    </script>
</body>
</html>
```

!!! note "HTTP Server Required"
    This file must be served from an HTTP server (not `file://`). Use `npx serve .` or `python -m http.server 8000`.

## Overview

IFClite supports multiple export formats, as well as GLB import for loading existing 3D assets:

```mermaid
flowchart LR
    subgraph Input["Input"]
        IFC["ParseResult"]
        GLBIn["GLB Import"]
    end

    subgraph Formats["Export Formats"]
        glTF["glTF/GLB"]
        IFCOut["IFC (roundtrip)"]
        Parquet["Apache Parquet"]
        Arrow["Apache Arrow"]
        JSON["JSON-LD"]
        CSV["CSV"]
    end

    subgraph Uses["Use Cases"]
        Viewer["3D Viewers"]
        Roundtrip["IFC Roundtrip"]
        Analytics["Data Analytics"]
        Linked["Linked Data"]
        Spreadsheet["Spreadsheets"]
    end

    GLBIn --> Viewer
    IFC --> glTF --> Viewer
    IFC --> IFCOut --> Roundtrip
    IFC --> Parquet --> Analytics
    IFC --> Arrow --> Analytics
    IFC --> JSON --> Linked
    IFC --> CSV --> Spreadsheet
```

## glTF Export

Export geometry for use in standard 3D viewers:

```typescript
import { GltfExporter } from '@ifc-lite/export';

const exporter = new GltfExporter();

// Export to glTF (JSON + binary)
const gltf = await exporter.export(parseResult, {
  format: 'gltf',
  embedImages: true,
  includeProperties: true
});

// Save files
await saveFile('model.gltf', gltf.json);
await saveFile('model.bin', gltf.binary);

// Export to GLB (single binary file)
const glb = await exporter.export(parseResult, {
  format: 'glb'
});
await saveFile('model.glb', glb);
```

### glTF Options

```typescript
interface GltfExportOptions {
  // Output format
  format: 'gltf' | 'glb';

  // Include IFC properties as extras
  includeProperties?: boolean;

  // Embed textures/images
  embedImages?: boolean;

  // Draco compression
  useDraco?: boolean;
  dracoOptions?: {
    quantization?: number;
    compressionLevel?: number;
  };

  // Filter entities
  entityFilter?: (entity: Entity) => boolean;

  // Coordinate system
  yUp?: boolean; // Convert from Z-up to Y-up
}
```

### glTF with Properties

```typescript
const gltf = await exporter.export(parseResult, {
  format: 'glb',
  includeProperties: true
});

// Properties are stored in node extras:
// {
//   "nodes": [{
//     "name": "Wall-001",
//     "extras": {
//       "expressId": 123,
//       "globalId": "2O2Fr$...",
//       "type": "IFCWALL",
//       "properties": {
//         "Pset_WallCommon": {
//           "IsExternal": true,
//           "FireRating": 60
//         }
//       }
//     }
//   }]
// }
```

## Parquet Export

Export to Apache Parquet for analytics with tools like DuckDB, Pandas, or Polars:

```typescript
import { ParquetExporter } from '@ifc-lite/export';

const exporter = new ParquetExporter();

// Export entities
const entitiesParquet = await exporter.exportEntities(parseResult);
await saveFile('entities.parquet', entitiesParquet);

// Export properties
const propsParquet = await exporter.exportProperties(parseResult);
await saveFile('properties.parquet', propsParquet);

// Export quantities
const quantsParquet = await exporter.exportQuantities(parseResult);
await saveFile('quantities.parquet', quantsParquet);

// Export all tables
const bundle = await exporter.exportAll(parseResult);
await saveFile('entities.parquet', bundle.entities);
await saveFile('properties.parquet', bundle.properties);
await saveFile('quantities.parquet', bundle.quantities);
await saveFile('relationships.parquet', bundle.relationships);
```

### Parquet Schema

```mermaid
erDiagram
    ENTITIES {
        int64 express_id PK
        string type
        string global_id
        string name
        string description
        boolean has_geometry
    }

    PROPERTIES {
        int64 entity_id FK
        string pset_name
        string prop_name
        string value
        string value_type
    }

    QUANTITIES {
        int64 entity_id FK
        string name
        float64 value
        string unit
    }

    RELATIONSHIPS {
        int64 from_id FK
        int64 to_id FK
        string rel_type
    }

    ENTITIES ||--o{ PROPERTIES : has
    ENTITIES ||--o{ QUANTITIES : has
    ENTITIES ||--o{ RELATIONSHIPS : from
    ENTITIES ||--o{ RELATIONSHIPS : to
```

### Using Parquet with Python

```python
import polars as pl

# Load exported data
entities = pl.read_parquet('entities.parquet')
properties = pl.read_parquet('properties.parquet')
quantities = pl.read_parquet('quantities.parquet')

# Analyze wall areas
wall_areas = (
    entities
    .filter(pl.col('type').str.contains('IFCWALL'))
    .join(quantities, left_on='express_id', right_on='entity_id')
    .filter(pl.col('name') == 'NetArea')
    .group_by('type')
    .agg([
        pl.count('express_id').alias('count'),
        pl.sum('value').alias('total_area'),
        pl.mean('value').alias('avg_area')
    ])
)
print(wall_areas)
```

## JSON-LD Export

Export as linked data for semantic web applications:

```typescript
import { JsonLdExporter } from '@ifc-lite/export';

const exporter = new JsonLdExporter();

const jsonld = await exporter.export(parseResult, {
  // Base URI for identifiers
  baseUri: 'https://example.com/project/',

  // Include geometry as GeoJSON
  includeGeometry: false,

  // IFC ontology namespace
  ontology: 'https://standards.buildingsmart.org/IFC/DEV/IFC4/ADD2/OWL'
});

await saveFile('model.jsonld', JSON.stringify(jsonld, null, 2));
```

### JSON-LD Structure

```json
{
  "@context": {
    "ifc": "https://standards.buildingsmart.org/IFC/DEV/IFC4/ADD2/OWL#",
    "schema": "https://schema.org/",
    "geo": "http://www.opengis.net/ont/geosparql#"
  },
  "@graph": [
    {
      "@id": "https://example.com/project/wall-123",
      "@type": "ifc:IfcWall",
      "ifc:globalId": "2O2Fr$t4X7Zf8NOew3FL9r",
      "ifc:name": "Wall-001",
      "ifc:hasPropertySet": [
        {
          "@type": "ifc:IfcPropertySet",
          "ifc:name": "Pset_WallCommon",
          "ifc:hasProperty": [
            {
              "@type": "ifc:IfcPropertySingleValue",
              "ifc:name": "IsExternal",
              "ifc:value": true
            }
          ]
        }
      ]
    }
  ]
}
```

## CSV Export

Export tabular data for spreadsheet applications:

```typescript
import { CsvExporter } from '@ifc-lite/export';

const exporter = new CsvExporter();

// Export entity list
const entitiesCsv = await exporter.exportEntities(parseResult, {
  columns: ['expressId', 'type', 'globalId', 'name']
});
await saveFile('entities.csv', entitiesCsv);

// Export properties (pivoted)
const propsCsv = await exporter.exportPropertiesPivot(parseResult, {
  psetName: 'Pset_WallCommon',
  entityTypes: ['IFCWALL', 'IFCWALLSTANDARDCASE']
});
await saveFile('wall_properties.csv', propsCsv);

// Export quantities
const quantsCsv = await exporter.exportQuantities(parseResult);
await saveFile('quantities.csv', quantsCsv);
```

### CSV Output Example

```csv
expressId,type,globalId,name,IsExternal,FireRating,LoadBearing
123,IFCWALL,2O2Fr$t4X7Zf8NOew3FL9r,Wall-001,true,60,true
456,IFCWALLSTANDARDCASE,3P3Gs$u5Y8Ag9PQfx4GM0s,Wall-002,false,30,false
```

## IFC Export

Export back to IFC format for roundtrip workflows and interoperability with other BIM tools:

```typescript
import { StepExporter } from '@ifc-lite/export';

const exporter = new StepExporter(dataStore, sourceBuffer);

// Full export
const result = exporter.export();
await saveFile('model.ifc', result.content);

// Visible-only export (exclude hidden entities)
const visibleResult = exporter.export({
  visibleOnly: true,
  hiddenEntityIds: hiddenSet,       // Set<number> of local expressIds
  isolatedEntityIds: isolatedSet,   // Set<number> | null
});
await saveFile('visible_only.ifc', visibleResult.content);
```

### Visible-Only Export

When `visibleOnly` is enabled, the exporter:

1. Always includes infrastructure (units, owner history) and spatial structure
2. Checks each product entity against `hiddenEntityIds` / `isolatedEntityIds`
3. Walks `#ID` references transitively to include all dependent geometry, properties, and materials
4. Collects `IfcStyledItem` entities via reverse reference pass (preserves colors/materials)
5. Propagates visibility to openings via `IfcRelVoidsElement` (hidden slab = hidden openings)

Supports all 202 `IfcProduct` subtypes from IFC4 and IFC4X3 schemas, including infrastructure types (bridges, roads, railways, marine facilities).

### Multi-Model Merged Export

Merge multiple IFC models into a single file:

```typescript
import { MergedExporter } from '@ifc-lite/export';

const exporter = new MergedExporter();
const result = await exporter.export([
  { dataStore: store1, source: buffer1, name: 'Architecture' },
  { dataStore: store2, source: buffer2, name: 'Structure' },
], {
  visibleOnly: true,
  hiddenEntityIds: hiddenSet,
  isolatedEntityIds: isolatedSet,
});
await saveFile('merged.ifc', result.content);
```

## GLB Import

Load existing GLB files for viewing alongside IFC models:

```typescript
import { GlbImporter } from '@ifc-lite/import';

const importer = new GlbImporter();
const glbBuffer = await fetch('model.glb').then(r => r.arrayBuffer());
const meshes = await importer.import(new Uint8Array(glbBuffer));

// Add imported meshes to the renderer
renderer.addMeshes(meshes);
```

## Arrow Export

Export to Apache Arrow for in-memory analytics and streaming pipelines:

```typescript
import { ArrowExporter } from '@ifc-lite/export';

const exporter = new ArrowExporter();
const arrowBuffer = await exporter.exportEntities(parseResult);

// Use with Arrow-compatible tools (DuckDB, DataFusion, etc.)
await saveFile('entities.arrow', arrowBuffer);
```

## Custom Export

Create custom export formats:

```typescript
import { ExportPipeline } from '@ifc-lite/export';

import { extractPropertiesOnDemand, extractQuantitiesOnDemand } from '@ifc-lite/parser';

// Define custom exporter
class CustomExporter {
  export(store: IfcDataStore, buffer: Uint8Array): CustomFormat {
    const output: CustomFormat = {
      metadata: {
        schema: store.schemaVersion,
        timestamp: new Date().toISOString()
      },
      elements: []
    };

    // Get all wall expressIds
    const wallIds = store.entityIndex.byType.get('IFCWALL') ?? [];

    for (const expressId of wallIds) {
      const entityRef = store.entityIndex.byId.get(expressId);
      if (entityRef) {
        output.elements.push({
          id: expressId,
          name: entityRef.name,
          properties: extractPropertiesOnDemand(store, expressId, buffer),
          quantities: extractQuantitiesOnDemand(store, expressId, buffer)
        });
      }
    }

    return output;
  }
}

// Use custom exporter
const exporter = new CustomExporter();
const custom = exporter.export(store, buffer);
```

## Filtered Export

Export only specific entities:

```typescript
import { GltfExporter } from '@ifc-lite/export';
import { IfcQuery } from '@ifc-lite/query';

// Filter entities with query
const query = new IfcQuery(parseResult);
const externalWalls = query
  .walls()
  .whereProperty('Pset_WallCommon', 'IsExternal', '=', true)
  .toArray();

// Export filtered set
const exporter = new GltfExporter();
const glb = await exporter.export(parseResult, {
  format: 'glb',
  entityFilter: (entity) =>
    externalWalls.some(w => w.expressId === entity.expressId)
});
```

## Export Pipeline

Chain multiple exports:

```mermaid
flowchart LR
    Parse["Parse IFC"]
    Filter["Filter Entities"]
    Transform["Transform Data"]

    subgraph Exports["Parallel Exports"]
        E1["glTF"]
        E2["Parquet"]
        E3["CSV"]
    end

    Parse --> Filter --> Transform --> Exports
```

```typescript
import { ExportPipeline } from '@ifc-lite/export';

const pipeline = new ExportPipeline(parseResult);

// Run multiple exports in parallel
const results = await pipeline.export([
  { format: 'glb', options: { useDraco: true } },
  { format: 'parquet', tables: ['entities', 'properties'] },
  { format: 'csv', columns: ['expressId', 'type', 'name'] }
]);

// Save all results
await saveFile('model.glb', results.glb);
await saveFile('entities.parquet', results.parquet.entities);
await saveFile('entities.csv', results.csv);
```

## Next Steps

- [Query Guide](querying.md) - Filter data before export
- [API Reference](../api/typescript.md) - Complete API docs
