javascriptangularoopmapsopenlayers-6

Map is coming up as undefined - OpenLayers


I am experiencing a strange issue. I am receiving an error message that reads "ERROR TypeError: Cannot read properties of null (reading 'map_') on line 29". Even though I am able to loop the layers on the map to get the list of features on hover (line 23). Please see my code below and let me know if you have any advice on what I can do to fix this.

The way this is set up is, that my component is using a service that holds the information about the map and overlay. All I want to do is set the position of overlay on hover.

Component

import { Component, OnInit } from '@angular/core';
    import { Map } from 'ol';
    import { GeneralMapService } from 'src/app/services/general-map.service';
    
    @Component({
      selector: 'app-application-map',
      templateUrl: './application-map.component.html',
      styleUrls: ['./application-map.component.css']
    })
    
    export class WhereisMapComponent implements OnInit {

  map_: Map;
  overlay: any;

  constructor(private generalMap: GeneralMapService) {
    this.map_ = this.generalMap.map
    
    this.map_.on("pointermove", (e) => {
      
      const positionOfMouse = e.coordinate
      
      this.map_.forEachFeatureAtPixel(e.pixel, function (feature) {
        
        if (feature) {
          const { as_tr_name, ass_track, id, descriptio, media } = feature.getProperties();

          // I receive an error message here.
          console.log(this.map_.getView())
          this.map_.getView().mapOverlay.setPosition(positionOfMouse);
        }

      })

    })

    this.overlay = document.getElementById('informationOverlay')
    this.generalMap.mapOverlay.set("id", "informationOverlay")
    this.generalMap.mapOverlay.setElement(this.overlay)

  }

  ngOnInit(): void {
  }

  ngAfterViewInit() {
    this.map_.setTarget("map")  

  }

}

General Service

import { Injectable } from '@angular/core';
import { Map, Overlay, Tile, View } from 'ol';
import TileLayer from 'ol/layer/Tile';
import OSM from 'ol/source/OSM';
import { Zoom, ZoomSlider, FullScreen } from 'ol/control';
import XYZ from 'ol/source/XYZ';
import BingMaps from 'ol/source/BingMaps';
import LayerGroup from 'ol/layer/Group';

import { mapDefaultCenter, mapeDefaultZoom } from '../shared/maptilerkey';
import { CreateLayerService } from './create-layer.service';
import { Fill, Stroke, Style } from 'ol/style';
import CircleStyle from 'ol/style/Circle';
import { OverlayService } from './overlay.service';

@Injectable({
  providedIn: 'root'
})
export class GeneralMapService {

  map: Map;
  zoomSlider: ZoomSlider = new ZoomSlider({
    className: "zoom-slider-dashboard"
  })
  mapLayers: LayerGroup = new LayerGroup({
    layers: []
  })
  mapOverlay: Overlay;

  constructor(private createLayers: CreateLayerService, overlayService: OverlayService) {

    const topographicLayer = new TileLayer({
      source: new XYZ({
        url: "https://api.maptiler.com/maps/topographique/{z}/{x}/{y}@2x.png?key=WYPadsIvfisd0PUHFJ6K"
      }),
      visible: false
    })
    topographicLayer.set('name', 'topographic');

    const openStreetMap = new TileLayer({
      source: new OSM(),
      visible: true
    })
    openStreetMap.set('name', 'openstreetmap');

    const bingMaps = new TileLayer({
      preload: Infinity,

      source: new BingMaps({
        key: 'AngczjEvgHNjwD8lTQe3DJ6CoFxavJfGTFCxxaGbS3bIgUW5_qn4k_m510RR53fe',
        imagerySet: "Aerial",
        hidpi: true,
        maxZoom: 19
      }),
      visible: false
    })
    bingMaps.set('name', 'bingmap');

    this.map = new Map({
      view: new View({
        center: mapDefaultCenter,
        zoom: mapeDefaultZoom,
        minZoom: 5,
        projection: "EPSG:3857",
        extent: [15766342.542104144, -5590291.031415702, 16739198.402589425, -4713511.626202316]
      }),
      controls: []
    })

    const mapLayerGroup = new LayerGroup({
      layers: [topographicLayer, openStreetMap, bingMaps]
    })

    this.map.setLayerGroup(mapLayerGroup)
    this.map.addControl(this.zoomSlider)

    // vector layer
    const walkPointStyle = new Style({
      image: new CircleStyle({
        radius: 6,
        fill: new Fill({
          color: '#44c4a1',
        }),
        stroke: new Stroke({
          color: '#fff',
          width: 2,
        }),
      }),
    })

    const walkPoints = this.createLayers.createVectorLayer("/assets/data/walking-data.geojson", walkPointStyle)
    const builtMapLayers = [
      walkPoints
    ]

    this.map.getLayers().extend(builtMapLayers)

    // Overlay registration
    this.mapOverlay = overlayService.overlay
    this.map.addOverlay(this.mapOverlay)
    console.log("this.map", this.map.getOverlays())

  }

  ngAfterContentInit() {

  }

  returnMap() {
    return this.map;
  }

  setMap(updatedMap: Map) {
    this.map = updatedMap;
  }
}

Any sort of help would be appreciated!


Solution

  • It looks like you need to store context somewhere and then try to use that context forEachFeatureAtPixel method.

    Let me show an example:

    let that = this;
    
    this.map_.forEachFeatureAtPixel(e.pixel, function (feature) {
            
        if (feature) {
          // ... other code is omitted for the brevity
         
          console.log(that.map_.getView())
          that.map_.getView().mapOverlay.setPosition(positionOfMouse);
        }