Typy map

Wybierz platformę: Android iOS JavaScript

W tym dokumencie omawiamy typy map, które można 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 translację układów współrzędnych ze współrzędnych ekranu na współrzędne świata (na mapie). Każdy element MapType musi zawierać kilka metod obsługi pobierania i zwalniania kafelków oraz właściwości, które definiują jego zachowanie wizualne.

Wewnętrzne działanie typów map w interfejsie Maps JavaScript API to zaawansowany temat. Most developers can use the basic map types noted below. Możesz też jednak modyfikować wygląd istniejących typów map za pomocą map z własnym stylem lub definiować własne kafelki 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 „malowanych” kafelków map drogowych interfejs Maps JavaScript API obsługuje też inne typy map.

W interfejsie Maps JavaScript API dostępne są te typy map:

  • roadmap wyświetla domyślny widok mapy drogowej z etykietami mapy podstawowej.
  • satellite wyświetla fotorealistyczną mapę na podstawie zdjęć lotniczych.
  • hybrid wyświetla widok mapy satelitarnej z etykietami mapy podstawowej.
  • terrain wyświetla mapę fizyczną opartą na informacjach o terenie.

Typ mapy używany przez obiekt Map możesz zmienić, ustawiając jego właściwość mapTypeId w konstruktorze przez ustawienie obiektu Map options lub wywołując metodę setMapTypeId() mapy. Właściwość mapTypeID ma domyślnie wartość roadmap.

Ustawianie mapTypeId podczas tworzenia:

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

Dynamiczne modyfikowanie mapTypeId:

map.setMapTypeId('terrain');

Pamiętaj, że nie ustawiasz typu mapy bezpośrednio, ale ustawiasz mapTypeId, aby odwoływać się do MapType za pomocą identyfikatora. Do zarządzania tymi odwołaniami interfejs Maps JavaScript API używa rejestru typów map, który opisujemy poniżej.

3D Maps

Interfejs Maps JavaScript API obsługuje zdjęcia 3D. Wiele działań i możliwości dostosowywania dostępnych w przypadku map 2D jest też dostępnych w przypadku map 3D.

Więcej informacji o mapach 3D znajdziesz w ich omówieniu.

Poniższy przykład pokazuje widok San Francisco w perspektywie 3D:

Więcej informacji o Mapach 3D znajdziesz w omówieniu Map 3D.

Zdjęcia pod kątem 45° (wycofane)

Poniższy przykład pokazuje, jak uzyskać widok San Francisco pod kątem 45° za pomocą map 3D:

TypeScript

async function init() {
    const { Map3DElement } = await google.maps.importLibrary('maps3d');
    const map = new Map3DElement({
        center: {
            lat: 37.789,
            lng: -122.401,
            altitude: 0,
        },
        range: 2200,
        tilt: 45,
        heading: 188,
    });
    map.mode = 'SATELLITE';
    document.body.append(map);
}

void init();

JavaScript

async function init() {
    const { Map3DElement } = await google.maps.importLibrary('maps3d');
    const map = new Map3DElement({
        center: {
            lat: 37.789,
            lng: -122.401,
            altitude: 0,
        },
        range: 2200,
        tilt: 45,
        heading: 188,
    });
    map.mode = 'SATELLITE';
    document.body.append(map);
}

void init();

CSS

/* 
 * Always set the map height explicitly to define the size of the div element
 * that contains the map. 
 */
#gmp-map-3d {
    height: 100%;
}
html,
body {
    height: 100%;
    margin: 0;
    padding: 0;
}

HTML

<html>
    <head>
        <title>3d map 45-degree perspective</title>
        <link rel="stylesheet" type="text/css" href="./style.css" />
        <script type="module" src="./index.js"></script>
        <script>
            // prettier-ignore
            (g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({
                key: "GOOGLE_MAPS_API_KEY"
            });
        </script>
    </head>
    <body>
        <div id="map"></div>
    </body>
</html>
Zobacz przykład

Modyfikowanie rejestru typów map

mapTypeId mapy to identyfikator w postaci ciągu znaków, który służy do powiązania MapType z unikalną wartością. Każdy obiekt Map zawiera MapTypeRegistry, który zawiera kolekcję dostępnych MapType dla tej mapy. Ten rejestr służy na przykład do wybierania typów map dostępnych w elemencie sterującym MapType.

Nie odczytujesz danych bezpośrednio z rejestru typów map. Zamiast tego modyfikujesz rejestr, dodając własne mapy i powiązując je z wybranym identyfikatorem ciągu znaków. Nie możesz modyfikować ani zmieniać podstawowych typów map (możesz je jednak usunąć z mapy, zmieniając wygląd powiązanego z mapą obiektu mapTypeControlOptions).

Poniższy kod ustawia mapę tak, aby w mapTypeControlOptions mapy wyświetlała tylko 2 typy map, i modyfikuje rejestr, aby dodać powiązanie z tym identyfikatorem do rzeczywistej 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 dostosowywanie wyglądu standardowych map podstawowych Google przez zmianę sposobu wyświetlania takich elementów jak drogi, parki i obszary zabudowane, aby odzwierciedlić styl inny niż używany w domyślnym typie mapy. StyledMapType wpływa tylko na domyślny typ mapy roadmap.

Więcej informacji o StyledMapType znajdziesz w artykule Korzystanie z deklaracji stylu JSON.

Niestandardowe typy map

Interfejs Maps JavaScript API obsługuje wyświetlanie niestandardowych typów map i zarządzanie nimi, co umożliwia implementowanie własnych zdjęć map lub nakładek kafelków.

W interfejsie Maps JavaScript API istnieje kilka możliwych implementacji typu mapy:

  • Standardowe zestawy kafelków składające się z obrazów, które razem tworzą pełne mapy kartograficzne. Te zestawy kafelków są też nazywane podstawowymi typami map. Te typy map działają i zachowują się jak dotychczasowe domyślne typy map: roadmap, satellite, hybrid i terrain. Możesz dodać niestandardowy typ mapy do tablicy mapTypes mapy, aby interfejs użytkownika w interfejsie Maps JavaScript API traktował niestandardowy typ mapy jako standardowy (np. przez uwzględnienie go w elemencie sterującym MapType).
  • Nakładki z kafelkami obrazów, które wyświetlają się na istniejących typach map podstawowych. Zazwyczaj te typy map są używane do rozszerzania istniejącego typu mapy w celu wyświetlania dodatkowych informacji i często są ograniczone do określonych lokalizacji lub poziomów powiększenia. Pamiętaj, że te kafelki mogą być przezroczyste, co pozwala dodawać funkcje do istniejących map.
  • Typy map niebędących mapami 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 udostępnia pewne wbudowane zachowania, które upraszczają tworzenie typów map obrazu.

Interfejs MapType

Zanim utworzysz klasy, które implementują interfejs MapType, musisz zrozumieć, jak Mapy Google określają współrzędne i decydują, które części mapy wyświetlić. Musisz zaimplementować podobną logikę w przypadku wszystkich typów map podstawowych i nakładanych. Przeczytaj przewodnik po współrzędnych mapy i kafelka.

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 typów map, gdy interfejs API stwierdzi, że musi wyświetlić kafelki mapy w bieżącym widocznym obszarze i na bieżącym poziomie powiększenia. Obsługujesz te żądania, aby zdecydować, którą kartę załadować.

Uwaga: aby zaimplementować ten interfejs, możesz utworzyć własną klasę. Jeśli masz zgodne obrazy, możesz też 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 (typu google.maps.Size). Rozmiary muszą być prostokątne, ale nie muszą być kwadratowe.
  • maxZoom (wymagany) określa maksymalny poziom powiększenia, przy którym mają być wyświetlane kafelki tego typu mapy.
  • minZoom (opcjonalny) określa minimalny poziom powiększenia, przy którym ma być wyświetlany kafelek tego typu mapy. Domyślnie ta wartość wynosi 0, co oznacza, że nie ma minimalnego poziomu powiększenia.
  • name (opcjonalny) określa nazwę tego typu mapy. Ta właściwość jest wymagana tylko wtedy, gdy chcesz, aby ten typ mapy można było wybrać w ramach elementu sterującego MapType. (patrz Opcje ustawień).
  • alt (opcjonalny) określa tekst alternatywny dla tego typu mapy, który jest wyświetlany po najechaniu kursorem. Ta właściwość jest wymagana tylko wtedy, gdy chcesz, aby ten typ mapy można było wybrać w ramach kontrolki MapType. (patrz Opcje ustawień).

Dodatkowo klasy implementujące interfejs MapType muszą implementować te metody:

  • getTile() (wymagany) jest wywoływany, gdy interfejs API określi, że mapa musi wyświetlić nowe kafelki dla danego widocznego obszaru. Metoda getTile() musi mieć następującą sygnaturę:

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

    Interfejs API określa, czy musi wywołać funkcję getTile() na podstawie właściwości tileSize, minZoom i maxZoom elementu MapType oraz bieżącego widocznego obszaru i poziomu powiększenia mapy. Obsługa tej metody powinna zwracać element HTML na podstawie przekazanej współrzędnej, poziomu powiększenia i elementu DOM, do którego ma zostać dołączony obraz kafelka.

  • releaseTile() (opcjonalnie) jest wywoływana, gdy interfejs API określi, że mapa musi usunąć kafelek, ponieważ nie jest on już widoczny. Ta metoda musi mieć następującą sygnaturę:

    releaseTile(tile:Node)

    Zwykle należy usunąć wszystkie elementy, które zostały dołączone do kafelków mapy po dodaniu do mapy. Jeśli na przykład do nakładek kafelków mapy zostały dołączone detektory zdarzeń, należy je usunąć w tym miejscu.

Metoda getTile() działa jako główny kontroler określający, które kafelki mają zostać wczytane w danym widocznym obszarze.

Typy map podstawowych

Typy map utworzone w ten sposób mogą być używane samodzielnie lub łączone z innymi typami map jako nakładki. Samodzielne typy map są nazywane podstawowymi typami map. Możesz chcieć, aby interfejs API traktował takie niestandardowe obiekty MapType tak samo jak każdy inny istniejący typ mapy podstawowej (ROADMAP, TERRAIN itp.). Aby to zrobić, dodaj niestandardowy element MapType do właściwości mapTypes elementu Map. Ta usługa jest typu MapTypeRegistry.

Poniższy kod tworzy podstawowy element MapType, który wyświetla współrzędne mapy i rysuje kontur fragmentów mapy:

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 przykład

Typy map nakładek

Niektóre typy map są przeznaczone do działania na istniejących typach map. Takie typy map mogą mieć przezroczyste warstwy wskazujące ciekawe miejsca lub wyświetlające użytkownikowi dodatkowe dane.

W takich przypadkach nie chcesz, aby typ mapy był traktowany jako osobna encja, ale jako nakładka. Możesz to zrobić, dodając typ mapy do istniejącego obiektu MapType bezpośrednio za pomocą właściwości overlayMapTypes obiektu Map. Ta właściwość zawiera MVCArray MapType. Wszystkie typy map (podstawowe i nakładki) są renderowane w warstwie mapPane. Typy map nakładanych będą wyświetlane na mapie podstawowej, 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).

Poniższy przykład jest identyczny z poprzednim, z tą różnicą, że utworzyliśmy nakładkę kafelkową MapType na mapie typu ROADMAP:

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;
Zobacz przykład

Typy map obrazów

Wdrożenie MapType jako typu mapy podstawowej może być czasochłonne i pracochłonne. Interfejs API udostępnia specjalną klasę, która implementuje interfejs MapType dla najpopularniejszych typów map: typów map składających się z kafelków utworzonych z pojedynczych plików graficznych.

Ta klasa, ImageMapType, jest tworzona za pomocą specyfikacji obiektu ImageMapTypeOptions, która definiuje te wymagane właściwości:

  • tileSize (wymagany) określa rozmiar kafelka (typu google.maps.Size). Rozmiary muszą być prostokątne, ale nie muszą być kwadratowe.
  • getTileUrl (wymagany) określa funkcję, zwykle podawaną jako literał funkcji wbudowanej, która obsługuje wybór odpowiedniego kafelka obrazu na podstawie podanych współrzędnych świata i poziomu powiększenia.

Poniższy kod implementuje podstawowy obiekt ImageMapType, który korzysta z kafelków księżyca Google. W przykładzie użyto funkcji normalizacji, aby kafelki powtarzały się wzdłuż osi X, ale nie wzdłuż 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;
Zobacz przykład

Prognozy

Ziemia jest (w przybliżeniu) trójwymiarową kulą, a mapa jest płaską, dwuwymiarową powierzchnią. Mapa, którą widzisz w interfejsie Maps JavaScript API, podobnie jak każda płaska mapa Ziemi, jest odwzorowaniem kuli na płaskiej powierzchni. W najprostszym ujęciu rzutowanie można zdefiniować jako mapowanie wartości szerokości i długości geograficznej na współrzędne na mapie rzutu.

Odwzorowania w interfejsie 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ż mapowanie dwukierunkowe. Oznacza to, że musisz określić, jak przekształcać współrzędne Ziemi (obiekty LatLng) na system współrzędnych świata klasy Projection i z powrotem. Mapy Google używają rzutu Mercatora do tworzenia map na podstawie danych geograficznych i konwertowania zdarzeń na mapie na współrzędne geograficzne. Możesz uzyskać tę projekcję, wywołując getProjection() na Map (lub dowolnym standardowym typie podstawowym MapType). W większości przypadków wystarczy standardowa projekcja Projection, ale możesz też zdefiniować i używać własnych niestandardowych projekcji.

Implementowanie projekcji

Podczas implementowania projekcji niestandardowej musisz zdefiniować kilka rzeczy:

  • Wzory do mapowania współrzędnych szerokości i długości geograficznej na płaszczyznę kartezjańską oraz odpowiednie wzory do mapowania z płaszczyzny kartezjańskiej na współrzędne szerokości i długości geograficznej. (Interfejs Projection obsługuje tylko przekształcenia na współrzędne prostoliniowe).
  • Rozmiar kafelka podstawowego. Wszystkie kafelki muszą być prostokątne.
  • „Rozmiar świata” mapy przy użyciu podstawowego zestawu kafelków na poziomie powiększenia 0. Pamiętaj, że w przypadku map składających się z jednego kafelka przy powiększeniu 0 rozmiar świata i rozmiar kafelka podstawowego są identyczne.

Przekształcenia współrzędnych w projekcjach

Każda projekcja udostępnia 2 metody, które umożliwiają tłumaczenie między tymi 2 systemami współrzędnych, co pozwala na konwersję współrzędnych geograficznych na współrzędne świata i odwrotnie:

  • Metoda Projection.fromLatLngToPoint() konwertuje wartość LatLng na współrzędną świata. Ta metoda służy do pozycjonowania nakładek na mapie (oraz do pozycjonowania 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.

Zasadniczo rzutowania można używać w 2 przypadkach: do tworzenia mapy świata lub mapy obszaru lokalnego. W pierwszym przypadku musisz się upewnić, że rzut jest prostoliniowy i normalny na wszystkich długościach geograficznych. Niektóre odwzorowania (zwłaszcza stożkowe) mogą być „lokalnie normalne” (czyli wskazywać północ), ale odbiegać od rzeczywistej północy, np. im dalej mapa jest położona względem pewnej referencyjnej długości geograficznej. Możesz używać takiego odwzorowania lokalnie, ale pamiętaj, że jest ono z natury niedokładne, a błędy przekształcenia będą coraz bardziej widoczne, im bardziej będziesz się oddalać od referencyjnej długości geograficznej.

Wybór kafelków mapy w odwzorowaniach

Rzuty 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 rzutu mapy, jak i metodę getTile() do pobierania kafelków mapy na podstawie wartości współrzędnych kafelków. Współrzędne mapy są oparte zarówno na podstawowym rozmiarze kafelka (który musi być prostokątny), jak i na „rozmiarze świata” mapy, czyli rozmiarze mapy w pikselach na poziomie powiększenia 0. (W przypadku map składających się z 1 kafelka przy powiększeniu 0 rozmiar kafelka i rozmiar świata są identyczne).

Podstawowy rozmiar kafelka określasz we właściwości tileSize elementu MapType. Rozmiar świata określasz pośrednio w metodach fromLatLngToPoint() i fromPointToLatLng() projekcji.

Ponieważ wybór obrazu zależy od przekazanych wartości, warto nazwać obrazy, które można wybrać automatycznie na podstawie tych wartości, np. map_zoom_tileX_tileY.png.

W tym przykładzie zdefiniowano ImageMapType przy użyciu odwzorowania Galla-Petersa:

TypeScript

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

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

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

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

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

    innerMap.addListener('mousemove', (event: google.maps.MapMouseEvent) => {
        coordsDiv.textContent = `lat: ${String(Math.round(event.latLng!.lat()))}, lng: ${String(Math.round(event.latLng!.lng()))}`;
    });

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

async function initGallPeters(): Promise<google.maps.ImageMapType> {
    const [{ ImageMapType }, { Size, Point, LatLng }] = await Promise.all([
        google.maps.importLibrary('maps'),
        google.maps.importLibrary('core'),
    ]);

    const GALL_PETERS_RANGE_X = 800;
    const GALL_PETERS_RANGE_Y = 512;

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

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

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

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

            return `gall-peters_${String(zoom)}_${String(x)}_${String(y)}.png`;
        },
        tileSize: new 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(latLng: google.maps.LatLng) {
            const latRadians = (latLng.lat() * Math.PI) / 180;
            return new Point(
                GALL_PETERS_RANGE_X * (0.5 + latLng.lng() / 360),
                GALL_PETERS_RANGE_Y * (0.5 - 0.5 * Math.sin(latRadians))
            );
        },
        fromPointToLatLng(point: google.maps.Point, noWrap?: boolean) {
            const x = point.x / GALL_PETERS_RANGE_X;
            const y = Math.max(0, Math.min(1, point.y / GALL_PETERS_RANGE_Y));

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

    return gallPetersMapType;
}

// 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' },
        },
    ],
};

void init();

JavaScript

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

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

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

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

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

    innerMap.addListener('mousemove', (event) => {
        coordsDiv.textContent = `lat: ${String(Math.round(event.latLng.lat()))}, lng: ${String(Math.round(event.latLng.lng()))}`;
    });

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

async function initGallPeters() {
    const [{ ImageMapType }, { Size, Point, LatLng }] = await Promise.all([
        google.maps.importLibrary('maps'),
        google.maps.importLibrary('core'),
    ]);

    const GALL_PETERS_RANGE_X = 800;
    const GALL_PETERS_RANGE_Y = 512;

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

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

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

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

            return `gall-peters_${String(zoom)}_${String(x)}_${String(y)}.png`;
        },
        tileSize: new 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(latLng) {
            const latRadians = (latLng.lat() * Math.PI) / 180;
            return new Point(
                GALL_PETERS_RANGE_X * (0.5 + latLng.lng() / 360),
                GALL_PETERS_RANGE_Y * (0.5 - 0.5 * Math.sin(latRadians))
            );
        },
        fromPointToLatLng(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 LatLng(
                (Math.asin(1 - 2 * y) * 180) / Math.PI,
                -180 + 360 * x,
                noWrap
            );
        },
    };

    return gallPetersMapType;
}

// 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' },
        },
    ],
};

void init();
Zobacz przykład