OpenStreetMap en Angular

Si has llegado hasta aquí, significa que necesitar agregar un mapa en tu web Angular. La forma más sencilla para lograr tu objetivo es utilizar Google Maps con el siguiente comando:

npm install --save @ types/googlemaps

Pero no lo recomiendo. Su uso gratuito es limitado y Google únicamente ofrece un descuento mensual de $200 que, en caso de superarlo, deberás de abonar. Además de todo esto, para obtener la clave de la Api google os pedirá que proporcionéis información sobre vuestra cuenta bancaria o tarjeta de débito, no sea que excedáis el pago y se quede pendiente el cobro. Mi recomendación, es clara, no lo utilices y pasaté a OpenLayers

OpenLayers es una biblioteca repleta de funciones y de alto rendimiento destinado para aplicaciones basadas en entornos GIS. OpenLayers es un código abierto que utiliza datos de OpenStreetMap. A continuación os dejo un ejemplo para que podáis interactuar con el:

Instalación.

El primer paso es la instalación de la librería:

npm install leaflet
npm i --save-dev @types/leaflet
npm i leaflet-routing-machine
npm i --save-dev @types/leaflet-routing-machine

Y añadir en el fichero angular.json la siguiente información:

{
  // ...
  "projects": {
    "angular-leaflet-example": {
      // ...
      "architect": {
        "build": {
          // ...
          "options": {
            // ...
             "assets": [
              {
                "glob": "**/*",
                "input": "node_modules/leaflet/dist/images/", // you may need to change this path, according to your files structure
                "output": "./assets"
              },
              ],
              "styles": [
              "node_modules/leaflet/dist/leaflet.css",
              "node_modules/leaflet-routing-machine/dist/leaflet-routing-machine.css",
              ],
              // ..
          },
          // ...
        },
        // ...
      }
    }},
}

Una vez importado, creamos un componente con la siguiente vista:

<div class="map-container">
  <div class="map-frame">
    <div id="map"></div>
  </div>
</div>

Documento .css

.map-container {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  margin: 30px;
}

.map-frame {
  border: 2px solid black;
  height: 100%;
}

#map {
  height: 100%;
}

Código Javascript.


import * as L from 'leaflet';
import 'leaflet-routing-machine';
import { icon, Marker } from 'leaflet';
import { Inject, Input, OnInit } from '@angular/core';

export const DEFAULT_LAT = 48.20807;
export const DEFAULT_LON =  16.37320;
export const TITULO = 'Proyecto';
const iconRetinaUrl = 'assets/marker-icon-2x.png';
const iconUrl = 'assets/marker-icon.png';
const shadowUrl = 'assets/marker-shadow.png';

export class StreetMapsEjemploComponent implements OnInit {
  private map:any;
  @Input() lat: number = DEFAULT_LAT;
  @Input() lon: number = DEFAULT_LON;
  @Input() titulo: string = TITULO ;

  constructor() {
  }

  ngOnInit(): void {
    this.initMap();
  }



  private initMap(): void {
      //configuración del mapa
      this.map = L.map('map', {
        center: [this.lat, this.lon],
        attributionControl: false,
        zoom: 14
      });

      //iconos personalizados
      var iconDefault = L.icon({
        iconRetinaUrl,
        iconUrl,
        shadowUrl,
        iconSize: [25, 41],
        iconAnchor: [12, 41],
        popupAnchor: [1, -34],
        tooltipAnchor: [16, -28],
        shadowSize: [41, 41]
      });
     L.Marker.prototype.options.icon = iconDefault;

      //titulo
      const tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        maxZoom: 19,
        attribution: '&copy; <a href="https://1938.com.es">Web Inteligencia Artificial</a>'
      });

      //marca con pop up
      const lon = this.lon + 0.009;
      const lat = this.lat + 0.009;
      const marker = L.marker([lat + 0.005, lon + 0.005]).bindPopup(this.titulo);
      marker.addTo(this.map);

      //marca forma de circulo
      const mark = L.circleMarker([this.lat, this.lon]).addTo(this.map);
      mark.addTo(this.map);


    //ruta
    L.Routing.control({
      router: L.Routing.osrmv1({
        serviceUrl: `https://router.project-osrm.org/route/v1/`
      }),
      showAlternatives: true,
      fitSelectedRoutes: false,
      show: false,
      routeWhileDragging: true,
      waypoints: [
        L.latLng(this.lat, this.lon),
        L.latLng(lat, lon)
      ]
    }).addTo(this.map);
      tiles.addTo(this.map);
    }
  }

Os podéis descargar el código en el siguiente repositorio: https://github.com/al118345/OpenStreetMapAngular12 y el siguiente video https://www.youtube.com/watch?v=2zYe5fSa2ZU:

Instalación en angular Universal

En Angular Universal el objetivo consiste en crear un Server Side Rendering (SSR) para ser detectada por Google y mejorar nuestro SEO (https://1938.com.es/app-seo-angular)

En estas circunstancias la configuración cambia y se deben aplicar las siguientes implementaciones

Importante Entendemos que punto de instalación del proyecto lo has realizado. Es decir, partimos desde el punto que lo dejamos en proyecto anterior.

LeafletService

Crearemos un servicio nuevo que nos permita obtener, en el caso de ser un usuario navegador (no la araña de google) la información de la web

Comando

ng generate service service/leaflet

fichero: service/leaflet.service.ts

import { Injectable, PLATFORM_ID, Inject } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';

@Injectable({
  providedIn: 'root'
})


@Injectable()
export class LeafletService {

  public L:any = null;
  public Routing:any = null

  constructor(@Inject(PLATFORM_ID) private platformId: Object) {
    if (isPlatformBrowser(platformId)) {
      this.L = require('leaflet');
      this.Routing = require('leaflet-routing-machine');
    }
  }

}

A continuación modificamos el componente.ts con la siguiente información


  var iconRetinaUrl = 'assets/marker-icon-2x.png';
  var iconUrl = 'assets/marker-icon.png';
  var shadowUrl = 'assets/marker-shadow.png';
  constructor(private mapService: LeafletService) {
  }
  ngOnInit(): void {
    if (this.mapService.L) {
        this.initMap();
    }
  }



  private initMap(): void {
    var iconDefault = this.mapService.L.icon({
      iconRetinaUrl,
      iconUrl,
      shadowUrl,
      iconSize: [25, 41],
      iconAnchor: [12, 41],
      popupAnchor: [1, -34],
      tooltipAnchor: [16, -28],
      shadowSize: [41, 41]
    });
      this.mapService.L.Marker.prototype.options.icon = iconDefault;

      this.map =  this.mapService.L.map('map', {
        center: [this.lat, this.lon],
        attributionControl: false,
        zoom: 14
      });

      const tiles = this.mapService.L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        maxZoom: 19,
        attribution: '&copy; <a href="https://1938.com.es">Web Inteligencia Artificial</a>'
      });
      //const marker = L.marker([this.lat, this.lon]);
      //marker.addTo(this.map);

      const lon = this.lon + 0.009;
      const lat = this.lat + 0.009;
      const marker = this.mapService.L.marker([lat + 0.005, lon + 0.005]).bindPopup(this.titulo);
      marker.addTo(this.map);

      const mark = this.mapService.L.circleMarker([this.lat, this.lon]).addTo(this.map);
      mark.bindPopup(this.titulo);
      mark.addTo(this.map);

     const mark2 = this.mapService.L.circleMarker([lat, lon]).addTo(this.map);
      mark2.addTo(this.map);



    this.mapService.L.Routing.control({
      router: this.mapService.L.Routing.osrmv1({
        serviceUrl: `https://router.project-osrm.org/route/v1/`
      }),
      showAlternatives: true,
      fitSelectedRoutes: false,
      show: false,
      routeWhileDragging: true,
      waypoints: [
        this.mapService.L.latLng(this.lat, this.lon),
        this.mapService.L.latLng(lat, lon)
      ]
    }).addTo(this.map);

      tiles.addTo(this.map);

    }

Eliminamos los siguientes imports:

import * as L from 'leaflet';
import 'leaflet-routing-machine';
import { icon, Marker } from 'leaflet';

Añadimos el servicio

import {LeafletService} from "./service/leaflet.service";

Y modificamos el fichero tsconfig.app.json con la siguiente configuración:

"compilerOptions": {
    "outDir": "./out-tsc/app",
    "types": [ "node" ],
    "typeRoots": [ "../node_modules/@types" ]
  },
  "angularCompilerOptions": {
    "strictNullChecks": false,
  },

Os podéis descargar el código en el siguiente repositorio: https://github.com/al118345/OpenStreetMap_Angular13_Universal