## Introduction

The purpose of this guide is to cover the most common uses of `KmlLayer` and
provide corresponding migration paths to alternative implementations. This
information is intended for developers who need to transition from using
KmlLayer due to its scheduled deprecation. The last [version](https://developers.google.com/maps/documentation/javascript/versions.md.txt)
which supports KmlLayer is 3.65, to be decommissioned in May 2027.

Your migration path depends on how you
are using the KmlLayer:

- **[KML file to style boundary/border/area of interest information](https://developers.google.com/maps/comms/kml-layer-migration.md.txt#kml_file_to_style_boundaryborderarea_of_interest_information)** : Use **Data-driven styling (DDS) for boundaries** for administrative areas using Google's boundary data.
- **[KML file with vector data (Points/Polylines/Boundaries/Polygons)](https://developers.google.com/maps/comms/kml-layer-migration.md.txt#kml_file_with_vector_data_pointspolylinesboundariespolygons)** : Use **DDS for datasets** , **GeoJSON** , or **3rd-party libraries** like `deck.gl` or `geoxml3`.
- **[KML file with interactive elements](https://developers.google.com/maps/comms/kml-layer-migration.md.txt#kml_file_with_interactive_elements)**: Implement manual event listeners and custom info windows for feature interaction.
- **[KML file with imagery](https://developers.google.com/maps/comms/kml-layer-migration.md.txt#kml_file_with_imagery)** : Use **GroundOverlays** or 3rd-party parsers for imagery overlays.
- **[KML File with network Links](https://developers.google.com/maps/comms/kml-layer-migration.md.txt#kml_file_with_network_links)** : Upload each KML as a separate dataset, or merge KMLs. If displaying dynamic data, refresh the dataset using the **Datasets API**.
- **[Use KML to display Screen Overlays](https://developers.google.com/maps/comms/kml-layer-migration.md.txt#use_kml_to_display_screen_overlays)** : Use **Custom Controls** to replace fixed UI elements like logos, legends, or navigation aids.

## KML file to style boundary/border/area of interest information

For developers using `KmlLayer` to display or style administrative
boundaries---such as highlighting a specific country, state, or locality---Google
Maps Platform recommends migrating to **Data-driven styling (DDS) for
boundaries**.

### Migration Recommendation: Data-driven Styling for Boundaries

<iframe src="https://developers.devsite.google/frame/maps/comms/kml-layer-migration_177e996d036efa56ffdfc65053417958e2d6fdfa6e67102f41a14418fd76aca4.frame" class="framebox inherit-locale " allow="clipboard-write https://developers.devsite.google" allowfullscreen is-upgraded></iframe>

[Data-driven styling for boundaries](https://developers.google.com/maps/documentation/javascript/dds-boundaries/overview.md.txt)
provides direct access to Google's
administrative boundary polygons, allowing you to apply custom styles (fill and
stroke) to these regions without needing to host or manage external KML files.

#### Available [FeatureType](https://developers.google.com/maps/documentation/javascript/reference/data-driven-styling.md.txt)

Administrative areas are categorized by function and arranged by levels. The
following feature types are supported for styling:

- **`COUNTRY`**: The national political entity.
- **`ADMINISTRATIVE_AREA_LEVEL_1`**: A first-order civil entity below the country level (e.g., states in the US).
- **`ADMINISTRATIVE_AREA_LEVEL_2`**: A second-order civil entity below the country level (e.g., counties in the US).
- **`LOCALITY`**: An incorporated city or town.
- **`POSTAL_CODE`**: Postal codes as used for mail.
- **`SCHOOL_DISTRICT`**: Unified, elementary, or secondary school districts.

See [boundary coverage](https://developers.google.com/maps/documentation/javascript/dds-boundaries/coverage.md.txt)
for regions where these feature types are available.

#### How to Highlight an Area

To style a specific region, you must target it by its **Place ID**.

- **Setup** : You must use a **Map ID** configured for JavaScript **Vector** map type and enable the [Feature layer](https://developers.google.com/maps/documentation/javascript/dds-boundaries/start.md.txt#select_feature_layers) available in the Google Cloud Console.
- **Implementation** : use the [Style a boundary polygon](https://developers.google.com/maps/documentation/javascript/examples/boundaries-simple.md.txt) sample code.

#### Restricting Panning to an Area

To prevent users from navigating outside the bounding box of your highlighted
area, you can use the `restriction` option within the `MapOptions`.

The `restriction` object defines a `latLngBounds` that limits the map's viewable
area. See the
[documentation](https://developers.google.com/maps/documentation/javascript/reference/map.md.txt#MapRestriction)
for more details on how the restriction works.

```javascript
// Restrict panning to a specific bounding box
restriction: {
  latLngBounds: {
    north: 47.8,
    south: 45.8,
    east: 10.5,
    west: 5.9,
  },
  strictBounds: true,
},

```

#### Summary Migration Implementation

Here is a complete example of how to use Data-driven styling for boundaries and
a region restriction to focus the map around a specific area.

```javascript
const myTargetRegion = "ChIJYW1Zb-9kjEcRFXvLDxG1Vlw"; // Place ID for Switzerland

function initMap() {
  const map = new google.maps.Map(document.getElementById("map"), {
    center: { lat: 46.8, lng: 8.2 },
    zoom: 9,
    mapId: "YOUR_MAP_ID", // Required for DDS
    // Restrict panning to a specific bounding box
    restriction: {
    // Bounding box for Switzerland
      latLngBounds: {
        north: 47.8,
        south: 45.8,
        east: 10.5,
        west: 5.9,
      },
      strictBounds: true,
    },
  });

  // Access the Country layer and style a specific region by Place ID
  const countryLayer = map.getFeatureLayer("COUNTRY");
  countryLayer.style = (options) => {
    if (options.feature.placeId === myTargetRegion) {
      return {
        fillColor: "#FF0000",
        fillOpacity: 0.5,
        strokeColor: "#FF0000",
        strokeWeight: 2,
      };
    } else {
    // Style everything else whited out, to make the area of interest pop out more.
      return {
        fillColor: '#ffffff',
        fillOpacity: 0.8,
      };
    }
  };
}
```

## KML file with vector data (Points/Polylines/Boundaries/Polygons)

### Migration Recommendation: Data-driven Styling for Datasets

<iframe src="https://developers.devsite.google/frame/maps/comms/kml-layer-migration_31a084e6cc7bb455b17c93e3039a4057fe928f58f587f614d083f9d28504810d.frame" class="framebox inherit-locale " allow="clipboard-write https://developers.devsite.google" allowfullscreen is-upgraded></iframe>

Google recommends the below path for displaying publicly available geographic
data while gaining more control over styling and performance.

Data-driven styling for datasets lets you upload your own geospatial data (KML,
GeoJSON, or CSV), apply custom styling based on data attributes, and display
features on vector maps.

#### 1. Setup and Upload

Unlike `KmlLayer`, which fetches a URL at runtime, DDS requires you to host the
data as a dataset in the Google Cloud Console.

- **Create a Map ID** : Use a Map ID configured for the **Vector map** type.
- **Upload the Dataset** : Upload your KML file to the Google Cloud Console to generate a unique **Dataset ID** . Read the [full documentation on how to manage Maps Datasets](https://developers.google.com/maps/documentation/javascript/dds-datasets/create-dataset.md.txt) for more details.
- **Display the Dataset:** Once you have created a Dataset ID, you need to associate the Dataset with a Maps Style and a Map ID. You will then use the Dataset ID to actually display the data on the map. Read the [full documentation on how to Add a dataset to a map](https://developers.google.com/maps/documentation/javascript/dds-datasets/add-dataset-to-map.md.txt) for all the details.
- **Note** the [KML requirements](https://developers.google.com/maps/documentation/datasets/prerequisites.md.txt#dataset-kml-requirements) for Datasets, if you decide to import your data from the KML format.

#### 2. Setting the Viewport to the Data

`KmlLayer` automatically pans and zooms to the data location by default. With
DDS for datasets, this behavior is not automatic and must be implemented
manually.

- **Hardcoded Restrictions** : If the data area is static, use the `restriction` option in `MapOptions` to lock the viewport to specific bounds.
- **Dynamic Zooming** : To dynamically set the viewport, you can use `map.fitBounds()` with the bounding box of your dataset.

#### 3. Styling from Feature Attributes

KML files often contain style information (like colors) that DDS does not
automatically apply. You must create a client-side style function that reads
attributes from the dataset features to apply colors and opacity. Consult
the
[developer documentation on how to style your data](https://developers.google.com/maps/documentation/javascript/dds-datasets/style-data-features.md.txt)
for the full details.

#### Example: Styling Function using Attributes

The following example demonstrates how to create a style function that reads
`background_color` and `opacity` attributes directly from the uploaded dataset:

```javascript
/**
 * Migration example: Styling features from dataset attributes.
 */
function styleDDSLayer(map, datasetId) {
  const datasetLayer = map.getDatasetFeatureLayer(datasetId);

  // Set the style function
  datasetLayer.style = (params) => {
    // Access attributes defined in your KML/Dataset
    const featureAttributes = params.feature.datasetAttributes;

    // Read style values from attributes, with fallback defaults
    const fillColor = featureAttributes['background_color'] || '#4285F4';
    const fillOpacity = parseFloat(featureAttributes['opacity']) || 0.5;
    const strokeColor = featureAttributes['border_color'] || '#34A853';

    return {
      fillColor: fillColor,
      fillOpacity: fillOpacity,
      strokeColor: strokeColor,
      strokeWeight: 2,
    };
  };
}
```

For further details on implementing interactions and styling, refer to the
[Data-driven styling for datasets overview](https://developers.google.com/maps/documentation/javascript/dds-datasets/overview.md.txt)
and [Datasets API](https://developers.google.com/maps/documentation/datasets/reference/rest.md.txt)
for dynamic data.

### Migration Recommendation: Client-Side Rendering with GeoJSON

For developers migrating from `KmlLayer`
to client-side rendering with GeoJSON,
Google Maps Platform recommends a migration path that involves converting your
data format and using the [**Data layer**](https://developers.google.com/maps/documentation/javascript/importing_data.md.txt)
to render and style features directly in the browser.

Client-side rendering using the Data layer provides a highly flexible way to
display geographic data. Unlike `KmlLayer`,
which is rendered on Google's
servers, the Data layer lets you interact with features as standard
JavaScript objects. Note, however, that for large datasets you might prefer
server-side processing and rendering of your data, such as with Data-driven
Styling for Datasets.

#### 1. Convert KML to GeoJSON

The first step is to convert your KML files into GeoJSON. This can be done using
several popular open-source tools:

- **[ogr2ogr](https://gdal.org/en/stable/programs/ogr2ogr.html)**: Part of the GDAL suite, this powerful command-line utility can convert between many vector formats.

```bash
ogr2ogr -f GeoJSON output.json input.kml
```

- **[togeojson](https://github.com/mapbox/togeojson)**: A tiny, well-tested tool designed specifically for converting KML and GPX to GeoJSON.

```bash
togeojson input.kml > output.json
```
<iframe src="https://developers.devsite.google/frame/maps/comms/kml-layer-migration_e03a29f28194e64e9760ae7275064de8ed88dc9fafb8c2926225f3a3854cd180.frame" class="framebox inherit-locale " allow="clipboard-write https://developers.devsite.google" allowfullscreen is-upgraded></iframe>

> [!NOTE]
> **Note:** These conversion tools are **not provided
> by Google**. They are community-maintained, open-source projects that have been found to work effectively for straightforward KML files.

#### 2. Setting the Viewport to the Data

While `KmlLayer` automatically pans and zooms to the data location, the Data
layer does not. To set the viewport to fit your GeoJSON data, you must manually
calculate the bounding box and call `map.fitBounds()`.

#### 3. Styling from Feature Attributes

In the Data layer, you can define a `style` function that reads attributes
(properties) directly from each GeoJSON feature to determine its appearance.

#### Example: Styling Function and Viewport Adjustment

The following example demonstrates how to load GeoJSON data, calculate its
bounds to set the viewport, and style features based on their attributes:

```javascript
/**
 * Migration example: Loading GeoJSON, fitting viewport, and styling from attributes.
 */
function initMap() {
  const map = new google.maps.Map(document.getElementById("map"), {
    zoom: 4,
    center: { lat: -28, lng: 137 },
  });

  // Load the GeoJSON data
  map.data.loadGeoJson('path/to/your/data.json', null, (features) => {
    // Adjust viewport to show all loaded features
    const bounds = new google.maps.LatLngBounds();
    features.forEach((feature) => {
      feature.getGeometry().forEachLatLng((latlng) => {
        bounds.extend(latlng);
      });
    });
    map.fitBounds(bounds);
  });

  // Set the style function to read from GeoJSON properties
  map.data.setStyle((feature) => {
    // Access attributes defined in your GeoJSON properties
    const fillColor = feature.getProperty('background_color') || '#4285F4';
    const opacity = parseFloat(feature.getProperty('opacity')) || 0.5;
    const strokeColor = feature.getProperty('border_color') || '#34A853';

    return {
      fillColor: fillColor,
      fillOpacity: opacity,
      strokeColor: strokeColor,
      strokeWeight: 2,
      visible: true
    };
  });
}
```

For more information on using the Data layer, see the
[Importing GeoJSON into Maps](https://developers.google.com/maps/documentation/javascript/importing_data.md.txt)
documentation.

### Migration Path: Client-Side Rendering with 3rd party libraries

For developers seeking other alternatives to the `KmlLayer`, there are
several community-maintained libraries that render KML data on the Google Maps
Platform JavaScript API.

> [!NOTE]
> **Note:** These 3rd party solutions are **not supported by Google**. They are independent open-source projects, and developers are responsible for evaluating their suitability, performance, and maintenance status for their specific use cases.

#### 1. deck.gl

`deck.gl` is a high-performance WebGL-powered visualization framework. It can be
used as a near drop-in replacement for KML rendering through its
`GoogleMapsOverlay` and `GeoJsonLayer`.

- **Canvas Requirement:** To use `deck.gl` effectively, you must **convert
  your map to use the Vector map type** (which renders to a canvas element) with its WebGL rendering capabilities.
- **KML Support:** Geometry parsing is handled by `@loaders.gl/kml`, which converts KML into GeoJSON. Note that some KML features like complex styles, icons, and NetworkLinks may require additional manual implementation.
- **Documentation:** [deck.gl Documentation](https://deck.gl/docs/developer-guide/base-maps/using-with-google-maps) \| [loaders.gl KML Loader](https://loaders.gl/docs/modules/kml/api-reference/kml-loader).
- **Examples:**
  - The [deckgl-kml-updated](https://github.com/googlemaps-samples/js-api-samples/tree/main/samples/deckgl-kml-updated) sample on the Google Maps GitHub repository demonstrates how to use deck.gl to render KML data that is updated in real-time.
  - The [deckgl-kml](https://github.com/googlemaps-samples/js-api-samples/tree/main/samples/deckgl-kml) sample demonstrates how to use deck.gl to render KML data.

#### 2. geoxml3

<iframe src="https://developers.devsite.google/frame/maps/comms/kml-layer-migration_0bd4a756ed1cfa08a0041bad983f34bd8f812077240dfa6e683f0518ad1cbcdc.frame" class="framebox inherit-locale " allow="clipboard-write https://developers.devsite.google" allowfullscreen is-upgraded></iframe>

`geoxml3` is a KML processor specifically designed for the Google Maps
JavaScript API v3. It parses KML locally in the browser and renders the data as
standard Google Maps API objects like Markers, Polylines, and Polygons.

- **Standard Map Support:** Unlike WebGL-based solutions, `geoxml3` works on standard Google Maps JS API v3 maps without requiring a specific rendering mode.
- **Caveats:**
  - **Limited KMZ Support:** The library does **not fully support KMZ
    files** natively; unzipping KMZ archives typically requires integration with additional 3rd party scripts like `ZipFile.complete.js`.
  - **Unsupported Elements:** Features such as 3D geometries, complex labels, and certain newer KML elements are not supported.
- **Documentation:** [geoxml3 GitHub Repository](https://github.com/geocodezip/geoxml3).

## KML file with interactive elements

### Migration Recommendation: Data-driven Styling for Datasets

For developers migrating from `KmlLayer` to **Data-driven styling (DDS) for
datasets**, this guide explains how to transition from automatic KML
interactions to custom, high-performance interactions like mouse clicks and
hover.

#### Initial Setup

Before implementing interactions, ensure you have followed the setup steps from
the **KML Migration: Vector Data** guide:

- **Map ID:** Configure a Map ID for the **Vector map** type.
- **Upload:** Upload your KML data to the Google Cloud Console to obtain a **Dataset ID**.
- **Layer Access:** Use `map.getDatasetFeatureLayer(datasetId)` to access the interactive layer.

#### 1. Handling Interaction Events

In `KmlLayer`, feature clicks are handled automatically by the API to pop up an
info window. With DDS for datasets, you must manually register listeners for
mouse events on the dataset layer.

- **`click`:** Triggered when a user clicks on a feature.
- **`mousemove`:** Triggered when the cursor moves over a feature, useful for hover effects.

#### 2. Dynamic Styling (Hover Effect)

Because DDS styles are applied globally to the layer, you should maintain a
state variable to track which feature is being interacted with and
re-apply the style.
<iframe src="https://developers.devsite.google/frame/maps/comms/kml-layer-migration_5a0ea814e2a5cce7a1153bd87b7ebec55aac999c5f9783e5c8b1c6c2e033376e.frame" class="framebox inherit-locale " allow="clipboard-write https://developers.devsite.google" allowfullscreen is-upgraded></iframe>

```javascript
let currentFeatureId = null;

function initInteraction(map, datasetId) {
  const datasetLayer = map.getDatasetFeatureLayer(datasetId);

  // Apply the style function
  datasetLayer.style = (params) => {
    const isHovered = params.feature.datasetAttributes['id'] === currentFeatureId;
    return {
      strokeColor: 'green',
      strokeWeight: isHovered ? 4.0 : 2.0, // Bold border on hover
      fillColor: 'green',
      fillOpacity: isHovered ? 0.5 : 0.3,
    };
  };

  // Add interaction listeners
  datasetLayer.addListener('mousemove', (event) => {
    if (event.features.length > 0) {
      currentFeatureId = event.features[0].datasetAttributes['id'];
      datasetLayer.style = datasetLayer.style; // Re-apply style to reflect changes
    }
  });

  // Clear hover state when the mouse leaves the features
  map.addListener('mousemove', () => {
    if (currentFeatureId !== null) {
      currentFeatureId = null;
      datasetLayer.style = datasetLayer.style;
    }
  });
}
```

#### 3. Displaying HTML from the `description` Attribute

In KML, the `<description>` tag often contains HTML for the default info window.
When this data is imported as a dataset, the `description` becomes a feature
attribute. To render it, pass the string directly to a standard
`google.maps.InfoWindow`.

```javascript
const infoWindow = new google.maps.InfoWindow();

datasetLayer.addListener('click', (event) => {
  if (event.features.length > 0) {
    const feature = event.features[0];
    // Access the HTML description attribute
    const htmlContent = feature.datasetAttributes['description'];

    infoWindow.setContent(htmlContent);
    infoWindow.setPosition(event.latLng);
    infoWindow.open(map);
  }
});
```

#### 4. Custom InfoWindow with `ExtendedData`

If your KML uses `<ExtendedData>` to store custom name/value pairs, these are
mapped to `datasetAttributes`. You can iterate through these attributes to build
a custom HTML display.

```javascript
function createCustomContent(feature) {
  const attributes = feature.datasetAttributes;
  const container = document.createElement("div");
  container.style.padding = "10px";
  container.innerHTML = "<h3>Feature Details</h3><dl></dl>";

  const dl = container.querySelector("dl");

  // Iterate through all data attributes imported from KML ExtendedData
  for (const key in attributes) {
    const dt = document.createElement("dt");
    dt.style.fontWeight = "bold";
    dt.textContent = key;

    const dd = document.createElement("dd");
    dd.textContent = attributes[key];

    dl.appendChild(dt);
    dl.appendChild(dd);
  }
  return container;
}

datasetLayer.addListener('click', (event) => {
  if (event.features.length > 0) {
    const content = createCustomContent(event.features[0]);
    infoWindow.setContent(content);
    infoWindow.setPosition(event.latLng);
    infoWindow.open(map);
  }
});
```

For more advanced visualization techniques, see the
[developer documentation on how to style data features](https://developers.google.com/maps/documentation/javascript/dds-datasets/style-data-features.md.txt).

### Migration Recommendation: Client-Side Rendering with GeoJSON

For developers migrating from `KmlLayer` to client-side rendering with GeoJSON
and the **Data layer**, this guide explains how to transition from automatic KML
interactions to custom, event-driven interactions and dynamic styling.

#### Initial Setup

Before implementing interactions, you must convert your KML data to GeoJSON and
load it into the Data layer. Refer to the **Migration Recommendation:
Client-Side Rendering with GeoJSON** guide for details on using tools like
`ogr2ogr` or `togeojson` and initializing the map with `map.data.loadGeoJson()`.

#### 1. Automatic versus Manual Interactions

A key difference between these layers is how they handle user input:

- **`KmlLayer`** : Automatically handles feature clicks and displays an `InfoWindow` containing the KML and data.
- **Data Layer** : Does not display `InfoWindow` objects automatically. You must manually add event listeners to capture user interactions and write code to display data.

#### 2. Handling Interaction Events

To make GeoJSON features interactive, use the `addListener()` method on the
`map.data` object. Common events include:

- **`click`**: Used to trigger info windows or selection logic.
- **`mouseover` / `mouseout`**: Used for hover effects and highlighting.

#### 3. Displaying HTML Descriptions in an InfoWindow

When KML is converted to GeoJSON, the `<description>` tag (which often contains
HTML) is typically mapped to a property named `description`. You can use
`feature.getProperty('description')` to retrieve this string and render it
inside a standard `google.maps.InfoWindow`.

```javascript
const infoWindow = new google.maps.InfoWindow();

// Handle clicks to show the HTML description
map.data.addListener('click', (event) => {
  // Access the 'description' property from the GeoJSON feature
  const htmlContent = event.feature.getProperty('description');

  if (htmlContent) {
    infoWindow.setContent(htmlContent);
    infoWindow.setPosition(event.latLng);
    infoWindow.open(map);
  }
});
```

#### 4. Custom InfoWindows and `ExtendedData`

If your original KML utilized `<ExtendedData>`, these name-value pairs are
converted into GeoJSON properties. Because the Data layer doesn't have a default
UI for these, you must implement a custom InfoWindow to iterate through and
display them.

You can access these attributes using
`event.feature.getProperty('attribute_name')` and construct a custom HTML string
or DOM element to pass to the `infoWindow.setContent()` method.

#### 5. Dynamic Styling (Hover Effects)

The Data layer lets you update feature styles programmatically in response
to events. Use `overrideStyle()` to temporarily change a feature's appearance
(e.g., on hover) and `revertStyle()` to return to the global style.

```javascript
// Set a base style for all features
map.data.setStyle({
  fillColor: 'blue',
  strokeWeight: 1
});

// Highlight feature on mouseover
map.data.addListener('mouseover', (event) => {
  map.data.revertStyle(); // Clear previous highlights
  map.data.overrideStyle(event.feature, {strokeWeight: 8});
});

// Revert style on mouseout
map.data.addListener('mouseout', (event) => {
  map.data.revertStyle();
});
```

For more detailed implementation details, see the documentation on
[Data Layer: Event Handling](https://developers.google.com/maps/documentation/javascript/examples/layer-data-event.md.txt)
and
[Data Layer: Dynamic Styling](https://developers.google.com/maps/documentation/javascript/examples/layer-data-dynamic.md.txt).

### Migration Path: Client-Side Rendering with 3rd party libraries

For developers migrating from `KmlLayer` to third-party solutions, this guide
focuses on handling interactive data such as mouse clicks and dynamic events
using **deck.gl** and **geoxml3**.

> [!NOTE]
> **Note:** These third-party integrations are **not supported by
> Google**. However, they have been tested and should work well for the documented use cases.

#### Initial Setup

Before implementing interactions, ensure you have followed the setup steps from
the **Migration Path: Client-Side Rendering with 3rd party libraries** guide.
This includes:

- **deck.gl** : Converting your map to use the **Vector map type** (canvas requirement).
- **geoxml3**: Serving the library scripts from your own host and managing Cross-Origin Resource Sharing (CORS).

#### 1. Interactive Data with deck.gl

`deck.gl` supports KML as a direct input format and automatically handles
feature interactions like clicks based on the data provided in the KML file.

- **KMLLoader Handling** : Using the `@loaders.gl/kml` module, geometry and properties are parsed into a format that `deck.gl` uses to trigger interaction events natively.
- **Feature Clicks** : When a feature is clicked, `deck.gl` can capture the event and display associated metadata (like `<name>` or `<description>`).
- **Example** : The [deckgl-kml-updated](https://github.com/googlemaps-samples/js-api-samples/tree/main/samples/deckgl-kml-updated) sample demonstrates real-time KML rendering where hovering over earthquake markers displays detailed event information.

#### 2. Interactive Data with geoxml3

`geoxml3` parses the KML locally in the browser, extracts style information, and
generates standard Google Maps API objects that retain their interactivity.

- **Native Style Extraction** : The library grabs `<Style>` and `<StyleMap>` elements from the KML to apply them to the generated Markers, Polylines, and Polygons.
- **Click Handlers** : By default, `geoxml3` provides click handlers for these objects. You can also define custom callback functions (`createMarker`, `createOverlay`) during parser instantiation to implement your own selection logic or sidebar updates.
- **Example:** [This example](https://developers.google.com/maps/comms/kml-layer-migration.md.txt#geoxml3) demonstrates how to use geoxml3 to render KML, with customization like circle markers with interaction, such as clicking on the markers to display earthquake information.
- **Usage Pattern**:

```javascript
var myParser = new geoXML3.parser({
  map: map,
  processStyles: true, // Automatically handle KML styles
  afterParse: function(doc) {
    // Code to run after the KML is fully parsed
  }
});
myParser.parse('interactive_data.kml');
```

## KML file with imagery

For developers using `KmlLayer` to display imagery---such as maps with
satellite-derived data, weather patterns, or historical blueprints---this guide
outlines the migration paths to **GroundOverlays** or 3rd party parsers.

### Migration Recommendation: Maps JavaScript API GroundOverlay

The recommended path for migrating imagery is to use the
`google.maps.GroundOverlay` class. This lets you place an image on the map
at specific geographic coordinates directly in your code.

#### 1. Implementation

Instead of relying on a KML file to define the bounds, you specify the image URL
and a `LatLngBounds` object representing the rectangle on the map.

- **Documentation** : [Ground Overlays Guide](https://developers.google.com/maps/documentation/javascript/groundoverlays.md.txt).
- **Image Preparation** : If your image is georeferenced but not in the correct projection (EPSG:4326), you can use the open-source tool **`gdalwarp`** to warp the image for use with the Maps JS API.

```bash
gdalwarp -t_srs EPSG:4326 image.jp2 image.jpg
```

### Migration Path: Using 3rd Party Libraries

If your workflow requires you to keep your data in KML format, third-party
libraries like **`geoxml3`** or
**`deck.gl`** can be used to render imagery overlays.

**Disclaimer:** These third-party solutions are **not supported by Google**.
However, they have been tested and should work for most use cases.

#### 1. geoxml3

`geoxml3` is a good option for parsing simple `GroundOverlay` elements locally
in the browser and converting them into Google Maps API objects.

**Example Usage:**
<iframe src="https://developers.devsite.google/frame/maps/comms/kml-layer-migration_60b8c1421407c91f13772bb6f94dbd5fe02f1dab72de2831026c8052212aa99b.frame" class="framebox inherit-locale " allow="clipboard-write https://developers.devsite.google" allowfullscreen is-upgraded></iframe>

```javascript
const geoXmlParser = new geoXML3.parser({
    map: map,
    afterParse: function(doc) {
        console.log("Parsing complete. Number of documents: " + doc.length);
        const bounds = doc[0].gbounds;
        if (bounds && !bounds.isEmpty()) {
           map.fitBounds(bounds);
        }
    },
    createOverlay: function(groundOverlayData) {
        // Extract bounds and URL from parsed KML data
        const imageUrl = groundOverlayData.icon.href;
        const imageBounds = {
            north: parseFloat(groundOverlayData.latLonBox.north),
            south: parseFloat(groundOverlayData.latLonBox.south),
            east: parseFloat(groundOverlayData.latLonBox.east),
            west: parseFloat(groundOverlayData.latLonBox.west)
        };

        // Create the Google Maps GroundOverlay
        const nativeOverlay = new google.maps.GroundOverlay(imageUrl, imageBounds);
        nativeOverlay.setMap(map);
    }
});
geoXmlParser.parse('your_file.kml');
```

#### 2. deck.gl

While `deck.gl`'s standard `GeoJsonLayer` handles vector data, it can also
support `GroundOverlays` through a manual implementation using the
**`BitmapLayer`**.

This approach involves leveraging `KMLLoader` to parse the file and then
explicitly defining a `BitmapLayer` with the image URL and coordinates extracted
from the KML data.

- **Requirement:** Using `deck.gl` requires a **Vector map** type.
- **Documentation:** [deck.gl Bitmap Layer](https://deck.gl/docs/api-reference/layers/bitmap-layer)

#### Advanced Case: Tile Pyramids using gdal2tiles

For complex KML files containing imagery tile pyramids, migration is more
difficult and requires extracting the imagery data.

- **Tool** : **`gdal2tiles`** can extract data from a KMZ pyramid and produce standard Maps JavaScript API code to display the tiles. Note that the end result may require manual modification to be incorporated into an existing map.

```bash
gdal2tiles -z 10- -n -u https://yourhost.com/tiles/ -w google input.kmz
```

## KML File with Network Links

Handling KML files with network links requires a shift from the automatic,
cloud-side fetching of `KmlLayer` to more explicit data management strategies.

### Supported Solution: Data-driven Styling (DDS) for Datasets

Because Google Maps Platform datasets don't natively parse `<NetworkLink>`
elements, you must choose a migration strategy based on your data structure:

- **Strategy A: Distinct Datasets (Best for User-Controlled Layers)** Upload each KML file that was previously a network link as its own individual dataset in the Google Cloud Console. You can then use JavaScript to dynamically load and display these layers when needed by calling `map.getDatasetFeatureLayer(datasetId)` and adjusting its visibility or style.
- **Strategy B: Flattened KML File (Best for High-Performance Display)** Combine all features from your various network-linked files into a single, comprehensive KML file before uploading it as a dataset. You can then use **dynamic styling** based on feature attributes to filter and display specific data subsets on the fly.

**Updating Dynamic Data:** To mimic the "auto-refresh" behavior of network
links, use the [**Datasets API**](https://developers.google.com/maps/documentation/datasets/overview.md.txt) to programmatically upload a new version of your
dataset whenever the source data changes.

### Open-Source Solutions: deck.gl and geoxml3

Neither `deck.gl` nor `geoxml3` provide robust support for parsing and
automatically fetching KML `<NetworkLink>` elements.

#### deck.gl

`deck.gl` utilizes the `KMLLoader` (built on `togeojson`), which explicitly
**does not support NetworkLinks**.

- **Why it is not a good solution:** The parser is designed to be a synchronous, "fuss-free" converter that avoids making its own network requests to ensure reliability and simplicity. If your application relies on KML files that point to multiple other URLs, `deck.gl` won't automatically resolve them.

#### geoxml3

While `geoxml3` was developed to parse KML for the Maps JS API, its support for
network links is **experimental and unmaintained**.

- **Why it is not a good solution:** The functionality exists only in a specific "network_link" branch that is old and not well-tested. Using this for production data migration is discouraged, as it may fail to handle complex link structures or modern security requirements like CORS.

### Summary Recommendation

For a reliable migration, developers should avoid third-party parsers for files
with network links and instead **rebuild the data-fetching logic using the
Datasets API**. This ensures your data is managed securely within the Google
Maps Platform infrastructure rather than relying on unmaintained client-side
parsers.

## Use KML to display Screen Overlays

For developers migrating from `KmlLayer` to modern alternatives like Data-driven
styling (DDS), it is important to note that **Screen Overlays are not supported
in Datasets** . To achieve the same effect of displaying fixed images, logos, or
legends on top of the map, you must create
**[Custom Controls](https://developers.google.com/maps/documentation/javascript/controls.md.txt#CustomControls)**
using the Maps JavaScript API.

### 1. What to Look for in Your KML File

To build an equivalent Custom Control, examine the `<ScreenOverlay>` element in
your KML file for the following key attributes:

- **`<Icon>&lt;href>`**: The URL of the image you want to display.
- **`<screenXY>`** : This defines where the overlay is positioned on the screen.
  - `x=0, y=1` (fractions) corresponds to the **Top
    Left**.
  - `x=1, y=1` corresponds to the **Top Right**.
  - `x=0, y=0` corresponds to the **Bottom Left**.
  - `x=1, y=0` corresponds to the **Bottom Right**.
- **`<size>`**: Defines the width and height of the overlay.
- **`<rotation>`**: Indicates if the image should be rotated.

### 2. Implementation: Creating a Custom Control

A Custom Control is essentially a standard HTML element (like a `<div>` or
`<img>`) that you "push" into one of the map's predefined positions.

#### Mapping KML Positions to `ControlPosition`

The Maps JavaScript API uses the `ControlPosition` enum to anchor controls. Use
the table below to map your KML `<screenXY>` to the appropriate JS API constant:

|---|---|
| **KML Position (`screenXY`)** | **JS API `ControlPosition`** |
| Top Left (`x:0, y:1`) | `TOP_LEFT` (Legacy) or `BLOCK_START_INLINE_START` (Logical) |
| Top Right (`x:1, y:1`) | `TOP_RIGHT` or `BLOCK_START_INLINE_END` |
| Bottom Left (`x:0, y:0`) | `BOTTOM_LEFT` or `BLOCK_END_INLINE_START` |
| Bottom Right (`x:1, y:0`) | `BOTTOM_RIGHT` or `BLOCK_END_INLINE_END` |

### 3. Migration Example: Fixed Logo Overlay

The following example mimics a KML ScreenOverlay logo positioned in the **Top
Right** of the map.

#### CSS Styling

Use CSS to define the size and appearance of your "overlay".

```javascript
#logo-control {
  padding: 10px;
  background-color: rgba(255, 255, 255, 0.8);
  margin: 10px;
  border-radius: 2px;
  box-shadow: 0 1px 4px rgba(0,0,0,0.3);
}

#logo-control img {
  width: 150px; /* Equivalent to KML <size> */
  display: block;
}
```

#### JavaScript Implementation

Add the element to the `map.controls` array.

```javascript
function initMap() {
  const map = new google.maps.Map(document.getElementById("map"), {
    zoom: 12,
    center: { lat: 41.85, lng: -87.65 },
  });

  // 1. Create the container for the overlay
  const logoControlDiv = document.createElement("div");
  logoControlDiv.id = "logo-control";

  // 2. Create the image (KML <Icon>)
  const logoImage = document.createElement("img");
  logoImage.src = "https://example.com/logo.png";
  logoImage.alt = "Company Logo";

  logoControlDiv.appendChild(logoImage);

  // 3. Position the control (KML <screenXY>)
  // In this case, we use TOP_RIGHT
  map.controls[google.maps.ControlPosition.TOP_RIGHT].push(logoControlDiv);
}
```

#### More Information

- [Custom Controls Documentation](https://developers.google.com/maps/documentation/javascript/controls.md.txt#CustomControls)
- [KML ScreenOverlay Reference](https://developers.google.com/kml/documentation/kmlreference.md.txt#screenoverlay)