Place Photos

Entwickler im Europäischen Wirtschaftsraum (EWR)

Mit der Funktion „Place Photos“ können Sie qualitativ hochwertige Fotos auf Ihren Webseiten einbinden. Sie können auf Millionen von Fotos in der Places-Datenbank zugreifen und mit Find Place, Nearby Search, Text Search, Autocomplete und Place Details Bilder in beliebiger Größe abrufen.

Vollständigen Quellcode des Beispiels ansehen

In dieser Demo wird ein zufälliges Foto aus einer von fünf Städten angezeigt. Die erforderlichen Quellenangaben sind unten rechts auf dem Foto zu sehen.

TypeScript

const CITIES = [
    { name: 'New York', id: 'ChIJOwg_06VPwokRYv534QaPC8g' },
    { name: 'London', id: 'ChIJdd4hrwug2EcRmSrV3Vo6llI' },
    { name: 'Paris', id: 'ChIJD7fiBh9u5kcRYJSMaMOCCwQ' },
    { name: 'Tokyo', id: 'ChIJ51cu8IcbXWARiRtXIothAS4' },
    { name: 'Rome', id: 'ChIJu46S-ZZhLxMROG5lkwZ3D7k' },
];

async function init() {
    const { Place } = await google.maps.importLibrary('places');

    const heading = document.getElementById('heading')!;
    const summary = document.getElementById('summary')!;
    const expandedImageDiv = document.getElementById('expanded-image')!;
    const randomizeBtn = document.getElementById(
        'randomize-btn'
    ) as HTMLButtonElement;

    async function showRandomCityPhoto() {
        randomizeBtn.disabled = true;

        // Pick a random city
        const city = CITIES[Math.floor(Math.random() * CITIES.length)];

        try {
            const place = new Place({ id: city.id });
            await place.fetchFields({
                fields: ['displayName', 'photos', 'editorialSummary'],
            });

            heading.textContent = place.displayName ?? city.name;
            summary.textContent = place.editorialSummary ?? '';
            expandedImageDiv.innerHTML = '';

            if (place.photos && place.photos.length > 0) {
                // Pick one of the first 10 photos
                const maxPhotos = Math.min(10, place.photos.length);
                const randomIndex = Math.floor(Math.random() * maxPhotos);
                const photo = place.photos[randomIndex];

                const img = document.createElement('img');
                img.alt = `Photo of ${place.displayName ?? city.name}`;
                img.src = photo.getURI({ maxHeight: 800 });
                expandedImageDiv.appendChild(img);

                if (photo.authorAttributions.length) {
                    expandedImageDiv.appendChild(
                        createAttribution(photo.authorAttributions[0])
                    );
                }
            } else {
                expandedImageDiv.textContent =
                    'No photos available for this location.';
            }
        } catch (error) {
            console.error('Failed to fetch place details:', error);
            summary.textContent = 'Failed to load data.';
        }

        // Re-enable button after 2 seconds
        setTimeout(() => {
            randomizeBtn.disabled = false;
        }, 2000);
    }

    // Helper function to create attribution DIV.
    function createAttribution(
        attribution: google.maps.places.AuthorAttribution
    ) {
        const attributionLabel = document.createElement('a');
        attributionLabel.classList.add('attribution-label');
        attributionLabel.textContent = attribution.displayName;
        attributionLabel.href = attribution.uri!;
        attributionLabel.target = '_blank';
        attributionLabel.rel = 'noopener noreferrer';
        return attributionLabel;
    }

    randomizeBtn.addEventListener('click', () => void showRandomCityPhoto());

    // Initial load
    void showRandomCityPhoto();
}

void init();

JavaScript

const CITIES = [
    { name: 'New York', id: 'ChIJOwg_06VPwokRYv534QaPC8g' },
    { name: 'London', id: 'ChIJdd4hrwug2EcRmSrV3Vo6llI' },
    { name: 'Paris', id: 'ChIJD7fiBh9u5kcRYJSMaMOCCwQ' },
    { name: 'Tokyo', id: 'ChIJ51cu8IcbXWARiRtXIothAS4' },
    { name: 'Rome', id: 'ChIJu46S-ZZhLxMROG5lkwZ3D7k' },
];

async function init() {
    const { Place } = await google.maps.importLibrary('places');

    const heading = document.getElementById('heading');
    const summary = document.getElementById('summary');
    const expandedImageDiv = document.getElementById('expanded-image');
    const randomizeBtn = document.getElementById('randomize-btn');

    async function showRandomCityPhoto() {
        randomizeBtn.disabled = true;

        // Pick a random city
        const city = CITIES[Math.floor(Math.random() * CITIES.length)];

        try {
            const place = new Place({ id: city.id });
            await place.fetchFields({
                fields: ['displayName', 'photos', 'editorialSummary'],
            });

            heading.textContent = place.displayName ?? city.name;
            summary.textContent = place.editorialSummary ?? '';
            expandedImageDiv.innerHTML = '';

            if (place.photos && place.photos.length > 0) {
                // Pick one of the first 10 photos
                const maxPhotos = Math.min(10, place.photos.length);
                const randomIndex = Math.floor(Math.random() * maxPhotos);
                const photo = place.photos[randomIndex];

                const img = document.createElement('img');
                img.alt = `Photo of ${place.displayName ?? city.name}`;
                img.src = photo.getURI({ maxHeight: 800 });
                expandedImageDiv.appendChild(img);

                if (photo.authorAttributions.length) {
                    expandedImageDiv.appendChild(
                        createAttribution(photo.authorAttributions[0])
                    );
                }
            } else {
                expandedImageDiv.textContent =
                    'No photos available for this location.';
            }
        } catch (error) {
            console.error('Failed to fetch place details:', error);
            summary.textContent = 'Failed to load data.';
        }

        // Re-enable button after 2 seconds
        setTimeout(() => {
            randomizeBtn.disabled = false;
        }, 2000);
    }

    // Helper function to create attribution DIV.
    function createAttribution(attribution) {
        const attributionLabel = document.createElement('a');
        attributionLabel.classList.add('attribution-label');
        attributionLabel.textContent = attribution.displayName;
        attributionLabel.href = attribution.uri;
        attributionLabel.target = '_blank';
        attributionLabel.rel = 'noopener noreferrer';
        return attributionLabel;
    }

    randomizeBtn.addEventListener('click', () => void showRandomCityPhoto());

    // Initial load
    void showRandomCityPhoto();
}

void init();

CSS

/* 
 * Optional: Makes the sample page fill the window. 
 */
html,
body {
    height: 100%;
    margin: 0;
    padding: 0;
    font-family: 'Inter', sans-serif;
    background: linear-gradient(135deg, #1e1e2f, #252540);
    display: flex;
    align-items: center;
    justify-content: center;
}

#container {
    display: flex;
    background: rgba(255, 255, 255, 0.05);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 20px;
    padding: 30px;
    max-width: 900px;
    width: 100%;
    box-sizing: border-box;
    box-shadow: 0 25px 50px rgba(0, 0, 0, 0.3);
    color: white;
}

.place-overview {
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: center;
    margin-right: 40px;
}

#info {
    position: relative;
}

#heading {
    font-size: 2.5rem;
    font-weight: 700;
    margin-top: 0;
    margin-bottom: 15px;
    background: linear-gradient(90deg, #fff, #a5b4fc);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
}

#summary {
    font-size: 1.1rem;
    line-height: 1.6;
    color: #cbd5e1;
}

#expanded-image {
    position: relative;
    width: 400px;
    height: 400px;
    border-radius: 16px;
    overflow: hidden;
    box-shadow: 0 15px 35px rgba(0, 0, 0, 0.4);
    transform: perspective(1000px) rotateY(-5deg);
    transition: transform 0.5s ease;
}

#expanded-image:hover {
    transform: perspective(1000px) rotateY(0deg) scale(1.02);
}

#expanded-image img {
    width: 100%;
    height: 100%;
    object-fit: contain;
}

#randomize-btn {
    margin-top: 20px;
    padding: 12px 24px;
    font-size: 1.1rem;
    font-weight: bold;
    color: white;
    background: linear-gradient(90deg, #4f46e5, #3b82f6);
    border: none;
    border-radius: 12px;
    cursor: pointer;
    transition:
        transform 0.2s,
        background 0.3s;
    box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
    font-family: 'Inter', sans-serif;
    align-self: flex-start;
}

#randomize-btn:hover:not(:disabled) {
    transform: scale(1.05);
}

#randomize-btn:disabled {
    background: #475569;
    cursor: not-allowed;
    opacity: 0.7;
}

.attribution-label {
    position: absolute;
    bottom: 10px;
    right: 10px;
    background-color: rgba(0, 0, 0, 0.6);
    color: white;
    padding: 6px 12px;
    border-radius: 20px;
    font-size: 0.8rem;
    text-decoration: none;
    backdrop-filter: blur(4px);
    transition: background-color 0.3s;
}

.attribution-label:hover {
    background-color: rgba(0, 0, 0, 0.8);
}

HTML

<html lang="en">
    <head>
        <title>Place Photos</title>
        <link
            href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap"
            rel="stylesheet" />
        <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: "AIzaSyA6myHzS10YXdcazAFalmXvDkrYCp5cLc8"
            });
        </script>
    </head>
    <body>
        <div id="container">
            <div class="place-overview">
                <div id="info">
                    <h1 id="heading"></h1>
                    <div id="summary"></div>
                </div>
                <button id="randomize-btn">Random City Photo</button>
            </div>
            <div id="expanded-image"></div>
        </div>
    </body>
</html>

Testbeispiel

Fotos herunterladen

Wenn Sie Fotos für einen Ort abrufen möchten, fügen Sie das Feld photos in die Anfrageparameter fetchFields() ein. Die resultierende Place-Instanz enthält ein Array mit bis zu 10 Photo-Objekten, über die Sie auf Bilder und die erforderlichen Quellenangaben zugreifen können. Rufen Sie getURI() auf, um den URI des Quellfotos zurückzugeben. Verwenden Sie PhotoOptions, um die maximale Höhe und/oder Breite des zurückgegebenen Bildes festzulegen. Wenn Sie sowohl für maxHeight als auch für maxWidth einen Wert angeben, wird das Bild vom Fotodienst auf die kleinere der beiden Größen skaliert, wobei das ursprüngliche Seitenverhältnis beibehalten wird. Wenn keine Dimension angegeben ist, wird das Bild in voller Größe zurückgegeben.

Die Klasse Photo stellt die folgenden Eigenschaften bereit:

  • authorAttributions: Ein Array von AuthorAttribution-Objekten mit dem erforderlichen Zuordnungstext und den erforderlichen URLs.
  • flagContentURI: Ein Link, über den der Nutzer ein Problem mit dem Foto melden kann.
  • googleMapsURI: Ein Link, um das Foto in Google Maps aufzurufen.
  • heightPx: Die Höhe des Fotos in Pixeln.
  • widthPx: Die Breite des Fotos in Pixeln.

Im folgenden Beispiel wird eine Place Details-Anfrage für Fotos gestellt, getURI() für eine Fotoinstanz aufgerufen, um den Quell-URI für das Bild zurückzugeben, und das erste Fotoergebnis wird einem img-Element hinzugefügt (die Quellenangaben werden aus Gründen der Übersichtlichkeit weggelassen):

const { Place } = await google.maps.importLibrary('places');

// Use a place ID to create a new Place instance.
const place = new Place({
    id: 'ChIJydSuSkkUkFQRsqhB-cEtYnw', // Woodland Park Zoo, Seattle WA
});

// Call fetchFields, passing the desired data fields.
await place.fetchFields({ fields: ['photos'] });

// Add the first photo to an img element.
const photoImg = document.getElementById('image-container');
photoImg.src = place.photos[0].getURI({maxHeight: 400});

Autorenangaben

Wenn Sie ein Foto anzeigen, müssen Sie auch die Autorenangaben für das Foto anzeigen. Verwenden Sie die Klasse AuthorAttribution, um Quellenangaben zurückzugeben. Eine Quellenangabe enthält den Namen des Autors (displayName), einen URI für sein Google Maps-Profil (uri) und einen URI für das Foto des Autors (photoURI). Im folgenden Snippet wird gezeigt, wie displayName, uri und photoURI für ein Ortsfoto zurückgegeben werden.

  let name = place.photos[0].authorAttributions[0].displayName;
  let url = place.photos[0].authorAttributions[0].uri;
  let authorPhoto = place.photos[0].authorAttributions[0].photoURI;