במסמך הזה מוסבר על סוגי המפות שאפשר להציג באמצעות Maps JavaScript API. ה-API משתמש באובייקט MapType כדי לשמור מידע על המפות האלה. MapType
הוא ממשק שמגדיר את התצוגה והשימוש באריחי המפה ואת
התרגום של מערכות הקואורדינטות מקואורדינטות המסך לקואורדינטות
העולם (במפה). כל MapType חייב להכיל כמה שיטות לטיפול באחזור ובשחרור של משבצות, ומאפיינים שמגדירים את ההתנהגות החזותית שלו.
הפעולות הפנימיות של סוגי המפות ב-Maps JavaScript API הן נושא מתקדם. רוב המפתחים יכולים להשתמש ב סוגי המפות הבסיסיים שמפורטים בהמשך. עם זאת, אתם יכולים גם לשנות את התצוגה של סוגי המפות הקיימים באמצעות מפות מסוגננות או להגדיר משבצות מפה משלכם באמצעות סוגי מפות בהתאמה אישית. כשמוסיפים סוגי מפות בהתאמה אישית, צריך להבין איך לשנות את מרשם סוגי המפות של המפה.
סוגי מפות בסיסיים
יש ארבעה סוגים של מפות ב-Maps JavaScript API. בנוסף למשבצות המוכרות של מפת הדרכים "המצוירת", Maps JavaScript API תומך גם בסוגים אחרים של מפות.
אלה סוגי המפות שזמינים ב-Maps JavaScript API:
roadmapמציג את תצוגת ברירת המחדל של מפת הדרכים עם תוויות של מפת הבסיס.satelliteמציג מפה פוטוריאליסטית שמבוססת על צילומי אוויר.hybridמציג את תצוגת מפת הלוויין עם תוויות של מפת הבסיס.terrainמציגה מפה פיזית שמבוססת על מידע על פני השטח.
כדי לשנות את סוג המפה שמשמש את Map, מגדירים את המאפיין mapTypeId שלו, בתוך ה-constructor על ידי הגדרת האובייקט Map options שלו, או על ידי קריאה ל-method 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 באמצעות מזהה.
כדי לנהל את ההפניות האלה, Maps JavaScript API משתמש במרשם של סוגי מפות, שמוסבר בהמשך.
מפות תלת-ממדיות
Maps JavaScript API תומך בתמונות תלת-ממדיות. רבות מהפעולות ואפשרויות ההתאמה האישית שזמינות במפות דו-ממדיות זמינות גם במפות תלת-ממדיות.
מידע נוסף על מפות תלת-ממדיות זמין בסקירה הכללית של מפות תלת-ממדיות.
בדוגמה הבאה מוצגת תצוגת פרספקטיבה תלת-ממדית של סן פרנסיסקו:
מידע נוסף על מפות תלת-ממדיות זמין בסקירה הכללית של מפות תלת-ממדיות.
תמונות בזווית של 45° (הוצא משימוש)
בדוגמה הבאה מוצגת תצוגת פרספקטיבה של 45° של סן פרנסיסקו באמצעות מפות תלת-ממדיות:
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>שינוי הרישום של סוגי המפות
ה-mapTypeId של המפה הוא מזהה מחרוזת שמשמש לשיוך של MapType לערך ייחודי. כל אובייקט Map מכיל MapTypeRegistry שכולל את אוסף האובייקטים מסוג MapType שזמינים במפה הזו. המרשם הזה משמש לבחירת סוגי המפות שזמינים בפקד MapType של המפה, לדוגמה.
אתם לא קוראים ישירות מהרישום של סוג המפה. במקום זאת, משנים את המרשם על ידי הוספת סוגי מפות בהתאמה אישית ושיוך שלהם למזהה מחרוזת לבחירתכם. אי אפשר לשנות את סוגי המפות הבסיסיים (אבל אפשר להסיר אותם מהמפה על ידי שינוי המראה של mapTypeControlOptions שמשויך למפה).
הקוד הבא מגדיר את המפה כך שיוצגו רק שני סוגי מפות ב-mapTypeControlOptions של המפה, ומשנה את הרישום כדי להוסיף את השיוך עם המזהה הזה להטמעה בפועל של הממשק MapType.
// Modify the control to only display two maptypes, the // default ROADMAP and the custom 'mymap'. // Note that because this is an association, we // don't need to modify the MapTypeRegistry beforehand. var MY_MAPTYPE_ID = 'mymaps'; var mapOptions = { zoom: 12, center: brooklyn, mapTypeControlOptions: { mapTypeIds: ['roadmap', MY_MAPTYPE_ID] }, mapTypeId: MY_MAPTYPE_ID }; // Create our map. This creation will implicitly create a // map type registry. map = new google.maps.Map(document.getElementById('map'), mapOptions); // Create your custom map type using your own code. // (See below.) var myMapType = new MyMapType(); // Set the registry to associate 'mymap' with the // custom map type we created, and set the map to // show that map type. map.mapTypes.set(MY_MAPTYPE_ID, myMapType);
מפות מסוגננות
StyledMapType מאפשר להתאים אישית את המצגת של המפות הבסיסיות הרגילות של Google, ולשנות את התצוגה החזותית של רכיבים כמו כבישים, פארקים ואזורים בנויים כדי לשקף סגנון שונה מזה שמשמש בסוג המפה שמוגדר כברירת מחדל. ה-StyledMapType משפיע רק על סוג המפה שמוגדר כברירת מחדל, roadmap.
מידע נוסף על StyledMapType זמין במאמר שימוש בהצהרות סגנון מוטמעות ב-JSON.
סוגי מפות בהתאמה אישית
Maps JavaScript API תומך בהצגה ובניהול של סוגי מפות בהתאמה אישית, ומאפשר לכם להטמיע תמונות מפה או שכבות-על של משבצות משלכם.
יש כמה הטמעות אפשריות של סוגי מפות ב-Maps JavaScript API:
- קבוצות סטנדרטיות של קטעי מפה שמורכבות מתמונות שביחד יוצרות מפות קרטוגרפיות מלאות. קבוצות המשבצות האלה נקראות גם סוגי מפות בסיסיות. סוגי המפות האלה פועלים ומתנהגים כמו סוגי המפות שמוגדרים כברירת מחדל:
roadmap,satellite,hybridו-terrain. אפשר להוסיף את סוג המפה בהתאמה אישית למערךmapTypesשל המפה כדי לאפשר לממשק המשתמש ב-Maps JavaScript API להתייחס לסוג המפה בהתאמה אישית כסוג מפה רגיל (לדוגמה, על ידי הכללה שלו בפקד MapType). - שכבות-על של קטעי מפה שמוצגות מעל סוגים קיימים של מפות בסיסיות. בדרך כלל, סוגי המפות האלה משמשים להרחבה של סוג מפה קיים כדי להציג מידע נוסף, ולעיתים קרובות הם מוגבלים למיקומים ספציפיים ו/או לרמות זום ספציפיות. חשוב לזכור שהמשבצות האלה יכולות להיות שקופות, כך שתוכלו להוסיף תכונות למפות קיימות.
- סוגי מפות שאינן תמונות, שמאפשרים לשנות את הצגת המידע במפה ברמה הבסיסית ביותר.
כל אחת מהאפשרויות האלה מסתמכת על יצירת מחלקה שמטמיעה את הממשק של MapType. בנוסף, המחלקה
ImageMapType מספקת התנהגות מובנית מסוימת כדי לפשט את היצירה של סוגי מפות של תמונות.
הממשק של MapType
לפני שיוצרים מחלקות שמטמיעות את MapType,
חשוב להבין איך מפות Google קובעת
את הקואורדינטות ומחליטה אילו חלקים של המפה להציג. צריך להטמיע לוגיקה דומה לכל סוג של מפת בסיס או שכבת-על.
מדריך בנושא קואורדינטות של מפות וקטעי מפות
סוגי מפות בהתאמה אישית חייבים להטמיע את הממשק MapType. הממשק הזה מציין מאפיינים ושיטות מסוימים שמאפשרים ל-API לשלוח בקשות לסוגי המפות שלכם, כשה-API קובע שצריך להציג משבצות מפה באזור התצוגה וברמת הזום הנוכחיים. אתם מטפלים בבקשות האלה כדי להחליט איזה משבצת לטעון.
הערה: אתם יכולים ליצור מחלקה משלכם כדי להטמיע את הממשק הזה. לחלופין, אם יש לכם תמונות תואמות, אתם יכולים להשתמש במחלקה
ImageMapType שכבר מטמיעה את הממשק הזה.
במחלקות שמטמיעות את הממשק MapType, צריך להגדיר ולאכלס את המאפיינים הבאים:
-
tileSize(חובה) מציין את גודל המשבצת (מסוגgoogle.maps.Size). הגדלים חייבים להיות מלבניים, אבל לא בהכרח ריבועיים. maxZoom(חובה) מציין את רמת הזום המקסימלית שבה יוצגו משבצות של סוג המפה הזה.-
minZoom(אופציונלי) מציין את רמת הזום המינימלית שבה יוצג אריח של סוג המפה הזה. ערך ברירת המחדל הוא0, שמציין שאין רמת זום מינימלית. -
name(אופציונלי) מציין את השם של סוג המפה הזה. המאפיין הזה נדרש רק אם רוצים שסוג המפה הזה יהיה ניתן לבחירה בתוך פקד MapType. (ראה אפשרויות בקרה.) -
alt(אופציונלי) מציין את הטקסט החלופי לסוג המפה הזה, שמוצג כטקסט כשמציבים את הסמן מעל המפה. המאפיין הזה נדרש רק אם רוצים שסוג המפה הזה יהיה ניתן לבחירה בתוך פקד MapType. (לעיון באפשרויות השליטה).
בנוסף, במחלקות שמטמיעות את הממשק של MapType צריך להטמיע את השיטות הבאות:
-
getTile()(חובה) נקראת בכל פעם שה-API קובע שהמפה צריכה להציג משבצות חדשות באזור התצוגה הנתון. ה-methodgetTile()חייבת לכלול את החתימה הבאה:getTile(tileCoord:Point,zoom:number,ownerDocument:Document):Nodeה-API קובע אם הוא צריך לקרוא ל-
getTile()על סמך המאפייניםtileSize,minZoomו-maxZoomשלMapTypeועל סמך אזור התצוגה ורמת הזום הנוכחיים של המפה. ה-handler של ה-method הזו אמור להחזיר רכיב HTML בהתאם לקואורדינטה, לרמת הזום ולרכיב ה-DOM שצוינו, שעליהם צריך להוסיף את תמונת המשבצת. -
releaseTile()(אופציונלי) נקראת בכל פעם שה-API קובע שצריך להסיר משבצת מהמפה כי היא לא מוצגת יותר. לשיטה הזו צריכה להיות החתימה הבאה:releaseTile(tile:Node)בדרך כלל צריך לטפל בהסרה של כל הרכיבים שצורפו לאריחי המפה כשמוסיפים אותם למפה. לדוגמה, אם הוספתם רכיבי event listener לשכבות-על של משבצות במפה, צריך להסיר אותם כאן.
השיטה 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;
תחזיות
כדור הארץ הוא כדור תלת-ממדי (בערך), ואילו מפה היא משטח דו-ממדי שטוח. המפה שמוצגת ב-Maps JavaScript API, כמו כל מפה שטוחה של כדור הארץ, היא היטל של הכדור על משטח שטוח. במילים פשוטות, אפשר להגדיר היטל כמיפוי של ערכי קו רוחב/קו אורך לקואורדינטות במפה של ההיטל.
היטלים ב-Maps JavaScript API חייבים להטמיע את הממשק Projection. הטמעה של Projection
צריכה לספק לא רק מיפוי ממערכת קואורדינטות
אחת לאחרת, אלא מיפוי דו-כיווני. כלומר, צריך להגדיר איך לתרגם מקואורדינטות של כדור הארץ (אובייקטים מסוג LatLng) למערכת הקואורדינטות העולמית של המחלקה Projection, וממערכת הקואורדינטות העולמית בחזרה לקואורדינטות של כדור הארץ.
מפות Google משתמשות בהיטל מרקטור כדי ליצור את המפות מנתונים גיאוגרפיים ולהמיר אירועים במפה לקואורדינטות גיאוגרפיות. כדי לקבל את ההיטל הזה, צריך להפעיל את getProjection() ב-Map (או בכל אחד מהסוגים הבסיסיים הרגילים של MapType). לרוב השימושים,
ההיטל הסטנדרטי הזה של Projection יספיק, אבל אתם יכולים גם
להגדיר היטלים מותאמים אישית משלכם ולהשתמש בהם.
הטמעת היטל
כשמטמיעים היטל בהתאמה אישית, צריך להגדיר כמה דברים:
- הנוסחאות למיפוי קואורדינטות של קווי רוחב ואורך למישור קרטזי, והנוסחאות התואמות למיפוי ממישור קרטזי לקואורדינטות של קווי רוחב ואורך. (הממשק של
Projectionתומך רק בטרנספורמציות לקואורדינטות ישרות זווית). - גודל המשבצת הבסיסי. כל המשבצות צריכות להיות מלבניות.
- "גודל העולם" של מפה שמשתמשת בקבוצת המשבצות הבסיסית ברמת זום 0. שימו לב שבמפות שמורכבות ממשבצת אחת ברמת זום 0, גודל העולם וגודל משבצת הבסיס זהים.
טרנספורמציות של קואורדינטות בהיטלים
כל היטל מספק שתי שיטות שמתרגמות בין שתי מערכות הקואורדינטות האלה, ומאפשרות לכם להמיר בין קואורדינטות גיאוגרפיות לקואורדינטות עולמיות:
- ה-method
Projection.fromLatLngToPoint()ממיר ערך שלLatLngלקואורדינטה גלובלית. השיטה הזו משמשת למיקום שכבות-על במפה (ולמיקום המפה עצמה). - ה-method
Projection.fromPointToLatLng()ממיר קואורדינטה גלובלית לערךLatLng. השיטה הזו משמשת להמרת אירועים כמו קליקים שמתרחשים במפה לקואורדינטות גיאוגרפיות.
במפות Google, ההנחה היא שההיטלים הם ישרים.
באופן כללי, אפשר להשתמש בהיטל בשני מקרים: כדי ליצור מפה של העולם או כדי ליצור מפה של אזור מקומי. במקרה הראשון, צריך לוודא שההיטל הוא גם ישר-זווית וגם רגיל בכל קווי האורך. היטלים מסוימים (במיוחד היטלים חרוטיים) עשויים להיות "נורמליים באופן מקומי" (כלומר, להצביע צפונה) אבל לסטות מהצפון האמיתי. לדוגמה, ככל שהמפה ממוקמת רחוק יותר ביחס לקו אורך מסוים של קובץ עזר. אפשר להשתמש בהיטל כזה באופן מקומי, אבל חשוב לזכור שההיטל לא מדויק בהכרח, ושגיאות הטרנספורמציה יהיו ברורות יותר ככל שמתרחקים מקו האורך של קובץ העזר.
בחירת משבצת מפה בהיטלים
היטלים שימושיים לא רק לקביעת המיקומים של מקומות או שכבות-על, אלא גם למיקום של משבצות המפה עצמן.
ממשק ה-API של מפות Google ל-JavaScript מעבד מפות בסיסיות באמצעות ממשק MapType, שבו צריך להצהיר גם על מאפיין projection לזיהוי ההיטל של המפה וגם על שיטת getTile() לאחזור קטעי מפה על סמך ערכי הקואורדינטות של הקטע במפה. הקואורדינטות של קטע במפה מבוססות על הגודל הבסיסי של הקטע (שחייב להיות מלבני) ועל "גודל העולם" של המפה, שהוא גודל הפיקסלים של עולם המפה ברמת זום 0. (במפות שמורכבות ממשבצת אחת ברמת זום 0, גודל המשבצת וגודל העולם זהים).
אתם מגדירים את גודל המשבצת הבסיסי במאפיין tileSize של MapType. אתם מגדירים את גודל העולם באופן משתמע בתוך השיטות 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')!; 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();