Legend

Explain what the colours and symbols on your map mean, with category swatches, gradients and your own content.

Categories

#

ng-map-legend renders a card from sections. A categories section lists swatches (circle, square or line) with labels. It sits top-left by default.

import { Component } from '@angular/core';
import {
  MapComponent,
  MapLegendComponent,
  MapMarkerComponent,
  MarkerContentComponent,
  MarkerTooltipComponent,
  type MapLegendSection,
} from 'ng-mapcn';

@Component({
  selector: 'app-legend-categories-map',
  standalone: true,
  imports: [
    MapComponent,
    MapLegendComponent,
    MapMarkerComponent,
    MarkerContentComponent,
    MarkerTooltipComponent,
  ],
  template: `
    <div class="h-[420px]">
      <ng-map [center]="[2.3522, 48.8566]" [zoom]="11.5">
        @for (place of places; track place.name) {
          <ng-map-marker [longitude]="place.lng" [latitude]="place.lat">
            <ng-marker-content>
              <span
                class="block size-4 rounded-full border-2 border-white shadow"
                [style.background-color]="place.color"
              ></span>
            </ng-marker-content>
            <ng-marker-tooltip>{{ place.name }}</ng-marker-tooltip>
          </ng-map-marker>
        }
        <ng-map-legend title="Places" [sections]="sections" />
      </ng-map>
    </div>
  `,
})
export class LegendCategoriesMapComponent {
  // keep the sections array stable: define it once, not inline in the template
  readonly sections: MapLegendSection[] = [
    {
      type: 'categories',
      entries: [
        { color: '#ef4444', label: 'Museum' },
        { color: '#3b82f6', label: 'Park' },
        { color: '#22c55e', label: 'Market', shape: 'square' },
      ],
    },
  ];
  readonly places = [
    { name: 'Louvre', lng: 2.3376, lat: 48.8606, color: '#ef4444' },
    { name: 'Orsay', lng: 2.3266, lat: 48.86, color: '#ef4444' },
    { name: 'Luxembourg', lng: 2.3372, lat: 48.8462, color: '#3b82f6' },
    { name: 'Buttes-Chaumont', lng: 2.3828, lat: 48.8809, color: '#3b82f6' },
    { name: 'Bastille Market', lng: 2.3691, lat: 48.8533, color: '#22c55e' },
  ];
}

Gradient

#

A gradient section draws a colour ramp from its stops. The first and last stops are always labelled; middle stops are labelled when they fit. Use format to render the values.

import { Component } from '@angular/core';
import {
  MapComponent,
  MapGeoJSONComponent,
  MapLegendComponent,
  type MapLegendSection,
} from 'ng-mapcn';

const STOPS = [
  { color: '#fef3c7', value: 0 },
  { color: '#fcd34d', value: 25 },
  { color: '#f59e0b', value: 50 },
  { color: '#dc2626', value: 75 },
  { color: '#7f1d1d', value: 100 },
];

// visitors (in thousands) per country, keyed by the feature's NAME_LONG
const VISITORS: Record<string, number> = {
  'United States': 100,
  'United Kingdom': 95,
  India: 90,
  Germany: 80,
  Canada: 70,
  Brazil: 65,
  France: 55,
  China: 45,
  Japan: 35,
  Australia: 25,
  'South Africa': 20,
};

@Component({
  selector: 'app-legend-gradient-map',
  standalone: true,
  imports: [MapComponent, MapGeoJSONComponent, MapLegendComponent],
  template: `
    <div class="h-[420px]">
      <ng-map [blank]="true" [center]="[12, 28]" [zoom]="1.4">
        <ng-map-geojson [data]="worldUrl" [fillPaint]="fillPaint" />
        <ng-map-legend position="bottom-left" [sections]="sections" />
      </ng-map>
    </div>
  `,
})
export class LegendGradientMapComponent {
  readonly worldUrl =
    'https://cdn.jsdelivr.net/gh/nvkelso/natural-earth-vector@v5.1.2/geojson/ne_110m_admin_0_countries.geojson';

  // The legend only explains the colours; the map paints them. Both read the same STOPS.
  readonly fillPaint = {
    'fill-color': [
      'let',
      'visitors',
      ['match', ['get', 'NAME_LONG'], ...Object.entries(VISITORS).flat(), -1],
      [
        'case',
        ['<', ['var', 'visitors'], 0],
        '#d4d4d8', // no data
        ['interpolate', ['linear'], ['var', 'visitors'], ...STOPS.flatMap((s) => [s.value, s.color])],
      ],
    ] as never,
  };

  readonly sections: MapLegendSection[] = [
    {
      type: 'gradient',
      title: 'Visitors',
      stops: STOPS,
      format: (value) => value + 'k',
    },
  ];
}

Collapsible with custom content

#

Set collapsible to add a header button; bind [(collapsed)] to control it. Anything you project into the legend renders below its sections.

import { Component, signal } from '@angular/core';
import {
  MapComponent,
  MapLegendComponent,
  MapRouteComponent,
  type MapLegendSection,
} from 'ng-mapcn';

@Component({
  selector: 'app-legend-collapsible-map',
  standalone: true,
  imports: [MapComponent, MapLegendComponent, MapRouteComponent],
  template: `
    <div class="h-[420px]">
      <ng-map [center]="[-0.12, 51.5]" [zoom]="11">
        <ng-map-route [coordinates]="route" color="#4285F4" />
        <!-- projected content renders below the sections -->
        <ng-map-legend
          title="Route"
          [collapsible]="true"
          [(collapsed)]="collapsed"
          [sections]="sections"
        >
          <p class="mt-2 text-xs">Length: 12.4 km</p>
          <button type="button" class="mt-1 text-xs underline" (click)="note.set('Thanks, reported.')">
            Report a problem
          </button>
          @if (note()) {
            <p class="text-xs">{{ note() }}</p>
          }
        </ng-map-legend>
      </ng-map>
    </div>
  `,
})
export class LegendCollapsibleMapComponent {
  readonly collapsed = signal(false);
  readonly note = signal('');
  readonly sections: MapLegendSection[] = [
    { type: 'categories', entries: [{ color: '#4285F4', label: 'Cycle route', shape: 'line' }] },
  ];
  readonly route: [number, number][] = [
    [-0.19, 51.49],
    [-0.14, 51.51],
    [-0.09, 51.51],
    [-0.05, 51.52],
  ];
}