خدمة "التجوّل الافتراضي"

نظرة عامة

اختيار النظام الأساسي: Android‏ iOS‏ JavaScript‏

توفّر ميزة "التجوّل الافتراضي" من Google صورًا بانورامية بزاوية 360 درجة من طرق محدّدة في جميع أنحاء منطقة التغطية. تغطية واجهة برمجة التطبيقات لميزة "التجوّل الافتراضي" هي نفسها تغطية تطبيق "خرائط Google" (https://maps.google.com/). وتتوفّر قائمة المدن التي تتيح حاليًا ميزة "التجوّل الافتراضي" على الموقع الإلكتروني لخدمة "خرائط Google".

في ما يلي نموذج لصورة من "التجوّل الافتراضي".


توفّر واجهة برمجة التطبيقات Maps JavaScript API خدمة "التجوّل الافتراضي" للحصول على الصور المستخدَمة في ميزة "التجوّل الافتراضي" في "خرائط Google" ومعالجتها. تتوفّر خدمة "التجوّل الافتراضي" هذه بشكلٍ تلقائي في المتصفّح.

استخدام خريطة "التجوّل الافتراضي"

على الرغم من إمكانية استخدام ميزة "التجوّل الافتراضي" ضمن عنصر DOM مستقل، إلا أنّها تكون أكثر فائدة عند الإشارة إلى موقع جغرافي على خريطة. By default, Street View is enabled on a map, and a Street View Pegman control appears integrated within the navigation (zoom and pan) controls. يمكنك إخفاء عنصر التحكّم هذا ضمن MapOptions الخريطة من خلال ضبط streetViewControl على false. يمكنك أيضًا تغيير الموضع التلقائي لعنصر التحكّم في "التجوّل الافتراضي" من خلال ضبط السمة streetViewControlOptions.position في Map على قيمة جديدة من ControlPosition.

يتيح لك عنصر التحكّم "دليل التجوّل الافتراضي" عرض صور بانوراما في "التجوّل الافتراضي" مباشرةً داخل الخريطة. عندما ينقر المستخدم مع الاستمرار على "دليل التجوّل الافتراضي"، يتم تعديل الخريطة لعرض خطوط خارجية زرقاء حول الشوارع التي تتيح ميزة "التجوّل الافتراضي"، ما يوفّر تجربة مستخدم مشابهة لتطبيق "خرائط Google".

عندما يُسقط المستخدم علامة "الدليل" على أحد الشوارع، يتم تعديل الخريطة لعرض بانوراما في "التجوّل الافتراضي" للموقع الجغرافي المُشار إليه.

بانوراما في "التجوّل الافتراضي"

تتم إتاحة صور "التجوّل الافتراضي" من خلال استخدام العنصر StreetViewPanorama الذي يوفّر واجهة برمجة تطبيقات لـ "عارض" "التجوّل الافتراضي". تحتوي كل خريطة على بانوراما تلقائية في "التجوّل الافتراضي"، والتي يمكنك استردادها من خلال استدعاء طريقة getStreetView() للخريطة. عند إضافة عنصر تحكّم في "التجوّل الافتراضي" إلى الخريطة من خلال ضبط خيار streetViewControl على true، يتم تلقائيًا ربط عنصر التحكّم في "دليل التجوّل الافتراضي" بصورة بانوراما التلقائية هذه في "التجوّل الافتراضي".

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

حاويات "التجوّل الافتراضي"

بدلاً من ذلك، قد ترغب في عرض StreetViewPanorama ضمن عنصر DOM منفصل، غالبًا ما يكون عنصر <div>. ما عليك سوى تمرير عنصر DOM ضمن دالة الإنشاء StreetViewPanorama. لعرض الصور على النحو الأمثل، ننصحك باستخدام صور لا يقلّ حجمها عن 200 × 200 بكسل.

ملاحظة: على الرغم من أنّ وظيفة "التجوّل الافتراضي" مصمّمة للاستخدام مع خريطة، ليس هذا الاستخدام مطلوبًا. يمكنك استخدام عنصر "التجوّل الافتراضي" المستقل بدون خريطة.

المواقع الجغرافية في "التجوّل الافتراضي" ووجهة النظر (POV)

تتيح لك الدالة الإنشائية StreetViewPanorama أيضًا ضبط الموقع الجغرافي وزاوية الرؤية في "التجوّل الافتراضي" باستخدام المَعلمة StreetViewOptions. يمكنك استدعاء setPosition() وsetPov() على العنصر بعد إنشائه لتغيير موقعه ووجهة نظره.

يحدّد الموقع الجغرافي في "التجوّل الافتراضي" موضع تركيز الكاميرا لصورة معيّنة، ولكنّه لا يحدّد اتجاه الكاميرا لهذه الصورة. لهذا الغرض، يحدّد العنصر StreetViewPov سمتَين:

  • heading (القيمة التلقائية 0) تحدّد زاوية الدوران حول موضع الكاميرا بالدرجات بالنسبة إلى الشمال الحقيقي. يتم قياس الاتجاهات في اتجاه عقارب الساعة (90 درجة هي الشرق الحقيقي).
  • pitch (القيمة التلقائية 0) تحدّد تباين الزاوية "لأعلى" أو "لأسفل" من درجة الميل التلقائية الأولية للكاميرا، والتي غالبًا ما تكون (ولكن ليس دائمًا) أفقية مسطحة. (على سبيل المثال، من المرجّح أن تعرض الصورة التي تم التقاطها على تلّ درجة ميل تلقائية غير أفقية). يتم قياس زوايا الانحدار بقيم موجبة عند النظر للأعلى (إلى +90 درجة للأعلى بشكل مستقيم ومتعامد مع الانحدار التلقائي) وقيم سالبة عند النظر للأسفل (إلى -90 درجة للأسفل بشكل مستقيم ومتعامد مع الانحدار التلقائي).

يتم استخدام الكائن StreetViewPov في أغلب الأحيان لتحديد وجهة نظر كاميرا "التجوّل الافتراضي". يمكنك أيضًا تحديد وجهة نظر المصوّر، أي الاتجاه الذي كانت تسير فيه السيارة أو الدراجة الثلاثية العجلات عادةً، باستخدام طريقة StreetViewPanorama.getPhotographerPov().

يعرض الرمز التالي خريطة لمدينة بوسطن مع عرض أوّلي لملعب "فينواي بارك". سيؤدي اختيار "الدليل" وسحبه إلى موقع جغرافي متوافق على الخريطة إلى تغيير بانوراما "التجوّل الافتراضي":

TypeScript

async function init(): Promise<void> {
    // Request needed libraries.
    const [{ StreetViewPanorama }] = await Promise.all([
        google.maps.importLibrary('streetView'),
        google.maps.importLibrary('maps'),
    ]);

    // Get the gmp-map element and its innerMap.
    const mapElement = document.querySelector('gmp-map')!;
    const innerMap = mapElement.innerMap;

    // Get the center of the map.
    const position = mapElement.center as google.maps.LatLng;

    // Create a new StreetViewPanorama, set its position, and attach it to the map.
    const panorama = new StreetViewPanorama(document.getElementById('pano')!, {
        position,
        pov: {
            heading: 34,
            pitch: 10,
        },
    });

    /**
     * Set the StreetViewPanorama so it displays in the pano element
     * rather than taking over the map.
     * This creates a link between the map and the panorama so that
     * the map is updated when the StreetViewPanorama position changes,
     * and the StreetViewPanorama is updated when the map changes.
     */
    innerMap.setStreetView(panorama);
}

void init();

JavaScript

async function init() {
    // Request needed libraries.
    const [{ StreetViewPanorama }] = await Promise.all([
        google.maps.importLibrary('streetView'),
        google.maps.importLibrary('maps'),
    ]);

    // Get the gmp-map element and its innerMap.
    const mapElement = document.querySelector('gmp-map');
    const innerMap = mapElement.innerMap;

    // Get the center of the map.
    const position = mapElement.center;

    // Create a new StreetViewPanorama, set its position, and attach it to the map.
    const panorama = new StreetViewPanorama(document.getElementById('pano'), {
        position,
        pov: {
            heading: 34,
            pitch: 10,
        },
    });

    /**
     * Set the StreetViewPanorama so it displays in the pano element
     * rather than taking over the map.
     * This creates a link between the map and the panorama so that
     * the map is updated when the StreetViewPanorama position changes,
     * and the StreetViewPanorama is updated when the map changes.
     */
    innerMap.setStreetView(panorama);
}

void init();

CSS

html,
body {
    height: 100%;
    margin: 0;
    padding: 0;
}

body {
    display: flex;
}

gmp-map,
#pano {
    flex: 1;
    height: 100%;
}

HTML

<html>
    <head>
        <title>Street View split-map-panes</title>

        <link rel="stylesheet" type="text/css" href="./style.css" />
        <script type="module" src="./index.ts"></script>
        <script>
            // prettier-ignore
            (g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({
                key: "GOOGLE_MAPS_API_KEY",
            });
        </script>
    </head>
    <body>
        <gmp-map
            center="42.345573,-71.098326"
            zoom="14"
            map-id="DEMO_MAP_ID"></gmp-map>
        <div id="pano"></div>
    </body>
</html>
الاطّلاع على مثال

تجربة عيّنة

تتبُّع الحركة على الأجهزة الجوّالة

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

بصفتك مطوّر تطبيقات، يمكنك تغيير الإعداد التلقائي على النحو التالي:

  • تفعيل وظيفة تتبُّع الحركة أو إيقافها يتم تفعيل ميزة تتبُّع الحركة تلقائيًا على أي جهاز متوافق معها. يؤدي النموذج التالي إلى إيقاف تتبُّع الحركة، ولكنّه يترك عنصر التحكّم في تتبُّع الحركة مرئيًا. (يُرجى العِلم أنّه يمكن للمستخدم تفعيل ميزة تتبُّع الحركة من خلال النقر على عنصر التحكّم).
    var panorama = new google.maps.StreetViewPanorama(
        document.getElementById('pano'), {
          position: {lat: 37.869260, lng: -122.254811},
          pov: {heading: 165, pitch: 0},
          motionTracking: false
        });
  • إخفاء عنصر التحكّم في تتبُّع الحركة أو إظهاره يتم عرض عنصر التحكّم تلقائيًا على الأجهزة التي تتيح تتبُّع الحركة. يمكن للمستخدم النقر على عنصر التحكّم لتفعيل ميزة تتبُّع الحركة أو إيقافها. يُرجى العِلم أنّ عنصر التحكّم لن يظهر أبدًا إذا كان الجهاز لا يتيح ميزة تتبُّع الحركة، بغض النظر عن قيمة motionTrackingControl.

    يؤدي النموذج التالي إلى إيقاف كل من تتبُّع الحركة وعنصر التحكّم في تتبُّع الحركة. في هذه الحالة، لا يمكن للمستخدم تفعيل تتبُّع الحركة:

    var panorama = new google.maps.StreetViewPanorama(
        document.getElementById('pano'), {
          position: {lat: 37.869260, lng: -122.254811},
          pov: {heading: 165, pitch: 0},
          motionTracking: false,
          motionTrackingControl: false
        });
  • غيِّر الموضع التلقائي لعنصر التحكّم في تتبُّع الحركة. يظهر عنصر التحكّم تلقائيًا بالقرب من أسفل يسار الصورة البانورامية (الموضع RIGHT_BOTTOM). يضبط النموذج التالي موضع عنصر التحكّم على أسفل اليمين:
    var panorama = new google.maps.StreetViewPanorama(
        document.getElementById('pano'), {
          position: {lat: 37.869260, lng: -122.254811},
          pov: {heading: 165, pitch: 0},
          motionTrackingControlOptions: {
            position: google.maps.ControlPosition.LEFT_BOTTOM
          }
        });

للاطّلاع على تتبُّع الحركة أثناء العمل، يمكنك الاطّلاع على النموذج التالي على جهاز جوّال (أو أي جهاز يتوافق مع أحداث اتجاه الجهاز):


الاطّلاع على مثال

التراكبات ضمن "التجوّل الافتراضي"

يتيح عنصر StreetViewPanorama التلقائي العرض الأصلي لتراكبات الخريطة. تظهر الأشكال المضافة عادةً على "مستوى الشارع" في مواضع LatLng. (ستظهر العلامات مع تثبيت ذيولها على المستوى الأفقي للموقع الجغرافي ضمن بانوراما في "التجوّل الافتراضي" على سبيل المثال).

في الوقت الحالي، تقتصر أنواع الأشكال التي يمكن إضافتها إلى صور بانوراما في "التجوّل الافتراضي" على Marker وInfoWindow وOverlayView المخصّصة. يمكن عرض الأشكال التي تعرضها على خريطة على بانوراما في "التجوّل الافتراضي" من خلال التعامل مع البانوراما كبديل لكائن Map، واستدعاء setMap() وتمرير StreetViewPanorama كوسيطة بدلاً من خريطة. يمكن أيضًا فتح نوافذ المعلومات ضمن بانوراما في "التجوّل الافتراضي" من خلال استدعاء open()، وتمرير StreetViewPanorama() بدلاً من خريطة.

بالإضافة إلى ذلك، عند إنشاء خريطة باستخدام StreetViewPanorama تلقائي، تتم مشاركة أي علامات تم إنشاؤها على الخريطة تلقائيًا مع بانوراما في "التجوّل الافتراضي" المرتبطة بالخريطة، شرط أن تكون هذه البانوراما مرئية. لاسترداد بانوراما في "التجوّل الافتراضي" التلقائية، يمكنك استدعاء getStreetView() على عنصر Map. يُرجى العِلم أنّه في حال ضبط السمة streetView للخريطة بشكل صريح على StreetViewPanorama من إنشائك، سيتم إلغاء الصورة البانورامية التلقائية.

يوضّح المثال التالي علامات تشير إلى مواقع جغرافية مختلفة حول "أستور بليس" في مدينة نيويورك. بدِّل العرض إلى "التجوّل الافتراضي" لعرض العلامات المشتركة التي تظهر ضمن StreetViewPanorama.

TypeScript

const ASSET_BASE_URL = new URL('https://maps-docs-team.web.app/samples/streetview-overlays/dist/');

let panorama: google.maps.StreetViewPanorama;
let innerMap: google.maps.Map;

async function init() {
    // Request needed libraries.
    const [{ Marker }] = await Promise.all([
        google.maps.importLibrary('marker'),
        google.maps.importLibrary('maps'),
    ]);

    // Set the location of Astor Place.
    const astorPlace = { lat: 40.729884, lng: -73.990988 };

    const mapElement = document.querySelector('gmp-map')!;

    innerMap = mapElement.innerMap;

    document
        .getElementById('streetview-toggle-button')!
        .addEventListener('click', toggleStreetView);

    const cafeIcon = document.createElement('img');
    cafeIcon.src = new URL('./cafe_icon.svg', ASSET_BASE_URL).href;

    const dollarIcon = document.createElement('img');
    dollarIcon.src = new URL('./bank_icon.svg', ASSET_BASE_URL).href;

    const busIcon = document.createElement('img');
    busIcon.src = new URL('./bus_icon.svg', ASSET_BASE_URL).href;

    // Set up the markers on the map
    new Marker({
        position: { lat: 40.730031, lng: -73.991428 },
        map: innerMap,
        title: 'Cafe',
        icon: cafeIcon.src,
    });

    new Marker({
        position: { lat: 40.729681, lng: -73.991138 },
        map: innerMap,
        title: 'Bank',
        icon: dollarIcon.src,
    });

    new Marker({
        position: { lat: 40.729559, lng: -73.990741 },
        map: innerMap,
        title: 'Bus Stop',
        icon: busIcon.src,
    });

    // We get the map's default panorama and set up some defaults.
    // Note that we don't yet set it visible.
    panorama = innerMap.getStreetView()!; // TODO fix type
    panorama.setPosition(astorPlace);
    panorama.setPov({
        heading: 265,
        pitch: 0,
    });
}

function toggleStreetView(): void {
    const toggle = panorama.getVisible();

    if (!toggle) {
        panorama.setVisible(true);
    } else {
        panorama.setVisible(false);
    }
}

void init();

JavaScript

const ASSET_BASE_URL = new URL('https://maps-docs-team.web.app/samples/streetview-overlays/dist/');

let panorama;
let innerMap;

async function init() {
    // Request needed libraries.
    const [{ Marker }] = await Promise.all([
        google.maps.importLibrary('marker'),
        google.maps.importLibrary('maps'),
    ]);

    // Set the location of Astor Place.
    const astorPlace = { lat: 40.729884, lng: -73.990988 };

    const mapElement = document.querySelector('gmp-map');

    innerMap = mapElement.innerMap;

    document
        .getElementById('streetview-toggle-button')
        .addEventListener('click', toggleStreetView);

    const cafeIcon = document.createElement('img');
    cafeIcon.src = new URL('./cafe_icon.svg', ASSET_BASE_URL).href;

    const dollarIcon = document.createElement('img');
    dollarIcon.src = new URL('./bank_icon.svg', ASSET_BASE_URL).href;

    const busIcon = document.createElement('img');
    busIcon.src = new URL('./bus_icon.svg', ASSET_BASE_URL).href;

    // Set up the markers on the map
    new Marker({
        position: { lat: 40.730031, lng: -73.991428 },
        map: innerMap,
        title: 'Cafe',
        icon: cafeIcon.src,
    });

    new Marker({
        position: { lat: 40.729681, lng: -73.991138 },
        map: innerMap,
        title: 'Bank',
        icon: dollarIcon.src,
    });

    new Marker({
        position: { lat: 40.729559, lng: -73.990741 },
        map: innerMap,
        title: 'Bus Stop',
        icon: busIcon.src,
    });

    // We get the map's default panorama and set up some defaults.
    // Note that we don't yet set it visible.
    panorama = innerMap.getStreetView(); // TODO fix type
    panorama.setPosition(astorPlace);
    panorama.setPov({
        heading: 265,
        pitch: 0,
    });
}

function toggleStreetView() {
    const toggle = panorama.getVisible();

    if (!toggle) {
        panorama.setVisible(true);
    } else {
        panorama.setVisible(false);
    }
}

void init();
export {};

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

#floating-panel {
    position: absolute;
    top: 10px;
    left: 25%;
    z-index: 5;
    background-color: #fff;
    padding: 5px;
    border: 1px solid #999;
    text-align: center;
    font-family: 'Roboto', sans-serif;
    line-height: 30px;
    padding-left: 10px;
}

#streetview-toggle-button {
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0 17px;
    border: none;
    background: white;
    cursor: pointer;
    border-radius: 2px;
    box-shadow: 0 1px 4px -1px rgba(0, 0, 0, 0.3);
    margin: 10px 0px 10px -2px;
    font-family: Roboto, Arial, sans-serif;
    font-size: 18px;
    font-weight: 400;
    color: rgb(86, 86, 86);
}

#streetview-toggle-button:hover {
    background: #f4f4f4;
    color: #000;
}

HTML

<html>
    <head>
        <title>Overlays Within Street View</title>

        <link rel="stylesheet" type="text/css" href="./style.css" />
        <script type="module" src="./index.js"></script>
        <script>
            // prettier-ignore
            (g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({
                key: "GOOGLE_MAPS_API_KEY"
            });
        </script>
    </head>
    <body>
        <gmp-map map-id="DEMO_MAP_ID" center="40.729884, -73.990988" zoom="18">
            <input
                type="button"
                value="Toggle Street View"
                id="streetview-toggle-button"
                slot="control-block-start-inline-start" />
        </gmp-map>
    </body>
</html>
الاطّلاع على مثال

تجربة عيّنة

أحداث "التجوّل الافتراضي"

عند التنقّل بين &quot;التجوّل الافتراضي&quot; أو معالجة اتجاهه، قد ترغب في مراقبة العديد من الأحداث التي تشير إلى تغييرات في حالة StreetViewPanorama:

  • يتم تنشيط pano_changed كلما تغيّر معرّف الصورة البانورامية الفردية. لا يضمن هذا الحدث أنّ أي بيانات مرتبطة داخل البانوراما (مثل الروابط) قد تغيّرت أيضًا بحلول وقت بدء هذا الحدث، بل يشير فقط إلى أنّ معرّف البانوراما قد تغيّر. يُرجى العِلم أنّ معرّف الصورة البانورامية (الذي يمكنك استخدامه للإشارة إلى هذه الصورة البانورامية) يكون ثابتًا فقط خلال جلسة المتصفّح الحالية.
  • يتم تنشيط position_changed عندما يتغيّر الموضع الأساسي (LatLng) للبانوراما. ولن يؤدي تدوير صورة بانورامية إلى تشغيل هذا الحدث. يُرجى العِلم أنّه يمكنك تغيير الموضع الأساسي للصورة البانورامية بدون تغيير رقم تعريف الصورة البانورامية المرتبط بها، لأنّ واجهة برمجة التطبيقات ستربط تلقائيًا أقرب رقم تعريف صورة بانورامية بموضع الصورة البانورامية.
  • يتم تنشيط pov_changed كلما تغيّر StreetViewPov في &quot;التجوّل الافتراضي&quot;. يُرجى العِلم أنّه قد يتم تنشيط هذا الحدث بينما يظلّ الموقع الجغرافي ومعرّف الصورة البانورامية ثابتَين.
  • يتم تنشيط links_changed عندما تتغير روابط "التجوّل الافتراضي". يُرجى العِلم أنّ هذا الحدث قد يتم تنفيذه بشكل غير متزامن بعد تغيير في رقم تعريف الصورة البانورامية المُشار إليه من خلال pano_changed.
  • يتم تنشيط visible_changed كلما تغيّر مستوى ظهور "التجوّل الافتراضي". يُرجى العِلم أنّ هذا الحدث قد يتم تنفيذه بشكل غير متزامن بعد تغيير في رقم تعريف الصورة البانورامية المُشار إليه من خلال pano_changed.

يوضّح الرمز التالي كيفية التعامل مع هذه الأحداث لجمع بيانات حول StreetViewPanorama الأساسي:

TypeScript

function initPano() {
  const panorama = new google.maps.StreetViewPanorama(
    document.getElementById("pano") as HTMLElement,
    {
      position: { lat: 37.869, lng: -122.255 },
      pov: {
        heading: 270,
        pitch: 0,
      },
      visible: true,
    }
  );

  panorama.addListener("pano_changed", () => {
    const panoCell = document.getElementById("pano-cell") as HTMLElement;

    panoCell.innerHTML = panorama.getPano();
  });

  panorama.addListener("links_changed", () => {
    const linksTable = document.getElementById("links_table") as HTMLElement;

    while (linksTable.hasChildNodes()) {
      linksTable.removeChild(linksTable.lastChild as ChildNode);
    }

    const links = panorama.getLinks();

    for (const i in links) {
      const row = document.createElement("tr");

      linksTable.appendChild(row);

      const labelCell = document.createElement("td");

      labelCell.innerHTML = "<b>Link: " + i + "</b>";

      const valueCell = document.createElement("td");

      valueCell.innerHTML = links[i].description as string;
      linksTable.appendChild(labelCell);
      linksTable.appendChild(valueCell);
    }
  });

  panorama.addListener("position_changed", () => {
    const positionCell = document.getElementById(
      "position-cell"
    ) as HTMLElement;

    (positionCell.firstChild as HTMLElement).nodeValue =
      panorama.getPosition() + "";
  });

  panorama.addListener("pov_changed", () => {
    const headingCell = document.getElementById("heading-cell") as HTMLElement;
    const pitchCell = document.getElementById("pitch-cell") as HTMLElement;

    (headingCell.firstChild as HTMLElement).nodeValue =
      panorama.getPov().heading + "";
    (pitchCell.firstChild as HTMLElement).nodeValue =
      panorama.getPov().pitch + "";
  });
}

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

JavaScript

function initPano() {
  const panorama = new google.maps.StreetViewPanorama(
    document.getElementById("pano"),
    {
      position: { lat: 37.869, lng: -122.255 },
      pov: {
        heading: 270,
        pitch: 0,
      },
      visible: true,
    },
  );

  panorama.addListener("pano_changed", () => {
    const panoCell = document.getElementById("pano-cell");

    panoCell.innerHTML = panorama.getPano();
  });
  panorama.addListener("links_changed", () => {
    const linksTable = document.getElementById("links_table");

    while (linksTable.hasChildNodes()) {
      linksTable.removeChild(linksTable.lastChild);
    }

    const links = panorama.getLinks();

    for (const i in links) {
      const row = document.createElement("tr");

      linksTable.appendChild(row);

      const labelCell = document.createElement("td");

      labelCell.innerHTML = "<b>Link: " + i + "</b>";

      const valueCell = document.createElement("td");

      valueCell.innerHTML = links[i].description;
      linksTable.appendChild(labelCell);
      linksTable.appendChild(valueCell);
    }
  });
  panorama.addListener("position_changed", () => {
    const positionCell = document.getElementById("position-cell");

    positionCell.firstChild.nodeValue = panorama.getPosition() + "";
  });
  panorama.addListener("pov_changed", () => {
    const headingCell = document.getElementById("heading-cell");
    const pitchCell = document.getElementById("pitch-cell");

    headingCell.firstChild.nodeValue = panorama.getPov().heading + "";
    pitchCell.firstChild.nodeValue = panorama.getPov().pitch + "";
  });
}

window.initPano = initPano;

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

#floating-panel {
  position: absolute;
  top: 10px;
  left: 25%;
  z-index: 5;
  background-color: #fff;
  padding: 5px;
  border: 1px solid #999;
  text-align: center;
  font-family: "Roboto", "sans-serif";
  line-height: 30px;
  padding-left: 10px;
}

#pano {
  width: 50%;
  height: 100%;
  float: left;
}

#floating-panel {
  width: 45%;
  height: 100%;
  float: right;
  text-align: left;
  overflow: auto;
  position: static;
  border: 0px solid #999;
}

HTML

<html>
  <head>
    <title>Street View Events</title>

    <link rel="stylesheet" type="text/css" href="./style.css" />
    <script type="module" src="./index.js"></script>
  </head>
  <body>
    <div id="pano"></div>
    <div id="floating-panel">
      <table>
        <tr>
          <td><b>Position</b></td>
          <td id="position-cell">&nbsp;</td>
        </tr>
        <tr>
          <td><b>POV Heading</b></td>
          <td id="heading-cell">270</td>
        </tr>
        <tr>
          <td><b>POV Pitch</b></td>
          <td id="pitch-cell">0.0</td>
        </tr>
        <tr>
          <td><b>Pano ID</b></td>
          <td id="pano-cell">&nbsp;</td>
        </tr>
        <table id="links_table"></table>
      </table>
    </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=initPano&v=weekly"
      defer
    ></script>
  </body>
</html>
الاطّلاع على مثال

عناصر التحكّم في "التجوّل الافتراضي"

عند عرض StreetViewPanorama، تظهر مجموعة متنوعة من عناصر التحكّم على الصورة البانورامية تلقائيًا. يمكنك تفعيل عناصر التحكّم هذه أو إيقافها من خلال ضبط الحقول المناسبة ضمن StreetViewPanoramaOptions على true أو false:

  • يتيح panControl طريقة لتدوير الصورة البانورامية. يظهر عنصر التحكّم هذا تلقائيًا كبوصلة مدمجة عادية وعنصر تحكّم في العرض الشامل. يمكنك تغيير موضع عنصر التحكّم من خلال تقديم PanControlOptions ضمن الحقل panControlOptions.
  • يوفّر zoomControl طريقة لتكبير الصورة. يظهر عنصر التحكّم هذا تلقائيًا بالقرب من أسفل يسار الصورة البانورامية. يمكنك تغيير مظهر عنصر التحكّم من خلال تقديم ZoomControlOptions ضمن حقل zoomControlOptions.
  • يوفّر addressControl تراكبًا نصيًا يشير إلى عنوان الموقع الجغرافي المرتبط، ويقدّم رابطًا لفتح الموقع الجغرافي في "خرائط Google". يمكنك تغيير مظهر عنصر التحكّم من خلال تقديم StreetViewAddressControlOptions ضمن حقل addressControlOptions.
  • يوفّر fullscreenControl خيار فتح "التجوّل الافتراضي" في وضع ملء الشاشة. يمكنك تغيير مظهر عنصر التحكّم من خلال تقديم FullscreenControlOptions ضمن حقل fullscreenControlOptions.
  • يتيح motionTrackingControl خيار تفعيل أو إيقاف تتبُّع الحركة على الأجهزة الجوّالة. لا يظهر عنصر التحكّم هذا إلا على الأجهزة التي تتيح أحداث اتجاه الجهاز. يظهر عنصر التحكّم تلقائيًا بالقرب من أسفل يسار الصورة البانورامية. يمكنك تغيير موضع عنصر التحكّم من خلال توفير MotionTrackingControlOptions. لمزيد من المعلومات، يُرجى الاطّلاع على القسم المتعلّق بتتبُّع الحركة.
  • يوفّر linksControl أسهمًا إرشادية على الصورة للانتقال إلى صور بانورامية مجاورة.
  • يتيح عنصر التحكّم "إغلاق" للمستخدم إغلاق عارض "التجوّل الافتراضي". يمكنك تفعيل عنصر التحكّم في الإغلاق أو إيقافه من خلال ضبط enableCloseButton على true أو false.

يغيّر المثال التالي عناصر التحكّم المعروضة ضمن "التجوّل الافتراضي" المرتبط ويزيل روابط العرض:

TypeScript

function initPano() {
  // Note: constructed panorama objects have visible: true
  // set by default.
  const panorama = new google.maps.StreetViewPanorama(
    document.getElementById("map") as HTMLElement,
    {
      position: { lat: 42.345573, lng: -71.098326 },
      addressControlOptions: {
        position: google.maps.ControlPosition.BOTTOM_CENTER,
      },
      linksControl: false,
      panControl: false,
      enableCloseButton: false,
    }
  );
}

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

JavaScript

function initPano() {
  // Note: constructed panorama objects have visible: true
  // set by default.
  const panorama = new google.maps.StreetViewPanorama(
    document.getElementById("map"),
    {
      position: { lat: 42.345573, lng: -71.098326 },
      addressControlOptions: {
        position: google.maps.ControlPosition.BOTTOM_CENTER,
      },
      linksControl: false,
      panControl: false,
      enableCloseButton: false,
    },
  );
}

window.initPano = initPano;

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

HTML

<html>
  <head>
    <title>Street View Controls</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=initPano&v=weekly"
      defer
    ></script>
  </body>
</html>
الاطّلاع على مثال

الوصول المباشر إلى بيانات "التجوّل الافتراضي"

قد ترغب في تحديد مدى توفّر بيانات "التجوّل الافتراضي" آليًا أو عرض معلومات عن صور بانورامية معيّنة بدون الحاجة إلى معالجة مباشرة للخريطة أو الصورة البانورامية. ويمكنك إجراء ذلك باستخدام الكائن StreetViewService الذي يوفّر واجهة للبيانات المخزَّنة في خدمة &quot;التجوّل الافتراضي&quot; من Google.

طلبات خدمة "التجوّل الافتراضي"

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

يمكنك بدء الطلبات إلى StreetViewService باستخدام StreetViewPanoRequest أو StreetViewLocationRequest.

يؤدي الطلب الذي يستخدم StreetViewPanoRequest إلى عرض بيانات بانورامية استنادًا إلى معرّف مرجعي يحدّد الصورة البانورامية بشكل فريد. يُرجى العِلم أنّ معرّفات المراجع هذه تكون ثابتة فقط طوال مدة عرض صور تلك البانوراما.

يبحث الطلب الذي يستخدم StreetViewLocationRequest عن بيانات بانورامية في موقع جغرافي محدّد، باستخدام المَعلمات التالية:

  • يحدّد location الموقع الجغرافي (خط العرض وخط الطول) للبحث عن صورة بانورامية.
  • preference يحدّد الإعداد المفضّل للصورة البانورامية التي يجب العثور عليها ضمن نطاق البحث: الصورة الأقرب إلى الموقع الجغرافي المقدَّم أو أفضل صورة ضمن نطاق البحث.
  • يحدّد radius نصف قطر، بالمتر، للبحث عن صورة بانورامية، ويكون مركزه خط العرض وخط الطول المحدّدَين. يتم ضبط القيمة تلقائيًا على 50 في حال عدم توفيرها.
  • تحدّد السمة source مصدر الصور البانورامية المطلوب البحث فيها. القيم الصالحة هي:
    • يستخدم default المصادر التلقائية لميزة &quot;التجوّل الافتراضي&quot;، ولا تقتصر عمليات البحث على مصادر معيّنة.
    • يقتصر استخدام outdoor على عمليات البحث في المجموعات الخارجية. يُرجى العِلم أنّ الصور البانورامية الخارجية قد لا تكون متوفّرة للموقع الجغرافي المحدّد.

الردود على خدمة "التجوّل الافتراضي"

تحتاج الدالة getPanorama() إلى دالة رد اتصال لتنفيذها عند استرداد نتيجة من خدمة "التجوّل الافتراضي". تعرض دالّة ردّ الاتصال هذه مجموعة من بيانات الصور البانورامية ضمن عنصر StreetViewPanoramaData ورمز StreetViewStatus يشير إلى حالة الطلب، بهذا الترتيب.

تحتوي مواصفات عنصر StreetViewPanoramaData على بيانات وصفية حول بانوراما في "التجوّل الافتراضي" بالشكل التالي:

{
  "location": {
    "latLng": LatLng,
    "description": string,
    "pano": string
  },
  "copyright": string,
  "links": [{
      "heading": number,
      "description": string,
      "pano": string,
      "roadColor": string,
      "roadOpacity": number
    }],
  "tiles": {
    "worldSize": Size,
    "tileSize": Size,
    "centerHeading": number
  }
}

يُرجى العِلم أنّ عنصر البيانات هذا ليس عنصر StreetViewPanorama بحد ذاته. لإنشاء عنصر في &quot;التجوّل الافتراضي&quot; باستخدام هذه البيانات، عليك إنشاء StreetViewPanorama وطلب setPano()، مع تمرير المعرّف كما هو موضّح في حقل location.pano الذي تم عرضه.

قد يعرض الرمز status إحدى القيم التالية:

  • تشير القيمة OK إلى أنّ الخدمة عثرت على صورة بانورامية مطابقة.
  • تشير القيمة ZERO_RESULTS إلى أنّ الخدمة لم تتمكّن من العثور على صورة بانورامية مطابقة للمعايير التي تم تمريرها.
  • تشير السمة UNKNOWN_ERROR إلى أنّه تعذّرت معالجة طلب "التجوّل الافتراضي"، مع أنّ السبب الدقيق غير معروف.

تنشئ التعليمة البرمجية التالية StreetViewService يستجيب لنقرات المستخدم على الخريطة من خلال إنشاء علامات، وعند النقر عليها، يتم عرض StreetViewPanorama لهذا الموقع الجغرافي. يستخدم الرمز محتوى StreetViewPanoramaData الذي تم عرضه من الخدمة.

TypeScript

/*
 * Click the map to set a new location for the Street View camera.
 */

let map: google.maps.Map;

let panorama: google.maps.StreetViewPanorama;

function initMap(): void {
  const berkeley = { lat: 37.869085, lng: -122.254775 };
  const sv = new google.maps.StreetViewService();

  panorama = new google.maps.StreetViewPanorama(
    document.getElementById("pano") as HTMLElement
  );

  // Set up the map.
  map = new google.maps.Map(document.getElementById("map") as HTMLElement, {
    center: berkeley,
    zoom: 16,
    streetViewControl: false,
  });

  // Set the initial Street View camera to the center of the map
  sv.getPanorama({ location: berkeley, radius: 50 }).then(processSVData);

  // Look for a nearby Street View panorama when the map is clicked.
  // getPanorama will return the nearest pano when the given
  // radius is 50 meters or less.
  map.addListener("click", (event) => {
    sv.getPanorama({ location: event.latLng, radius: 50 })
      .then(processSVData)
      .catch((e) =>
        console.error("Street View data not found for this location.")
      );
  });
}

function processSVData({ data }: google.maps.StreetViewResponse) {
  const location = data.location!;

  const marker = new google.maps.Marker({
    position: location.latLng,
    map,
    title: location.description,
  });

  panorama.setPano(location.pano as string);
  panorama.setPov({
    heading: 270,
    pitch: 0,
  });
  panorama.setVisible(true);

  marker.addListener("click", () => {
    const markerPanoID = location.pano;

    // Set the Pano to use the passed panoID.
    panorama.setPano(markerPanoID as string);
    panorama.setPov({
      heading: 270,
      pitch: 0,
    });
    panorama.setVisible(true);
  });
}

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

JavaScript

/*
 * Click the map to set a new location for the Street View camera.
 */
let map;
let panorama;

function initMap() {
  const berkeley = { lat: 37.869085, lng: -122.254775 };
  const sv = new google.maps.StreetViewService();

  panorama = new google.maps.StreetViewPanorama(
    document.getElementById("pano"),
  );
  // Set up the map.
  map = new google.maps.Map(document.getElementById("map"), {
    center: berkeley,
    zoom: 16,
    streetViewControl: false,
  });
  // Set the initial Street View camera to the center of the map
  sv.getPanorama({ location: berkeley, radius: 50 }).then(processSVData);
  // Look for a nearby Street View panorama when the map is clicked.
  // getPanorama will return the nearest pano when the given
  // radius is 50 meters or less.
  map.addListener("click", (event) => {
    sv.getPanorama({ location: event.latLng, radius: 50 })
      .then(processSVData)
      .catch((e) =>
        console.error("Street View data not found for this location."),
      );
  });
}

function processSVData({ data }) {
  const location = data.location;
  const marker = new google.maps.Marker({
    position: location.latLng,
    map,
    title: location.description,
  });

  panorama.setPano(location.pano);
  panorama.setPov({
    heading: 270,
    pitch: 0,
  });
  panorama.setVisible(true);
  marker.addListener("click", () => {
    const markerPanoID = location.pano;

    // Set the Pano to use the passed panoID.
    panorama.setPano(markerPanoID);
    panorama.setPov({
      heading: 270,
      pitch: 0,
    });
    panorama.setVisible(true);
  });
}

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

HTML

<html>
  <head>
    <title>Directly Accessing Street View Data</title>

    <link rel="stylesheet" type="text/css" href="./style.css" />
    <script type="module" src="./index.js"></script>
  </head>
  <body>
    <div id="map" style="width: 45%; height: 100%; float: left"></div>
    <div id="pano" style="width: 45%; height: 100%; float: left"></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>
الاطّلاع على مثال

تقديم صور بانوراما مخصّصة في "التجوّل الافتراضي"

تتيح واجهة برمجة التطبيقات Maps JavaScript API عرض الصور البانورامية المخصّصة ضمن الكائن StreetViewPanorama. باستخدام الصور البانورامية المخصّصة، يمكنك عرض الأجزاء الداخلية للمباني أو المناظر من المواقع الجغرافية الخلابة أو أي شيء من خيالك. ويمكنك أيضًا ربط هذه الصور البانورامية المخصّصة بصور "التجوّل الافتراضي" البانورامية الحالية من Google.

يتضمن إعداد مجموعة من صور البانوراما المخصصة الخطوات التالية:

  • أنشئ صورة بانورامية أساسية لكل صورة بانورامية مخصّصة. يجب أن تكون هذه الصورة الأساسية بأعلى دقة تريد عرض الصور المكبّرة بها.
  • (اختياري، ولكن يُنصح به) أنشئ مجموعة من المربّعات البانورامية بمستويات تكبير مختلفة من الصورة الأساسية.
  • أنشئ روابط بين صورك البانورامية المخصّصة.
  • (اختياري) يمكنك تحديد صور بانورامية "للدخول" ضمن صور "التجوّل الافتراضي" الحالية من Google وتخصيص الروابط من/إلى المجموعة المخصّصة إلى المجموعة العادية.
  • حدِّد البيانات الوصفية لكل صورة بانورامية ضمن عنصر StreetViewPanoramaData.
  • نفِّذ طريقة تحدّد بيانات وصور البانوراما المخصّصة وعيِّن هذه الطريقة كمعالج مخصّص ضمن الكائن StreetViewPanorama.

توضّح الأقسام التالية هذه العملية.

إنشاء صور بانورامية مخصّصة

كل بانوراما في "التجوّل الافتراضي" هي صورة أو مجموعة من الصور التي توفّر عرضًا كاملاً بزاوية 360 درجة من موقع جغرافي واحد. يستخدم الكائن StreetViewPanorama صورًا تتوافق مع الإسقاط متساوي المستطيلات (Plate Carrée). يتضمّن هذا الإسقاط عرضًا أفقيًا بزاوية 360 درجة (عرض كامل) وعرضًا عموديًا بزاوية 180 درجة (من الأعلى إلى الأسفل). وينتج عن مجالات الرؤية هذه صورة بنسبة عرض إلى ارتفاع تبلغ 2:1. يظهر أدناه صورة بانورامية كاملة.

عرض بانورامي لشارع في المدينة

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

كرة مع عرض بانورامي لشارع على سطحها

إنّ التعامل مع الصورة البانورامية كإسقاط على كرة باستخدام نظام إحداثيات مستقيمة الخطوط يكون مفيدًا عند تقسيم الصورة إلى مربّعات مستقيمة الخطوط، وعرض الصور استنادًا إلى إحداثيات المربّعات المحسوبة.

إنشاء مربّعات بانورامية مخصّصة

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

عند تحميل StreetViewPanorama لأول مرة، يعرض تلقائيًا صورة تتألف من %25 (قوس بزاوية 90 درجة) من العرض الأفقي للبانوراما عند مستوى التكبير 1. يتوافق هذا العرض تقريبًا مع مجال الرؤية البشري العادي. ويؤدي التصغير من طريقة العرض التلقائية هذه إلى توفير قوس أوسع، بينما يؤدي التكبير إلى تضييق مجال العرض إلى قوس أصغر. تحسب StreetViewPanorama تلقائيًا مجال الرؤية المناسب لمستوى التكبير المحدّد، ثم تختار الصور الأكثر ملاءمةً لدرجة الدقة هذه من خلال اختيار مجموعة مربّعات تتطابق تقريبًا مع أبعاد مجال الرؤية الأفقي. تتوافق مجالات الرؤية التالية مع مستويات التكبير في ميزة "التجوّل الافتراضي":

مستوى التكبير في "التجوّل الافتراضي" مجال الرؤية (بالدرجات)
0 180
1 (افتراضي) 90
2 45
3 22.5
4 11.25

يُرجى العِلم أنّ حجم الصورة المعروضة ضمن "التجوّل الافتراضي" يعتمد بالكامل على حجم الشاشة (العرض) لحاوية "التجوّل الافتراضي". إذا قدّمت حاوية أوسع، ستظل الخدمة توفّر مجال الرؤية نفسه لأي مستوى تكبير معيّن، مع أنّها قد تختار مربّعات أكثر ملاءمةً لهذه الدقة بدلاً من ذلك.

بما أنّ كل صورة بانورامية تتألف من إسقاط متساوي المستطيلات، فإنّ إنشاء مربّعات بانورامية يُعدّ أمرًا سهلاً نسبيًا. بما أنّ الإسقاط يوفّر صورة بنسبة عرض إلى ارتفاع 2:1، فإنّ المربّعات التي تبلغ نسبتها 2:1 تكون أسهل في الاستخدام، مع أنّ المربّعات قد توفّر أداءً أفضل على الخرائط المربّعة (لأنّ مجال الرؤية سيكون مربّعًا).

بالنسبة إلى المربّعات التي تبلغ نسبة العرض إلى الارتفاع فيها 2:1، تمثّل صورة واحدة تشمل البانوراما بأكملها "عالم" البانوراما بأكمله (الصورة الأساسية) عند مستوى التكبير 0، مع زيادة كل مستوى تكبير يقدّم مربّعات 4zoomLevel. (على سبيل المثال، في مستوى التكبير 2، تتألف الصورة البانورامية بأكملها من 16 مربّعًا). ملاحظة: لا تتطابق مستويات التكبير في مربّعات "التجوّل الافتراضي" مباشرةً مع مستويات التكبير المقدَّمة باستخدام عنصر التحكّم في "التجوّل الافتراضي". بدلاً من ذلك، تختار مستويات التكبير في عنصر التحكّم في "التجوّل الافتراضي" "مجال رؤية" (FoV) يتم اختيار المربّعات المناسبة منه.

عرض بانورامي لشارع في مدينة مقسّم إلى مربّعات

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

التعامل مع طلبات الصور البانورامية المخصّصة

لاستخدام بانوراما مخصّصة، يمكنك استدعاء StreetViewPanorama.registerPanoProvider()، مع تحديد اسم طريقة موفّر البانوراما المخصّصة. يجب أن يعرض الإجراء الخاص بموفّر الصور البانورامية عنصر StreetViewPanoramaData، وله التوقيع التالي:

Function(pano):StreetViewPanoramaData

‫StreetViewPanoramaData هو عنصر بالشكل التالي:

{
  copyright: string,
  location: {
    description: string,
    latLng: google.maps.LatLng,
    pano: string
  },
  tiles: {
    tileSize: google.maps.Size,
    worldSize: google.maps.Size,
    heading: number,
    getTileUrl: Function
  },
  links: [
    description: string,
    heading: number,
    pano: string,
    roadColor: string,
    roadOpacity: number
  ]
}

يمكنك عرض بانوراما مخصّصة على النحو التالي:

  • اضبط السمة StreetViewPanoramaOptions.pano على قيمة مخصّصة.
  • استخدِم StreetViewPanorama.registerPanoProvider() لتوفير دالة مخصّصة لموفّر الصور البانورامية.
  • نفِّذ دالة موفّر البانوراما المخصّصة لمعالجة قيمة pano المحدّدة.
  • أنشئ عنصر StreetViewPanoramaData.
  • اضبط السمة StreetViewTileData.getTileUrl على اسم دالة مخصّصة لموفّر المربّعات التي تقدّمها. مثلاً: getCustomPanoramaTileUrl
  • نفِّذ دالة موفّر المربّعات المخصّصة، كما هو موضّح في النماذج أدناه.
  • يجب إرجاع العنصر StreetViewPanoramaData.

ملاحظة: لا تضبط position مباشرةً على StreetViewPanorama عندما تريد عرض صور بانورامية مخصّصة، لأنّ هذا الموضع سيوجّه خدمة "التجوّل الافتراضي" لطلب صور "التجوّل الافتراضي" التلقائية القريبة من ذلك الموقع الجغرافي. بدلاً من ذلك، اضبط هذا الموضع ضمن حقل location.latLng الخاص بكائن StreetViewPanoramaData المخصّص.

يعرض المثال التالي صورة بانورامية مخصّصة لمكتب Google في سيدني. يُرجى العِلم أنّ هذا المثال لا يستخدم خريطة أو صور "التجوّل الافتراضي" التلقائية:

TypeScript

function initPano() {
  // Set up Street View and initially set it visible. Register the
  // custom panorama provider function. Set the StreetView to display
  // the custom panorama 'reception' which we check for below.
  const panorama = new google.maps.StreetViewPanorama(
    document.getElementById("map") as HTMLElement,
    { pano: "reception", visible: true }
  );

  panorama.registerPanoProvider(getCustomPanorama);
}

// Return a pano image given the panoID.
function getCustomPanoramaTileUrl(
  pano: string,
  zoom: number,
  tileX: number,
  tileY: number
): string {
  return (
    "https://developers.google.com/maps/documentation/javascript/examples/full/images/" +
    "panoReception1024-" +
    zoom +
    "-" +
    tileX +
    "-" +
    tileY +
    ".jpg"
  );
}

// Construct the appropriate StreetViewPanoramaData given
// the passed pano IDs.
function getCustomPanorama(pano: string): google.maps.StreetViewPanoramaData {
  if (pano === "reception") {
    return {
      location: {
        pano: "reception",
        description: "Google Sydney - Reception",
      },
      links: [],
      // The text for the copyright control.
      copyright: "Imagery (c) 2010 Google",
      // The definition of the tiles for this panorama.
      tiles: {
        tileSize: new google.maps.Size(1024, 512),
        worldSize: new google.maps.Size(2048, 1024),
        // The heading in degrees at the origin of the panorama
        // tile set.
        centerHeading: 105,
        getTileUrl: getCustomPanoramaTileUrl,
      },
    };
  }
  // @ts-ignore TODO fix typings
  return null;
}

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

JavaScript

function initPano() {
  // Set up Street View and initially set it visible. Register the
  // custom panorama provider function. Set the StreetView to display
  // the custom panorama 'reception' which we check for below.
  const panorama = new google.maps.StreetViewPanorama(
    document.getElementById("map"),
    { pano: "reception", visible: true },
  );

  panorama.registerPanoProvider(getCustomPanorama);
}

// Return a pano image given the panoID.
function getCustomPanoramaTileUrl(pano, zoom, tileX, tileY) {
  return (
    "https://developers.google.com/maps/documentation/javascript/examples/full/images/" +
    "panoReception1024-" +
    zoom +
    "-" +
    tileX +
    "-" +
    tileY +
    ".jpg"
  );
}

// Construct the appropriate StreetViewPanoramaData given
// the passed pano IDs.
function getCustomPanorama(pano) {
  if (pano === "reception") {
    return {
      location: {
        pano: "reception",
        description: "Google Sydney - Reception",
      },
      links: [],
      // The text for the copyright control.
      copyright: "Imagery (c) 2010 Google",
      // The definition of the tiles for this panorama.
      tiles: {
        tileSize: new google.maps.Size(1024, 512),
        worldSize: new google.maps.Size(2048, 1024),
        // The heading in degrees at the origin of the panorama
        // tile set.
        centerHeading: 105,
        getTileUrl: getCustomPanoramaTileUrl,
      },
    };
  }
  // @ts-ignore TODO fix typings
  return null;
}

window.initPano = initPano;

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

HTML

<html>
  <head>
    <title>Custom Street View Panoramas</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=initPano&v=weekly"
      defer
    ></script>
  </body>
</html>
الاطّلاع على مثال

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

يضيف المثال التالي سهمًا آخر إلى الصورة، بالإضافة إلى أسهم التنقل التلقائية في &quot;التجوّل الافتراضي&quot;، والتي تشير إلى Google Sydney وترتبط بالصور المخصّصة:

TypeScript

let panorama: google.maps.StreetViewPanorama;

// StreetViewPanoramaData of a panorama just outside the Google Sydney office.
let outsideGoogle: google.maps.StreetViewPanoramaData;

// StreetViewPanoramaData for a custom panorama: the Google Sydney reception.
function getReceptionPanoramaData(): google.maps.StreetViewPanoramaData {
  return {
    location: {
      pano: "reception", // The ID for this custom panorama.
      description: "Google Sydney - Reception",
      latLng: new google.maps.LatLng(-33.86684, 151.19583),
    },
    links: [
      {
        heading: 195,
        description: "Exit",
        pano: (outsideGoogle.location as google.maps.StreetViewLocation).pano,
      },
    ],
    copyright: "Imagery (c) 2010 Google",
    tiles: {
      tileSize: new google.maps.Size(1024, 512),
      worldSize: new google.maps.Size(2048, 1024),
      centerHeading: 105,
      getTileUrl: function (
        pano: string,
        zoom: number,
        tileX: number,
        tileY: number
      ): string {
        return (
          "https://developers.google.com/maps/documentation/javascript/examples/full/images/" +
          "panoReception1024-" +
          zoom +
          "-" +
          tileX +
          "-" +
          tileY +
          ".jpg"
        );
      },
    },
  };
}

function initPanorama() {
  panorama = new google.maps.StreetViewPanorama(
    document.getElementById("street-view") as HTMLElement,
    { pano: (outsideGoogle.location as google.maps.StreetViewLocation).pano }
  );
  // Register a provider for the custom panorama.
  panorama.registerPanoProvider(
    (pano: string): google.maps.StreetViewPanoramaData => {
      if (pano === "reception") {
        return getReceptionPanoramaData();
      }
      // @ts-ignore TODO fix typings
      return null;
    }
  );

  // Add a link to our custom panorama from outside the Google Sydney office.
  panorama.addListener("links_changed", () => {
    if (
      panorama.getPano() ===
      (outsideGoogle.location as google.maps.StreetViewLocation).pano
    ) {
      panorama.getLinks().push({
        description: "Google Sydney",
        heading: 25,
        pano: "reception",
      });
    }
  });
}

function initMap(): void {
  // Use the Street View service to find a pano ID on Pirrama Rd, outside the
  // Google office.
  new google.maps.StreetViewService()
    .getPanorama({ location: { lat: -33.867386, lng: 151.195767 } })
    .then(({ data }: google.maps.StreetViewResponse) => {
      outsideGoogle = data;
      initPanorama();
    });
}

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

JavaScript

let panorama;
// StreetViewPanoramaData of a panorama just outside the Google Sydney office.
let outsideGoogle;

// StreetViewPanoramaData for a custom panorama: the Google Sydney reception.
function getReceptionPanoramaData() {
  return {
    location: {
      pano: "reception", // The ID for this custom panorama.
      description: "Google Sydney - Reception",
      latLng: new google.maps.LatLng(-33.86684, 151.19583),
    },
    links: [
      {
        heading: 195,
        description: "Exit",
        pano: outsideGoogle.location.pano,
      },
    ],
    copyright: "Imagery (c) 2010 Google",
    tiles: {
      tileSize: new google.maps.Size(1024, 512),
      worldSize: new google.maps.Size(2048, 1024),
      centerHeading: 105,
      getTileUrl: function (pano, zoom, tileX, tileY) {
        return (
          "https://developers.google.com/maps/documentation/javascript/examples/full/images/" +
          "panoReception1024-" +
          zoom +
          "-" +
          tileX +
          "-" +
          tileY +
          ".jpg"
        );
      },
    },
  };
}

function initPanorama() {
  panorama = new google.maps.StreetViewPanorama(
    document.getElementById("street-view"),
    { pano: outsideGoogle.location.pano },
  );
  // Register a provider for the custom panorama.
  panorama.registerPanoProvider((pano) => {
    if (pano === "reception") {
      return getReceptionPanoramaData();
    }
    // @ts-ignore TODO fix typings
    return null;
  });
  // Add a link to our custom panorama from outside the Google Sydney office.
  panorama.addListener("links_changed", () => {
    if (panorama.getPano() === outsideGoogle.location.pano) {
      panorama.getLinks().push({
        description: "Google Sydney",
        heading: 25,
        pano: "reception",
      });
    }
  });
}

function initMap() {
  // Use the Street View service to find a pano ID on Pirrama Rd, outside the
  // Google office.
  new google.maps.StreetViewService()
    .getPanorama({ location: { lat: -33.867386, lng: 151.195767 } })
    .then(({ data }) => {
      outsideGoogle = data;
      initPanorama();
    });
}

window.initMap = initMap;

CSS

html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}

#street-view {
  height: 100%;
}

HTML

<html>
  <head>
    <title>Custom Street View Panorama Tiles</title>

    <link rel="stylesheet" type="text/css" href="./style.css" />
    <script type="module" src="./index.js"></script>
  </head>
  <body>
    <div id="street-view"></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>
الاطّلاع على مثال