You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OpenLayers中map_为undefined报错,hover无法设置Overlay位置

问题描述

报错信息:ERROR TypeError: Cannot read properties of null (reading 'map_')(第29行),但第23行可正常遍历地图图层获取hover要素列表。组件依赖GeneralMapService管理地图实例与Overlay,需求是实现鼠标hover时设置Overlay的位置。

相关代码

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

          // 报错位置
          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;
  }
}

修复建议

1. 修复回调函数的this指向问题

forEachFeatureAtPixel的回调使用普通function时,内部this会脱离组件上下文,导致this.map_为null。改用箭头函数绑定组件上下文:

this.map_.forEachFeatureAtPixel(e.pixel, (feature) => {
  if (feature) {
    const { as_tr_name, ass_track, id, descriptio, media } = feature.getProperties();
    console.log(this.map_.getView());
    // 修正Overlay访问路径
    this.generalMap.mapOverlay.setPosition(positionOfMouse);
  }
});

2. 修正Overlay的错误访问路径

原代码中this.map_.getView().mapOverlay是错误的,Overlay挂载在Map实例上,而非View。直接通过GeneralMapService的mapOverlay属性访问即可。

3. 调整DOM元素获取时机

构造函数执行时组件视图尚未初始化,document.getElementById('informationOverlay')会返回null,将这部分逻辑移到ngAfterViewInit钩子中:

ngAfterViewInit() {
  this.map_.setTarget("map");
  this.overlay = document.getElementById('informationOverlay');
  this.generalMap.mapOverlay.set("id", "informationOverlay");
  this.generalMap.mapOverlay.setElement(this.overlay);
}

4. 可选:添加空值防护

为避免潜在的空指针问题,可在关键位置添加条件判断:

if (feature && this.generalMap.mapOverlay) {
  // 执行业务逻辑
}

修改后的完整Component代码

import { Component, OnInit, AfterViewInit } 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, AfterViewInit {

  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, (feature) => {
        if (feature && this.generalMap.mapOverlay) {
          const { as_tr_name, ass_track, id, descriptio, media } = feature.getProperties();
          console.log(this.map_.getView());
          this.generalMap.mapOverlay.setPosition(positionOfMouse);
        }
      });
    });
  }

  ngOnInit(): void {
  }

  ngAfterViewInit(): void {
    this.map_.setTarget("map");
    this.overlay = document.getElementById('informationOverlay');
    if (this.overlay && this.generalMap.mapOverlay) {
      this.generalMap.mapOverlay.set("id", "informationOverlay");
      this.generalMap.mapOverlay.setElement(this.overlay);
    }
  }
}

内容的提问来源于stack exchange,提问作者Sarbraj Dulai

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 12:45:33