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 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 controlar la recuperación y liberación de mosaicos, y las propiedades que definen su comportamiento visual.
El funcionamiento interno de los tipos de mapas en 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 los mapas con diseños o definir tus propios mosaicos mediante los 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 API de Maps JavaScript. Además de los mosaicos de mapas de rutas "pintados", la API de Maps JavaScript también admite otros tipos de mapas.
Los siguientes tipos de mapas están disponibles en la API de Maps JavaScript:
roadmap
muestra la vista del mapa de ruta predeterminado. 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.
Para modificar el tipo de mapa que usa el Map
, debes configurar 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
.
Para configurar el mapTypeId
durante la construcción, usa este código:
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);
Para modificar el mapTypeId
de forma dinámica, usa este código:
map.setMapTypeId('terrain');
Ten en cuenta que, en realidad, no configuras el tipo de mapa de forma directa, sino que configuras su mapTypeId
para que haga 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. Las imágenes de alta resolución proporcionan vistas en perspectiva de cada punto cardinal (norte, sur, este y oeste). Estas imágenes se encuentran disponibles en niveles de zoom más altos para los tipos de mapas admitidos.
En la siguiente imagen, se muestra una vista en perspectiva de 45° de la ciudad de Nueva York:
Los tipos de mapas satellite
y hybrid
admiten imágenes a 45° en niveles de zoom altos (12 o más) cuando están disponibles. Si un usuario acerca una ubicación para la que existen estas imágenes, las vistas de estos tipos de mapas se modifican automáticamente de la siguiente manera:
- Se reemplazan las imágenes satelitales o híbridas por imágenes que ofrecen una perspectiva de 45°, centradas en la ubicación actual. De manera predeterminada, esas vistas se orientan hacia el norte. Si el usuario aleja el mapa, 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, se muestra el mapa base con vista cenital (0°) de forma predeterminada, a menos que la
tilt
se haya establecido en 45. - En los niveles de zoom 18 y superiores, se muestra el mapa base a 45°, a menos que la
tilt
se haya establecido en 0. - El control de rotación se vuelve visible. Este control ofrece 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
enfalse
.
Cuando el usuario aleja un tipo de mapa en el que se muestran imágenes a 45°, estos cambios se revierten y se restablecen los tipos de mapas originales.
Cómo habilitar e inhabilitar imágenes a 45°
Para inhabilitar las imágenes a 45°, llama a setTilt(0)
en el objeto Map
. Para habilitar las imágenes a 45° para los tipos de mapas admitidos, llama a setTilt(45)
. El método getTilt()
de Map
siempre reflejará la tilt
actual que se muestra en el mapa. Si configuras una tilt
en un mapa y, luego, quitas esa tilt
(por ejemplo, al alejar el mapa), el método getTilt()
del mapa mostrará 0
.
Importante: Las imágenes a 45° solo son compatibles con los mapas de trama; estas imágenes no pueden usarse con mapas de vectores.
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;
Prueba la muestra
Cómo rotar imágenes a 45°
Las imágenes a 45° son, en realidad, colecciones de imágenes correspondientes a cada punto cardinal (norte, sur, este, oeste). Una vez que el mapa muestre imágenes a 45°, puedes orientarlas hacia uno de sus puntos cardinales. Para eso, llama a setHeading()
en el objeto Map
y pásale un valor numérico expresado en grados con el norte como punto de partida.
En el siguiente ejemplo, se muestra un mapa aéreo que 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;
Prueba la muestra
Cómo modificar el registro de tipos de mapas
El mapTypeId
de un mapa es un identificador de cadena 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 de Map.
Las lecturas no se hacen directamente desde el registro de tipos de mapas En lugar de ello, se modifica el registro y se agregan tipos de mapas personalizados que se asocian con un identificador de cadena que elijas. No se pueden modificar ni alterar los tipos de mapas básicos (aunque se pueden quitar del mapa si modificas el aspecto de las mapTypeControlOptions
asociadas).
El siguiente código permite configurar el mapa para que se muestren únicamente dos tipos de mapas en las mapTypeControlOptions
del mapa y modificar el registro para agregar la asociación con su 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 diseños
StyledMapType
te permite personalizar la presentación de los mapas base estándares de Google y cambiar la visualización de elementos como rutas, parques y áreas con edificaciones para reflejar un diseño diferente del que se usa en el tipo de mapa predeterminado.
Para obtener más información sobre StyledMapType
, consulta la guía sobre mapas con diseño.
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 mosaicos o imágenes de mapas.
Existen varias implementaciones posibles de tipos de mapas en 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 base. Estos tipos de mapas actúan y se comportan como aquellos existentes que son los predeterminados:
roadmap
,satellite
,hybrid
yterrain
. Puedes agregar tu tipo de mapa personalizado a un array demapTypes
de Map para permitir que la IU de la API de Maps JavaScript trate tu tipo de mapa personalizado como un tipo de mapa estándar (si lo incluyes en el control MapType, por ejemplo). - Superposiciones de mosaicos de imágenes que se muestran sobre los tipos de mapas base existentes. Generalmente, estos tipos de mapas se usan para aumentar un tipo de mapa existente para que se muestre información adicional y a menudo están limitados a ubicaciones o niveles de zoom específicos. Ten en cuenta que estos mosaicos pueden ser transparentes. Esto te permite agregar elementos a mapas existentes.
- Tipos de mapas sin imágenes, que te permiten manipular la visualización de información en el nivel más esencial.
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.
Interfaz de MapType
Antes de crear clases que implementen MapType
, es importante comprender la forma en que Google Maps determina las coordenadas y decide qué partes del mapa mostrar. Debes implementar una lógica similar para cualquier tipo de mapa base o de superposición.
Consulta 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 para tus tipos de mapas cuando determina que debe mostrar mosaicos de mapas con el viewport y el nivel de zoom actuales. Debes controlar estas solicitudes para decidir qué mosaico se 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 tipogoogle.maps.Size
). Debe tener forma rectangular, aunque no tiene que ser necesariamente cuadrado.maxZoom
(obligatorio): Especifica el nivel de zoom máximo en el que se deben mostrar los mosaicos de este tipo de mapa.minZoom
(opcional): Especifica el nivel de zoom mínimo en el que se deben mostrar los mosaicos de este tipo de mapa. De forma predeterminada, este valor es0
, 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 en un control MapType. (Consulta Cómo agregar controlesMapType
a continuación).alt
(opcional): Especifica el texto alternativo de este tipo de mapa, que se muestra cuando un usuario coloca el cursor sobre este. Esta propiedad solo es necesaria si deseas que se pueda seleccionar este tipo de mapa en un control MapType. (Consulta Cómo agregar controlesMapType
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 un viewport determinado. El métodogetTile()
debe tener la siguiente firma:getTile(tileCoord:Point,zoom:number,ownerDocument:Document):Node
La API determina si necesita llamar a
getTile()
según las propiedadestileSize
,minZoom
ymaxZoom
deMapType
y el nivel de zoom y el viewport actuales del mapa. El controlador de este método debe mostrar un elemento HTML en función de una coordenada que se pasó, el nivel de zoom y el elemento del DOM al cual debe adjuntarse 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)
Normalmente, debes quitar los elementos adjuntos a los mosaicos del mapa cuando los agregues al mapa. Por ejemplo, si adjuntaste objetos de escucha de eventos a superposiciones de mosaicos del mapa, debes quitarlos allí.
El método getTile()
actúa como el controlador principal para determinar qué mosaicos cargar en un viewport determinado.
Tipos de mapas base
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 base. Es recomendable que la API trate a los MapType
personalizados como a cualquier otro tipo de mapa base existente (ROADMAP
, TERRAIN
, etc.). Para ello, agrega tu MapType
personalizado a la propiedad mapTypes
de Map
. Esta propiedad es de tipo MapTypeRegistry
.
El siguiente código crea un 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;
Prueba la muestra
Tipos de mapas de superposición
Algunos tipos de mapas están diseñados para funcionar encima de tipos de mapas existentes. Estos tipos de mapas tienen capas transparentes que muestran lugares de interés o datos adicionales a los usuarios.
En estos casos, no es recomendable 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 MapType
existente directamente mediante la propiedad overlayMapTypes
de Map
. Esta propiedad contiene un MVCArray
de MapType
. Todos los tipos de mapas (base y de superposición) se renderizan en la capa mapPane
. Los tipos de mapas de superposición aparecen encima del mapa base al que se adjuntan, en el orden en que aparecen en el array Map.overlayMapTypes
(las superposiciones con valores de índices más altos se muestran encima de las superposiciones con valores de índices 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 implements google.maps.MapType { tileSize: google.maps.Size; alt: string|null = null; maxZoom: number = 17; minZoom: number = 0; name: string|null = null; projection: google.maps.Projection|null = null; radius: number = 6378137; 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. const coordMapType = new CoordMapType(new google.maps.Size(256, 256)) map.overlayMapTypes.insertAt( 0, coordMapType ); } 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; alt = null; maxZoom = 17; minZoom = 0; name = null; projection = null; radius = 6378137; 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. const coordMapType = new CoordMapType(new google.maps.Size(256, 256)); map.overlayMapTypes.insertAt(0, coordMapType); } window.initMap = initMap;
Prueba la muestra
Tipos de mapas de imágenes
La implementación de un MapType
para que actúe como un tipo de mapa base puede demandar mucho tiempo y esfuerzo. 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 del objeto ImageMapTypeOptions
que define las siguientes propiedades obligatorias:
tileSize
(obligatorio): Especifica el tamaño del mosaico (del tipogoogle.maps.Size
). Debe tener forma rectangular, aunque no tiene que ser necesariamente cuadrado.getTileUrl
(obligatorio): Especifica la función, generalmente proporcionada 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.
En el siguiente código, se implementa un ImageMapType
básico con los mosaicos de luna de Google. En el ejemplo, se emplea una función de normalización para garantizar que los mosaicos se repitan en el eje X (y no en el 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;
Prueba la muestra
Proyecciones
La Tierra es una esfera tridimensional (aproximadamente), mientras que un mapa es una superficie bidimensional plana. 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 simples, una proyección se puede definir como una asignación de valores de latitud y longitud en las coordenadas del mapa de la proyección.
Las proyecciones de 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 las coordenadas terrestres (objetos LatLng
) al sistema de coordenadas mundiales de la clase Projection
, y viceversa. Google Maps usa la proyección Mercator para crear mapas a partir de datos geográficos y convertir eventos del mapa en coordenadas geográficas. Para obtener esta proyección, llama a getProjection()
en el Map
(o cualquiera de los tipos de MapType
base estándares). 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
Al implementar una proyección personalizada, deberás definir algunos aspectos:
- Las fórmulas para asignar coordenadas de latitud y longitud al 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 conjunto de mosaicos básicos en el nivel de zoom 0. Ten en cuenta que, en el caso de los mapas compuestos por un mosaico en el nivel de zoom 0, el tamaño del mundo y el tamaño del mosaico básico son idénticos.
Cómo transformar coordenadas en proyecciones
Cada proyección proporciona dos métodos que permiten traducir de uno de estos dos sistemas de coordenadas al otro, de manera tal que puedes convertir coordenadas geográficas en mundiales y viceversa:
- El método
Projection.fromLatLngToPoint()
convierte un valorLatLng
en una coordenada mundial. Se usa para posicionar superposiciones en el mapa (y el mapa en sí). - El método
Projection.fromPointToLatLng()
convierte una coordenada mundial en un valorLatLng
. Se usa para convertir eventos, como los clics en el mapa, en coordenadas geográficas.
Google Maps interpreta que las proyecciones son rectilíneas.
En general, puedes usar una proyección en dos casos: para 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 desviarse del norte geográfico; por ejemplo, cuanto más lejos se posiciona el mapa respecto de una longitud de referencia determinada. Puedes usar esa proyección a nivel local, pero debes tener en cuenta que esta es necesariamente imprecisa y que los errores de transformación serán cada vez más notorios cuanta más desviación exista respecto de la longitud de referencia.
Selección de mosaicos de mapas en las proyecciones
Las proyecciones no solo son útiles para determinar las posiciones de ubicaciones o superposiciones, sino también para posicionar los propios mosaicos del mapa.
La API de Maps JavaScript renderiza mapas base con una interfaz MapType
, que debe declarar no solo una propiedad projection
para identificar la proyección del mapa, sino también un método getTile()
a fin de recuperar mosaicos de mapas en función de los valores de coordenadas de mosaicos. Las coordenadas de mosaicos se basan en el tamaño de tu mosaico básico (que debe ser rectangular) y el "tamaño del mundo" de tu mapa, que equivale al tamaño en píxeles de tu mapa del mundo en el nivel de zoom 0. Ten en cuenta que, en el caso de 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.
Debes definir el tamaño del mosaico básico en la propiedad tileSize
de tu MapType
. Debes definir el tamaño del mundo de forma implícita en los métodos fromLatLngToPoint()
y fromPointToLatLng()
de tu proyección.
Dado que la selección de imágenes depende de los valores que se pasan, 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") as string, 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;