如何将图例添加到 Angular 和 ngx-leaflet 编写的传单地图

How to add legend to leaflet map written by Angular and ngx-leaflet

我正在尝试为 Asymmetrik/ngx-leaflet 创建的地图添加图例。该地图是按照 https://github.com/Asymmetrik/ngx-leaflet 中的教程创建的。有两个不同的层,每一层都应该有不同的图例。该代码是使用 angular CLI 和传单制作的。有一个地图组件。 map.component.ts文件如下:

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)
    };
  }

  private createGermanyLayer() {
    this.http.get('assets/bundeslaender.geojson')
      .subscribe((res: any) => {
        const deIndex = this.data.children.findIndex(e => e.name === 'de');
        const germanData = this.data.children[deIndex];
        res.features.forEach(feat => {
          const stateIndex = germanData.children.findIndex(e => {
            if (e.name) {
              return e.name.toLowerCase() === feat.properties.NAME_1.toLowerCase();
            }
          });
          feat.properties.SALES = germanData.children[stateIndex].label;
        });
        const max = Math.max.apply(Math, res.features.map(feat => feat.properties.SALES));
        const min = Math.min.apply(Math, res.features.map(feat => feat.properties.SALES));

        const geoJsonGermanyLayer = {
          id: 'geoJSON',
          name: 'Geo JSON Polygon',
          enabled: true,
          layer: geoJSON(
            res as any,
            {
              style: (d) => {
                const color = this.getColor(d.properties.SALES, max, min);
                return ({
                  color: color,
                  weight: 1
                });
              },
              onEachFeature: (feature, layer) => {
                layer.bindPopup('<h5>' + feature.properties.NAME_1 + '</h5><p>Revenue: ' + feature.properties.SALES.toFixed(2) + '</p>');
              }
            })
        };
        this.layersControl.baseLayers['Germany'] = geoJsonGermanyLayer.layer;

        // begining of legend

        const v1 = min;
        const v2 = min + Math.round((max - min ) / 2);
        const v3 = max;
        const legend = new (L.Control.extend({
          options: { position: 'bottomright' }
        }));
        // const legend = L.control({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 ], this.max, this.min) + '"> &nbsp; &nbsp;</i> &nbsp; &nbsp;'
            + labels[i] + '<br/>';
          }
          return div;
        };
        legend.addTo(geoJsonGermanyLayer);
        // end of legend


      });



  }

  private createEuropeLayer() {
    this.http.get('assets/europe.geojson')
      .subscribe((res: any) => {
        res.features.forEach(feat => {
          const countryIndex = this.data.children.findIndex(e => {
            if (e.name) {
              return e.name.toLowerCase() === feat.properties.FIPS.toLowerCase() || e.name.toLowerCase() === feat.properties.ISO2.toLowerCase();
            }
          });
          feat.properties.SALES = countryIndex !== -1 ? this.data.children[countryIndex].label : undefined;
        });
        const max = Math.max.apply(Math, res.features.filter(feat => feat.properties.SALES !== undefined).map(feat => feat.properties.SALES));
        const min = Math.min.apply(Math, res.features.filter(feat => feat.properties.SALES !== undefined).map(feat => feat.properties.SALES));
        const maxLog = Math.log(max);
        const minLog = Math.log(min);

        const geoJsonEuropeLayer = {
          id: 'geoJSON',
          name: 'Geo JSON Polygon',
          enabled: true,
          layer: geoJSON(
            res as any,
            {
              style: (d) => {
                const color = this.getColor(Math.log(d.properties.SALES), maxLog, minLog);
                return ({
                  color: color,
                  weight: 1
                });
              },
              onEachFeature: (feature, layer) => {
                const sales = feature.properties.SALES !== undefined ? feature.properties.SALES.toFixed(2) : 'No orders';
                layer.bindPopup('<h5>' + feature.properties.NAME + '</h5>' +
            '<p>Revenue: ' + sales + '</p>');
              }
            })
        };
        this.layersControl.baseLayers['Europe'] = geoJsonEuropeLayer.layer;
      });
  }

  private updateChart() {
    this.createGermanyLayer();
    this.createEuropeLayer();
  }

}

图例没有出现在页面上。控制台显示以下错误:无法读取 属性 'bottomright' of undefined 如下图所示:

地图显示正确,但没有图例。如果您能告诉我我的代码有什么问题以及为什么没有显示图例,我将不胜感激。感谢您的关注。

好的,我根据评论自己找到了答案。图例只能添加到地图本身,而不能添加到图层。但是当您使用以下代码时地图不可用:

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)
    };
}

要获取leaflet自己制作的地图,需要绑定。这是在模板文件中完成的:

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

然后我定义了 onMapReady() 函数如下:

onMapReady(map: Map) {
    this.updateChart();
    // Do stuff 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);
    });

}

图例仅在地图准备就绪后出现。地图是第一个被创建的东西然后图层出现。因此,我在 onMapReady() 中调用了 updateChart() 来访问每一层的最小值和最大值。

还有一个问题,就是换图层的时候又多了一个图例。但与本题无关