Типы карт

Оптимизируйте свои подборки Сохраняйте и классифицируйте контент в соответствии со своими настройками.
Выберите платформу: Android iOS JavaScript

В этом документе обсуждаются типы карт, которые вы можете отображать с помощью Maps JavaScript API. 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°

Maps JavaScript API поддерживает специальные изображения под углом 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() вернет 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 карты.

Вы не читаете напрямую из реестра типов карт. Вместо этого вы изменяете реестр, добавляя пользовательские типы карт и связывая их со строковым идентификатором по вашему выбору. Вы не можете модифицировать или изменять основные типы карт (хотя вы можете удалить их с карты, изменив внешний вид связанных с картой 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 см. в руководстве по стилизованным картам .

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

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

В 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. (См. Добавление элементов управления MapType ниже.)
  • alt (необязательно) указывает альтернативный текст для этого типа карты, отображаемый как текст при наведении курсора. Это свойство необходимо только в том случае, если вы хотите, чтобы этот тип карты можно было выбрать в элементе управления MapType. (См. Добавление элементов управления MapType ниже.)

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

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

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

    API определяет необходимость вызова getTile() на основе свойств MapType tileSize , minZoom и maxZoom , а также текущего окна просмотра и уровня масштабирования карты. Обработчик этого метода должен возвращать элемент 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 напрямую, используя свойство Map 's overlayMapTypes . Это свойство содержит MVCArray MapType s. Все типы карт (базовые и оверлейные) отображаются в слое 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, как и любая плоская карта Земли, представляет собой проекцию этой сферы на плоскую поверхность. Проще говоря, проекцию можно определить как отображение значений широты/долготы в координаты на карте проекции.

Проекции в Maps JavaScript API должны реализовывать интерфейс Projection . Реализация Projection должна обеспечивать не только отображение из одной системы координат в другую, но и двунаправленное отображение. То есть вы должны определить, как преобразовать координаты Земли (объекты LatLng ) в мировую систему координат класса Projection и наоборот. Карты Google используют проекцию Меркатора для создания своих карт из географических данных и преобразования событий на карте в географические координаты. Вы можете получить эту проекцию, вызвав 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

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

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

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

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

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

let gallPetersMapType;

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

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

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

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

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

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

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

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

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

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

JavaScript

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

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

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

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

let gallPetersMapType;

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

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

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

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

window.initMap = initMap;
Посмотреть пример

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