מבוא
שכבות-על הן אובייקטים במפה שקשורים לקואורדינטות של קווי אורך וקווי רוחב, ולכן הן זזות כשגוררים את המפה או משנים את רמת הזום שלה. מידע על סוגי שכבות-על מוגדרים מראש זמין במאמר ציור במפה.
Maps JavaScript API מספק מחלקה OverlayView
ליצירת שכבות-על מותאמות אישית. OverlayView
היא מחלקת בסיס שמספקת כמה שיטות שצריך להטמיע כשיוצרים שכבות-על. המחלקות מספקות גם כמה שיטות שמאפשרות לתרגם בין קואורדינטות של מסך לבין מיקומים במפה.
הוספת שכבת-על מותאמת אישית
לפניכם סיכום של השלבים הנדרשים ליצירת שכבת-על בהתאמה אישית:
- מגדירים את
prototype
של אובייקט שכבת-העל המותאמת אישית למופע חדש שלgoogle.maps.OverlayView()
. בפועל, הפעולה הזו תיצור מחלקת משנה של מחלקת שכבת-העל. - יוצרים קונסטרוקטור לשכבת העל המותאמת אישית ומגדירים פרמטרים לאתחול.
- מטמיעים שיטה של
onAdd()
באב-טיפוס ומצרפים את שכבת העל למפה. המערכת תקרא ל-OverlayView.onAdd()
כשהמפה תהיה מוכנה לצירוף שכבת העל. - מטמיעים שיטת
draw()
באב-טיפוס ומטפלים בתצוגה החזותית של האובייקט. המערכת תקרא ל-OverlayView.draw()
כשהאובייקט יוצג בפעם הראשונה. - כדאי גם להטמיע שיטה של
onRemove()
כדי לנקות את כל הרכיבים שהוספתם בתוך שכבת העל.
בהמשך מפורטים פרטים נוספים על כל שלב. תוכלו לראות את הקוד המלא לדוגמה: הצגת קוד לדוגמה.
יצירת מחלקת משנה של שכבת-העל
בדוגמה הבאה נעשה שימוש ב-OverlayView
כדי ליצור שכבת-על פשוטה של תמונה.
עכשיו יוצרים קונסטרוקטור למחלקה USGSOverlay
ומאתחלים את הפרמטרים שהועברו כמאפיינים של האובייקט החדש.
TypeScript
/** * The custom USGSOverlay object contains the USGS image, * the bounds of the image, and a reference to the map. */ class USGSOverlay extends google.maps.OverlayView { private bounds: google.maps.LatLngBounds; private image: string; private div?: HTMLElement; constructor(bounds: google.maps.LatLngBounds, image: string) { super(); this.bounds = bounds; this.image = image; }
JavaScript
/** * The custom USGSOverlay object contains the USGS image, * the bounds of the image, and a reference to the map. */ class USGSOverlay extends google.maps.OverlayView { bounds; image; div; constructor(bounds, image) { super(); this.bounds = bounds; this.image = image; }
עדיין אי אפשר לצרף את שכבת העל הזו למפה ב-constructor של שכבת העל. קודם כול, צריך לוודא שכל החלוניות של המפה זמינות, כי הן מציינות את הסדר שבו האובייקטים מוצגים במפה. ה-API מספק שיטת עזר שמציינת שזה קרה. בקטע הבא נסביר על השיטה הזו.
אתחול שכבת-העל
כששכבת העל מופעלת בפעם הראשונה ומוכנה להצגה, צריך לצרף אותה למפה באמצעות ה-DOM של הדפדפן. ה-API מציין שהשכבה נוספה למפה על ידי הפעלת השיטה onAdd()
של השכבה. כדי לטפל בשיטה הזו, יוצרים <div>
שיכיל את התמונה, מוסיפים רכיב <img>
, מצרפים אותו ל-<div>
ואז מצרפים את שכבת העל לאחד מהחלוניות של המפה. חלונית היא צומת בעץ ה-DOM.
החלוניות, מהסוג
MapPanes
, מציינות את
סדר השכבות השונות במפה. אלה החלוניות שזמינות, והן ממוספרות לפי הסדר שבו הן מוצגות מלמטה למעלה:
mapPane
הוא החלונית התחתונה, והיא נמצאת מעל הרכיבים המוטמעים. יכול להיות שלא יתקבלו אירועי DOM. (חלונית 0).-
overlayLayer
מכיל קווי פוליגון, פוליגונים, שכבות-על של קרקע ושכבות-על של משבצות. יכול להיות שהיא לא תקבל אירועי DOM. (חלונית 1). -
markerLayer
מכיל סמנים. יכול להיות שהיא לא תקבל אירועי DOM. (חלונית 2). -
overlayMouseTarget
מכיל רכיבים שמקבלים אירועי DOM. (חלונית 3). - השדה
floatPane
מכיל את חלון המידע. הוא מופיע מעל כל שכבות העל של המפה. (Pane 4).
התמונה שלנו היא 'שכבת-על של הקרקע', לכן נשתמש בחלונית overlayLayer
. אחרי שיוצרים את החלונית, מצמידים אליה את האובייקט כצאצא.
TypeScript
/** * onAdd is called when the map's panes are ready and the overlay has been * added to the map. */ onAdd() { this.div = document.createElement("div"); this.div.style.borderStyle = "none"; this.div.style.borderWidth = "0px"; this.div.style.position = "absolute"; // Create the img element and attach it to the div. const img = document.createElement("img"); img.src = this.image; img.style.width = "100%"; img.style.height = "100%"; img.style.position = "absolute"; this.div.appendChild(img); // Add the element to the "overlayLayer" pane. const panes = this.getPanes()!; panes.overlayLayer.appendChild(this.div); }
JavaScript
/** * onAdd is called when the map's panes are ready and the overlay has been * added to the map. */ onAdd() { this.div = document.createElement("div"); this.div.style.borderStyle = "none"; this.div.style.borderWidth = "0px"; this.div.style.position = "absolute"; // Create the img element and attach it to the div. const img = document.createElement("img"); img.src = this.image; img.style.width = "100%"; img.style.height = "100%"; img.style.position = "absolute"; this.div.appendChild(img); // Add the element to the "overlayLayer" pane. const panes = this.getPanes(); panes.overlayLayer.appendChild(this.div); }
ציור שכבת-העל
שימו לב שלא הפעלנו בקוד שלמעלה תצוגה חזותית מיוחדת. ה-API מפעיל שיטה נפרדת של draw()
בשכבת העל בכל פעם שהוא צריך לצייר את שכבת העל במפה, כולל כשמוסיפים אותה בפעם הראשונה.
לכן, נטמיע את השיטה draw()
, נאחזר את MapCanvasProjection
של שכבת העל באמצעות getProjection()
ונחשב את הקואורדינטות המדויקות שבהן צריך לעגן את הנקודות הימנית העליונה והשמאלית התחתונה של האובייקט.
אחרי זה אפשר לשנות את הגודל של <div>
. כתוצאה מכך, גודל התמונה ישתנה כך שיתאים לגבולות שציינו בבונה של שכבת העל.
TypeScript
draw() { // We use the south-west and north-east // coordinates of the overlay to peg it to the correct position and size. // To do this, we need to retrieve the projection from the overlay. const overlayProjection = this.getProjection(); // Retrieve the south-west and north-east coordinates of this overlay // in LatLngs and convert them to pixel coordinates. // We'll use these coordinates to resize the div. const sw = overlayProjection.fromLatLngToDivPixel( this.bounds.getSouthWest() )!; const ne = overlayProjection.fromLatLngToDivPixel( this.bounds.getNorthEast() )!; // Resize the image's div to fit the indicated dimensions. if (this.div) { this.div.style.left = sw.x + "px"; this.div.style.top = ne.y + "px"; this.div.style.width = ne.x - sw.x + "px"; this.div.style.height = sw.y - ne.y + "px"; } }
JavaScript
draw() { // We use the south-west and north-east // coordinates of the overlay to peg it to the correct position and size. // To do this, we need to retrieve the projection from the overlay. const overlayProjection = this.getProjection(); // Retrieve the south-west and north-east coordinates of this overlay // in LatLngs and convert them to pixel coordinates. // We'll use these coordinates to resize the div. const sw = overlayProjection.fromLatLngToDivPixel( this.bounds.getSouthWest(), ); const ne = overlayProjection.fromLatLngToDivPixel( this.bounds.getNorthEast(), ); // Resize the image's div to fit the indicated dimensions. if (this.div) { this.div.style.left = sw.x + "px"; this.div.style.top = ne.y + "px"; this.div.style.width = ne.x - sw.x + "px"; this.div.style.height = sw.y - ne.y + "px"; } }
הסרת שכבת-על מותאמת אישית
אנחנו גם מוסיפים onRemove()
שיטה להסרה נקייה של שכבת העל מהמפה.
TypeScript
/** * The onRemove() method will be called automatically from the API if * we ever set the overlay's map property to 'null'. */ onRemove() { if (this.div) { (this.div.parentNode as HTMLElement).removeChild(this.div); delete this.div; } }
JavaScript
/** * The onRemove() method will be called automatically from the API if * we ever set the overlay's map property to 'null'. */ onRemove() { if (this.div) { this.div.parentNode.removeChild(this.div); delete this.div; } }
הסתרה והצגה של שכבת-על מותאמת אישית
אם אתם רוצים להסתיר או להציג שכבת-על במקום פשוט ליצור או להסיר אותה,
אתם יכולים להטמיע שיטות משלכם hide()
ו-show()
כדי לשנות את הנראות של שכבת-העל. אפשר גם לנתק את שכבת העל מ-DOM של המפה, אבל הפעולה הזו קצת יותר יקרה. שימו לב: אם תצרפו מחדש את שכבת העל ל-DOM של המפה, הפונקציה onAdd()
של שכבת העל תופעל מחדש.
בדוגמה הבאה מוספות שיטות hide()
ו-show()
לאב-טיפוס של שכבת העל, שמשמשות להחלפת מצב החשיפה של הקונטיינר <div>
. בנוסף, אנחנו מוסיפים את השיטה toggleDOM()
, שמצרפת או מפרידה את שכבת העל למפה או ממנה.
TypeScript
/** * Set the visibility to 'hidden' or 'visible'. */ hide() { if (this.div) { this.div.style.visibility = "hidden"; } } show() { if (this.div) { this.div.style.visibility = "visible"; } } toggle() { if (this.div) { if (this.div.style.visibility === "hidden") { this.show(); } else { this.hide(); } } } toggleDOM(map: google.maps.Map) { if (this.getMap()) { this.setMap(null); } else { this.setMap(map); } }
JavaScript
/** * Set the visibility to 'hidden' or 'visible'. */ hide() { if (this.div) { this.div.style.visibility = "hidden"; } } show() { if (this.div) { this.div.style.visibility = "visible"; } } toggle() { if (this.div) { if (this.div.style.visibility === "hidden") { this.show(); } else { this.hide(); } } } toggleDOM(map) { if (this.getMap()) { this.setMap(null); } else { this.setMap(map); } }
הוספת לחצני בקרה
כדי להפעיל את השיטות toggle
ו-toggleDom
, מוסיפים למפה לחצני בקרה.
TypeScript
const toggleButton = document.createElement("button"); toggleButton.textContent = "Toggle"; toggleButton.classList.add("custom-map-control-button"); const toggleDOMButton = document.createElement("button"); toggleDOMButton.textContent = "Toggle DOM Attachment"; toggleDOMButton.classList.add("custom-map-control-button"); toggleButton.addEventListener("click", () => { overlay.toggle(); }); toggleDOMButton.addEventListener("click", () => { overlay.toggleDOM(map); }); map.controls[google.maps.ControlPosition.TOP_RIGHT].push(toggleDOMButton); map.controls[google.maps.ControlPosition.TOP_RIGHT].push(toggleButton);
JavaScript
const toggleButton = document.createElement("button"); toggleButton.textContent = "Toggle"; toggleButton.classList.add("custom-map-control-button"); const toggleDOMButton = document.createElement("button"); toggleDOMButton.textContent = "Toggle DOM Attachment"; toggleDOMButton.classList.add("custom-map-control-button"); toggleButton.addEventListener("click", () => { overlay.toggle(); }); toggleDOMButton.addEventListener("click", () => { overlay.toggleDOM(map); }); map.controls[google.maps.ControlPosition.TOP_RIGHT].push(toggleDOMButton); map.controls[google.maps.ControlPosition.TOP_RIGHT].push(toggleButton);
קוד לדוגמה מלא
בהמשך מופיע קוד לדוגמה מלא:
TypeScript
// This example adds hide() and show() methods to a custom overlay's prototype. // These methods toggle the visibility of the container <div>. // overlay to or from the map. function initMap(): void { const map = new google.maps.Map( document.getElementById("map") as HTMLElement, { zoom: 11, center: { lat: 62.323907, lng: -150.109291 }, mapTypeId: "satellite", } ); const bounds = new google.maps.LatLngBounds( new google.maps.LatLng(62.281819, -150.287132), new google.maps.LatLng(62.400471, -150.005608) ); // The photograph is courtesy of the U.S. Geological Survey. let image = "https://developers.google.com/maps/documentation/javascript/"; image += "examples/full/images/talkeetna.png"; /** * The custom USGSOverlay object contains the USGS image, * the bounds of the image, and a reference to the map. */ class USGSOverlay extends google.maps.OverlayView { private bounds: google.maps.LatLngBounds; private image: string; private div?: HTMLElement; constructor(bounds: google.maps.LatLngBounds, image: string) { super(); this.bounds = bounds; this.image = image; } /** * onAdd is called when the map's panes are ready and the overlay has been * added to the map. */ onAdd() { this.div = document.createElement("div"); this.div.style.borderStyle = "none"; this.div.style.borderWidth = "0px"; this.div.style.position = "absolute"; // Create the img element and attach it to the div. const img = document.createElement("img"); img.src = this.image; img.style.width = "100%"; img.style.height = "100%"; img.style.position = "absolute"; this.div.appendChild(img); // Add the element to the "overlayLayer" pane. const panes = this.getPanes()!; panes.overlayLayer.appendChild(this.div); } draw() { // We use the south-west and north-east // coordinates of the overlay to peg it to the correct position and size. // To do this, we need to retrieve the projection from the overlay. const overlayProjection = this.getProjection(); // Retrieve the south-west and north-east coordinates of this overlay // in LatLngs and convert them to pixel coordinates. // We'll use these coordinates to resize the div. const sw = overlayProjection.fromLatLngToDivPixel( this.bounds.getSouthWest() )!; const ne = overlayProjection.fromLatLngToDivPixel( this.bounds.getNorthEast() )!; // Resize the image's div to fit the indicated dimensions. if (this.div) { this.div.style.left = sw.x + "px"; this.div.style.top = ne.y + "px"; this.div.style.width = ne.x - sw.x + "px"; this.div.style.height = sw.y - ne.y + "px"; } } /** * The onRemove() method will be called automatically from the API if * we ever set the overlay's map property to 'null'. */ onRemove() { if (this.div) { (this.div.parentNode as HTMLElement).removeChild(this.div); delete this.div; } } /** * Set the visibility to 'hidden' or 'visible'. */ hide() { if (this.div) { this.div.style.visibility = "hidden"; } } show() { if (this.div) { this.div.style.visibility = "visible"; } } toggle() { if (this.div) { if (this.div.style.visibility === "hidden") { this.show(); } else { this.hide(); } } } toggleDOM(map: google.maps.Map) { if (this.getMap()) { this.setMap(null); } else { this.setMap(map); } } } const overlay: USGSOverlay = new USGSOverlay(bounds, image); overlay.setMap(map); const toggleButton = document.createElement("button"); toggleButton.textContent = "Toggle"; toggleButton.classList.add("custom-map-control-button"); const toggleDOMButton = document.createElement("button"); toggleDOMButton.textContent = "Toggle DOM Attachment"; toggleDOMButton.classList.add("custom-map-control-button"); toggleButton.addEventListener("click", () => { overlay.toggle(); }); toggleDOMButton.addEventListener("click", () => { overlay.toggleDOM(map); }); map.controls[google.maps.ControlPosition.TOP_RIGHT].push(toggleDOMButton); map.controls[google.maps.ControlPosition.TOP_RIGHT].push(toggleButton); } declare global { interface Window { initMap: () => void; } } window.initMap = initMap;
JavaScript
// This example adds hide() and show() methods to a custom overlay's prototype. // These methods toggle the visibility of the container <div>. // overlay to or from the map. function initMap() { const map = new google.maps.Map(document.getElementById("map"), { zoom: 11, center: { lat: 62.323907, lng: -150.109291 }, mapTypeId: "satellite", }); const bounds = new google.maps.LatLngBounds( new google.maps.LatLng(62.281819, -150.287132), new google.maps.LatLng(62.400471, -150.005608), ); // The photograph is courtesy of the U.S. Geological Survey. let image = "https://developers.google.com/maps/documentation/javascript/"; image += "examples/full/images/talkeetna.png"; /** * The custom USGSOverlay object contains the USGS image, * the bounds of the image, and a reference to the map. */ class USGSOverlay extends google.maps.OverlayView { bounds; image; div; constructor(bounds, image) { super(); this.bounds = bounds; this.image = image; } /** * onAdd is called when the map's panes are ready and the overlay has been * added to the map. */ onAdd() { this.div = document.createElement("div"); this.div.style.borderStyle = "none"; this.div.style.borderWidth = "0px"; this.div.style.position = "absolute"; // Create the img element and attach it to the div. const img = document.createElement("img"); img.src = this.image; img.style.width = "100%"; img.style.height = "100%"; img.style.position = "absolute"; this.div.appendChild(img); // Add the element to the "overlayLayer" pane. const panes = this.getPanes(); panes.overlayLayer.appendChild(this.div); } draw() { // We use the south-west and north-east // coordinates of the overlay to peg it to the correct position and size. // To do this, we need to retrieve the projection from the overlay. const overlayProjection = this.getProjection(); // Retrieve the south-west and north-east coordinates of this overlay // in LatLngs and convert them to pixel coordinates. // We'll use these coordinates to resize the div. const sw = overlayProjection.fromLatLngToDivPixel( this.bounds.getSouthWest(), ); const ne = overlayProjection.fromLatLngToDivPixel( this.bounds.getNorthEast(), ); // Resize the image's div to fit the indicated dimensions. if (this.div) { this.div.style.left = sw.x + "px"; this.div.style.top = ne.y + "px"; this.div.style.width = ne.x - sw.x + "px"; this.div.style.height = sw.y - ne.y + "px"; } } /** * The onRemove() method will be called automatically from the API if * we ever set the overlay's map property to 'null'. */ onRemove() { if (this.div) { this.div.parentNode.removeChild(this.div); delete this.div; } } /** * Set the visibility to 'hidden' or 'visible'. */ hide() { if (this.div) { this.div.style.visibility = "hidden"; } } show() { if (this.div) { this.div.style.visibility = "visible"; } } toggle() { if (this.div) { if (this.div.style.visibility === "hidden") { this.show(); } else { this.hide(); } } } toggleDOM(map) { if (this.getMap()) { this.setMap(null); } else { this.setMap(map); } } } const overlay = new USGSOverlay(bounds, image); overlay.setMap(map); const toggleButton = document.createElement("button"); toggleButton.textContent = "Toggle"; toggleButton.classList.add("custom-map-control-button"); const toggleDOMButton = document.createElement("button"); toggleDOMButton.textContent = "Toggle DOM Attachment"; toggleDOMButton.classList.add("custom-map-control-button"); toggleButton.addEventListener("click", () => { overlay.toggle(); }); toggleDOMButton.addEventListener("click", () => { overlay.toggleDOM(map); }); map.controls[google.maps.ControlPosition.TOP_RIGHT].push(toggleDOMButton); map.controls[google.maps.ControlPosition.TOP_RIGHT].push(toggleButton); } window.initMap = initMap;
CSS
/* * Always set the map height explicitly to define the size of the div element * that contains the map. */ #map { height: 100%; } /* * Optional: Makes the sample page fill the window. */ html, body { height: 100%; margin: 0; padding: 0; } .custom-map-control-button { background-color: #fff; border: 0; border-radius: 2px; box-shadow: 0 1px 4px -1px rgba(0, 0, 0, 0.3); margin: 10px; padding: 0 0.5em; font: 400 18px Roboto, Arial, sans-serif; overflow: hidden; height: 40px; cursor: pointer; } .custom-map-control-button:hover { background: rgb(235, 235, 235); }
HTML
<html> <head> <title>Showing/Hiding Overlays</title> <link rel="stylesheet" type="text/css" href="./style.css" /> <script type="module" src="./index.js"></script> </head> <body> <div id="map"></div> <!-- The `defer` attribute causes the script to execute after the full HTML document has been parsed. For non-blocking uses, avoiding race conditions, and consistent behavior across browsers, consider loading using Promises. See https://developers.google.com/maps/documentation/javascript/load-maps-js-api for more information. --> <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg&callback=initMap&v=weekly" defer ></script> </body> </html>