W tym dokumencie omawiamy typy map, które możesz wyświetlać za pomocą interfejsu Maps JavaScript API. Do przechowywania informacji na temat tych map w interfejsie API używany jest obiekt MapType
. MapType
to interfejs, który definiuje wyświetlanie i używanie kafelków mapy oraz przekształcanie układów współrzędnych z układu ekranowego na układ globalny (na mapie). Każdy obiekt MapType
musi zawierać kilka metod służących do pobierania i zwalniania płytek oraz właściwości określających ich zachowanie wizualne.
Zagadnienie działania typów map w interfejsie Maps JavaScript API jest zaawansowane. Większość deweloperów może korzystać z podstawowych typów map wymienionych poniżej. Możesz jednak zmodyfikować prezentację istniejących typów map za pomocą map stylizowanych lub zdefiniować własne płytki mapy za pomocą niestandardowych typów map. Jeśli udostępniasz niestandardowe typy map, musisz wiedzieć, jak zmodyfikować rejestr typów map.
Podstawowe typy map
W interfejsie Maps JavaScript API dostępne są 4 typy map. Oprócz znanych „namalowanych” fragmentów mapy drogowej interfejs Maps JavaScript API obsługuje również inne typy map.
W Maps JavaScript API dostępne są te typy map:
roadmap
wyświetla domyślny widok mapy drogowej. Jest to domyślny typ mapy.satellite
wyświetla zdjęcia satelitarne Google Earth.hybrid
wyświetla widok normalny i satelitarny.terrain
wyświetla mapę fizyczną opartą na informacjach o terenie.
Aby zmienić typ mapy używany przez Map
, ustaw właściwość mapTypeId
, ustawiając obiekt Map options
w konstruktorze lub wywołując metodę setMapTypeId()
mapy. Właściwość mapTypeID
ma domyślnie wartość roadmap
.
Ustawiam mapTypeId
podczas budowy:
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);
Dynamiczna zmiana parametru mapTypeId
:
map.setMapTypeId('terrain');
Pamiętaj, że nie ustawiasz bezpośrednio typu mapy,
ale zamiast tego ustawiasz jej mapTypeId
, aby odwoływać się do
MapType
za pomocą identyfikatora.
Aby zarządzać tymi odwołaniami, interfejs Maps JavaScript API korzysta z rejestru typów map, którego opis znajdziesz poniżej.
Obrazy w kącie 45°
Interfejs Maps JavaScript API obsługuje specjalne zdjęcia pod kątem 45° w niektórych lokalizacjach. Te zdjęcia o wysokiej rozdzielczości umożliwiają pokazanie z perspektywy każdego z kierunków głównych (północ, południe, wschód, zachód). Te obrazy są dostępne na wyższych poziomach powiększenia w przypadku obsługiwanych typów map.
Na tym obrazie widać Nowy Jork z perspektywy 45°:
Typy map satellite
i hybrid
obsługują obrazy o kącie 45° przy dużych poziomach powiększenia (12 i większych), jeśli są dostępne. Jeśli użytkownik
przybliży widok w miejscu, w którym dostępne są takie obrazy, te typy map
automatycznie zmienią widok w ten sposób:
- Zdjęcia satelitarne lub hybrydowe są zastępowane obrazami z perspektywy 45°, które są wyśrodkowane na bieżącej lokalizacji. Domyślnie takie widoki są zorientowane na północ. Jeśli użytkownik oddali obraz, ponownie pojawi się domyślny widok satelitarny lub hybrydowy. Zachowanie zależy od poziomu powiększenia i wartości
tilt
: - Na poziomach powiększenia 12–18 domyślnie wyświetlana jest mapa bazowa z lotu ptaka (0°), chyba że
tilt
jest ustawiony na 45. - Przy powiększeniu równym 18 lub większym mapa bazowa jest wyświetlana pod kątem 45°, chyba że
tilt
ma wartość 0. - Element sterujący obrotem staje się widoczny. Element sterujący obracaniem umożliwia użytkownikowi przełączanie pochylenia i obracanie widoku o 90° w dowolnym kierunku. Aby ukryć element sterujący obrotem, ustaw parametr
rotateControl
nafalse
.
Pomniejszanie z typu mapy wyświetlającego zdjęcia pod kątem 45° powoduje cofnięcie każdej z tych zmian i przywrócenie oryginalnych typów map.
Włączanie i wyłączanie zdjęć pod kątem 45°
Możesz wyłączyć obrazy pod kątem 45°, wywołując funkcję setTilt(0)
obiektu Map
. Aby włączyć obrazy pod kątem 45° w przypadku obsługiwanych typów map, zadzwoń pod numer setTilt(45)
. Metoda getTilt()
obiektu Map
zawsze odzwierciedla bieżącą wartość tilt
wyświetlaną na mapie. Jeśli na mapie ustawisz wartość tilt
, a potem ją usuniesz (np. przez oddalenie mapy), metoda getTilt()
mapy zwróci wartość 0
.
Ważne: obrazy o kącie 45° są obsługiwane tylko na mapach rastrowych. Nie można ich używać na mapach wektorowych.
Na przykładowym obrazie widok Nowego Jorku pod kątem 45°:
TypeScript
function initMap(): void { const map = new google.maps.Map( document.getElementById("map") as HTMLElement, { center: { lat: 40.76, lng: -73.983 }, zoom: 15, mapTypeId: "satellite", } ); map.setTilt(45); } declare global { interface Window { initMap: () => void; } } window.initMap = initMap;
JavaScript
function initMap() { const map = new google.maps.Map(document.getElementById("map"), { center: { lat: 40.76, lng: -73.983 }, zoom: 15, mapTypeId: "satellite", }); map.setTilt(45); } window.initMap = initMap;
Wypróbuj próbkę
Obracanie o 45°
Zdjęcia w kącie 45° to w istocie zbiór zdjęć dla każdego kierunku świata (północ, południe, wschód, zachód). Gdy mapa wyświetla obrazy pod kątem 45°, możesz ustawić kierunek obrazów, wywołując funkcję setHeading()
obiektu Map
, podając wartość liczbową wyrażoną w stopniach od północy.
Ten przykład pokazuje mapę lotniczą i automatyczne obracanie mapy co 3 sekundy po kliknięciu przycisku:
TypeScript
let map: google.maps.Map; function initMap(): void { map = new google.maps.Map(document.getElementById("map") as HTMLElement, { center: { lat: 40.76, lng: -73.983 }, zoom: 15, mapTypeId: "satellite", heading: 90, tilt: 45, }); // add listener to button document.getElementById("rotate")!.addEventListener("click", autoRotate); } function rotate90(): void { const heading = map.getHeading() || 0; map.setHeading(heading + 90); } function autoRotate(): void { // Determine if we're showing aerial imagery. if (map.getTilt() !== 0) { window.setInterval(rotate90, 3000); } } declare global { interface Window { initMap: () => void; } } window.initMap = initMap;
JavaScript
let map; function initMap() { map = new google.maps.Map(document.getElementById("map"), { center: { lat: 40.76, lng: -73.983 }, zoom: 15, mapTypeId: "satellite", heading: 90, tilt: 45, }); // add listener to button document.getElementById("rotate").addEventListener("click", autoRotate); } function rotate90() { const heading = map.getHeading() || 0; map.setHeading(heading + 90); } function autoRotate() { // Determine if we're showing aerial imagery. if (map.getTilt() !== 0) { window.setInterval(rotate90, 3000); } } window.initMap = initMap;
Wypróbuj próbkę
Modyfikowanie rejestru typów map
Identyfikator mapTypeId
mapy to ciąg znaków służący do powiązania MapType
z wartością unikalną. Każdy obiekt Map
ma obiekt MapTypeRegistry
, który zawiera zbiór dostępnych MapType
dla danej mapy. Ten rejestr służy do wybierania typów map, które są dostępne na przykład w elemencie sterującym MapType mapy.
Nie odczytujesz bezpośrednio z rejestru typów map. Zamiast tego modyfikujesz rejestr, dodając niestandardowe typy map i wiążąc je z wybranym identyfikatorem ciągu. Nie możesz modyfikować ani zmieniać podstawowych typów map (chociaż możesz usunąć je z mapy, zmieniając wygląd powiązanego z nią elementu).mapTypeControlOptions
Ten kod ustawia w mapie tylko 2 typy map w atrybucie mapTypeControlOptions
mapy i modyfikuje rejestr, aby dodać powiązanie z tym identyfikatorem do faktycznej implementacji interfejsu 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);
Stylizowane Mapy Google
StyledMapType
umożliwia dostosowanie wyglądu standardowych map podstawowych Google przez zmianę sposobu wyświetlania takich elementów, jak drogi, parki czy obszary zabudowane, w taki sposób, aby odzwierciedlały inny styl niż używany w domyślnym typie mapy.
Więcej informacji o StyledMapType
znajdziesz w artykule Używanie wbudowanych deklaracji stylu JSON.
Niestandardowe typy map
Interfejs Maps JavaScript API obsługuje wyświetlanie i zarządzanie niestandardowymi typami map, co umożliwia implementowanie własnych obrazów mapy lub nakładek płytek.
W interfejsie Maps JavaScript API istnieje kilka możliwych implementacji typu mapy:
- standardowe zestawy kafli, które składają się z obrazów stanowiących razem pełne mapy kartograficzne; Te zestawy płytek są też nazywane podstawowymi typami map. Te typy map działają i działają tak jak istniejące domyślne typy map:
roadmap
,satellite
,hybrid
iterrain
. Możesz dodać niestandardowy typ mapy do tablicymapTypes
obiektu Map, aby umożliwić interfejsowi użytkownika w Maps JavaScript API traktowanie niestandardowego typu mapy jako standardowego typu mapy (np. przez uwzględnienie go w elemencie sterującym MapType). - Nakładki z kafelkami obrazów, które są wyświetlane nad istniejącymi typami map podstawowych. Zazwyczaj te typy map służą do rozszerzenia istniejącego typu mapy o dodatkowe informacje i często są ograniczone do określonych lokalizacji lub poziomów powiększenia. Pamiętaj, że te elementy mogą być przezroczyste, co pozwoli Ci dodawać funkcje do istniejących map.
- Typy map bez obrazu, które umożliwiają manipulowanie wyświetlaniem informacji na mapie na najbardziej podstawowym poziomie.
Każda z tych opcji wymaga utworzenia klasy, która implementuje interfejs MapType
. Dodatkowo klasa
ImageMapType
zawiera wbudowane zachowanie, które upraszcza tworzenie map obrazkowych.
Interfejs MapType
Zanim utworzysz klasy, które implementują MapType
, musisz zrozumieć, jak Mapy Google określają współrzędne i decydują, które części mapy mają być widoczne. Musisz zaimplementować podobną logikę dla wszystkich typów map bazowych lub nakładowych.
Przeczytaj przewodnik dotyczący współrzędnych mapy i płytek.
Niestandardowe typy map muszą implementować interfejs MapType
. Ten interfejs określa pewne właściwości i metody, które umożliwiają interfejsowi API inicjowanie żądań do Twojego typu mapy, gdy interfejs API stwierdzi, że musi wyświetlić elementy mapy w ramach bieżącego widoku i poziomu powiększenia. Przetwarzasz te żądania, aby zdecydować, która karta ma zostać załadowana.
Uwaga: aby zaimplementować ten interfejs, możesz utworzyć własną klasę. Jeśli masz zgodne obrazy, możesz użyć klasy
ImageMapType
, która już implementuje ten interfejs.
Klasy implementujące interfejs MapType
wymagają zdefiniowania i wypełnienia tych właściwości:
tileSize
(wymagany) określa rozmiar kafelka (typugoogle.maps.Size
). Rozmiary muszą być prostokątne, ale nie muszą być kwadratowe.maxZoom
(wymagany) określa maksymalny poziom powiększenia, na którym mają być wyświetlane kafelki tego typu mapy.minZoom
(opcjonalnie) określa minimalny poziom powiększenia, przy którym wyświetlany jest kafelek tego typu mapy. Domyślnie ta wartość to0
, co oznacza, że nie ma minimalnego poziomu powiększenia.name
(opcjonalnie) określa nazwę tego typu mapy. Ta właściwość jest wymagana tylko wtedy, gdy chcesz, aby ten typ mapy był dostępny do wyboru w elemencie sterującym MapType. Zobacz Opcje kontroli.alt
(opcjonalnie) określa tekst alternatywny dla tego typu mapy, wyświetlany jako tekst najechania kursorem. Ta właściwość jest wymagana tylko wtedy, gdy chcesz, aby ten typ mapy można było wybierać w elemencie sterującym MapType. (zobacz Opcje sterowania).
Dodatkowo klasy implementujące interfejs MapType
muszą implementować te metody:
-
getTile()
(wymagany) jest wywoływany za każdym razem, gdy interfejs API stwierdzi, że mapa musi wyświetlić nowe płytki dla danego widoku. MetodagetTile()
musi mieć taki podpis:getTile(tileCoord:Point,zoom:number,ownerDocument:Document):Node
Interfejs API określa, czy musi wywołać funkcję
getTile()
na podstawie właściwościMapType
,minZoom
imaxZoom
oraz bieżącego widoku mapy i poziomu powiększenia. Obsługa tej metody powinna zwracać element HTML na podstawie przekazanych współrzędnych, poziomu powiększenia i elementu DOM, do którego ma zostać dołączony obraz płytki. -
releaseTile()
(opcjonalnie) jest wywoływany za każdym razem, gdy interfejs API stwierdzi, że mapa musi usunąć kafelek, ponieważ zniknął z pola widzenia. Ta metoda musi mieć następującą sygnaturę:releaseTile(tile:Node)
Zwykle należy usunąć wszystkie elementy, które zostały dołączone do elementów mapy po jej dodaniu. Jeśli na przykład dołączysz detektory zdarzeń do nakładek kafelków mapy, musisz je usunąć.
Metoda getTile()
działa jako główny kontroler określający, które kafelki mają być wczytane w danym widocznym obszarze.
Typy map podstawowych
Typy map utworzone w ten sposób mogą występować samodzielnie lub w połączeniu z innymi typami map jako nakładki. Typy map samodzielnych to typy map podstawowych. Możesz chcieć, aby interfejs API traktował takie niestandardowe MapType
jak dowolny inny istniejący typ mapy bazowej (ROADMAP
, TERRAIN
itp.). Aby to zrobić, dodaj niestandardową właściwość MapType
do właściwości mapTypes
elementu Map
. Typ tej właściwości to MapTypeRegistry
.
Poniższy kod tworzy bazę MapType
, aby wyświetlać współrzędne płytki mapy i rysować kontury płytek:
TypeScript
/* * This demo demonstrates how to replace default map tiles with custom imagery. * In this case, the CoordMapType displays gray tiles annotated with the tile * coordinates. * * Try panning and zooming the map to see how the coordinates change. */ class CoordMapType { tileSize: google.maps.Size; maxZoom = 19; name = "Tile #s"; alt = "Tile Coordinate Map Type"; constructor(tileSize: google.maps.Size) { this.tileSize = tileSize; } getTile( coord: google.maps.Point, zoom: number, ownerDocument: Document ): HTMLElement { const div = ownerDocument.createElement("div"); div.innerHTML = String(coord); div.style.width = this.tileSize.width + "px"; div.style.height = this.tileSize.height + "px"; div.style.fontSize = "10"; div.style.borderStyle = "solid"; div.style.borderWidth = "1px"; div.style.borderColor = "#AAAAAA"; div.style.backgroundColor = "#E5E3DF"; return div; } releaseTile(tile: HTMLElement): void {} } function initMap(): void { const map = new google.maps.Map( document.getElementById("map") as HTMLElement, { zoom: 10, center: { lat: 41.85, lng: -87.65 }, streetViewControl: false, mapTypeId: "coordinate", mapTypeControlOptions: { mapTypeIds: ["coordinate", "roadmap"], style: google.maps.MapTypeControlStyle.DROPDOWN_MENU, }, } ); map.addListener("maptypeid_changed", () => { const showStreetViewControl = (map.getMapTypeId() as string) !== "coordinate"; map.setOptions({ streetViewControl: showStreetViewControl, }); }); // Now attach the coordinate map type to the map's registry. map.mapTypes.set( "coordinate", new CoordMapType(new google.maps.Size(256, 256)) ); } declare global { interface Window { initMap: () => void; } } window.initMap = initMap;
JavaScript
/* * This demo demonstrates how to replace default map tiles with custom imagery. * In this case, the CoordMapType displays gray tiles annotated with the tile * coordinates. * * Try panning and zooming the map to see how the coordinates change. */ class CoordMapType { tileSize; maxZoom = 19; name = "Tile #s"; alt = "Tile Coordinate Map Type"; constructor(tileSize) { this.tileSize = tileSize; } getTile(coord, zoom, ownerDocument) { const div = ownerDocument.createElement("div"); div.innerHTML = String(coord); div.style.width = this.tileSize.width + "px"; div.style.height = this.tileSize.height + "px"; div.style.fontSize = "10"; div.style.borderStyle = "solid"; div.style.borderWidth = "1px"; div.style.borderColor = "#AAAAAA"; div.style.backgroundColor = "#E5E3DF"; return div; } releaseTile(tile) {} } function initMap() { const map = new google.maps.Map(document.getElementById("map"), { zoom: 10, center: { lat: 41.85, lng: -87.65 }, streetViewControl: false, mapTypeId: "coordinate", mapTypeControlOptions: { mapTypeIds: ["coordinate", "roadmap"], style: google.maps.MapTypeControlStyle.DROPDOWN_MENU, }, }); map.addListener("maptypeid_changed", () => { const showStreetViewControl = map.getMapTypeId() !== "coordinate"; map.setOptions({ streetViewControl: showStreetViewControl, }); }); // Now attach the coordinate map type to the map's registry. map.mapTypes.set( "coordinate", new CoordMapType(new google.maps.Size(256, 256)), ); } window.initMap = initMap;
Zobacz próbkę
Typy nakładek mapy
Niektóre typy map są przeznaczone do stosowania na istniejących mapach. Takie mapy mogą mieć przezroczyste warstwy wskazujące ciekawe miejsca lub zawierające dodatkowe dane.
W takich przypadkach nie chcesz, aby typ mapy był traktowany jako osobny element, ale jako nakładka.
Możesz to zrobić, dodając typ mapy do istniejącego MapType
bezpośrednio za pomocą właściwości overlayMapTypes
Map
. Ta usługa zawiera MVCArray
elementów MapType
. Wszystkie typy map (podstawowa i nakładka) są renderowane w warstwie mapPane
. Typy nakładanych map są wyświetlane nad mapą podstawową, do której są dołączone, w kolejności, w jakiej występują w tablicy Map.overlayMapTypes
(nakładki o wyższych wartościach indeksu są wyświetlane przed nakładkami o niższych wartościach indeksu).
Ten przykład jest identyczny jak poprzedni, z tym że na mapie typu ROADMAP
utworzyliśmy nakładkę płytek MapType
:
TypeScript
/* * This demo illustrates the coordinate system used to display map tiles in the * API. * * Tiles in Google Maps are numbered from the same origin as that for * pixels. For Google's implementation of the Mercator projection, the origin * tile is always at the northwest corner of the map, with x values increasing * from west to east and y values increasing from north to south. * * Try panning and zooming the map to see how the coordinates change. */ class CoordMapType implements google.maps.MapType { tileSize: google.maps.Size; alt: string|null = null; maxZoom: number = 17; minZoom: number = 0; name: string|null = null; projection: google.maps.Projection|null = null; radius: number = 6378137; constructor(tileSize: google.maps.Size) { this.tileSize = tileSize; } getTile( coord: google.maps.Point, zoom: number, ownerDocument: Document ): HTMLElement { const div = ownerDocument.createElement("div"); div.innerHTML = String(coord); div.style.width = this.tileSize.width + "px"; div.style.height = this.tileSize.height + "px"; div.style.fontSize = "10"; div.style.borderStyle = "solid"; div.style.borderWidth = "1px"; div.style.borderColor = "#AAAAAA"; return div; } releaseTile(tile: Element): void {} } function initMap(): void { const map = new google.maps.Map( document.getElementById("map") as HTMLElement, { zoom: 10, center: { lat: 41.85, lng: -87.65 }, } ); // Insert this overlay map type as the first overlay map type at // position 0. Note that all overlay map types appear on top of // their parent base map. const coordMapType = new CoordMapType(new google.maps.Size(256, 256)) map.overlayMapTypes.insertAt( 0, coordMapType ); } declare global { interface Window { initMap: () => void; } } window.initMap = initMap;
JavaScript
/* * This demo illustrates the coordinate system used to display map tiles in the * API. * * Tiles in Google Maps are numbered from the same origin as that for * pixels. For Google's implementation of the Mercator projection, the origin * tile is always at the northwest corner of the map, with x values increasing * from west to east and y values increasing from north to south. * * Try panning and zooming the map to see how the coordinates change. */ class CoordMapType { tileSize; alt = null; maxZoom = 17; minZoom = 0; name = null; projection = null; radius = 6378137; constructor(tileSize) { this.tileSize = tileSize; } getTile(coord, zoom, ownerDocument) { const div = ownerDocument.createElement("div"); div.innerHTML = String(coord); div.style.width = this.tileSize.width + "px"; div.style.height = this.tileSize.height + "px"; div.style.fontSize = "10"; div.style.borderStyle = "solid"; div.style.borderWidth = "1px"; div.style.borderColor = "#AAAAAA"; return div; } releaseTile(tile) {} } function initMap() { const map = new google.maps.Map(document.getElementById("map"), { zoom: 10, center: { lat: 41.85, lng: -87.65 }, }); // Insert this overlay map type as the first overlay map type at // position 0. Note that all overlay map types appear on top of // their parent base map. const coordMapType = new CoordMapType(new google.maps.Size(256, 256)); map.overlayMapTypes.insertAt(0, coordMapType); } window.initMap = initMap;
Wypróbuj próbkę
Typy map obrazów
Wdrożenie MapType
jako mapy podstawowej może być czasochłonne i wymagające dużego nakładu pracy. Interfejs API udostępnia specjalną klasę, która implementuje interfejs MapType
do najczęściej używanych typów map: typów map, które składają się z płytek utworzonych z pojedynczych plików z obrazami.
Tę klasę (ImageMapType
) tworzy się za pomocą specyfikacji obiektu ImageMapTypeOptions
definiującej te wymagane właściwości:
tileSize
(wymagany) określa rozmiar kafelka (typugoogle.maps.Size
). Rozmiary muszą być prostokątne, ale nie mogą być kwadratowe.getTileUrl
(wymagany) określa funkcję, która zwykle jest podawana jako litera funkcji wbudowanej, aby umożliwić wybór odpowiedniej karty obrazu na podstawie podanych współrzędnych świata i poziomu powiększenia.
Poniższy kod implementuje podstawowy ImageMapType
za pomocą kafelków księżyca Google. Przykład korzysta z funkcji normalizacji, aby zapewnić powtarzanie się płytek na osi x, ale nie na osi y mapy.
TypeScript
function initMap(): void { const map = new google.maps.Map( document.getElementById("map") as HTMLElement, { center: { lat: 0, lng: 0 }, zoom: 1, streetViewControl: false, mapTypeControlOptions: { mapTypeIds: ["moon"], }, } ); const moonMapType = new google.maps.ImageMapType({ getTileUrl: function (coord, zoom): string { const normalizedCoord = getNormalizedCoord(coord, zoom); if (!normalizedCoord) { return ""; } const bound = Math.pow(2, zoom); return ( "https://mw1.google.com/mw-planetary/lunar/lunarmaps_v1/clem_bw" + "/" + zoom + "/" + normalizedCoord.x + "/" + (bound - normalizedCoord.y - 1) + ".jpg" ); }, tileSize: new google.maps.Size(256, 256), maxZoom: 9, minZoom: 0, // @ts-ignore TODO 'radius' does not exist in type 'ImageMapTypeOptions' radius: 1738000, name: "Moon", }); map.mapTypes.set("moon", moonMapType); map.setMapTypeId("moon"); } // Normalizes the coords that tiles repeat across the x axis (horizontally) // like the standard Google map tiles. function getNormalizedCoord(coord, zoom) { const y = coord.y; let x = coord.x; // tile range in one direction range is dependent on zoom level // 0 = 1 tile, 1 = 2 tiles, 2 = 4 tiles, 3 = 8 tiles, etc const tileRange = 1 << zoom; // don't repeat across y-axis (vertically) if (y < 0 || y >= tileRange) { return null; } // repeat across x-axis if (x < 0 || x >= tileRange) { x = ((x % tileRange) + tileRange) % tileRange; } return { x: x, y: y }; } declare global { interface Window { initMap: () => void; } } window.initMap = initMap;
JavaScript
function initMap() { const map = new google.maps.Map(document.getElementById("map"), { center: { lat: 0, lng: 0 }, zoom: 1, streetViewControl: false, mapTypeControlOptions: { mapTypeIds: ["moon"], }, }); const moonMapType = new google.maps.ImageMapType({ getTileUrl: function (coord, zoom) { const normalizedCoord = getNormalizedCoord(coord, zoom); if (!normalizedCoord) { return ""; } const bound = Math.pow(2, zoom); return ( "https://mw1.google.com/mw-planetary/lunar/lunarmaps_v1/clem_bw" + "/" + zoom + "/" + normalizedCoord.x + "/" + (bound - normalizedCoord.y - 1) + ".jpg" ); }, tileSize: new google.maps.Size(256, 256), maxZoom: 9, minZoom: 0, // @ts-ignore TODO 'radius' does not exist in type 'ImageMapTypeOptions' radius: 1738000, name: "Moon", }); map.mapTypes.set("moon", moonMapType); map.setMapTypeId("moon"); } // Normalizes the coords that tiles repeat across the x axis (horizontally) // like the standard Google map tiles. function getNormalizedCoord(coord, zoom) { const y = coord.y; let x = coord.x; // tile range in one direction range is dependent on zoom level // 0 = 1 tile, 1 = 2 tiles, 2 = 4 tiles, 3 = 8 tiles, etc const tileRange = 1 << zoom; // don't repeat across y-axis (vertically) if (y < 0 || y >= tileRange) { return null; } // repeat across x-axis if (x < 0 || x >= tileRange) { x = ((x % tileRange) + tileRange) % tileRange; } return { x: x, y: y }; } window.initMap = initMap;
Wypróbuj próbkę
Prognozy
Ziemia jest kulą trójwymiarową (w przybliżeniu), a mapa jest płaską dwuwymiarową powierzchnią. Mapa wyświetlana w interfejsie Maps JavaScript API, podobnie jak każda płaska mapa Ziemi, jest projekcją kuli ziemskiej na płaską powierzchnię. Najprościej mówiąc, projekcja to mapowanie wartości szerokości i długości geograficznej na współrzędne na mapie.
Prognozy w Maps JavaScript API muszą implementować interfejs Projection
. Implementacja Projection
musi zapewniać nie tylko mapowanie z jednego układu współrzędnych na inny, ale też w obie strony. Oznacza to, że musisz zdefiniować sposób przekształcania współrzędnych Ziemi (obiekty LatLng
) na system współrzędnych klasy Projection
oraz z systemu współrzędnych światowych z powrotem na współrzędne Ziemi.
Mapy Google korzystają z projekcji Mercatora do tworzenia map na podstawie danych geograficznych i przekształcania zdarzeń na mapie w współrzędne geograficzne. Możesz uzyskać tę projekcję, wywołując funkcję getProjection()
w ramach Map
(lub dowolnego standardowego typu bazy MapType
). W większości przypadków wystarczy standardowa Projection
, ale możesz też zdefiniować i używać własnych projekcji niestandardowych.
Wdrażanie prognozy
Podczas wdrażania projekcji niestandardowej musisz określić kilka kwestii:
- wzory na mapowanie współrzędnych szerokości i długości geograficznej na płaszczyznę kartezjańską oraz odpowiadające im wzory na mapowanie z płaszczyzny kartezjańskiej na współrzędne szerokości i długości geograficznej; (interfejs
Projection
obsługuje tylko transformacje do współrzędnych prostokątnych). - Podstawowy rozmiar kafelka. Wszystkie kafelki muszą być prostokątne.
- „Rozmiar świata” mapy przy użyciu podstawowego zestawu płytek na poziomie powiększenia 0. Pamiętaj, że w przypadku map złożonych z 1 kafelka przy powiększeniu 0 rozmiar świata i rozmiar kafelka podstawowego są identyczne.
Przekształcenia współrzędnych w projekcjach
Każda projekcja zawiera 2 metody, które umożliwiają przekształcanie między tymi dwoma systemami współrzędnych, co pozwala na konwersję współrzędnych geograficznych na współrzędne światowe i odwrotnie:
- Metoda
Projection.fromLatLngToPoint()
konwertuje wartośćLatLng
na współrzędną świata. Ta metoda służy do umieszczania nakładek na mapie (oraz do umieszczania samej mapy). - Metoda
Projection.fromPointToLatLng()
konwertuje współrzędną świata na wartośćLatLng
. Ta metoda służy do konwertowania zdarzeń, takich jak kliknięcia na mapie, na współrzędne geograficzne.
Mapy Google zakładają, że rzuty są prostoliniowe.
Odwzorowanie może być na ogół wystarczające w 2 przypadkach: utworzenia mapy świata lub mapy lokalnego obszaru. W pierwszym przypadku upewnij się, że rzutowanie jest prostokątne i normalne na wszystkich długościach geograficznych. Niektóre projekcje (zwłaszcza projekcje stożkowe) mogą być „lokalnie normalne” (czyli wskazują na północ), ale odbiegają od prawdziwej północy; na przykład im dalej mapa jest umieszczona względem jakiejś długości geograficznej odniesienia. Możesz lokalnie używać takiej projekcji, ale pamiętaj, że jest ona nieprecyzyjna, a błędy związane z przekształceniem będą się zwiększać wraz z odchylaniem się od długości geograficznej odniesienia.
Wybór fragmentu mapy w prognozach
Projekcje są przydatne nie tylko do określania pozycji lokalizacji lub nakładek, ale też do pozycjonowania samych kafelków mapy.
Interfejs Maps JavaScript API renderuje mapy podstawowe za pomocą interfejsu MapType
, który musi deklarować zarówno właściwość projection
do identyfikowania projekcji mapy, jak i metodę getTile()
do pobierania elementów mapy na podstawie wartości pobranych współrzędnych. współrzędne kafelka są określane na podstawie podstawowego rozmiaru kafelka (który musi być prostokątny) oraz „wielkości świata” mapy, czyli rozmiaru świata mapy w poziomie powiększenia 0. (w przypadku map składających się z 1 płytki przy powiększeniu 0 rozmiar płytki i wielkość świata są identyczne).
Rozmiar podstawowej płytki definiujesz w przypadku właściwości tileSize
w usługach MapType
. Rozmiar świata jest zdefiniowany domyślnie w metodach fromLatLngToPoint()
i fromPointToLatLng()
projekcji.
Ponieważ wybór obrazu zależy od tych wartości, warto nadać nazwy obrazom, które można wybrać programowo na podstawie tych wartości, np. map_zoom_tileX_tileY.png
.
Poniższy przykład definiuje ImageMapType
przy użyciu odwzorowania
Gall-Peters:
TypeScript
// This example defines an image map type using the Gall-Peters // projection. // https://en.wikipedia.org/wiki/Gall%E2%80%93Peters_projection function initMap(): void { // Create a map. Use the Gall-Peters map type. const map = new google.maps.Map( document.getElementById("map") as HTMLElement, { zoom: 0, center: { lat: 0, lng: 0 }, mapTypeControl: false, } ); initGallPeters(); map.mapTypes.set("gallPeters", gallPetersMapType); map.setMapTypeId("gallPeters"); // Show the lat and lng under the mouse cursor. const coordsDiv = document.getElementById("coords") as HTMLElement; map.controls[google.maps.ControlPosition.TOP_CENTER].push(coordsDiv); map.addListener("mousemove", (event: google.maps.MapMouseEvent) => { coordsDiv.textContent = "lat: " + Math.round(event.latLng!.lat()) + ", " + "lng: " + Math.round(event.latLng!.lng()); }); // Add some markers to the map. map.data.setStyle((feature) => { return { title: feature.getProperty("name") 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;