Angular2 - How to setup a google.maps.OverlayView? (translate JS prototype into Typescript)

后端 未结 6 1918
逝去的感伤
逝去的感伤 2020-12-10 21:12

I\'d like to create a google map component that work like that : https://jsfiddle.net/gvvy5vxz/2/

It\'s based on this : https://developers.google.com/maps/documentat

相关标签:
6条回答
  • 2020-12-10 21:58

    Here is what worked for me (using ionic 2):

    1. Use these commands:

      npm install typings --global
      typings install dt~google.maps --global --save
      typings install google.maps --global
      
    2. Add "typings/*.d.ts" in your tsconfig.json:

      "include": [
        "src/**/*.ts",
        "typings/*.d.ts"
      ]
      
    3. Here is the TypeScript code:

      import { Component, ViewChild, ElementRef } from '@angular/core';
      import { NavController } from 'ionic-angular';
      // import typings
      import { } from '@types/googlemaps';
      /*
      /*  Component Map
      */
      @Component({
          selector: 'page-overlay',
          templateUrl: 'overlay.html'
      })
      export class OverlayPage {
          @ViewChild('map') mapElement: ElementRef;
          map: any;
          // weird syntax, I know, but it works
          constructor(public navCtrl: NavController) {
          }
          USGSOverlay = class extends google.maps.OverlayView {
              bounds_: any;
              image_: any;
              map_: any;
              div_: any;
              constructor(bounds, image, private map) {
                  super();
                  // Initialize all properties.
                  this.bounds_ = bounds;
                  this.image_ = image;
                  this.map_ = map;
                  // Define a property to hold the image's div. We'll
                  // actually create this div upon receipt of the onAdd()
                  // method so we'll leave it null for now.
                  this.div_ = null;
                  // Explicitly call setMap on this overlay.
                  this.setMap(map);
                  this.set
              }
              /**
               * onAdd is called when the map's panes are ready and the overlay has been
               * added to the map.
               */
              onAdd() {
                  const div = document.createElement('div');
                  div.style.borderStyle = 'none';
                  div.style.borderWidth = '0px';
                  div.style.position = 'absolute';
                  // Create the img element and attach it to the div.
                  const img = document.createElement('img');
                  img.src = this.image_;
                  img.style.width = '100%';
                  img.style.height = '100%';
                  img.style.position = 'absolute';
                  div.appendChild(img);
                  this.div_ = div;
                  // Add the element to the "overlayLayer" pane.
                  const panes = this.getPanes();
                  panes.overlayLayer.appendChild(div);
              };
              draw() {
                  // We use the south-west and north-east
                  // coordinates of the overlay to peg it to the correct position and size.
                  // To do this, we need to retrieve the projection from the overlay.
                  const overlayProjection = this.getProjection();
                  // Retrieve the south-west and north-east coordinates of this overlay
                  // in LatLngs and convert them to pixel coordinates.
                  // We'll use these coordinates to resize the div.
                  const sw = overlayProjection.fromLatLngToDivPixel(this.bounds_.getSouthWest());
                  const ne = overlayProjection.fromLatLngToDivPixel(this.bounds_.getNorthEast());
                  // Resize the image's div to fit the indicated dimensions.
                  const div = this.div_;
                  div.style.left = sw.x + 'px';
                  div.style.top = ne.y + 'px';
                  div.style.width = (ne.x - sw.x) + 'px';
                  div.style.height = (sw.y - ne.y) + 'px';
              };
              // The onRemove() method will be called automatically from the API if
              // we ever set the overlay's map property to 'null'.
              onRemove() {
                  this.div_.parentNode.removeChild(this.div_);
                  this.div_ = null;
              };
          };
          ngOnInit() {
              this.loadMap();
              var bounds = new google.maps.LatLngBounds(
                  new google.maps.LatLng(62.281819, -150.287132),
                  new google.maps.LatLng(62.400471, -150.005608));
              // The photograph is courtesy of the U.S. Geological Survey.
              var srcImage = 'https://developers.google.com/maps/documentation/' +
                  'javascript/examples/full/images/talkeetna.png';
              const overlay = new this.USGSOverlay(bounds, srcImage, this.map);
              //overlay.setMap(this.map);
          }
          ionViewDidLoad() {
              this.loadMap();
              var bounds = new google.maps.LatLngBounds(
                  new google.maps.LatLng(62.281819, -150.287132),
                  new google.maps.LatLng(62.400471, -150.005608));
              // The photograph is courtesy of the U.S. Geological Survey.
              var srcImage = 'https://developers.google.com/maps/documentation/' +
                  'javascript/examples/full/images/talkeetna.png';
              const overlay = new this.USGSOverlay(bounds, srcImage, this.map);
              //overlay.setMap(this.map);
          }
          loadMap() {        
              let latLng = new google.maps.LatLng(62.323907, -150.109291);
              var mapOptions = {
                  zoom :15,
                  center : latLng,
                  enableHighAccuracy: true,
                  timeout: 5000,
                  maximumAge: 0
              };
              this.map = new google.maps.Map(this.mapElement.nativeElement, mapOptions);
          }
      }
      
    0 讨论(0)
  • 2020-12-10 22:08

    Use this simple code to create a custom overlay.

        // this.userPositionDiv is defined as variable
        let overlayView = new google.maps.OverlayView();
        overlayView.draw = () => {
            if (!this.userPositionDiv) {
                this.userPositionDiv = document.createElement('div');
                this.userPositionDiv.className = 'marker';
                this.userPositionDiv.style.cssText = `width: 35px; height: 35px; text-align: center; line-height: 35px; position: absolute; cursor: pointer; border-radius: 50%; color: #fff; background: #000`;
                this.userPositionDiv.innerHTML = `<span class="number-id">Hello</span>`;
                const panes = overlayView.getPanes();
                panes.overlayImage.appendChild(this.userPositionDiv);
            }
            const point = this.overlayView.getProjection().fromLatLngToDivPixel(this.latLng);
            if (point) {
                this.userPositionDiv.style.left = (point.x - 10) + 'px';
                this.userPositionDiv.style.top = (point.y - 10) + 'px';
            }
        }
        overlayView.setMap(this.gmap);
    
    0 讨论(0)
  • 2020-12-10 22:09

    I was on the good way it works...

    import { Component } from '@angular/core';
    declare const google: any;
    
    class USGSOverlay extends google.maps.OverlayView{
    
      bounds_;
      image_;
      map_;
      div_;
    
      constructor(bounds, image, map){
        // Initialize all properties.
        this.bounds_ = bounds;
        this.image_ = image;
        this.map_ = map;
    
        // Define a property to hold the image's div. We'll
        // actually create this div upon receipt of the onAdd()
        // method so we'll leave it null for now.
        this.div_ = null;
    
        // Explicitly call setMap on this overlay.
        this.setMap(map);
      }
    
      /**
       * onAdd is called when the map's panes are ready and the overlay has been
       * added to the map.
       */
      onAdd(){
        const div = document.createElement('div');
        div.style.borderStyle = 'none';
        div.style.borderWidth = '0px';
        div.style.position = 'absolute';
    
        // Create the img element and attach it to the div.
        const img = document.createElement('img');
        img.src = this.image_;
        img.style.width = '100%';
        img.style.height = '100%';
        img.style.position = 'absolute';
        div.appendChild(img);
    
        this.div_ = div;
    
        // Add the element to the "overlayLayer" pane.
        const panes = this.getPanes();
        panes.overlayLayer.appendChild(div);
      };
    
      draw(){
    
        // We use the south-west and north-east
        // coordinates of the overlay to peg it to the correct position and size.
        // To do this, we need to retrieve the projection from the overlay.
        const overlayProjection = this.getProjection();
    
        // Retrieve the south-west and north-east coordinates of this overlay
        // in LatLngs and convert them to pixel coordinates.
        // We'll use these coordinates to resize the div.
        const sw = overlayProjection.fromLatLngToDivPixel(this.bounds_.getSouthWest());
        const ne = overlayProjection.fromLatLngToDivPixel(this.bounds_.getNorthEast());
    
        // Resize the image's div to fit the indicated dimensions.
        const div = this.div_;
        div.style.left = sw.x + 'px';
        div.style.top = ne.y + 'px';
        div.style.width = (ne.x - sw.x) + 'px';
        div.style.height = (sw.y - ne.y) + 'px';
      };
    
      // The onRemove() method will be called automatically from the API if
      // we ever set the overlay's map property to 'null'.
      onRemove(){
        this.div_.parentNode.removeChild(this.div_);
        this.div_ = null;
      };
    };
    
    
    
    /*
    /*  Component Map
    */
    @Component({
      selector: 'map',
      template: `
        <div id="map"></div>
      `,
      styles: [
        `#map{ width:100%; height:100%; position: absolute; width:100%; height:100%; top:0; left:0;}`
      ],
    })
    export class MapComponent {
    
      overlay;
    
      ngOnInit(){
        google.maps.event.addDomListener(window, 'load', this.initMap);
      }
    
      initMap() {
    
        const map = new google.maps.Map(document.getElementById('map'), {
          zoom: 11,
          center: {lat: 62.323907, lng: -150.109291},
          mapTypeId: google.maps.MapTypeId.SATELLITE
        });
    
        const bounds = new google.maps.LatLngBounds(
            new google.maps.LatLng(62.281819, -150.287132),
            new google.maps.LatLng(62.400471, -150.005608));
    
        // The photograph is courtesy of the U.S. Geological Survey.
        const srcImage = 'https://developers.google.com/maps/documentation/' +
            'javascript/examples/full/images/talkeetna.png';
    
        // The custom USGSOverlay object contains the USGS image,
        // the bounds of the image, and a reference to the map.
        this.overlay = new USGSOverlay(bounds, srcImage, map);
      }
    }
    
    0 讨论(0)
  • 2020-12-10 22:14

    As my code loads google maps API asynchronously, the above answers did not work for me. My problem was typescript use google variable before loaded google API.

    Here is a solution I found.

    To see async map loader code, see Load Google Maps JS API in component [Angular]

    service

    //map.service.ts
    
    import { Injectable } from '@angular/core';
    
    ...
    declare var google;    
    export let MyOverlay; // declare and use this from other ts file
    
    @Injectable({ providedIn: 'root' })
    export class MapService {
    
        private static promise;
        map: any;
    
        public static load() {
            if (!MapService.promise) {
                MapService.promise = new Promise((resolve) => {
                  //load google api asynchronously
                });
            }
    
            return MapService.promise;
        }
    
        async initMap(gmapElement, lat = -33.92, lng = 151.25) {
            return MapService.load()  
                .then((gapi) => {
                    //map init
                    this.map = new google.maps.Map(gmapElement.nativeElement, {
                       //map options
                    })
                    /*** before init MyOverlay, 
                    1. Google API should be loaded
                    2. AFTER MAP init(rendering), Overlay Object has Projection to calculate Overlay location
                    ***/
                    this.initMyOverlay()
                })
                .catch(console.log)
        }
    
        initMyOverlay(){            
            class MyOverlayCls extends google.maps.OverlayView {    
             //omitted for code readability
            }
            MyOverlay = MyOverlayCls ; //assign class to variable
        }
    
    }
    

    component

    //map.component.ts
    import { MapService, MyOverlay} from 'map.service';
    ...
    
      @ViewChild('gmap') gmapElement: any
      ...
    
      ngOnInit() {
        console.log("maps init")
        this.mapSvc.initMap(this.gmapElement).then(_ => {
    
          //At this time, google API has been loaded and assigned MyOverlay as MyOverlayCls
          let overlay= new MyOverlay(...)    
    
        })
    
    0 讨论(0)
  • 2020-12-10 22:15

    I also had loads of trouble with this but eventually came right. It's vital that the google maps script is pre-loaded (I loaded mine in my route resolver) otherwise you'll get a 'google' is not defined error. See here on how I installed typings and imported the library.

    As for the syntax, I ended up using an inner class for this. This is because, if it's a top-level class WebPack attempts (unsuccessfully) to find the google maps library at compile time:

    // import typings
    import {} from '@types/googlemaps';
    
    @Component({...})
    export class MapComponent implements OnInit {
      // weird syntax, I know, but it works
      USGSOverlay = class extends google.maps.OverlayView {
        constructor(bounds, image, private map){
          super();
          ...
          this.setMap(this.map);
        }
        onAdd(){...}
        draw(){...}
      }
      ngOnInit() {
        const overlay = new this.Overlay(bounds, image, map);
      }
    }
    
    0 讨论(0)
  • 2020-12-10 22:16

    In my case, i can not have an inner-class in the same class responsible for loading map. It took me sometime to figure out how to do it. This is how i did it

    export class mapTextOverlayService {
    
      //Overlay is creatd in an inner-class because if it is at top-level class google maps library may not be available at the class load time
      mapTextOverlay = class extends google.maps.OverlayView {
    
        pos_;
        text_;
        map_;
        div_;
    
        constructor(pos, text, map) {
          super();
          // Initialize all properties.
          this.pos_ = pos;
          this.text_ = text;
          this.map_ = map;
    
          // Define a property to hold the text's div. We'll
          // actually create this div upon receipt of the onAdd()
          // method so we'll leave it null for now.
          this.div_ = null;
    
          // Explicitly call setMap on this overlay.
          this.setMap(map);
        }
    
        /**
         * onAdd is called when the map's panes are ready and the overlay has been
         * added to the map.
         */
        onAdd() {
          const div = document.createElement('div');
          div.style.borderStyle = 'none';
          div.style.borderWidth = '0px';
          div.style.position = 'absolute';
    
          div.innerHTML = this.text_;
    
          this.div_ = div;
    
          // Add the element to the "overlayLayer" pane.
          const panes = this.getPanes();
          panes.overlayLayer.appendChild(div);
        };
    
        draw() {
          // We are using bounds centre to peg it to the correct position.
          // To do this, we need to retrieve the projection from the overlay.
          const overlayProjection = this.getProjection();
    
          // Convert the centre coordinates of this overlay from LatLngs to pixel coordinates.
          // We'll use these coordinates to provide the dimensions of div.
          const pixelPos = overlayProjection.fromLatLngToDivPixel(this.pos_);
    
          const div = this.div_;
          div.style.left = pixelPos.x + 'px';
          div.style.top = pixelPos.y + 'px';
        };
    
        // The onRemove() method will be called automatically from the API if
        // we ever set the overlay's map property to 'null'.
        onRemove() {
          this.div_.parentNode.removeChild(this.div_);
          this.div_ = null;
        };
    
        onHide() {
          if (this.div_) {
            this.div_.style.visibility = 'hidden';
          } 
        };
    
      }
    
      createMapTextOverlay(pos, text, map) {
        return new this.mapTextOverlay(pos, text, map);
      }
    
    };
    
    

    And then in the component where i want to create a text overlay:

    let mtos = new mapTextOverlayService();
    let label = mtos.createMapTextOverlay(boundsCenter, description, this.map.mapobject);
    
    0 讨论(0)
提交回复
热议问题