# How to Load Google Photorealistic 3D Tiles in CesiumJS: A Complete Implementation Guide

> Learn to load Google Photorealistic 3D Tiles in CesiumJS. Implement a complete guide to display stunning 3D data with CesiumJS and Google Maps API.

- Repository: [Bilawal Sidhu/gods-eye-view](https://github.com/bilawalsidhu/gods-eye-view)
- Tags: how-to-guide
- Published: 2026-09-04

---

**To load Google Photorealistic 3D Tiles in CesiumJS, set `Cesium.GoogleMaps.defaultApiKey` to your Google Maps API key, call `Cesium.createGooglePhotorealistic3DTileset()` with `onlyUsingWithGoogleGeocoder: true`, add the returned tileset to `viewer.scene.primitives`, and hide the default globe by setting `viewer.scene.globe.show` to `false`.**

Google Photorealistic 3D Tiles provide high-resolution, street-level 3D basemaps that serve as drop-in replacements for Cesium's default globe terrain. In the **bilawalsidhu/gods-eye-view** repository, the loading logic lives in [`src/mapStartup.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/mapStartup.js), which demonstrates production-ready patterns for API key management, automatic fallback handling, and seamless viewer integration when you load Google Photorealistic 3D Tiles in CesiumJS.

## Prerequisites: API Keys and Tokens

Before initializing the tileset, you must configure authentication credentials. The implementation supports two routes: direct access via Google Maps Platform or fallback access through Cesium Ion.

- **Google Maps API Key**: Required for direct access to Google's tile servers. Set this to `Cesium.GoogleMaps.defaultApiKey` before creating the tileset.
- **Cesium Ion Token**: Optional fallback token. If the direct Google request fails, the loader attempts to fetch the tileset through Cesium's hosted endpoint using `Cesium.Ion.defaultAccessToken`.

## Loading the Tileset: The Core Implementation

The [`src/mapStartup.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/mapStartup.js) file exports the `loadPhotorealisticTileset` function, which orchestrates the entire loading sequence with graceful degradation.

### The loadPhotorealisticTileset Function

This asynchronous function attempts to create the tileset in priority order: direct Google access first, then Cesium Ion hosting, and finally a keyless OpenStreetMap globe. It accepts a `Cesium` instance and an options object containing `googleApiKey` and `cesiumToken`.

```javascript
// src/mapStartup.js – core loader
export async function loadPhotorealisticTileset(
  Cesium,
  { googleApiKey = '', cesiumToken = '' } = {}
) {
  const googleKey = clean(googleApiKey);
  const ionToken  = clean(cesiumToken);
  const errors = [];

  // If we have an Ion token, make it the default for Cesium Ion requests
  if (ionToken) Cesium.Ion.defaultAccessToken = ionToken;

  // Build the list of possible routes (direct Google → Ion → fallback)
  const attempts = [];
  if (googleKey) attempts.push({ route: 'google-direct', googleKey });
  if (ionToken)  attempts.push({ route: 'google-ion', googleKey: undefined });

  // Try each route in order
  for (const attempt of attempts) {
    Cesium.GoogleMaps.defaultApiKey = attempt.googleKey;
    try {
      const tileset = await Cesium.createGooglePhotorealistic3DTileset({
        onlyUsingWithGoogleGeocoder: true,
      });
      return { tileset, route: attempt.route, errors };
    } catch (e) {
      errors.push(e instanceof Error ? e : new Error(String(e)));
    }
  }

  // All attempts failed – clean up and fall back
  Cesium.GoogleMaps.defaultApiKey = undefined;
  return { tileset: null, route: 'osm', errors };
}

```

**Key implementation details from the source code:**

- **API Key Injection**: The function assigns `Cesium.GoogleMaps.defaultApiKey` immediately before each attempt to ensure the correct credentials are used for each route.
- **Geocoder Declaration**: The `onlyUsingWithGoogleGeocoder: true` option tells the tileset that the application will use Google's geocoder for coordinate lookups, which is required for proper integration.
- **Error Aggregation**: Failed attempts are captured in an `errors` array and returned to the caller for debugging without crashing the application.

## Integrating with the Cesium Viewer

Once `loadPhotorealisticTileset` returns a tileset, you must add it to the scene and adjust the viewer configuration to prevent visual conflicts with the default globe.

### Adding the Tileset and Hiding the Default Globe

In [`src/main.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/main.js), the initialization code checks the loader result and updates the viewer accordingly. The Google tileset provides its own terrain and imagery, so the native Cesium globe must be disabled.

```javascript
import { loadPhotorealisticTileset } from './mapStartup.js';

const photoreal = await loadPhotorealisticTileset(Cesium, {
  googleApiKey,
  cesiumToken,
});

const tileset = photoreal.tileset;
if (tileset) {
  viewer.scene.primitives.add(tileset);
  viewer.scene.globe.show = false;        // Google tiles provide their own globe
  console.info(`[Init] Google 3D Tiles loaded via ${photoreal.route}.`);
} else {
  // Fallback UI – key‑less globe
  viewer.scene.globe.show = true;
}

```

### Handling Map Stack Controllers

The repository's [`src/mapStackController.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/mapStackController.js) manages the active map "stack" (Google 3D, Bing, Esri, etc.) and reacts to load success or failure events. This separation of concerns ensures that UI components can respond to tileset state changes without tightly coupling the loader to the interface.

## Complete Code Examples

### Minimal Standalone Implementation

For projects not using the God's Eye View loader, you can implement the pattern directly in a single HTML file. This example demonstrates the essential API calls without the abstraction layer.

```html
<script src="https://unpkg.com/cesium/Build/Cesium/Cesium.js"></script>
<script>
  Cesium.Ion.defaultAccessToken = '<YOUR_ION_TOKEN>'; // optional fallback
  Cesium.GoogleMaps.defaultApiKey = '<YOUR_GOOGLE_API_KEY>';

  const viewer = new Cesium.Viewer('cesiumContainer', { baseLayer: false });
  
  Cesium.createGooglePhotorealistic3DTileset({
    onlyUsingWithGoogleGeocoder: true,
  }).then(tileset => {
    viewer.scene.primitives.add(tileset);
    viewer.scene.globe.show = false;
  }).catch(err => console.error('Google 3D Tiles failed:', err));
</script>
<div id="cesiumContainer" style="width:100%;height:100vh;"></div>

```

### Production-Ready Loader from God's Eye View

For robust applications, import the battle-tested loader from [`src/mapStartup.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/mapStartup.js) and handle the three possible return states (success via Google direct, success via Ion, or complete failure).

```javascript
import * as Cesium from 'cesium';
import { loadPhotorealisticTileset } from './src/mapStartup.js';

const { tileset, route, errors } = await loadPhotorealisticTileset(Cesium, {
  googleApiKey: import.meta.env.GOOGLE_MAPS_API_KEY,
  cesiumToken: import.meta.env.CESIUM_ION_TOKEN,
});

if (tileset) {
  viewer.scene.primitives.add(tileset);
  viewer.scene.globe.show = false;
  console.log(`Loaded via ${route}`);
} else {
  console.warn('Falling back to key‑less globe', errors);
}

```

## Summary

- **Authentication**: Set `Cesium.GoogleMaps.defaultApiKey` for direct access or `Cesium.Ion.defaultAccessToken` for hosted fallback before calling the creation method.
- **Creation**: Use `Cesium.createGooglePhotorealistic3DTileset({ onlyUsingWithGoogleGeocoder: true })` to instantiate the photorealistic tileset.
- **Integration**: Add the returned tileset to `viewer.scene.primitives` and immediately set `viewer.scene.globe.show = false` to avoid rendering conflicts.
- **Fallback**: Implement retry logic that captures errors and falls back to a keyless globe (OpenStreetMap) when both Google direct and Ion routes fail, as demonstrated in [`src/mapStartup.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/mapStartup.js).

## Frequently Asked Questions

### Do I need both a Google API key and a Cesium Ion token?

No, only the **Google Maps API key** is required to load Google Photorealistic 3D Tiles in CesiumJS. The Cesium Ion token is optional and serves as a fallback mechanism if the direct Google endpoint fails or if you prefer Cesium's managed hosting. According to the [`src/mapStartup.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/mapStartup.js) implementation, the loader attempts the direct Google route first, then falls back to Ion only if the first attempt fails.

### Why must I hide the default Cesium globe?

You must set `viewer.scene.globe.show = false` because **Google Photorealistic 3D Tiles include their own terrain and imagery**. Rendering both the default Cesium globe and the Google tileset simultaneously creates z-fighting and visual artifacts. The [`src/main.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/main.js) file in the repository handles this by hiding the globe immediately after successfully adding the tileset to the primitives collection.

### What happens if the Google tiles fail to load entirely?

If both the direct Google request and the Cesium Ion fallback fail, the `loadPhotorealisticTileset` function returns `{ tileset: null, route: 'osm', errors }`. The calling code in [`src/main.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/main.js) detects the null tileset and keeps the default globe visible, typically configured with open-source imagery like OpenStreetMap or Esri, ensuring the application remains functional even without Google data.

### Can I use the Google geocoder without loading the 3D tiles?

Yes, but you must still provide a Google Maps API key. The `onlyUsingWithGoogleGeocoder: true` flag specifically tells the tileset that you intend to use Google's geocoder services alongside the 3D tiles. In [`src/keySetup.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/keySetup.js), the repository handles key acquisition and validation separately from the tileset loading logic, allowing you to use Google's location services even if the photorealistic tileset fails to initialize.