The KmlLayer
renders KML and GeoRSS elements into a
Maps JavaScript API tile overlay.
Overview
The Maps JavaScript API supports the KML and GeoRSS data formats
for displaying geographic information. These data formats are displayed on a
map using a KmlLayer
object, whose constructor takes the URL of a
publicly accessible KML or GeoRSS file.
Note: The KmlLayer
class that
generates KML overlays in the Maps JavaScript API uses
a Google hosted service to retrieve and parse KML files for rendering.
Consequently, it is only possible to display KML files if they are hosted at
a publicly accessible URL that does not require authentication to access.
If you require access to private files, fine-grained control over caches, or
send the browser viewport to a geospatial data server as a query parameter,
we recommend using data
layers instead of KmlLayer
. This will direct your users'
browsers to directly request resources from your web server.
The Maps JavaScript API converts the provided geographic
XML data into a KML representation which is displayed on the map using a
Maps JavaScript API tile overlay. This KML looks
(and somewhat behaves) like familiar Maps JavaScript API
overlay elements. KML <Placemark>
and GeoRSS point
elements are rendered as markers, for example, <LineString>
elements are
rendered as polylines and <Polygon>
elements are rendered
as polygons. Similarly, <GroundOverlay>
elements are
rendered as rectangular images on the map. Importantly, however, these objects
are not Maps JavaScript API Markers
,
Polylines
, Polygons
or GroundOverlays
;
instead, they are rendered into a single object on the map.
KmlLayer
objects appear on a map once their map
property has been set. You can remove them from the map by calling
setMap()
passing null
. The KmlLayer
object manages the rendering of these child elements by automatically
retrieving appropriate features for the map’s given bounds. As the
bounds change, features in the current viewport are automatically
rendered.
Because the components within a KmlLayer
are rendered on demand,
the layer allows you to easily manage the rendering of thousands of markers,
polylines, and polygons. Note that you can’t access these constituent
objects directly, though they each provide click events which return data on
those individual objects.
KML layer options
The KmlLayer()
constructor optionally passes a number of
KmlLayerOptions
:
map
specifies theMap
on which to render theKmlLayer
. You can hide aKmlLayer
by setting this value tonull
within thesetMap()
method.preserveViewport
specifies that the map should not be adjusted to the bounds of theKmlLayer
’s contents when showing the layer. By default, when displaying aKmlLayer
, the map is zoomed and positioned to show the entirety of the layer’s contents.suppressInfoWindows
indicates that clickable features within theKmlLayer
should not trigger the display ofInfoWindow
objects.
Additionally, once the KmlLayer
is rendered, it contains an
immutable metadata
property containing the layer’s name,
description, snippet and author within a KmlLayerMetadata
object
literal. You can inspect this information using the getMetadata()
method. Because rendering of KmlLayer
objects requires
asynchronous communication to an external server, you will want to listen for
the metadata_changed
event, which will indicate that the property
has been populated.
The following example constructs a KmlLayer
from the given
GeoRSS feed:
TypeScript
function initMap(): void { const map = new google.maps.Map( document.getElementById("map") as HTMLElement, { zoom: 4, center: { lat: 49.496675, lng: -102.65625 }, } ); const georssLayer = new google.maps.KmlLayer({ url: "http://api.flickr.com/services/feeds/geo/?g=322338@N20&lang=en-us&format=feed-georss", }); georssLayer.setMap(map); } declare global { interface Window { initMap: () => void; } } window.initMap = initMap;
JavaScript
function initMap() { const map = new google.maps.Map(document.getElementById("map"), { zoom: 4, center: { lat: 49.496675, lng: -102.65625 }, }); const georssLayer = new google.maps.KmlLayer({ url: "http://api.flickr.com/services/feeds/geo/?g=322338@N20&lang=en-us&format=feed-georss", }); georssLayer.setMap(map); } 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>GeoRSS Layers</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=initMap&v=weekly" defer ></script> </body> </html>
Try Sample
The following example constructs a KmlLayer
from the given KML
feed:
TypeScript
function initMap(): void { const map = new google.maps.Map( document.getElementById("map") as HTMLElement, { zoom: 11, center: { lat: 41.876, lng: -87.624 }, } ); const ctaLayer = new google.maps.KmlLayer({ url: "https://googlearchive.github.io/js-v2-samples/ggeoxml/cta.kml", map: map, }); } declare global { interface Window { initMap: () => void; } } window.initMap = initMap;
JavaScript
function initMap() { const map = new google.maps.Map(document.getElementById("map"), { zoom: 11, center: { lat: 41.876, lng: -87.624 }, }); const ctaLayer = new google.maps.KmlLayer({ url: "https://googlearchive.github.io/js-v2-samples/ggeoxml/cta.kml", map: map, }); } 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>KML Layers</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=initMap&v=weekly" defer ></script> </body> </html>
Try Sample
KML feature details
Because KML may include a large number of features, you may not access
feature data from the KmlLayer
object directly. Instead, as
features are displayed, they are rendered to look like clickable
Maps JavaScript API overlays.
Clicking on individual features, by default, brings up an
InfoWindow
containing KML <title>
and
<description>
information on the given feature.
Additionally, a click on a KML feature generates a KmlMouseEvent
,
which passes the following information:
position
indicates the latitude/longitude coordinates at which to anchor theInfoWindow
for this KML feature. This position is generally the clicked location for polygons, polylines, and GroundOverlays, but the true origin for markers.pixelOffset
indicates the offset from the aboveposition
to anchor theInfoWindow
“tail.” For polygonal objects, this offset is typically0,0
but for markers includes the height of the marker.featureData
contains a JSON structure ofKmlFeatureData
.
A sample KmlFeatureData
object is shown below:
{ author: { email: "[email protected]", name: "Mr Nobody", uri: "http://example.com" }, description: "description", id: "id", infoWindowHtml: "html", name: "name", snippet: "snippet" }
The following example displays KML feature <Description>
text within a side <div>
when the feature is clicked:
TypeScript
function initMap(): void { const map = new google.maps.Map( document.getElementById("map") as HTMLElement, { zoom: 12, center: { lat: 37.06, lng: -95.68 }, } ); const kmlLayer = new google.maps.KmlLayer({ url: "https://raw.githubusercontent.com/googlearchive/kml-samples/gh-pages/kml/Placemark/placemark.kml", suppressInfoWindows: true, map: map, }); kmlLayer.addListener("click", (kmlEvent) => { const text = kmlEvent.featureData.description; showInContentWindow(text); }); function showInContentWindow(text: string) { const sidebar = document.getElementById("sidebar") as HTMLElement; sidebar.innerHTML = text; } } declare global { interface Window { initMap: () => void; } } window.initMap = initMap;
JavaScript
function initMap() { const map = new google.maps.Map(document.getElementById("map"), { zoom: 12, center: { lat: 37.06, lng: -95.68 }, }); const kmlLayer = new google.maps.KmlLayer({ url: "https://raw.githubusercontent.com/googlearchive/kml-samples/gh-pages/kml/Placemark/placemark.kml", suppressInfoWindows: true, map: map, }); kmlLayer.addListener("click", (kmlEvent) => { const text = kmlEvent.featureData.description; showInContentWindow(text); }); function showInContentWindow(text) { const sidebar = document.getElementById("sidebar"); sidebar.innerHTML = text; } } window.initMap = initMap;
CSS
/* Optional: Makes the sample page fill the window. */ html, body { height: 100%; margin: 0; padding: 0; } #container { height: 100%; display: flex; } #sidebar { flex-basis: 15rem; flex-grow: 1; padding: 1rem; max-width: 30rem; height: 100%; box-sizing: border-box; overflow: auto; } #map { flex-basis: 0; flex-grow: 4; height: 100%; }
HTML
<html> <head> <title>KML Feature Details</title> <link rel="stylesheet" type="text/css" href="./style.css" /> <script type="module" src="./index.js"></script> </head> <body> <div id="container"> <div id="map"></div> <div id="sidebar"></div> </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>
Try Sample
Size and complexity restrictions for KML rendering
The Maps JavaScript API has limitations to the size and complexity of loaded KML files. Below is a summary of the current limits.
Note: These limits are subject to change at any time.
- Maximum fetched file size (raw KML, raw GeoRSS, or compressed KMZ)
- 3MB
- Maximum uncompressed KML file size
- 10MB
- Maximum uncompressed image file size in KMZ files
- 500KB per file
- Maximum number of network Links
- 10
- Maximum number of total document-wide features
- 1,000
- Number of KML layers
- There is a limit on the number of KML Layers that can be
displayed on a single Google Map. If you exceed this limit, none of your
layers will appear on the map, and an error will be reported in your web
browser's JavaScript console. The limit is based on a combination of the
number of
KmlLayer
classes created and the total length of all the URLs used to create those layers. Each newKmlLayer
you create will take up a portion of the limit for the layer and a further portion of the limit depending on the length of the URL where the KML file was loaded from. Consequently, the number of layers you can add will vary by application; on average, you should be able to load between 10 and 20 layers without hitting the limit. If you still hit the limit, use a URL shortener to shorten the KML URLs. Alternatively, create a single KML file consisting of NetworkLinks to the individual KML URLs.
Performance and caching considerations
Google's servers will temporarily cache KML files to reduce load on your servers. This will also improve performance for your users by serving a space-efficient representation of appropriate segments your KML file, as your users click on, pan and zoom the map.
For best performance, we recommend that you:
- Use an appropriate
<expires>
tag in KML.
KmlLayer
will not use HTTP headers when deciding how to cache KML files. - Don't generate files dynamically at request time.
Instead, generate the files before they will be needed, and serve them statically. If it takes a long time for your server to transmit the KML file, theKmlLayer
may not display. - Don't attempt to bypass caches unless you know definitively that your
file was updated.
Always bypassing caches (for example, by appending a random number or the user's clock time as a query parameter) can easily cause your servers to be overwhelmed if your site suddenly gets popular, and you are serving large KML files.
It can also cause the cache to serve stale data to users, if any user's clock is incorrect, and the<expires>
tag has not been set correctly.
Instead, publish updated static files with a new, discrete revision number, and use server-side code to dynamically update the URL passed toKmlLayer
with the current version. - Limit changes to your KML files to once per minute.
If all files total more than 1MB in size (uncompressed), limit changes to once per 5 minutes. - When using a geospatial data server, avoid using query parameters to
limit the viewport of layers.
Instead, you can limit the map viewport with thebounds_changed
event. Users will only be sent features that can be displayed automatically.
If there is a large amount of data in your geospatial data server, consider using data layers instead. - When using a geospatial data server, use multiple
KmlLayer
s for each group of features that you wish to permit users to toggle, rather than a singleKmlLayer
with different query parameters. - Use compressed KMZ files to reduce file size.
- If you are using Google Cloud Storage or another cloud storage solution, avoid using features like Signed URLs or temporary tokens to enforce access controls. These can unintentionally prevent caching.
- Reduce the precision of all points to an appropriate precision.
- Merge and simplify the geometry of similar features, such as polygons and polylines.
- Remove any unused elements or image resources.
- Remove any unsupported elements.
If you need to access private data, prevent caching, or send the browser
viewport to a geospatial data server as a query parameter, we recommend using
data layers instead of
KmlLayer
. This will direct your users' browsers to directly
request resources from your web server.
Supported KML elements
The Maps JavaScript API supports the following KML elements. The KML parser generally silently ignores XML tags it does not understand.
- Placemarks
- Icons
- Folders
- Descriptive HTML—Entity replacement via <BalloonStyle> and <text>
- KMZ (compressed KML, including attached images)
- Polylines and polygons
- Styles for polylines and polygons, including color, fill, and opacity
- Network links to import data dynamically
- Ground overlays and screen overlays
The following table gives full details of the supported KML elements.
KML element | Supported in the API? | Comment |
---|---|---|
<address> | no | |
<AddressDetails> | no | |
<Alias> | N/A | <Model> is not supported |
<altitude> | no | |
<altitudeMode> | no | |
<atom:author> | yes | |
<atom:link> | yes | |
<atom:name> | yes | |
<BalloonStyle> | partially | only <text> is supported |
<begin> | N/A | <TimeSpan> is not supported |
<bgColor> | no | |
<bottomFov> | N/A | <PhotoOverlay> is not supported |
<Camera> | no | |
<Change> | partially | only style changes are supported |
<color> | partially | includes #AABBGGRR and #BBGGRR; not supported in <IconStyle>, <ScreenOverlay>, and <GroundOverlay> |
<colorMode> | no | |
<cookie> | no | |
<coordinates> | yes | |
<Create> | no | |
<Data> | yes | |
<Delete> | no | |
<description> | yes | HTML content is allowed but is sanitized to protect from cross-browser
attacks. Entity replacements of the form $[dataName]
are not supported. |
<displayMode> | no | |
<displayName> | no | |
<Document> | partially | implicitly, children are supported; no effect as child of other Features |
<drawOrder> | no | |
<east> | yes | |
<end> | N/A | <TimeSpan> is not supported |
<expires> | yes | see Summary section for details |
<ExtendedData> | partially | untyped <Data> only, no <SimpleData> or <Schema>, and
entity replacements of the form $[dataName] are not
supported.
|
<extrude> | no | |
<fill> | yes | |
<flyToView> | no | |
<Folder> | yes | |
<geomColor> | no | deprecated |
<GeometryCollection> | no | deprecated |
<geomScale> | no | deprecated |
<gridOrigin> | N/A | <PhotoOverlay> is not supported |
<GroundOverlay> | yes | cannot be rotated |
<h> | yes | deprecated |
<heading> | yes | |
hint | yes | target=... supported |
<hotSpot> | yes | |
<href> | yes | |
<httpQuery> | no | |
<Icon> | yes | cannot be rotated |
<IconStyle> | yes | |
<ImagePyramid> | N/A | <PhotoOverlay> is not supported |
<innerBoundaryIs> | yes | implicitly from <LinearRing> order |
<ItemIcon> | N/A | <ListStyle> is not supported |
<key> | N/A | <StyleMap> is not supported |
<kml> | yes | |
<labelColor> | no | deprecated |
<LabelStyle> | no | |
<latitude> | yes | |
<LatLonAltBox> | yes | |
<LatLonBox> | yes | |
<leftFov> | N/A | <PhotoOverlay> is not supported |
<LinearRing> | yes | |
<LineString> | yes | |
<LineStyle> | yes | |
<Link> | yes | |
<linkDescription> | no | |
<linkName> | no | |
<linkSnippet> | no | |
<listItemType> | N/A | <ListStyle> is not supported |
<ListStyle> | no | |
<Location> | N/A | <Model> is not supported |
<Lod> | yes | |
<longitude> | yes | |
<LookAt> | no | |
<maxAltitude> | yes | |
<maxFadeExtent> | yes | |
<maxHeight> | N/A | <PhotoOverlay> is not supported |
<maxLodPixels> | yes | |
<maxSessionLength> | no | |
<maxWidth> | N/A | <PhotoOverlay> is not supported |
<message> | no | |
<Metadata> | no | deprecated |
<minAltitude> | yes | |
<minFadeExtent> | yes | |
<minLodPixels> | yes | |
<minRefreshPeriod> | no | <NetworkLink> |
<Model> | no | |
<MultiGeometry> | partially | rendered but displayed as separate features in left side panel |
<name> | yes | |
<near> | N/A | <PhotoOverlay> is not supported |
<NetworkLink> | yes | |
<NetworkLinkControl> | partially | <Update> and <expires> partially supported. The API ignores expiration settings in the HTTP headers but does use the expiration settings specified in KML. In the absence of expiration settings, or within the time validity interval, Google Maps may cache data fetched from the Internet for unspecified durations. A refetch of the data from the Internet can be forced by renaming the document and fetching it under a different URL, or by making sure that the document contains appropriate expiration settings. |
<north> | yes | |
<open> | yes | |
<Orientation> | N/A | <Model> is not supported |
<outerBoundaryIs> | yes | implicitly from <LinearRing> order |
<outline> | yes | |
<overlayXY> | no | |
<Pair> | N/A | <StyleMap> is not supported |
<phoneNumber> | no | |
<PhotoOverlay> | no | |
<Placemark> | yes | |
<Point> | yes | |
<Polygon> | yes | |
<PolyStyle> | yes | |
<range> | yes | |
<refreshInterval> | partially | <Link> only; not in <Icon> |
<refreshMode> | yes | HTTP headers not supported for "onExpire" mode. See notes on <Update> and <expires> above. |
<refreshVisibility> | no | |
<Region> | yes | |
<ResourceMap> | N/A | <Model> is not supported |
<rightFov> | N/A | <PhotoOverlay> is not supported |
<roll> | N/A | <Camera> and <Model> are not supported |
<rotation> | no | |
<rotationXY> | no | |
<Scale> | N/A | <Model> is not supported |
<scale> | no | |
<Schema> | no | |
<SchemaData> | no | |
<ScreenOverlay> | yes | cannot be rotated |
<screenXY> | no | |
<shape> | N/A | <PhotoOverlay> is not supported |
<SimpleData> | N/A | <SchemaData> are not supported |
<SimpleField> | N/A | <Schema> are not supported |
<size> | yes | |
<Snippet> | yes | |
<south> | yes | |
<state> | N/A | <ListStyle> is not supported |
<Style> | yes | |
<StyleMap> | no | rollover (highlight) effects are not supported |
<styleUrl> | N/A | <StyleMap> is not supported |
<targetHref> | partially | supported in <Update>, not in <Alias> |
<tessellate> | no | |
<text> | yes | replacement of $[geDirections] is not supported |
<textColor> | no | |
<tileSize> | N/A | <PhotoOverlay> is not supported |
<tilt> | no | |
<TimeSpan> | no | |
<TimeStamp> | no | |
<topFov> | N/A | <PhotoOverlay> is not supported |
<Update> | partially | only style changes, not <Create> or <Delete> |
<Url> | yes | deprecated |
<value> | yes | |
<viewBoundScale> | no | |
<viewFormat> | no | |
<viewRefreshMode> | partially | "onStop" is supported |
<viewRefreshTime> | yes | |
<ViewVolume> | N/A | <PhotoOverlay> is not supported |
<visibility> | partially | yes on <Folder> - child placemarks inherit their visibility |
<w> | yes | deprecated |
<west> | yes | |
<when> | N/A | <TimeStamp> is not supported |
<width> | yes | |
<x> | yes | deprecated |
<y> | yes | deprecated |