npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@googlemaps/three

v4.0.13

Published

[![npm](https://img.shields.io/npm/v/@googlemaps/three)](https://www.npmjs.com/package/@googlemaps/three) ![Build](https://github.com/googlemaps/js-three/workflows/Test/badge.svg) ![Release](https://github.com/googlemaps/js-three/workflows/Release/badge.s

Downloads

2,503

Readme

Google Maps ThreeJS Overlay View and Utilities

npm Build Release codecov GitHub contributors semantic-release Discord

Description

Add three.js objects to Google Maps Platform JS. The library provides a ThreeJSOverlayView class extending google.maps.WebGLOverlayView and utility functions for converting geo-coordinates (latitude/longitude) to vectors in the coordinate system used by three.js.

Install

Available via npm as the package @googlemaps/three.

npm i @googlemaps/three

Alternatively you can load the package directly to the html document using unpkg or other CDNs. In this case, make sure to load three.js before loading this library:

<script src="https://unpkg.com/three/build/three.min.js"></script>
<script src="https://unpkg.com/@googlemaps/three/dist/index.min.js"></script>

When adding via unpkg, the package can be accessed as google.maps.plugins.three. A version can be specified by using https://unpkg.com/@googlemaps/three@VERSION/dist/....

The third option to use it is via ES-Module imports, similar to how the three.js examples work. For this, you first need to specify an importmap (example using unpkg.com, but it works the same way with any other CDN or self-hosted files):

<script type="importmap">
  {
    "imports": {
      "three": "https://unpkg.com/three/build/three.module.js",
      "@googlemaps/three": "https://unpkg.com/@googlemaps/three/dist/index.esm.js"
    }
  }
</script>

In order to support browsers that don't yet implement importmap, you can use the es-module-shims package.

After that, you can use three.js and the ThreeJSOverlayView like you would when using a bundler.

<script type="module">
  import * as THREE from "three";
  import { ThreeJSOverlayView } from "@googlemaps/three";

  // ...
</script>

Documentation

Checkout the reference documentation.

Coordinates, Projection and Anchor-Points

The coordinate system within the three.js scene (so-called 'world coordinates') is a right-handed coordinate system in z-up orientation. The y-axis is pointing true north, and the x-axis is pointing east. The units are meters. So the point new Vector3(0, 50, 10) is 10 meters above ground and 50 meters east of the specified anchor point.

This anchor-point and orientation can be set in the constructor, or by using the setAnchor() and setUpAxis()-methods (be aware that all object-positions in your scene depend on the anchor-point and orientation, so they have to be recomputed when either of them is changed):

import { ThreeJSOverlayView } from "@googlemaps/three";

const overlay = new ThreeJSOverlayView({
  anchor: { lat: 37.7793, lng: -122.4192, altitude: 0 },
  upAxis: "Y",
});

overlay.setAnchor({ lat: 35.680432, lng: 139.769013, altitude: 0 });
overlay.setUpAxis("Z");
// can also be specified as Vector3:
overlay.setUpAxis(new Vector3(0, 0, 1));

The default up-axis used in this library is the z-axis (+x is east and +y is north), which is different from the y-up orientation normally used in three.

All computations on the GPU related to the position use float32 numbers, which limits the possible precision to about 7 decimal digits. Because of this, we cannot use a global reference system and still have the precision to show details in the meters to centimeters range.

This is where the anchor point is important. The anchor specifies the geo-coordinates (lat/lng/altitude) where the origin of the world-space coordinate system is, and you should always define it close to where the objects are placed in the scene - unless of course you are only working with large-scale (city-sized) objects distributed globally.

Another reason why setting the anchor close to the objects in the scene is generally a good idea: In the mercator map-projection used in Google Maps, the scaling of meters is only accurate in regions close to the equator. This can be compensated for by applying a scale factor that depends on the latitude of the anchor. This scale factor is factored into the coordinate calculations in WebGlOverlayView based on the latitude of the anchor.

Converting coordinates

When you need more than just a single georeferenced object in your scene, you need to compute the world-space position for those coordinates. The ThreeJSOverlayView class provides a helper function for this conversion that takes the current anchor and upAxis into account:

const coordinates = { lat: 12.34, lng: 56.78 };
const position: Vector3 = overlay.latLngAltitudeToVector3(coordinates);

// alternative: pass the Vector3 to write the position
// to as the second parameter, so to set the position of a mesh:
overlay.latLngAltitudeToVector3(coordinates, mesh.position);

Raycasting and Interactions

If you want to add interactivity to any three.js content, you typically have to implement raycasting. We took care of that for you, and the ThreeJSOverlayView provides a method overlay.raycast() for this. To make use of it, you first have to keep track of mouse movements on the map:

import { Vector2 } from "three";

// ...

const mapDiv = map.getDiv();
const mousePosition = new Vector2();

map.addListener("mousemove", (ev) => {
  const { domEvent } = ev;
  const { left, top, width, height } = mapDiv.getBoundingClientRect();

  const x = domEvent.clientX - left;
  const y = domEvent.clientY - top;

  mousePosition.x = 2 * (x / width) - 1;
  mousePosition.y = 1 - 2 * (y / height);

  // since the actual raycasting is performed when the next frame is
  // rendered, we have to make sure that it will be called for the next frame.
  overlay.requestRedraw();
});

With the mouse position being always up to date, you can then use the raycast() function in the onBeforeDraw callback. In this example, we change the color of the object under the cursor:

const DEFAULT_COLOR = 0xffffff;
const HIGHLIGHT_COLOR = 0xff0000;

let highlightedObject = null;

overlay.onBeforeDraw = () => {
  const intersections = overlay.raycast(mousePosition);
  if (highlightedObject) {
    highlightedObject.material.color.setHex(DEFAULT_COLOR);
  }

  if (intersections.length === 0) return;

  highlightedObject = intersections[0].object;
  highlightedObject.material.color.setHex(HIGHLIGHT_COLOR);
};

The full examples can be found in ./examples/raycasting.ts.

Example

The following example provides a skeleton for adding objects to the map with this library.

import * as THREE from "three";
import { ThreeJSOverlayView, latLngToVector3 } from "@googlemaps/three";

// when loading via UMD, remove the imports and use this instead:
// const { ThreeJSOverlayView, latLngToVector3 } = google.maps.plugins.three;

const map = new google.maps.Map(document.getElementById("map"), mapOptions);
const overlay = new ThreeJSOverlayView({
  map,
  upAxis: "Y",
  anchor: mapOptions.center,
});

// create a box mesh
const box = new THREE.Mesh(
  new THREE.BoxGeometry(10, 50, 10),
  new THREE.MeshMatcapMaterial()
);
// move the box up so the origin of the box is at the bottom
box.geometry.translateY(25);

// set position at center of map
box.position.copy(overlay.latLngAltitudeToVector3(mapOptions.center));

// add box mesh to the scene
overlay.scene.add(box);

// rotate the box using requestAnimationFrame
const animate = () => {
  box.rotateY(THREE.MathUtils.degToRad(0.1));

  requestAnimationFrame(animate);
};

// start animation loop
requestAnimationFrame(animate);

This adds a box to the map.

Demos

View the package in action: