Tipos de mapas

Seleccionar plataforma: Android iOS JavaScript

En este documento, se analizan los tipos de mapas que puedes mostrar con la API de Maps JavaScript. La API usa un objeto MapType para contener información sobre estos mapas. Un objeto MapType es una interfaz que define la visualización y el uso de los mosaicos de mapas y la traducción de los sistemas de coordenadas desde coordenadas en pantalla hasta coordenadas mundiales (en el mapa). Cada MapType debe contener algunos métodos para administrar la recuperación y liberación de mosaicos, y propiedades que definen su comportamiento visual.

El funcionamiento interno de los tipos de mapas dentro de la API de Maps JavaScript es un tema avanzado. La mayoría de los desarrolladores pueden usar los tipos de mapas básicos que se indican a continuación. Sin embargo, también puedes modificar la presentación de los tipos de mapas existentes mediante mapas con ajustes de estilo o definir tus propios mosaicos mediante tipos de mapas personalizados. Cuando proporciones tipos de mapas personalizados, deberás comprender cómo modificar el registro de tipos de mapas.

Tipos de mapas básicos

Hay cuatro tipos de mapas disponibles en la Maps JavaScript API. Además de los mosaicos de mapas de carreteras conocidos, la API de Maps JavaScript también admite otros tipos de mapas.

Los siguientes tipos de mapas están disponibles en la Maps JavaScript API:

  • roadmap muestra la vista predeterminada del mapa de ruta. Este es el tipo de mapa predeterminado.
  • satellite muestra imágenes satelitales de Google Earth.
  • hybrid muestra una combinación de vistas normales y satelitales.
  • terrain muestra un mapa físico basado en la información del terreno.

Puedes modificar el tipo de mapa que usa el Map estableciendo su propiedad mapTypeId, ya sea dentro del constructor mediante la configuración de su objeto Map options o con una llamada al método setMapTypeId() del mapa. El valor predeterminado de la propiedad mapTypeID es roadmap.

Configura mapTypeId durante la construcción:

var myLatlng = new google.maps.LatLng(-34.397, 150.644);
var mapOptions = {
  zoom: 8,
  center: myLatlng,
  mapTypeId: 'satellite'
};
var map = new google.maps.Map(document.getElementById('map'),
    mapOptions);

Modifica mapTypeId de forma dinámica:

map.setMapTypeId('terrain');

Ten en cuenta que, en realidad, no configuras el tipo de mapa directamente, sino que estableces su mapTypeId para hacer referencia a un MapType mediante un identificador. La API de Maps JavaScript usa un registro de tipos de mapas, que se explica a continuación, para administrar estas referencias.

Imágenes a 45°

La API de Maps JavaScript admite imágenes especiales a 45° para determinadas ubicaciones. Estas imágenes de alta resolución proporcionan vistas en perspectiva de cada dirección cardinal (norte, sur, este, oeste). Estas imágenes están disponibles en niveles de zoom más altos para los tipos de mapas admitidos.

En la siguiente imagen, se muestra una vista a 45° de la ciudad de Nueva York:

Los tipos de mapas satellite y hybrid admiten imágenes a 45° con altos niveles de zoom (12 y superiores) cuando están disponibles. Si el usuario aplica zoom de acercamiento a una ubicación para la que existen estas imágenes, los tipos de mapas modifican automáticamente sus vistas de la siguiente manera:

  • Las imágenes satelitales o híbridas se reemplazan por imágenes que ofrecen una perspectiva de 45°, centradas en la ubicación actual. De forma predeterminada, esas vistas se orientan hacia el norte. Si el usuario aplica zoom de alejamiento, las imágenes satelitales o híbridas predeterminadas aparecen nuevamente. El comportamiento varía según el nivel de zoom y el valor de tilt:
    • Entre los niveles de zoom 12 y 18, el mapa base de arriba hacia abajo (0°) se muestra de forma predeterminada, a menos que tilt esté establecido en 45.
    • Con niveles de zoom de 18 o mayores, se muestra el mapa base a 45°, a menos que tilt esté establecido en 0.
  • El control de rotación se vuelve visible. El control de rotación proporciona opciones que permiten al usuario activar o desactivar la inclinación y rotar la vista en incrementos de 90° en cualquier dirección. Para ocultar el control de rotación, establece rotateControl en false.

Al alejarte de un tipo de mapa que muestra imágenes a 45°, se revierten cada uno de estos cambios y se restablecen los tipos de mapas originales.

Cómo habilitar y deshabilitar imágenes a 45°

Para inhabilitar las imágenes a 45°, llama a setTilt(0) en el objeto Map. A fin de habilitar las imágenes a 45° para los tipos de mapas compatibles, llama a setTilt(45). El método getTilt() de Map siempre mostrará el objeto tilt actual que se muestra en el mapa. Por ejemplo, si configuras un elemento tilt en un mapa y, luego, quitas ese elemento tilt (si alejas el mapa), el método getTilt() mostrará 0.

Importante: Las imágenes a 45° solo son compatibles con los mapas de trama; estas no pueden usarse con mapas vectoriales.

En el siguiente ejemplo, se muestra una vista a 45° de la ciudad de Nueva York:

TypeScript

function initMap(): void {
  const map = new google.maps.Map(
    document.getElementById("map") as HTMLElement,
    {
      center: { lat: 40.76, lng: -73.983 },
      zoom: 15,
      mapTypeId: "satellite",
    }
  );

  map.setTilt(45);
}

declare global {
  interface Window {
    initMap: () => void;
  }
}
window.initMap = initMap;

JavaScript

function initMap() {
  const map = new google.maps.Map(document.getElementById("map"), {
    center: { lat: 40.76, lng: -73.983 },
    zoom: 15,
    mapTypeId: "satellite",
  });

  map.setTilt(45);
}

window.initMap = initMap;
Ver ejemplo

Probar la muestra

Ver el ejemplo.

Cómo rotar imágenes a 45°

Las imágenes de 45° en realidad consisten en una colección de imágenes para cada dirección cardinal (norte, sur, este, oeste). Una vez que el mapa muestre imágenes a 45°, puedes orientarlas hacia una de las indicaciones cardinales llamando a setHeading() en el objeto Map y pasando un valor numérico expresado en grados desde el norte.

En el ejemplo siguiente se muestra un mapa aéreo y este gira automáticamente cada 3 segundos cuando se hace clic en el botón:

TypeScript

let map: google.maps.Map;

function initMap(): void {
  map = new google.maps.Map(document.getElementById("map") as HTMLElement, {
    center: { lat: 40.76, lng: -73.983 },
    zoom: 15,
    mapTypeId: "satellite",
    heading: 90,
    tilt: 45,
  });

  // add listener to button
  document.getElementById("rotate")!.addEventListener("click", autoRotate);
}

function rotate90(): void {
  const heading = map.getHeading() || 0;

  map.setHeading(heading + 90);
}

function autoRotate(): void {
  // Determine if we're showing aerial imagery.
  if (map.getTilt() !== 0) {
    window.setInterval(rotate90, 3000);
  }
}

declare global {
  interface Window {
    initMap: () => void;
  }
}
window.initMap = initMap;

JavaScript

let map;

function initMap() {
  map = new google.maps.Map(document.getElementById("map"), {
    center: { lat: 40.76, lng: -73.983 },
    zoom: 15,
    mapTypeId: "satellite",
    heading: 90,
    tilt: 45,
  });
  // add listener to button
  document.getElementById("rotate").addEventListener("click", autoRotate);
}

function rotate90() {
  const heading = map.getHeading() || 0;

  map.setHeading(heading + 90);
}

function autoRotate() {
  // Determine if we're showing aerial imagery.
  if (map.getTilt() !== 0) {
    window.setInterval(rotate90, 3000);
  }
}

window.initMap = initMap;
Ver ejemplo

Probar la muestra

Ver el ejemplo.

Cómo modificar el registro de tipos de mapas

Un mapTypeId de un mapa es un identificador de strings que se usa para asociar un MapType con un valor único. Cada objeto Map mantiene un MapTypeRegistry que contiene la colección de MapType disponibles para ese mapa. Este registro se usa para seleccionar los tipos de mapas disponibles, por ejemplo, en el control MapType.

Las lecturas no se hacen directamente desde el registro de tipos de mapas En su lugar, modifica el registro agregando tipos de mapas personalizados y asociándolos con un identificador de string de tu elección. No se pueden modificar ni alterar los tipos de mapas básicos (aunque se pueden quitar del mapa modificando el aspecto del mapa asociado mapTypeControlOptions).

El siguiente código configura el mapa para mostrar solo dos tipos de mapas en el mapTypeControlOptions del mapa y modifica el registro para agregar la asociación con este identificador a la implementación real de la interfaz MapType.

// Modify the control to only display two maptypes, the
// default ROADMAP and the custom 'mymap'.
// Note that because this is an association, we
// don't need to modify the MapTypeRegistry beforehand.

var MY_MAPTYPE_ID = 'mymaps';

var mapOptions = {
  zoom: 12,
  center: brooklyn,
  mapTypeControlOptions: {
     mapTypeIds: ['roadmap', MY_MAPTYPE_ID]
  },
  mapTypeId: MY_MAPTYPE_ID
};

// Create our map. This creation will implicitly create a
// map type registry.
map = new google.maps.Map(document.getElementById('map'),
    mapOptions);

// Create your custom map type using your own code.
// (See below.)
var myMapType = new MyMapType();

// Set the registry to associate 'mymap' with the
// custom map type we created, and set the map to
// show that map type.
map.mapTypes.set(MY_MAPTYPE_ID, myMapType);

Mapas con estilos

El objeto StyledMapType te permite personalizar la presentación de los mapas base estándar de Google y cambiar la visualización de elementos como rutas, parques y áreas con edificaciones para reflejar un estilo diferente al que se usa en el tipo de mapa predeterminado.

Para obtener más información sobre StyledMapType, consulta la guía sobre mapas con ajustes de estilo.

Tipos de mapas personalizados

La API de Maps JavaScript admite la visualización y administración de tipos de mapas personalizados, lo que te permite implementar tus propias superposiciones de imágenes o mosaicos de mapas.

Existen varias implementaciones de tipos de mapas posibles dentro de la API de Maps JavaScript:

  • Conjuntos de mosaicos estándares que consisten en imágenes que en conjunto constituyen mapas cartográficos completos. Estos conjuntos de mosaicos también se conocen como tipos de mapas básicos. Estos tipos de mapas actúan y se comportan como los tipos de mapa predeterminados existentes: roadmap, satellite, hybrid y terrain. Puedes agregar tu tipo de mapa personalizado a un arreglo de mapTypes de Maps para permitir que la IU de la API de Maps JavaScript lo trate como un tipo de mapa estándar (por ejemplo, incluyéndolo en el control MapType).
  • Superposiciones de mosaicos de imágenes que se muestran sobre los tipos de mapas base existentes. En general, estos tipos de mapas se usan para aumentar un tipo de mapa existente con el fin de mostrar información adicional y, a menudo, se limitan a ubicaciones o niveles de zoom específicos. Ten en cuenta que estos mosaicos pueden ser transparentes, lo que te permite agregar elementos a mapas existentes.
  • Tipos de mapas sin imágenes, que te permiten manipular la visualización de la información de los mapas en su nivel más fundamental.

Cada una de estas opciones se basa en la creación de una clase que implementa la interfaz MapType. Además, la clase ImageMapType proporciona un comportamiento integrado para simplificar la creación de tipos de mapas de imágenes.

La interfaz de MapType

Antes de crear clases que implementen MapType, es importante comprender cómo Google Maps determina las coordenadas y decidir qué partes del mapa mostrar. Debes implementar una lógica similar para cualquier tipo de mapa base o de superposición. Lee la guía sobre coordenadas de mosaicos y mapas.

Los tipos de mapas personalizados deben implementar la interfaz MapType. Esta interfaz especifica ciertas propiedades y métodos que permiten a la API iniciar solicitudes a tus tipos de mapa cuando la API determina que necesita mostrar mosaicos de mapas dentro del viewport y el nivel de zoom actuales. Debes administrar estas solicitudes para decidir qué mosaico cargar.

Nota: Puedes crear tu propia clase para implementar esta interfaz. Como alternativa, si tienes imágenes compatibles, puedes usar la clase ImageMapType, que ya implementa esta interfaz.

Las clases que implementan la interfaz MapType requieren que definas y propagues las siguientes propiedades:

  • tileSize (obligatorio) especifica el tamaño del mosaico (del tipo google.maps.Size). Los tamaños deben ser rectangulares, pero no cuadrados.
  • maxZoom (obligatorio) especifica el nivel de zoom máximo en el que deben mostrarse mosaicos de este tipo de mapa.
  • minZoom (opcional) especifica el nivel de zoom mínimo en el que se muestran mosaicos de este tipo de mapa. De forma predeterminada, este valor es 0, lo que indica que no existe un nivel de zoom mínimo.
  • name (opcional) especifica el nombre de este tipo de mapa. Esta propiedad solo es necesaria si deseas que se pueda seleccionar este tipo de mapa dentro de un control MapType. (Consulta Agrega controles MapType a continuación).
  • alt (opcional) especifica el texto alternativo para este tipo de mapa, que se muestra como texto flotante. Esta propiedad solo es necesaria si deseas que se pueda seleccionar este tipo de mapa dentro de un control MapType. (Consulta Agrega controles MapType a continuación).

Además, las clases que implementan la interfaz MapType deben implementar los siguientes métodos:

  • Se llama a getTile() (obligatorio) cada vez que la API determina que el mapa debe mostrar mosaicos nuevos para la ventana de visualización determinada. El método getTile() debe tener la siguiente firma:

    getTile(tileCoord:Point,zoom:number,ownerDocument:Document):Node

    La API determina si debe llamar a getTile() según las propiedades tileSize, minZoom y maxZoom de MapType, y la ventana gráfica y el nivel de zoom actuales del mapa. El controlador de este método debe mostrar un elemento HTML con una coordenada, nivel de zoom y un elemento DOM pasados en los que se agregará la imagen de mosaico.

  • Se llama a releaseTile() (opcional) cada vez que la API determina que el mapa debe quitar un mosaico, ya que queda fuera de la vista. El método debe tener la siguiente firma:

    releaseTile(tile:Node)

    Por lo general, debes manejar la eliminación de cualquier elemento que se haya adjuntado a los mosaicos del mapa después de agregarlos. Por ejemplo, si adjuntaste objetos de escucha de eventos para mapear superposiciones de mosaicos, debes quitarlos aquí.

El método getTile() actúa como el controlador principal para determinar qué mosaicos cargar dentro de un viewport determinado.

Tipos de mapas básicos

Los tipos de mapas que construyas de esta manera pueden ser independientes o combinarse con otros como superposiciones. Los tipos de mapas independientes se conocen como tipos de mapas básicos. Es posible que desees que la API trate a los MapType personalizados como a cualquier otro tipo de mapa base existente (ROADMAP, TERRAIN, etc.). Para hacerlo, agrega tu MapType personalizado a la propiedad mapTypes de Map. Esta propiedad es de tipo MapTypeRegistry.

El siguiente código crea un objeto MapType base para mostrar las coordenadas de mosaicos de un mapa y dibuja un contorno de los mosaicos:

TypeScript

/*
 * This demo demonstrates how to replace default map tiles with custom imagery.
 * In this case, the CoordMapType displays gray tiles annotated with the tile
 * coordinates.
 *
 * Try panning and zooming the map to see how the coordinates change.
 */

class CoordMapType {
  tileSize: google.maps.Size;
  maxZoom = 19;
  name = "Tile #s";
  alt = "Tile Coordinate Map Type";

  constructor(tileSize: google.maps.Size) {
    this.tileSize = tileSize;
  }

  getTile(
    coord: google.maps.Point,
    zoom: number,
    ownerDocument: Document
  ): HTMLElement {
    const div = ownerDocument.createElement("div");

    div.innerHTML = String(coord);
    div.style.width = this.tileSize.width + "px";
    div.style.height = this.tileSize.height + "px";
    div.style.fontSize = "10";
    div.style.borderStyle = "solid";
    div.style.borderWidth = "1px";
    div.style.borderColor = "#AAAAAA";
    div.style.backgroundColor = "#E5E3DF";
    return div;
  }

  releaseTile(tile: HTMLElement): void {}
}

function initMap(): void {
  const map = new google.maps.Map(
    document.getElementById("map") as HTMLElement,
    {
      zoom: 10,
      center: { lat: 41.85, lng: -87.65 },
      streetViewControl: false,
      mapTypeId: "coordinate",
      mapTypeControlOptions: {
        mapTypeIds: ["coordinate", "roadmap"],
        style: google.maps.MapTypeControlStyle.DROPDOWN_MENU,
      },
    }
  );

  map.addListener("maptypeid_changed", () => {
    const showStreetViewControl =
      (map.getMapTypeId() as string) !== "coordinate";

    map.setOptions({
      streetViewControl: showStreetViewControl,
    });
  });

  // Now attach the coordinate map type to the map's registry.
  map.mapTypes.set(
    "coordinate",
    new CoordMapType(new google.maps.Size(256, 256))
  );
}

declare global {
  interface Window {
    initMap: () => void;
  }
}
window.initMap = initMap;

JavaScript

/*
 * This demo demonstrates how to replace default map tiles with custom imagery.
 * In this case, the CoordMapType displays gray tiles annotated with the tile
 * coordinates.
 *
 * Try panning and zooming the map to see how the coordinates change.
 */
class CoordMapType {
  tileSize;
  maxZoom = 19;
  name = "Tile #s";
  alt = "Tile Coordinate Map Type";
  constructor(tileSize) {
    this.tileSize = tileSize;
  }
  getTile(coord, zoom, ownerDocument) {
    const div = ownerDocument.createElement("div");

    div.innerHTML = String(coord);
    div.style.width = this.tileSize.width + "px";
    div.style.height = this.tileSize.height + "px";
    div.style.fontSize = "10";
    div.style.borderStyle = "solid";
    div.style.borderWidth = "1px";
    div.style.borderColor = "#AAAAAA";
    div.style.backgroundColor = "#E5E3DF";
    return div;
  }
  releaseTile(tile) {}
}

function initMap() {
  const map = new google.maps.Map(document.getElementById("map"), {
    zoom: 10,
    center: { lat: 41.85, lng: -87.65 },
    streetViewControl: false,
    mapTypeId: "coordinate",
    mapTypeControlOptions: {
      mapTypeIds: ["coordinate", "roadmap"],
      style: google.maps.MapTypeControlStyle.DROPDOWN_MENU,
    },
  });

  map.addListener("maptypeid_changed", () => {
    const showStreetViewControl = map.getMapTypeId() !== "coordinate";

    map.setOptions({
      streetViewControl: showStreetViewControl,
    });
  });
  // Now attach the coordinate map type to the map's registry.
  map.mapTypes.set(
    "coordinate",
    new CoordMapType(new google.maps.Size(256, 256))
  );
}

window.initMap = initMap;
Ver ejemplo

Probar la muestra

Tipos de mapas de superposición

Algunos tipos de mapas están diseñados para funcionar en conjunto con los tipos de mapas existentes. Estos tipos de mapas pueden tener capas transparentes que indiquen lugares de interés o que muestren datos adicionales al usuario.

En estos casos, no deseas que el tipo de mapa se trate como una entidad independiente, sino como una superposición. Para ello, agrega el tipo de mapa a un objeto MapType existente directamente con la propiedad overlayMapTypes de Map. Esta propiedad contiene un MVCArray de MapType. Todos los tipos de mapas (básicos y superpuestos) se procesan en la capa mapPane. Los tipos de mapas superpuestos aparecen en la parte superior del mapa base al que se adjuntan, en el orden en que aparecen en el arreglo Map.overlayMapTypes (las superposiciones con valores de índice más altos se muestran delante de las superposiciones con valores de índice más bajos).

El siguiente ejemplo es idéntico al anterior, con la excepción de que creamos una superposición de mosaicos MapType sobre el tipo de mapa ROADMAP:

TypeScript

/*
 * This demo illustrates the coordinate system used to display map tiles in the
 * API.
 *
 * Tiles in Google Maps are numbered from the same origin as that for
 * pixels. For Google's implementation of the Mercator projection, the origin
 * tile is always at the northwest corner of the map, with x values increasing
 * from west to east and y values increasing from north to south.
 *
 * Try panning and zooming the map to see how the coordinates change.
 */

class CoordMapType {
  tileSize: google.maps.Size;

  constructor(tileSize: google.maps.Size) {
    this.tileSize = tileSize;
  }
  getTile(
    coord: google.maps.Point,
    zoom: number,
    ownerDocument: Document
  ): HTMLElement {
    const div = ownerDocument.createElement("div");

    div.innerHTML = String(coord);
    div.style.width = this.tileSize.width + "px";
    div.style.height = this.tileSize.height + "px";
    div.style.fontSize = "10";
    div.style.borderStyle = "solid";
    div.style.borderWidth = "1px";
    div.style.borderColor = "#AAAAAA";
    return div;
  }
  releaseTile(tile: Element): void {}
}

function initMap(): void {
  const map = new google.maps.Map(
    document.getElementById("map") as HTMLElement,
    {
      zoom: 10,
      center: { lat: 41.85, lng: -87.65 },
    }
  );

  // Insert this overlay map type as the first overlay map type at
  // position 0. Note that all overlay map types appear on top of
  // their parent base map.
  map.overlayMapTypes.insertAt(
    0,
    new CoordMapType(new google.maps.Size(256, 256))
  );
}

declare global {
  interface Window {
    initMap: () => void;
  }
}
window.initMap = initMap;

JavaScript

/*
 * This demo illustrates the coordinate system used to display map tiles in the
 * API.
 *
 * Tiles in Google Maps are numbered from the same origin as that for
 * pixels. For Google's implementation of the Mercator projection, the origin
 * tile is always at the northwest corner of the map, with x values increasing
 * from west to east and y values increasing from north to south.
 *
 * Try panning and zooming the map to see how the coordinates change.
 */
class CoordMapType {
  tileSize;
  constructor(tileSize) {
    this.tileSize = tileSize;
  }
  getTile(coord, zoom, ownerDocument) {
    const div = ownerDocument.createElement("div");

    div.innerHTML = String(coord);
    div.style.width = this.tileSize.width + "px";
    div.style.height = this.tileSize.height + "px";
    div.style.fontSize = "10";
    div.style.borderStyle = "solid";
    div.style.borderWidth = "1px";
    div.style.borderColor = "#AAAAAA";
    return div;
  }
  releaseTile(tile) {}
}

function initMap() {
  const map = new google.maps.Map(document.getElementById("map"), {
    zoom: 10,
    center: { lat: 41.85, lng: -87.65 },
  });

  // Insert this overlay map type as the first overlay map type at
  // position 0. Note that all overlay map types appear on top of
  // their parent base map.
  map.overlayMapTypes.insertAt(
    0,
    new CoordMapType(new google.maps.Size(256, 256))
  );
}

window.initMap = initMap;
Ver ejemplo

Probar la muestra

Tipos de mapas de imágenes

Implementar un MapType para que actúe como tipo de mapa base puede llevar mucho tiempo y trabajo. La API proporciona una clase especial que implementa la interfaz MapType para los tipos de mapa más comunes: tipos de mapa que constan de mosaicos compuestos por archivos de una sola imagen.

Esta clase, la clase ImageMapType, se construye mediante una especificación de objeto ImageMapTypeOptions que define las siguientes propiedades obligatorias:

  • tileSize (obligatorio) especifica el tamaño del mosaico (del tipo google.maps.Size). Los tamaños deben ser rectangulares, pero no cuadrados.
  • getTileUrl (obligatorio) especifica la función, que se suele proporcionar como un literal de función intercalada, para controlar la selección del mosaico de imagen adecuado según las coordenadas mundiales y el nivel de zoom proporcionados.

El siguiente código implementa un objeto ImageMapType básico con mosaicos de Google. En el ejemplo, se usa una función de normalización para garantizar que los mosaicos se repitan a lo largo del eje x, pero no a lo largo del eje y de tu mapa.

TypeScript

function initMap(): void {
  const map = new google.maps.Map(
    document.getElementById("map") as HTMLElement,
    {
      center: { lat: 0, lng: 0 },
      zoom: 1,
      streetViewControl: false,
      mapTypeControlOptions: {
        mapTypeIds: ["moon"],
      },
    }
  );

  const moonMapType = new google.maps.ImageMapType({
    getTileUrl: function (coord, zoom): string {
      const normalizedCoord = getNormalizedCoord(coord, zoom);

      if (!normalizedCoord) {
        return "";
      }

      const bound = Math.pow(2, zoom);
      return (
        "https://mw1.google.com/mw-planetary/lunar/lunarmaps_v1/clem_bw" +
        "/" +
        zoom +
        "/" +
        normalizedCoord.x +
        "/" +
        (bound - normalizedCoord.y - 1) +
        ".jpg"
      );
    },
    tileSize: new google.maps.Size(256, 256),
    maxZoom: 9,
    minZoom: 0,
    // @ts-ignore TODO 'radius' does not exist in type 'ImageMapTypeOptions'
    radius: 1738000,
    name: "Moon",
  });

  map.mapTypes.set("moon", moonMapType);
  map.setMapTypeId("moon");
}

// Normalizes the coords that tiles repeat across the x axis (horizontally)
// like the standard Google map tiles.
function getNormalizedCoord(coord, zoom) {
  const y = coord.y;
  let x = coord.x;

  // tile range in one direction range is dependent on zoom level
  // 0 = 1 tile, 1 = 2 tiles, 2 = 4 tiles, 3 = 8 tiles, etc
  const tileRange = 1 << zoom;

  // don't repeat across y-axis (vertically)
  if (y < 0 || y >= tileRange) {
    return null;
  }

  // repeat across x-axis
  if (x < 0 || x >= tileRange) {
    x = ((x % tileRange) + tileRange) % tileRange;
  }

  return { x: x, y: y };
}

declare global {
  interface Window {
    initMap: () => void;
  }
}
window.initMap = initMap;

JavaScript

function initMap() {
  const map = new google.maps.Map(document.getElementById("map"), {
    center: { lat: 0, lng: 0 },
    zoom: 1,
    streetViewControl: false,
    mapTypeControlOptions: {
      mapTypeIds: ["moon"],
    },
  });
  const moonMapType = new google.maps.ImageMapType({
    getTileUrl: function (coord, zoom) {
      const normalizedCoord = getNormalizedCoord(coord, zoom);

      if (!normalizedCoord) {
        return "";
      }

      const bound = Math.pow(2, zoom);
      return (
        "https://mw1.google.com/mw-planetary/lunar/lunarmaps_v1/clem_bw" +
        "/" +
        zoom +
        "/" +
        normalizedCoord.x +
        "/" +
        (bound - normalizedCoord.y - 1) +
        ".jpg"
      );
    },
    tileSize: new google.maps.Size(256, 256),
    maxZoom: 9,
    minZoom: 0,
    // @ts-ignore TODO 'radius' does not exist in type 'ImageMapTypeOptions'
    radius: 1738000,
    name: "Moon",
  });

  map.mapTypes.set("moon", moonMapType);
  map.setMapTypeId("moon");
}

// Normalizes the coords that tiles repeat across the x axis (horizontally)
// like the standard Google map tiles.
function getNormalizedCoord(coord, zoom) {
  const y = coord.y;
  let x = coord.x;
  // tile range in one direction range is dependent on zoom level
  // 0 = 1 tile, 1 = 2 tiles, 2 = 4 tiles, 3 = 8 tiles, etc
  const tileRange = 1 << zoom;

  // don't repeat across y-axis (vertically)
  if (y < 0 || y >= tileRange) {
    return null;
  }

  // repeat across x-axis
  if (x < 0 || x >= tileRange) {
    x = ((x % tileRange) + tileRange) % tileRange;
  }
  return { x: x, y: y };
}

window.initMap = initMap;
Ver ejemplo

Probar la muestra

Proyecciones

La Tierra es una esfera tridimensional (aproximadamente), mientras que un mapa es una superficie bidimensional. El mapa que ves en la API de Maps JavaScript, como cualquier mapa plano de la Tierra, es una proyección de esa esfera en una superficie plana. En términos más simples, una proyección se puede definir como una asignación de valores de latitud y longitud a coordenadas del mapa de proyección.

Las proyecciones en la API de Maps JavaScript deben implementar la interfaz Projection. Una implementación de Projection no solo debe proporcionar una asignación de un sistema de coordenadas a otro, sino también una asignación bidireccional. Es decir, debes definir cómo traducir coordenadas de Earth (objetos LatLng) al sistema de coordenadas mundiales de la clase Projection, y viceversa. Google Maps usa la proyección de Mercator para crear sus mapas a partir de datos geográficos y convertir eventos en el mapa en coordenadas geográficas. Para obtener esta proyección, llama a getProjection() en el Map (o en cualquiera de los tipos base estándar MapType). Para la mayoría de los usos, esta Projection estándar será suficiente, pero también puedes definir y usar tus propias proyecciones personalizadas.

Cómo implementar una proyección

Cuando implementes una proyección personalizada, tendrás que definir algunos aspectos:

  • Las fórmulas para asignar coordenadas de latitud y longitud a un plano cartesiano y viceversa. (La interfaz Projection solo admite transformaciones en coordenadas rectilíneas).
  • El tamaño de mosaico básico. Todos los mosaicos deben ser rectangulares.
  • El “tamaño del mundo” de un mapa con el mosaico básico establecido en el nivel de zoom 0. Ten en cuenta que para los mapas compuestos por un mosaico en el nivel de zoom 0, el tamaño del mundo y el tamaño de mosaico básico son idénticos.

Coordina transformaciones en proyecciones

Cada proyección proporciona dos métodos que se traducen entre estos dos sistemas de coordenadas, lo que te permite realizar conversiones entre coordenadas geográficas y mundiales:

  • El método Projection.fromLatLngToPoint() convierte un valor LatLng en una coordenada mundial. Este método se usa para posicionar superposiciones en el mapa (y el mapa en sí).
  • El método Projection.fromPointToLatLng() convierte una coordenada mundial en un valor LatLng. Este método se utiliza para convertir eventos como los clics que se producen en el mapa en coordenadas geográficas.

Google Maps interpreta que las proyecciones son rectilíneas.

En general, puedes usar una proyección para dos casos: a fin de crear un mapa del mundo o para crear un mapa de un área local. En el primer caso, debes asegurarte de que tu proyección también sea rectilínea y normal en todas las longitudes. Algunas proyecciones (en especial, las cónicas) pueden ser "normales a nivel local" (es decir, apuntar hacia el norte), pero pueden desviarse del verdadero norte. Por ejemplo, cuanto más se ubique el mapa en relación con la longitud de referencia, Puedes usar esta proyección de forma local, pero ten en cuenta que esta es necesariamente imprecisa y que los errores serán cada vez más evidentes a medida que la longitud de referencia se desvíe.

Selección de mosaicos de mapas en proyecciones

Las proyecciones no solo son útiles para determinar las posiciones de las ubicaciones o las superposiciones, sino también para posicionar los mosaicos del mapa en sí. La API de Maps JavaScript renderiza mapas básicos con una interfaz MapType, que debe declarar una propiedad projection para identificar la proyección del mapa y un método getTile() para recuperar mosaicos de mapas en función de valores de coordenadas de mosaicos. Las coordenadas de mosaicos se basan en el tamaño de mosaico básico (que debe ser rectangular) y el tamaño del mundo en tu mapa, que es el tamaño en píxeles del mundo del mapa en el nivel de zoom 0. (Para los mapas que constan de un mosaico en el nivel de zoom 0, el tamaño del mosaico y el tamaño del mundo son idénticos).

Debes definir el tamaño de mosaico básico dentro de la propiedad tileSize de MapType. Debes definir el tamaño del mundo de forma implícita dentro de los métodos fromLatLngToPoint() y fromPointToLatLng() de tu proyección.

Dado que la selección de imágenes depende de estos valores pasados, es útil nombrar imágenes que se puedan seleccionar de manera programática a partir de esos valores, como map_zoom_tileX_tileY.png.

En el siguiente ejemplo, se define un ImageMapType con la proyección Gall-Peters:

TypeScript

// This example defines an image map type using the Gall-Peters
// projection.
// https://en.wikipedia.org/wiki/Gall%E2%80%93Peters_projection

function initMap(): void {
  // Create a map. Use the Gall-Peters map type.
  const map = new google.maps.Map(
    document.getElementById("map") as HTMLElement,
    {
      zoom: 0,
      center: { lat: 0, lng: 0 },
      mapTypeControl: false,
    }
  );

  initGallPeters();
  map.mapTypes.set("gallPeters", gallPetersMapType);
  map.setMapTypeId("gallPeters");

  // Show the lat and lng under the mouse cursor.
  const coordsDiv = document.getElementById("coords") as HTMLElement;

  map.controls[google.maps.ControlPosition.TOP_CENTER].push(coordsDiv);
  map.addListener("mousemove", (event: google.maps.MapMouseEvent) => {
    coordsDiv.textContent =
      "lat: " +
      Math.round(event.latLng!.lat()) +
      ", " +
      "lng: " +
      Math.round(event.latLng!.lng());
  });

  // Add some markers to the map.
  map.data.setStyle((feature) => {
    return {
      title: feature.getProperty("name"),
      optimized: false,
    };
  });
  map.data.addGeoJson(cities);
}

let gallPetersMapType;

function initGallPeters() {
  const GALL_PETERS_RANGE_X = 800;
  const GALL_PETERS_RANGE_Y = 512;

  // Fetch Gall-Peters tiles stored locally on our server.
  gallPetersMapType = new google.maps.ImageMapType({
    getTileUrl: function (coord, zoom) {
      const scale = 1 << zoom;

      // Wrap tiles horizontally.
      const x = ((coord.x % scale) + scale) % scale;

      // Don't wrap tiles vertically.
      const y = coord.y;

      if (y < 0 || y >= scale) return "";

      return (
        "https://developers.google.com/maps/documentation/" +
        "javascript/examples/full/images/gall-peters_" +
        zoom +
        "_" +
        x +
        "_" +
        y +
        ".png"
      );
    },
    tileSize: new google.maps.Size(GALL_PETERS_RANGE_X, GALL_PETERS_RANGE_Y),
    minZoom: 0,
    maxZoom: 1,
    name: "Gall-Peters",
  });

  // Describe the Gall-Peters projection used by these tiles.
  gallPetersMapType.projection = {
    fromLatLngToPoint: function (latLng) {
      const latRadians = (latLng.lat() * Math.PI) / 180;
      return new google.maps.Point(
        GALL_PETERS_RANGE_X * (0.5 + latLng.lng() / 360),
        GALL_PETERS_RANGE_Y * (0.5 - 0.5 * Math.sin(latRadians))
      );
    },
    fromPointToLatLng: function (point, noWrap) {
      const x = point.x / GALL_PETERS_RANGE_X;
      const y = Math.max(0, Math.min(1, point.y / GALL_PETERS_RANGE_Y));

      return new google.maps.LatLng(
        (Math.asin(1 - 2 * y) * 180) / Math.PI,
        -180 + 360 * x,
        noWrap
      );
    },
  };
}

// GeoJSON, describing the locations and names of some cities.
const cities = {
  type: "FeatureCollection",
  features: [
    {
      type: "Feature",
      geometry: { type: "Point", coordinates: [-87.65, 41.85] },
      properties: { name: "Chicago" },
    },
    {
      type: "Feature",
      geometry: { type: "Point", coordinates: [-149.9, 61.218] },
      properties: { name: "Anchorage" },
    },
    {
      type: "Feature",
      geometry: { type: "Point", coordinates: [-99.127, 19.427] },
      properties: { name: "Mexico City" },
    },
    {
      type: "Feature",
      geometry: { type: "Point", coordinates: [-0.126, 51.5] },
      properties: { name: "London" },
    },
    {
      type: "Feature",
      geometry: { type: "Point", coordinates: [28.045, -26.201] },
      properties: { name: "Johannesburg" },
    },
    {
      type: "Feature",
      geometry: { type: "Point", coordinates: [15.322, -4.325] },
      properties: { name: "Kinshasa" },
    },
    {
      type: "Feature",
      geometry: { type: "Point", coordinates: [151.207, -33.867] },
      properties: { name: "Sydney" },
    },
    {
      type: "Feature",
      geometry: { type: "Point", coordinates: [0, 0] },
      properties: { name: "0°N 0°E" },
    },
  ],
};

declare global {
  interface Window {
    initMap: () => void;
  }
}
window.initMap = initMap;

JavaScript

// This example defines an image map type using the Gall-Peters
// projection.
// https://en.wikipedia.org/wiki/Gall%E2%80%93Peters_projection
function initMap() {
  // Create a map. Use the Gall-Peters map type.
  const map = new google.maps.Map(document.getElementById("map"), {
    zoom: 0,
    center: { lat: 0, lng: 0 },
    mapTypeControl: false,
  });

  initGallPeters();
  map.mapTypes.set("gallPeters", gallPetersMapType);
  map.setMapTypeId("gallPeters");

  // Show the lat and lng under the mouse cursor.
  const coordsDiv = document.getElementById("coords");

  map.controls[google.maps.ControlPosition.TOP_CENTER].push(coordsDiv);
  map.addListener("mousemove", (event) => {
    coordsDiv.textContent =
      "lat: " +
      Math.round(event.latLng.lat()) +
      ", " +
      "lng: " +
      Math.round(event.latLng.lng());
  });
  // Add some markers to the map.
  map.data.setStyle((feature) => {
    return {
      title: feature.getProperty("name"),
      optimized: false,
    };
  });
  map.data.addGeoJson(cities);
}

let gallPetersMapType;

function initGallPeters() {
  const GALL_PETERS_RANGE_X = 800;
  const GALL_PETERS_RANGE_Y = 512;

  // Fetch Gall-Peters tiles stored locally on our server.
  gallPetersMapType = new google.maps.ImageMapType({
    getTileUrl: function (coord, zoom) {
      const scale = 1 << zoom;
      // Wrap tiles horizontally.
      const x = ((coord.x % scale) + scale) % scale;
      // Don't wrap tiles vertically.
      const y = coord.y;

      if (y < 0 || y >= scale) return "";
      return (
        "https://developers.google.com/maps/documentation/" +
        "javascript/examples/full/images/gall-peters_" +
        zoom +
        "_" +
        x +
        "_" +
        y +
        ".png"
      );
    },
    tileSize: new google.maps.Size(GALL_PETERS_RANGE_X, GALL_PETERS_RANGE_Y),
    minZoom: 0,
    maxZoom: 1,
    name: "Gall-Peters",
  });
  // Describe the Gall-Peters projection used by these tiles.
  gallPetersMapType.projection = {
    fromLatLngToPoint: function (latLng) {
      const latRadians = (latLng.lat() * Math.PI) / 180;
      return new google.maps.Point(
        GALL_PETERS_RANGE_X * (0.5 + latLng.lng() / 360),
        GALL_PETERS_RANGE_Y * (0.5 - 0.5 * Math.sin(latRadians))
      );
    },
    fromPointToLatLng: function (point, noWrap) {
      const x = point.x / GALL_PETERS_RANGE_X;
      const y = Math.max(0, Math.min(1, point.y / GALL_PETERS_RANGE_Y));
      return new google.maps.LatLng(
        (Math.asin(1 - 2 * y) * 180) / Math.PI,
        -180 + 360 * x,
        noWrap
      );
    },
  };
}

// GeoJSON, describing the locations and names of some cities.
const cities = {
  type: "FeatureCollection",
  features: [
    {
      type: "Feature",
      geometry: { type: "Point", coordinates: [-87.65, 41.85] },
      properties: { name: "Chicago" },
    },
    {
      type: "Feature",
      geometry: { type: "Point", coordinates: [-149.9, 61.218] },
      properties: { name: "Anchorage" },
    },
    {
      type: "Feature",
      geometry: { type: "Point", coordinates: [-99.127, 19.427] },
      properties: { name: "Mexico City" },
    },
    {
      type: "Feature",
      geometry: { type: "Point", coordinates: [-0.126, 51.5] },
      properties: { name: "London" },
    },
    {
      type: "Feature",
      geometry: { type: "Point", coordinates: [28.045, -26.201] },
      properties: { name: "Johannesburg" },
    },
    {
      type: "Feature",
      geometry: { type: "Point", coordinates: [15.322, -4.325] },
      properties: { name: "Kinshasa" },
    },
    {
      type: "Feature",
      geometry: { type: "Point", coordinates: [151.207, -33.867] },
      properties: { name: "Sydney" },
    },
    {
      type: "Feature",
      geometry: { type: "Point", coordinates: [0, 0] },
      properties: { name: "0°N 0°E" },
    },
  ],
};

window.initMap = initMap;
Ver ejemplo

Probar la muestra