پوشش های سفارشی

پلتفرم مورد نظر را انتخاب کنید: اندروید، iOS، جاوا اسکریپت

مقدمه

پوشش‌ها، اشیایی روی نقشه هستند که به مختصات عرض/طول جغرافیایی گره خورده‌اند، بنابراین وقتی نقشه را می‌کشیم یا بزرگنمایی می‌کنیم، حرکت می‌کنند. برای اطلاعات در مورد انواع پوشش از پیش تعریف شده، به ترسیم روی نقشه مراجعه کنید.

API جاوا اسکریپت Maps یک کلاس OverlayView برای ایجاد پوشش‌های سفارشی شما ارائه می‌دهد. OverlayView یک کلاس پایه است که چندین متد را ارائه می‌دهد که باید هنگام ایجاد پوشش‌های خود پیاده‌سازی کنید. این کلاس همچنین چند متد ارائه می‌دهد که امکان ترجمه بین مختصات صفحه و مکان‌های روی نقشه را فراهم می‌کند.

یک پوشش سفارشی اضافه کنید

در اینجا خلاصه‌ای از مراحل مورد نیاز برای ایجاد یک پوشش سفارشی آورده شده است:

  • prototype شیء overlay سفارشی خود را روی یک نمونه جدید از google.maps.OverlayView() تنظیم کنید. در واقع، این کار کلاس overlay را زیرکلاس می‌کند.
  • یک سازنده برای پوشش سفارشی خود ایجاد کنید و پارامترهای اولیه را تنظیم کنید.
  • یک متد onAdd() را در نمونه اولیه خود پیاده‌سازی کنید و لایه رویی را به نقشه متصل کنید. OverlayView.onAdd() زمانی فراخوانی می‌شود که نقشه برای اتصال لایه رویی آماده باشد.
  • یک متد draw() را در نمونه اولیه خود پیاده‌سازی کنید و نمایش بصری شیء خود را مدیریت کنید. OverlayView.draw() زمانی که شیء برای اولین بار نمایش داده می‌شود، فراخوانی می‌شود.
  • همچنین باید یک متد onRemove() برای پاک کردن هر عنصری که درون لایه پوششی اضافه کرده‌اید، پیاده‌سازی کنید.

در زیر جزئیات بیشتری در مورد هر مرحله آمده است. می‌توانید کد نمونه کامل و کاربردی را مشاهده کنید: view example code .

زیرکلاس پوشش

مثال زیر از OverlayView برای ایجاد یک تصویر ساده با روکش استفاده می‌کند.

حالا یک سازنده برای کلاس USGSOverlay ایجاد می‌کنیم و پارامترهای ارسالی را به عنوان ویژگی‌های شیء جدید مقداردهی اولیه می‌کنیم.

تایپ اسکریپت

/**
 * 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;
  }

جاوا اسکریپت

/**
 * 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 را دریافت نکند. (پنل 0).
  • overlayLayer شامل چندخطی‌ها، چندضلعی‌ها، لایه‌های رویی زمین و لایه‌های رویی کاشی است. ممکن است رویدادهای DOM را دریافت نکند. (صفحه ۱).
  • markerLayer حاوی نشانگرها است. ممکن است رویدادهای DOM را دریافت نکند. (صفحه ۲).
  • overlayMouseTarget شامل عناصری است که رویدادهای DOM را دریافت می‌کنند. (صفحه ۳).
  • floatPane شامل پنجره اطلاعات است. این پنجره بالاتر از همه پوشش‌های نقشه قرار دارد. (صفحه ۴).

از آنجا که تصویر ما یک "پوشش زمین" است، از پنل overlayLayer استفاده خواهیم کرد. وقتی این پنل را داشتیم، شیء خود را به عنوان یک فرزند به آن متصل می‌کنیم.

تایپ اسکریپت

/**
 * 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);
}

جاوا اسکریپت

/**
 * 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 مربوط به overlay را با استفاده getProjection() بازیابی می‌کنیم و مختصات دقیقی را که باید نقاط بالا سمت راست و پایین سمت چپ شیء را در آن قرار دهیم، محاسبه می‌کنیم. سپس می‌توانیم اندازه <div> را تغییر دهیم. در عوض، این کار باعث تغییر اندازه تصویر می‌شود تا با مرزهایی که در سازنده overlay مشخص کرده‌ایم، مطابقت داشته باشد.

تایپ اسکریپت

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";
  }
}

جاوا اسکریپت

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() اضافه می‌کنیم تا پوشش را به طور کامل از نقشه حذف کنیم.

تایپ اسکریپت

/**
 * 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;
  }
}

جاوا اسکریپت

/**
 * 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() را به نمونه اولیه overlay اضافه می‌کند که قابلیت مشاهده کانتینر <div> را تغییر می‌دهند. علاوه بر این، یک متد toggleDOM() اضافه می‌کنیم که overlay را به/از نقشه متصل یا جدا می‌کند.

تایپ اسکریپت

/**
 *  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);
  }
}

جاوا اسکریپت

/**
 *  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 ، کنترل‌های دکمه به نقشه اضافه می‌شوند.

تایپ اسکریپت

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);

جاوا اسکریپت

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);

نمونه کد کامل

کد نمونه کامل در زیر آمده است:

تایپ اسکریپت

// 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;

جاوا اسکریپت

// 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;

سی‌اس‌اس

/* 
 * 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>
  <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>

نمونه را امتحان کنید