מבוא
שכבות-על הן אובייקטים במפה שקשורים לקואורדינטות של קו רוחב/אורך, כך שהן זזות כשגוררים או מגדילים את המפה. למידע נוסף על סוגי שכבות-על מוגדרים מראש, ראו שרטוט במפה.
Maps JavaScript API מספק את המחלקה OverlayView
כדי ליצור שכבות-על בהתאמה אישית. OverlayView
היא מחלקה בסיסית שמספקת כמה שיטות שצריך ליישם כשיוצרים את שכבות-העל. בכיתה יש גם כמה שיטות לתרגם בין קואורדינטות במסך לבין מיקומים במפה.
הוספת שכבת-על מותאמת אישית
לפניכם סיכום של השלבים הנדרשים ליצירת שכבת-על מותאמת אישית:
- מגדירים את
prototype
של אובייקט שכבת-העל בהתאמה אישית למופע חדש שלgoogle.maps.OverlayView()
. בפועל, הסיווג של שכבת-העל יתבצע במחלקה משנית. - יוצרים בנאי לשכבת-על בהתאמה אישית ומגדירים פרמטרים של אתחול.
- צריך להטמיע שיטת
onAdd()
באב הטיפוס ולצרף את שכבת-העל למפה. תתבצע קריאה ל-OverlayView.onAdd()
כשהמפה תהיה מוכנה לצירוף של שכבת-העל. - צריך להטמיע שיטת
draw()
באב הטיפוס ולטפל בתצוגה החזותית של האובייקט. תתבצע קריאה ל-OverlayView.draw()
כשהאובייקט יוצג בפעם הראשונה. - כדאי גם להשתמש בשיטה
onRemove()
כדי לנקות את הרכיבים שהוספתם בשכבת-העל.
בהמשך מופיעים פרטים נוספים על כל שלב. תוכלו לראות את הקוד המלא לדוגמה: view example code.
מחלקת משנה של שכבת-העל
בדוגמה הבאה השתמשנו ב-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; }
בשלב זה אין אפשרות לצרף שכבת-על זו למפה בבנאי שכבת-העל. ראשית, עלינו לוודא שכל החלוניות של המפה זמינות, כי הן מציינות את הסדר שבו האובייקטים מוצגים במפה. ה-API כולל שיטה שעוזרת להבין שמצב זה התרחש. נטפל בשיטה הזו בקטע הבא.
הפעלה של שכבת-העל
כאשר שכבת-העל נוצרת לראשונה ומוכנה להצגה, עלינו לצרף אותה למפה דרך ה-DOM של הדפדפן. ה-API מציין ששכבת-העל נוספה למפה על ידי הפעלת השיטה onAdd()
של שכבת-העל. כדי להשתמש בשיטה הזו, יוצרים <div>
כדי לשמור את התמונה, מוסיפים רכיב <img>
, מצרפים אותו ל-<div>
ומצרפים את שכבת-העל לאחת מהחלוניות במפה. חלונית היא צומת בעץ ה-DOM.
בחלוניות, מסוג MapPanes
, מציינים את סדר הסידור בשכבות שונות במפה. החלוניות הבאות זמינות ומפורטות לפי הסדר שבו הן מסודרות מלמטה למעלה:
mapPane
היא החלונית הנמוכה ביותר ונמצאת מעל המשבצות. יכול להיות שהוא לא יקבל אירועי DOM. (P פאן 0).overlayLayer
מכיל קווים מרובים, פוליגונים, שכבות-על של קרקע ושכבות-על של אריחים. יכול להיות שהוא לא יקבל אירועי DOM. (חלונית 1).markerLayer
מכיל סמנים. יכול להיות שהוא לא יקבל אירועי DOM. (חלונית 2).overlayMouseTarget
מכיל רכיבים שמקבלים אירועי DOM. (חלונית 3).floatPane
מכיל את חלון המידע. הוא מופיע מעל כל שכבות-העל של המפה. (לחצן 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> <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script> <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 callback 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>