A step-by-step guide to integrating a legend on a leaflet map using Angular and the ngx-leaflet plugin

I am attempting to integrate a legend into a map generated using Asymmetrik/ngx-leaflet. The tutorial I followed for creating the map can be found at https://github.com/Asymmetrik/ngx-leaflet. There are two distinct layers on the map, each requiring its own unique legend. The code has been developed using angular CLI and leaflet within a map component. Here is the content of the map.component.ts file:

import {Component, Input, OnChanges, OnInit} from '@angular/core';
import {circle, geoJSON, GeoJSONOptions, latLng, Layer, LeafletMouseEvent, polygon, tileLayer} from 'leaflet';
import * as L from 'leaflet';
import {SimpleResult} from '../../models/SimpleResult';
import {HttpClient} from '@angular/common/http';
import {IDrilldownResult} from '../../models/DrilldownResult';

@Component({
  selector: 'app-map-chart',
  templateUrl: './map-chart.component.html',
  styleUrls: ['./map-chart.component.css']
})
export class MapChartComponent implements OnInit, OnChanges {

  @Input() private data: IDrilldownResult;
  public options: any;
  public layersControl = {
    baseLayers: { }
  };

  private getColor(value, max, min) {
    const val = (value - min) / (max - min) ;
    const hue = (val * 120).toString(10);
    return ['hsl(', hue, ',100%,50%)'].join('');
  }

  constructor(
    private http: HttpClient
  ) { }

  ngOnInit() {
    this.createChart();
    /*if (this.data) {
      this.updateChart();
    }*/
  }

  ngOnChanges() {
    this.updateChart();
  }

  private createChart() {
    this.options = {
      layers: [
        tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 18, attribution: '...' }),
      ],
      zoom: 6,
      center: latLng(51.5167, 9.9167)
    };
  }

  // Rest of the code remains unchanged

}

The issue I'm facing is that the legend does not display on the web page. Upon inspecting the console, it shows an error message stating "cannot read property 'bottomright' of undefined" as illustrated in the image linked below:

https://i.sstatic.net/kdgMe.png

Although the map renders correctly, the legend fails to appear. I would greatly appreciate any insights on what might be causing this problem with my code and why the legend is not showing up. Thank you for your assistance.

Answer №1

After conducting some research and considering the comments provided, I have discovered that legends can only be added to the map itself, not the layer. However, there was an issue with accessing the map when using the code snippet below:

private createChart() {
    this.options = {
        layers: [
            tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 18, attribution: '...' }),
       ],
        zoom: 6,
        center: latLng(51.5167, 9.9167)
    };
}

To properly bind the map created by Leaflet itself, it needs to be done in the template file as shown below:

<div style="height: 700px;"
     leaflet
     [leafletOptions]="options"
     [leafletLayersControl]="layersControl"
     (leafletMapReady)="onMapReady($event)">
</div>

In order to ensure that the legend appears correctly, I implemented the onMapReady() function with the following code:

onMapReady(map: Map) {
    this.updateChart();
    // Implement actions with map
    map.on('baselayerchange', (eventLayer) => {
      const v1 = this.min;
      const v2 = this.min + Math.round((this.max - this.min ) / 2);
      const v3 = this.max;
      const legend = new (L.Control.extend({
        options: { position: 'bottomright' }
      }));

      const vm = this;
      legend.onAdd = function (map) {
        const div = L.DomUtil.create('div', 'legend');
        const labels = [
          'Sales greater than ' + v1,
          'Sales greater than ' + v2,
          'Sales equal or less than ' + v3
        ];
        const grades = [v1+ 1, v2+ 1, v3 ];
        div.innerHTML = '<div><b>Legend</b></div>';
        for (let i = 0; i < grades.length; i++) {
          div.innerHTML += '<i style="background:' + vm.getColor(grades[ i ], v3, v1) + '"> &nbsp; &nbsp;</i> &nbsp; &nbsp;'
        + labels[i] + '<br/>';
        }
        return div;
      };
      legend.addTo(map);
    });

}

The legend will now appear once the map is ready to ensure accurate presentation of data. It's important to note that the map is created first followed by the layers, which is why updateChart() is called within onMapReady() to access essential values.

Although a minor issue persists where an additional legend is displayed upon changing the layer, it does not directly relate to the main topic discussed.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Highlighting DOM elements that have recently been modified in Angular

Is there a simple way to change the style of an element when a bound property value changes, without storing a dedicated property in the component? The elements I want to highlight are input form elements: <tr field label="Lieu dépôt"> ...

Utilizing TypeScript's higher-order components to exclude a React property when implementing them

Trying to create a higher-order component in TypeScript that takes a React component class, wraps it, and returns a type with one of the declared properties omitted. Here's an attempt: interface MyProps { hello: string; world: number; } interfac ...

Issue with Primeng p-chips component: unable to dynamically add objects

Passing an array of objects value1 to the p-chips component is causing some issues. app.component.ts import { Component } from '@angular/core'; import {MenuItem} from 'primeng/api'; @Component({ selector: 'app-root', tem ...

Is it possible to verify type property names using a union type?

Given type UnionType = 'prop1' | 'prop2' | 'prop3'; type DerivedType = { prop1: string; prop2: number; prop3: boolean; }; Is there a method to define DerivedType in such a way that if I introduce a new member to UnionT ...

Ways to delete a class in typescript

There's a menu on my website with li tags containing an a element for navigation. Everything is working fine, but I'm facing an issue where I need to remove all elements with the class seleccionado and only add it to the clicked li. I tried using ...

The Azure function application's automatic reload feature does not function properly with the v4 model

Struggling to get Azure Function to recognize and incorporate changes in source code. I have set up a launch task to initiate the local server and run an npm script with tsc -w in watch mode. While I can see the modifications reflected in the /dist folder ...

Move the creation of the HTML string to an HTML template file within ngx bootstrap popover

I have incorporated ngx bootstrap in my project through this link To display dynamic HTML content in the popover body, I am using a combination of ngx-bootstrap directives and Angular template syntax as shown below: <span *ngFor="let item of items;"&g ...

Ways to increase the number of responses in an Express app after the initial response

In order to comply with the Facebook messenger API requirements, a 200 response must be sent immediately upon receiving the webhook request on my server, within 20 seconds. However, this process may take longer than the execution time of other middleware f ...

Issue with interface result: does not match type

Hey there! I've been working on creating an interface in TypeScript to achieve the desired return as shown below: { "field": "departament_name", "errors": [ "constraint": "O nome do departam ...

Angular 2 fails to identify any modifications

Within my template, the links are set to change based on the value of the 'userId' variable. <nav> <div class="nav-wrapper"> <a href="#" class="brand-logo"><img src="../../public/images/logo.png" alt="" /></a> ...

Issue encountered in cdk-virtual-scroll-viewport upon modifying the item array

Encountering an issue with a list of products displayed in a virtual scroll. The problem arises when the array of items is altered. For instance: Initially, there are 100 items in the scroll. Upon running the "reloadItems()" function to change the 100 i ...

Using Angular: How to set the index value from a dropdown to a local variable after a button is clicked

Can someone please provide guidance on how to assign the index value (i = index) to EmployeeIndex: any; after a button click event? Your suggestions are greatly appreciated. Here is my code: HTML <select class="form-control" [(ngModel)]="EmployeeNam ...

Is there a kind soul out there who can shed some light on the error that pops up when I try to execute "npm run

As I embark on creating my first angular app, I started by installing it using the command npm i -g @angular/cli. However, when I attempt to create a new app with npm run ng new app, an error pops up: npm ERR! path E:\ddii\package.json npm ...

The measure of the leaflet map's vertical dimension in a Shiny module application

I'm facing an issue while trying to incorporate my small app as a module within my larger app. Everything seems to be working fine except for the height of the leaflet map. In the standalone app, I had: ui <- fluidPage( tags$style(type = "te ...

Creating PropTypes from TypeScript

Currently in my React project, I am utilizing TypeScript along with PropTypes to ensure type checking and validation of props. It feels redundant to write types for both TypeScript and PropTypes, especially when defining components like ListingsList: inte ...

Authentication for file uploads in Angular 2 using Dropzone and passportjs

I am currently working on implementing authentication for an admin user using Express, Passport, and MySQL in a specific page. The authentication process works fine, but I am facing an issue with verifying whether the user is logged in while uploading file ...

Using Flickity API in Vue 3 with Typescript Integration

I have encountered an issue with implementing Flickity in my Vue 3 application. Everything works perfectly fine when using a static HTML carousel with fixed cells. However, I am facing difficulties when attempting to dynamically add cells during runtime us ...

Looking to showcase a nested JSON object within a Material Data Table

My goal is to present a nested JSON Object received from the backend as the column fields in my MatTableDataSource. This is the structure of my JSON Object: [{ "workstationId": 100, "assemblylineId": 100, "workstationDescription": "Testing1", ...

Error message in Typescript: "Property cannot be assigned to because it is immutable, despite not being designated as read-only"

Here is the code snippet I am working with: type SetupProps = { defaults: string; } export class Setup extends React.Component<SetupProps, SetupState> { constructor(props: any) { super(props); this.props.defaults = "Whatever ...

Has the GridToolbarExport functionality in Material UI stopped working since the latest version update to 5.0.0-alpha.37?

I have created a custom toolbar for my Data Grid with the following layout: return ( <GridToolbarContainer> <GridToolbarColumnsButton /> <GridToolbarFilterButton /> <GridToolbarDensitySelector /> <Gr ...