File

src/app/GN2CommonModule/map/map.component.ts

Metadata

providers NominatimService
selector pnx-map
styleUrls map.component.scss
templateUrl map.component.html

Constructor

constructor(http: any, config: any)

Methods

search
search(term: string)
Returns: void

Properties

config
config: any
PARAMS
PARAMS: any
import { Component, Input, OnInit, ViewChild, Injectable } from '@angular/core';
import { MapService } from './map.service';
import { Map, LatLngExpression, LatLngBounds } from 'leaflet';
import { HttpClient, HttpParams } from '@angular/common/http';
import * as L from 'leaflet';
import { CommonService } from '../service/common.service';

import 'leaflet-draw';
import 'leaflet.locatecontrol';
import { UntypedFormControl } from '@angular/forms';
import { Observable, of } from 'rxjs';
import {
  catchError,
  debounceTime,
  distinctUntilChanged,
  tap,
  switchMap,
  map,
} from 'rxjs/operators';
import { ConfigService } from '@geonature/services/config.service';

const NOMINATIM_URL = 'https://nominatim.openstreetmap.org/search';

@Injectable()
export class NominatimService {
  PARAMS = null;

  constructor(
    private http: HttpClient,
    public config: ConfigService
  ) {
    this.PARAMS = new HttpParams({
      fromObject: {
        format: 'json',
        limit: '10',
        polygon_geojson: '1',
        countrycodes: this.config.MAPCONFIG.OSM_RESTRICT_COUNTRY_CODES,
      },
    });
  }

  search(term: string) {
    if (term === '') {
      return of([]);
    }

    return this.http
      .get(NOMINATIM_URL, { params: this.PARAMS.set('q', term) })
      .pipe(map((res) => res));
  }
}

/**
 * Ce composant affiche une carte Leaflet ainsi qu'un outil de recherche de lieux dits et d'adresses (basé sur l'API OpenStreetMap).
 * @example
 * <pnx-map height="80vh" [center]="center" [zoom]="zoom" > </pnx-map>`
 */
@Component({
  selector: 'pnx-map',
  templateUrl: './map.component.html',
  styleUrls: ['./map.component.scss'],
  providers: [NominatimService],
})
export class MapComponent implements OnInit {
  /**
   *  coordonnées du centrage de la carte: [long,lat]
   */
  @Input() center: Array<number>;
  /** Niveaux de zoom à l'initialisation de la carte */
  @Input() zoom: number = null;
  /** Hauteur de la carte (obligatoire) */
  @Input() height: string;

  /*@Input() mapContainer: string = 'map';*/

  /** Activer la barre de recherche */
  @Input() searchBar: boolean = true;

  /**Ajouter un bouton de géolocalisation */
  @Input() geolocation: boolean = false;

  @ViewChild('mapDiv', { static: true }) mapContainer;
  searchLocation: string;
  public searching = false;
  public searchFailed = false;
  public locationControl = new UntypedFormControl();
  public map: Map;
  constructor(
    private mapService: MapService,
    private _commonService: CommonService,
    private _nominatim: NominatimService,
    public config: ConfigService
  ) {
    this.searchLocation = '';
    this.zoom = this.config.MAPCONFIG.ZOOM_LEVEL;
  }

  ngOnInit() {
    this.initialize();
  }

  search = (text$: Observable<string>) =>
    text$.pipe(
      debounceTime(300),
      distinctUntilChanged(),
      tap(() => (this.searching = true)),
      switchMap((term) =>
        this._nominatim.search(term).pipe(
          tap(() => (this.searchFailed = false)),
          catchError(() => {
            this._commonService.translateToaster('Warning', 'Map.LocationError');
            this.searchFailed = true;
            return of([]);
          })
        )
      ),
      tap(() => (this.searching = false))
    );

  onResultSelected(nomatimObject) {
    let bounds: LatLngBounds;
    if (nomatimObject.item?.geojson) {
      const geojson = L.geoJSON(nomatimObject.item.geojson);
      bounds = geojson.getBounds();
    } else {
      const boundingBox: number[] = nomatimObject.item.boundingbox;
      bounds = L.latLngBounds(
        L.latLng(boundingBox[0], boundingBox[2]),
        L.latLng(boundingBox[1], boundingBox[3])
      );
    }
    this.map.fitBounds(bounds);
  }

  initialize() {
    let center: LatLngExpression = L.latLng(
      this.config.MAPCONFIG.CENTER[0],
      this.config.MAPCONFIG.CENTER[1]
    );
    if (this.center !== undefined) {
      center = L.latLng(this.center[0], this.center[1]);
    }

    // --- Create MAP
    this.map = L.map(this.mapContainer.nativeElement, {
      zoomControl: false,
      center: center,
      zoom: this.zoom,
      preferCanvas: true,
    });
    (this.map as any)._onResize();

    // --- MAP CONTROLS
    // ZOOM CONTROL
    L.control.zoom({ position: 'topright' }).addTo(this.map);

    // SCALE
    L.control.scale().addTo(this.map);

    //  GEOLOCATION
    if (this.geolocation && this.config.MAPCONFIG.GEOLOCATION) {
      (L.control as any).locate().addTo(this.map);
    }

    // --- LAYERS CONTROL
    // Baselayers
    const baseControl = {};
    const BASEMAP = JSON.parse(JSON.stringify(this.config.MAPCONFIG.BASEMAP));
    BASEMAP.forEach((basemap, index) => {
      const formatedBasemap = this.formatBaseMapConfig(basemap);
      if (basemap.service === 'wms') {
        baseControl[formatedBasemap.name] = L.tileLayer.wms(
          formatedBasemap.url,
          formatedBasemap.options
        );
      } else {
        baseControl[formatedBasemap.name] = L.tileLayer(
          formatedBasemap.url,
          formatedBasemap.options
        );
      }
    });
    // --- Layers selection
    // overlays layers
    const overlaysLayers = this.mapService.createOverLayers(this.map);
    // create control layers
    this.mapService.layerControl = L.control.layers(baseControl, overlaysLayers, {
      sortLayers: false, // When false, layers will keep the order in which they were added to the control
      collapsed: true, //If true, the control will be collapsed into an icon and expanded on mouse hover
    });
    this.mapService.layerControl.addTo(this.map);

    this.mapService.setMap(this.map);

    // ADD DRAW CONTROL
    this.mapService.initializeLeafletDrawFeatureGroup();

    // GET EXTEND ON EACH ZOOM
    this.map.on('moveend', (e) => {
      // keep current extend only if current zoom != 0
      if (this.map.getZoom() !== 0) {
        this.mapService.currentExtend = {
          center: this.map.getCenter(),
          zoom: this.map.getZoom(),
        };
      }
    });

    // on L.controler.layers add over layer to map
    this.map.on('overlayadd', (overlay) => {
      // once - load JSON or WFS overlay data async if not already loaded
      this.mapService.loadOverlay(overlay);
    });

    // Store last selected basemap
    this.map.on('baselayerchange', (layer) => {
      localStorage.setItem('MapLayer', layer.name);
    });

    // Select and add the current tile layer
    const userMapLayer = localStorage.getItem('MapLayer');
    if (userMapLayer && baseControl[userMapLayer]) {
      this.map.addLayer(baseControl[userMapLayer]);
    } else {
      this.map.addLayer(baseControl[BASEMAP[0].name]);
    }

    setTimeout(() => {
      this.map.invalidateSize();
    }, 50);
  }

  /** Retrocompatibility hack to format map config to the expected format:
   *
   {
    name: string,
    url: string,
    service?: wms|wmts|null
    options?: {
        layer?: string,
        attribution?: string,
        format?: string
        [...]
    }
  }
   */
  formatBaseMapConfig(baseMap) {
    // eslint-disable-next-line guard-for-in
    for (let attr in baseMap) {
      if (attr === 'layer') {
        baseMap['url'] = baseMap[attr];
        delete baseMap['layer'];
      }
      if (!['url', 'layer', 'name', 'service', 'options'].includes(attr)) {
        if (!baseMap['options']) {
          baseMap['options'] = {};
        }
        baseMap['options'][attr] = baseMap[attr];
        delete baseMap[attr];
      }
    }
    return baseMap;
  }

  formatter(nominatim) {
    return nominatim.display_name;
  }
}

results matching ""

    No results matching ""