В этом документе обсуждаются типы карт, которые можно отображать с помощью 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
с использованием идентификатора. API JavaScript Карт использует реестр типов карт, описанный ниже, для управления этими ссылками.
Снимки под углом 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()
вернет 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
см. в разделе Использование встроенных объявлений стилей JSON .
Пользовательские типы карт
Maps JavaScript API поддерживает отображение и управление пользовательскими типами карт, что позволяет вам создавать собственные изображения карт или наложения плиток.
В Maps JavaScript API существует несколько возможных реализаций типов карт:
- Стандартные наборы плиток, состоящие из изображений, которые в совокупности составляют полные картографические карты. Эти наборы листов также известны как базовые типы карт. Эти типы карт действуют и ведут себя как существующие типы карт по умолчанию:
roadmap
,satellite
,hybrid
иterrain
. Вы можете добавить свой пользовательский тип карты в массивmapTypes
карты, чтобы пользовательский интерфейс Maps JavaScript API мог обрабатывать ваш пользовательский тип карты как стандартный тип карты (например, включив его в элемент управления MapType). - Наложения фрагментов изображений, которые отображаются поверх существующих типов базовых карт. Как правило, эти типы карт используются для дополнения существующего типа карты для отображения дополнительной информации и часто ограничены конкретными местоположениями и/или уровнями масштабирования. Обратите внимание, что эти плитки могут быть прозрачными, что позволяет добавлять объекты на существующие карты.
- Типы карт без изображений, которые позволяют манипулировать отображением картографической информации на самом фундаментальном уровне.
Каждый из этих вариантов основан на создании класса, реализующего интерфейс MapType
. Кроме того, класс ImageMapType
предоставляет некоторые встроенные функции, упрощающие создание типов карт изображений.
Интерфейс MapType
Прежде чем создавать классы, реализующие MapType
, важно понять, как Карты Google определяют координаты и решают, какие части карты показывать. Вам необходимо реализовать аналогичную логику для любых типов базовых или наложенных карт. Прочтите руководство по координатам карты и плитки .
Пользовательские типы карт должны реализовывать интерфейс 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
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 предполагают, что проекции прямолинейны.
Как правило, вы можете использовать проекцию в двух случаях: для создания карты мира или для создания карты местности. В первом случае вам следует убедиться, что ваша проекция также прямолинейна и нормальна на всех долготах. Некоторые проекции (особенно конические) могут быть «локально нормальными» (т.е. указывать на север), но отклоняться от истинного севера; например, чем дальше расположена карта относительно некоторой эталонной долготы. Вы можете использовать такую проекцию локально, но имейте в виду, что проекция обязательно будет неточной, и ошибки преобразования будут становиться все более очевидными, чем дальше вы отклоняетесь от эталонной долготы.
Выбор фрагмента карты в проекциях
Проекции полезны не только для определения положения местоположений или наложений, но и для позиционирования самих фрагментов карты. API JavaScript Карт отображает базовые карты с помощью интерфейса 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;