Tipos de mapa

Mantenha tudo organizado com as coleções Salve e categorize o conteúdo com base nas suas preferências.
Selecione a plataforma: Android iOS JavaScript

Neste documento, falamos sobre os tipos de mapa que podem ser exibidos usando a API Maps JavaScript. A API usa um objeto MapType para armazenar informações sobre esses mapas. Uma MapType é uma interface que define a exibição e o uso de blocos de mapa e a conversão de sistemas de coordenadas de coordenadas de tela em coordenadas mundiais (no mapa). Cada MapType precisa conter alguns métodos para processar a recuperação e a liberação de blocos e propriedades que definem o comportamento visual dele.

O funcionamento interno dos tipos de mapas na API Maps JavaScript é um tópico avançado. A maioria dos desenvolvedores pode usar os tipos de mapa básicos indicados abaixo. No entanto, você também pode modificar a apresentação de tipos de mapa existentes usando mapas estilizados ou definir seus próprios blocos de mapas usando tipos de mapa personalizados. Ao fornecer tipos de mapas personalizados, você precisará entender como modificar o Registro de tipos de mapas do mapa.

Tipos de mapa básicos

Há quatro tipos de mapa disponíveis na API Maps JavaScript. Além dos blocos de mapa de vias conhecidos "pintados", a API Maps JavaScript também é compatível com outros tipos de mapas.

Os seguintes tipos de mapa estão disponíveis na API Maps JavaScript:

  • roadmap exibe a visualização de mapa padrão de vias. Esse é o tipo de mapa padrão.
  • satellite exibe imagens de satélite do Google Earth.
  • hybrid exibe uma combinação de visualizações normais e de satélite.
  • terrain exibe um mapa físico com base nas informações do terreno.

Para modificar o tipo de mapa em uso pelo Map, defina a propriedade mapTypeId, seja no construtor, definindo o objeto Map options, ou chamando o método setMapTypeId() do mapa. A propriedade mapTypeID é definida como roadmap por padrão.

Como definir mapTypeId na construção:

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);

Modifique o mapTypeId dinamicamente:

map.setMapTypeId('terrain');

Você não define o tipo de mapa do mapa diretamente, mas define o mapTypeId para referenciar um MapType usando um identificador. A API Maps JavaScript usa um registro de tipo de mapa, explicado abaixo, para gerenciar essas referências.

Imagens de 45°

A API Maps JavaScript é compatível com imagens especiais de 45° para determinados locais. Essas imagens de alta resolução fornecem visualizações em perspectiva para cada direção cardeal (norte, sul, leste, oeste). Essas imagens estão disponíveis em níveis mais altos de zoom para os tipos de mapa compatíveis.

A imagem a seguir mostra uma vista em perspectiva de 45° da cidade de Nova York:

Os tipos de mapa satellite e hybrid são compatíveis com imagens de 45° em níveis altos de zoom (12 e mais recentes), quando disponíveis. Se o usuário aumentar o zoom em um local no qual essas imagens existem, esses tipos de mapa mudarão automaticamente as visualizações da seguinte maneira:

  • As imagens híbridas ou de satélite são substituídas por imagens que proporcionam uma perspectiva de 45°, centralizada na localização atual. Por padrão, essas visualizações são orientadas para o norte. Se o usuário diminuir o zoom, a imagem padrão de satélite ou híbrida vai aparecer novamente. O comportamento varia de acordo com o nível de zoom e o valor de tilt:
    • Entre os níveis de zoom 12 e 18, o mapa básico de cima para baixo (0°) é exibido por padrão, a menos que tilt esteja definido como 45.
    • Em níveis de zoom de 18 ou maiores, o mapa básico de 45° é exibido, a menos que tilt seja definido como 0.
  • O controle de rotação fica visível. O controle de rotação oferece opções que permitem ao usuário alternar inclinação e girar a visualização em incrementos de 90° em qualquer direção. Para ocultar o controle de rotação, defina rotateControl como false.

Diminuir o zoom de um tipo de mapa que exibe imagens a 45° reverte cada uma dessas mudanças, restabelecendo os tipos de mapa originais.

Ativar e desativar imagens de 45°

Para desativar as imagens de 45°, chame setTilt(0) no objeto Map. Para ativar imagens em 45° para os tipos de mapa compatíveis, chame setTilt(45). O método getTilt() do Map sempre reflete o tilt atual mostrado no mapa. Se você definir um tilt em um mapa e depois remover esse tilt (diminuindo o zoom no mapa, por exemplo), o método getTilt() do mapa retornará 0.

Importante:imagens de 45° só são compatíveis com mapas de varredura. Elas não podem ser usadas com mapas vetoriais.

O exemplo a seguir exibe uma vista de 45° da cidade de Nova 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 exemplo

Testar amostra

Veja um exemplo.

Girar imagens de 45°

As imagens de 45° consistem em um conjunto de imagens para cada direção cardeal (norte, sul, leste, oeste). Quando o mapa estiver exibindo uma imagem de 45°, você poderá orientá-la em uma direção cardinal chamando setHeading() no objeto Map, transmitindo um valor numérico expresso em graus em relação ao Norte.

O exemplo a seguir mostra um mapa aéreo e gira automaticamente o mapa a cada três segundos quando o botão é clicado:

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 exemplo

Testar amostra

Veja um exemplo.

Modificar o registro de tipo de mapa

Um mapTypeId do mapa é um identificador de string usado para associar um MapType a um valor exclusivo. Cada objeto Map mantém um MapTypeRegistry que contém a coleção de MapTypes disponíveis para esse mapa. Esse registro é usado para selecionar os tipos de mapas que estão disponíveis no controle de PPIDs do Map, por exemplo.

O registro de tipos de mapa não é lido diretamente. Em vez disso, modifique o registro adicionando tipos de mapas personalizados e associando-os a um identificador de strings de sua escolha. Não é possível modificar nem alterar os tipos básicos de mapa, mas você pode removê-los do mapa alterando a aparência do mapTypeControlOptions associado ao mapa.

O código a seguir define o mapa para mostrar apenas dois tipos no mapTypeControlOptions do mapa e modifica o registro para adicionar a associação com esse identificador à implementação real da interface 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 estilizados

O StyledMapType permite personalizar a apresentação dos mapas básicos padrão do Google, alterando a exibição visual desses elementos, como vias, parques e áreas construídas para refletir um estilo diferente do usado no tipo de mapa padrão.

Para mais informações sobre StyledMapType, consulte o guia sobre mapas estilizados.

Tipos de mapa personalizados

A API Maps JavaScript é compatível com a exibição e o gerenciamento de tipos de mapa personalizados. Assim, você pode implementar suas próprias imagens de mapa ou sobreposições de blocos.

Existem várias implementações de tipo de mapa possíveis na API Maps JavaScript:

  • Conjuntos de blocos padrão que consistem em imagens que, coletivamente, constituem mapas cartográficos completos. Esses conjuntos de blocos também são conhecidos como tipos de mapa básicos. Esses tipos de mapa funcionam e se comportam como os tipos de mapa padrão atuais: roadmap, satellite, hybrid e terrain. Você pode adicionar o tipo de mapa personalizado a uma matriz mapTypes do Map para permitir que a IU na API Maps JavaScript trate o tipo de mapa personalizado como um tipo padrão (por exemplo, incluindo no controle StreetView).
  • Sobreposições de bloco de imagem mostradas sobre os tipos de mapa básicos existentes. Geralmente, esses tipos de mapa são usados para aumentar o tipo de um mapa existente e exibir informações adicionais. Eles geralmente são restritos a locais e/ou níveis de zoom específicos. Esses blocos podem ser transparentes, permitindo que você adicione recursos a mapas existentes.
  • Tipos de mapa que não são de imagem, que permitem manipular a exibição de informações do mapa no nível mais fundamental.

Cada uma dessas opções depende da criação de uma classe que implementa a interface MapType. Além disso, a classe ImageMapType fornece alguns comportamentos integrados para simplificar a criação de tipos de mapas de imagens.

A interface MapType

Antes de criar classes que implementem MapType, é importante entender como o Google Maps determina as coordenadas e decide quais partes do mapa serão exibidas. Você precisa implementar uma lógica semelhante para todos os tipos de mapa básico ou de sobreposição. Leia o guia sobre coordenadas de mapa e bloco.

Os tipos de mapa personalizados precisam implementar a interface MapType. Essa interface especifica determinadas propriedades e métodos que permitem que a API inicie solicitações aos seus tipos de mapa quando a API determinar que ela precisa exibir blocos de mapa na janela de visualização e no nível de zoom atuais. Você processa essas solicitações para decidir qual bloco carregar.

Observação: você pode criar sua própria classe para implementar essa interface. Como alternativa, se você tiver imagens compatíveis, é possível usar a classe ImageMapType, que já implementa essa interface.

As classes que implementam a interface MapType exigem que você defina e preencha as seguintes propriedades:

  • tileSize (obrigatório) especifica o tamanho do bloco (do tipo google.maps.Size). Os tamanhos precisam ser retangulares, mas não precisam ser quadrados.
  • maxZoom (obrigatório) especifica o nível de zoom máximo em que os blocos desse tipo de mapa serão exibidos.
  • minZoom (opcional) especifica o nível mínimo de zoom em que o bloco do tipo de mapa será exibido. Por padrão, esse valor é 0, indicando que não há nível mínimo de zoom.
  • name (opcional) especifica o nome desse tipo de mapa. Essa propriedade só é necessária se você quiser que esse tipo de mapa seja selecionável em um controle de lookback. Consulte Adicionar controles MapType abaixo.
  • alt (opcional) especifica o texto alternativo para esse tipo de mapa, exibido como um texto aberto. Essa propriedade só é necessária se você quiser que esse tipo de mapa seja selecionável em um controle de gclid. Consulte Como adicionar controles MapType abaixo.

Além disso, as classes que implementam a interface MapType precisam implementar os seguintes métodos:

  • getTile() (obrigatório) é chamado sempre que a API determina que o mapa precisa exibir novos blocos para a janela de visualização especificada. O método getTile() precisa ter a seguinte assinatura:

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

    A API determina se é necessário chamar getTile() com base nas propriedades tileSize, minZoom e maxZoom do MapType e da janela de visualização e do nível de zoom atuais do mapa. O gerenciador desse método precisa retornar um elemento HTML conforme a coordenada transmitida, o nível de zoom e o elemento DOM em que a imagem do bloco será anexada.

  • releaseTile() (opcional) é chamado sempre que a API determina que o mapa precisa remover um bloco que sai da visualização. Esse método precisa ter a seguinte assinatura:

    releaseTile(tile:Node)

    Normalmente, você deve processar a remoção de todos os elementos que foram anexados aos blocos de mapa após a adição ao mapa. Por exemplo, se você anexou listeners de eventos para mapear sobreposições de blocos, será necessário removê-los aqui.

O método getTile() atua como o principal controlador para determinar quais blocos serão carregados em uma determinada janela de visualização.

Tipos de mapa básicos

Os tipos de mapa que você cria dessa maneira podem ser autônomos ou combinados com outros tipos de mapa como sobreposições. Os tipos de mapa autônomos são conhecidos como tipos de mapa base. Você pode fazer com que a API trate esses MapTypes personalizados da mesma forma que faria com qualquer outro tipo de mapa básico (ROADMAP, TERRAIN etc.). Para isso, adicione seu MapType personalizado à propriedade mapTypes de Map' Esta propriedade é do tipo MapTypeRegistry.

O código abaixo cria um MapType base para exibir as coordenadas de um bloco de mapas e exibe um contorno dos blocos:

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 exemplo

Testar amostra

Tipos de mapa de sobreposição

Alguns tipos de mapa foram projetados para funcionar por cima dos tipos de mapa existentes. Esses tipos de mapa podem ter camadas transparentes indicando pontos de interesse ou mostrando dados adicionais ao usuário.

Nesses casos, você não quer que o tipo de mapa seja tratado como uma entidade separada, mas como uma sobreposição. Para fazer isso, adicione o tipo de mapa a um MapType diretamente usando a propriedade overlayMapTypes do Map. Essa propriedade contém MVCArray de MapTypes. Todos os tipos de mapa (base e sobreposição) são renderizados na camada mapPane. Os tipos de mapa de sobreposição aparecem sobre o mapa básico em que estão anexados, na ordem em que aparecem na matriz Map.overlayMapTypes. As sobreposições com valores de índice mais altos são exibidas na frente de sobreposições com valores de índice mais baixos.

O exemplo a seguir é idêntico ao anterior, mas nós criamos uma sobreposição de blocos MapType sobre o 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;
Ver exemplo

Testar amostra

Tipos de mapa de imagem

Implementar um MapType para atuar como um tipo de mapa básico pode ser uma tarefa demorada e demorada. A API fornece uma classe especial que implementa a interface MapType para os tipos de mapa mais comuns: tipos de mapa que consistem em blocos formados por arquivos de imagem única.

Essa classe, ImageMapType, é construída usando uma especificação de objeto ImageMapTypeOptions que define as seguintes propriedades obrigatórias:

  • tileSize (obrigatório) especifica o tamanho do bloco (do tipo google.maps.Size). Os tamanhos precisam ser retangulares, mas não precisam ser quadrados.
  • getTileUrl (obrigatório) especifica a função, normalmente fornecida como um literal de função in-line, para lidar com a seleção do bloco de imagem adequado com base nas coordenadas mundiais e no nível de zoom informados.

O código a seguir implementa um ImageMapType básico usando os blocos da Lua do Google. O exemplo usa uma função de normalização para garantir que os blocos se repitam ao longo do eixo x, mas não ao longo do eixo y do 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 exemplo

Testar amostra

Projeções

A Terra é uma esfera tridimensional (aproximadamente), enquanto um mapa é uma superfície bidimensional plana. O mapa que você vê na API Maps JavaScript, como qualquer mapa plano da Terra, é uma projeção dessa esfera em uma superfície plana. De maneira simplificada, uma projeção pode ser definida como um mapeamento de valores de latitude/longitude em coordenadas no mapa da projeção.

As projeções na API Maps JavaScript precisam implementar a interface Projection. Uma implementação de Projection não pode fornecer apenas um mapeamento de um sistema de coordenadas para outro, mas um mapeamento bidirecional. Ou seja, é preciso definir como traduzir as coordenadas do Google Earth (objetos LatLng) para o sistema de coordenadas mundiais da classe Projection e vice-versa. O Google Maps usa a projeção de Mercator para criar mapas com base nos dados geográficos e converter eventos no mapa em coordenadas geográficas. Você pode conseguir essa projeção chamando getProjection() no Map (ou qualquer um dos tipos base MapType padrão). Para a maioria dos usos, este Projection padrão será suficiente, mas você também pode definir e usar suas próprias projeções personalizadas.

Implementar uma projeção

Ao implementar uma projeção personalizada, você precisará definir alguns itens:

  • As fórmulas para mapear coordenadas de latitude e longitude em um plano cartesiano e vice-versa. A interface Projection é compatível apenas com transformações em coordenadas retilíneas.
  • O tamanho de bloco básico. Todos os blocos precisam ser retangulares.
  • O "tamanho do mundo" de um mapa usando o bloco base é definido como zoom de nível zero. Para mapas que consistem em um bloco com zoom 0, o tamanho do mundo e o tamanho do bloco base são idênticos.

Transformações de coordenadas em projeções

Cada projeção oferece dois métodos que se traduzem entre esses dois sistemas de coordenadas, permitindo a conversão entre coordenadas geográficas e mundiais:

  • O método Projection.fromLatLngToPoint() converte um valor LatLng em uma coordenada mundial. Esse método é usado para posicionar sobreposições no mapa e posicionar o próprio mapa.
  • O método Projection.fromPointToLatLng() converte uma coordenada mundial em um valor LatLng. Esse método é usado para converter eventos, por exemplo, cliques que acontecem no mapa, em coordenadas geográficas.

O Google Maps assume que as projeções são retilíneas.

Geralmente, é possível usar uma projeção para dois casos: para criar um mapa do mundo ou um mapa de uma área local. No primeiro caso, é necessário garantir que sua projeção também seja retilínea e normal em todas as longitudes. Algumas projeções (especialmente projeções cônicas) podem ser "localmente normais" (ou seja, apontar para o norte), mas se desviar do norte verdadeiro. Por exemplo, quanto mais distante o mapa estiver posicionado em relação a alguma longitude de referência. É possível usar essa projeção localmente, mas esteja ciente de que a projeção é necessariamente imprecisa e os erros de transformação se tornarão cada vez mais aparentes quanto mais longe da longitude de referência que você desviar.

Seleção de blocos de mapa em projeções

As projeções não são úteis apenas para determinar as posições de locais ou sobreposições, mas para posicionar os próprios blocos de mapa. A API Maps JavaScript renderiza mapas de base usando uma interface MapType, que precisa declarar uma propriedade projection para identificar a projeção do mapa e um método getTile() para recuperar blocos de mapa com base em valores de coordenadas de bloco. As coordenadas de bloco são baseadas no tamanho básico do bloco (que precisa ser retangular) e no tamanho real do mapa, que é o tamanho de pixel do seu mapa no nível de zoom 0. Nos mapas que consistem em um bloco com zoom 0, o tamanho do bloco e o tamanho do mundo são idênticos.

Você define o tamanho do bloco base na propriedade tileSize de MapType. Você define o tamanho do mundo implicitamente nos métodos fromLatLngToPoint() e fromPointToLatLng() da projeção.

Como a seleção de imagens depende desses valores transmitidos, é útil nomear as imagens que podem ser selecionadas programaticamente considerando esses valores transmitidos, como map_zoom_tileX_tileY.png.

O exemplo a seguir define um ImageMapType usando a projeção 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 exemplo

Testar amostra