diff --git a/.gitignore b/.gitignore index 9bc3d3d6..6463aab5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /node_modules /dist +/.env* /examples/**/package-lock.json /examples/**/node_modules /examples/**/dist diff --git a/examples/map-3d-routes-widget/README.md b/examples/map-3d-routes-widget/README.md new file mode 100644 index 00000000..94ad068a --- /dev/null +++ b/examples/map-3d-routes-widget/README.md @@ -0,0 +1,38 @@ +# 3D Map Route Example + +This is a standalone example demonstrating how to render a client-side 3D route using the modern `` custom element inside a client-side 3D Map (``). + +It showcases the seamless integration of the Maps JS API routes and maps3d libraries in React. + +## Google Maps Platform API Key & Requirements + +To run this example locally, you must satisfy the following platform requirements: + +1. **Billing Enabled:** The Routes API and 3D Maps are premium Google Maps features and require a Google Cloud project with an **active billing account** linked to it. +2. **Enabled APIs & Alpha Channel:** Ensure that both the **Routes API** and the **Maps JavaScript API** are explicitly enabled in your Google Cloud Console. **Note:** The `` custom element is currently only available in the **Alpha channel** (e.g., by setting `version="alpha"` on your ``). +3. **API Key Environment Variable:** The API key has to be provided via an environment variable `GOOGLE_MAPS_API_KEY`. This can be done by creating a file named `.env` in the example directory with the following content: + +```shell title=".env" +GOOGLE_MAPS_API_KEY="" +``` + +## WebGL2 Browser Compatibility + +Photorealistic 3D Maps require WebGL2 support and hardware graphics acceleration. Please refer to the official **[Google Maps 3D Maps Browser Support Guide](https://developers.google.com/maps/documentation/javascript/3d-maps-support)** for detailed browser requirements and system compatibility guidelines. + +_(Note: Virtual machine or remote desktop environments like Cloudtop do not support direct WebGL2 hardware rendering overlays by default. Please run the example locally on your physical host machine)._ + +## Development & How to Run + +Go into the example directory: + +```shell +cd examples/map-3d-route +``` + +Install dependencies and start the development server: + +```shell +npm install +npm run start-local +``` diff --git a/examples/map-3d-routes-widget/index.html b/examples/map-3d-routes-widget/index.html new file mode 100644 index 00000000..d868ce78 --- /dev/null +++ b/examples/map-3d-routes-widget/index.html @@ -0,0 +1,32 @@ + + + + + + Example: 3D Map Route + + + + +
+ + + diff --git a/examples/map-3d-routes-widget/package.json b/examples/map-3d-routes-widget/package.json new file mode 100644 index 00000000..574fd9e8 --- /dev/null +++ b/examples/map-3d-routes-widget/package.json @@ -0,0 +1,18 @@ +{ + "type": "module", + "dependencies": { + "@googlemaps/js-api-loader": "^2.0.2", + "@vis.gl/react-google-maps": "latest", + "fast-deep-equal": "^3.1.3", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "typescript": "^6.0.3", + "vite": "^5.4.11" + }, + "scripts": { + "start": "vite", + "start-local": "vite --config ../vite.config.local.js", + "build-local": "vite build --config ../vite.config.local.js", + "build": "vite build" + } +} diff --git a/examples/map-3d-routes-widget/src/app.css b/examples/map-3d-routes-widget/src/app.css new file mode 100644 index 00000000..b435bbfe --- /dev/null +++ b/examples/map-3d-routes-widget/src/app.css @@ -0,0 +1,10 @@ +/* Styles for 3D Map Route Example */ +html, +body, +#app { + width: 100%; + height: 100%; + margin: 0; + padding: 0; + overflow: hidden; +} diff --git a/examples/map-3d-routes-widget/src/app.tsx b/examples/map-3d-routes-widget/src/app.tsx new file mode 100644 index 00000000..8e64a36f --- /dev/null +++ b/examples/map-3d-routes-widget/src/app.tsx @@ -0,0 +1,74 @@ +import React from 'react'; +import {createRoot} from 'react-dom/client'; + +import {APIProvider, Map3D, useMapsLibrary} from '@vis.gl/react-google-maps'; +import ControlPanel from './control-panel'; + +import './app.css'; + +const API_KEY = + globalThis.GOOGLE_MAPS_API_KEY ?? (process.env.GOOGLE_MAPS_API_KEY as string); + +// Tell TypeScript to allow the custom element +declare global { + namespace JSX { + interface IntrinsicElements { + 'gmp-route-3d': React.DetailedHTMLProps< + React.HTMLAttributes, + HTMLElement + > & { + 'autofits-camera'?: boolean; + 'departure-time'?: string; + destination?: any; + origin?: any; + 'routing-preference'?: string; + 'travel-mode'?: string; + }; + } + } +} + +const Map3DRouteExample = () => { + // Make sure routes and maps3d libraries are loaded to register the custom elements + useMapsLibrary('routes'); + useMapsLibrary('maps3d'); + + return ( + <> + + + + + ); +}; + +const App = () => { + return ( + + + + + ); +}; +export default App; + +export function renderToDom(container: HTMLElement) { + const root = createRoot(container); + + root.render( + + + + ); +} diff --git a/examples/map-3d-routes-widget/src/control-panel.css b/examples/map-3d-routes-widget/src/control-panel.css new file mode 100644 index 00000000..c668bff7 --- /dev/null +++ b/examples/map-3d-routes-widget/src/control-panel.css @@ -0,0 +1,42 @@ +.control-panel { + position: absolute; + top: 20px; + right: 20px; + background: rgba(255, 255, 255, 0.95); + padding: 15px; + border-radius: 8px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + max-width: 300px; + z-index: 10; + font-family: sans-serif; +} + +.control-panel h3 { + margin-top: 0; + margin-bottom: 8px; + font-size: 16px; +} + +.control-panel p { + font-size: 12px; + margin: 0 0 10px 0; + color: #4a5568; +} + +.control-panel p.note { + font-size: 11px; + background: #edf2f7; + padding: 6px; + border-radius: 4px; +} + +.control-panel .links a { + font-size: 12px; + color: #3182ce; + text-decoration: none; +} + +.control-panel p.note a { + color: #3182ce; + text-decoration: underline; +} diff --git a/examples/map-3d-routes-widget/src/control-panel.tsx b/examples/map-3d-routes-widget/src/control-panel.tsx new file mode 100644 index 00000000..4f9023cd --- /dev/null +++ b/examples/map-3d-routes-widget/src/control-panel.tsx @@ -0,0 +1,41 @@ +import * as React from 'react'; +import './control-panel.css'; + +function ControlPanel() { + return ( +
+

3D Map Routes

+

+ This example demonstrates how to render a client-side 3D route using the + modern <gmp-route-3d> custom element inside a 3D Map. +

+ +

+ Note: This utilizes custom 3D elements from the Maps JS + API{' '} + + routes + {' '} + and{' '} + + maps3d + {' '} + libraries. +

+ + +
+ ); +} + +export default React.memo(ControlPanel); diff --git a/examples/map-3d-routes-widget/tsconfig.json b/examples/map-3d-routes-widget/tsconfig.json new file mode 100644 index 00000000..e7170a03 --- /dev/null +++ b/examples/map-3d-routes-widget/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../tsconfig.base.json", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "website-examples/*": ["../*"] + } + } +} diff --git a/examples/map-3d-routes-widget/vite.config.js b/examples/map-3d-routes-widget/vite.config.js new file mode 100644 index 00000000..8de2a313 --- /dev/null +++ b/examples/map-3d-routes-widget/vite.config.js @@ -0,0 +1,30 @@ +import {defineConfig, loadEnv} from 'vite'; +import {resolve} from 'node:path'; + +export default defineConfig(({mode}) => { + const {GOOGLE_MAPS_API_KEY = ''} = loadEnv(mode, resolve('../../'), ''); + + return { + envDir: '../../', + define: { + 'process.env.GOOGLE_MAPS_API_KEY': JSON.stringify(GOOGLE_MAPS_API_KEY) + }, + resolve: { + alias: { + '@vis.gl/react-google-maps/examples.js': resolve( + '../../website/static/scripts/examples.js' + ), + '@vis.gl/react-google-maps/examples.css': resolve( + '../../examples/examples.css' + ), + '@vis.gl/react-google-maps': resolve('../../src/index.ts'), + '@googlemaps/js-api-loader': resolve( + './node_modules/@googlemaps/js-api-loader' + ), + 'fast-deep-equal': resolve('./node_modules/fast-deep-equal'), + react: resolve('./node_modules/react'), + 'react-dom': resolve('./node_modules/react-dom') + } + } + }; +}); diff --git a/examples/map-3d-routes/README.md b/examples/map-3d-routes/README.md new file mode 100644 index 00000000..656b8628 --- /dev/null +++ b/examples/map-3d-routes/README.md @@ -0,0 +1,38 @@ +# 3D Map Routes Example (JS API Programmatic) + +This example shows how to programmatically retrieve route details using the modern `google.maps.routes.Route.computeRoutes(...)` method and render a thinned 3D polyline overlay dynamically on a photorealistic 3D Map (``). + +It showcases full visual control and thinned styling of polylines using the client-side Javascript SDK. + +## Google Maps Platform API Key & Requirements + +To run this example locally, you must satisfy the following platform requirements: + +1. **Billing Enabled:** The Routes API and 3D Maps are premium Google Maps features and require a Google Cloud project with an **active billing account** linked to it. +2. **Enabled APIs & Alpha Channel:** Ensure that both the **Routes API** and the **Maps JavaScript API** are explicitly enabled in your Google Cloud Console. **Note:** 3D Maps and elements are currently only available in the **Alpha channel** (e.g., by setting `version="alpha"` on your ``). +3. **API Key Environment Variable:** The API key has to be provided via an environment variable `GOOGLE_MAPS_API_KEY`. This can be done by creating a file named `.env` in the example directory with the following content: + +```shell title=".env" +GOOGLE_MAPS_API_KEY="" +``` + +## Browser Compatibility + +Photorealistic 3D Maps require WebGL2 support and hardware graphics acceleration. Please refer to the official **[Google Maps 3D Maps Browser Support Guide](https://developers.google.com/maps/documentation/javascript/3d-maps-support)** for detailed browser requirements and system compatibility guidelines. + +_(Note: Virtual machine or remote desktop environments like Cloudtop do not support direct WebGL2 hardware rendering overlays by default. Please run the example locally on your physical host machine)._ + +## Development & How to Run + +Go into the example directory: + +```shell +cd examples/map-3d-routes +``` + +Install dependencies and start the development server: + +```shell +npm install +npm run start-local +``` diff --git a/examples/map-3d-routes/index.html b/examples/map-3d-routes/index.html new file mode 100644 index 00000000..993a7c13 --- /dev/null +++ b/examples/map-3d-routes/index.html @@ -0,0 +1,31 @@ + + + + + + Example: Routes API Rendering + + + + +
+ + + diff --git a/examples/map-3d-routes/package.json b/examples/map-3d-routes/package.json new file mode 100644 index 00000000..4dec837c --- /dev/null +++ b/examples/map-3d-routes/package.json @@ -0,0 +1,19 @@ +{ + "name": "map-3d-routes", + "type": "module", + "dependencies": { + "@googlemaps/js-api-loader": "^2.0.2", + "@vis.gl/react-google-maps": "latest", + "fast-deep-equal": "^3.1.3", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "typescript": "^6.0.3", + "vite": "^5.4.11" + }, + "scripts": { + "start": "vite", + "start-local": "vite --config ../vite.config.local.js", + "build-local": "vite build --config ../vite.config.local.js", + "build": "vite build" + } +} diff --git a/examples/map-3d-routes/src/app.css b/examples/map-3d-routes/src/app.css new file mode 100644 index 00000000..a1318085 --- /dev/null +++ b/examples/map-3d-routes/src/app.css @@ -0,0 +1,10 @@ +/* Styles for 3D Map Routes Programmatic Example */ +html, +body, +#app { + width: 100%; + height: 100%; + margin: 0; + padding: 0; + overflow: hidden; +} diff --git a/examples/map-3d-routes/src/app.tsx b/examples/map-3d-routes/src/app.tsx new file mode 100644 index 00000000..31c7c168 --- /dev/null +++ b/examples/map-3d-routes/src/app.tsx @@ -0,0 +1,108 @@ +import React, {useEffect, useState} from 'react'; +import {createRoot} from 'react-dom/client'; + +import { + APIProvider, + Map3D, + useMapsLibrary, + useMap3D +} from '@vis.gl/react-google-maps'; +import ControlPanel from './control-panel'; + +import './app.css'; + +const API_KEY = + globalThis.GOOGLE_MAPS_API_KEY ?? (process.env.GOOGLE_MAPS_API_KEY as string); + +const Programmatic3DRoute = () => { + const map = useMap3D(); + const routesLibrary = useMapsLibrary('routes') as any; + const maps3dLibrary = useMapsLibrary('maps3d') as any; + + useEffect(() => { + console.log('=== 3D Routes Diagnostics ==='); + console.log('Map3D instance resolved:', map); + console.log('routesLibrary state:', routesLibrary); + console.log('maps3dLibrary state:', maps3dLibrary); + if (window.google && window.google.maps) { + console.log( + 'Global window.google.maps keys:', + Object.keys(window.google.maps) + ); + } + }, [map, routesLibrary, maps3dLibrary]); + + useEffect(() => { + if (!map || !routesLibrary || !maps3dLibrary) return; + + let polyline: any = null; + + // Fetch the route programmatically using SDK computeRoutes + routesLibrary.Route.computeRoutes({ + origin: {lat: 43.65, lng: -79.38}, + destination: {lat: 43.69, lng: -79.42}, + travelMode: 'DRIVING', + fields: ['*'] + }).then((response: any) => { + const route = response.routes?.[0]; + if (!route) return; + + // Instantiate gmp-polyline-3d using the browser's custom element registry + polyline = document.createElement('gmp-polyline-3d') as any; + polyline.path = route.path; + polyline.strokeColor = '#b903adff'; + polyline.strokeWidth = 5; // 5 meters wide (thinned, real-world width!) + polyline.altitudeMode = 'RELATIVE_TO_GROUND'; + + map.appendChild(polyline); + }); + + // Clean up polyline from the 3D Map on unmount + return () => { + if (polyline && polyline.parentNode) { + polyline.parentNode.removeChild(polyline); + } + }; + }, [map, routesLibrary, maps3dLibrary]); + + return null; +}; + +const Map3DRoutesExample = () => { + return ( + <> + + + + + ); +}; + +const App = () => { + return ( + + + + + ); +}; +export default App; + +export function renderToDom(container: HTMLElement) { + const root = createRoot(container); + + root.render( + + + + ); +} diff --git a/examples/map-3d-routes/src/control-panel.css b/examples/map-3d-routes/src/control-panel.css new file mode 100644 index 00000000..e253f170 --- /dev/null +++ b/examples/map-3d-routes/src/control-panel.css @@ -0,0 +1,50 @@ +.control-panel { + position: absolute; + top: 20px; + right: 20px; + background: rgba(255, 255, 255, 0.95); + padding: 15px; + border-radius: 8px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + max-width: 300px; + z-index: 10; + font-family: sans-serif; +} + +.control-panel h3 { + margin-top: 0; + margin-bottom: 8px; + font-size: 16px; +} + +.control-panel p { + font-size: 12px; + margin: 0 0 10px 0; + color: #4a5568; +} + +.control-panel p.note { + font-size: 11px; + background: #edf2f7; + padding: 6px; + border-radius: 4px; +} + +.control-panel .links a { + font-size: 12px; + color: #3182ce; + text-decoration: none; +} + +.control-panel p.note a { + color: #3182ce; + text-decoration: underline; +} + +.control-panel code { + word-break: break-word; + overflow-wrap: anywhere; + background: #edf2f7; + padding: 2px 4px; + border-radius: 4px; +} diff --git a/examples/map-3d-routes/src/control-panel.tsx b/examples/map-3d-routes/src/control-panel.tsx new file mode 100644 index 00000000..ee948c05 --- /dev/null +++ b/examples/map-3d-routes/src/control-panel.tsx @@ -0,0 +1,49 @@ +import * as React from 'react'; +import './control-panel.css'; + +function ControlPanel() { + return ( +
+

3D Map Routes (JS API)

+

+ This example demonstrates how to retrieve a route programmatically using + the modern google.maps.routes.Route.computeRoutes(...) SDK + method and visualize it cleanly using a custom-styled 3D polyline. +

+ +

+ Note: This utilizes custom 3D elements and routing from + the Maps JS API{' '} + + Route + {' '} + and{' '} + + 3D Map + {' '} + libraries.{' '} + Note that ROADMAP mode is currently in Alpha. +

+ + +
+ ); +} + +export default React.memo(ControlPanel); diff --git a/examples/map-3d-routes/tsconfig.json b/examples/map-3d-routes/tsconfig.json new file mode 100644 index 00000000..5eea691a --- /dev/null +++ b/examples/map-3d-routes/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../tsconfig.base.json", + "include": ["src/**/*", "types/**/*"], + "exclude": ["dist", "node_modules"] +} diff --git a/examples/map-3d-routes/vite.config.js b/examples/map-3d-routes/vite.config.js new file mode 100644 index 00000000..8de2a313 --- /dev/null +++ b/examples/map-3d-routes/vite.config.js @@ -0,0 +1,30 @@ +import {defineConfig, loadEnv} from 'vite'; +import {resolve} from 'node:path'; + +export default defineConfig(({mode}) => { + const {GOOGLE_MAPS_API_KEY = ''} = loadEnv(mode, resolve('../../'), ''); + + return { + envDir: '../../', + define: { + 'process.env.GOOGLE_MAPS_API_KEY': JSON.stringify(GOOGLE_MAPS_API_KEY) + }, + resolve: { + alias: { + '@vis.gl/react-google-maps/examples.js': resolve( + '../../website/static/scripts/examples.js' + ), + '@vis.gl/react-google-maps/examples.css': resolve( + '../../examples/examples.css' + ), + '@vis.gl/react-google-maps': resolve('../../src/index.ts'), + '@googlemaps/js-api-loader': resolve( + './node_modules/@googlemaps/js-api-loader' + ), + 'fast-deep-equal': resolve('./node_modules/fast-deep-equal'), + react: resolve('./node_modules/react'), + 'react-dom': resolve('./node_modules/react-dom') + } + } + }; +}); diff --git a/website/src/examples-sidebar.js b/website/src/examples-sidebar.js index 7e6473bc..1415e622 100644 --- a/website/src/examples-sidebar.js +++ b/website/src/examples-sidebar.js @@ -26,9 +26,11 @@ const sidebars = { 'autocomplete', 'directions', 'routes-api', - 'deckgl-overlay', 'map-3d', 'map-3d-markers', + 'map-3d-routes', + 'map-3d-routes-widget', + 'deckgl-overlay', 'extended-component-library', 'static-map', 'places-ui-kit' diff --git a/website/src/examples/map-3d-routes-widget.mdx b/website/src/examples/map-3d-routes-widget.mdx new file mode 100644 index 00000000..10a1d6e3 --- /dev/null +++ b/website/src/examples/map-3d-routes-widget.mdx @@ -0,0 +1,5 @@ +# 3D Map Routes (Widget) + +import App from 'website-examples/map-3d-routes-widget/src/app'; + + diff --git a/website/src/examples/map-3d-routes.mdx b/website/src/examples/map-3d-routes.mdx new file mode 100644 index 00000000..7d59664f --- /dev/null +++ b/website/src/examples/map-3d-routes.mdx @@ -0,0 +1,5 @@ +# 3D Map Routes (JS API) + +import App from 'website-examples/map-3d-routes/src/app'; + +