elbe/ui

Add GeoJSON data to the map with a declarative directive.

Usage

MglGeoJSONSource is a directive ([mglGeoJSONSource] or mgl-geojson-source). Bind the data directly or leave it out while an async value is pending.

<mgl-map [mapStyle]="style" [center]="center" [zoom]="10">  <mgl-geojson-source id="places" [data]="places" /></mgl-map>

A source only registers data. To render it, add an mgl-layer that references the source id:

<mgl-layer  id="places"  type="circle"  source="places"  [paint]="{    'circle-radius': 8,    'circle-color': '#009689'  }"/>

Features

Instead of a FeatureCollection, you can declare features as children. They are merged into the source and update it as they are added or removed.

<mgl-geojson-source id="places">  @for (place of places; track place.id) {    <mgl-feature      [id]="place.id"      [geometry]="place.geometry"      [properties]="place.properties"    />  }</mgl-geojson-source>

Popups

Layer events include the clicked feature. Store it on a signal and render an mgl-popup with [feature]. The feature input anchors the popup at the feature's geometry, and MglPopup recreates the underlying popup when the feature reference changes, so it also survives MapLibre closing the previous popup on click.

<mgl-layer  id="places"  type="circle"  source="places"  (layerClick)="showPopup($event)"/>@if (selected(); as place) {  <mgl-popup [feature]="place" class="flex flex-col gap-1 p-4">    <p class="text-sm font-medium">{{ place.properties?.['name'] }}</p>    <p class="text-muted-foreground text-xs">GeoJSON feature</p>  </mgl-popup>}
selected = signal<GeoJSON.Feature<GeoJSON.Point> | null>(null);showPopup(event: MapLayerMouseEvent) {  this.selected.set(event.features![0] as GeoJSON.Feature<GeoJSON.Point>);}

The cast is valid for point/circle layers, where the rendered feature geometry is a Point. For line or fill layers, prefer anchoring with lngLat.

Clustering

Point sources can be clustered in MapLibre. The clustering options are inputs on the source, so they can be bound like any other value.

<mgl-geojson-source  id="places"  [data]="places"  [cluster]="true"  [clusterRadius]="50"  [clusterMaxZoom]="14"/>

Cluster-aware helpers such as getClusterExpansionZoom, getClusterChildren and getClusterLeaves are available on the directive.

To render clustered points as HTML markers instead of WebGL symbols, add mgl-marker-cluster from @elbe-ui/map/marker-cluster, provide a template for points and clusters, and import the two template directives MglPointTemplate and MglClusterPointTemplate in the host component's imports.

<mgl-marker-cluster source="places">  <ng-template mglPoint>    <span class="bg-primary block size-2.5 rounded-full border border-white"></span>  </ng-template>  <ng-template mglClusterPoint let-feature>    <span class="bg-primary text-primary-foreground flex size-9 items-center justify-center rounded-full border-2 border-white text-xs font-semibold">      {{ feature.properties?.['point_count'] }}    </span>  </ng-template></mgl-marker-cluster>

Attribute form

The attribute selector is useful with ng-container, which does not render a DOM element:

<ng-container mglGeoJSONSource id="places" [data]="places" />