> For the complete documentation index, see [llms.txt](https://docs.metacity.cc/metacity/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.metacity.cc/metacity/archives/bananagl.md).

# BananaGL

3D Web Visualization Library

{% hint style="danger" %}
The development of this library is now on hold; Metacity Tools are in the process of transitioning from BananaGL to [MetacityGL](https://github.com/MetacityTools/MetacityGL)
{% endhint %}

Visualize data preprocessed by `Metacity` with `BananaGL` - a client-side web visualization library. It is built with [three.js](https://threejs.org/) and `Typescript`.&#x20;

* 👩‍💻 Newest releases are always available [on our GitHub](https://github.com/MetacitySuite/BananaGL/releases)
* 📦 Datasets can be obtained from our [DataAPI](https://api.metacity.cc/) service

### Quick Start Guide

See Releases on our GitHub.&#x20;

1. Download `bananagl.zip` and use its contents in your project.
2. Create `index.html` and start coding!

Minimal HTML starting template:

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>BananaGL Project</title>
</head>
<body>
    <div id="container">
        <canvas id="canvas">Your browser does not support canvas</canvas>
    </div>
    <script src="bananagl.js"></script>
    <script>
        window.onload = () => {
            const canvas = document.getElementById("canvas");

            const gl = new BananaGL.BananaGL({ 
                canvas: canvas,
                metacityWorker: "metacityWorker.js",
            });
    </script>
</body>
</html>
```

{% hint style="warning" %}
**Never** set any **CSS styles** (directly or in a separate style sheet) modifying the **width or height of the`<canvas>`** passed to BananaGL. Set the sizes on its **parent** instead.
{% endhint %}

`BananaGL` keeps the provided `<canvas>` at 100% width and height of its parents, and adds a small annotation to its bottom.&#x20;

## Initialization

First, you need to initialize the library:

```javascript
const gl = new BananaGL.BananaGL({ 
    canvas: canvas,
    metacityWorker: "metacityWorker.js",
});
```

The function accepts a parameter object with the following properties:

<table><thead><tr><th width="212">Parameter</th><th width="134" align="center">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>canvas</code><br></td><td align="center">✅</td><td><code>HTMLCanvasElement</code></td></tr><tr><td><code>metacityWorker</code></td><td align="center">✅</td><td><code>string</code>, a path to background Worker JS file which handles data loading</td></tr><tr><td><code>background</code></td><td align="center">-</td><td><code>number</code>, such as <code>0xffffff</code> defining the background color</td></tr><tr><td><code>far</code></td><td align="center">-</td><td><code>number</code>, camera far</td></tr><tr><td><code>maxDistance</code></td><td align="center">-</td><td><code>number</code>, limits how far can user zoom out</td></tr><tr><td><code>maxPolarAngle</code></td><td align="center">-</td><td><code>number</code>, limits how low can user rotate the camera, horizontal view corresponds to <code>Math.PI / 2</code> </td></tr><tr><td><code>minDistance</code></td><td align="center">-</td><td><code>number</code>, limits how close can user zoom in</td></tr><tr><td><code>minPolarAngle</code></td><td align="center">-</td><td><code>number</code>, limits how high can user rotate the camera, top view corresponds to <code>0</code>, but for numerical stability reasons it is advisable to use something like <code>0.001</code></td></tr><tr><td><code>near</code></td><td align="center">-</td><td><code>number</code>, camera near</td></tr><tr><td><code>offset</code></td><td align="center">-</td><td><code>number</code>, when the camera is initialized in <em>isometric</em> style of view, this is the camera offset from the camera target (focus point) </td></tr><tr><td><code>position</code></td><td align="center">-</td><td><code>number</code>, initial position of the camera, overwrites the coordinates passed in URL</td></tr><tr><td><code>target</code></td><td align="center">-</td><td><code>number</code>, initial target of the camera, overwrites the coordinates passed in URL</td></tr><tr><td><code>zoomSpeed</code></td><td align="center">-</td><td><code>number</code>, speed of zooming</td></tr><tr><td><code>lightIntensity</code></td><td align="center">-</td><td><code>number</code>, the total intensity of lights, backup for <code>ambientLightIntensity</code> and <code>directionalLightIntensity</code></td></tr><tr><td><code>ambientLightIntensity</code></td><td align="center">-</td><td><code>number</code>, the intensity of the ambient light</td></tr><tr><td><code>directionalLightIntensity</code></td><td align="center">-</td><td><code>number</code>, the intensity of directional top-down light</td></tr><tr><td><code>onClick</code></td><td align="center">-</td><td><code>(id: number, metadata: Object) => string</code>, attach a callback that will return a string to be placed on top of an object after a click; requires <code>pickable</code> to be set up to true on layers where the callback should be applied; <code>id</code> corresponds to the clicked object id, metadata is the clicked object metadata record (can also be modified here!)</td></tr><tr><td><code>invertCopyrightColor</code></td><td align="center">-</td><td><code>boolean</code>, if true, the copyright label colors get inverted</td></tr></tbody></table>

### How Camera works

The `Camera` has two significant attributes which influence the LOD loading, etc.:

* `position` - actual position of the camera in 3D space
* `target` - focus point of the camera that always lies in the `z = 0` plane

Despite its looks, `BananaGL` uses Perspective camera with `fov = 5`, and by default, all limits are set up for viewing data in the coordinate system `EPSG:5514`.

## Layer

You can add new geographic layers to you visualization following way:

```javascript
const gl = new BananaGL.BananaGL({...});

gl.loadLayer({
    api: "https://data.metacity.cc/terrain",
    baseColor: 0xffffff
});
```

The function accepts a parameter object with the following properties:

<table><thead><tr><th width="188">Parameter</th><th width="116" align="center">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>api</code></td><td align="center">✅</td><td><code>string</code>, path to dataset exported from <code>Metacity</code> (see <a href="/metacity/metacity.md#exporting-data">Exporting data</a>), some sets are available through our <a href="https://api.metacity.cc/">DataAPI</a>, or you can use self-hosted datasets if you have your own data</td></tr><tr><td><code>baseColor</code></td><td align="center">-</td><td><code>number</code>, default color used when no styles are applied</td></tr><tr><td><code>loadingColor</code></td><td align="center">-</td><td><code>number</code>, a color used in the loading animation</td></tr><tr><td><code>placeholderColor</code></td><td align="center">-</td><td><code>number</code>, a color used for a placeholder when the geometry is not loaded yet</td></tr><tr><td><code>placeholderOpacity</code></td><td align="center">-</td><td><code>number</code>, opacity of the placeholder geometry</td></tr><tr><td><code>loadRadius</code></td><td align="center">-</td><td><code>number</code>, radius around camera target (focus point) where all objects will be loaded</td></tr><tr><td><code>lodLimits</code></td><td align="center">-</td><td><code>number[]</code>, breakpoints of camera target-position distance to switch LODs</td></tr><tr><td><code>name</code></td><td align="center">-</td><td><code>string</code>, name of the layer</td></tr><tr><td><code>pickable</code></td><td align="center">-</td><td><code>boolean</code>, whether the layer objects should be pickable</td></tr><tr><td><code>pointInstance</code></td><td align="center">-</td><td><code>string</code>, path to GLTF model used for instances</td></tr><tr><td><code>styles</code></td><td align="center">-</td><td><code>Style[]</code>, array of styles applicable to layer</td></tr></tbody></table>

## Styles

The color of mesh models can be modified by providing styling rules to individual layers.

```javascript
const gl = new BananaGL.BananaGL({...});

gl.loadLayer({
    api: "https://data.metacity.cc/buildings",
    baseColor: 0xffffff,
    style: [
        gl.style.withAttributeEqualTo("utilization", "office").useColor(0x0000ff),
        gl.style.withAttributeEqualTo("height", 50).useColor(0xff0000)
    ]
});
```

In the example above, the object with attribute `utilization` set to value `office` will be blue, and all buildings with `height` equal to 50 will be red.

### Computed Properties

On loading, all mesh objects are automatically assigned the following attributes:

* `height` - `number`, height of the object
* `baseHeight` - `number`, minimal z-coordinate of all object vertices
* `bbox` -  `[number`\[`], number[]]`, object bounding box, `bbox[0]` is the minimal coordinate , `bbox[1]` is maximum

### Rule Resolution

If two or more rules apply to a single object, the last matched rule is applied.&#x20;

If no rule matches, the `baseColor` of the layer is applied.

### Rule Chaining

If you want to specify a rule where both of the rules must apply, you can chain them in a following way:

```javascript
const gl = new BananaGL.BananaGL({...});

gl.loadLayer({
    api: "https://data.metacity.cc/buildings",
    baseColor: 0xffffff,
    style: [
        gl.style.withAttributeEqualTo("utilization", "office")
                .withAttributeEqualTo("height", 50).useColor(0xffe135)
    ]
});
```

## Style Rules

Several styling rules are available:

### `.forAll`

Applies the same color to all objects, essentially the same as using `baseColor` (just more computationally expensive, use only for testing or if you really despise your users).

```javascript
gl.style.forAll().useColor(0xffe135)
```

### `.withAttributeEqualTo`

Applies color to all objects with a certain attribute equal to a provided value.

```javascript
gl.style.withAttributeEqualTo("usage", "restaurant").useColor(0xffe135)
```

### `.withAttributeRange`

Applies color to all objects with a certain attribute in provided range.&#x20;

```javascript
gl.style.withAttributeRange("height", 10, 20).useColor(0xffe135)
gl.style.withAttributeRange("height", 10, 20).useColor([0x000000, 0xffffff])
```

### `.withAttributeRangeExt`

Applies color to all objects with a certain attribute in provided range. If the value is outside of the range, the color is extrapolated (constant method).

Combined with `useColor` using a single color works as an indication of whether the attribute is present.

```javascript
gl.style.withAttributeRangeExt("height", 10, 20).useColor(0xffe135)
gl.style.withAttributeRangeExt("height", 10, 20).useColor([0x000000, 0xffffff])
```

### `.useColor`

Specifies which color or colormap should be used if this particular rule applies. You can specify a single color

```javascript
gl.style.withAttributeEqualTo("usage", "restaurant").useColor(0xffe135)
```

or a color palette. Palettes work well in combination with `AttributeRange` rules.

```javascript
gl.style.withAttributeRangeExt("height", 10, 20).useColor([0x000000, 0xffffff])
```

If you call `useColor` more than once on a single rule, the last provided value is used.

```javascript
gl.style.withAttributeEqualTo("usage", "restaurant")
        .useColor(0xffe135) //<- not used
        .useColor(0xffffff) //<- used
```
