Google Building Outlines and Entrances API

In this blog post, I’ll introduce you to a new and potentially useful tool from Google Maps: the Building Outlines and Entrances API. An add on to the Google Maps Geocoding API, Building Outlines and Entrances lets you display a building's outline and indicate the location of its entrances on a map. Unlike traditional geocoders that merely point to the middle of a property, this new feature enables drivers know exactly where to go when making a delivery, saving time and enabling logistics companies to increase the number of deliveries they make each day.

I'll show you how to make a Building Outlines and Entrances API request, interpret the response, and build a web app that visualizes a building's outline and entrance locations on a Google Map. As always, you can find sample code for this project on Github, together with a link to a working demo.

The Building Outlines and Entrances API visualized on a Google map

What is Building Outlines and Entrances API?

The Building Outlines and Entrances API accepts an address, such as "555 West Hastings Street," and provides the building's outline along with the locations of any known entrances associated with it. In the consumer Google Maps app, building outlines are highlighted in light pink when you search for a specific address.

How the Building Outlines and Entrances API is used on the Google Maps app

More formally, the Building Outlines and Entrances API takes an address string and returns two pieces of data:

entrances[], an array of location.lat and location.lng coordinates corresponding to the geographic location of each building entrance.

buildings[], an array of building objects each with its own building_outlines[] associated with the building. This array has only one entry, a display_polygon object containing a coordinates[] array which holds the GeoJSON encoding of the polygon that approximates the building footprint.

⚠️
Building Outlines and Entrances is currently in Preview (pre-GA). This means that while the API is functional, it may not yet meet all the requirements for production use and has limited support.

Building Outlines and Entrances API pricing

Building Outlines and Entrances comes free with the Geocoding API, which is billed at $5.00 USD per 1,000 requests (official pricing page for the Geocoding API). There is no extra charge for using it when you make a Geocoding API call. Reach out to a Google Maps Partner for volume pricing and discounts.

Building Outlines and Entrances API use case

A common (maybe the only?) use case for Building Outlines and Entrances is in logistics, where delivery companies overlay the building outline and entrances on a map interface. This helps drivers quickly locate the correct building and entrance for their deliveries.

The Building Outlines and Entrances API used in a driver app

Although it doesn't sound like much, this feature could be particularly helpful in cities like New York and Boston where many townhouses and apartment blocks are indistinguishable from one another, or in areas that the driver is unfamiliar with.

Where does data from the Building Outlines and Entrances API come from?

Google Maps gathers data for its APIs from sources like satellite imagery, aerial photos, Street View, and machine learning models. However, based on my research, the Building Outlines and Entrances API appears to rely primarily on urban planning data and proprietary datasets from private mapping and geospatial companies, instead of automated data collection. This is both a strength and a weakness. It’s a strength because the data quality is likely high, given that Google has invested significantly in obtaining it. However, it’s a weakness because it may not reflect the actual entrances commonly used by delivery drivers.

Building Outlines and Entrances example

Making a call to the Building Outlines and Entrances API is just like making a call to the Google Maps Geocoding API, but with an extra parameter extra_computations=BUILDING_AND_ENTRANCES added on to the request.

Endpoint GET https://maps.googleapis.com/maps/api/geocode/json?address={address_string}&extra_computations=BUILDING_AND_ENTRANCES&key={google_maps_api_key}

{address_string} is the address you are trying to geocode. Try to include as much information as you can including the country, city, and postal code.

extra_computations is a new parameter that lets you ask for additional data in the response. BUILDING_AND_ENTRANCES gives you entrances and building outlines while ADDRESS_DESCRIPTORS helps describe a location using landmarks and areas.

{google_maps_api_key} is a is a valid Google Maps API key with the Geocoding API enabled.

Method: GET

https://maps.googleapis.com/maps/api/geocode/json?address=555+west+hastings+street+vancouver&extra_computations=BUILDING_AND_ENTRANCES&key={GOOGLE_MAPS_API_KEY}

Response

{
    "results": [
        {
            "address_components": [
                {
                    "long_name": "555",
                    "short_name": "555",
                    "types": [
                        "street_number"
                    ]
                },
                {
                    "long_name": "West Hastings Street",
                    "short_name": "W Hastings St",
                    "types": [
                        "route"
                    ]
                },
                {
                    "long_name": "Central Vancouver",
                    "short_name": "Central Vancouver",
                    "types": [
                        "neighborhood",
                        "political"
                    ]
                },
                {
                    "long_name": "Vancouver",
                    "short_name": "Vancouver",
                    "types": [
                        "locality",
                        "political"
                    ]
                },
                {
                    "long_name": "Metro Vancouver",
                    "short_name": "Metro Vancouver",
                    "types": [
                        "administrative_area_level_2",
                        "political"
                    ]
                },
                {
                    "long_name": "British Columbia",
                    "short_name": "BC",
                    "types": [
                        "administrative_area_level_1",
                        "political"
                    ]
                },
                {
                    "long_name": "Canada",
                    "short_name": "CA",
                    "types": [
                        "country",
                        "political"
                    ]
                },
                {
                    "long_name": "V6B 4N4",
                    "short_name": "V6B 4N4",
                    "types": [
                        "postal_code"
                    ]
                }
            ],
            "buildings": [
                {
                    "building_outlines": [
                        {
                            "display_polygon": {
                                "coordinates": [
                                    [
                                        [
                                            -123.112454778407,
                                            49.284592790671
                                        ],
                                        [
                                            -123.112374159217,
                                            49.284541897714
                                        ],
                                        // 28 more entries
                                    ]
                                ],
                                "type": "Polygon"
                            }
                        }
                    ],
                    "place_id": "ChIJUcKFZ3hxhlQREJGVU1foPaE"
                }
            ],
            "entrances": [
                {
                    "building_place_id": "ChIJUcKFZ3hxhlQREJGVU1foPaE",
                    "location": {
                        "lat": 49.284524,
                        "lng": -123.1124022
                    }
                }
            ],
            "formatted_address": "555 W Hastings St, Vancouver, BC V6B 4N4, Canada",
            "geometry": {
                "bounds": {
                    "northeast": {
                        "lat": 49.2851751,
                        "lng": -123.111479
                    },
                    "southwest": {
                        "lat": 49.28441489999999,
                        "lng": -123.1126507
                    }
                },
                "location": {
                    "lat": 49.2846966,
                    "lng": -123.1119349
                },
                "location_type": "ROOFTOP",
                "viewport": {
                    "northeast": {
                        "lat": 49.28614398029149,
                        "lng": -123.1107158697085
                    },
                    "southwest": {
                        "lat": 49.28344601970849,
                        "lng": -123.1134138302915
                    }
                }
            },
            "navigation_points": [
                {
                    "location": {
                        "latitude": 49.2845048,
                        "longitude": -123.1124829
                    }
                }
            ],
            "place_id": "ChIJUcKFZ3hxhlQREJGVU1foPaE",
            "types": [
                "premise"
            ]
        }
    ],
    "status": "OK"
}

How to draw a polygon in Google Maps

Before using the coordinates from the display_polygon to draw the building outline for 555 West Hastings Street Vancouver BC, I'll first demonstrate how to draw a basic polygon on a Google Map using the Maps Javascript API.

A polygon drawn on a map using the Google Maps Javascript API
  1. Import the Google Maps Javascript API
<script
  src="https://maps.googleapis.com/maps/api/js?key={GOOGLE_MAPS_API_KEY}&callback=initMap&v=weekly"
  defer
></script>;
  1. Create an array of lat and lng coordinate pairs representing the vertices of the shape you want to display. In the example below, I'm drawing a square over downtown Vancouver. Ensure that the coordinate pairs follow the GeoJSON convention: they should be arranged in a counter-clockwise order, and the first and last coordinates must be identical to "close" the polygon.
const squareCoords = [
          { lat: 49.27468369696294, lng: -123.13060312624661 },
          { lat: 49.2836788528944, lng: -123.1306268809628 },
          { lat: 49.28369357865754, lng: -123.1168767147865 },
          { lat: 49.27469841808173, lng: -123.11685546054629 },
          { lat: 49.27468369696294, lng: -123.13060312624661 },
        ];
  1. Create a new polygon object using the Google Maps Javascript API. The code below creates a red polygon with a partially transparent fill and a solid outline.
const squareOverVancouver = new google.maps.Polygon({
          paths: squareCoords,
          strokeColor: "#FF0000",
          strokeOpacity: 0.8,
          strokeWeight: 2,
          fillColor: "#FF0000",
          fillOpacity: 0.35,
        });
  1. Instantiate a Google Map object and add your polygon to the map.
squareOverVancouver.setMap(map);
const map = new google.maps.Map(document.getElementById("map"), {
          zoom: 13,
          center: { lat: 49.2772413, lng: -123.1399321 }
        });
  1. Make sure to include a <div/> element with an id of "map" in your HTML file.
<body>
    <div id="map"></div>
</body>

Full source code for the example above is provided below in google_maps_simple_polygon.html. To run the code, open the file in your favorite text editor and replace {YOUR_API_KEY} with a valid Google Maps API key. Open google_maps_simple_polygon.html in your browser and the app should work out of the box.

In the next section, I'll take this exercise one step further and show you how to use the @vis.gl/react-google-maps component library to draw the building outline provided by the Building Outlines and Entrances API.

Coding up a Building Outlines and Entrances viewer tool

The building_outlines_and_entrances_demo is a React app created with the Create React App build tool. It features four main components:

  1. A search bar in the top-left corner powered by Google Places Autocomplete, allowing you to quickly find any address in the Google Maps database.
  2. A blue dot marking the geocoded location of the selected address.
  3. A blue polygon representing the building outline.
  4. A green marker indicating the location of entrances, if available.
How our Building Outlines and Entrances viewer tool looks like

As usual, we'll be using the very excellent Google Maps React component library from @vis.gl/react-google-maps to quickly scaffold our app. Specifically, we'll be using the <Polygon/> component from the Geometry library to draw building outlines and <AdvancedMarker/> to display entrance locations.

Clone the building_outlines_and_entrances_demo repository from GitHub and run the code locally to follow along with this tutorial. If you're not interested in understanding how the code works but still want to see the Building Outlines and Entrances API in action, you can use the Building Outlines and Entrances viewer tool at https://building-outlines-and-entrances-demo.afi.dev/ instead.

How our code is structured

Our app consists of two main parts. The /frontend is responsible for displaying the map, markers, and building outlines. It also handles calls to the Google Places Autocomplete API to retrieve the place_id of the selected address. The /backend uses this place_id to call the Geocoding API and retrieve the corresponding building outline and entrance locations.

The system architecture of the Building Outlines and Entrances API viewer tool

App.jsx

App.jsx is the main entry point into the app. It handles the overall structure, routing, and layout.

/* frontend/components/PlaceAutocompleteInput.jsx */
import React, { useState } from "react";
import { APIProvider } from "@vis.gl/react-google-maps";
import Map from "./components/Map";
import LocationSearchPanel from "./components/LocationSearchPanel";
import axios from "axios";
import "./App.scss";

const App = () => {
  const [selectedPlace, setSelectedPlace] = useState(null);

  const handlePlaceSelection = async (placeResult) => {
    try {
      const placeId = placeResult.place_id;
      // const placeId = "ChIJ4TTDdzS3j4AR78EQgu5EADA";
      const response = await axios.get(
        `${process.env.REACT_APP_BACKEND_URI}/place-details`,
        {
          params: { placeId },
        },
      );

      const details = response.data;

      if (details) {
        setSelectedPlace({
          name: details.name,
          rating: details.rating,
          reviewsCount: details.reviewsCount,
          categories: details.category,
          imageUrl: details.imageUrl,
          address: placeResult.formatted_address,
          latitude: placeResult.geometry?.location?.lat(),
          longitude: placeResult.geometry?.location?.lng(),
          outlines: details.buildingData?.outlines,
          entrances: details.buildingData?.entrances,
        });
      }
    } catch (error) {
      console.error("Error fetching place details:", error);
    }
  };

  return (
    <div className="app">
      <APIProvider apiKey={process.env.REACT_APP_GOOGLE_API_KEY}>
        <div className="location-panel">
          <LocationSearchPanel onPlaceSelect={handlePlaceSelection} />
        </div>
        <Map place={selectedPlace} />
      </APIProvider>
    </div>
  );
};

export default App;

When a user selects an address from the address autocomplete widget in <PlaceAutocompleteInput/>, the placeResult object (shown below) is passed to the handlePlaceSelection() method in App.jsx.

/* placeResult */
{
    "formatted_address": "555 W Hastings St, Vancouver, BC V6B 4N4, Canada",
    "geometry": {
        "location": {
            "lat": 49.2846966,
            "lng": -123.1119349
        },
        "viewport": {
            "south": 49.2832275197085,
            "west": -123.1135952802915,
            "north": 49.2859254802915,
            "east": -123.1108973197085
        }
    },
    "name": "555 W Hastings St",
    "place_id": "ChIJUcKFZ3hxhlQREJGVU1foPaE",
    "html_attributions": []
}

The place_id is extracted from placeResult and sent to the backend, where it is used to request additional data from the Google Maps Places and Geocoding APIs.

👨‍💻
For an in depth look at Google Places Autocomplete, see our tutorial on the Google address autocomplete with the Places API.

placeController.js

The place_id ends up in placeController.js where it is used to make three follow on API calls.

  1. Place Details API: GET https://maps.googleapis.com/maps/api/place/details/json?place_id=${placeId}&key=${apiKey}
  2. Place Photos API: GET https://maps.googleapis.com/maps/api/place/photo?maxwidth=400&photoreference=${photoReference}&key=${apiKey}
  3. Geocoding API: GET https://maps.googleapis.com/maps/api/geocode/json?place_id=${placeId}&extra_computations=BUILDING_AND_ENTRANCES&key=${apiKey}

In a previous blog post, I discussed in detail how the Place Details and Place Photos APIs work. In this one, I’ll focus on explaining how the Geocoding API is used to retrieve building outlines and entrances.

/* backend/controllers/placeController.js */
const getPlaceOutline = async (placeId, apiKey) => {
  const outlineUrl = `https://maps.googleapis.com/maps/api/geocode/json?place_id=${placeId}&extra_computations=BUILDING_AND_ENTRANCES&key=${apiKey}`;
  const outlineResponse = await axios.get(outlineUrl);
  const { buildings, entrances } = outlineResponse.data.results[0];

  if (!buildings) return { outlines: [], entrances: [] };

  // Extract all outlines for each building with a unique ID
  const buildingOutlines = buildings.flatMap((building, buildingIndex) =>
    building.building_outlines.map((outline, outlineIndex) => {
      const coordinates = outline.display_polygon.coordinates;
      let outlineCoordinates;

      // MultiPolygon
      if (coordinates.length > 1) {
        outlineCoordinates = coordinates.flatMap((multiPolygon) =>
          multiPolygon[0].map((polygon) => ({
            lat: polygon[1],
            lng: polygon[0],
          })),
        );
      } else {
        // Polygon
        outlineCoordinates = coordinates[0].map((polygon) => ({
          lat: polygon[1],
          lng: polygon[0],
        }));
      }

      return {
        id: `building_${buildingIndex + 1}_outline_${outlineIndex + 1}`, // Unique ID for each building outline
        outline: outlineCoordinates,
      };
    }),
  );

  // Extract entrance coordinates with unique IDs if available
  const entranceCoordinates = entrances
    ? entrances.map((entrance, index) => ({
        id: `entrance_${index + 1}`, // Unique ID for each entrance
        lat: entrance.location.lat,
        lng: entrance.location.lng,
      }))
    : [];

  return { outlines: buildingOutlines, entrances: entranceCoordinates };
};

Let's run through the code one line at a time. First, we use the place_id (instead of an address) to make an API call to the Geocoding API and include the extra_computations=BUILDING_AND_ENTRANCES parameter.

const outlineUrl = `https://maps.googleapis.com/maps/api/geocode/json?place_id=${placeId}&extra_computations=BUILDING_AND_ENTRANCES&key=${apiKey}`;

Next, we use the Axios HTTP request library to make the actual call to the Geocoding API. The keyword await indicates that this is an asynchronous operation and pauses execution until the request completes.

const outlineResponse = await axios.get(outlineUrl);

Finally, we use Javascript object destructuring to extract the buildings and entrances properties from the response.

const { buildings, entrances } = outlineResponse.data.results[0];

Recall from the earlier section that both buildings and entrances are nested arrays containing the building outline in GeoJSON format and the entrance coordinates, respectively. This next bit of code unrolls the coordinates array in display_polygon and saves them to the { lat, lng } tuple array in buildingOutlines so that we can draw them on a Google Map.

/* backend/controllers/placeController.js */
const buildingOutlines = buildings.flatMap((building, buildingIndex) =>
  building.building_outlines.map((outline, outlineIndex) => {
    const coordinates = outline.display_polygon.coordinates;
    let outlineCoordinates;

    // MultiPolygon
    if (coordinates.length > 1) {
      outlineCoordinates = coordinates.flatMap((multiPolygon) =>
        multiPolygon[0].map((polygon) => ({
          lat: polygon[1],
          lng: polygon[0],
        })),
      );
    } else {
      // Polygon
      outlineCoordinates = coordinates[0].map((polygon) => ({
        lat: polygon[1],
        lng: polygon[0],
      }));
    }

    return {
      id: `building_${buildingIndex + 1}_outline_${outlineIndex + 1}`, // Unique ID for each building outline
      outline: outlineCoordinates,
    };
  }),
);

It's important to remember that in a GeoJSON object, the order of coordinates is always longitude first, latitude second. Therefore, when saving the data to lat and lng, you need to map the values correctly, such as lat: polygon[1] and lng: polygon[0].

Handling entrances is much simpler: we just need to check if the entrances object exists. If it does, we save the values to entranceCoordinates, an array of { id, lat, lng } tuples.

/* backend/controllers/placeController.js */
const entranceCoordinates = entrances
  ? entrances.map((entrance, index) => ({
      id: `entrance_${index + 1}`, // Unique ID for each entrance
      lat: entrance.location.lat,
      lng: entrance.location.lng,
    }))
  : [];

Finally, we combine the outlines and entrances data with the response payload and send it back to the frontend to be displayed on a Google Map.

return { outlines: buildingOutlines, entrances: entranceCoordinates };

Map.jsx

<Map /> (or <GMap /> in some parts of the code) is a React component imported from the @vis.gl/react-google-maps library. It receives data from the backend via the place prop. When the useEffect hook is triggered, the place data is used to center the map on the latitude and longitude of the selected address.

/* frontend/components/Map.jsx */
import React, { useEffect } from "react";
import { Map as GMap, useMap } from "@vis.gl/react-google-maps";
import PlaceMarker from "./PlaceMarker";
import EntranceMarker from "./EntranceMarker";
import Polygon from "./Polygon";

const Map = ({ place }) => {
  const mapInstance = useMap();

  useEffect(() => {
    if (place && mapInstance) {
      const { latitude, longitude } = place;
      mapInstance.panTo({ lat: latitude, lng: longitude });
      mapInstance.setZoom(20);

      if (place.outlines) {
        const bounds = new window.google.maps.LatLngBounds();
        let hasBounds = false;

        place.outlines.forEach((polygon) => {
          polygon.outline.forEach(({ lat, lng }) => {
            if (!!lat && !!lng) {
              hasBounds = true;
              bounds.extend(new window.google.maps.LatLng(lat, lng));
            }
          });
        });
        if (hasBounds) {
          mapInstance.fitBounds(bounds);
        }
      }
    }
  }, [place, mapInstance]);

  return (
    <GMap
      mapId={process.env.REACT_APP_GOOGLE_MAP_ID}
      defaultCenter={{ lat: 49.2569501547411, lng: -123.11058970045666 }}
      defaultZoom={12}
      disableDefaultUI
    >
      {place?.outlines &&
        place.outlines.map((polygon) => (
          <Polygon key={polygon.id} polygon={polygon.outline} />
        ))}
      {place?.entrances &&
        place.entrances.map((entrance) => (
          <EntranceMarker key={entrance.id} entrance={entrance} />
        ))}
      {place && <PlaceMarker place={place} />}
    </GMap>
  );
};

export default Map;

In the <Map/> component's return statement, we iterate over place.outlines and render a <Polygon/> component for each outline to display the building's shape.

Polygon.jsx

The <Polygon/> component should look familiar to you - it's essentially the same google.maps.Polygon() object but rewritten as a React component. To make this work, we took the original polygon.tsx file file from the react-google-maps Github repository and converted it to JSX, as our project does not use TypeScript.

/* frontend/components/Polygon.jsx */
import { Polygon as GPolygon } from "./External/Polygon";

function Polygon({ polygon }) {
  return (
    <GPolygon
      paths={polygon}
      options={{
        fillColor: "#0094FF",
        strokeColor: "#0094FF",
        fillOpacity: 0.4,
        strokeOpacity: 1,
      }}
    />
  );
}

export default Polygon;

By passing the polygon.outline array as the polygon prop to the <Polygon/> component, we can use it as the paths prop in the <GPolygon/> component to render the shape of the building outline.

Using the GeoJSON returned by the Building Outlines and Entrances API to style a map

The options object applies styling and sets the fill color (blue), stroke color (also blue), and opacity (0.4) to customize the appearance of the polygon.

The next feature we need to include are the entrance locations. Just like with the building outlines, we loop over place.entrances and add an <EntranceMarker/> for each location in Map.jsx .

EntranceMarker.jsx

<EntranceMarker/> is a wrapper for the @vis.gl/react-google-maps <AdvancedMarker/>, a React component that lets you add a fully customized map pin to a Google Map.

import React from "react";
import { AdvancedMarker as GMarker } from "@vis.gl/react-google-maps";
import { ReactComponent as GateSVG } from "../images/stop-gate.svg";

const EntranceMarker = ({ entrance }) => {
  return (
    <>
      <GMarker
        position={{
          lat: entrance.lat,
          lng: entrance.lng,
        }}
      >
        <GateSVG style={{ transform: "translateY(4px)" }} />
      </GMarker>
    </>
  );
};

export default EntranceMarker;

By default, the marker appears as the standard Google Maps red balloon pin we all know and love. To customize it, we can create a new marker using a Scalable Vector Graphics (SVG) file by directly importing the SVG with the following line: import { ReactComponent as GateSVG } from "../images/stop-gate.svg";.

SVG files imported as a React component and used as map markers

When you import an SVG this way, GateSVG becomes a React component that renders the contents of the SVG file (green entrance marker, above), which we can use just like any other React component by inserting the <GateSVG/> tag. The <StopSVG/> component (the blue circle icon used to indicate the geocoded address) works similarly.

To run building_outlines_and_entrances_demo locally on your machine, simply clone the Github repository, run npm install in both the /backend and /frontend folders to install dependencies and update the respective .env files. Start the app by running npm start in each folder, then open http://localhost:3000 in your browser to start using it.

Google Maps Building Outlines and Entrances Demo
How to use the Google Maps Geocoding API to display building outlines and entrances

So how good is the Building Outlines and Entrances API?

I think the Building Outlines and Entrances API is fantastic! It's user-friendly, easy to understand, and offering it as a free add-on to the Geocoding API provides excellent value. However, while the API has the potential to be immensely useful, it's not yet ready for primetime. Here's why.

Google Maps (left) vs a competing geocoding service (right)

Take a look at the screenshots above which show building information for "75 9th Ave, New York, NY 10011, United States", Google's New York City office. The one on the left is taken from our online demo of the Building Outlines and Entrances API and the one on the right is from a competitor.

Google's data, though complete, is not particularly useful for delivery drivers. The Building Outlines and Entrances API provides a clear building outline and marks all entrances, but as a delivery driver, my priorities are different: finding a safe parking spot (ideally without getting a ticket), locating the correct entrance, and completing the delivery as quickly as possible. The competing API shows only one entrance on 9th Avenue (the designated entrance for delivery drivers) and a suitable parking spot. That's all you need, really.

Street View showing a potential delivery van parking spot outside the Google NYC office

If you zoom in on Street View, you’ll notice that the suggested "parking spot" is actually a protected right-turn lane. Having a delivery van block a turning lane feels absolutely on brand for New York City. But how did the competing API obtain this information? The answer is straightforward: from actual delivery drivers. Logistics companies embed tracking code in driver apps in exchange for a small fee. These apps collect data on where drivers park, enter, and exit, which is then sent to a server for analysis. Other logistics companies can access this data either by purchasing it or agreeing to share their own.

And therein lies the rub - as an upstanding multi-billion dollar public company, it's difficult for Google to provide potentially illegal information as part of a public facing API. But if they don't provide useful parking information together with delivery entrance locations, the Building Outlines and Entrances API will remain little more than an interesting novelty.

👋 As always, if you have any questions or suggestions for me, please reach out or say hello on LinkedIn.