סוגי מפות

הפלטפורמה: Android iOS JavaScript

במסמך הזה מוסבר על סוגי המפות שאפשר להציג באמצעות Maps JavaScript API. ה-API משתמש באובייקט MapType כדי לאחסן מידע על המפות האלה. ‫MapType הוא ממשק שמגדיר את התצוגה והשימוש במשבצות מפה ואת התרגום של מערכות קואורדינטות מקואורדינטות מסך לקואורדינטות עולמיות (במפה). כל רכיב MapType צריך להכיל כמה שיטות לטיפול באחזור ובשחרור של אריחים, ומאפיינים שמגדירים את ההתנהגות החזותית שלו.

הפעולות הפנימיות של סוגי מפות ב-Maps JavaScript API הן נושא מתקדם. רוב המפתחים יכולים להשתמש ב סוגי המפות הבסיסיים שמפורטים בהמשך. עם זאת, אפשר גם לשנות את הצגת סוגי המפות הקיימים באמצעות מפות עם סגנון או להגדיר משבצות מפה משלכם באמצעות סוגי מפות בהתאמה אישית. כשמספקים סוגי מפות בהתאמה אישית, צריך להבין איך לשנות את הרישום של סוגי המפות.

סוגים בסיסיים של מפות

יש ארבעה סוגים של מפות שזמינים בממשק API של JavaScript במפות Google. בנוסף למשבצות המוכרות של מפת הדרכים, ממשק API של JavaScript במפות Google תומך גם בסוגים אחרים של מפות.

סוגי המפות הבאים זמינים בממשק API של JavaScript במפות Google:

  • roadmap מציג את תצוגת ברירת המחדל של מפת הדרכים. זהו סוג המפה שמוגדר כברירת מחדל.
  • satellite מציג תמונות לוויין של Google Earth.
  • hybrid מוצג שילוב של תצוגות רגילות ותצוגות לוויין.
  • terrain מציג מפה פיזית שמבוססת על מידע על פני השטח.

כדי לשנות את סוג המפה שבה נעשה שימוש ב-Map, צריך להגדיר את המאפיין mapTypeId שלו, או בתוך ה-constructor על ידי הגדרת האובייקט Map options שלו, או על ידי קריאה לשיטה setMapTypeId() של המפה. ערך ברירת המחדל של המאפיין mapTypeID הוא roadmap.

הגדרת mapTypeId במהלך הבנייה:

var myLatlng = new google.maps.LatLng(-34.397, 150.644);
var mapOptions = {
  zoom: 8,
  center: myLatlng,
  mapTypeId: 'satellite'
};
var map = new google.maps.Map(document.getElementById('map'),
    mapOptions);

שינוי mapTypeId באופן דינמי:

map.setMapTypeId('terrain');

שימו לב: לא מגדירים את סוג המפה של המפה ישירות, אלא מגדירים את mapTypeId שלה כך שתפנה אל MapType באמצעות מזהה. ממשק ה-API של JavaScript במפות Google משתמש במאגר של סוגי מפות, שמפורט בהמשך, כדי לנהל את ההפניות האלה.

תמונות בזווית של 45°

‫Maps JavaScript API תומך בתמונות מיוחדות בזווית של 45° במיקומים מסוימים. התמונות האלה ברזולוציה גבוהה ומספקות תצוגות פרספקטיבה לכל אחד מהכיוונים הראשיים (צפון, דרום, מזרח, מערב). התמונות האלה זמינות ברמות זום גבוהות יותר בסוגי מפות נתמכים.

בתמונה הבאה אפשר לראות את העיר ניו יורק מנקודת מבט של 45 מעלות:

סוגי המפות satellite ו-hybrid תומכים בתמונות בזווית של 45° ברמות זום גבוהות (12 ומעלה) במקומות שבהם התמונות האלה זמינות. אם המשתמש מגדיל תצוגה של מיקום שיש לו תמונות כאלה, סוגי המפות האלה משנים את התצוגה באופן אוטומטי בצורה הבאה:

  • תמונות הלוויין או התמונות ההיברידיות מוחלפות בתמונות שמציגות פרספקטיבה של 45° עם מיקום מרכזי במיקום הנוכחי. כברירת מחדל, התצוגות האלה מכוונות לצפון. אם המשתמש מבצע זום אאוט, תמונות הלוויין או התמונות ההיברידיות שמוגדרות כברירת מחדל מופיעות שוב. ההתנהגות משתנה בהתאם לרמת הזום ולערך של tilt:
    • בין רמות הזום 12 ו-18, מפת הבסיס מלמעלה למטה (0°) מוצגת כברירת מחדל, אלא אם הערך של tilt מוגדר ל-45.
    • במפות בסיס ברמות זום של 18 ומעלה מוצגת מפת בסיס בזווית של 45 מעלות, אלא אם הערך של tilt הוא 0.
  • פקד הסיבוב מוצג. אמצעי הבקרה לסיבוב מספק אפשרויות שמאפשרות למשתמש להחליף את ההטיה ולסובב את התצוגה במרווחים של 90° בכל אחד מהכיוונים. כדי להסתיר את לחצן הסיבוב, מגדירים את rotateControl ל-false.

אם מתרחקים מתצוגה של סוג מפה שמציג תמונות בזווית של 45°, כל השינויים האלה מתבטלים וסוגי המפות המקוריים חוזרים.

הפעלה והשבתה של תמונות בזווית של 45°

כדי להשבית את התמונות בזווית של 45°, צריך להפעיל את setTilt(0) באובייקט Map. כדי להפעיל תמונות בזווית של 45 מעלות בסוגי מפות נתמכים, צריך להתקשר למספר setTilt(45). השיטה Map של getTilt() תמיד תשקף את tilt הנוכחי שמוצג במפה. אם מגדירים tilt במפה ואז מסירים אותו (לדוגמה, על ידי הרחקת התצוגה של המפה), השיטה getTilt() של המפה תחזיר 0.tilt

חשוב: תמונות בזווית של 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°, אפשר לכוון את התמונות לאחד הכיוונים הראשיים על ידי קריאה ל-setHeading() באובייקט Map והעברת ערך מספרי שמייצג מעלות מצפון.

בדוגמה הבאה מוצג מפה אווירית, והמפה מסתובבת אוטומטית כל 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;
לצפייה בדוגמה

דוגמה לניסיון

לצפייה בדוגמה

שינוי של Map Type Registry

mapTypeId של מיפוי הוא מזהה מחרוזת שמשמש לשיוך MapType לערך ייחודי. כל אובייקט Map שומר על MapTypeRegistry שמכיל את האוסף של MapTypes שזמינים למפה הזו. המאגר הזה משמש לבחירת סוגי המפות שזמינות בפקד MapType של המפה, למשל.

אתם לא קוראים ישירות ממאגר סוגי המפות. במקום זאת, משנים את הרישום על ידי הוספת סוגי מפות בהתאמה אישית ושיוך שלהם למזהה מחרוזת לבחירתכם. אי אפשר לשנות את סוגי המפות הבסיסיים (אבל אפשר להסיר אותם מהמפה על ידי שינוי המראה של mapTypeControlOptions שמשויך למפה).

הקוד הבא מגדיר את המפה כך שיוצגו בה רק שני סוגים של מפות ב-mapTypeControlOptions שלה, ומשנה את הרישום כדי להוסיף את השיוך למזהה הזה להטמעה בפועל של הממשק MapType.

// Modify the control to only display two maptypes, the
// default ROADMAP and the custom 'mymap'.
// Note that because this is an association, we
// don't need to modify the MapTypeRegistry beforehand.

var MY_MAPTYPE_ID = 'mymaps';

var mapOptions = {
  zoom: 12,
  center: brooklyn,
  mapTypeControlOptions: {
     mapTypeIds: ['roadmap', MY_MAPTYPE_ID]
  },
  mapTypeId: MY_MAPTYPE_ID
};

// Create our map. This creation will implicitly create a
// map type registry.
map = new google.maps.Map(document.getElementById('map'),
    mapOptions);

// Create your custom map type using your own code.
// (See below.)
var myMapType = new MyMapType();

// Set the registry to associate 'mymap' with the
// custom map type we created, and set the map to
// show that map type.
map.mapTypes.set(MY_MAPTYPE_ID, myMapType);

מפות מסוגננות

האפשרות StyledMapType מאפשרת להתאים אישית את הצגת מפות הבסיס הרגילות של Google, ולשנות את התצוגה החזותית של רכיבים כמו כבישים, פארקים ואזורים בנויים, כך שתשקף סגנון שונה מזה שמשמש בסוג המפה שמוגדר כברירת מחדל. ההגדרה StyledMapType משפיעה רק על סוג המפה שמוגדר כברירת מחדל roadmap.

מידע נוסף על StyledMapType זמין במאמר שימוש בהצהרות מוטמעות בסגנון JSON.

סוגי מפות בהתאמה אישית

ממשק Maps JavaScript API תומך בהצגה ובניהול של סוגי מפות בהתאמה אישית, ומאפשר לכם להטמיע תמונות מפה משלכם או שכבות-על של משבצות.

קיימים כמה יישומים אפשריים של סוגי מפות ב-Maps JavaScript API:

  • קבוצות משבצות רגילות שמורכבות מתמונות שביחד יוצרות מפות קרטוגרפיות מלאות. ערכות המשבצות האלה נקראות גם סוגי מפות בסיסיות. סוגי המפות האלה פועלים ומתנהגים כמו סוגי המפות הקיימים שמוגדרים כברירת מחדל: roadmap, satellite, hybrid ו-terrain. אתם יכולים להוסיף את סוג המפה המותאם אישית שלכם למערך mapTypes של Map כדי לאפשר לממשק המשתמש ב-Maps JavaScript API להתייחס לסוג המפה המותאם אישית שלכם כאל סוג מפה רגיל (למשל, על ידי הכללתו באמצעי הבקרה MapType).
  • שכבות-על של משבצות תמונה שמוצגות מעל סוגי מפות בסיסיות קיימים. בדרך כלל, סוגי המפות האלה משמשים להוספת מידע למפה קיימת, ולעתים קרובות הם מוגבלים למיקומים ספציפיים או לרמות זום מסוימות. שימו לב שהמשבצות האלה עשויות להיות שקופות, כך שתוכלו להוסיף תכונות למפות קיימות.
  • סוגי מפות שאינם תמונות, שמאפשרים לכם לשנות את התצוגה של פרטי המפה ברמה הבסיסית ביותר.

כל אחת מהאפשרויות האלה מסתמכת על יצירת מחלקה שמטמיעה את הממשק MapType. בנוסף, המחלקה ImageMapType מספקת התנהגות מובנית מסוימת כדי לפשט את היצירה של סוגי מפות של תמונות.

הממשק של MapType

לפני שיוצרים מחלקות שמטמיעות את MapType, חשוב להבין איך מפות Google קובעות קואורדינטות ומחליטות אילו חלקים של המפה להציג. צריך להטמיע לוגיקה דומה לכל סוגי המפות הבסיסיות או מפות השכבות. מומלץ לקרוא את המדריך בנושא קואורדינטות של מפות ומשבצות.

סוגי מפות מותאמים אישית צריכים להטמיע את הממשק MapType. הממשק הזה מציין מאפיינים ושיטות מסוימים שמאפשרים ל-API ליזום בקשות לסוגי המפות כש-API קובע שהוא צריך להציג משבצות מפה בתוך אזור התצוגה הנוכחי ורמת הזום. אתם מטפלים בבקשות האלה כדי להחליט איזו משבצת לטעון.

הערה: אתם יכולים ליצור מחלקה משלכם כדי להטמיע את הממשק הזה. לחלופין, אם יש לכם תמונות תואמות, אתם יכולים להשתמש במחלקה ImageMapType שכבר מטמיעה את הממשק הזה.

במחלקה שמטמיעה את הממשק MapType, צריך להגדיר ולאכלס את המאפיינים הבאים:

  • tileSize (חובה) מציין את גודל המשבצת (מסוג google.maps.Size). הגדלים צריכים להיות מלבניים, אבל לא בהכרח ריבועיים.
  • maxZoom (חובה) מציין את רמת הזום המקסימלית שבה יוצגו משבצות של סוג המפה הזה.
  • minZoom (אופציונלי) מציין את רמת הזום המינימלית שבה יוצג אריח של סוג המפה הזה. ערך ברירת המחדל הוא 0, שמשמעותו שאין רמת זום מינימלית.
  • name (אופציונלי) מציין את השם של סוג המפה הזה. המאפיין הזה נדרש רק אם רוצים לאפשר בחירה בסוג המפה הזה באמצעות רכיב MapType. (ראו אפשרויות שליטה).
  • alt (אופציונלי) מציין את הטקסט החלופי לסוג המפה הזה, שמוצג כטקסט כשמעבירים את העכבר מעל המפה. המאפיין הזה נדרש רק אם רוצים לאפשר בחירה של סוג המפה הזה באמצעות רכיב MapType. (לעיון באפשרויות השליטה).

בנוסף, מחלקות שמטמיעות את הממשק MapType צריכות להטמיע את השיטות הבאות:

  • getTile() (נדרש) נקרא בכל פעם שממשק ה-API קובע שהמפה צריכה להציג משבצות חדשות עבור אזור התצוגה הנתון. ל-getTile() method צריכה להיות החתימה הבאה:

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

    ממשק ה-API קובע אם צריך לקרוא ל-getTile() על סמך המאפיינים MapType,‏ tileSize,‏ minZoom ו-maxZoom של getTile(), אזור התצוגה הנוכחי של המפה ורמת הזום. ה-handler של השיטה הזו צריך להחזיר אלמנט HTML בהינתן קואורדינטה, רמת זום ואלמנט DOM שצריך לצרף אליו את תמונת המשבצת.

  • releaseTile() (אופציונלי) מופעל בכל פעם שממשק ה-API קובע שצריך להסיר משבצת מהמפה כי היא לא מוצגת יותר. לשיטה הזו צריכה להיות החתימה הבאה:

    releaseTile(tile:Node)

    בדרך כלל, צריך לטפל בהסרה של כל הרכיבים שצורפו למשבצות של המפה כשהם נוספו למפה. לדוגמה, אם צירפתם event listeners לשכבות-על של משבצות מפה, אתם צריכים להסיר אותם כאן.

השיטה getTile() פועלת כבקר הראשי לקביעת האריחים שייטענו באזור תצוגה נתון.

סוגי מפות בסיסיות

סוגי מפות שיוצרים בדרך הזו יכולים להיות עצמאיים או לשמש כשכבות-על בשילוב עם סוגי מפות אחרים. סוגי מפות עצמאיים נקראים סוגי מפות בסיס. יכול להיות שתרצו שממשק ה-API יתייחס ל-MapType בהתאמה אישית כמו לכל סוג אחר של מפת בסיס (ROADMAP,‏ TERRAIN וכו'). כדי לעשות את זה, מוסיפים את MapType המותאם אישית לנכס mapTypes של Map. סוג הנכס הוא 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;
לצפייה בדוגמה

דוגמה לניסיון

סוגי מפות שמוצגות בשכבת-על

חלק מסוגי המפות מיועדים לפעול על גבי סוגי מפות קיימים. סוגי מפות כאלה יכולים לכלול שכבות שקופות שמציינות נקודות עניין או להציג למשתמש נתונים נוספים.

במקרים כאלה, לא רוצים שסוג המפה יטופל כישות נפרדת אלא כשכבת-על.  כדי לעשות את זה, מוסיפים את סוג המיפוי ל-MapType קיים ישירות באמצעות המאפיין overlayMapTypes של Map. הנכס הזה מכיל MVCArray של MapType. כל סוגי המפות (מפת בסיס ומפת שכבת-על) עוברים רינדור בשכבה mapPane. סוגי מפות של שכבות-על יוצגו מעל מפת הבסיס שאליה הם מצורפים, בסדר שבו הם מופיעים במערך Map.overlayMapTypes (שכבות-על עם ערכי אינדקס גבוהים יותר מוצגות לפני שכבות-על עם ערכי אינדקס נמוכים יותר).

הדוגמה הבאה זהה לדוגמה הקודמת, אבל יצרנו בה שכבת כיסוי של משבצות MapType מעל סוג המפה 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;
לצפייה בדוגמה

דוגמה לניסיון

סוגים של מפות תמונה

הטמעה של MapType שתשמש כסוג של מפת בסיס יכולה להיות משימה ארוכה ומייגעת. ה-API מספק מחלקה מיוחדת שמטמיעה את הממשק MapType עבור סוגי המפות הנפוצים ביותר: סוגי מפות שמורכבות מאריחים שמבוססים על קובצי תמונה בודדים.

המחלקות האלה, כולל המחלקה ImageMapType, מורכבות ממפרט של אובייקט ImageMapTypeOptions שמגדיר את מאפייני החובה הבאים:

  • tileSize (חובה) מציין את גודל המשבצת (מסוג google.maps.Size). הגדלים צריכים להיות מלבניים, אבל לא בהכרח ריבועיים.
  • getTileUrl (חובה) מציין את הפונקציה, שבדרך כלל מסופקת כמחרוזת של פונקציה מוטבעת, כדי לטפל בבחירה של משבצת התמונה המתאימה על סמך קואורדינטות העולם ורמת הזום שסופקו.

הקוד הבא מיישם ImageMapType בסיסי באמצעות משבצות של הירח מ-Google. בדוגמה נעשה שימוש בפונקציית נורמליזציה כדי לוודא שהמשבצות חוזרות לאורך ציר 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;
לצפייה בדוגמה

דוגמה לניסיון

תחזיות

כדור הארץ הוא כדור תלת-ממדי (בערך), ומפה היא משטח דו-ממדי שטוח. המפה שמוצגת בממשק API של JavaScript במפות Google, כמו כל מפה שטוחה של כדור הארץ, היא הטלה של הכדור על משטח שטוח. במילים פשוטות, אפשר להגדיר היטל כמיפוי של ערכי קו רוחב/אורך לקואורדינטות במפה של ההיטל.

ההטלות ב-Maps JavaScript API צריכות להטמיע את הממשק Projection. הטמעה של Projection חייבת לספק לא רק מיפוי ממערכת קואורדינטות אחת למערכת אחרת, אלא מיפוי דו-כיווני. כלומר, צריך להגדיר איך לתרגם מקואורדינטות של כדור הארץ (אובייקטים של LatLng) למערכת הקואורדינטות העולמית של המחלקה Projection, וממערכת הקואורדינטות העולמית בחזרה לקואורדינטות של כדור הארץ. מפות Google משתמשת בהטלת מרקטור כדי ליצור את המפות שלה מנתונים גאוגרפיים ולהמיר אירועים במפה לקואורדינטות גאוגרפיות. אפשר לקבל את התחזית הזו על ידי קריאה ל-getProjection() ב-Map (או בכל אחד מסוגי הבסיס הרגילים של MapType). ברוב המקרים, התקן Projection הזה יספיק, אבל אפשר גם להגדיר ולהשתמש בהטלות מותאמות אישית משלכם.

יישום של תחזית

כשמטמיעים הקרנה בהתאמה אישית, צריך להגדיר כמה דברים:

  • הנוסחאות למיפוי קואורדינטות של קו רוחב וקו אורך למישור קרטזיאני, והנוסחאות המתאימות למיפוי ממישור קרטזיאני לקואורדינטות של קו רוחב וקו אורך. (ממשק Projection תומך רק בהמרות לקואורדינטות קרטזיות).
  • גודל הבסיס של קטע המפה. כל המשבצות חייבות להיות מלבניות.
  • הגודל של מפה ברמת העולם באמצעות קבוצת משבצות הבסיס ברמת זום 0. שימו לב: במפות שמורכבות ממשבצת אחת ברמת זום 0, גודל העולם וגודל משבצת הבסיס זהים.

המרות של מערכות קואורדינטות בהטלות

כל הטלה מספקת שתי שיטות לתרגום בין שתי מערכות הקואורדינטות האלה, שמאפשרות לכם להמיר בין קואורדינטות גיאוגרפיות לקואורדינטות עולמיות:

  • השיטה Projection.fromLatLngToPoint() ממירה ערך LatLng לקואורדינטה עולמית. השיטה הזו משמשת למיקום שכבות-על במפה (ולמיקום המפה עצמה).
  • השיטה Projection.fromPointToLatLng() ממירה קואורדינטה עולמית לערך LatLng. השיטה הזו משמשת להמרת אירועים כמו קליקים שמתרחשים במפה לקואורדינטות גאוגרפיות.

מפות Google מניחות שההטלות הן ישרות.

בדרך כלל, אפשר להשתמש בהטלה בשני מקרים: כדי ליצור מפה של העולם או כדי ליצור מפה של אזור מקומי. במקרה הראשון, צריך לוודא שההטלה שלכם היא גם ישרה וגם רגילה בכל קווי האורך. חלק מההטלות (במיוחד הטלות חרוטיות) עשויות להיות 'נורמליות באופן מקומי' (כלומר, מצביעות צפונה) אבל להיות שונות מהצפון האמיתי. לדוגמה, ככל שהמפה ממוקמת רחוק יותר ביחס לקו אורך מסוים. אפשר להשתמש בהקרנה כזו באופן מקומי, אבל צריך לזכור שההקרנה לא מדויקת, ושגיאות בהמרה יהיו ברורות יותר ככל שיהיה מרחק גדול יותר בין קו האורך של ההפניה לבין קו האורך של המיקום.

בחירת משבצות במפה בחיזויים

ההטלות שימושיות לא רק לקביעת המיקומים של מקומות או שכבות-על, אלא גם למיקום של משבצות המפה עצמן. ממשק API של JavaScript במפות Google מעבד מפות בסיס באמצעות ממשק MapType שבו צריך להצהיר על מאפיין projection לזיהוי ההטלה של המפה ועל שיטה getTile() לאחזור משבצות מפה על סמך ערכי קואורדינטות של משבצת. קואורדינטות של אריחים מבוססות על גודל האריח הבסיסי (שחייב להיות מלבני) ועל 'גודל העולם' של המפה, שהוא גודל הפיקסלים של עולם המפה ברמת זום 0. (במפות שמורכבות ממשבצת אחת ברמת זום 0, גודל המשבצת וגודל העולם זהים).

מגדירים את גודל אריח הבסיס במאפיין MapType's tileSize. אתם מגדירים את גודל העולם באופן מרומז בשיטות fromLatLngToPoint() ו-fromPointToLatLng() של ההטלה.

מכיוון שהבחירה של התמונה תלויה בערכים האלה, כדאי לתת שמות לתמונות שאפשר לבחור באופן פרוגרמטי בהתאם לערכים האלה, למשל map_zoom_tileX_tileY.png.

בדוגמה הבאה מוגדר ImageMapType באמצעות ההטלה 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
const mapElement = document.querySelector("gmp-map") as google.maps.MapElement;
let innerMap;

async function initMap() {
  // 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.
  initGallPeters();
  innerMap.mapTypes.set("gallPeters", gallPetersMapType);
  innerMap.setMapTypeId("gallPeters");

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

  innerMap.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.
  innerMap.data.setStyle((feature) => {
    return {
      title: feature.getProperty("name") as string,
      optimized: false,
    };
  });
  innerMap.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 (
        "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" },
    },
  ],
};

initMap();

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 initMap() {
    // 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.
    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: " +
                Math.round(event.latLng.lat()) +
                ", " +
                "lng: " +
                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);
}
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 ("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" },
        },
    ],
};
initMap();
לצפייה בדוגמה

דוגמה לניסיון