تراكبات مخصصة

تنظيم صفحاتك في مجموعات يمكنك حفظ المحتوى وتصنيفه حسب إعداداتك المفضّلة.
اختَر النظام الأساسي: Android iOS JavaScript

المقدمة

العناصر المركّبة هي عناصر على الخريطة مرتبطة بإحداثيات خطوط الطول/العرض، لذا تتحرك عند سحب الخريطة أو تكبيرها/تصغيرها. للحصول على معلومات حول أنواع التراكبات المحددة مسبقًا، يمكنك الاطلاع على الرسم على الخريطة.

توفّر واجهة برمجة تطبيقات JavaScript لـ "خرائط Google" فئة 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;
  }

لا يمكننا إرفاق هذا التراكب بالخريطة في مُنشئ التراكب. أولاً، نحتاج إلى التأكد من توفر جميع أجزاء الخريطة، لأنها تحدد الترتيب الذي تظهر به العناصر على الخريطة. وتقدّم واجهة برمجة التطبيقات طريقة مساعدّة تشير إلى حدوث هذا الأمر. سنتناول هذه الطريقة في القسم التالي.

إعداد التراكب

عند إنشاء التراكب لأول مرة وجاهزة للعرض، علينا إرفاقه بالخريطة من خلال نموذج العناصر في المستند. تشير واجهة برمجة التطبيقات إلى أنه قد تمت إضافة التراكب إلى الخريطة عن طريق استدعاء أسلوب onAdd() المركب. لمعالجة هذه الطريقة، ننشئ <div> لتثبيت الصورة، وإضافة عنصر <img>، وإرفاقه بـ <div>، ثم إرفاق التراكب بإحدى أجزاء الخريطة. الجزء هو عقدة داخل شجرة نموذج العناصر في المستند.

تحدِّد الأجزاء من النوع MapPanes ترتيب تكديس الطبقات المختلفة على الخريطة. وتتوفّر الأجزاء التالية، ويتم سردها بالترتيب الذي يتم فيها تكديسها من الأسفل إلى الأعلى:

  • يمثّل mapPane الجزء السفلي فوق الأجزاء. قد لا يتلقى أحداث DOM. (العرض الشامل 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);
}

رسم التراكب

تجدر الإشارة إلى أننا لم نستدعي أي عرض مرئي خاص في الرمز أعلاه. تستدعي واجهة برمجة التطبيقات طريقة 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
     with https://www.npmjs.com/package/@googlemaps/js-api-loader.
    -->
    <script
      src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg&callback=initMap&v=weekly"
      defer
    ></script>
  </body>
</html>

جرّب عيّنة