نمای نقشه به صورت دوربینی مدلسازی شده است که از بالا به یک صفحه مسطح نگاه میکند. موقعیت دوربین (و در نتیجه رندر نقشه) با ویژگیهای زیر مشخص میشود: latitude ، longitude ، altitude ، heading ، tilt ، range و fov .
هدف دوربین، موقعیت مرکز نقشه است که به صورت مختصات طول و عرض جغرافیایی مشخص میشود. میتوانید موقعیت دوربین را به دو روش مشخص کنید:
- بر اساس نقطه هدف : از ویژگی
centerبرای مشخص کردن مختصاتی روی نقشه که دوربین باید رو به آن باشد استفاده کنید. این بهترین زمانی است که میخواهید مطمئن شوید یک نقطه عطف یا منطقه در فوکوس است. - بر اساس مختصات دوربین : از ویژگی
cameraPositionبرای قرار دادن دوربین در مختصات عرض جغرافیایی، طول جغرافیایی و ارتفاع مشخص استفاده کنید. این برای تعریف یک نقطه دید دقیق ایدهآل است.
از نظر منطقی، center و cameraPosition به هم مرتبط هستند. وقتی یکی را تنظیم میکنید، دیگری به طور خودکار بر اساس جهت و فاصله دوربین محاسبه میشود. ویژگیهایی مانند tilt ، heading و roll نحوه هدفگیری دوربین را کنترل میکنند، صرف نظر از اینکه از کدام روش موقعیتیابی استفاده میکنید.
مثال زیر به شما امکان میدهد بین center (دوربین رو به مرکز نقشه) و cameraPosition (دوربین در مرکز نقشه قرار دارد) جابجا شوید.
کد منبع کامل مثال را ببینید
تایپ اسکریپت
async function init(): Promise<void> { // Import the needed libraries. await google.maps.importLibrary('maps3d'); const map3DElement = document.querySelector('gmp-map-3d')!; const btn = document.getElementById('switch-mode-btn') as HTMLButtonElement; const initialCenter = { lat: 40.7860524, lng: -73.9634983, altitude: 0 }; let isCenterMode = true; btn.addEventListener('click', () => { if (isCenterMode) { // Switch to Camera Position Mode. // Place the camera at the marker's location, but 50m up in the air map3DElement.cameraPosition = { ...initialCenter, altitude: 50 }; map3DElement.tilt = 80; btn.textContent = 'Switch to Center Mode'; isCenterMode = false; } else { // Revert back to Center Mode (looking AT the marker) map3DElement.center = initialCenter; map3DElement.tilt = 70; map3DElement.range = 1500; // Restore the original range value. btn.textContent = 'Switch to Camera Position'; isCenterMode = true; } }); } void init();
جاوا اسکریپت
async function init() { // Import the needed libraries. await google.maps.importLibrary('maps3d'); const map3DElement = document.querySelector('gmp-map-3d'); const btn = document.getElementById('switch-mode-btn'); const initialCenter = { lat: 40.7860524, lng: -73.9634983, altitude: 0 }; let isCenterMode = true; btn.addEventListener('click', () => { if (isCenterMode) { // Switch to Camera Position Mode. // Place the camera at the marker's location, but 50m up in the air map3DElement.cameraPosition = { ...initialCenter, altitude: 50 }; map3DElement.tilt = 80; btn.textContent = 'Switch to Center Mode'; isCenterMode = false; } else { // Revert back to Center Mode (looking AT the marker) map3DElement.center = initialCenter; map3DElement.tilt = 70; map3DElement.range = 1500; // Restore the original range value. btn.textContent = 'Switch to Camera Position'; isCenterMode = true; } }); } void init();
سیاساس
html, body { height: 100%; margin: 0; padding: 0; } #ui-container { position: absolute; top: 20px; left: 20px; z-index: 10; } button { background: rgba(15, 23, 42, 0.75); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); border: 1px solid rgba(255, 255, 255, 0.1); color: #f8fafc; padding: 12px 20px; border-radius: 8px; cursor: pointer; font-size: 0.9rem; font-weight: 600; transition: all 0.2s ease; box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37); } button:hover { background: rgba(56, 189, 248, 0.2); border-color: rgba(56, 189, 248, 0.4); transform: translateY(-1px); } button:active { transform: translateY(0); }
اچتیامال
<html>
<head>
<title>3D Camera Position</title>
<link rel="stylesheet" type="text/css" href="./style.css" />
<script type="module" src="./index.js"></script>
<script>
// prettier-ignore
(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({
key: "AIzaSyA6myHzS10YXdcazAFalmXvDkrYCp5cLc8"
});
</script>
</head>
<body>
<gmp-map-3d
center="40.7860524,-73.9634983"
range="1500"
tilt="70"
heading="-150"
mode="satellite">
<gmp-marker
position="40.7860524,-73.9634983"
altitude-mode="clamp-to-ground"></gmp-marker>
</gmp-map-3d>
<div id="ui-container">
<button id="switch-mode-btn" type="button">
Switch to Camera Position
</button>
</div>
</body>
</html>نمونه را امتحان کنید
میدان دید و برد
در یک محیط سهبعدی، مفهوم «بزرگنمایی» توسط دو پارامتر مجزا کنترل میشود: range و میدان دید ( fov ). اگرچه هر دو بر چگونگی نمایش اشیاء بزرگ روی نقشه تأثیر میگذارند، اما این کار را با استفاده از مکانیکهای متفاوتی انجام میدهند:
-
range: فاصله فیزیکی بین دوربین و نقطه مرکزی آن. تنظیم برد مانند نزدیک یا دور کردن دوربین از سوژه است. -
fov: زاویه عمودی لنز دوربین که بر حسب درجه اندازهگیری میشود. تنظیم میدان دید معادل تغییر لنز دوربین است. مقدار بالاتر (تا ۸۰ درجه) مانند یک لنز واید عمل میکند و حاشیه بیشتری را نشان میدهد، در حالی که مقدار پایینتر (تا ۵ درجه) مانند یک لنز تله فوتو عمل میکند و فوکوس را محدودتر میکند.
مثال زیر به شما امکان میدهد تا ببینید که چگونه گزینههای مختلف موقعیتیابی نقشه و دوربین با هم کار میکنند. پارامترها را با استفاده از اسلایدرهای رابط کاربری تنظیم کنید، یا مستقیماً با نقشه و کنترلهای نقشه تعامل داشته باشید. پارامترهای حاصل به یک عنصر gmp-map-3d اضافه میشوند که میتوانید آن را کپی کرده و دوباره استفاده کنید.
کد منبع کامل مثال را ببینید
تایپ اسکریپت
async function initMap(): Promise<void> { // Declare the needed libraries. await google.maps.importLibrary('maps3d'); const map3DElement = document.querySelector('gmp-map-3d')!; // Elements from HTML const headingSlider = document.getElementById( 'heading' ) as HTMLInputElement; const tiltSlider = document.getElementById('tilt') as HTMLInputElement; const rangeSlider = document.getElementById('range') as HTMLInputElement; const latSlider = document.getElementById('lat') as HTMLInputElement; const lngSlider = document.getElementById('lng') as HTMLInputElement; const fovSlider = document.getElementById('fov') as HTMLInputElement; const rollSlider = document.getElementById('roll') as HTMLInputElement; const headingVal = document.getElementById('heading-val')!; const tiltVal = document.getElementById('tilt-val')!; const rangeVal = document.getElementById('range-val')!; const altitudeVal = document.getElementById('altitude-val')!; const fovVal = document.getElementById('fov-val')!; const rollVal = document.getElementById('roll-val')!; const codeElem = document.getElementById('generated-code')!; const copyBtn = document.getElementById('copy-btn') as HTMLButtonElement; let currentAltitude = 30; let isUserInteracting = false; // Update values on UI when the map changes. const updateUI = () => { const heading = map3DElement.heading?.toFixed(0) ?? '0'; const tilt = map3DElement.tilt?.toFixed(0) ?? '0'; const range = map3DElement.range?.toFixed(0) ?? '0'; const rawFov = parseFloat(map3DElement.fov?.toFixed(0) ?? '45'); const fovClamped = Math.min(80, Math.max(5, rawFov)); const fov = fovClamped.toString(); const roll = map3DElement.roll?.toFixed(0) ?? '0'; const center = map3DElement.center; const mode = map3DElement.mode; headingVal.textContent = heading; tiltVal.textContent = tilt; rangeVal.textContent = range; fovVal.textContent = fov; rollVal.textContent = roll; if (!isUserInteracting) { fovSlider.value = fov; headingSlider.value = heading; tiltSlider.value = tilt; rangeSlider.value = Math.min(10000, parseFloat(range)).toString(); rollSlider.value = roll; } if (center) { const lat = center.lat.toFixed(4); const lng = center.lng.toFixed(4); const alt = currentAltitude.toFixed(0); latSlider.value = lat; lngSlider.value = lng; altitudeVal.textContent = alt; codeElem.textContent = `<gmp-map-3d center="${lat},${lng},${alt}" mode="${mode}" tilt="${tilt}" range="${range}" heading="${heading}" fov="${fov}" roll="${roll}"></gmp-map-3d>`; } }; // Copy generated HTML to clipboard. copyBtn.addEventListener('click', () => { void navigator.clipboard.writeText(codeElem.textContent || ''); copyBtn.textContent = 'Copied!'; setTimeout(() => { copyBtn.textContent = 'Copy HTML'; }, 2000); }); // Listen to slider changes using event delegation. const panel = document.querySelector('.panel')!; panel.addEventListener('input', (e) => { const target = e.target as HTMLInputElement; if (target.tagName !== 'INPUT') return; isUserInteracting = true; const prop = target.name; const val = parseFloat(target.value); if (prop === 'lat') { const currentCenter = map3DElement.center; if (currentCenter) { map3DElement.center = { lat: val, lng: currentCenter.lng, altitude: currentCenter.altitude, }; } } else if (prop === 'lng') { const currentCenter = map3DElement.center; if (currentCenter) { map3DElement.center = { lat: currentCenter.lat, lng: val, altitude: currentCenter.altitude, }; } } else if (prop === 'altitude') { currentAltitude = val; const currentCenter = map3DElement.center; if (currentCenter) { map3DElement.center = { lat: currentCenter.lat, lng: currentCenter.lng, altitude: val, }; } } else if (prop === 'tilt') { map3DElement.tilt = Math.max(0, val); } else if (prop === 'fov') { map3DElement.fov = Math.min(80, Math.max(5, val)); } else { // eslint-disable-next-line @typescript-eslint/no-explicit-any (map3DElement as any)[prop] = val; } updateUI(); }); panel.addEventListener('change', (e) => { const target = e.target as HTMLInputElement; if (target.tagName === 'INPUT') { isUserInteracting = false; } }); // Update UI on camera change events. map3DElement.addEventListener('gmp-headingchange', updateUI); map3DElement.addEventListener('gmp-tiltchange', updateUI); map3DElement.addEventListener('gmp-rangechange', updateUI); map3DElement.addEventListener('gmp-fovchange', updateUI); // Initial UI sync setTimeout(updateUI, 500); } void initMap();
جاوا اسکریپت
async function initMap() { // Declare the needed libraries. await google.maps.importLibrary('maps3d'); const map3DElement = document.querySelector('gmp-map-3d'); // Elements from HTML const headingSlider = document.getElementById('heading'); const tiltSlider = document.getElementById('tilt'); const rangeSlider = document.getElementById('range'); const latSlider = document.getElementById('lat'); const lngSlider = document.getElementById('lng'); const fovSlider = document.getElementById('fov'); const rollSlider = document.getElementById('roll'); const headingVal = document.getElementById('heading-val'); const tiltVal = document.getElementById('tilt-val'); const rangeVal = document.getElementById('range-val'); const altitudeVal = document.getElementById('altitude-val'); const fovVal = document.getElementById('fov-val'); const rollVal = document.getElementById('roll-val'); const codeElem = document.getElementById('generated-code'); const copyBtn = document.getElementById('copy-btn'); let currentAltitude = 30; let isUserInteracting = false; // Update values on UI when the map changes. const updateUI = () => { const heading = map3DElement.heading?.toFixed(0) ?? '0'; const tilt = map3DElement.tilt?.toFixed(0) ?? '0'; const range = map3DElement.range?.toFixed(0) ?? '0'; const rawFov = parseFloat(map3DElement.fov?.toFixed(0) ?? '45'); const fovClamped = Math.min(80, Math.max(5, rawFov)); const fov = fovClamped.toString(); const roll = map3DElement.roll?.toFixed(0) ?? '0'; const center = map3DElement.center; const mode = map3DElement.mode; headingVal.textContent = heading; tiltVal.textContent = tilt; rangeVal.textContent = range; fovVal.textContent = fov; rollVal.textContent = roll; if (!isUserInteracting) { fovSlider.value = fov; headingSlider.value = heading; tiltSlider.value = tilt; rangeSlider.value = Math.min(10000, parseFloat(range)).toString(); rollSlider.value = roll; } if (center) { const lat = center.lat.toFixed(4); const lng = center.lng.toFixed(4); const alt = currentAltitude.toFixed(0); latSlider.value = lat; lngSlider.value = lng; altitudeVal.textContent = alt; codeElem.textContent = `<gmp-map-3d center="${lat},${lng},${alt}" mode="${mode}" tilt="${tilt}" range="${range}" heading="${heading}" fov="${fov}" roll="${roll}"></gmp-map-3d>`; } }; // Copy generated HTML to clipboard. copyBtn.addEventListener('click', () => { void navigator.clipboard.writeText(codeElem.textContent || ''); copyBtn.textContent = 'Copied!'; setTimeout(() => { copyBtn.textContent = 'Copy HTML'; }, 2000); }); // Listen to slider changes using event delegation. const panel = document.querySelector('.panel'); panel.addEventListener('input', (e) => { const target = e.target; if (target.tagName !== 'INPUT') return; isUserInteracting = true; const prop = target.name; const val = parseFloat(target.value); if (prop === 'lat') { const currentCenter = map3DElement.center; if (currentCenter) { map3DElement.center = { lat: val, lng: currentCenter.lng, altitude: currentCenter.altitude, }; } } else if (prop === 'lng') { const currentCenter = map3DElement.center; if (currentCenter) { map3DElement.center = { lat: currentCenter.lat, lng: val, altitude: currentCenter.altitude, }; } } else if (prop === 'altitude') { currentAltitude = val; const currentCenter = map3DElement.center; if (currentCenter) { map3DElement.center = { lat: currentCenter.lat, lng: currentCenter.lng, altitude: val, }; } } else if (prop === 'tilt') { map3DElement.tilt = Math.max(0, val); } else if (prop === 'fov') { map3DElement.fov = Math.min(80, Math.max(5, val)); } else { // eslint-disable-next-line @typescript-eslint/no-explicit-any map3DElement[prop] = val; } updateUI(); }); panel.addEventListener('change', (e) => { const target = e.target; if (target.tagName === 'INPUT') { isUserInteracting = false; } }); // Update UI on camera change events. map3DElement.addEventListener('gmp-headingchange', updateUI); map3DElement.addEventListener('gmp-tiltchange', updateUI); map3DElement.addEventListener('gmp-rangechange', updateUI); map3DElement.addEventListener('gmp-fovchange', updateUI); // Initial UI sync setTimeout(updateUI, 500); } void initMap();
سیاساس
html, body { height: 100%; margin: 0; padding: 0; font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; background-color: #0f172a; color: #e2e8f0; } gmp-map-3d { height: 100%; width: 100%; } /* Glassmorphism UI Overlay */ #ui-container { position: absolute; top: 20px; left: 20px; width: 320px; z-index: 10; } .panel { background: rgba(15, 23, 42, 0.75); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 16px; padding: 24px; margin-top: 10px; box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37); } h1 { font-size: 1.25rem; font-weight: 700; margin: 0 0 4px 0; background: linear-gradient(to right, #38bdf8, #818cf8); -webkit-background-clip: text; -webkit-text-fill-color: transparent; } .sub-title { font-size: 0.85rem; color: #94a3b8; margin: 0 0 20px 0; } h2 { font-size: 0.95rem; font-weight: 600; margin: 16px 0 8px 0; color: #f8fafc; } .control-group { margin-bottom: 16px; } label { display: block; font-size: 0.85rem; margin-bottom: 6px; color: #cbd5e1; } span { font-weight: 600; color: #38bdf8; } .row { display: flex; gap: 12px; } .col { flex: 1; } input[type='number'] { font-family: 'Fira Code', monospace; background: rgba(15, 23, 42, 0.5); border: 1px solid rgba(255, 255, 255, 0.1); color: #f8fafc; padding: 4px 8px; border-radius: 6px; outline: none; } input[type='range'] { width: 100%; height: 4px; background: #334155; border-radius: 2px; outline: none; -webkit-appearance: none; } input[type='range']::-webkit-slider-thumb { -webkit-appearance: none; width: 16px; height: 16px; border-radius: 50%; background: #38bdf8; cursor: pointer; box-shadow: 0 0 8px rgba(56, 189, 248, 0.5); transition: all 0.2s ease; } input[type='range']::-webkit-slider-thumb:hover { transform: scale(1.2); background: #60a5fa; } .buttons { display: flex; flex-direction: column; gap: 8px; } button { background: rgba(51, 65, 85, 0.5); border: 1px solid rgba(255, 255, 255, 0.05); color: #f8fafc; padding: 10px 16px; border-radius: 8px; cursor: pointer; font-size: 0.85rem; font-weight: 500; transition: all 0.2s ease; text-align: left; } button:hover { background: rgba(56, 189, 248, 0.2); border-color: rgba(56, 189, 248, 0.4); transform: translateY(-1px); } button:active { transform: translateY(0); } .status-group p { font-size: 0.8rem; color: #94a3b8; margin: 4px 0; background: rgba(30, 41, 59, 0.5); padding: 6px 10px; border-radius: 6px; font-family: monospace; } .code-box { position: relative; background: rgba(15, 23, 42, 0.9); border-radius: 8px; border: 1px solid rgba(255, 255, 255, 0.05); margin-top: 8px; } pre { margin: 0; padding: 12px; overflow-x: auto; } code { font-family: 'Fira Code', monospace; font-size: 0.75rem; color: #38bdf8; } #copy-btn { display: block; width: 100%; margin-top: 8px; padding: 8px; font-size: 0.85rem; font-weight: 600; background: #334155; color: #f8fafc; border: none; border-radius: 6px; text-align: center; cursor: pointer; transition: all 0.2s ease; } #copy-btn:hover { background: #38bdf8; color: #0f172a; }
اچتیامال
<html>
<head>
<title>Google Maps 3D - Camera Position Controller</title>
<link rel="stylesheet" type="text/css" href="./style.css" />
<script type="module" src="./index.js"></script>
<script>
// prettier-ignore
(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({
key: "AIzaSyA6myHzS10YXdcazAFalmXvDkrYCp5cLc8"
});
</script>
</head>
<body>
<gmp-map-3d
center="40.7811,-73.9599,0"
mode="HYBRID"
tilt="76"
range="3270"
heading="-154"></gmp-map-3d>
<div id="ui-container">
<div class="panel">
<div class="control-group">
<label for="heading">
Heading: <span id="heading-val">0</span>°
</label>
<input
type="range"
id="heading"
name="heading"
min="-180"
max="180"
value="0"
step="1" />
</div>
<div class="control-group">
<label for="tilt">
Tilt: <span id="tilt-val">45</span>°
</label>
<input
type="range"
id="tilt"
name="tilt"
min="0"
max="90"
value="45"
step="1" />
</div>
<div class="control-group">
<label for="range">
Range: <span id="range-val">1000</span>m
</label>
<input
type="range"
id="range"
name="range"
min="100"
max="10000"
value="1000"
step="100" />
</div>
<div class="control-group row">
<div class="col">
<label for="lat">Latitude</label>
<input
type="number"
id="lat"
name="lat"
min="-90"
max="90"
value="40.7040"
step="0.0001" />
</div>
<div class="col">
<label for="lng">Longitude</label>
<input
type="number"
id="lng"
name="lng"
min="-180"
max="180"
value="-74.0180"
step="0.0001" />
</div>
</div>
<div class="control-group">
<label for="altitude">
Altitude: <span id="altitude-val">30</span>m
</label>
<input
type="range"
id="altitude"
name="altitude"
min="0"
max="5000"
value="30"
step="10" />
</div>
<div class="control-group">
<label for="fov">
FOV: <span id="fov-val">35</span>°
</label>
<input
type="range"
id="fov"
name="fov"
min="5"
max="80"
value="35"
step="1" />
</div>
<div class="control-group">
<label for="roll">
Roll: <span id="roll-val">0</span>°
</label>
<input
type="range"
id="roll"
name="roll"
min="-180"
max="180"
value="0"
step="1" />
</div>
<div class="status-group">
<div class="code-box">
<pre><code id="generated-code"></code></pre>
</div>
<button id="copy-btn">Copy HTML</button>
</div>
</div>
</div>
</body>
</html>