简体   繁体   English

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

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

I am trying to add a legend to a map created by Asymmetrik/ngx-leaflet. 我正在尝试将图例添加到由Asymmetrik / ngx-leaflet创建的地图中。 The map is created by following the tutorial in https://github.com/Asymmetrik/ngx-leaflet . 通过遵循https://github.com/Asymmetrik/ngx-leaflet中的教程来创建地图。 There are two different layers and for each layer there shoud be a different legend. 有两个不同的层,对于每个层,应该有一个不同的图例。 The code is made by using angular CLI and leaflet. 该代码是通过使用角度CLI和小叶编写的。 There is a map component. 有一个地图组件。 The map.component.ts file is as follows: 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();
  }

}

The legend does not appear on the page. 图例未出现在页面上。 The console shows the follwoing error: cannot read property 'bottomright' of undefined as shown in the image below: 控制台显示以下错误:无法读取未定义的属性“ bottomright”,如下图所示:

在此处输入图片说明

The map is shown correctly but there is no legend. 该地图可以正确显示,但没有图例。 I appreciate if you tell me what is wrong with my code and why the legend is not showing. 如果您告诉我我的代码有什么问题以及为什么不显示图例,我将不胜感激。 Thank you for your attention. 感谢您的关注。

OK I find the answer myself based on the comment made. 好吧,我根据评论发表了自己的答案。 Legend can only be added to the map itself and not the layer. 图例只能添加到地图本身,不能添加到图层。 But the map is not available when you use the following code: 但是,当您使用以下代码时,该地图不可用:

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

In order to get the map made by leaflet itself, you need to bind it. 为了获得由传单本身制作的地图,您需要将其绑定。 This is done in template file: 这是在模板文件中完成的:

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

and then I defined onMapReady() function as follows: 然后我定义了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);
    });

}

The legend appears only after the map is ready. 图例仅在地图准备好后出现。 Map is the first thing that is being created Then the layers appear. 首先创建地图,然后显示图层。 Therefore, I called updateChart() in onMapReady() to have access to min and max values of each layer. 因此,我在onMapReady()中调用了updateChart()来访问每一层的最小值和最大值。

Still there is an issue which is another legend is added when the layer is changed. 仍然存在一个问题,即在更改图层时会添加另一个图例。 But it is not relevant to this question. 但这与这个问题无关。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM