Типы карт

Выберите платформу: Android iOS JavaScript

В этом документе рассматриваются типы карт, которые можно отображать с помощью API JavaScript Карт. API использует объект MapType для хранения информации об этих картах. MapType — это интерфейс, определяющий отображение и использование фрагментов карты, а также преобразование систем координат из экранных в мировые (на карте). Каждый MapType должен содержать несколько методов для обработки извлечения и освобождения фрагментов, а также свойства, определяющие его визуальное поведение.

Внутреннее устройство типов карт в Maps JavaScript API — сложная тема. Большинство разработчиков могут использовать базовые типы карт, перечисленные ниже. Однако вы также можете изменять представление существующих типов карт с помощью стилизованных карт или определять собственные фрагменты карт с помощью пользовательских типов карт . При предоставлении пользовательских типов карт необходимо понимать, как изменять реестр типов карт .

Основные типы карт

В Maps JavaScript API доступны четыре типа карт. Помимо привычных «рисованных» фрагментов дорожных карт, Maps JavaScript API поддерживает и другие типы карт.

В Maps JavaScript API доступны следующие типы карт:

  • roadmap отображает вид дорожной карты по умолчанию. Это тип карты по умолчанию.
  • satellite отображает спутниковые снимки Google Earth.
  • hybrid отображает смесь обычного и спутникового видов.
  • terrain отображает физическую карту, основанную на информации о рельефе местности.

Тип карты, используемый объектом Map , можно изменить, установив его свойство mapTypeId либо в конструкторе, установив объект Map options , либо вызвав метод setMapTypeId() карты. Свойство mapTypeID по умолчанию имеет значение roadmap .

Установка mapTypeId при построении:

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

Динамическое изменение mapTypeId :

map.setMapTypeId('terrain');

Обратите внимание, что вы не задаёте тип карты напрямую, а вместо этого указываете её mapTypeId для ссылки на MapType с помощью идентификатора. Для управления этими ссылками Maps JavaScript API использует реестр типов карт, описанный ниже.

Изображения под углом 45°

API JavaScript Карт поддерживает специальные изображения под углом 45° для некоторых мест. Эти изображения высокого разрешения предоставляют перспективные виды по всем сторонам света (север, юг, восток, запад). Эти изображения доступны в более высоком масштабе для поддерживаемых типов карт.

На следующем изображении показан вид Нью-Йорка под углом 45°:

satellite и hybrid карты поддерживают снимки с углом обзора 45° при высоком уровне масштабирования (12 и выше), где это возможно. Если пользователь увеличивает масштаб изображения объекта, для которого доступно такое изображение, эти карты автоматически изменяют вид следующим образом:

  • Спутниковое или гибридное изображение заменяется изображением с перспективой 45°, центрированным относительно текущего местоположения. По умолчанию такие виды ориентированы на север. При уменьшении масштаба снова отображается спутниковое или гибридное изображение по умолчанию. Поведение зависит от уровня масштабирования и значения tilt :
    • Между уровнями масштабирования 12 и 18 по умолчанию отображается базовая карта сверху вниз (0°), если только tilt не установлен на 45.
    • При уровнях масштабирования 18 и выше отображается базовая карта под углом 45°, если tilt не установлен на 0.
  • Элемент управления поворотом становится видимым. Он предоставляет возможности, позволяющие пользователю изменять наклон и поворачивать вид с шагом 90° в любом направлении. Чтобы скрыть элемент управления поворотом, установите для свойства rotateControl значение false .

Уменьшение масштаба карты, отображающей изображение под углом 45°, отменяет все эти изменения, восстанавливая исходные типы карт.

Включить и отключить 45°-изображения

Вы можете отключить отображение изображений под углом 45°, вызвав setTilt(0) объекта Map . Чтобы включить отображение изображений под углом 45° для поддерживаемых типов карт, вызовите setTilt(45) . Метод getTilt() объекта Map всегда будет учитывать текущий tilt , отображаемый на карте. Если вы установите tilt на карте, а затем устраните tilt (например, уменьшив масштаб), метод getTilt() объекта Map вернет 0 .

Важно: изображения под углом 45° поддерживаются только на растровых картах; эти изображения нельзя использовать с векторными картами.

В следующем примере показан вид на Нью-Йорк под углом 45°:

Машинопись

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;
Посмотреть пример

Попробуйте образец

Посмотреть пример .

Повернуть изображение на 45°

Изображение под углом 45° фактически представляет собой набор изображений для каждой стороны света (север, юг, восток, запад). После того, как на карте отображается изображение под углом 45°, вы можете сориентировать его по одной из сторон света, вызвав setHeading() объекта Map , передав числовое значение, выраженное в градусах от севера.

В следующем примере показана аэрофотоснимок, который автоматически поворачивается каждые 3 секунды при нажатии кнопки:

Машинопись

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;
Посмотреть пример

Попробуйте образец

Посмотреть пример .

Изменить реестр типов карт

mapTypeId карты — это строковый идентификатор, используемый для связывания MapType с уникальным значением. Каждый объект Map поддерживает реестр MapTypeRegistry , содержащий коллекцию доступных MapType для этой карты. Этот реестр используется, например, для выбора типов карт, доступных в элементе управления MapType объекта MapType.

Вы не считываете данные из реестра типов карт напрямую. Вместо этого вы изменяете реестр, добавляя пользовательские типы карт и связывая их с выбранным строковым идентификатором. Вы не можете изменять или модифицировать базовые типы карт (хотя можете удалить их из карты, изменив внешний вид соответствующего свойства mapTypeControlOptions ).

Следующий код настраивает карту на отображение только двух типов карт в mapTypeControlOptions карты и изменяет реестр, добавляя связь с этим идентификатором к фактической реализации интерфейса 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);

Стилизованные карты

StyledMapType позволяет настраивать отображение стандартных базовых карт Google, изменяя визуальное отображение таких элементов, как дороги, парки и застроенные территории, в соответствии со стилем, отличным от используемого в типе карты по умолчанию. StyledMapType влияет только на тип карты по умолчанию roadmap .

Дополнительную информацию о StyledMapType см. в разделе Использование встроенных объявлений стилей JSON .

Пользовательские типы карт

API Карт JavaScript поддерживает отображение и управление пользовательскими типами карт, позволяя вам реализовывать собственные изображения карт или наложения плиток.

В Maps JavaScript API существует несколько возможных реализаций типов карт:

  • Стандартные наборы тайлов , состоящие из изображений, которые в совокупности составляют полноценные картографические карты. Эти наборы тайлов также известны как базовые типы карт. Эти типы карт действуют аналогично существующим типам карт по умолчанию: roadmap , satellite , hybrid и terrain . Вы можете добавить свой собственный тип карты в массив mapTypes объекта Maps, чтобы пользовательский интерфейс в JavaScript API Карт мог обрабатывать ваш собственный тип карты как стандартный (например, включив его в элемент управления MapType).
  • Наложения фрагментов изображений, которые отображаются поверх существующих базовых типов карт. Как правило, эти типы карт используются для дополнения существующего типа карты для отображения дополнительной информации и часто ограничены определёнными местоположениями и/или уровнями масштабирования. Обратите внимание, что эти фрагменты могут быть прозрачными, что позволяет добавлять объекты на существующие карты.
  • Типы карт, не являющиеся изображениями, которые позволяют управлять отображением информации на самом фундаментальном уровне.

Каждый из этих вариантов предполагает создание класса, реализующего интерфейс MapType . Кроме того, класс ImageMapType предоставляет встроенные функции для упрощения создания типов карт изображений.

Интерфейс MapType

Прежде чем создавать классы, реализующие MapType , важно понимать, как Google Maps определяет координаты и определяет, какие части карты отображать. Аналогичную логику необходимо реализовать для любых базовых и оверлейных типов карт. Ознакомьтесь с руководством по координатам карт и тайлов .

Пользовательские типы карт должны реализовывать интерфейс MapType . Этот интерфейс определяет определённые свойства и методы, позволяющие API инициировать запросы к вашему типу(ам) карты, когда API определяет необходимость отображения фрагментов карты в текущей области просмотра и с текущим уровнем масштабирования. Вы обрабатываете эти запросы, чтобы определить, какой фрагмент загрузить.

Примечание : вы можете создать собственный класс для реализации этого интерфейса. Кроме того, если у вас есть совместимые изображения, вы можете использовать класс ImageMapType , который уже реализует этот интерфейс.

Классы, реализующие интерфейс MapType , требуют определения и заполнения следующих свойств:

  • tileSize (обязательно) определяет размер плитки (типа google.maps.Size ). Размеры должны быть прямоугольными, но не обязательно квадратными.
  • maxZoom (обязательно) указывает максимальный уровень масштабирования, при котором будут отображаться фрагменты карты этого типа.
  • minZoom (необязательно) задаёт минимальный уровень масштабирования для отображения фрагмента карты данного типа. По умолчанию это значение равно 0 , что означает отсутствие минимального уровня масштабирования.
  • name (необязательно) определяет имя данного типа карты. Это свойство необходимо только в том случае, если вы хотите, чтобы этот тип карты можно было выбрать в элементе управления MapType. (См. Параметры элемента управления .)
  • alt (необязательно) задаёт альтернативный текст для данного типа карты, отображаемый при наведении курсора. Это свойство необходимо только в том случае, если требуется, чтобы данный тип карты можно было выбрать в элементе управления MapType. (См. раздел «Параметры элемента управления »).

Кроме того, классы, реализующие интерфейс MapType , должны реализовать следующие методы:

  • Метод getTile() (обязательный) вызывается всякий раз, когда API определяет, что карте необходимо отобразить новые тайлы для заданной области просмотра. Метод getTile() должен иметь следующую сигнатуру:

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

    API определяет необходимость вызова метода getTile() на основе свойств tileSize , minZoom и maxZoom объекта MapType , а также текущего размера области просмотра и уровня масштабирования карты. Обработчик этого метода должен возвращать HTML-элемент с переданными координатами, уровнем масштабирования и элементом DOM, к которому добавляется изображение плитки.

  • Метод releaseTile() (необязательный) вызывается всякий раз, когда API определяет, что необходимо удалить тайл карты, поскольку он выпадает из поля зрения. Этот метод должен иметь следующую сигнатуру:

    releaseTile(tile:Node)

    Обычно необходимо обрабатывать удаление любых элементов, прикреплённых к фрагментам карты, при их добавлении на карту. Например, если вы прикрепили прослушиватели событий к слоям фрагментов карты, их следует удалить здесь.

Метод getTile() действует как основной контроллер, определяющий, какие плитки следует загружать в заданной области просмотра.

Базовые типы карт

Типы карт, создаваемые таким образом, могут быть как самостоятельными, так и комбинироваться с другими типами карт в качестве наложений. Отдельные типы карт называются базовыми типами карт . API может потребоваться обрабатывать такие пользовательские MapType так же, как и любые другие существующие базовые типы карт ( ROADMAP , TERRAIN и т. д.). Для этого добавьте свой пользовательский MapType к свойству mapTypes объекта Map . Это свойство имеет тип MapTypeRegistry .

Следующий код создает базовый MapType для отображения координат фрагментов карты и рисует контур фрагментов:

Машинопись

/*
 * 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;
Посмотреть пример

Попробуйте образец

Типы наложенных карт

Некоторые типы карт предназначены для работы поверх существующих. Такие типы карт могут иметь прозрачные слои, обозначающие объекты интереса или предоставляющие пользователю дополнительные данные.

В таких случаях требуется, чтобы тип карты рассматривался не как отдельная сущность, а как наложение. Это можно сделать, добавив тип карты непосредственно к существующему MapType с помощью свойства overlayMapTypes объекта Map . Это свойство содержит массив MVCArray объектов MapType . Все типы карт (базовые и наложения) визуализируются в слое mapPane . Наложения будут отображаться поверх базовой карты, к которой они прикреплены, в том порядке, в котором они указаны в массиве Map.overlayMapTypes (наложения с более высокими значениями индекса отображаются перед наложениями с более низкими значениями индекса).

Следующий пример идентичен предыдущему, за исключением того, что мы создали мозаичное наложение MapType поверх типа карты ROADMAP :

Машинопись

/*
 * 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;
Посмотреть пример

Попробуйте образец

Типы карт изображений

Реализация MapType в качестве базового типа карты может быть трудоёмкой и отнимающей много времени задачей. API предоставляет специальный класс, реализующий интерфейс MapType для наиболее распространённых типов карт: карт, состоящих из фрагментов, составленных из отдельных файлов изображений.

Этот класс, класс ImageMapType , создан с использованием спецификации объекта ImageMapTypeOptions , определяющей следующие обязательные свойства:

  • tileSize (обязательно) определяет размер плитки (типа google.maps.Size ). Размеры должны быть прямоугольными, но не обязательно квадратными.
  • getTileUrl (обязательно) указывает функцию, обычно предоставляемую как встроенный функциональный литерал, для обработки выбора нужного фрагмента изображения на основе предоставленных мировых координат и уровня масштабирования.

Следующий код реализует базовый тип ImageMapType с использованием тайлов луны Google. В примере используется функция нормализации, чтобы гарантировать повторение тайлов вдоль оси X, но не вдоль оси Y вашей карты.

Машинопись

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;
Посмотреть пример

Попробуйте образец

Прогнозы

Земля представляет собой трёхмерную сферу (приблизительно), а карта — плоскую двумерную поверхность. Карта, которую вы видите в Maps JavaScript API, как и любая плоская карта Земли, представляет собой проекцию этой сферы на плоскую поверхность. Проще говоря, проекцию можно определить как отображение значений широты и долготы в координаты на карте проекции.

Проекции в JavaScript API Карт должны реализовывать интерфейс Projection . Реализация Projection должна обеспечивать не только отображение из одной системы координат в другую, но и двунаправленное отображение. То есть, необходимо определить, как преобразовывать земные координаты (объекты LatLng ) в мировую систему координат класса Projection и обратно в земные координаты. Google Maps использует проекцию Меркатора для создания карт из географических данных и преобразования событий на карте в географические координаты. Эту проекцию можно получить, вызвав getProjection() для Map (или любого стандартного базового типа MapType ). Для большинства случаев достаточно стандартной Projection , но вы также можете определить и использовать свои собственные проекции.

Реализовать проекцию

При реализации пользовательской проекции вам необходимо определить несколько вещей:

  • Формулы для отображения координат широты и долготы в декартову плоскость и соответствующие формулы для отображения координат из декартовой плоскости в координаты широты и долготы. (Интерфейс Projection поддерживает только преобразования в прямолинейные координаты.)
  • Размер базовой плитки. Все плитки должны быть прямоугольными.
  • «Размер мира» карты, использующей базовый набор плиток с уровнем масштабирования 0. Обратите внимание, что для карт, состоящих из одной плитки с уровнем масштабирования 0, размер мира и базовый размер плитки идентичны.

Преобразования координат в проекциях

Каждая проекция предоставляет два метода преобразования между этими двумя системами координат, позволяя выполнять преобразование между географическими и мировыми координатами:

  • Метод Projection.fromLatLngToPoint() преобразует значение LatLng в мировые координаты. Этот метод используется для позиционирования наложений на карту (и позиционирования самой карты).
  • Метод Projection.fromPointToLatLng() преобразует мировые координаты в значение LatLng . Этот метод используется для преобразования событий, таких как щелчки мыши на карте, в географические координаты.

Google Maps предполагает, что проекции прямолинейны.

Как правило, проекцию можно использовать в двух случаях: для создания карты мира или карты местности. В первом случае необходимо убедиться, что проекция также прямолинейна и нормальна для всех долгот. Некоторые проекции (особенно конические) могут быть «локально нормальными» (т.е. указывать на север), но отклоняться от истинного севера, например, чем дальше расположена карта относительно какой-либо опорной долготы. Вы можете использовать такую ​​проекцию локально, но имейте в виду, что проекция неизбежно неточна, и ошибки преобразования будут тем более очевидными, чем дальше вы отклоняетесь от опорной долготы.

Выбор фрагмента карты в проекциях

Проекции полезны не только для определения положения точек или наложений, но и для позиционирования самих фрагментов карты. JavaScript API Карт отображает базовые карты с помощью интерфейса MapType , который должен объявлять как свойство projection для определения проекции карты, так и метод getTile() для получения фрагментов карты на основе значений координат фрагмента . Координаты фрагмента основаны как на базовом размере фрагмента (который должен быть прямоугольным), так и на «размере мира» вашей карты, который представляет собой размер мира карты в пикселях при уровне масштабирования 0. (Для карт, состоящих из одного фрагмента при уровне масштабирования 0, размер фрагмента и размер мира идентичны.)

Базовый размер плитки определяется свойством tileSize объекта MapType . Размер мира определяется неявно в методах fromLatLngToPoint() и fromPointToLatLng() вашей проекции.

Поскольку выбор изображения зависит от этих переданных значений, полезно называть изображения, которые можно выбирать программно, с учетом этих переданных значений, например map _ zoom _ tileX _ tileY .png .

В следующем примере определяется ImageMapType с использованием проекции Галла-Петерса :

Машинопись

// This example defines an image map type using the Gall-Peters
// projection.
// https://en.wikipedia.org/wiki/Gall%E2%80%93Peters_projection
const mapElement = document.querySelector("gmp-map") as google.maps.MapElement;
let innerMap;

async function initMap() {
  // Request the needed libraries.
  await google.maps.importLibrary("maps");

  // Create a map.
  innerMap = mapElement.innerMap;
  innerMap.setOptions({
    mapTypeControl: false,
  });

  // Set the Gall-Peters map type.
  initGallPeters();
  innerMap.mapTypes.set("gallPeters", gallPetersMapType);
  innerMap.setMapTypeId("gallPeters");

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

  innerMap.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.
  innerMap.data.setStyle((feature) => {
    return {
      title: feature.getProperty("name") as string,
      optimized: false,
    };
  });
  innerMap.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 (
        "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" },
    },
  ],
};

initMap();

JavaScript

// This example defines an image map type using the Gall-Peters
// projection.
// https://en.wikipedia.org/wiki/Gall%E2%80%93Peters_projection
const mapElement = document.querySelector("gmp-map");
let innerMap;
async function initMap() {
    // Request the needed libraries.
    await google.maps.importLibrary("maps");
    // Create a map.
    innerMap = mapElement.innerMap;
    innerMap.setOptions({
        mapTypeControl: false,
    });
    // Set the Gall-Peters map type.
    initGallPeters();
    innerMap.mapTypes.set("gallPeters", gallPetersMapType);
    innerMap.setMapTypeId("gallPeters");
    // Show the lat and lng under the mouse cursor.
    const coordsDiv = document.getElementById("coords");
    innerMap.addListener("mousemove", (event) => {
        coordsDiv.textContent =
            "lat: " +
                Math.round(event.latLng.lat()) +
                ", " +
                "lng: " +
                Math.round(event.latLng.lng());
    });
    // Add some markers to the map.
    innerMap.data.setStyle((feature) => {
        return {
            title: feature.getProperty("name"),
            optimized: false,
        };
    });
    innerMap.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 ("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" },
        },
    ],
};
initMap();
Посмотреть пример

Попробуйте образец

,
Выберите платформу: Android iOS JavaScript

В этом документе рассматриваются типы карт, которые можно отображать с помощью API JavaScript Карт. API использует объект MapType для хранения информации об этих картах. MapType — это интерфейс, определяющий отображение и использование фрагментов карты, а также преобразование систем координат из экранных в мировые (на карте). Каждый MapType должен содержать несколько методов для обработки извлечения и освобождения фрагментов, а также свойства, определяющие его визуальное поведение.

Внутреннее устройство типов карт в Maps JavaScript API — сложная тема. Большинство разработчиков могут использовать базовые типы карт, перечисленные ниже. Однако вы также можете изменять представление существующих типов карт с помощью стилизованных карт или определять собственные фрагменты карт с помощью пользовательских типов карт . При предоставлении пользовательских типов карт необходимо понимать, как изменять реестр типов карт .

Основные типы карт

В Maps JavaScript API доступны четыре типа карт. Помимо привычных «рисованных» фрагментов дорожных карт, Maps JavaScript API поддерживает и другие типы карт.

В Maps JavaScript API доступны следующие типы карт:

  • roadmap отображает вид дорожной карты по умолчанию. Это тип карты по умолчанию.
  • satellite отображает спутниковые снимки Google Earth.
  • hybrid отображает смесь обычного и спутникового видов.
  • terrain отображает физическую карту, основанную на информации о рельефе местности.

Тип карты, используемый объектом Map , можно изменить, установив его свойство mapTypeId либо в конструкторе, установив объект Map options , либо вызвав метод setMapTypeId() карты. Свойство mapTypeID по умолчанию имеет значение roadmap .

Установка mapTypeId при построении:

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

Динамическое изменение mapTypeId :

map.setMapTypeId('terrain');

Обратите внимание, что вы не задаёте тип карты напрямую, а вместо этого указываете её mapTypeId для ссылки на MapType с помощью идентификатора. Для управления этими ссылками Maps JavaScript API использует реестр типов карт, описанный ниже.

Изображения под углом 45°

API JavaScript Карт поддерживает специальные изображения под углом 45° для некоторых мест. Эти изображения высокого разрешения предоставляют перспективные виды по всем сторонам света (север, юг, восток, запад). Эти изображения доступны в более высоком масштабе для поддерживаемых типов карт.

На следующем изображении показан вид Нью-Йорка под углом 45°:

satellite и hybrid карты поддерживают снимки с углом обзора 45° при высоком уровне масштабирования (12 и выше), где это возможно. Если пользователь увеличивает масштаб изображения объекта, для которого доступно такое изображение, эти карты автоматически изменяют вид следующим образом:

  • Спутниковое или гибридное изображение заменяется изображением с перспективой 45°, центрированным относительно текущего местоположения. По умолчанию такие виды ориентированы на север. При уменьшении масштаба снова отображается спутниковое или гибридное изображение по умолчанию. Поведение зависит от уровня масштабирования и значения tilt :
    • Между уровнями масштабирования 12 и 18 по умолчанию отображается базовая карта сверху вниз (0°), если только tilt не установлен на 45.
    • При уровнях масштабирования 18 и выше отображается базовая карта под углом 45°, если tilt не установлен на 0.
  • Элемент управления поворотом становится видимым. Он предоставляет возможности, позволяющие пользователю изменять наклон и поворачивать вид с шагом 90° в любом направлении. Чтобы скрыть элемент управления поворотом, установите для свойства rotateControl значение false .

Уменьшение масштаба карты, отображающей изображение под углом 45°, отменяет все эти изменения, восстанавливая исходные типы карт.

Включить и отключить 45°-изображения

Вы можете отключить отображение изображений под углом 45°, вызвав setTilt(0) объекта Map . Чтобы включить отображение изображений под углом 45° для поддерживаемых типов карт, вызовите setTilt(45) . Метод getTilt() объекта Map всегда будет учитывать текущий tilt , отображаемый на карте. Если вы установите tilt на карте, а затем устраните tilt (например, уменьшив масштаб), метод getTilt() объекта Map вернет 0 .

Важно: изображения под углом 45° поддерживаются только на растровых картах; эти изображения нельзя использовать с векторными картами.

В следующем примере показан вид на Нью-Йорк под углом 45°:

Машинопись

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;
Посмотреть пример

Попробуйте образец

Посмотреть пример .

Повернуть изображение на 45°

Изображение под углом 45° фактически представляет собой набор изображений для каждой стороны света (север, юг, восток, запад). После того, как на карте отображается изображение под углом 45°, вы можете сориентировать его по одной из сторон света, вызвав setHeading() объекта Map , передав числовое значение, выраженное в градусах от севера.

В следующем примере показана аэрофотоснимок, который автоматически поворачивается каждые 3 секунды при нажатии кнопки:

Машинопись

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;
Посмотреть пример

Попробуйте образец

Посмотреть пример .

Изменить реестр типов карт

mapTypeId карты — это строковый идентификатор, используемый для связывания MapType с уникальным значением. Каждый объект Map поддерживает реестр MapTypeRegistry , содержащий коллекцию доступных MapType для этой карты. Этот реестр используется, например, для выбора типов карт, доступных в элементе управления MapType объекта MapType.

Вы не считываете данные из реестра типов карт напрямую. Вместо этого вы изменяете реестр, добавляя пользовательские типы карт и связывая их с выбранным строковым идентификатором. Вы не можете изменять или модифицировать базовые типы карт (хотя можете удалить их из карты, изменив внешний вид соответствующего свойства mapTypeControlOptions ).

Следующий код настраивает карту на отображение только двух типов карт в mapTypeControlOptions карты и изменяет реестр, добавляя связь с этим идентификатором к фактической реализации интерфейса 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);

Стилизованные карты

StyledMapType позволяет настраивать отображение стандартных базовых карт Google, изменяя визуальное отображение таких элементов, как дороги, парки и застроенные территории, в соответствии со стилем, отличным от используемого в типе карты по умолчанию. StyledMapType влияет только на тип карты по умолчанию roadmap .

Дополнительную информацию о StyledMapType см. в разделе Использование встроенных объявлений стилей JSON .

Пользовательские типы карт

API Карт JavaScript поддерживает отображение и управление пользовательскими типами карт, позволяя вам реализовывать собственные изображения карт или наложения плиток.

В Maps JavaScript API существует несколько возможных реализаций типов карт:

  • Стандартные наборы тайлов , состоящие из изображений, которые в совокупности составляют полноценные картографические карты. Эти наборы тайлов также известны как базовые типы карт. Эти типы карт действуют аналогично существующим типам карт по умолчанию: roadmap , satellite , hybrid и terrain . Вы можете добавить свой собственный тип карты в массив mapTypes объекта Maps, чтобы пользовательский интерфейс в JavaScript API Карт мог обрабатывать ваш собственный тип карты как стандартный (например, включив его в элемент управления MapType).
  • Наложения фрагментов изображений, которые отображаются поверх существующих базовых типов карт. Как правило, эти типы карт используются для дополнения существующего типа карты для отображения дополнительной информации и часто ограничены определёнными местоположениями и/или уровнями масштабирования. Обратите внимание, что эти фрагменты могут быть прозрачными, что позволяет добавлять объекты на существующие карты.
  • Типы карт, не являющиеся изображениями, которые позволяют управлять отображением информации на самом фундаментальном уровне.

Каждый из этих вариантов предполагает создание класса, реализующего интерфейс MapType . Кроме того, класс ImageMapType предоставляет встроенные функции для упрощения создания типов карт изображений.

Интерфейс MapType

Прежде чем создавать классы, реализующие MapType , важно понимать, как Google Maps определяет координаты и определяет, какие части карты отображать. Аналогичную логику необходимо реализовать для любых базовых и оверлейных типов карт. Ознакомьтесь с руководством по координатам карт и тайлов .

Пользовательские типы карт должны реализовывать интерфейс MapType . Этот интерфейс определяет определённые свойства и методы, позволяющие API инициировать запросы к вашему типу(ам) карты, когда API определяет необходимость отображения фрагментов карты в текущей области просмотра и с текущим уровнем масштабирования. Вы обрабатываете эти запросы, чтобы определить, какой фрагмент загрузить.

Примечание : вы можете создать собственный класс для реализации этого интерфейса. Кроме того, если у вас есть совместимые изображения, вы можете использовать класс ImageMapType , который уже реализует этот интерфейс.

Классы, реализующие интерфейс MapType , требуют определения и заполнения следующих свойств:

  • tileSize (обязательно) определяет размер плитки (типа google.maps.Size ). Размеры должны быть прямоугольными, но не обязательно квадратными.
  • maxZoom (обязательно) указывает максимальный уровень масштабирования, при котором будут отображаться фрагменты карты этого типа.
  • minZoom (необязательно) задаёт минимальный уровень масштабирования для отображения фрагмента карты данного типа. По умолчанию это значение равно 0 , что означает отсутствие минимального уровня масштабирования.
  • name (необязательно) определяет имя данного типа карты. Это свойство необходимо только в том случае, если вы хотите, чтобы этот тип карты можно было выбрать в элементе управления MapType. (См. Параметры элемента управления .)
  • alt (необязательно) задаёт альтернативный текст для данного типа карты, отображаемый при наведении курсора. Это свойство необходимо только в том случае, если требуется, чтобы данный тип карты можно было выбрать в элементе управления MapType. (См. раздел «Параметры элемента управления »).

Кроме того, классы, реализующие интерфейс MapType , должны реализовать следующие методы:

  • Метод getTile() (обязательный) вызывается всякий раз, когда API определяет, что карте необходимо отобразить новые тайлы для заданной области просмотра. Метод getTile() должен иметь следующую сигнатуру:

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

    API определяет необходимость вызова метода getTile() на основе свойств tileSize , minZoom и maxZoom объекта MapType , а также текущего размера области просмотра и уровня масштабирования карты. Обработчик этого метода должен возвращать HTML-элемент с переданными координатами, уровнем масштабирования и элементом DOM, к которому добавляется изображение плитки.

  • Метод releaseTile() (необязательный) вызывается всякий раз, когда API определяет, что необходимо удалить тайл карты, поскольку он выпадает из поля зрения. Этот метод должен иметь следующую сигнатуру:

    releaseTile(tile:Node)

    Обычно необходимо обрабатывать удаление любых элементов, прикреплённых к фрагментам карты, при их добавлении на карту. Например, если вы прикрепили прослушиватели событий к слоям фрагментов карты, их следует удалить здесь.

Метод getTile() действует как основной контроллер, определяющий, какие плитки следует загружать в заданной области просмотра.

Базовые типы карт

Типы карт, создаваемые таким образом, могут быть как самостоятельными, так и комбинироваться с другими типами карт в качестве наложений. Отдельные типы карт называются базовыми типами карт . API может потребоваться обрабатывать такие пользовательские MapType так же, как и любые другие существующие базовые типы карт ( ROADMAP , TERRAIN и т. д.). Для этого добавьте свой пользовательский MapType к свойству mapTypes объекта Map . Это свойство имеет тип MapTypeRegistry .

Следующий код создает базовый MapType для отображения координат фрагментов карты и рисует контур фрагментов:

Машинопись

/*
 * 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;
Посмотреть пример

Попробуйте образец

Типы наложенных карт

Некоторые типы карт предназначены для работы поверх существующих. Такие типы карт могут иметь прозрачные слои, обозначающие объекты интереса или предоставляющие пользователю дополнительные данные.

In these cases, you do not want the map type treated as a separate entity but as an overlay. You can do this by adding the map type to an existing MapType directly using the Map 's overlayMapTypes property. This property contains an MVCArray of MapType s. All map types (base and overlay) are rendered within the mapPane layer. Overlay map types will display on top of the base map they are attached to, in the order in which they appear in the Map.overlayMapTypes array (overlays with higher index values are displayed in front of overlays with lower index values).

The following example is identical to the previous one except that we've created a tile overlay MapType on top of the ROADMAP map type:

Машинопись

/*
 * 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;
View example

Try Sample

Image Map Types

Implementing a MapType to act as a base map type can be a time-consuming and laborious task. The API provides a special class that implements the MapType interface for the most common map types: map types that consist of tiles made up of single image files.

This class, the ImageMapType class, is constructed using an ImageMapTypeOptions object specification defining the following required properties:

  • tileSize (required) specifies the size of the tile (of type google.maps.Size ). Sizes must be rectangular though they need not be square.
  • getTileUrl (required) specifies the function, usually provided as an inline function literal, to handle selection of the proper image tile based on supplied world coordinates and zoom level.

The following code implements a basic ImageMapType using Google's moon tiles. The example makes use of a normalization function to ensure that tiles repeat along the x-axis, but not along the y-axis of your map.

Машинопись

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;
View example

Try Sample

Прогнозы

The Earth is a three-dimensional sphere (approximately), while a map is a flat two-dimensional surface. The map that you see within the Maps JavaScript API, like any flat map of the Earth, is a projection of that sphere onto a flat surface. In its simplest terms, a projection can be defined as a mapping of latitude/longitude values into coordinates on the projection's map.

Projections in the Maps JavaScript API must implement the Projection interface. A Projection implementation must provide not only a mapping from one coordinate system to another, but a bi-directional mapping. That is, you must define how to translate from Earth coordinates ( LatLng objects) to the Projection class's world coordinate system, and from the world coordinate system back to the Earth coordinates. Google Maps uses the Mercator projection to create its maps from geographic data and convert events on the map into geographic coordinates. You can obtain this projection by calling getProjection() on the Map (or any of the standard base MapType types.) For most uses, this standard Projection will suffice, but you may also define and use your own custom projections.

Implement a Projection

When implementing a custom projection, you will need to define a few things:

  • The formulae for mapping latitude and longitude coordinates into a Cartesian plane and the corresponding formulae for mapping from a Cartesian plane to latitude and longitude coordinates. (The Projection interface only supports transformations into rectilinear coordinates.)
  • The base tile size. All tiles must be rectangular.
  • The "world size" of a map using the base tile set at zoom level 0. Note that for maps consisting of one tile at zoom 0, the world size and base tile size are identical.

Coordinate Transformations in Projections

Each projection provides two methods which translate between these two coordinate systems, allowing you to convert between geographic and world coordinates:

  • The Projection.fromLatLngToPoint() method converts a LatLng value into a world coordinate. This method is used to position overlays on the map (and to position the map itself).
  • The Projection.fromPointToLatLng() method converts a world coordinate into a LatLng value. This method is used to convert events such as clicks that happen on the map into geographic coordinates.

Google Maps assumes that projections are rectilinear.

Generally, you may use a projection for two cases: to create a map of the world, or to create a map of a local area. In the former case, you should ensure that your projection is also rectilinear and normal at all longitudes. Some projections (especially conic projections) may be "locally normal" (ie point north) but deviate from true north; for example, the further the map is positioned relative to some reference longitude. You may use such a projection locally, but be aware that the projection is necessarily imprecise and transformation errors will become increasingly apparently the further away from the reference longitude you deviate.

Map Tile Selection in Projections

Projections are not only useful for determining the positions of locations or overlays, but for positioning the map tiles themselves. The Maps JavaScript API renders base maps using a MapType interface, which must declare both a projection property for identifying the map's projection and a getTile() method for retrieving map tiles based on tile coordinate values. Tile coordinates are based on both your basic tile size (which must be rectangular) and the "world size" of your map, which is the pixel size of your map world at zoom level 0. (For maps consisting of one tile at zoom 0, the tile size and world size are identical.)

You define the base tile size within your MapType 's tileSize property. You define the world size implicitly within your projection's fromLatLngToPoint() and fromPointToLatLng() methods.

Since image selection depends on these passed values, it is useful to name images that can be selected programmatically given those passed values, such as map _ zoom _ tileX _ tileY .png .

The following example defines an ImageMapType using the Gall-Peters projection:

Машинопись

// This example defines an image map type using the Gall-Peters
// projection.
// https://en.wikipedia.org/wiki/Gall%E2%80%93Peters_projection
const mapElement = document.querySelector("gmp-map") as google.maps.MapElement;
let innerMap;

async function initMap() {
  // Request the needed libraries.
  await google.maps.importLibrary("maps");

  // Create a map.
  innerMap = mapElement.innerMap;
  innerMap.setOptions({
    mapTypeControl: false,
  });

  // Set the Gall-Peters map type.
  initGallPeters();
  innerMap.mapTypes.set("gallPeters", gallPetersMapType);
  innerMap.setMapTypeId("gallPeters");

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

  innerMap.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.
  innerMap.data.setStyle((feature) => {
    return {
      title: feature.getProperty("name") as string,
      optimized: false,
    };
  });
  innerMap.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 (
        "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" },
    },
  ],
};

initMap();

JavaScript

// This example defines an image map type using the Gall-Peters
// projection.
// https://en.wikipedia.org/wiki/Gall%E2%80%93Peters_projection
const mapElement = document.querySelector("gmp-map");
let innerMap;
async function initMap() {
    // Request the needed libraries.
    await google.maps.importLibrary("maps");
    // Create a map.
    innerMap = mapElement.innerMap;
    innerMap.setOptions({
        mapTypeControl: false,
    });
    // Set the Gall-Peters map type.
    initGallPeters();
    innerMap.mapTypes.set("gallPeters", gallPetersMapType);
    innerMap.setMapTypeId("gallPeters");
    // Show the lat and lng under the mouse cursor.
    const coordsDiv = document.getElementById("coords");
    innerMap.addListener("mousemove", (event) => {
        coordsDiv.textContent =
            "lat: " +
                Math.round(event.latLng.lat()) +
                ", " +
                "lng: " +
                Math.round(event.latLng.lng());
    });
    // Add some markers to the map.
    innerMap.data.setStyle((feature) => {
        return {
            title: feature.getProperty("name"),
            optimized: false,
        };
    });
    innerMap.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 ("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" },
        },
    ],
};
initMap();
View example

Try Sample

,
Select platform: Android iOS JavaScript

This document discusses the types of maps you can display using the Maps JavaScript API. The API uses a MapType object to hold information about these maps. A MapType is an interface that defines the display and usage of map tiles and the translation of coordinate systems from screen coordinates to world coordinates (on the map). Each MapType must contain a few methods to handle retrieval and release of tiles, and properties that define its visual behavior.

The inner workings of map types within the Maps JavaScript API is an advanced topic. Most developers can use the basic map types noted below. However, you can also modify the presentation of existing map types using Styled Maps or define your own map tiles using custom map types . When providing custom map types, you will need to understand how to modify the map's Map Type Registry .

Basic Map Types

There are four types of maps available within the Maps JavaScript API. In addition to the familiar "painted" road map tiles, the Maps JavaScript API also supports other maps types.

The following map types are available in the Maps JavaScript API:

  • roadmap displays the default road map view. This is the default map type.
  • satellite displays Google Earth satellite images.
  • hybrid displays a mixture of normal and satellite views.
  • terrain displays a physical map based on terrain information.

You modify the map type in use by the Map by setting its mapTypeId property, either within the constructor by setting its Map options object, or by calling the map's setMapTypeId() method. The mapTypeID property defaults to roadmap .

Setting the mapTypeId upon construction:

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

Modifying the mapTypeId dynamically:

map.setMapTypeId('terrain');

Note that you don't actually set the map's map type directly, but instead set its mapTypeId to reference a MapType using an identifier. The Maps JavaScript API uses a map type registry, explained below, to manage these references.

45° Imagery

The Maps JavaScript API supports special 45° imagery for certain locations. This high-resolution imagery provides perspective views towards each of the cardinal direction (North, South, East, West). These images are available at higher zoom levels for supported map types.

The following image shows a 45° perspective view of New York City:

The satellite and hybrid map types support 45° imagery at high zoom levels (12 and greater) where available. If the user zooms into a location for which such imagery exists, these map types automatically alter their views in the following manner:

  • The satellite or hybrid imagery is replaced with imagery giving a 45° perspective, centered on the current location. By default, such views are oriented towards north. If the user zooms out, the default satellite or hybrid imagery appears again. The behavior varies depending on zoom level and the value of tilt :
    • Between zoom levels 12 and 18 the top-down basemap (0°) displays by default unless tilt is set to 45.
    • At zoom levels of 18 or greater the 45° basemap displays unless tilt is set to 0.
  • The rotate control becomes visible. The rotate control provides options that enable the user to toggle tilt, and to rotate the view in 90° increments in either direction. To hide the rotate control, set rotateControl to false .

Zooming out from a map type displaying 45° imagery reverts each of these changes, re-establishing the original map types.

Enable and Disable 45° Imagery

You can disable 45° imagery by calling setTilt(0) on the Map object. To enable 45° imagery for supported map types, call setTilt(45) . The Map 's getTilt() method will always reflect the current tilt being shown on the map; if you set a tilt on a map and then later remove that tilt (by zooming the map out, for example), the map's getTilt() method will return 0 .

Important: 45° imagery is only supported on raster maps; this imagery cannot be used with vector maps.

The following example displays a 45° view of New York City:

Машинопись

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;
View example

Try Sample

View example .

Rotate 45° Imagery

The 45° imagery actually consists of a collection of images for each cardinal direction (North, South, East, West). Once your map is displaying 45° imagery, you can orient the imagery towards one of its cardinal directions by calling setHeading() on the Map object, passing a number value expressed as degrees from North.

The following example shows an aerial map and auto-rotates the map every 3 seconds when the button is clicked:

Машинопись

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;
View example

Try Sample

View example .

Modify the Map Type Registry

A map's mapTypeId is a string identifier that is used to associate a MapType with a unique value. Each Map object maintains a MapTypeRegistry which contains the collection of available MapType s for that map. This registry is used to select the types of maps which are available in the Map's MapType control, for example.

You don't read directly from the map type registry. Instead, you modify the registry by adding custom map types and associating them with a string identifier of your choosing. You cannot modify or alter the basic map types (though you can remove them from the map by altering the appearance of the map's associated mapTypeControlOptions ).

The following code sets the map to show only two map types in the map's mapTypeControlOptions and modifies the registry to add the association with this identifier to the actual implementation of the MapType interface.

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

Styled Maps

The StyledMapType lets you customize the presentation of the standard Google base maps, changing the visual display of such elements as roads, parks, and built-up areas to reflect a different style than that used in the default map type. The StyledMapType affects only the default roadmap map type.

For more information about the StyledMapType , see Using embedded JSON style declarations .

Custom Map Types

The Maps JavaScript API supports the display and management of custom map types, allowing you to implement your own map imagery or tile overlays.

Several possible map type implementations exist within the Maps JavaScript API:

  • Standard tile sets consisting of images which collectively constitute full cartographic maps. These tile sets are also known as base map types. These map types act and behave like the existing default map types: roadmap , satellite , hybrid and terrain . You can add your custom map type to a Map's mapTypes array to allow the UI within the Maps JavaScript API to treat your custom map type as a standard map type (by including it in the MapType control, for example).
  • Image tile overlays which display on top of existing base map types. Generally, these map types are used to augment an existing map type to display additional information and are often constrained to specific locations and/or zoom levels. Note that these tiles may be transparent, allowing you to add features to existing maps.
  • Non-image map types, which allow you to manipulate the display of map information at its most fundamental level.

Each of these options relies on creating a class that implements the MapType interface. Additionally, the ImageMapType class provides some built-in behavior to simplify the creation of imagery map types.

The MapType Interface

Before you create classes which implement MapType , it is important to understand how Google Maps determines coordinates and decides which parts of the map to show. You need to implement similar logic for any base or overlay map types. Read the guide to map and tile coordinates .

Custom map types must implement the MapType interface. This interface specifies certain properties and methods that allow the API to initiate requests to your map type(s) when the API determines that it needs to display map tiles within the current viewport and zoom level. You handle these requests to decide which tile to load.

Note : You may create your own class to implement this interface. Alternatively, if you have compatible imagery you can use the ImageMapType class which already implements this interface.

Classes implementing the MapType interface require that you define and populate the following properties:

  • tileSize (required) specifies the size of the tile (of type google.maps.Size ). Sizes must be rectangular though they need not be square.
  • maxZoom (required) specifies the maximum zoom level at which to display tiles of this map type.
  • minZoom (optional) specifies the minimum zoom level at which to display tile of this map type. By default, this value is 0 indicating that no minimum zoom level exists.
  • name (optional) specifies the name for this map type. This property is only necessary if you want this map type to be selectable within a MapType control. (See Control Options .)
  • alt (optional) specifies the alternate text for this map type, exhibited as hover text. This property is only necessary if you want this map type to be selectable within a MapType control. (See Control Options .)

Additionally, classes implementing the MapType interface need to implement the following methods:

  • getTile() (required) is called whenever the API determines that the map needs to display new tiles for the given viewport. The getTile() method must have the following signature:

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

    The API determines whether it needs to call getTile() based on the MapType 's tileSize , minZoom , and maxZoom properties and the map's current viewport and zoom level. The handler for this method should return an HTML element given a passed coordinate, zoom level, and DOM element on which to append the tile image.

  • releaseTile() (optional) is called whenever the API determines that the map needs to remove a tile as it falls out of view. This method must have the following signature:

    releaseTile(tile:Node)

    You typically should handle removal of any elements that were attached to the map tiles upon addition to the map. For example, if you attached event listeners to map tile overlays, you should remove them here.

The getTile() method acts as the main controller for determining which tiles to load within a given viewport.

Base Map Types

Map types which you construct in this manner may either stand alone or be combined with other map types as overlays. Standalone map types are known as base map types . You may want to have the API treat such custom MapType s as it would any other existing base map type ( ROADMAP , TERRAIN , etc.). To do so, add your custom MapType to the Map 's mapTypes property. This property is of type MapTypeRegistry .

The following code creates a base MapType to display a map's tile coordinates and draws an outline of the tiles:

Машинопись

/*
 * 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;
View example

Try Sample

Overlay Map Types

Some map types are designed to work on top of existing map types. Such map types may have transparent layers indicating points of interest, or showing additional data to the user.

In these cases, you do not want the map type treated as a separate entity but as an overlay. You can do this by adding the map type to an existing MapType directly using the Map 's overlayMapTypes property. This property contains an MVCArray of MapType s. All map types (base and overlay) are rendered within the mapPane layer. Overlay map types will display on top of the base map they are attached to, in the order in which they appear in the Map.overlayMapTypes array (overlays with higher index values are displayed in front of overlays with lower index values).

The following example is identical to the previous one except that we've created a tile overlay MapType on top of the ROADMAP map type:

Машинопись

/*
 * 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;
View example

Try Sample

Image Map Types

Implementing a MapType to act as a base map type can be a time-consuming and laborious task. The API provides a special class that implements the MapType interface for the most common map types: map types that consist of tiles made up of single image files.

This class, the ImageMapType class, is constructed using an ImageMapTypeOptions object specification defining the following required properties:

  • tileSize (required) specifies the size of the tile (of type google.maps.Size ). Sizes must be rectangular though they need not be square.
  • getTileUrl (required) specifies the function, usually provided as an inline function literal, to handle selection of the proper image tile based on supplied world coordinates and zoom level.

The following code implements a basic ImageMapType using Google's moon tiles. The example makes use of a normalization function to ensure that tiles repeat along the x-axis, but not along the y-axis of your map.

Машинопись

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;
View example

Try Sample

Прогнозы

The Earth is a three-dimensional sphere (approximately), while a map is a flat two-dimensional surface. The map that you see within the Maps JavaScript API, like any flat map of the Earth, is a projection of that sphere onto a flat surface. In its simplest terms, a projection can be defined as a mapping of latitude/longitude values into coordinates on the projection's map.

Projections in the Maps JavaScript API must implement the Projection interface. A Projection implementation must provide not only a mapping from one coordinate system to another, but a bi-directional mapping. That is, you must define how to translate from Earth coordinates ( LatLng objects) to the Projection class's world coordinate system, and from the world coordinate system back to the Earth coordinates. Google Maps uses the Mercator projection to create its maps from geographic data and convert events on the map into geographic coordinates. You can obtain this projection by calling getProjection() on the Map (or any of the standard base MapType types.) For most uses, this standard Projection will suffice, but you may also define and use your own custom projections.

Implement a Projection

When implementing a custom projection, you will need to define a few things:

  • The formulae for mapping latitude and longitude coordinates into a Cartesian plane and the corresponding formulae for mapping from a Cartesian plane to latitude and longitude coordinates. (The Projection interface only supports transformations into rectilinear coordinates.)
  • The base tile size. All tiles must be rectangular.
  • The "world size" of a map using the base tile set at zoom level 0. Note that for maps consisting of one tile at zoom 0, the world size and base tile size are identical.

Coordinate Transformations in Projections

Each projection provides two methods which translate between these two coordinate systems, allowing you to convert between geographic and world coordinates:

  • The Projection.fromLatLngToPoint() method converts a LatLng value into a world coordinate. This method is used to position overlays on the map (and to position the map itself).
  • The Projection.fromPointToLatLng() method converts a world coordinate into a LatLng value. This method is used to convert events such as clicks that happen on the map into geographic coordinates.

Google Maps assumes that projections are rectilinear.

Generally, you may use a projection for two cases: to create a map of the world, or to create a map of a local area. In the former case, you should ensure that your projection is also rectilinear and normal at all longitudes. Some projections (especially conic projections) may be "locally normal" (ie point north) but deviate from true north; for example, the further the map is positioned relative to some reference longitude. You may use such a projection locally, but be aware that the projection is necessarily imprecise and transformation errors will become increasingly apparently the further away from the reference longitude you deviate.

Map Tile Selection in Projections

Projections are not only useful for determining the positions of locations or overlays, but for positioning the map tiles themselves. The Maps JavaScript API renders base maps using a MapType interface, which must declare both a projection property for identifying the map's projection and a getTile() method for retrieving map tiles based on tile coordinate values. Tile coordinates are based on both your basic tile size (which must be rectangular) and the "world size" of your map, which is the pixel size of your map world at zoom level 0. (For maps consisting of one tile at zoom 0, the tile size and world size are identical.)

You define the base tile size within your MapType 's tileSize property. You define the world size implicitly within your projection's fromLatLngToPoint() and fromPointToLatLng() methods.

Since image selection depends on these passed values, it is useful to name images that can be selected programmatically given those passed values, such as map _ zoom _ tileX _ tileY .png .

The following example defines an ImageMapType using the Gall-Peters projection:

Машинопись

// This example defines an image map type using the Gall-Peters
// projection.
// https://en.wikipedia.org/wiki/Gall%E2%80%93Peters_projection
const mapElement = document.querySelector("gmp-map") as google.maps.MapElement;
let innerMap;

async function initMap() {
  // Request the needed libraries.
  await google.maps.importLibrary("maps");

  // Create a map.
  innerMap = mapElement.innerMap;
  innerMap.setOptions({
    mapTypeControl: false,
  });

  // Set the Gall-Peters map type.
  initGallPeters();
  innerMap.mapTypes.set("gallPeters", gallPetersMapType);
  innerMap.setMapTypeId("gallPeters");

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

  innerMap.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.
  innerMap.data.setStyle((feature) => {
    return {
      title: feature.getProperty("name") as string,
      optimized: false,
    };
  });
  innerMap.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 (
        "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" },
    },
  ],
};

initMap();

JavaScript

// This example defines an image map type using the Gall-Peters
// projection.
// https://en.wikipedia.org/wiki/Gall%E2%80%93Peters_projection
const mapElement = document.querySelector("gmp-map");
let innerMap;
async function initMap() {
    // Request the needed libraries.
    await google.maps.importLibrary("maps");
    // Create a map.
    innerMap = mapElement.innerMap;
    innerMap.setOptions({
        mapTypeControl: false,
    });
    // Set the Gall-Peters map type.
    initGallPeters();
    innerMap.mapTypes.set("gallPeters", gallPetersMapType);
    innerMap.setMapTypeId("gallPeters");
    // Show the lat and lng under the mouse cursor.
    const coordsDiv = document.getElementById("coords");
    innerMap.addListener("mousemove", (event) => {
        coordsDiv.textContent =
            "lat: " +
                Math.round(event.latLng.lat()) +
                ", " +
                "lng: " +
                Math.round(event.latLng.lng());
    });
    // Add some markers to the map.
    innerMap.data.setStyle((feature) => {
        return {
            title: feature.getProperty("name"),
            optimized: false,
        };
    });
    innerMap.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 ("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" },
        },
    ],
};
initMap();
View example

Try Sample

,
Select platform: Android iOS JavaScript

This document discusses the types of maps you can display using the Maps JavaScript API. The API uses a MapType object to hold information about these maps. A MapType is an interface that defines the display and usage of map tiles and the translation of coordinate systems from screen coordinates to world coordinates (on the map). Each MapType must contain a few methods to handle retrieval and release of tiles, and properties that define its visual behavior.

The inner workings of map types within the Maps JavaScript API is an advanced topic. Most developers can use the basic map types noted below. However, you can also modify the presentation of existing map types using Styled Maps or define your own map tiles using custom map types . When providing custom map types, you will need to understand how to modify the map's Map Type Registry .

Basic Map Types

There are four types of maps available within the Maps JavaScript API. In addition to the familiar "painted" road map tiles, the Maps JavaScript API also supports other maps types.

The following map types are available in the Maps JavaScript API:

  • roadmap displays the default road map view. This is the default map type.
  • satellite displays Google Earth satellite images.
  • hybrid displays a mixture of normal and satellite views.
  • terrain displays a physical map based on terrain information.

You modify the map type in use by the Map by setting its mapTypeId property, either within the constructor by setting its Map options object, or by calling the map's setMapTypeId() method. The mapTypeID property defaults to roadmap .

Setting the mapTypeId upon construction:

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

Modifying the mapTypeId dynamically:

map.setMapTypeId('terrain');

Note that you don't actually set the map's map type directly, but instead set its mapTypeId to reference a MapType using an identifier. The Maps JavaScript API uses a map type registry, explained below, to manage these references.

45° Imagery

The Maps JavaScript API supports special 45° imagery for certain locations. This high-resolution imagery provides perspective views towards each of the cardinal direction (North, South, East, West). These images are available at higher zoom levels for supported map types.

The following image shows a 45° perspective view of New York City:

The satellite and hybrid map types support 45° imagery at high zoom levels (12 and greater) where available. If the user zooms into a location for which such imagery exists, these map types automatically alter their views in the following manner:

  • The satellite or hybrid imagery is replaced with imagery giving a 45° perspective, centered on the current location. By default, such views are oriented towards north. If the user zooms out, the default satellite or hybrid imagery appears again. The behavior varies depending on zoom level and the value of tilt :
    • Between zoom levels 12 and 18 the top-down basemap (0°) displays by default unless tilt is set to 45.
    • At zoom levels of 18 or greater the 45° basemap displays unless tilt is set to 0.
  • The rotate control becomes visible. The rotate control provides options that enable the user to toggle tilt, and to rotate the view in 90° increments in either direction. To hide the rotate control, set rotateControl to false .

Zooming out from a map type displaying 45° imagery reverts each of these changes, re-establishing the original map types.

Enable and Disable 45° Imagery

You can disable 45° imagery by calling setTilt(0) on the Map object. To enable 45° imagery for supported map types, call setTilt(45) . The Map 's getTilt() method will always reflect the current tilt being shown on the map; if you set a tilt on a map and then later remove that tilt (by zooming the map out, for example), the map's getTilt() method will return 0 .

Important: 45° imagery is only supported on raster maps; this imagery cannot be used with vector maps.

The following example displays a 45° view of New York City:

Машинопись

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;
View example

Try Sample

View example .

Rotate 45° Imagery

The 45° imagery actually consists of a collection of images for each cardinal direction (North, South, East, West). Once your map is displaying 45° imagery, you can orient the imagery towards one of its cardinal directions by calling setHeading() on the Map object, passing a number value expressed as degrees from North.

The following example shows an aerial map and auto-rotates the map every 3 seconds when the button is clicked:

Машинопись

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;
View example

Try Sample

View example .

Modify the Map Type Registry

A map's mapTypeId is a string identifier that is used to associate a MapType with a unique value. Each Map object maintains a MapTypeRegistry which contains the collection of available MapType s for that map. This registry is used to select the types of maps which are available in the Map's MapType control, for example.

You don't read directly from the map type registry. Instead, you modify the registry by adding custom map types and associating them with a string identifier of your choosing. You cannot modify or alter the basic map types (though you can remove them from the map by altering the appearance of the map's associated mapTypeControlOptions ).

The following code sets the map to show only two map types in the map's mapTypeControlOptions and modifies the registry to add the association with this identifier to the actual implementation of the MapType interface.

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

Styled Maps

The StyledMapType lets you customize the presentation of the standard Google base maps, changing the visual display of such elements as roads, parks, and built-up areas to reflect a different style than that used in the default map type. The StyledMapType affects only the default roadmap map type.

For more information about the StyledMapType , see Using embedded JSON style declarations .

Custom Map Types

The Maps JavaScript API supports the display and management of custom map types, allowing you to implement your own map imagery or tile overlays.

Several possible map type implementations exist within the Maps JavaScript API:

  • Standard tile sets consisting of images which collectively constitute full cartographic maps. These tile sets are also known as base map types. These map types act and behave like the existing default map types: roadmap , satellite , hybrid and terrain . You can add your custom map type to a Map's mapTypes array to allow the UI within the Maps JavaScript API to treat your custom map type as a standard map type (by including it in the MapType control, for example).
  • Image tile overlays which display on top of existing base map types. Generally, these map types are used to augment an existing map type to display additional information and are often constrained to specific locations and/or zoom levels. Note that these tiles may be transparent, allowing you to add features to existing maps.
  • Non-image map types, which allow you to manipulate the display of map information at its most fundamental level.

Each of these options relies on creating a class that implements the MapType interface. Additionally, the ImageMapType class provides some built-in behavior to simplify the creation of imagery map types.

The MapType Interface

Before you create classes which implement MapType , it is important to understand how Google Maps determines coordinates and decides which parts of the map to show. You need to implement similar logic for any base or overlay map types. Read the guide to map and tile coordinates .

Custom map types must implement the MapType interface. This interface specifies certain properties and methods that allow the API to initiate requests to your map type(s) when the API determines that it needs to display map tiles within the current viewport and zoom level. You handle these requests to decide which tile to load.

Note : You may create your own class to implement this interface. Alternatively, if you have compatible imagery you can use the ImageMapType class which already implements this interface.

Classes implementing the MapType interface require that you define and populate the following properties:

  • tileSize (required) specifies the size of the tile (of type google.maps.Size ). Sizes must be rectangular though they need not be square.
  • maxZoom (required) specifies the maximum zoom level at which to display tiles of this map type.
  • minZoom (optional) specifies the minimum zoom level at which to display tile of this map type. By default, this value is 0 indicating that no minimum zoom level exists.
  • name (optional) specifies the name for this map type. This property is only necessary if you want this map type to be selectable within a MapType control. (See Control Options .)
  • alt (optional) specifies the alternate text for this map type, exhibited as hover text. This property is only necessary if you want this map type to be selectable within a MapType control. (See Control Options .)

Additionally, classes implementing the MapType interface need to implement the following methods:

  • getTile() (required) is called whenever the API determines that the map needs to display new tiles for the given viewport. The getTile() method must have the following signature:

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

    The API determines whether it needs to call getTile() based on the MapType 's tileSize , minZoom , and maxZoom properties and the map's current viewport and zoom level. The handler for this method should return an HTML element given a passed coordinate, zoom level, and DOM element on which to append the tile image.

  • releaseTile() (optional) is called whenever the API determines that the map needs to remove a tile as it falls out of view. This method must have the following signature:

    releaseTile(tile:Node)

    You typically should handle removal of any elements that were attached to the map tiles upon addition to the map. For example, if you attached event listeners to map tile overlays, you should remove them here.

The getTile() method acts as the main controller for determining which tiles to load within a given viewport.

Base Map Types

Map types which you construct in this manner may either stand alone or be combined with other map types as overlays. Standalone map types are known as base map types . You may want to have the API treat such custom MapType s as it would any other existing base map type ( ROADMAP , TERRAIN , etc.). To do so, add your custom MapType to the Map 's mapTypes property. This property is of type MapTypeRegistry .

The following code creates a base MapType to display a map's tile coordinates and draws an outline of the tiles:

Машинопись

/*
 * 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;
View example

Try Sample

Overlay Map Types

Some map types are designed to work on top of existing map types. Such map types may have transparent layers indicating points of interest, or showing additional data to the user.

In these cases, you do not want the map type treated as a separate entity but as an overlay. You can do this by adding the map type to an existing MapType directly using the Map 's overlayMapTypes property. This property contains an MVCArray of MapType s. All map types (base and overlay) are rendered within the mapPane layer. Overlay map types will display on top of the base map they are attached to, in the order in which they appear in the Map.overlayMapTypes array (overlays with higher index values are displayed in front of overlays with lower index values).

The following example is identical to the previous one except that we've created a tile overlay MapType on top of the ROADMAP map type:

Машинопись

/*
 * 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;
View example

Try Sample

Image Map Types

Implementing a MapType to act as a base map type can be a time-consuming and laborious task. The API provides a special class that implements the MapType interface for the most common map types: map types that consist of tiles made up of single image files.

This class, the ImageMapType class, is constructed using an ImageMapTypeOptions object specification defining the following required properties:

  • tileSize (required) specifies the size of the tile (of type google.maps.Size ). Sizes must be rectangular though they need not be square.
  • getTileUrl (required) specifies the function, usually provided as an inline function literal, to handle selection of the proper image tile based on supplied world coordinates and zoom level.

The following code implements a basic ImageMapType using Google's moon tiles. The example makes use of a normalization function to ensure that tiles repeat along the x-axis, but not along the y-axis of your map.

Машинопись

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;
View example

Try Sample

Прогнозы

The Earth is a three-dimensional sphere (approximately), while a map is a flat two-dimensional surface. The map that you see within the Maps JavaScript API, like any flat map of the Earth, is a projection of that sphere onto a flat surface. In its simplest terms, a projection can be defined as a mapping of latitude/longitude values into coordinates on the projection's map.

Projections in the Maps JavaScript API must implement the Projection interface. A Projection implementation must provide not only a mapping from one coordinate system to another, but a bi-directional mapping. That is, you must define how to translate from Earth coordinates ( LatLng objects) to the Projection class's world coordinate system, and from the world coordinate system back to the Earth coordinates. Google Maps uses the Mercator projection to create its maps from geographic data and convert events on the map into geographic coordinates. You can obtain this projection by calling getProjection() on the Map (or any of the standard base MapType types.) For most uses, this standard Projection will suffice, but you may also define and use your own custom projections.

Implement a Projection

When implementing a custom projection, you will need to define a few things:

  • The formulae for mapping latitude and longitude coordinates into a Cartesian plane and the corresponding formulae for mapping from a Cartesian plane to latitude and longitude coordinates. (The Projection interface only supports transformations into rectilinear coordinates.)
  • The base tile size. All tiles must be rectangular.
  • The "world size" of a map using the base tile set at zoom level 0. Note that for maps consisting of one tile at zoom 0, the world size and base tile size are identical.

Coordinate Transformations in Projections

Each projection provides two methods which translate between these two coordinate systems, allowing you to convert between geographic and world coordinates:

  • The Projection.fromLatLngToPoint() method converts a LatLng value into a world coordinate. This method is used to position overlays on the map (and to position the map itself).
  • The Projection.fromPointToLatLng() method converts a world coordinate into a LatLng value. This method is used to convert events such as clicks that happen on the map into geographic coordinates.

Google Maps assumes that projections are rectilinear.

Generally, you may use a projection for two cases: to create a map of the world, or to create a map of a local area. In the former case, you should ensure that your projection is also rectilinear and normal at all longitudes. Some projections (especially conic projections) may be "locally normal" (ie point north) but deviate from true north; for example, the further the map is positioned relative to some reference longitude. You may use such a projection locally, but be aware that the projection is necessarily imprecise and transformation errors will become increasingly apparently the further away from the reference longitude you deviate.

Map Tile Selection in Projections

Projections are not only useful for determining the positions of locations or overlays, but for positioning the map tiles themselves. The Maps JavaScript API renders base maps using a MapType interface, which must declare both a projection property for identifying the map's projection and a getTile() method for retrieving map tiles based on tile coordinate values. Tile coordinates are based on both your basic tile size (which must be rectangular) and the "world size" of your map, which is the pixel size of your map world at zoom level 0. (For maps consisting of one tile at zoom 0, the tile size and world size are identical.)

You define the base tile size within your MapType 's tileSize property. You define the world size implicitly within your projection's fromLatLngToPoint() and fromPointToLatLng() methods.

Since image selection depends on these passed values, it is useful to name images that can be selected programmatically given those passed values, such as map _ zoom _ tileX _ tileY .png .

The following example defines an ImageMapType using the Gall-Peters projection:

Машинопись

// This example defines an image map type using the Gall-Peters
// projection.
// https://en.wikipedia.org/wiki/Gall%E2%80%93Peters_projection
const mapElement = document.querySelector("gmp-map") as google.maps.MapElement;
let innerMap;

async function initMap() {
  // Request the needed libraries.
  await google.maps.importLibrary("maps");

  // Create a map.
  innerMap = mapElement.innerMap;
  innerMap.setOptions({
    mapTypeControl: false,
  });

  // Set the Gall-Peters map type.
  initGallPeters();
  innerMap.mapTypes.set("gallPeters", gallPetersMapType);
  innerMap.setMapTypeId("gallPeters");

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

  innerMap.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.
  innerMap.data.setStyle((feature) => {
    return {
      title: feature.getProperty("name") as string,
      optimized: false,
    };
  });
  innerMap.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 (
        "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" },
    },
  ],
};

initMap();

JavaScript

// This example defines an image map type using the Gall-Peters
// projection.
// https://en.wikipedia.org/wiki/Gall%E2%80%93Peters_projection
const mapElement = document.querySelector("gmp-map");
let innerMap;
async function initMap() {
    // Request the needed libraries.
    await google.maps.importLibrary("maps");
    // Create a map.
    innerMap = mapElement.innerMap;
    innerMap.setOptions({
        mapTypeControl: false,
    });
    // Set the Gall-Peters map type.
    initGallPeters();
    innerMap.mapTypes.set("gallPeters", gallPetersMapType);
    innerMap.setMapTypeId("gallPeters");
    // Show the lat and lng under the mouse cursor.
    const coordsDiv = document.getElementById("coords");
    innerMap.addListener("mousemove", (event) => {
        coordsDiv.textContent =
            "lat: " +
                Math.round(event.latLng.lat()) +
                ", " +
                "lng: " +
                Math.round(event.latLng.lng());
    });
    // Add some markers to the map.
    innerMap.data.setStyle((feature) => {
        return {
            title: feature.getProperty("name"),
            optimized: false,
        };
    });
    innerMap.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 ("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" },
        },
    ],
};
initMap();
View example

Try Sample