מעבר מגרסה 3 לגרסה 4 של Geocoding

מפתחים באזור הכלכלי האירופי (EEA)

בגרסה 4 של Geocoding API יש כמה שיטות חדשות שמחליפות את הפונקציונליות של גרסה 3. במדריך הזה מוסבר איך להעביר את האפליקציה לשימוש בשיטות החדשות של גרסה 4.

אתם יכולים להשתמש במפתחות ה-API הקיימים שלכם עם ה-methods החדשות של גרסה 4. עם זאת, אם ביקשתם להגדיל את המכסה בגרסה 3 של ה-API, אתם צריכים לבקש להגדיל את המכסה בממשקי ה-API החדשים בגרסה 4.

מעבר מ-v3 Forward Geocoding

אם אתם משתמשים ב-v3 Geocoding כדי להמיר כתובות לקואורדינטות, אתם צריכים לעבור לשיטה Geocode an address בגרסה 4, שמקבלת בקשת GET.

בגרסה 4 של ה-API יש שינויים בשמות, במבנה ובתמיכה בכמה פרמטרים. מומלץ מאוד להשתמש במסכת שדה כדי לציין את השדות שרוצים להחזיר בתגובה.

בקשה לשינוי פרמטרים

פרמטר בגרסה 3 פרמטר v4 הערות
address, components address כתובת לא מובנית (גרסה 3 address) מועברת עכשיו בנתיב כתובת ה-URL. אפשר להעביר רכיבי כתובת מובנים (גרסה 3 components) כפרמטרים של שאילתה ב-address.*. איך משחזרים מסנני רכיבים מגרסה 3
bounds locationBias.rectangle השם השתנה, המבנה השתנה לאובייקט.
language languageCode השם שונה. אם משמיטים את הפרמטר, בגרסה 3 נעשה שימוש בכותרת Accept-Language או בערך ברירת המחדל en, ואילו בגרסה 4 נעשה שימוש בשפה המקומית ובכתב המקומי של המיקום כברירת מחדל.
region regionCode השם שונה. אם משמיטים את הפרמטר, ערך ברירת המחדל בגרסה 3 הוא us, ואילו בגרסה 4 אין ערך ברירת מחדל.
extra_computations הוסר הוחלפה ב-SearchDestinations.

שינויים בשדה התשובה

שדה בגרסה 3 שדה בגרסה 4 הערות
status, error_message הוסר בגרסה 4 נעשה שימוש בקודי סטטוס של HTTP ובגופי שגיאות.
results.address_components.long_name / results.address_components.short_name results.addressComponents.longText / results.addressComponents.shortText השם שונה.
results.geometry.location_type results.granularity השם שונה.
results.geometry.location results.location שמות השדות: lat/lng -> latitude/longitude.
results.geometry.viewport results.viewport שמות השדות: northeast/southwest -> high/low.
results.postcode_localities results.postalCodeLocalities השם שונה. עכשיו מוחזר עבור יישוב אחד או יותר (נדרשת גרסה 3 >1).
results.partial_match הוסר
חדש results.addressComponents.languageCode השפה של רכיב הכתובת הספציפי.
חדש results.bounds גבולות מפורשים באמצעות high/low.
חדש results.place שם המשאב של המקום.
חדש results.postalAddress אובייקט PostalAddress מובנה.

שחזור מסנני רכיבים בגרסה 3

המרת כתובות לקואורדינטות (geocoding) בגרסה 3 של Geocoding כללה פרמטר components שאפשר סינון קשיח של תוצאות לרכיבים ספציפיים (למשל, components=country:US). המרת כתובות לקואורדינטות בגרסה 4 לא תומכת בסוג הזה של סינון קשיח בפרמטרים של הבקשה. בגרסה 4, אפשר לספק את פרטי הכתובת כמחרוזת אחת לא מובנית בנתיב, או כפרמטרים מובנים של שאילתה כמו address.addressLines, address.locality, address.administrativeArea, address.postalCode ו-address.regionCode. פרמטרים מובְנים לא פועלים כמסננים קשיחים. במקום זאת, כל הרכיבים שסופקו משולבים כדי ליצור את הכתובת המלאה שה-API ינסה להמיר לקואורדינטות. ה-API מחפש את ההתאמה הטובה ביותר לכתובת המלאה שצוינה בכל הרכיבים. אם תספקו את הרכיבים בצורה מובנית, ה-API יוכל להבין טוב יותר את הכוונה, במיוחד אם פרטי הכתובת נאספים משדות נפרדים בטופס. כך ה-API יכול לטפל בשגיאות הקלדה קלות או במידע לא ברור בכל רכיב, כי סוג המידע בכל שדה ברור.

כדי להשיג התנהגות דומה למסנני הרכיבים הקשיחים של גרסה 3, צריך להטמיע סינון בצד הלקוח במערך results שמוחזר על ידי ה-API של גרסה 4, על סמך האובייקטים postalAddress ו-addressComponents בתגובה.

בטבלה הבאה מוצגת ההתאמה הטובה ביותר בין המסננים של components בגרסה 3 לבין השדות של postalAddress בגרסה 4:

מסנן רכיבים בגרסה 3 שדה תשובה בגרסה 4
country postalAddress.regionCode
postal_code postalAddress.postalCode
administrative_area postalAddress.administrativeArea או addressComponents

דוגמאות לסינון בצד הלקוח

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

סינון לפי מדינה

מתאימים בין address.regionCode מהבקשה לבין postalAddress.regionCode בכל תוצאה. שימו לב: ה-API מקבל קודי אזור באותיות קטנות בבקשה, אבל תמיד מחזיר אותם באותיות רישיות בתגובה, לכן צריך לנרמל את הקלט לאותיות רישיות לפני ההשוואה.

דוגמה לקוד Python
def filter_by_country(results, region_code):
    return [
        res for res in results
        if res.get('postalAddress', {}).get('regionCode') == region_code.upper()
    ]

# Example usage:
# v4_response = ... # API call response
# filtered = filter_by_country(v4_response.get('results', []), 'US')
קוד לדוגמה ב-Node.js
function filterByCountry(results, regionCode) {
    return results.filter(res => res.postalAddress?.regionCode === regionCode.toUpperCase());
}

// Example usage:
// const v4Response = ... # API call response
// const filtered = filterByCountry(v4Response.results, 'US');
סינון לפי אזור מנהלי

מתאימים בין address.administrativeArea מהבקשה לבין postalAddress.administrativeArea בכל תוצאה. בדומה לקודי מדינות, צריך לנרמל את הקלט לאותיות רישיות לפני ההשוואה. האפשרות הזו אמינה רק אם הפרמטר administrativeArea בבקשה כולל קיצור סטנדרטי של 2 אותיות. יכול להיות שהערך של postalAddress.administrativeArea בתגובה לא יתאים ישירות לסיומות של קודי ISO 3166-2, מכמה סיבות. קודם כול, באזורים מסוימים, חלוקת המשנה שתואמת לקוד ISO 3166-2 היא לא חלק מהייצוג הסטנדרטי של כתובות דואר. לדוגמה, בספרד, האזור המנהלי ברמה 1 (למשל, "CN" עבור Canarias) עשוי להופיע ב-addressComponents, אבל postalAddress.administrativeArea עשוי להכיל את האזור ברמה 2 (למשל, "Santa Cruz de Tenerife"). שנית, באזורים מסוימים לא נהוג להשתמש בקיצור של החלוקה המנהלית, והיא מיוצגת ב-postalAddress.administrativeArea בשם ארוך יותר (מסיבות דומות, יכול להיות ש-addressComponents.shortText של רכיב הכתובת שתואם לחלוקה המנהלית לא יתאים לסיומת של קוד ISO 3166-2 של החלוקה המנהלית). בנוסף, במקרים מסוימים, החלוקה המשנית עשויה להיות מיוצגת ברכיב הכתובת של administrative_area_level_n כאשר n גדול מ-1. כדי לקבל את התוצאות המקיפות ביותר, צריך לבדוק אם יש התאמה גם ב-postalAddress.administrativeArea וגם בכל addressComponents עם סוג administrative_area_level_n (למשל administrative_area_level_1).

דוגמה לקוד Python
def filter_by_admin_area(results, admin_area):
    target = admin_area.upper()
    filtered = []
    for res in results:
        # Check postalAddress
        pa_aa = res.get('postalAddress', {}).get('administrativeArea', '')
        if pa_aa == target:
            filtered.append(res)
            continue
        # Check all administrative area levels in addressComponents
        for comp in res.get('addressComponents', []):
            is_admin_level = any(t.startswith('administrative_area_level_')
                               for t in comp.get('types', []))
            if is_admin_level:
                short = comp.get('shortText', '')
                if short == target:
                    filtered.append(res)
                    break
    return filtered

# Example usage:
# filtered = filter_by_admin_area(v4_response.get('results', []), 'CA')
קוד לדוגמה ב-Node.js
function filterByAdminArea(results, adminArea) {
    const target = adminArea.toUpperCase();
    return results.filter(res => {
        // Check postalAddress.administrativeArea
        if (res.postalAddress?.administrativeArea === target) {
            return true;
        }
        // Check addressComponents for any administrative area level
        return res.addressComponents?.some(c => {
            const isAdmin = c.types?.some(t => t.startsWith('administrative_area_level_'));
            return isAdmin && c.shortText === target;
        });
    });
}

// Example usage:
// const filtered = filterByAdminArea(v4Response.results, 'CA');
סינון לפי מיקוד

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

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

הדוגמה שמוצגת כאן מתייחסת למיקודים בארה"ב ומאפשרת התאמה ל-5 הספרות הראשונות, גם אם המיקוד שסופק או הוחזר כולל 4 ספרות נוספות.

דוגמה לקוד Python (התמקדות במיקוד בארה"ב)
import re

def normalize_postal_code_us(pc):
    # Basic normalization for US: uppercase, alphanumeric only
    if not pc:
        return ""
    normalized = re.sub(r'[^A-Z0-9]', '', pc.upper())
    # Return only the first 5 digits for US ZIP codes
    return normalized[:5]

def filter_by_postal_code_us(results, postal_code):
    normalized_input = normalize_postal_code_us(postal_code)
    if not normalized_input:
        return []
    filtered_results = []
    for res in results:
        pa_pc = res.get('postalAddress', {}).get('postalCode', '')
        if normalize_postal_code_us(pa_pc) == normalized_input:
            filtered_results.append(res)
    return filtered_results

# Example usage:
# Matches '94043', '94043-1351', '940431351'
# filtered = filter_by_postal_code_us(v4_response.get('results', []), '94043')
# filtered = filter_by_postal_code_us(v4_response.get('results', []), '94043-1234')
דוגמה לקוד Node.js (מתמקד במיקוד בארה"ב)
function normalizePostalCodeUs(pc) {
    // Basic normalization for US: uppercase, alphanumeric only
    const normalized = (pc || '').toUpperCase().replace(/[^A-Z0-9]/g, '');
    // Return only the first 5 digits for US ZIP codes
    return normalized.substring(0, 5);
}

function filterByPostalCodeUs(results, postalCode) {
    const normalizedInput = normalizePostalCodeUs(postalCode);
    if (!normalizedInput) {
        return [];
    }
    return results.filter(res => {
        const paPc = res.postalAddress?.postalCode || '';
        return normalizePostalCodeUs(paPc) === normalizedInput;
    });
}

// Example usage:
// Matches '94043', '94043-1351', '940431351'
// const filtered = filterByPostalCodeUs(v4Response.results, '94043');
// const filtered = filterByPostalCodeUs(v4Response.results, '94043-1234');

מעבר מ-v3 Reverse Geocoding

אם אתם משתמשים ב-v3 Reverse Geocoding כדי להמיר קואורדינטות לכתובות, אתם צריכים לעבור ל-v4 Reverse geocode a location, שמקבלת בקשת GET.

בגרסה 4 של ה-API יש שינויים בשמות, במבנה ובתמיכה בכמה פרמטרים. מומלץ מאוד להשתמש במסכת שדה כדי לציין את השדות שרוצים להחזיר בתגובה.

בקשה לשינוי פרמטרים

פרמטר בגרסה 3 פרמטר v4 הערות
language languageCode השם שונה. אם משמיטים את הפרמטר, בגרסה 3 נעשה שימוש בכותרת Accept-Language או בערך ברירת המחדל en, ואילו בגרסה 4 נעשה שימוש בשפה המקומית ובכתב המקומי של המיקום כברירת מחדל.
region regionCode השם שונה. אם משמיטים את הפרמטר, ערך ברירת המחדל בגרסה 3 הוא us, ואילו בגרסה 4 אין ערך ברירת מחדל.
result_type types שונה השם; משתמש בפרמטרים חוזרים של שאילתות.
location_type granularity שונה השם; משתמש בפרמטרים חוזרים של שאילתות.
extra_computations הוסר הוחלפה ב-SearchDestinations.

שינויים בשדה התשובה

שדה בגרסה 3 שדה בגרסה 4 הערות
status, error_message הוסר בגרסה 4 נעשה שימוש בקודי סטטוס של HTTP ובגופי שגיאות.
results.address_components.long_name / results.address_components.short_name results.addressComponents.longText / results.addressComponents.shortText השם שונה.
results.geometry.location_type results.granularity השם שונה.
results.geometry.location results.location שמות השדות: lat/lng -> latitude/longitude.
results.geometry.viewport results.viewport שמות השדות: northeast/southwest -> high/low.
חדש results.addressComponents.languageCode השפה של רכיב הכתובת הספציפי.
חדש results.bounds גבולות מפורשים באמצעות high/low.
חדש results.place שם המשאב של המקום.
חדש results.postalAddress אובייקט PostalAddress מובנה.

מעבר מתיאורי כתובות בגרסה 3

ב-Geocoding API v3, העברת extra_computations=ADDRESS_DESCRIPTORS החזירה אובייקט address_descriptor שמכיל את landmarks[] ו-areas[]. בגרסה 4 של Geocoding API, היכולת הזו מוחלפת בשיטה SearchDestinations (POST /v4/geocode/destinations), שמחזירה הקשר מרחבי עשיר ישירות בתוך כל אובייקט Destination.

בטבלה הבאה מפורטים השדות בגרסה הקודמת (v3) של address_descriptor והשדות המקבילים בגרסה החדשה (v4) של SearchDestinations:

שדה address_descriptor בגרסה 3 שדה v4 SearchDestinations הערות לגבי ההעברה
landmarks[] destinations[].landmarks[] מבקשים destinations.landmarks בX-Goog-FieldMask.
landmarks[].place_id landmarks[].place.placeId עטוף בתוך האובייקט place (PlaceView).
landmarks[].display_name landmarks[].place.displayName טקסט מותאם לשוק המקומי (text ו-languageCode).
landmarks[].types landmarks[].place.types מערך של מחרוזות מסוג מקום.
landmarks[].spatial_relationship landmarks[].relationalDescription במקום ערך enum גולמי (ACROSS_THE_ROAD, AROUND_THE_CORNER), גרסה 4 מחזירה ביטוי מקומי שקריא לאנשים (למשל, "Across from Central Park").
landmarks[].straight_line_distance_meters landmarks[].straightLineDistanceMeters השם שונה ל-camelCase קטן.
landmarks[].travel_distance_meters landmarks[].travelDistanceMeters השם שונה ל-camelCase קטן.
‫areas[] (containment enum) ‫destinations[].containingPlaces[] וגם subDestinations[] השווה ערך הקרוב ביותר הזמין להיררכיה מבנית: מתחמים, קמפוסים, קניונים ובניינים מקיפים מוחזרים כאובייקטים מובנים של PlaceView ב-containingPlaces[] וב-subDestinations[] (כאשר structureType מוגדר ל-GROUNDS או ל-BUILDING וקווי מתאר אופציונליים של displayPolygon). שימו לב שאין תחליף ישיר של 1:1 לאזורי קרבה של שכונה/מחוז או לערך הטיפוס המוגדר (enum) של גרסה 3 (containment) (NEAR, WITHIN, OUTSKIRTS).

מעבר מהמרת כתובות לקואורדינטות של מקומות בגרסה 3

אם אתם משתמשים ב-place_id כדי לקבל את הכתובת של מזהה מקום ספציפי באמצעות Geocoding v3, אתם צריכים לעבור לשיטה Place Geocoding בגרסה v4, שמקבלת בקשת GET.

בגרסה 4 של ה-API יש שינויים בשמות, במבנה ובתמיכה בכמה פרמטרים. מומלץ מאוד להשתמש במסכת שדה כדי לציין את השדות שרוצים להחזיר בתגובה.

בקשה לשינוי פרמטרים

פרמטר בגרסה 3 פרמטר v4 הערות
place_id השדה place בבקשת הפרוטוקול מזהה המקום מסופק עכשיו כפרמטר של הנתיב places/{place}, לדוגמה: https://geocode.googleapis.com/v4/geocode/places/ChIJj61dQgK6j4AR4GeTYWZsKWw. השדה הזה ממופה לשדה המקום בבקשה הבסיסית.
language languageCode השם שונה. אם משמיטים את הפרמטר, בגרסה 3 נעשה שימוש בכותרת Accept-Language או בערך ברירת המחדל en, ואילו בגרסה 4 נעשה שימוש בשפה המקומית ובכתב המקומי של המיקום כברירת מחדל.
region regionCode השם שונה. אם משמיטים את הפרמטר, ערך ברירת המחדל בגרסה 3 הוא us, ואילו בגרסה 4 אין ערך ברירת מחדל.

שינויים בשדה התשובה

שדה בגרסה 3 שדה בגרסה 4 הערות
status, error_message הוסר בגרסה 4 נעשה שימוש בקודי סטטוס של HTTP ובגופי שגיאות.
results (root) גרסה 4 מחזירה אובייקט תוצאה יחיד, ולא מערך results.
results.address_components.long_name / results.address_components.short_name addressComponents.longText / addressComponents.shortText השם שונה.
results.geometry.location_type granularity השם שונה.
results.geometry.location location שמות השדות: lat/lng -> latitude/longitude.
results.geometry.viewport viewport שמות השדות: northeast/southwest -> high/low.
results.postcode_localities postalCodeLocalities השם שונה. עכשיו מוחזר עבור יישוב אחד או יותר (נדרשת גרסה 3 >1).
חדש addressComponents.languageCode השפה של רכיב הכתובת הספציפי.
חדש bounds גבולות מפורשים באמצעות high/low.
חדש place שם המשאב של המקום.
חדש postalAddress אובייקט PostalAddress מובנה.

מעבר מהמרת כתובות לקואורדינטות (geocoding) של נתונים לקהילה ממוקדת אל Destinations

התכונות הבאות ב-Geocoding API v3 מוחלפות ב-method‏ SearchDestinations של Geocoding API v4:

  • כניסות
  • נקודות ניווט
  • קווי מתאר של מבנים
  • שטחים

אם השתמשתם ב-Geocoding API v3 בשביל התכונות שלמעלה, אתם יכולים להיעזר במסמך הזה כדי להשתמש ב-method‏ SearchDestinations במקום זאת כדי לקבל את התכונות האלה. במסמך הזה מוסבר איפה בתגובה של SearchDestinations אפשר למצוא את המאפיינים האלה, ומה ההבדלים בייצוג של המאפיינים האלה בתגובות של ה-API בין Geocoding API v3 לבין השיטה SearchDestinations של Geocoding API v4.

כניסות

כדי לקבל את הכניסות שמשויכות ל-destination, משתמשים בשדה destination.entrances.

לכל entrance ב-destination.entrances יש את השדות הבאים (שמחליפים את הסכימה הקודמת של גרסה 3, buildings.entrances):

  • ‫displayName – שדה אופציונלי חדש שמכיל שם קריא לאנשים של הכניסה, לדוגמה "Gate B".
  • ‫location – מיקום מסוג LatLng באמצעות latitude ו-longitude (מחליף את lat/lng בגרסה 3).
  • ‫tags – זהה לשדה tags מכניסות של Geocoding API v3 (כמו PREFERRED).
  • ‫place – שם המשאב של המקום (לדוגמה, places/ChIJ...), שמחליף את השדה building_place_id מ-Geocoding API v3. בגרסה 4, המקום הזה יכול לייצג כל מבנה של יעד (בניין, קמפוס או פארק), ולא רק בניין.

כדי לקבל את נקודות הניווט שמשויכות ל-destination, משתמשים בשדה destination.navigationPoints.

לכל navigationPoint ב-destination.navigationPoints יש את השדות הבאים (שמחליפים את הסכימה הקודמת של גרסה 3, navigation_points):

  • ‫navigationPointToken – מחרוזת טוקן מוצפנת שמשמשת לניתוב לנקודת הניווט הספציפית הזו ב-method‏ ComputeRoutes (Waypoint.navigationPointToken) של Routes API. מידע נוסף זמין במאמר ניתוב באמצעות טוקנים של נקודות ניווט.
  • ‫displayName – שדה אופציונלי חדש שמכיל שם קריא לאנשים של נקודת הניווט, לדוגמה "5th Ave".
  • ‫location – מיקום מסוג LatLng באמצעות latitude ו-longitude.
  • ‫travelModes – דומה לשדה restricted_travel_modes ב-Geocoding API v3, אבל בגרסה 4 מצוינים אמצעי התחבורה המותרים (DRIVE,‏ WALK) במקום אמצעי התחבורה המוגבלים.
  • ‫usage – שדה חדש שמציין את תרחישי השימוש הנתמכים של ההגעה לנקודת הניווט (למשל PICKUP,‏ DROPOFF או PARKING).

קווי מתאר של מבנים

כדי לקבל את קווי המתאר של המבנים שמשויכים ל-destination, משתמשים בשדה displayPolygon של האובייקטים placeView ב-destination שמייצגים מבנים (מחליפים את המערך buildings.building_outlines מדור קודם בגרסה 3). לכל placeView, בודקים אם מדובר בבניין באמצעות השדה placeView.structureType. אם סוג המבנה הוא BUILDING, אפשר לקבל את קווי המתאר מהשדה placeView.displayPolygon. ב-placeView יהיו גם שדות נוספים לבניין שלא היו ב-Geocoding API v3.

אובייקט destination יכול להכיל אובייקט placeView שמייצג בניין בשדות הבאים:

  • ‫destination.primary – המקום הראשי של היעד.
  • destination.containingPlaces – שדה חוזר שמכיל מקומות גדולים יותר ש "מכילים" את המקום הראשי. לדוגמה, אם המקום הראשי הוא subpremise, בדרך כלל containingPlaces מכיל את placeView שמייצג את הבניין.
  • ‫destination.subDestinations – שדה חוזר שמכיל יעדי משנה של המקום הראשי (לדוגמה, דירות או סוויטות בודדות בתוך בניין).

שימו לב שהפורמט של placeView.displayPolygon תואם לפורמט של קווי המתאר של המבנה ב-Geocoding API גרסה 3, שהוא פורמט GeoJSON, באמצעות פורמט RFC 7946.

שטחים

בדומה לקווי המתאר של מבנים, כדי לקבל את השטחים שמשויכים ל-destination, משתמשים בשדה displayPolygon של האובייקטים מסוג placeView ב-destination שמייצגים שטחים (מחליפים את המערך grounds של גרסה 3 הקודמת). לכל placeView, בודקים אם מדובר בפוליגון של שטח באמצעות השדה placeView.structureType. אם סוג המבנה הוא GROUNDS, אפשר לקבל את המתאר מהשדה placeView.displayPolygon. ב-placeView יהיו גם שדות נוספים לשטחים שלא היו ב-Geocoding API v3.

אובייקט destination יכול להכיל אובייקט placeView שמייצג שטח בשדות הבאים:

  • destination.primary
  • destination.containingPlaces
  • destination.subDestinations

שימו לב שהפורמט של placeView.displayPolygon תואם לפורמט של מתאר השטח ב-Geocoding API v3, שהוא פורמט GeoJSON, באמצעות פורמט RFC 7946.

רמת הדיוק של התוצאות

בגרסה 3 של Geocoding API, השדה location_type בגיאומטריית התגובה ציין את רמת הדיוק של התוצאות, וחלק מהלקוחות דירגו או סיננו את התוצאות על סמך הערכים האלה (ROOFTOP,‏ RANGE_INTERPOLATED,‏ GEOMETRIC_CENTER ו-APPROXIMATE). בהעברות רגילות לגרסה 4 של Geocoding API, השם של השדה הזה משתנה ל-granularity.

ב-Destinations API (גרסה 4 SearchDestinations), אין שדה location_type. במקום זאת, המידע המרחבי מטופל באופן שונה:

  • אין צורך בסינון ידני בצד הלקוח: בעוד שהמרת כתובות רגילה לקואורדינטות מספקת כמה תוצאות ברמות פירוט שונות, השיטה SearchDestinations מצמצמת את חוסר הבהירות על ידי פתרון ליעד אחד שעבר אופטימיזציה בכל פעם שאפשר. כך הלקוחות לא צריכים לסנן לפי סוג המיקום כדי לקבוע איזו תוצאה היא הטובה ביותר.
  • מידע מרחבי שמיוצג על ידי סוג המבנה ומצולעי התצוגה: הגיאומטריה והמבנה המרחביים מצוינים על ידי:
    • displayPolygon (לגיאומטריה מדויקת).
    • השדה structureType בתוך האובייקט placeView.
  • מיפוי סוג המבנה:
    • ‫structureType של POINT, ‏BUILDING או SECTION בדרך כלל תואם למה שנקרא בעבר ROOFTOP.
    • ערך structureType של GROUNDS בדרך כלל תואם לערך GEOMETRIC_CENTER.

שימוש במסכת שדות כדי לבקש את התכונות האלה

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

curl -X POST -d '{"place": "places/ChIJG3kh4hq6j4AR_XuFQnV0_t8"}' \
  -H "X-Goog-Api-Key: API_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Goog-FieldMask: destinations.entrances,destinations.navigationPoints,destinations.primary,destinations.containingPlaces,destinations.subDestinations" \
  https://geocode.googleapis.com/v4/geocode/destinations

מתכוני העברה נפוצים ותבניות קוד

כשמעבירים תהליכי עבודה מורכבים מ-Geocoding v3 או מ-Geocoding++‎ אל SearchDestinations, אפשר להשתמש במתכוני הקוד הבאים כדי לחלץ מבנים נפוצים של מרחב וכתובות.

חילוץ הפוליגון הטוב ביותר של המבנה

אם רוצים להציג את שטח המבנה של מיקום או יעד:

  1. בודקים אם המקום primary כולל structureType === 'BUILDING' ו-displayPolygon.
  2. אם לא, בודקים את containingPlaces כדי למצוא את המבנה שמכיל את היעד (שימושי כשהיעד הראשי הוא יעד משני של מבנה גדול יותר).
  3. אם לא נמצא מבנה שמכיל את המיקום, צריך לבדוק ב-subDestinations אם יש מבנים ספציפיים ברשימה.
דוגמה לקוד Python
def get_best_building_polygon(destination):
    primary = destination.get('primary', {})
    if primary.get('structureType') == 'BUILDING' and 'displayPolygon' in primary:
        return primary['displayPolygon']

    # Check containing places for the parent building
    for place in destination.get('containingPlaces', []):
        if place.get('structureType') == 'BUILDING' and 'displayPolygon' in place:
            return place['displayPolygon']

    # Fallback to sub-destinations if available
    for place in destination.get('subDestinations', []):
        if place.get('structureType') == 'BUILDING' and 'displayPolygon' in place:
            return place['displayPolygon']

    return None
קוד לדוגמה ב-Node.js
function getBestBuildingPolygon(destination) {
    const { primary = {}, containingPlaces = [], subDestinations = [] } = destination;

    if (primary.structureType === 'BUILDING' && primary.displayPolygon) {
        return primary.displayPolygon;
    }

    const parentBuilding = containingPlaces.find(
        p => p.structureType === 'BUILDING' && p.displayPolygon
    );
    if (parentBuilding) {
        return parentBuilding.displayPolygon;
    }

    const subBuilding = subDestinations.find(
        p => p.structureType === 'BUILDING' && p.displayPolygon
    );
    return subBuilding?.displayPolygon || null;
}

חילוץ של ראשי הפרקים של הנימוקים

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

  1. אם המקום primary מייצג את כל המתחם (structureType === 'GROUNDS'), צריך להשתמש ב-primary.displayPolygon.
  2. אם המקום primary הוא בניין או כניסה שנמצאים בתוך מתחם גדול יותר, צריך למצוא את המתחם שמקיף אותו ב-containingPlaces.
דוגמה לקוד Python
def get_grounds_polygon(destination):
    primary = destination.get('primary', {})
    if primary.get('structureType') == 'GROUNDS' and 'displayPolygon' in primary:
        return primary['displayPolygon']

    for place in destination.get('containingPlaces', []):
        if place.get('structureType') == 'GROUNDS' and 'displayPolygon' in place:
            return place['displayPolygon']

    return None
קוד לדוגמה ב-Node.js
function getGroundsPolygon(destination) {
    const { primary = {}, containingPlaces = [] } = destination;

    if (primary.structureType === 'GROUNDS' && primary.displayPolygon) {
        return primary.displayPolygon;
    }

    const groundsPlace = containingPlaces.find(
        p => p.structureType === 'GROUNDS' && p.displayPolygon
    );
    return groundsPlace?.displayPolygon || null;
}

בחירת ציוני דרך להצגת הכתובת לעומת הגעה ליעד הסופי

המרת כתובות לקואורדינטות (geocoding) בגרסה 3 address_descriptor החזירה טיפוס מוגדר מראש (enum) של spatial_relationship (WITHIN, BESIDE, BEHIND, NEAR, ACROSS_THE_ROAD, AROUND_THE_CORNER). ב-SearchDestinations:

  • אין ערך enum גולמי של קשר. במקום זאת, צריך להשתמש ישירות ב-relationalDescription.text. הוא מכיל ביטויים מותאמים לשוק המקומי שמוכנים להצגה, כמו "Across from Central Park" או "Behind the convention center". אל תוסיפו את "Near " באופן ידני.
  • כדי לבחור את נקודת הציון שמתאימה לתרחיש לדוגמה, משתמשים ב-tags:
    • ARRIVAL: ציוני דרך מקומיים שנמצאים ממש ליד היעד, מולו או מעבר לרחוב (בדרך כלל בטווח של 50 עד 100 מטרים). האפשרות הזו מתאימה במיוחד להצגת זמן ההגעה של נהג בזמן אמת, אישור מסירה של שליח או כרטיסי הגעה בתוך האפליקציה.
    • ADDRESS: ציוני דרך בולטים בשכונה (בדרך כלל בטווח של 100 עד 500 מטרים). האפשרות הזו מתאימה במיוחד לתהליך התשלום של הלקוח, לבדיקת ההזמנה או להפגת עמימות לגבי היעד לפני הנסיעה.
    • אם לציון דרך יש גם את התג ADDRESS וגם את התג ARRIVAL, הוא גם מוכר בשכונה וגם נמצא ממש ליד היעד.
דוגמה לקוד Python
def get_best_relational_landmark(destination, is_last_mile=True):
    """Select the best landmark based on user context.

    Args:
        destination: Destination object from SearchDestinations.
        is_last_mile: True for arrival and delivery guidance (prefers ARRIVAL tags).
                      False for address display and disambiguation (prefers ADDRESS tags).

    Returns:
        Dict with landmark description, distances, tags, and placeId, or None.
    """
    landmarks = destination.get('landmarks', [])
    if not landmarks:
        return None

    target_tag = 'ARRIVAL' if is_last_mile else 'ADDRESS'

    # Landmarks are ranked by relevance and proximity. Select the first candidate matching the target tag.
    for lm in landmarks:
        tags = lm.get('tags', [])
        if target_tag in tags or ('ARRIVAL' in tags and 'ADDRESS' in tags):
            description = lm.get('relationalDescription', {}).get('text')
            if description:
                return {
                    'description': description,
                    'travelDistanceMeters': lm.get('travelDistanceMeters'),
                    'straightLineDistanceMeters': lm.get('straightLineDistanceMeters'),
                    'tags': tags,
                    'placeId': lm.get('place', {}).get('placeId')
                }

    # Fallback to the first available landmark with a relational description if no tagged match is found
    for lm in landmarks:
        description = lm.get('relationalDescription', {}).get('text')
        if description:
            return {
                'description': description,
                'travelDistanceMeters': lm.get('travelDistanceMeters'),
                'straightLineDistanceMeters': lm.get('straightLineDistanceMeters'),
                'tags': lm.get('tags', []),
                'placeId': lm.get('place', {}).get('placeId')
            }

    return None
קוד לדוגמה ב-Node.js
/**
 * Select the best landmark based on user context.
 *
 * @param {Object} destination Destination object from SearchDestinations.
 * @param {boolean} [isLastMile=true] True for arrival and delivery guidance
 *        (prefers ARRIVAL tags). False for address display and disambiguation
 *        (prefers ADDRESS tags).
 * @return {Object|null}
 */
function getBestRelationalLandmark(destination, isLastMile = true) {
    const landmarks = destination.landmarks || [];
    if (!landmarks.length) return null;

    const targetTag = isLastMile ? 'ARRIVAL' : 'ADDRESS';

    // Landmarks are ranked by relevance and proximity. Select the first candidate matching the target tag.
    const match = landmarks.find((lm) => {
        const tags = lm.tags || [];
        return (tags.includes(targetTag) || (tags.includes('ARRIVAL') && tags.includes('ADDRESS'))) &&
               lm.relationalDescription?.text;
    }) || landmarks.find((lm) => lm.relationalDescription?.text);

    if (match) {
        return {
            description: match.relationalDescription.text,
            travelDistanceMeters: match.travelDistanceMeters,
            straightLineDistanceMeters: match.straightLineDistanceMeters,
            tags: match.tags || [],
            placeId: match.place?.placeId || null,
        };
    }

    return null;
}

מסלול לנקודות ניווט של כלי רכב ולכניסות מועדפות

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

  • מסלול נסיעה מפורט: ניתוב כלי רכב לנקודות איסוף והורדה ייעודיות בכביש באמצעות navigationPoints. נקודות הניווט מדורגות מראש לפי קרבה ורלוונטיות, כך שהמועמד הראשון עם אמצעי הנסיעה הרצוי (כמו 'DRIVE') ותג השימוש (כמו 'DROPOFF' או 'PICKUP') מספק את נקודת הציון האופטימלית לרכב. שולחים את ה-navigationPointToken שלו ל-method‏ ComputeRoutes של Routes API‏ (Waypoint.navigationPointToken) או ל-Navigation SDK. אם אין נקודת נסיעה שמוצמדת לכביש, חוזרים לכניסה לבניין או למרכז הכובד הראשי.
  • גישה פיזית לכניסה: מכוונים את השליח או הנוסע לכניסה לבניין באמצעות destinations.entrances. כדאי לבדוק את tags של 'PREFERRED' כדי למצוא כניסות שסביר להניח שיש בהן גישה פיזית ישירה. אם אין נתונים זמינים על הכניסה, צריך לחזור לנקודת ניווט להולכי רגל (travelModes כולל 'WALK') או למרכז הכובד הראשי.
דוגמה לקוד Python
def get_routing_and_arrival_points(destination, usage_tag=None):
    navigation_points = destination.get('navigationPoints', [])
    entrances = destination.get('entrances', [])
    primary_location = destination.get('primary', {}).get('location')

    # 1. Doorstep / Physical Entrance
    # Prefer PREFERRED entrance -> any entrance -> WALK nav point -> primary location
    preferred_entrance = None
    for ent in entrances:
        if 'PREFERRED' in ent.get('tags', []):
            preferred_entrance = ent
            break
    if not preferred_entrance and entrances:
        preferred_entrance = entrances[0]

    walk_nav_point = None
    for np in navigation_points:
        if 'WALK' in np.get('travelModes', []):
            walk_nav_point = np
            break

    if preferred_entrance:
        doorstep_location = preferred_entrance.get('location')
    elif walk_nav_point:
        doorstep_location = walk_nav_point.get('location')
    else:
        doorstep_location = primary_location

    # 2. Vehicle Routing Waypoint
    # Find a driving navigation point. If usage_tag (such as 'DROPOFF' or 'PICKUP')
    # is specified, prioritize a candidate with that usage tag; otherwise fall back
    # to the first available driving point.
    drive_nav_point = None
    fallback_drive_point = None
    for np in navigation_points:
        if 'DRIVE' in np.get('travelModes', []):
            if usage_tag and usage_tag in np.get('usages', []):
                drive_nav_point = np
                break  # Exact match for desired usage tag
            elif fallback_drive_point is None:
                fallback_drive_point = np  # First driving point as fallback

    if not drive_nav_point:
        drive_nav_point = fallback_drive_point

    if drive_nav_point:
        vehicle_location = drive_nav_point.get('location')
        nav_token = drive_nav_point.get('navigationPointToken')
    elif preferred_entrance:
        vehicle_location = preferred_entrance.get('location')
        nav_token = None
    else:
        vehicle_location = primary_location
        nav_token = None

    return {
        'vehicleRoutingWaypoint': {
            'navigationPointToken': nav_token,
            'location': vehicle_location,
        },
        'doorstepLocation': doorstep_location,
    }
קוד לדוגמה ב-Node.js
function getRoutingAndArrivalPoints(destination, usageTag = null) {
    const navigationPoints = destination.navigationPoints || [];
    const entrances = destination.entrances || [];
    const primaryLocation = destination.primary?.location || null;

    // 1. Doorstep / Physical Entrance
    const preferredEntrance = entrances.find(ent => ent.tags?.includes('PREFERRED'))
        || entrances[0]
        || null;

    const walkNavPoint = navigationPoints.find(np => np.travelModes?.includes('WALK'))
        || null;

    const doorstepLocation = preferredEntrance?.location
        || walkNavPoint?.location
        || primaryLocation;

    // 2. Vehicle Routing Waypoint
    // Find a driving navigation point. If usageTag (such as 'DROPOFF' or 'PICKUP')
    // is specified, prioritize a candidate with that usage tag; otherwise fall back
    // to the first available driving point.
    const driveNavPoint = (usageTag && navigationPoints.find(np => np.travelModes?.includes('DRIVE') && np.usages?.includes(usageTag)))
        || navigationPoints.find(np => np.travelModes?.includes('DRIVE'))
        || null;

    let vehicleLocation = primaryLocation;
    let navToken = null;
    if (driveNavPoint) {
        vehicleLocation = driveNavPoint.location;
        navToken = driveNavPoint.navigationPointToken;
    } else if (preferredEntrance) {
        vehicleLocation = preferredEntrance.location;
    }

    return {
        vehicleRoutingWaypoint: {
            navigationPointToken: navToken,
            location: vehicleLocation,
        },
        doorstepLocation: doorstepLocation,
    };
}

שיקולי אבטחה

‫Geocoding API v4 הוא ממשק API לתקשורת שרת-אל-שרת. אין דרך ישירה להעביר משתמשי JavaScript מגרסה 3 לגרסה 4. אם תפעילו את השיטות של גרסה 4 ישירות מ-JavaScript בצד הלקוח (לדוגמה, בדפדפן) באמצעות מפתח API, מפתח ה-API שלכם יהיה חשוף לסיכון גבוה של גניבה ושימוש לרעה.

הגבלות על הגורמים המפנים מסוג HTTP, למרות שהן שימושיות, לא מספקות הגנה מספיקה לנקודות קצה של שירותי אינטרנט, כי תוקפים יכולים לעקוף אותן בקלות על ידי זיוף הכותרת Referer בבקשות שלהם.

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

חלופות לצרכים בצד הלקוח

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