-
Notifications
You must be signed in to change notification settings - Fork 186
docs(example): map 3d markers example #765
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
andreashelms
wants to merge
6
commits into
visgl:main
Choose a base branch
from
andreashelms:docs/map-3d-markers-example
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
b2de09b
docs(example): maps 3d markers example
5ccc860
Merge branch 'main' into docs/map-3d-markers-example
andreashelms b46c267
chore: adjust location
KatvonRivia 271e1c6
Merge pull request #1 from KatvonRivia/docs/map-3d-markers-example
andreashelms 42c8d0f
Merge remote-tracking branch 'upstream/main' into docs/map-3d-markers…
usefulthink 358d793
fix: update example to use latest maps3d components
usefulthink File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| # 3D Maps with Markers Example | ||
|
|
||
| This example implements a new `Map3D` component that renders | ||
| a 3D Globe based on the new experimental [`Map3DElement`][gmp-map3d-overview] | ||
| web-component. | ||
|
|
||
| The map contains basic [`Marker3DElements`][gmp-map3d-marker-add] as well as markers with a custom pin and a 3D model. | ||
|
|
||
| [gmp-map3d-overview]: https://developers.google.com/maps/documentation/javascript/3d-maps-overview | ||
| [gmp-map3d-marker-add]: https://developers.google.com/maps/documentation/javascript/3d/marker-add | ||
|
|
||
| ## Google Maps API key | ||
|
|
||
| This example does not come with an API key. Running the examples locally requires a valid API key for the Google Maps Platform. | ||
| See [the official documentation][get-api-key] on how to create and configure your own key. | ||
|
|
||
| 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="<YOUR API KEY HERE>" | ||
| ``` | ||
|
|
||
| If you are on the CodeSandbox playground you can also choose to [provide the API key like this](https://codesandbox.io/docs/learn/environment/secrets) | ||
|
|
||
| ## Development | ||
|
|
||
| Go into the example-directory and run | ||
|
|
||
| ```shell | ||
| npm install | ||
| ``` | ||
|
|
||
| To start the example with the local library run | ||
|
|
||
| ```shell | ||
| npm run start-local | ||
| ``` | ||
|
|
||
| The regular `npm start` task is only used for the standalone versions of the example (CodeSandbox for example) | ||
|
|
||
| [get-api-key]: https://developers.google.com/maps/documentation/javascript/get-api-key | ||
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| <!doctype html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="utf-8" /> | ||
| <meta | ||
| name="viewport" | ||
| content="width=device-width, initial-scale=1.0, user-scalable=no" /> | ||
| <title>Example: Photorealistic 3D Map with Markers</title> | ||
|
|
||
| <style> | ||
| body { | ||
| margin: 0; | ||
| font-family: sans-serif; | ||
| } | ||
| #app { | ||
| width: 100vw; | ||
| height: 100vh; | ||
| } | ||
| </style> | ||
| </head> | ||
| <body> | ||
| <div id="app"></div> | ||
| <script type="module"> | ||
| import '@vis.gl/react-google-maps/examples.css'; | ||
| import '@vis.gl/react-google-maps/examples.js'; | ||
| import {renderToDom} from './src/app'; | ||
|
|
||
| renderToDom(document.querySelector('#app')); | ||
| </script> | ||
| </body> | ||
| </html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| { | ||
| "type": "module", | ||
| "dependencies": { | ||
| "@vis.gl/react-google-maps": "^1.8.0-rc.8", | ||
| "react": "^19.0.0", | ||
| "react-dom": "^19.0.0", | ||
| "typescript": "^5.4.5", | ||
| "vite": "^6.0.11" | ||
| }, | ||
| "scripts": { | ||
| "start": "vite", | ||
| "start-local": "vite --config ../vite.config.local.js", | ||
| "build": "vite build" | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,174 @@ | ||
| /* eslint-disable no-console */ | ||
| import React, {useEffect, useState} from 'react'; | ||
| import {createRoot} from 'react-dom/client'; | ||
|
|
||
| import { | ||
| AltitudeMode, | ||
| APIProvider, | ||
| Map3D, | ||
| MapMode, | ||
| Marker3D, | ||
| Pin, | ||
| Popover3D | ||
| } from '@vis.gl/react-google-maps'; | ||
|
|
||
| import {Model3D, Model3DProps} from './model-3d'; | ||
| import ControlPanel from './control-panel'; | ||
|
|
||
| const API_KEY = | ||
| globalThis.GOOGLE_MAPS_API_KEY ?? (process.env.GOOGLE_MAPS_API_KEY as string); | ||
|
|
||
| const INITIAL_VIEW_PROPS = { | ||
| defaultCenter: {lat: 40.7093, lng: -73.9968, altitude: 32}, | ||
| defaultRange: 1733, | ||
| defaultHeading: 5, | ||
| defaultTilt: 70, | ||
| defaultRoll: 0 | ||
| }; | ||
|
|
||
| /** | ||
| * AnimatedModel3D wraps the Model3D component with rotation animation. | ||
| * Demonstrates how to animate 3D models using requestAnimationFrame. | ||
| */ | ||
| const AnimatedModel3D = (modelProps: Model3DProps) => { | ||
| const rotationSpeed = 10; | ||
| const [modelHeading, setModelHeading] = useState(0); | ||
|
|
||
| // Animate the model rotation using requestAnimationFrame | ||
| useEffect(() => { | ||
| let animationFrameId: number; | ||
| let lastTimestamp = 0; | ||
|
|
||
| const animate = (timestamp: number) => { | ||
| if (lastTimestamp === 0) lastTimestamp = timestamp; | ||
|
|
||
| const deltaTime = (timestamp - lastTimestamp) / 1000; // Convert to seconds | ||
| lastTimestamp = timestamp; | ||
|
|
||
| setModelHeading(prev => (prev + rotationSpeed * deltaTime) % 360); | ||
|
|
||
| animationFrameId = requestAnimationFrame(animate); | ||
| }; | ||
|
|
||
| animationFrameId = requestAnimationFrame(animate); | ||
|
|
||
| return () => cancelAnimationFrame(animationFrameId); | ||
| }, [rotationSpeed]); | ||
|
|
||
| return ( | ||
| <Model3D | ||
| {...modelProps} | ||
| orientation={{heading: modelHeading, tilt: 0, roll: 0}} | ||
| /> | ||
| ); | ||
| }; | ||
|
|
||
| const App = () => { | ||
| const [openPopoverId, setOpenPopoverId] = useState<string | null>(null); | ||
| const [interactiveMarker, setInteractiveMarker] = | ||
| useState<google.maps.maps3d.Marker3DInteractiveElement | null>(null); | ||
|
|
||
| return ( | ||
| <APIProvider apiKey={API_KEY} libraries={['maps3d', 'marker']}> | ||
| <Map3D | ||
| mode={MapMode.SATELLITE} | ||
| {...INITIAL_VIEW_PROPS} | ||
| defaultLabelsDisabled> | ||
| {/* Basic marker with popover (non-interactive) */} | ||
| <Marker3D | ||
| position={{lat: 40.704876, lng: -73.995379, altitude: 50}} | ||
| altitudeMode={AltitudeMode.RELATIVE_TO_GROUND} | ||
| /> | ||
|
|
||
| {/* Basic extruded marker */} | ||
| <Marker3D | ||
| position={{lat: 40.704118, lng: -73.994371, altitude: 150}} | ||
| altitudeMode={AltitudeMode.RELATIVE_TO_GROUND} | ||
| extruded | ||
| /> | ||
|
|
||
| {/* Interactive marker with colored pin and popover */} | ||
| <Marker3D | ||
| ref={setInteractiveMarker} | ||
| position={{lat: 40.705666, lng: -73.996382, altitude: 50}} | ||
| altitudeMode={AltitudeMode.RELATIVE_TO_GROUND} | ||
| onClick={() => { | ||
| console.log('Interactive marker clicked!'); | ||
| setOpenPopoverId('colored-pin'); | ||
| }} | ||
| title="Click to see details"> | ||
| <Pin borderColor="#0D652D" background="#34A853" glyphColor="white" /> | ||
| </Marker3D> | ||
|
|
||
| {openPopoverId === 'colored-pin' && ( | ||
| <Popover3D | ||
| open | ||
| anchor={interactiveMarker} | ||
| onClose={() => { | ||
| setOpenPopoverId(null); | ||
| console.log('close'); | ||
| }}> | ||
| <div style={{padding: '12px', maxWidth: '200px'}}> | ||
| <h3 style={{margin: '0 0 8px 0', fontSize: '14px'}}> | ||
| Custom Pin Marker | ||
| </h3> | ||
| <p style={{margin: 0, fontSize: '12px'}}> | ||
| An interactive marker with custom pin colors. Click the marker | ||
| to toggle this popover. | ||
| </p> | ||
| </div> | ||
| </Popover3D> | ||
| )} | ||
|
|
||
| {/* Marker with custom logo pin */} | ||
| <Marker3D | ||
| position={{lat: 40.706461, lng: -73.997409, altitude: 50}} | ||
| altitudeMode={AltitudeMode.RELATIVE_TO_GROUND}> | ||
| <Pin | ||
| borderColor="white" | ||
| background="white" | ||
| glyph="https://www.gstatic.com/images/branding/productlogos/maps/v7/192px.svg" | ||
| /> | ||
| </Marker3D> | ||
|
|
||
| {/* Marker with SVG image */} | ||
| <Marker3D | ||
| position={{lat: 40.707275, lng: -73.998332, altitude: 80}} | ||
| altitudeMode={AltitudeMode.RELATIVE_TO_GROUND}> | ||
| <img | ||
| src="https://www.gstatic.com/images/branding/productlogos/maps/v7/192px.svg" | ||
| width={64} | ||
| height={64} | ||
| /> | ||
| </Marker3D> | ||
|
|
||
| {/* Animated 3D Model */} | ||
| <AnimatedModel3D | ||
| position={{ | ||
| lat: 40.708804, | ||
| lng: -74.000229, | ||
| altitude: 150 | ||
| }} | ||
| altitudeMode={ | ||
| AltitudeMode.RELATIVE_TO_GROUND as google.maps.maps3d.AltitudeMode | ||
| } | ||
| src={new URL('../data/balloon-red.glb', import.meta.url)} | ||
| scale={10} | ||
| /> | ||
| </Map3D> | ||
|
|
||
| <ControlPanel /> | ||
| </APIProvider> | ||
| ); | ||
| }; | ||
| export default App; | ||
|
|
||
| export function renderToDom(container: HTMLElement) { | ||
| const root = createRoot(container); | ||
|
|
||
| root.render( | ||
| <React.StrictMode> | ||
| <App /> | ||
| </React.StrictMode> | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| import * as React from 'react'; | ||
|
|
||
| const GMP_3D_MAPS_OVERVIEW_URL = | ||
| 'https://developers.google.com/maps/documentation/javascript/3d-maps-overview'; | ||
|
|
||
| const GMP_3D_MAPS_MARKER_ADD_URL = | ||
| 'https://developers.google.com/maps/documentation/javascript/3d/marker-add'; | ||
|
|
||
| function ControlPanel() { | ||
| return ( | ||
| <div className="control-panel"> | ||
| <h3>3D Maps with Markers</h3> | ||
| <p> | ||
| This example demonstrates the <code>Map3D</code>, <code>Marker3D</code>, | ||
| and <code>Pin</code> components for rendering 3D maps based on the{' '} | ||
| <a href={GMP_3D_MAPS_OVERVIEW_URL} target={'_blank'}> | ||
| Google Maps 3D | ||
| </a>{' '} | ||
| web components. | ||
| </p> | ||
|
|
||
| <p> | ||
| The map showcases various marker types including basic markers, extruded | ||
| markers, markers with custom pins, SVG/image markers, and 3D models. | ||
| Learn more about{' '} | ||
| <a href={GMP_3D_MAPS_MARKER_ADD_URL} target={'_blank'}> | ||
| adding markers to 3D maps | ||
| </a> | ||
| . | ||
| </p> | ||
|
|
||
| <div className="links"> | ||
| <a | ||
| href="https://codesandbox.io/s/github/visgl/react-google-maps/tree/main/examples/map-3d-markers" | ||
| target="_new"> | ||
| Try on CodeSandbox ↗ | ||
| </a> | ||
|
|
||
| <a | ||
| href="https://github.com/visgl/react-google-maps/tree/main/examples/map-3d-markers" | ||
| target="_new"> | ||
| View Code ↗ | ||
| </a> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| export default React.memo(ControlPanel); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| import type {PropsWithChildren, Ref} from 'react'; | ||
| import React, {forwardRef} from 'react'; | ||
|
|
||
| /** | ||
| * Props for the Model3D component. | ||
| */ | ||
| export type Model3DProps = | ||
| PropsWithChildren<google.maps.maps3d.Model3DElementOptions>; | ||
|
|
||
| /** | ||
| * Model3D component for displaying 3D models on a Map3D. | ||
| * | ||
| * @example | ||
| * ```tsx | ||
| * <Model3D | ||
| * position={{ lat: 37.7749, lng: -122.4194, altitude: 150 }} | ||
| * altitudeMode="RELATIVE_TO_GROUND" | ||
| * src={new URL('./model.glb', import.meta.url)} | ||
| * scale={10} | ||
| * orientation={{ heading: 0, tilt: 0, roll: 0 }} | ||
| * /> | ||
| * ``` | ||
| */ | ||
| export const Model3D = forwardRef(function Model3D( | ||
| props: Model3DProps, | ||
| ref: Ref<google.maps.maps3d.Model3DElement> | ||
| ) { | ||
| const {position, altitudeMode, src, orientation, scale} = props; | ||
|
|
||
| return ( | ||
| <gmp-model-3d | ||
| ref={ref} | ||
| position={position} | ||
| altitude-mode={altitudeMode} | ||
| src={src} | ||
| orientation={orientation} | ||
| scale={scale} | ||
| /> | ||
| ); | ||
| }); | ||
|
|
||
| Model3D.displayName = 'Model3D'; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| { | ||
| "compilerOptions": { | ||
| "typeRoots": ["./types", "../../types", "./node_modules/@types"], | ||
| "strict": true, | ||
| "sourceMap": true, | ||
| "noEmit": true, | ||
| "noImplicitAny": true, | ||
| "module": "ESNext", | ||
| "moduleResolution": "bundler", | ||
| "esModuleInterop": true, | ||
| "target": "ES2020", | ||
| "lib": ["es2020", "dom"], | ||
| "jsx": "react", | ||
| "skipLibCheck": true | ||
| }, | ||
| "exclude": ["./dist", "./node_modules"], | ||
| "include": ["src/**/*.ts", "src/**/*.tsx", "types/**/*.ts"] | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| export declare global { | ||
| // const or let does not work in this case, it has to be var | ||
| // eslint-disable-next-line no-var | ||
| var GOOGLE_MAPS_API_KEY: string | undefined; | ||
| // eslint-disable-next-line no-var, @typescript-eslint/no-explicit-any | ||
| var process: any; | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.