В этом документе рассматриваются типы карт, которые можно отображать с помощью 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 function initMap(): void { // Create a map. Use the Gall-Peters map type. const map = new google.maps.Map( document.getElementById("map") as HTMLElement, { zoom: 0, center: { lat: 0, lng: 0 }, mapTypeControl: false, } ); initGallPeters(); map.mapTypes.set("gallPeters", gallPetersMapType); map.setMapTypeId("gallPeters"); // Show the lat and lng under the mouse cursor. const coordsDiv = document.getElementById("coords") as HTMLElement; map.controls[google.maps.ControlPosition.TOP_CENTER].push(coordsDiv); map.addListener("mousemove", (event: google.maps.MapMouseEvent) => { coordsDiv.textContent = "lat: " + Math.round(event.latLng!.lat()) + ", " + "lng: " + Math.round(event.latLng!.lng()); }); // Add some markers to the map. map.data.setStyle((feature) => { return { title: feature.getProperty("name") as string, optimized: false, }; }); map.data.addGeoJson(cities); } let gallPetersMapType; function initGallPeters() { const GALL_PETERS_RANGE_X = 800; const GALL_PETERS_RANGE_Y = 512; // Fetch Gall-Peters tiles stored locally on our server. gallPetersMapType = new google.maps.ImageMapType({ getTileUrl: function (coord, zoom) { const scale = 1 << zoom; // Wrap tiles horizontally. const x = ((coord.x % scale) + scale) % scale; // Don't wrap tiles vertically. const y = coord.y; if (y < 0 || y >= scale) return ""; return ( "https://developers.google.com/maps/documentation/" + "javascript/examples/full/images/gall-peters_" + zoom + "_" + x + "_" + y + ".png" ); }, tileSize: new google.maps.Size(GALL_PETERS_RANGE_X, GALL_PETERS_RANGE_Y), minZoom: 0, maxZoom: 1, name: "Gall-Peters", }); // Describe the Gall-Peters projection used by these tiles. gallPetersMapType.projection = { fromLatLngToPoint: function (latLng) { const latRadians = (latLng.lat() * Math.PI) / 180; return new google.maps.Point( GALL_PETERS_RANGE_X * (0.5 + latLng.lng() / 360), GALL_PETERS_RANGE_Y * (0.5 - 0.5 * Math.sin(latRadians)) ); }, fromPointToLatLng: function (point, noWrap) { const x = point.x / GALL_PETERS_RANGE_X; const y = Math.max(0, Math.min(1, point.y / GALL_PETERS_RANGE_Y)); return new google.maps.LatLng( (Math.asin(1 - 2 * y) * 180) / Math.PI, -180 + 360 * x, noWrap ); }, }; } // GeoJSON, describing the locations and names of some cities. const cities = { type: "FeatureCollection", features: [ { type: "Feature", geometry: { type: "Point", coordinates: [-87.65, 41.85] }, properties: { name: "Chicago" }, }, { type: "Feature", geometry: { type: "Point", coordinates: [-149.9, 61.218] }, properties: { name: "Anchorage" }, }, { type: "Feature", geometry: { type: "Point", coordinates: [-99.127, 19.427] }, properties: { name: "Mexico City" }, }, { type: "Feature", geometry: { type: "Point", coordinates: [-0.126, 51.5] }, properties: { name: "London" }, }, { type: "Feature", geometry: { type: "Point", coordinates: [28.045, -26.201] }, properties: { name: "Johannesburg" }, }, { type: "Feature", geometry: { type: "Point", coordinates: [15.322, -4.325] }, properties: { name: "Kinshasa" }, }, { type: "Feature", geometry: { type: "Point", coordinates: [151.207, -33.867] }, properties: { name: "Sydney" }, }, { type: "Feature", geometry: { type: "Point", coordinates: [0, 0] }, properties: { name: "0°N 0°E" }, }, ], }; declare global { interface Window { initMap: () => void; } } window.initMap = initMap;
JavaScript
// This example defines an image map type using the Gall-Peters // projection. // https://en.wikipedia.org/wiki/Gall%E2%80%93Peters_projection function initMap() { // Create a map. Use the Gall-Peters map type. const map = new google.maps.Map(document.getElementById("map"), { zoom: 0, center: { lat: 0, lng: 0 }, mapTypeControl: false, }); initGallPeters(); map.mapTypes.set("gallPeters", gallPetersMapType); map.setMapTypeId("gallPeters"); // Show the lat and lng under the mouse cursor. const coordsDiv = document.getElementById("coords"); map.controls[google.maps.ControlPosition.TOP_CENTER].push(coordsDiv); map.addListener("mousemove", (event) => { coordsDiv.textContent = "lat: " + Math.round(event.latLng.lat()) + ", " + "lng: " + Math.round(event.latLng.lng()); }); // Add some markers to the map. map.data.setStyle((feature) => { return { title: feature.getProperty("name"), optimized: false, }; }); map.data.addGeoJson(cities); } let gallPetersMapType; function initGallPeters() { const GALL_PETERS_RANGE_X = 800; const GALL_PETERS_RANGE_Y = 512; // Fetch Gall-Peters tiles stored locally on our server. gallPetersMapType = new google.maps.ImageMapType({ getTileUrl: function (coord, zoom) { const scale = 1 << zoom; // Wrap tiles horizontally. const x = ((coord.x % scale) + scale) % scale; // Don't wrap tiles vertically. const y = coord.y; if (y < 0 || y >= scale) return ""; return ( "https://developers.google.com/maps/documentation/" + "javascript/examples/full/images/gall-peters_" + zoom + "_" + x + "_" + y + ".png" ); }, tileSize: new google.maps.Size(GALL_PETERS_RANGE_X, GALL_PETERS_RANGE_Y), minZoom: 0, maxZoom: 1, name: "Gall-Peters", }); // Describe the Gall-Peters projection used by these tiles. gallPetersMapType.projection = { fromLatLngToPoint: function (latLng) { const latRadians = (latLng.lat() * Math.PI) / 180; return new google.maps.Point( GALL_PETERS_RANGE_X * (0.5 + latLng.lng() / 360), GALL_PETERS_RANGE_Y * (0.5 - 0.5 * Math.sin(latRadians)), ); }, fromPointToLatLng: function (point, noWrap) { const x = point.x / GALL_PETERS_RANGE_X; const y = Math.max(0, Math.min(1, point.y / GALL_PETERS_RANGE_Y)); return new google.maps.LatLng( (Math.asin(1 - 2 * y) * 180) / Math.PI, -180 + 360 * x, noWrap, ); }, }; } // GeoJSON, describing the locations and names of some cities. const cities = { type: "FeatureCollection", features: [ { type: "Feature", geometry: { type: "Point", coordinates: [-87.65, 41.85] }, properties: { name: "Chicago" }, }, { type: "Feature", geometry: { type: "Point", coordinates: [-149.9, 61.218] }, properties: { name: "Anchorage" }, }, { type: "Feature", geometry: { type: "Point", coordinates: [-99.127, 19.427] }, properties: { name: "Mexico City" }, }, { type: "Feature", geometry: { type: "Point", coordinates: [-0.126, 51.5] }, properties: { name: "London" }, }, { type: "Feature", geometry: { type: "Point", coordinates: [28.045, -26.201] }, properties: { name: "Johannesburg" }, }, { type: "Feature", geometry: { type: "Point", coordinates: [15.322, -4.325] }, properties: { name: "Kinshasa" }, }, { type: "Feature", geometry: { type: "Point", coordinates: [151.207, -33.867] }, properties: { name: "Sydney" }, }, { type: "Feature", geometry: { type: "Point", coordinates: [0, 0] }, properties: { name: "0°N 0°E" }, }, ], }; window.initMap = initMap;