sebholstein / angular-google-maps

Angular 2+ Google Maps Components
https://angular-maps.com/
MIT License
2.03k stars 816 forks source link

Use google maps provided @Types file and make agm more extensible #1462

Closed wdunn001 closed 5 years ago

wdunn001 commented 6 years ago

Issue description

Make AGM extensible and Use @Types/googlemaps so that features can be added easily

Steps to reproduce and a minimal demo of the problem

In its current form AGM uses custom mapTypes sometimes these Types slightly veer slightly from the googlemaps types and may be the source of some known bugs. I propose we use the @types/googlemaps dependency instead of custom types. This makes AGM easily extensible in most cases. I have made a pull request #1461 that is refactored to use googlemaps and also identified a number of variations from the @types library

What steps should we try in your demo?

you can build https://github.com/wdunn001/angular-google-maps and use it to try it I currently don't have all the tests passing but it runs just fine I use it in my current project.

alternatively you can npm install --save @agme/core which I set up for my use

Current behavior agm uses custom mapTypes

Expected/desired behavior use the typings provided by google

angular2 & angular-google-maps version @latest

Other information

in addition, this pr identified numerous logic issues such as recasting of already typed values and redundant splice logic. some changes were required to make this compatible with googlemaps such as using a map(f => l.add(f)) to populate the features with the built in google maps funtion.

I found enum's to be particularly problematic I implemented a switch case that could probrably be switched to a map.

rppala3 commented 6 years ago

Anyone help?

It seems that there is a namespace conflict between trivial google object windows.google and namespace google.maps in the @types/googlemaps package.

The compiler says error TS2503: Cannot find namespace 'google' at the line 4

I'm using these package versions "@agm/core": "^1.0.0-beta.3", "@types/googlemaps": "^3.30.12",

and here below there is is my service:

import { Injectable } from '@angular/core';
import { MapsAPILoader } from '@agm/core';

import Geocoder = google.maps.Geocoder; // <-- this conflicts with the line below and in the constructor
declare const google: any;              // <--

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

  private geocoder: Geocoder;

  constructor(private mapsApiLoader: MapsAPILoader) {
    this.mapsApiLoader.load()
      .then(() => {
        this.geocoder = new google.maps.Geocoder(); // <--
      });
  }
}
wdunn001 commented 6 years ago

this will actually require structural changes to the rest of the project to perform. here is an example of circle-manager using google map types.

import {Injectable, NgZone} from '@angular/core';
import {Observable, Observer} from 'rxjs';
import {AgmCircle} from '../../directives/circle';
import {GoogleMapsAPIWrapper} from '../google-maps-api-wrapper';

@Injectable()
export class CircleManager {
  private _circles: Map<AgmCircle, Promise<google.maps.Circle>> =
      new Map<AgmCircle, Promise<google.maps.Circle>>();

  constructor(private _apiWrapper: GoogleMapsAPIWrapper, private _zone: NgZone) {}

  addCircle(circle: AgmCircle) {
    this._circles.set(circle, this._apiWrapper.createCircle({
      center: {lat: circle.latitude, lng: circle.longitude},
      clickable: circle.clickable,
      draggable: circle.draggable,
      editable: circle.editable,
      fillColor: circle.fillColor,
      fillOpacity: circle.fillOpacity,
      radius: circle.radius,
      strokeColor: circle.strokeColor,
      strokeOpacity: circle.strokeOpacity,
      strokePosition: circle._strokePosition,
      strokeWeight: circle.strokeWeight,
      visible: circle.visible,
      zIndex: circle.zIndex
    }));
  }

  /**
   * Removes the given circle from the map.
   */
  removeCircle(circle: AgmCircle): Promise<void> {
    return this._circles.get(circle).then((c) => {
      c.setMap(null);
      this._circles.delete(circle);
    });
  }

  setOptions(circle: AgmCircle, options: google.maps.CircleOptions): Promise<void> {
    return this._circles.get(circle).then((c) => c.setOptions(options));
  }

  getBounds(circle: AgmCircle): Promise<google.maps.LatLngBounds> {
    return this._circles.get(circle).then((c) => c.getBounds());
  }

  getCenter(circle: AgmCircle): Promise<google.maps.LatLng> {
    return this._circles.get(circle).then((c) => c.getCenter());
  }

  getRadius(circle: AgmCircle): Promise<number> {
    return this._circles.get(circle).then((c) => c.getRadius());
  }

  setCenter(circle: AgmCircle): Promise<void> {
    return this._circles.get(circle).then(
        (c) => { return c.setCenter({lat: circle.latitude, lng: circle.longitude}); });
  }

  setEditable(circle: AgmCircle): Promise<void> {
    return this._circles.get(circle).then((c) => { return c.setEditable(circle.editable); });
  }

  setDraggable(circle: AgmCircle): Promise<void> {
    return this._circles.get(circle).then((c) => { return c.setDraggable(circle.draggable); });
  }

  setVisible(circle: AgmCircle): Promise<void> {
    return this._circles.get(circle).then((c) => { return c.setVisible(circle.visible); });
  }

  setRadius(circle: AgmCircle): Promise<void> {
    return this._circles.get(circle).then((c) => { return c.setRadius(circle.radius); });
  }

  createEventObservable<T>(eventName: string, circle: AgmCircle): Observable<T> {
    return new Observable((observer: Observer<T>) => {
      let listener: google.maps.MapsEventListener = null;
      this._circles.get(circle).then((c) => {
        listener = c.addListener(eventName, (e: T) => this._zone.run(() => observer.next(e)));
      });

      return () => {
        if (listener !== null) {
          listener.remove();
        }
      };
    });
  }
}
ideodora commented 6 years ago

+1 I want MVCObject…specially #set #get agm types only defines addlistener…

stale[bot] commented 5 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

bleuscyther commented 5 years ago

i followed this approach : https://stackoverflow.com/a/55451667/1427338

/* /src/google-maps.d.ts */
import '@types/googlemaps';

declare global {
  interface Window {
    google: typeof google;
  }
}
/* /tsconfig.json  */
{
  ...
  "compilerOptions": {
    ...
    "typeRoots": [
      "node_modules/@types",
      "src/google-maps.d.ts"
    ],
   ...
  }
}
export classMyComponent implements OnInit {

    lat: number ;
    lng: number ;
    drawingManager: google.maps.drawing.DrawingManager;

   // wait for map to be ready or API to load
    onMapReady() {
        this.drawingManager = new google.maps.drawing.DrawingManager({
            // ...
        })
    }
}

NB: Don't forget to restart the active ng serve session Also if you are using extra libraries like DrawingManager:

AgmCoreModule.forRoot({
      apiKey: 'YOUR_API_CODE',
      libraries: ['drawing']
    }),