マップタイプ

プラットフォームを選択: Android iOS JavaScript

このドキュメントは、Maps JavaScript API を使用して表示できる地図の種類について説明します。この API は、MapType オブジェクトを使用してこれらの地図に関する情報を保持します。MapType は、地図タイルの表示と使用、またスクリーン座標から地図上のワールド座標への座標系の変換を定義するインターフェースです。各 MapType は、タイルの取得と解放を処理するためのメソッド、およびその視覚動作を定義するプロパティをいくつか含んでいる必要があります。

Maps JavaScript API にあるマップタイプの内部動作は高度なトピックです。多くのデベロッパーにとっては、以下に説明する基本のマップタイプを使用すれば十分です。一方、スタイル付き地図を使用して既存のマップタイプの表示方法を変更したり、カスタムのマップタイプを使用して独自の地図タイルを定義したりすることも可能です。カスタムのマップタイプを使用する場合、マップタイプ レジストリを修正する方法を理解しましょう。

基本のマップタイプ

Maps JavaScript API では、4 種類の地図を使用できます。一般的な「絵として描画される」道路地図タイルに加え、Maps JavaScript API ではその他のマップタイプもサポートされています。

以下のマップタイプを、Maps JavaScript API で使用できます。

  • roadmap は、道路地図ビューを表示します。これは、デフォルトのマップタイプです。
  • satellite は、Google Earth の衛星画像を表示します。
  • hybrid は、通常ビューと衛星画像ビューの複合ビューを表示します。
  • terrain は、地形情報に基づいた物理地図を表示します。

Map が使用中のマップタイプを変更するには、コンストラクタ内でその Map options オブジェクトを設定するか、地図の setMapTypeId() メソッドを呼び出して、マップタイプの mapTypeId プロパティを設定します。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 を設定し、ID を使用して MapType を参照しています。Maps JavaScript API では、下で説明するマップタイプ レジストリを使用してこれらの参照を管理します。

45° 画像

Maps JavaScript API は、特定の場所で 45° 画像をサポートしています。この高解像度画像は、各基本方角(北、南、東、西)への展望ビューを提供します。これらの画像は、サポート対象のマップタイプでズームレベルが一定の大きさを超えると表示されます。

次の画像は、ニューヨーク市の 45° 展望ビューを示しています。

satellite および hybrid のマップタイプでは、ズームレベルが 12 以上になると 45° 画像が表示されます(可能な場合)。このような画像が存在する場所にユーザーがズームインすると、このマップタイプでは次の方法で自動的にビューが変化します。

  • 衛星画像またはハイブリッド画像は、45° 展望を提供する画像に置き換わり、現在の場所が中央に配置されます。デフォルトでは、このようなビューは北を向きます。ユーザーがズームアウトすると、デフォルトの衛星画像またはハイブリッド画像が再び表示されます。この動作は、ズームレベルと tilt の値によって異なります。
    • ズームレベル 12~18 の間は、tilt が 45 に設定されていない限り、真上からの基本地図(0°)がデフォルトで表示されます。
    • ズームレベルが 18 以上の場合、tilt が 0 に設定されていない限り、45° の基本地図が表示されます。
  • 回転コントロールが表示されます。ユーザーは、回転コントロールを使って傾斜を切り替え、ビューを両方向に 90° ずつ回転できます。回転コントロールを非表示にするには、rotateControlfalse に設定します。

45° 画像を表示しているマップタイプからズームアウトすると、これらの変更がすべて元に戻り、元のマップタイプに戻ります。

45° 画像の有効化と無効化

45° 画像を無効にするには、Map オブジェクトの setTilt(0) を呼び出します。サポート対象のマップタイプの 45° 画像を有効化するには、setTilt(45) を呼び出します。MapgetTilt() メソッドは常に、地図上に表示されている現在の tilt を反映します。地図上で tilt を設定した後、その tilt を解除する操作(たとえば地図のズームアウト)を行うと、地図の getTilt() メソッドが返す値は 0 になります。

重要: 45° 画像がサポートされているのはラスター地図のみで、ベクター地図では使用できません。

次のサンプルは、ニューヨーク市の 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;
サンプルを表示

サンプルを試す

サンプルを表示

45° 画像の回転

45° 画像は、実際には各基本方角(北、南、東、西)の画像の集合で構成されています。地図で 45° 画像が表示されると、画像をいずれかの基本方角に向けることができます。これは、Map オブジェクト上で setHeading() を呼び出して、北を基にした角度として表される数値を渡すことで行います。

次のサンプルでは、航空写真を表示し、ボタンがクリックされると 3 秒ごとに地図を自動的に回転します。

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;
サンプルを表示

サンプルを試す

サンプルを表示

マップタイプ レジストリの修正

地図の mapTypeId は、MapType を一意の値に関連付ける文字列 ID です。各 Map オブジェクトは、その地図で使用可能な MapType の集合を含む MapTypeRegistry を保持します。このレジストリは、たとえば地図の MapType コントロールで使用可能な地図の種類を選択するために使用されます。

マップタイプ レジストリから直接読み取りは行いません。その代り、カスタムのマップタイプを追加してこれらを任意の文字列 ID と関連付けることで、レジストリを修正します。基本のマップタイプの修正や変更はできません(ただし、地図に関連付けられている mapTypeControlOptions の外観を変更することで、基本のマップタイプを地図から除去できます)。

次のコードは、地図の mapTypeControlOptions で地図が 2 つのマップタイプのみを表示するように設定し、さらにレジストリを修正してこの ID と 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 には、マップタイプの複数の実装が含まれています。

  • 標準タイルセット。集めると地図作製法に従った完全な地図になる画像で構成されています。これらのタイルセットは、基本地図タイプとも呼ばれます。これらのマップタイプは、既存のデフォルトのマップタイプの roadmapsatellitehybridterrain と同様に動作します。カスタムのマップタイプを地図の mapTypes 配列に追加すると、Maps JavaScript API 内の UI でカスタムのマップタイプを標準のマップタイプのように扱うことができます(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 は、MapTypetileSizeminZoommaxZoom の各プロパティ、および地図の現在のビューポートとズームレベルに基づいて、getTile() 呼び出しが必要かどうかを判断します。このメソッドのハンドラは、渡された座標、ズームレベル、およびタイル画像を付加する DOM 要素を受け取り、HTML 要素を返します。

  • releaseTile()(省略可)は、タイルがビューの範囲外になった場合に、地図で該当タイルを削除する必要があると API が判断した場合に必ず呼び出されます。このメソッドは、次のシグネチャを持つ必要があります。

    releaseTile(tile:Node)

    通常、地図に加え、地図タイルに付加したすべての要素の除去を処理する必要があります。たとえば、地図タイル オーバーレイにイベント リスナーを付加した場合、これらもここで除去する必要があります。

getTile() メソッドは、所定のビューポート内に読み込むタイルを決定するためのメイン コントローラとして機能します。

基本地図タイプ

この方法で作成したマップタイプは、スタンドアロンにするか、オーバーレイとして他のマップタイプと組み合わせることができます。スタンドアロンのマップタイプは、基本地図タイプと呼ばれます。このようなカスタム MapType を、その他の既存の基本地図タイプ(ROADMAPTERRAIN など)のように API で扱われるようにする必要のある場合は、カスタム MapTypeMapmapTypes プロパティに追加します。このプロパティは、MapTypeRegistry タイプです。

次のコードは、基本の MapType を作成して、地図のタイル座標を表示し、タイルの枠線を描画します。

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;
サンプルを表示

サンプルを試す

オーバーレイのマップタイプ

一部のマップタイプは、既存のマップタイプの上で動作するように意図されています。このようなマップタイプでは、透明なレイヤで、スポットを示したり、ユーザーに追加データを示したりすることができます。

このような場合、マップタイプを別のエンティティとしてではなく、オーバーレイとして扱います。 それには、MapoverlayMapTypes プロパティを使用して、マップタイプを既存の MapType に直接追加することができます。このプロパティには MapTypeMVCArray が含まれます。すべてのマップタイプ(基本とオーバーレイ)は、mapPane レイヤ内でレンダリングされます。オーバーレイのマップタイプは、関連付けられている任意の基本地図の上に表示され、その順序は Map.overlayMapTypes 配列での記述順序です(より高いインデックス値を持つオーバーレイが、低いインデックス値を持つオーバーレイの前に表示されます)。

次のサンプルは前のサンプルと同じですが、タイル オーバーレイ MapTypeROADMAP のマップタイプ上に作成している点が異なります。

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;
サンプルを表示

サンプルを試す

イメージマップ タイプ

MapType を基本地図タイプとして動作するように実装することは、多くの時間と労力を要します。この API は、最も一般的なマップタイプ(単一の画像ファイルからなる複数のタイルで構成されるマップタイプ)に対して、MapType インターフェースを実装している特別なクラスを提供します。

この ImageMapType クラスは、次の必須プロパティを定義している ImageMapTypeOptions オブジェクト仕様を使用して作成されています。

  • tileSize(必須)は、タイプ google.maps.Size のタイルのサイズを指定します。サイズは、正方形ではなく長方形でなければなりません。
  • getTileUrl(必須)は、通常インライン関数リテラルとして提供され、指定されたワールド座標とズームレベルに基づいて適切な画像タイルの選択を処理する関数を指定します。

次のコードは、Google の月のタイルを使用して基本の ImageMapType を実装しています。このサンプルは、正規化関数を利用して、タイルが地図の x 軸に沿って繰り返され、y 軸に沿っては繰り返されないようにしています。

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;
サンプルを表示

サンプルを試す

投影法

地球は、3 次元のほぼ球体ですが、地図は 2 次元の平面です。Maps JavaScript API で表示されている地図は、その他の平面地球地図と同様に、球体を平面に投影したものです。簡単に言うと、投影は緯度 / 経度値の投影地図上の座標へのマッピングとして定義できます。

Maps JavaScript API での投影には、Projection インターフェースを実装する必要があります。Projection 実装は、ある座標システムから別の座標システムへのマッピングに加え、双方向のマッピングも提供する必要があります。つまり、地球の座標(LatLng オブジェクト)から Projection クラスのワールド座標系への変換方法と、ワールド座標系から地球の座標への変換方法を定義する必要があります。Google マップは、メルカトル図法を使用して地理データから地図を作成し、地図上のイベントを地理座標に変換します。この投影は、Map(または任意の標準の基本 MapType タイプ)で getProjection() を呼び出すことで取得できます。通常は、この標準の Projection で十分ですが、独自のカスタム投影を定義して使用することも可能です。

投影を実装する

カスタム投影を実装する場合、いくつかの定義が必要になります。

  • 緯度と経度の座標をデカルト平面にマッピングする式と、デカルト平面から緯度と経度の座標にマッピングする対応する式。(Projection インターフェースは直線座標への変換のみをサポートします)。
  • 基本タイルサイズ。タイルはすべて長方形にする必要があります。
  • ズームレベル 0 に設定した基本タイルを使用した地図の「世界サイズ」。ズームが 0 の場合に 1 つのタイルで構成される地図では、世界サイズと基本タイルサイズは同一になります。

投影における座標変換

すべての投影は、これらの 2 つの座標系間の変換を行う 2 つのメソッドを提供し、地理座標とワールド座標間の変換を可能にしています。

  • Projection.fromLatLngToPoint() メソッドは LatLng 値をワールド座標に変換します。このメソッドは、地図上のオーバーレイの位置を決めるために(また地図自体の位置を決めるために)使用されます。
  • Projection.fromPointToLatLng() メソッドはワールド座標を LatLng 値に変換します。このメソッドは、地図上で発生するクリックなどのイベントを地理座標に変換するために使用されます。

Google マップでは、投影が直線的であると想定しています。

一般的に、投影は 2 つの場合に使用されます。それは、世界地図を作成する場合と特定の地域の地図を作成する場合です。前者の場合は、使用している投影も直線的であり、すべての経度で垂直に投影されている必要があります。一部の投影(特に円錐投影)では、一部の地域で垂直に投影されますが(真上が北になる)、たとえばある基準経度から相対的に遠い位置の地図では、真上が北を指さなくなります。このような投影法は限られた地域では使用できますが、投影は必然的に不正確となり、基準経度から離れるほど変換の誤差が徐々に顕著になります。

投影における地図タイルの選択

投影は、場所またはオーバーレイの位置の決定に便利なだけではなく、地図タイル自体の位置決めにも有用です。Maps JavaScript API は MapType インターフェースを使用して基本地図をレンダリングします。このインターフェースでは、地図の投影を識別する projection プロパティと、タイル座標値に基づいて地図タイルを取得するための getTile() メソッドの両方を宣言する必要があります。タイル座標は、基本タイルサイズ(長方形である必要がある)とズームレベル 0 での地図世界のピクセルサイズである地図の「世界サイズ」の両方に基づきます(ズーム 0 で 1 つのタイルで構成される地図では、タイルサイズと世界サイズは同一です)。

基本タイルのサイズは、MapTypetileSize プロパティで定義します。世界サイズは、投影の fromLatLngToPoint() メソッドと fromPointToLatLng() メソッドの中で暗黙的に定義します。

画像の選択はこれらの渡される値に依存するため、これらの渡される値を使って、map_zoom_tileX_tileY.png などの名前を画像に付けると、プログラムで選択するときに便利です。

次のサンプルは、ガル ピーターズ図法を使用して ImageMapType を定義しています。

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;
サンプルを表示

サンプルを試す