简体   繁体   中英

Present geoJSON data on Leaflet map in Angular

I'm trying to present some geoJSON data on a leaflet map. The geoJSON file is big (60mb) and the performance of the site is terrible when the data is being loaded. The geoJSON is about traffic density and more, so it contains about 230k segments...

What I've tried so far, is to implement leaflet.vectorgrid in angular by creating the leaflet.vectorgrid.d.ts as it mentioned here . This is the file:

import * as L from "leaflet";

declare module "leaflet" {
  namespace vectorGrid {
    export function slicer(data: any, options?: any): any;
  }
}

Although the performance is still bad.

This is my code so far:

import { Component, OnInit } from "@angular/core";
import {
  MapOptions,
  LatLng,
  TileLayer,
  Map,
  LeafletEvent,
  Circle,
  Polygon
} from "leaflet";

import * as L from "leaflet";
import { HttpClient } from "@angular/common/http";

@Component({
  selector: "map-visualization",
  templateUrl: "./map-visualization.component.html",
  styleUrls: ["./map-visualization.component.scss"]
})
export class MapVisualizationComponent implements OnInit {
  leafletOptions: MapOptions;
  layersControl: any;
  map: Map;

  constructor(private http: HttpClient) {}

  ngOnInit() {
    this.initializeMap();
  }

  /**
   * Initializes the map
   */
  initializeMap() {
    this.leafletOptions = {
      layers: [
        new TileLayer(
          "https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}",
          {
            maxZoom: 18
          }
        )
      ],
      zoom: 4,
      center: new LatLng(48.1323827, 4.172899)
    };
  }

  /**
   * Once the map is ready, it pans to the user's current location and loads the map.geojson
   * @param map Map instance
   */
  onMapReady(map: Map) {
    this.map = map;

    if (navigator) {
      navigator.geolocation.getCurrentPosition(position => {
        this.map.setView(
          new LatLng(position.coords.latitude, position.coords.longitude),
          12
        );
      });
    }

    this.http.get("assets/map.json").subscribe((json: any) => {
      L.geoJSON(json).addTo(this.map);
    });
  }

  /**
   * Return the current bound box
   * @param event Leaflet event
   */
  onMapMoveEnd(event: LeafletEvent) {
    console.log("Current BBox", this.map.getBounds().toBBoxString());
  }
}

Finally, the geoJSON is always going to be that big (60mb)... So, I was wondering if there is a way to filter the data that is fetched within the current bounding box.

Note the file is stored locally for now but later I will fetch it from the API.

The following approach should work natively with leaflet (without relying on another library):

this.map.getBounds() - returns LatLngBounds - the boundaries (coordinates of the 4 corners) of the map - the "bounding box" - this you are already doing.

LatLngBounds has a method called contains() , which returns true if the value of coords is within the bounding box: https://leafletjs.com/reference-1.5.0.html#latlngbounds-contains

You can create a method which would be called both from onMapReady() and onMapMoveEnd() that does something like this:

addItemsToMap(items: []): Layer[] {
  const tempLayer: Layer[] = [];

  items.forEach(item => {
    if (item.coordinates) {
      const itemCoordinates = latLng(
        item.coordinates.latitude,
        item.coordinates.longitude
      );
      /** Only add items to map if they are within map bounds */
      if (this.map.getBounds().contains(itemCoordinates)) {
        tempLayer.push(
          marker(itemCoordinates, {
            icon: icon(
              this.markers['red']
            ),
            title: item.description
          })
        );
      }
    }
  });

  return tempLayer;
}

In my experience, Leaflet could handle up to ~800 features comfortably. If user experience permits, you can also show a message to the user, asking them to zoom or pan, until the number of features is below the tolerable number.

Note: contains() accepts LatLng and LatLngBounds . To see if a polyline or a polygon overlaps or "is contained by" a bounding box, either:

The two methods will obviously return different results: the centroid/overlap should return more matches.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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