# wxtiles

This is a project for weather data visualization.
There are two main parts of the project:

1. [Splitter](https://github.com/metocean/wxtile-splitter) - a service that splits the datasets into tiles (PNG) and some metadata (JSON) served by a fileserver backend.
2. [@metoceanapi/wxtiles-leaflet](https://www.npmjs.com/package/@metoceanapi/wxtiles-leaflet) - a JS API providing work with metadata, dataset manager and an implementation of a Custom Leaflet Layer for visualizing the tiles using [Leaflet](https://leafletjs.com/).

## API

The API for Leaflet is designed for working with weather data tiles and allows the integration of custom layers for data visualization.

## DOCS

- [Leaflet](https://metoceanapi.github.io/wxtiles-leaflet/docs/)

## Examples

1. [SimpleDemo](https://metoceanapi.github.io/wxtiles-leaflet/examples/simpleDemo.html)
2. [Animated blur parameter](https://metoceanapi.github.io/wxtiles-leaflet/examples/seaMaskAndAnimatedBlur.html)
3. [Mouse Interaction](https://metoceanapi.github.io/wxtiles-leaflet/examples/interactive.html)

### Leaflet

```ts
import "leaflet/dist/leaflet.css";
import L from "leaflet";

async function start() {
  const wxapi = new WxAPI({
    dataServerURL: "https://tiles.metoceanapi.com/data/",
    requestInit: {
      headers: new Headers({'x-api-key': 'your_key_here'}),
    },
  });

  const wxdatasetManager = await wxapi.createDatasetManager("gfs.global");
  const variable = "air.temperature.at-2m";
  // 'wind.speed.eastward.at-10m' - Vector example

  const leafletOptions = {
    opacity: 1,
    attribution: "WxTiles",
  };

  // Create a layer
  const wxsource = wxdatasetManager.createSourceLayer(
    { variable },
    leafletOptions
  );

  const map = L.map("map", { center: [0, 0], zoom: 2, zoomControl: true });
  map.addLayer(wxsource);
  await new Promise((done) => wxsource.once("load", done)); // Highly recommended to await the first load
}
```

### Change the time step

```ts
await wxsource.setTimeStep(1); // 1 - index of the time step in the dataset
```

or

```ts
await wxsource.setTimeStep("2020-01-01T00:00:00Z"); // '2020-01-01T00:00:00Z' - time step in the dataset
```

or

```ts
await wxsource.setTimeStep(2345323454); //  time in seconds since 1970-01-01T00:00:00Z
```

or

```ts
await wxsource.setTimeStep(new Date("2020-01-01T00:00:00Z")); // Date object
```

### Update the Style

```ts
await wxsource.updateCurrentStyleObject({ units: "m/s", levels: undefined }); // set levels to undefined - to automatically calculate the levels from the dataset
```

### Preload the Time Steps

```ts
const prom = wxsource.preloadTime(10); // Load the time step 10 to the cache but do not render it
// Do stuff asynchronously
// ...
await prom; // Wait for the time step to finish loading
await wxsource.setTime(10); // Now set the time step to 10; it will render quickly from the cache
```

### Abort Loading

```ts
const abortController = new AbortController();
console.log("setTime(5)");
const prom = wxsource.setTime(5, abortController);
abortController.abort(); // Aborts the request
await prom; // Always await, even if aborted
console.log("aborted");
```

### Get the Current Time Step

```ts
const timeStep = wxsource.getTime();
```

### Read Lon/Lat Data

```ts
map.on("mousemove", (e) => {
  if (!wxsource) return;
  const pos = position(e);
  const tileInfo: WxTileInfo | undefined = wxsource.getLayerInfoAtLatLon(
    pos.wrap(),
    map
  );
  if (tileInfo) {
    console.log(tileInfo);
  }
});
```

### Animated Blur Effect

```ts
(async function step(n: number = 0) {
  await wxsource.updateCurrentStyleObject({
    isolineText: false,
    blurRadius: ~~(10 * Math.sin(n / 500) + 10),
  });
  requestAnimationFrame(step);
})();
```

### More Interactive – Additional Level and Red Transparency around the Level Based on Current Mouse Position

```ts
await wxsource.updateCurrentStyleObject({ levels: undefined }); // Reset levels if they exist in the style
const levels = wxsource.getCurrentStyleObjectCopy().levels || [];
const colMap: [number, string][] = levels.map((level) => [
  level,
  "#" + Math.random().toString(16).slice(2, 8) + "ff",
]);

let busy = false;
map.on("mousemove", async (e) => {
  if (!wxsource || busy) return;
  busy = true;
  const tileInfo: WxTileInfo | undefined = wxsource.getLayerInfoAtLatLon(
    position(e),
    map
  );
  if (tileInfo) {
    await wxsource.updateCurrentStyleObject({
      colorMap: [...colMap, [tileInfo.inStyleUnits[0], "#ff000000"]],
    });
    console.log(tileInfo);
  }
  busy = false;
});
```

### Legend Usage

The `WxCreateLegend` function is used to generate a color legend for weather map visualizations. This legend helps represent weather data with corresponding colors, making it easier to interpret the map.
Pass the `legendSize` and `style` to the `WxCreateLegend` function to generate the legend. The function returns a `WxLegend` object containing:

- `size`: The number of color steps.
- `colors`: A `Uint32Array` of color values.
- `ticks`: An array of `WxTick` objects representing data points and their corresponding colors.
- `units`: The units of the data.
- `showBelowMin` / `showAboveMax`: Whether values outside the range are shown.

#### Example: Temperature Legend

Here’s an example of creating a temperature legend:

```typescript
const style: WxColorStyleStrict = {
  colorMap: [
    [0, "#0000FF"], // blue at 0°C
    [15, "#00FF00"], // green at 15°C
    [30, "#FF0000"], // red at 30°C
  ],
  showBelowMin: true,
  showAboveMax: true,
  units: "°C",
  fill: "gradient",
};

const legend = WxCreateLegend(100, style);
console.log(legend);
```

This creates legend with:

- 100 color steps interpolating between blue, green, and red.
- Ticks at 0°C, 15°C, and 30°C.
- Support for values below 0°C and above 30°C.

### Color Style

The `WxColorStyleStrict` interface defines the configuration for styling visualizations. It includes properties for controlling colors, gradients, isolines, vectors, streamlines, and more. Below is a detailed explanation of each property.

#### Properties

##### Basic Properties

- **`name`**: The name of the style.
- **`parent`**: The name of a parent style to inherit properties from (optional).

##### Tile Filling

- **`fill`**: Defines how tiles are filled:
  - `"none"`: No fill, leaves the tile transparent.
  - `"solid"`: Solid fill, no gradient between levels.
  - `"gradient"`: Gradient fill between levels.

##### Isolines

- **`isolineColor`**: Defines the color of isolines:
  - `"none"`: Do not render isolines.
  - `"fill"`: Use the value of `fill` for each pixel.
  - `"inverted"`: Use the inverted value of `fill` for each pixel.
  - `#RGB`, `#RRGGBB`, or `#RRGGBBAA`: Use a specific web color.
- **`isolineText`**: If `true`, render text values on isolines.
- **`isolineTextSizeEm`**: Font size of isoline text values in `em` units.
- **`isolineTextBackground`**: If `true`, render a solid background behind isoline text values.

##### Vectors (Wind/Current Fields)

- **`vectorType`**: Defines how vectors are represented:
  - `"none"`: Do not render vectors.
  - `"arrows"`: Use arrows to represent winds/currents.
  - `"barbs"`: Use barbs to represent winds/currents.
- **`vectorColor`**: Defines the color of vectors:
  - `"none"`: Do not render vectors.
  - `"fill"`: Use the value of `fill` for each pixel.
  - `"inverted"`: Use the inverted value of `fill` for each pixel.
  - `#RGB`, `#RRGGBB`, or `#RRGGBBAA`: Use a specific web color.
- **`vectorFactor`**: Scaling factor for vector length.

##### Streamlines

- **`streamLineColor`**: Defines the color of streamlines:
  - `"none"`: Do not render streamlines.
  - `"fill"`: Use the value of `fill` for each pixel.
  - `"inverted"`: Use the inverted value of `fill` for each pixel.
  - `#RGB`, `#RRGGBB`, or `#RRGGBBAA`: Use a specific web color.
- **`streamLineSpeedFactor`**: Scaling factor for streamline length.
- **`streamLineGridStep`**: Step to seed streamlines. Smaller values create more streamlines.
- **`streamLineSteps`**: Number of steps in each streamline.
- **`streamLineStatic`**: If `true`, render streamlines as static lines. If `false`, render as animated lines.

##### Color Configuration

- **`showBelowMin`**: If `true`, fill values below the minimum range.
- **`showAboveMax`**: If `true`, fill values above the maximum range.
- **`colorScheme`**: Name of the color scheme from the default set.
- **`colors`**: Custom array of colors to use for interpolation (optional).
- **`colorMap`**: Array of `[level, color]` tuples to map specific data values to colors (optional).
- **`levels`**: Array of levels to build the legend. If not provided, 10 levels are calculated evenly based on the data.

##### Additional Styling

- **`blurRadius`**: Radius for the BOX filter (used for blurring).
- **`addDegrees`**: Additional rotation of wind or current vectors by a specified angle in degrees.
- **`units`**: Units of the style (e.g., `°C` for temperature).
- **`extraUnits`**: Additional units to extend the default set (optional).

##### Masking

- **`mask`**: Defines masking behavior:
  - `"none"`: No masking.
  - `"sea"`: Make sea areas transparent.
  - `"land"`: Make land areas transparent.

##### WebGL Configuration (Optional)

- **`gl`**: Configuration for WebGL rendering:
  - `animationSpeed`: Speed of animations.
  - `noiseTexturePow`: Power of noise texture.
  - `vectorFieldFactor`: Scaling factor for vector fields.
  - `animationIntensity`: Intensity of animations.
  - `wavesCount`: Number of waves to render.

##### Default styles

Default styles can be found [here](https://github.com/metoceanapi/wxtiles-common/blob/master/defaults/styles.ts)
