首页 > 解决方案 > 提高地图绘制和过滤的性能 - Angular 8,LeafletJS

问题描述

我有一张leafletjs 地图,上面有8,200 个圆形标记。我有 8 个垫子选择列表,旁边有值。基本思想是您选择一个值,然后根据选择过滤标记。

一切正常,问题是性能不是最好的;用于加载屏幕并进行过滤。

角度组件代码:

import { Component, OnInit, ViewChild, OnDestroy } from '@angular/core';
import { MatSelectionList, MatSelectionListChange } from '@angular/material';
import { SelectionModel } from '@angular/cdk/collections';
import { DataService } from 'src/app/services/data.service';

import stores from 'src/assets/stores.json';
import filterTestStores from 'src/assets/filter-stores.json';
import filterTestRegions from 'src/assets/filter-regions.json';
import filterTestCities from 'src/assets/filter-cities.json';
import filterTestStates from 'src/assets/filter-states.json';
import filterTestRaces from 'src/assets/filter-races.json';
import filterTestAreas from 'src/assets/filter-urban.json';
import filterTestSeasons from 'src/assets/filter-season.json';
import filterTestPlanograms from 'src/assets/filter-planogram.json';
import filterTestTimes from 'src/assets/filter-time.json';

declare var $: any;
declare let L;

@Component({
  selector: 'app-test-location',
  templateUrl: './test-location.component.html',
  styleUrls: ['./test-location.component.scss']
})
export class TestLocationComponent implements OnInit, OnDestroy {
  @ViewChild('regions', {static: false}) regions: MatSelectionList;
  @ViewChild('states', {static: false}) states: MatSelectionList;
  @ViewChild('cities', {static: false}) cities: MatSelectionList;
  @ViewChild('races', {static: false}) races: MatSelectionList;
  @ViewChild('areas', {static: false}) areas: MatSelectionList;
  @ViewChild('seasons', {static: false}) seasons: MatSelectionList;
  @ViewChild('planograms', {static: false}) planograms: MatSelectionList;
  @ViewChild('times', {static: false}) times: MatSelectionList;

  constructor(public _dataService: DataService) {

  }

  map: any;
  markers: any;
  defaultMarkerGroup: any;
  showDefault = true;
  testFilterMarkerGroup: any;
  myRenderer: any;

  regionsList: string[] = filterTestRegions
  statesList: string[] = filterTestStates
  cityList: string[] = filterTestCities;
  storeList: number[] = filterTestStores;
  racesList: string[] = filterTestRaces;
  areasList: string[] = filterTestAreas;
  seasonsList: string[] = filterTestSeasons;
  planogramsList: string[] = filterTestPlanograms;
  timesList: string[] = filterTestTimes;

  removeDuplicates(myArr, prop) {
    return myArr.filter((obj, pos, arr) => {
      return arr.map(mapObj => mapObj[prop]).indexOf(obj[prop]) === pos;
    });
  }

  ngOnDestroy() {
    this._dataService.setSelectedMapList("regionsList", this.regions.selectedOptions.selected);
    this._dataService.setSelectedMapList("statesList", this.states.selectedOptions.selected);
    this._dataService.setSelectedMapList("citiesList", this.cities.selectedOptions.selected);
    this._dataService.setSelectedMapList("racesList", this.races.selectedOptions.selected);
    this._dataService.setSelectedMapList("areasList", this.areas.selectedOptions.selected);
    this._dataService.setSelectedMapList("seasonsList", this.seasons.selectedOptions.selected);
    this._dataService.setSelectedMapList("planogramsList", this.planograms.selectedOptions.selected);
    this._dataService.setSelectedMapList("timesList", this.times.selectedOptions.selected);

    this._dataService.setSameLocationArea(true);

    if(this.showDefault) {
      this._dataService.setUniverseSize(this.defaultMarkerGroup.getLayers().length);
    } else {
      this._dataService.setUniverseSize(this.testFilterMarkerGroup.getLayers().length);
    }
  }

  ngOnInit() {
    // this.regionsList = this.removeDuplicates(stores, "MARKET UNIT");
    // this.filteredRegions = this.removeDuplicates(stores, "MARKET UNIT");
    // this.statesList = this.removeDuplicates(stores, "State Abbreviation");
    // this.cityList = this.removeDuplicates(stores, "CITY");
    // this.racesList = this.removeDuplicates(stores, "Race");
    // this.areasList = this.removeDuplicates(stores, "Urbanicity");
    // this.seasonsList = this.removeDuplicates(stores, "Seasonality");
    // this.planogramsList = this.removeDuplicates(stores, "Planogram");
    // this.timesList = this.removeDuplicates(stores, "Time Open");


    this.map = L.map('map').setView([37.8, -96], 4);

    this.myRenderer = L.canvas({ padding: 0.2 });

    L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}', {
      attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
      maxZoom: 18,
      id: 'mapbox.light',
      accessToken: 'pk.eyJ1IjoiY29sdHN0cmVldCIsImEiOiJjanFjZmZsODIwcDNoNDJud3gzMjI4OGF4In0.h9uLyByXir4aaTDxRxleiA'
    }).addTo(this.map);

    // setup a marker group
    this.defaultMarkerGroup = L.featureGroup().addTo(this.map);

    //Apply Markers
    for (var i = 0; i < stores.length; i++) {
      L.circleMarker([stores[i].LATITUDE, stores[i].LONGITUDE], {
        radius: 5,
        color: "green",
        renderer: this.myRenderer
      }).addTo(this.defaultMarkerGroup).bindPopup('marker ' + i);
    }


    let svc = this;
    $(document).ready(function() {
      // svc.regions.selectAll();
      // svc.states.selectAll();
      // svc.cities.selectAll();
      // svc.races.selectAll();
    });

    this.map.fitBounds(this.defaultMarkerGroup.getBounds());
  }

  clearStoreMarkers() {
    this.map.removeLayer(this.testFilterMarkerGroup);
    this.map.addLayer(this.defaultMarkerGroup);
    this.showDefault = true;

    this.regions.deselectAll();
    this.states.deselectAll();
    this.cities.deselectAll();
    this.races.deselectAll();
    this.areas.deselectAll();
    this.seasons.deselectAll();
    this.planograms.deselectAll();
    this.times.deselectAll();
  }

  filterMarkers() {
    this.map.removeLayer(this.defaultMarkerGroup);
    if (this.testFilterMarkerGroup) {
      this.map.removeLayer(this.testFilterMarkerGroup);
    }

    // setup a marker group
    this.testFilterMarkerGroup = L.featureGroup().addTo(this.map);

    if (this.regions.selectedOptions.selected.length === 0 &&
      this.states.selectedOptions.selected.length === 0 &&
      this.cities.selectedOptions.selected.length === 0 &&
      this.races.selectedOptions.selected.length === 0 &&
      this.areas.selectedOptions.selected.length === 0 &&
      this.seasons.selectedOptions.selected.length === 0 &&
      this.planograms.selectedOptions.selected.length === 0 &&
      this.times.selectedOptions.selected.length === 0) {

      this.map.addLayer(this.defaultMarkerGroup);
      this.showDefault = true;
      this.map.fitBounds(this.defaultMarkerGroup.getBounds());
    } else { //Apply Markers
      this.showDefault = false;
      for (var i = 0; i < stores.length; i++) {
        let store = stores[i];

        this.regions.selectedOptions.selected.forEach((selection: any, key: any) => {
          if (store["MARKET UNIT"].toLowerCase() === selection.value.toLowerCase()) {
            //Add Marker
            L.circleMarker([store.LATITUDE, store.LONGITUDE], {
              radius: 5,
              color: "green",
              renderer: this.myRenderer
            }).addTo(this.testFilterMarkerGroup).bindPopup('marker ' + i);
          }
        });

        this.states.selectedOptions.selected.forEach((selection: any, key: any) => {
          if (store["State Abbreviation"].toLowerCase() === selection.value.toLowerCase()) {
            //Add Marker
            L.circleMarker([store.LATITUDE, store.LONGITUDE], {
              radius: 5,
              color: "green",
              renderer: this.myRenderer
            }).addTo(this.testFilterMarkerGroup).bindPopup('marker ' + i);
          }
        });

        this.cities.selectedOptions.selected.forEach((selection: any, key: any) => {
          if (store["CITY"].toLowerCase() === selection.value.toLowerCase()) {
            //Add Marker
            L.circleMarker([store.LATITUDE, store.LONGITUDE], {
              radius: 5,
              color: "green",
              renderer: this.myRenderer
            }).addTo(this.testFilterMarkerGroup).bindPopup('marker ' + i);
          }
        });

        this.races.selectedOptions.selected.forEach((selection: any, key: any) => {
          if (store["Race"]["Ethnicity"].toLowerCase() === selection.value.toLowerCase()) {
            //Add Marker
            L.circleMarker([store.LATITUDE, store.LONGITUDE], {
              radius: 5,
              color: "green",
              renderer: this.myRenderer
            }).addTo(this.testFilterMarkerGroup).bindPopup('marker ' + i);
          }
        });

        this.areas.selectedOptions.selected.forEach((selection: any, key: any) => {
          if (store["Urbanicity"].toLowerCase() === selection.value.toLowerCase()) {
            //Add Marker
            L.circleMarker([store.LATITUDE, store.LONGITUDE], {
              radius: 5,
              color: "green",
              renderer: this.myRenderer
            }).addTo(this.testFilterMarkerGroup).bindPopup('marker ' + i);
          }
        });

        this.seasons.selectedOptions.selected.forEach((selection: any, key: any) => {
          if (store["Seasonality"].toLowerCase() === selection.value.toLowerCase()) {
            //Add Marker
            L.circleMarker([store.LATITUDE, store.LONGITUDE], {
              radius: 5,
              color: "green",
              renderer: this.myRenderer
            }).addTo(this.testFilterMarkerGroup).bindPopup('marker ' + i);
          }
        });

        this.planograms.selectedOptions.selected.forEach((selection: any, key: any) => {
          if (store["Planogram"].toLowerCase() === selection.value.toLowerCase()) {
            //Add Marker
            L.circleMarker([store.LATITUDE, store.LONGITUDE], {
              radius: 5,
              color: "green",
              renderer: this.myRenderer
            }).addTo(this.testFilterMarkerGroup).bindPopup('marker ' + i);
          }
        });

        this.times.selectedOptions.selected.forEach((selection: any, key: any) => {
          if (store["Time Open"].toLowerCase() === selection.value.toLowerCase()) {
            //Add Marker
            L.circleMarker([store.LATITUDE, store.LONGITUDE], {
              radius: 5,
              color: "green",
              renderer: this.myRenderer
            }).addTo(this.testFilterMarkerGroup).bindPopup('marker ' + i);
          }
        });
      }
      this.map.fitBounds(this.testFilterMarkerGroup.getBounds());
    }
  }

  selectAll(filter) {
    filter.selectAll();
    this.filterMarkers();
  }

  deselectAll(filter) {
    filter.deselectAll();
    this.filterMarkers();
  }

  onSelection(evt, property) {
    // this.filteredRegions = [];
    // if (evt.option.selectionList.selectedOptions.selected.length === 0) {
    //   this.filteredRegions.push(...this.regionsList);
    // } else {

    //   this.filteredRegions = stores.filter(function (region) {
    //     return region[property] === evt.option.value;
    //   });
    //   this.filteredRegions = this.removeDuplicates(this.filteredRegions, "MARKET UNIT");
    // }

    this.filterMarkers();
  }

}

这是HTML:

<mat-toolbar class="header">
  <mat-toolbar-row>
    <a mat-icon-button matTooltip="Back to Step 3" routerLink="/stepThree" routerLinkActive="active">
      <i class="far fa-caret-square-left fa-3x"></i>
    </a>
    <div class="header-img">
      <img src="/assets/images/Dollar_Tree_logo.png">
    </div>
    <img class="dv-logo" src="/assets/images/DV-Transparent.png">
    <!-- <span class="spacer"></span> -->
    <a mat-icon-button matTooltip="Review and Submit" routerLink="/review" routerLinkActive="active">
      <i class="far fa-caret-square-right fa-3x"></i>
    </a>
  </mat-toolbar-row>
</mat-toolbar>
<div class="col-12 p-0 step-header">
  <button mat-icon-button class="export-button" matTooltip="Export Selected Stores">
    <i class="fas fa-share"></i>
  </button>
  <div>Test & Control Universe Selection</div>
  <button mat-icon-button class="export-all-button" matTooltip="Export All Stores">
    <i class="fas fa-share-square"></i>
  </button>
</div>
<div class="row filter-bk">
  <div class="col-md-3 text-center store-filter-content">
    <button mat-raised-button class="mt-1" (click)="clearStoreMarkers()">Reset</button>
    <mat-card class="filter-section">
      <div>Region</div>
      <mat-divider></mat-divider>
      <mat-selection-list #regions (selectionChange)="onSelection($event, 'MARKET UNIT')">
        <mat-list-option *ngFor="let region of regionsList" [value]='region'>
          <span>{{region}}</span>
        </mat-list-option>
      </mat-selection-list>
    </mat-card>

    <mat-card class="filter-section">
      <div>State</div>
      <mat-divider></mat-divider>
      <mat-selection-list #states (selectionChange)="onSelection($event, 'State Abbreviation')">
        <mat-list-option *ngFor="let state of statesList" [value]='state'>
          {{state}}
        </mat-list-option>
      </mat-selection-list>
    </mat-card>

    <mat-card class="filter-section">
      <div>City</div>
      <mat-divider></mat-divider>
      <mat-selection-list #cities (selectionChange)="onSelection()">
        <mat-list-option *ngFor="let city of cityList" [value]='city'>
          {{city}}
        </mat-list-option>
      </mat-selection-list>
    </mat-card>

    <mat-card class="filter-section">
      <div>Race/Ethnicity</div>
      <mat-divider></mat-divider>
      <mat-selection-list #races (selectionChange)="onSelection()">
        <mat-list-option *ngFor="let race of racesList" [value]='race'>
          {{race}}
        </mat-list-option>
      </mat-selection-list>
    </mat-card>

    <mat-card class="filter-section">
      <div>Urbanicity</div>
      <mat-divider></mat-divider>
      <mat-selection-list #areas (selectionChange)="onSelection()">
        <mat-list-option *ngFor="let area of areasList" [value]='area'>
          {{area}}
        </mat-list-option>
      </mat-selection-list>
    </mat-card>

    <mat-card class="filter-section">
      <div>Seasonality</div>
      <mat-divider></mat-divider>
      <mat-selection-list #seasons (selectionChange)="onSelection()">
        <mat-list-option *ngFor="let season of seasonsList" [value]='season'>
          {{season}}
        </mat-list-option>
      </mat-selection-list>
    </mat-card>

    <mat-card class="filter-section">
      <div>Planogram</div>
      <mat-divider></mat-divider>
      <mat-selection-list #planograms (selectionChange)="onSelection($event, 'Planogram')">
        <mat-list-option *ngFor="let planogram of planogramsList" [value]='planogram'>
          {{planogram}}
        </mat-list-option>
      </mat-selection-list>
    </mat-card>

    <mat-card class="filter-section">
      <div>Store Open Duration</div>
      <mat-divider></mat-divider>
      <mat-selection-list #times (selectionChange)="onSelection()">
        <mat-list-option *ngFor="let time of timesList" [value]='time'>
          {{time}}
        </mat-list-option>
      </mat-selection-list>
    </mat-card>
  </div>
  <div class="col-md-9 p-0 m-0">
    <div id="map"></div>
  </div>
</div>

标签: angularfilterangular-materialleaflet

解决方案


我将开始研究https://angular.io/api/core/ChangeDetectionStrategyhttps://angular.io/api/core/ChangeDetectorRef

在第二个链接中有一个如何使用变化检测策略和变化检测参考的例子。试试看,让我们知道它是否有助于解决性能问题。


推荐阅读