ספריית Geometry

  1. סקירה כללית
  2. מושגים של גיאומטריה כדורית
    1. פונקציות מרחק ושטח
    2. פונקציות ניווט
  3. קידוד גיאומטריה
  4. פונקציות של פוליגונים וקווי פוליגונים
    1. containsLocation()
    2. isLocationOnEdge()

סקירה כללית

המושגים במסמך הזה מתייחסים לתכונות שזמינות רק בספרייה google.maps.geometry. הספרייה הזו לא נטענת כברירת מחדל כשאתם טוענים את Maps JavaScript API, אבל צריך לציין אותה במפורש באמצעות פרמטר libraries של Bootstrap. מידע נוסף זמין במאמר סקירה כללית על ספריות.

ספריית הגיאומטריה של Maps JavaScript API מספקת פונקציות שימושיות לחישוב נתונים גיאומטריים על פני כדור הארץ. הספרייה כוללת שלושה מרחבי שמות:

  • spherical מכיל כלי גיאומטריה ספרית שמאפשרים לחשב זוויות, מרחקים ואזורים לפי קוי רוחב ואורך.
  • encoding מכיל כלי שירות לקידוד ולפענוח של נתיבים של קווים פוליגונליים בהתאם לאלגוריתם של קווים פוליגונליים מקודדים.
  • poly מכיל פונקציות שירות לחישוב של פוליגונים וקווים פוליגוניים.

הספרייה google.maps.geometry לא מכילה שום כיתות, אלא שיטות סטטיות במרחבי השמות שלמעלה.

מושגים של גיאומטריה ספרית

התמונות בממשק API של JavaScript במפות Google הן דו-ממדיות ו'שטוחות'. עם זאת, כדור הארץ הוא תלת-ממדי, ולרוב הוא מוצג כאליפסואיד מעוגל או ככדור. ב-Maps API אנחנו משתמשים בספירה, וכדי לייצג את כדור הארץ על משטח שטוח דו-מימדי – כמו מסך המחשב – אנחנו משתמשים ב-Maps API ב הקרנה.

בהקרנות דו-ממדיות, לפעמים המראה מטעה. מכיוון שהקרנה של המפה מחייבת בהכרח עיוות מסוים, לרוב לא ניתן להשתמש בגיאומטריה אוקלידית פשוטה. לדוגמה, המרחק הקצר ביותר בין שתי נקודות על פני ספירה הוא לא קו ישר, אלא מעגל גדול (סוג של גיאודזית), והזווית שמרכיבת משולש על פני ספירה היא יותר מ-180 מעלות.

בגלל ההבדלים האלה, כדי לחשב פונקציות גיאומטריות על פני כדור (או על ההקרנה שלו) צריך להשתמש בגיאומטריה כדורית. כך אפשר לחשב מבנים כמו מרחק, כיוון ושטח. כלי השירות לחישוב המבנים הגיאומטריים הכדוריים האלה נכללים במרחב השמות google.maps.geometry.spherical של Maps API. במרחב השמות הזה יש שיטות סטטיות לחישוב ערכים סקלריים מקואורדינטות כדוריות (קוי רוחב ואורך).

פונקציות מרחק ושטח

המרחק בין שתי נקודות הוא אורך הדרך הקצרה ביותר ביניהן. הנתיב הקצר ביותר נקרא נתיב גיאודטי. בכדור, כל הקווים הגיאודטיים הם קטעים של מעגל גדול. כדי לחשב את המרחק הזה, צריך להפעיל את computeDistanceBetween() ולהעביר אליה שני אובייקטים מסוג LatLng.

אם יש לכם כמה מיקומים, תוכלו להשתמש במקום זאת ב- computeLength() כדי לחשב את האורך של נתיב נתון.

תוצאות המרחק מוצגות במטרים.

כדי לחשב את השטח (במטרים רבועים) של אזור פוליגונלי, צריך להפעיל את הפונקציה computeArea() ולהעביר את המערך של אובייקטים מסוג LatLng שמגדירים לולאה סגורה.

כשמנווטים בספירה, כיוון הוא הזווית של כיוון מנקודה קבועה של עיון, בדרך כלל הצפון האמיתי. ב-Google Maps API, כיוון מוגדר בזוויות מצפון אמיתי, כאשר הכיוונים נמדדים בכיוון השעון מצפון אמיתי (0 מעלות). אפשר לחשב את הכיוון הזה בין שני מיקומים באמצעות השיטה computeHeading(), ולהעביר לה שני אובייקטים מסוג LatLng של from ו-to.

אם נתונים כיוון מסוים, מיקום מוצא והמרחק לנסיעה (במטרים), אפשר לחשב את קואורדינטות היעד באמצעות computeOffset().

אם נתונים שני אובייקטים מסוג LatLng וערך בין 0 ל-1, אפשר גם לחשב יעד ביניהן באמצעות השיטה interpolate(), שמבצעת אינטרפולציה לינארית ספרית בין שני המיקומים, כאשר הערך מציין את המרחק החלקי שצריך לעבור לאורך הנתיב מהמקור ליעד.

בדוגמה הבאה נוצרות שתי קווים פוליגונים כשלוחצים על שתי נקודות במפה – קו גיאודטי אחד וקו "ישר" אחד שמחבר בין שני המיקומים – ומחושבת כיוון הנסיעה בין שתי הנקודות:

TypeScript

// This example requires the Geometry library. Include the libraries=geometry
// parameter when you first load the API. For example:
// <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=geometry">

let marker1: google.maps.Marker, marker2: google.maps.Marker;
let poly: google.maps.Polyline, geodesicPoly: google.maps.Polyline;

function initMap(): void {
  const map = new google.maps.Map(
    document.getElementById("map") as HTMLElement,
    {
      zoom: 4,
      center: { lat: 34, lng: -40.605 },
    }
  );

  map.controls[google.maps.ControlPosition.TOP_CENTER].push(
    document.getElementById("info") as HTMLElement
  );

  marker1 = new google.maps.Marker({
    map,
    draggable: true,
    position: { lat: 40.714, lng: -74.006 },
  });

  marker2 = new google.maps.Marker({
    map,
    draggable: true,
    position: { lat: 48.857, lng: 2.352 },
  });

  const bounds = new google.maps.LatLngBounds(
    marker1.getPosition() as google.maps.LatLng,
    marker2.getPosition() as google.maps.LatLng
  );

  map.fitBounds(bounds);

  google.maps.event.addListener(marker1, "position_changed", update);
  google.maps.event.addListener(marker2, "position_changed", update);

  poly = new google.maps.Polyline({
    strokeColor: "#FF0000",
    strokeOpacity: 1.0,
    strokeWeight: 3,
    map: map,
  });

  geodesicPoly = new google.maps.Polyline({
    strokeColor: "#CC0099",
    strokeOpacity: 1.0,
    strokeWeight: 3,
    geodesic: true,
    map: map,
  });

  update();
}

function update() {
  const path = [
    marker1.getPosition() as google.maps.LatLng,
    marker2.getPosition() as google.maps.LatLng,
  ];

  poly.setPath(path);
  geodesicPoly.setPath(path);

  const heading = google.maps.geometry.spherical.computeHeading(
    path[0],
    path[1]
  );

  (document.getElementById("heading") as HTMLInputElement).value =
    String(heading);
  (document.getElementById("origin") as HTMLInputElement).value = String(
    path[0]
  );
  (document.getElementById("destination") as HTMLInputElement).value = String(
    path[1]
  );
}

declare global {
  interface Window {
    initMap: () => void;
  }
}
window.initMap = initMap;

JavaScript

// This example requires the Geometry library. Include the libraries=geometry
// parameter when you first load the API. For example:
// <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=geometry">
let marker1, marker2;
let poly, geodesicPoly;

function initMap() {
  const map = new google.maps.Map(document.getElementById("map"), {
    zoom: 4,
    center: { lat: 34, lng: -40.605 },
  });

  map.controls[google.maps.ControlPosition.TOP_CENTER].push(
    document.getElementById("info"),
  );
  marker1 = new google.maps.Marker({
    map,
    draggable: true,
    position: { lat: 40.714, lng: -74.006 },
  });
  marker2 = new google.maps.Marker({
    map,
    draggable: true,
    position: { lat: 48.857, lng: 2.352 },
  });

  const bounds = new google.maps.LatLngBounds(
    marker1.getPosition(),
    marker2.getPosition(),
  );

  map.fitBounds(bounds);
  google.maps.event.addListener(marker1, "position_changed", update);
  google.maps.event.addListener(marker2, "position_changed", update);
  poly = new google.maps.Polyline({
    strokeColor: "#FF0000",
    strokeOpacity: 1.0,
    strokeWeight: 3,
    map: map,
  });
  geodesicPoly = new google.maps.Polyline({
    strokeColor: "#CC0099",
    strokeOpacity: 1.0,
    strokeWeight: 3,
    geodesic: true,
    map: map,
  });
  update();
}

function update() {
  const path = [marker1.getPosition(), marker2.getPosition()];

  poly.setPath(path);
  geodesicPoly.setPath(path);

  const heading = google.maps.geometry.spherical.computeHeading(
    path[0],
    path[1],
  );

  document.getElementById("heading").value = String(heading);
  document.getElementById("origin").value = String(path[0]);
  document.getElementById("destination").value = String(path[1]);
}

window.initMap = initMap;
להצגת דוגמה

ניסיון של דוגמה

שיטות קידוד

לרוב, נתיבים ב-Maps JavaScript API מצוינים בתור Array של אובייקטים מסוג LatLng. עם זאת, העברת מערך כזה לעיתים קרובות מכבידה על הקוד. במקום זאת, אפשר להשתמש באלגוריתם של Google לקידוד קווים פוליגונליים כדי לדחוס נתיב נתון, שאפשר לבצע לו לאחר מכן דחיסה חוזרת באמצעות פענוח.

הספרייה geometry מכילה מרחב שמות encoding לשירותים לקודד ולפענח קווים פוליגונליים.

השיטה הסטטית encodePath() מקודדת את הנתיב שצוין. אפשר להעביר מערך של LatLng או MVCArray (שחוזר על ידי Polyline.getPath()).

כדי לפענח נתיב מוצפן, צריך לבצע קריאה ל-method‏ decodePath() ולהעביר לו את המחרוזת המוצפנת.

בדוגמה הבאה מוצגת מפה של אוקספורד, מיסיסיפי. לחיצה על המפה מוסיפה נקודה לקו פוליגון. בזמן היצירה של קו הפוליגון, הקידוד שלו מופיע מתחתיו.

TypeScript

// This example requires the Geometry library. Include the libraries=geometry
// parameter when you first load the API. For example:
// <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=geometry">

function initMap(): void {
  const map = new google.maps.Map(
    document.getElementById("map") as HTMLElement,
    {
      zoom: 14,
      center: { lat: 34.366, lng: -89.519 },
    }
  );
  const poly = new google.maps.Polyline({
    strokeColor: "#000000",
    strokeOpacity: 1,
    strokeWeight: 3,
    map: map,
  });

  // Add a listener for the click event
  google.maps.event.addListener(map, "click", (event) => {
    addLatLngToPoly(event.latLng, poly);
  });
}

/**
 * Handles click events on a map, and adds a new point to the Polyline.
 * Updates the encoding text area with the path's encoded values.
 */
function addLatLngToPoly(
  latLng: google.maps.LatLng,
  poly: google.maps.Polyline
) {
  const path = poly.getPath();

  // Because path is an MVCArray, we can simply append a new coordinate
  // and it will automatically appear
  path.push(latLng);

  // Update the text field to display the polyline encodings
  const encodeString = google.maps.geometry.encoding.encodePath(path);

  if (encodeString) {
    (document.getElementById("encoded-polyline") as HTMLInputElement).value =
      encodeString;
  }
}

declare global {
  interface Window {
    initMap: () => void;
  }
}
window.initMap = initMap;

JavaScript

// This example requires the Geometry library. Include the libraries=geometry
// parameter when you first load the API. For example:
// <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=geometry">
function initMap() {
  const map = new google.maps.Map(document.getElementById("map"), {
    zoom: 14,
    center: { lat: 34.366, lng: -89.519 },
  });
  const poly = new google.maps.Polyline({
    strokeColor: "#000000",
    strokeOpacity: 1,
    strokeWeight: 3,
    map: map,
  });

  // Add a listener for the click event
  google.maps.event.addListener(map, "click", (event) => {
    addLatLngToPoly(event.latLng, poly);
  });
}

/**
 * Handles click events on a map, and adds a new point to the Polyline.
 * Updates the encoding text area with the path's encoded values.
 */
function addLatLngToPoly(latLng, poly) {
  const path = poly.getPath();

  // Because path is an MVCArray, we can simply append a new coordinate
  // and it will automatically appear
  path.push(latLng);

  // Update the text field to display the polyline encodings
  const encodeString = google.maps.geometry.encoding.encodePath(path);

  if (encodeString) {
    document.getElementById("encoded-polyline").value = encodeString;
  }
}

window.initMap = initMap;
להצגת דוגמה

ניסיון של דוגמה

פונקציות של פוליגונים וקווים פוליגונים

מרחב השמות poly של ספריית הגיאומטריה מכיל פונקציות שירות שמאפשרות לקבוע אם נקודה מסוימת נמצאת בתוך פוליגון או קו פוליגון, או ליד אחד מהם.

containsLocation()

containsLocation(point:LatLng, polygon:Polygon)

כדי לבדוק אם נקודה מסוימת נמצאת בתוך פוליגון, מעבירים את הנקודה ואת הפוליגון ל-google.maps.geometry.poly.containsLocation(). הפונקציות מחזירות את הערך true אם הנקודה נמצאת בתוך הפוליגון או על הקצה שלו.

הקוד הבא כותב 'true' למסוף הדפדפן אם הלחיצה של המשתמש נמצאת בתוך המשולש המוגדר, אחרת הוא כותב 'false'.

function initialize() {
  var mapOptions = {
    zoom: 5,
    center: new google.maps.LatLng(24.886, -70.269),
    mapTypeId: 'terrain'
  };

  var map = new google.maps.Map(document.getElementById('map'),
      mapOptions);

  var bermudaTriangle = new google.maps.Polygon({
    paths: [
      new google.maps.LatLng(25.774, -80.190),
      new google.maps.LatLng(18.466, -66.118),
      new google.maps.LatLng(32.321, -64.757)
    ]
  });

  google.maps.event.addListener(map, 'click', function(event) {
    console.log(google.maps.geometry.poly.containsLocation(event.latLng, bermudaTriangle));
  });
}

google.maps.event.addDomListener(window, 'load', initialize);

גרסה אחרת של הקוד הזה גורמת לציור של משולש כחול במפה אם הלחיצה נמצאת בתוך משולש ברמודה, ושל עיגול אדום אחרת:

להצגת דוגמה

isLocationOnEdge()

isLocationOnEdge(point:LatLng, poly:Polygon|Polyline, tolerance?:number)

כדי לקבוע אם נקודה נמצאת על קו פוליגון או לידו, או על קצה של פוליגון או לידו, מעבירים את הנקודה, את קו הפוליגון או את הפוליגון, ואם רוצים גם ערך סבילות בדרגות, ל-google.maps.geometry.poly.isLocationOnEdge(). הפונקציה מחזירה את הערך true אם המרחק בין הנקודה לנקודה הקרובה ביותר על הקו או על הקצה נמצא בטווח הסף שצוין. ערך ברירת המחדל של הסובלנות הוא 10-9 מעלות.

function initialize() {
  var myPosition = new google.maps.LatLng(46.0, -125.9);

  var mapOptions = {
    zoom: 5,
    center: myPosition,
    mapTypeId: 'terrain'
  };

  var map = new google.maps.Map(document.getElementById('map'),
      mapOptions);

  var cascadiaFault = new google.maps.Polyline({
    path: [
      new google.maps.LatLng(49.95, -128.1),
      new google.maps.LatLng(46.26, -126.3),
      new google.maps.LatLng(40.3, -125.4)
    ]
  });

  cascadiaFault.setMap(map);

  if (google.maps.geometry.poly.isLocationOnEdge(myPosition, cascadiaFault, 10e-1)) {
    alert("Relocate!");
  }
}

google.maps.event.addDomListener(window, 'load', initialize);