Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to capture a mouse over event on a <aol-feature> with OpenLayer #261

Open
pascal101 opened this issue Sep 9, 2020 · 3 comments
Open

Comments

@pascal101
Copy link

Is there a way to capture mouse over event on a aol-feature on a OSM map ? I have a device list (points) which are dysplayed on the map and my goal is to be able to show a pop-up once the mouse is over a device (aol-feature).
I'm beginner on OpenLayer librairie and on OSM map.

First I tried to follow this explaination with onClick event : #84 But it does not work for me.

I'm using Angular 7 with : bootstrap: 4.1.3, ngx-bootstrap: 5.6.1, ngx-openlayers : 0.8.22, ol: 6.4.3.

Here is my template code :

<aol-map [logo]="true" [width]="width" [height]="height" (onClick)="onClick($event)">
    <aol-interaction-default></aol-interaction-default>
    <aol-view [zoom]="zoom">
        <aol-coordinate [x]="longitude" [y]="latitude" [srid]="'EPSG:4326'"></aol-coordinate>
    </aol-view>
    <aol-layer-tile [opacity]="opacity">
        <aol-source-osm></aol-source-osm>
    </aol-layer-tile>
    <aol-layer-vector [opacity]="opacity">
        <aol-source-vector>    
            <aol-feature *ngFor="let device of devices; let i = index" (click)="setPopupContent(device)" [id]="i">                  
                <aol-geometry-point>
                    <aol-coordinate [x]="device.gpsLongitude" [y]="device.gpsLatitude" [srid]="'EPSG:4326'">
                    </aol-coordinate>
                </aol-geometry-point>
                <aol-style>
                    <aol-style-circle [radius]="10">
                        <aol-style-stroke [color]="'black'" [width]="width" ></aol-style-stroke>
                        <aol-style-fill [color]="'green'"></aol-style-fill>
                    </aol-style-circle>
                </aol-style>
            </aol-feature>
            <aol-overlay #popup *ngIf="popupContent.shown"> 
                   <aol-coordinate [x]="popupContent.gpsLongitude" [y]="popupContent.gpsLatitude" [srid]="'EPSG:4326'">      
                   </aol-coordinate>
                   <aol-content>
                        <div><p>hi</p></div>
                   </aol-content>
            </aol-overlay>
        </aol-source-vector>
    </aol-layer-vector>

Here is the typescript code :

import { Component, OnInit, OnDestroy, Input, Output, EventEmitter } from '@angular/core'
import { GeoLocationService } from 'src/app/common/services/geo-location.service'
import { marker } from './marker.image'
import * as olProj from 'ol/proj'
import { View } from 'ol'
import { HttpClient } from '@angular/common/http'
import { Device } from '../../common/model/device'
import { TooltipModule } from 'ngx-bootstrap/tooltip';
import { MapBrowserEvent, Feature } from 'ol';

@Component({
  selector: 'app-osm-view',
  templateUrl: './osm-view.component.html',
  styleUrls: ['./osm-view.component.css'],
  providers: [HttpClient, GeoLocationService]
})
export class OsmViewComponent implements OnInit {

    public features: Device[];
    public popupContent: { shown: boolean; feature: Device};

    constructor(private httpClient: HttpClient, 
            private geoLocationService: GeoLocationService,
            private reportManagementService: ReportManagementService
            ) {
        this.features = [];
        this.popupContent = {
          shown: false,
          feature: undefined
        };
        
    }
    
    public onClick(event: MapBrowserEvent) {
        console.log("onClick : this.event : ", event);
        console.log("onClick : this.devices : ", this.devices);
        event.map.forEachFeatureAtPixel(event.pixel, (feature: Feature) => {
          let featureId: number;
          featureId = feature.deviceId;
          console.log("onClick : feature.getId() : ", feature.deviceId);
          console.log("onClick : this.features[featureId] : ", this.features[featureId]);
          this.setPopupContent(this.features[featureId]);
        });
      }
    
      public setPopupContent(feature: Device) {
        this.popupContent.shown = true;
        this.popupContent.feature = feature;
        console.log("In setPopupContent method : this.popupContent.shown : ", this.popupContent.shown);
        console.log("In setPopupContent method : this.popupContent.feature : ", this.popupContent.feature);
      }

You can see in the browser console the result of my console.log trace

onClick : this.event :  Object { type: "click", target: {…}, map: {…}, frameState: {…}, originalEvent: pointerdown, pixel: (2) […], coordinate: (2) […], dragging: false, b: {…} }
onClick : this.devices :  Array [ {…}, {…} ]
onClick : feature.getId() :  undefined
onClick : this.features[featureId] :  undefined
In setPopupContent method : this.popupContent.shown :  true
In setPopupContent method : this.popupContent.feature :  undefined

So when I click on a device, the event is captured by no popup is displayed.
What is the problem ?

How to modify the code if I would like to do this on mouse over event ?
I tried this (mouseover)="mouseover($event)" but it does not work.

Please help. Thank you in advance...

@wflemingnz-retired
Copy link

Based on the code you have given, you aren't setting popupContent.gpsLongitude and popupContent.gpsLatitude so the overlay will not be positioned hence you won't see the popup.

Regarding mouseover, you can do it by handling the map pointermove event and calling map.forEachFeatureAtPixel(..) once to get the feature.

@pascal101
Copy link
Author

Thank you very much for your answer.

I made some changes in my code like you can see below. Now a popup appears on click event on a DEVICE. But it is always at the bottom left of the screen, and quite far from the clicked point, as you can see on the image below.

Popup_out_of_map

I don't understand why.

First, I updated my Angular framework to the 10 version and librairies.
I also updated librairies like ol, bootstrap, ngx-openlayers
In my package.json :

 "dependencies": {
    "@angular/animations": "^10.1.2",
    "@angular/common": "^10.1.2",
    "@angular/compiler": "^10.1.2",
    "@angular/core": "^10.1.2",
    "@angular/forms": "^10.1.2",
    "@angular/localize": "^10.1.2",
    "@angular/platform-browser": "^10.1.2",
    "@angular/platform-browser-dynamic": "^10.1.2",
    "@angular/platform-server": "^10.1.2",
    "@angular/router": "^10.1.2",
    "@ng-bootstrap/ng-bootstrap": "^7.0.0",
    "@popperjs/core": "^2.5.1",
    "angular-font-awesome": "^3.1.2",
    "bootstrap": "^4.5.2",
    "core-js": "^3.6.5",
    "ngx-bootstrap": "^6.1.0",
    "ngx-openlayers": "^0.8.22",
    "ol": "^6.4.3",
    "popper.js": "^1.16.1",
    "rxjs": "^6.6.3",
  },

Here is my new template code :

<aol-map [logo]="true" [width]="width" [height]="height" (onClick)="onClick($event)">
	<aol-interaction-default></aol-interaction-default>
	<aol-view [zoom]="zoom">
		<aol-coordinate [x]="longitude" [y]="latitude" [srid]="'EPSG:4326'"></aol-coordinate>
	</aol-view>
	<aol-layer-tile [opacity]="opacity">
		<aol-source-osm></aol-source-osm>
	</aol-layer-tile>
	<aol-layer-vector [opacity]="opacity">
		<aol-source-vector>

			<aol-feature *ngFor="let device of devices" (click)="setPopupContent(device) [id]="device.deviceId">					
				<aol-geometry-point>
					<aol-coordinate [x]="device.gpsLongitude" [y]="device.gpsLatitude" [srid]="'EPSG:4326'">
				  	</aol-coordinate>
				</aol-geometry-point>
				<aol-style>
					<aol-style-circle [radius]="10">
						<aol-style-stroke [color]="'black'" [width]="width" ></aol-style-stroke>
						<aol-style-fill [color]="'green'"></aol-style-fill>						
					</aol-style-circle>
				</aol-style>
			</aol-feature>
			
			<aol-overlay *ngIf="popupContent.shown"> 
				   <aol-coordinate [x]="popupContent.feature.gpsLongitude" [y]="popupContent.feature.gpsLatitude" [srid]="'EPSG:4326'">      
				   </aol-coordinate>
	               <aol-content>
	                   <div id="popup" class="ol-popup">
					      <a href="#" id="popup-closer" class="ol-popup-closer"></a>
					      <div id="popup-content">DEVICE : gpsLongitude {{ popupContent.feature.gpsLongitude }} / gpsLatitude {{ popupContent.feature.gpsLatitude }}</div>
					    </div>
	               </aol-content>
			</aol-overlay>
		</aol-source-vector>
	</aol-layer-vector>

Here is the new typescript code :

import { Component, OnInit, OnDestroy, Input, Output, EventEmitter } from '@angular/core'
import { GeoLocationService } from 'src/app/common/services/geo-location.service'
import { marker } from './marker.image'
import * as olProj from 'ol/proj'
import { HttpClient } from '@angular/common/http'
import { Subscription } from 'rxjs'
import { Device } from '../../common/model/device'
import { ReportManagementService } from 'src/app/common/services/report-management.service'
import { MapBrowserEvent, Feature } from 'ol';
import { Map } from 'ol/Map';
import { View } from 'ol/View';
import { TileLayer } from 'ol/layer/Tile';
import { XYZ } from 'ol/source/XYZ';
import { NgbdTooltipBasicModule } from 'src/app/shared/tooltip/tooltip.module'
import { TooltipModule } from 'ngx-bootstrap/tooltip';
import { Overlay, OverlayPositioning} from 'ol/Overlay';
import { toLonLat } from 'ol/proj';
import { toStringHDMS } from 'ol/coordinate';

@Component({
  selector: 'app-osm-view',
  templateUrl: './osm-view.component.html',
  styleUrls: ['./osm-view.component.css'],
  providers: [HttpClient, GeoLocationService]
})
export class OsmViewComponent implements OnInit {
    
    public features: Device[];
    public popupContent: { shown: boolean; feature: Device};
        
    constructor(private httpClient: HttpClient, 
            private geoLocationService: GeoLocationService,
            private reportManagementService: ReportManagementService
            ) {
        this.features = [];
        this.popupContent = {
          shown: false,
          feature: undefined
        };
    }
    
    public onClick(event: MapBrowserEvent) {
        event.map.forEachFeatureAtPixel(event.pixel, (feature: Feature) => {
            console.log("onClick : event.pixel : ", event.pixel);
            console.log("onClick : event : ", event);

          let featureId: number;
          featureId = feature.getId();
          console.log("onClick : feature : ", feature);
          console.log("onClick : feature.getId() : ", feature.getId());
		  
          let device =  this.devices.find(x => x.deviceId === featureId);
          console.log("onClick : device : ", device);

          this.setPopupContent(device);
        });
      }
    
      public setPopupContent(feature: Device) {
        this.popupContent.shown = true;
        this.popupContent.feature = feature;
        console.log("In setPopupContent method : this.popupContent.shown : ", this.popupContent.shown);
        console.log("In setPopupContent method : this.popupContent.feature : ", this.popupContent.feature);
      }

You can see in the browser console the result of my console.log trace

onClick : event.pixel :  Array [ 628, 844.4000244140625 ]
onClick : event :  Object { type: "click", target: {…}, map: {…}, frameState: {…}, originalEvent: pointerdown, pixel: (2) […], coordinate: (2) […], dragging: false, b: {…} }
onClick : feature :  Object { Ua: {}, sa: {}, ra: {…}, f: 5, cp: 30, O: {…}, c: 57, a: "geometry", g: {…}, j: b(), … }
onClick : feature.getId() :  57
onClick : device :  Object { deviceId: 57, gpsLatitude: 45.71499557422359, gpsLongitude: 4.806758317260741, reports: [], trackingSystem: null, sensorCharacteristic: (1) […], sensorType: (1) […], active: true, visible: false }
In setPopupContent method : this.popupContent.shown :  true
In setPopupContent method : this.popupContent.feature :  Object { deviceId: 57, gpsLatitude: 45.71499557422359, gpsLongitude: 4.806758317260741, reports: [], trackingSystem: null, sensorCharacteristic: (1) […], sensorType: (1) […], active: true, visible: false }

Here is the styles I got from https://openlayers.org/en/latest/examples/popup.html

 <style>
      .map {
        width: 100%;
        height:400px;
      }
      .ol-popup {
        position: absolute;
        background-color: white;
        box-shadow: 0 1px 4px rgba(0,0,0,0.2);
        padding: 15px;
        border-radius: 10px;
        border: 1px solid #cccccc;
        bottom: 12px;
        left: -50px;
        min-width: 280px;
      }
      .ol-popup:after, .ol-popup:before {
        top: 100%;
        border: solid transparent;
        content: " ";
        height: 0;
        width: 0;
        position: absolute;
        pointer-events: none;
      }
      .ol-popup:after {
        border-top-color: white;
        border-width: 10px;
        left: 48px;
        margin-left: -10px;
      }
      .ol-popup:before {
        border-top-color: #cccccc;
        border-width: 11px;
        left: 48px;
        margin-left: -11px;
      }
      .ol-popup-closer {
        text-decoration: none;
        position: absolute;
        top: 2px;
        right: 8px;
      }
      .ol-popup-closer:after {
        content: "✖";
      }
    </style>
  1. So the popup is displayed but outside the map. What is the problem, how to correct that ?

  2. As you advised me regarding mouseover, i tried the map pointermove event ( (mouseover)="mouseover($event)" ) and I had "TypeError: event.map is undefined" once my mouse is on the map.

  3. I tried a little test with mouseover event but OUTSIDE the map and it works perfectly :

	<p (mouseover)="mouseover()" placement="top" tooltip="Tooltip on top"><b>Mouseover</b> here</p>
	<h1>{{counter}} </h1>

Please help. Thank you in advance...

@wflemingnz-retired
Copy link

wflemingnz-retired commented Oct 8, 2020

  1. Hard to say without running example(ie: stackblitz) and further investigation. Probably either a projection issue or an html layout issue
  2. Use the openlayers pointermove event rather than mouseover:
    <aol-map (pointermove)="pointerMoved()">

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants