Fotorealistyczne kafelki 3D są w standardowym formacie glTF OGC, co oznacza, że do tworzenia wizualizacji 3D możesz używać dowolnego renderera obsługującego specyfikację OGC 3D Tiles. Przykładem jest Cesium, podstawowa biblioteka open source do renderowania wizualizacji 3D.
Praca z CesiumJS
CesiumJS to biblioteka JavaScript typu open source do wizualizacji 3D w internecie. Więcej informacji o korzystaniu z CesiumJS znajdziesz w artykule Learn CesiumJS (w języku angielskim).
Kontrola użytkowników
Renderowanie kafelków CesiumJS ma standardowy zestaw elementów sterujących.
Działanie | Opis |
---|---|
Widok panoramiczny | Kliknij lewym przyciskiem myszy i przeciągnij |
Widok powiększony | Kliknij prawym przyciskiem myszy i przeciągnij lub przewiń kółkiem myszy. |
Obracanie widoku | Ctrl + kliknięcie lewym lub prawym przyciskiem myszy i przeciągnięcie albo kliknięcie środkowym przyciskiem myszy i przeciągnięcie |
Sprawdzone metody
Istnieje kilka sposobów na skrócenie czasu wczytywania obiektów 3D w CesiumJS. Na przykład:
Włącz jednoczesne wysyłanie żądań, dodając do kodu HTML renderowania to stwierdzenie:
Cesium.RequestScheduler.requestsByServer["tile.googleapis.com:443"] = <REQUEST_COUNT>
Im wyższa wartość
REQUEST_COUNT
, tym szybciej wczytują się kafelki. Jeśli jednak wczytujesz stronę w przeglądarce Chrome z wartościąREQUEST_COUNT
większą niż 10 i wyłączoną pamięcią podręczną, możesz napotkać znany problem z Chrome. W większości przypadków zalecamy wartośćREQUEST_COUNT
18, aby uzyskać optymalną wydajność.Włącz pomijanie poziomów szczegółowości. Więcej informacji znajdziesz w tym artykule.
Zadbaj o prawidłowe wyświetlanie atrybucji danych, włączając showCreditsOnScreen: true
. Więcej informacji znajdziesz w zasadach.
Dane renderowania
Aby sprawdzić liczbę klatek na sekundę, zobacz, ile razy na sekundę wywoływana jest metoda requestAnimationFrame.
Aby dowiedzieć się, jak obliczana jest latencja klatki, zapoznaj się z klasą PerformanceDisplay.
Przykłady renderowania w CesiumJS
Możesz używać renderera CesiumJS z interfejsem Map Tiles API w przypadku kafelków 3D, podając po prostu adres URL głównego zbioru kafelków.
Prosty przykład
Poniższy przykład inicjuje moduł renderujący CesiumJS, a następnie wczytuje główny zestaw kafelków.
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<title>CesiumJS 3D Tiles Simple Demo</title>
<script src="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Cesium.js"></script>
<link href="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
</head>
<body>
<div id="cesiumContainer"></div>
<script>
// Enable simultaneous requests.
Cesium.RequestScheduler.requestsByServer["tile.googleapis.com:443"] = 18;
// Create the viewer.
const viewer = new Cesium.Viewer('cesiumContainer', {
imageryProvider: false,
baseLayerPicker: false,
geocoder: false,
globe: false,
// https://cesium.com/blog/2018/01/24/cesium-scene-rendering-performance/#enabling-request-render-mode
requestRenderMode: true,
});
// Add 3D Tiles tileset.
const tileset = viewer.scene.primitives.add(new Cesium.Cesium3DTileset({
url: "https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY",
// This property is needed to appropriately display attributions
// as required.
showCreditsOnScreen: true,
}));
</script>
</body>
Więcej informacji o requestRenderMode
znajdziesz w artykule Włączanie trybu renderowania żądań.
Strona HTML jest renderowana w sposób pokazany poniżej.
Integracja z interfejsem Places API
Aby uzyskać więcej informacji, możesz użyć CesiumJS z Places API. Za pomocą widżetu autouzupełniania możesz przenieść widok do obszaru miejsc. Ten przykład korzysta z interfejsu Places Autocomplete API, który można włączyć, postępując zgodnie z tymi instrukcjami, oraz z interfejsu Maps JavaScript API, który można włączyć, postępując zgodnie z tymi instrukcjami.
<!DOCTYPE html>
<head>
<meta charset="utf-8" />
<title>CesiumJS 3D Tiles Places API Integration Demo</title>
<script src="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Cesium.js"></script>
<link href="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
</head>
<body>
<label for="pacViewPlace">Go to a place: </label>
<input
type="text"
id="pacViewPlace"
name="pacViewPlace"
placeholder="Enter a location..."
style="width: 300px"
/>
<div id="cesiumContainer"></div>
<script>
// Enable simultaneous requests.
Cesium.RequestScheduler.requestsByServer["tile.googleapis.com:443"] = 18;
// Create the viewer.
const viewer = new Cesium.Viewer("cesiumContainer", {
imageryProvider: false,
baseLayerPicker: false,
requestRenderMode: true,
geocoder: false,
globe: false,
});
// Add 3D Tiles tileset.
const tileset = viewer.scene.primitives.add(
new Cesium.Cesium3DTileset({
url: "https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY",
// This property is required to display attributions as required.
showCreditsOnScreen: true,
})
);
const zoomToViewport = (viewport) => {
viewer.entities.add({
polyline: {
positions: Cesium.Cartesian3.fromDegreesArray([
viewport.getNorthEast().lng(), viewport.getNorthEast().lat(),
viewport.getSouthWest().lng(), viewport.getNorthEast().lat(),
viewport.getSouthWest().lng(), viewport.getSouthWest().lat(),
viewport.getNorthEast().lng(), viewport.getSouthWest().lat(),
viewport.getNorthEast().lng(), viewport.getNorthEast().lat(),
]),
width: 10,
clampToGround: true,
material: Cesium.Color.RED,
},
});
viewer.flyTo(viewer.entities);
};
function initAutocomplete() {
const autocomplete = new google.maps.places.Autocomplete(
document.getElementById("pacViewPlace"),
{
fields: [
"geometry",
"name",
],
}
);
autocomplete.addListener("place_changed", () => {
viewer.entities.removeAll();
const place = autocomplete.getPlace();
if (!place.geometry || !place.geometry.viewport) {
window.alert("No viewport for input: " + place.name);
return;
}
zoomToViewport(place.geometry.viewport);
});
}
</script>
<script
async=""
src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places&callback=initAutocomplete"
></script>
</body>
Widok z drona z obrotem
Możesz sterować kamerą, aby animować kafelki. W połączeniu z interfejsami Places API i Elevation API ta animacja symuluje interaktywny przelot drona nad dowolnym punktem orientacyjnym.
Ten przykładowy kod przenosi Cię w okolice miejsca wybranego w widżecie autouzupełniania.
<!DOCTYPE html>
<head>
<meta charset="utf-8" />
<title>CesiumJS 3D Tiles Rotating Drone View Demo</title>
<script src="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Cesium.js"></script>
<link href="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
</head>
<body>
<label for="pacViewPlace">Go to a place: </label>
<input type="text" id="pacViewPlace" name="pacViewPlace" placeholder="Enter a location..." style="width: 300px" />
<div id="cesiumContainer"></div>
<script>
// Enable simultaneous requests.
Cesium.RequestScheduler.requestsByServer["tile.googleapis.com:443"] = 18;
// Create the viewer and remove unneeded options.
const viewer = new Cesium.Viewer("cesiumContainer", {
imageryProvider: false,
baseLayerPicker: false,
homeButton: false,
fullscreenButton: false,
navigationHelpButton: false,
vrButton: false,
sceneModePicker: false,
geocoder: false,
globe: false,
infobox: false,
selectionIndicator: false,
timeline: false,
projectionPicker: false,
clockViewModel: null,
animation: false,
requestRenderMode: true,
});
// Add 3D Tile set.
const tileset = viewer.scene.primitives.add(
new Cesium.Cesium3DTileset({
url: "https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY",
// This property is required to display attributions.
showCreditsOnScreen: true,
})
);
// Point the camera at a location and elevation, at a viewport-appropriate distance.
function pointCameraAt(location, viewport, elevation) {
const distance = Cesium.Cartesian3.distance(
Cesium.Cartesian3.fromDegrees(
viewport.getSouthWest().lng(), viewport.getSouthWest().lat(), elevation),
Cesium.Cartesian3.fromDegrees(
viewport.getNorthEast().lng(), viewport.getNorthEast().lat(), elevation)
) / 2;
const target = new Cesium.Cartesian3.fromDegrees(location.lng(), location.lat(), elevation);
const pitch = -Math.PI / 4;
const heading = 0;
viewer.camera.lookAt(target, new Cesium.HeadingPitchRange(heading, pitch, distance));
}
// Rotate the camera around a location and elevation, at a viewport-appropriate distance.
let unsubscribe = null;
function rotateCameraAround(location, viewport, elevation) {
if(unsubscribe) unsubscribe();
pointCameraAt(location, viewport, elevation);
unsubscribe = viewer.clock.onTick.addEventListener(() => {
viewer.camera.rotate(Cesium.Cartesian3.UNIT_Z);
});
}
function initAutocomplete() {
const autocomplete = new google.maps.places.Autocomplete(
document.getElementById("pacViewPlace"), {
fields: [
"geometry",
"name",
],
}
);
autocomplete.addListener("place_changed", async () => {
const place = autocomplete.getPlace();
if (!(place.geometry && place.geometry.viewport && place.geometry.location)) {
window.alert(`Insufficient geometry data for place: ${place.name}`);
return;
}
// Get place elevation using the ElevationService.
const elevatorService = new google.maps.ElevationService();
const elevationResponse = await elevatorService.getElevationForLocations({
locations: [place.geometry.location],
});
if(!(elevationResponse.results && elevationResponse.results.length)){
window.alert(`Insufficient elevation data for place: ${place.name}`);
return;
}
const elevation = elevationResponse.results[0].elevation || 10;
rotateCameraAround(
place.geometry.location,
place.geometry.viewport,
elevation
);
});
}
</script>
<script async src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places&callback=initAutocomplete"></script>
</body>
Rysowanie polilinii i etykiet
Ten przykładowy kod pokazuje, jak dodać do mapy polilinie i etykiety. Możesz dodawać do mapy polilinie, aby wyświetlać wskazówki dojazdu samochodem i pieszo, granice nieruchomości lub obliczać czas dojazdu samochodem i pieszo. Możesz też pobierać atrybuty bez renderowania sceny.
Możesz zabrać użytkowników na wycieczkę po okolicy lub pokazać sąsiednie nieruchomości, które są obecnie na sprzedaż, a następnie dodać do sceny obiekty 3D, takie jak billboardy.
Możesz podsumować wycieczkę, wymieniając obejrzane nieruchomości i wyświetlając te szczegóły w obiektach wirtualnych.
<!DOCTYPE html>
<head>
<meta charset="utf-8" />
<title>CesiumJS 3D Tiles Polyline and Label Demo</title>
<script src="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Cesium.js"></script>
<link
href="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Widgets/widgets.css"
rel="stylesheet"
/>
</head>
<body>
<div id="cesiumContainer"></div>
<script>
// Enable simultaneous requests.
Cesium.RequestScheduler.requestsByServer["tile.googleapis.com:443"] = 18;
// Create the viewer.
const viewer = new Cesium.Viewer("cesiumContainer", {
imageryProvider: false,
baseLayerPicker: false,
requestRenderMode: true,
geocoder: false,
globe: false,
});
// Add 3D Tiles tileset.
const tileset = viewer.scene.primitives.add(
new Cesium.Cesium3DTileset({
url: "https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY",
// This property is required to display attributions as required.
showCreditsOnScreen: true,
})
);
// Draws a circle at the position, and a line from the previous position.
const drawPointAndLine = (position, prevPosition) => {
viewer.entities.removeAll();
if (prevPosition) {
viewer.entities.add({
polyline: {
positions: [prevPosition, position],
width: 3,
material: Cesium.Color.WHITE,
clampToGround: true,
classificationType: Cesium.ClassificationType.CESIUM_3D_TILE,
},
});
}
viewer.entities.add({
position: position,
ellipsoid: {
radii: new Cesium.Cartesian3(1, 1, 1),
material: Cesium.Color.RED,
},
});
};
// Compute, draw, and display the position's height relative to the previous position.
var prevPosition;
const processHeights = (newPosition) => {
drawPointAndLine(newPosition, prevPosition);
const newHeight = Cesium.Cartographic.fromCartesian(newPosition).height;
let labelText = "Current altitude (meters above sea level):\n\t" + newHeight;
if (prevPosition) {
const prevHeight =
Cesium.Cartographic.fromCartesian(prevPosition).height;
labelText += "\nHeight from previous point (meters):\n\t" + Math.abs(newHeight - prevHeight);
}
viewer.entities.add({
position: newPosition,
label: {
text: labelText,
disableDepthTestDistance: Number.POSITIVE_INFINITY,
pixelOffset: new Cesium.Cartesian2(0, -10),
showBackground: true,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
}
});
prevPosition = newPosition;
};
const handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
handler.setInputAction(function (event) {
const earthPosition = viewer.scene.pickPosition(event.position);
if (Cesium.defined(earthPosition)) {
processHeights(earthPosition);
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
</script>
</body>
Orbita kamery
W Cesium możesz obracać kamerę wokół wybranego punktu, unikając kolizji z budynkami. Możesz też sprawić, że budynki staną się przezroczyste, gdy kamera będzie się przez nie przesuwać.
Najpierw zablokuj kamerę w jednym punkcie, a potem utwórz orbitę kamery, aby zaprezentować swój zasób. Możesz to zrobić, używając funkcji lookAtTransform
kamery z detektorem zdarzeń, jak pokazano w tym przykładzie kodu.
// Lock the camera onto a point.
const center = Cesium.Cartesian3.fromRadians(
2.4213211833389243,
0.6171926869414084,
3626.0426275055174
);
const transform = Cesium.Transforms.eastNorthUpToFixedFrame(center);
viewer.scene.camera.lookAtTransform(
transform,
new Cesium.HeadingPitchRange(0, -Math.PI / 8, 2900)
);
// Orbit around this point.
viewer.clock.onTick.addEventListener(function (clock) {
viewer.scene.camera.rotateRight(0.005);
});
Więcej informacji o sterowaniu kamerą znajdziesz w artykule Sterowanie kamerą.
Praca z Cesium for Unreal
Aby używać wtyczki Cesium for Unreal z interfejsem 3D Tiles API, wykonaj te czynności:
Zainstaluj wtyczkę Cesium for Unreal.
Utwórz nowy projekt Unreal.
Połącz się z interfejsem Google Photorealistic 3D Tiles API.
Otwórz okno Cesium, wybierając w menu Cesium > Cesium.
Kliknij Blank 3D Tiles Tileset (Pusty zestaw kafelków 3D).
W Szkicowniku świata otwórz panel Szczegóły, klikając ten zbiór kafelków 3D Cesium.
Zmień Źródło z Z Cesium Ion na Z adresu URL.
Ustaw adres URL na adres URL Google 3D Tiles.
https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY
- Aby prawidłowo wyświetlać atrybucje, włącz opcję Wyświetlaj informacje o autorach na ekranie.
Świat zostanie wczytany. Aby przejść do dowolnego obiektu LatLng, wybierz element CesiumGeoreference w panelu Szkicownik, a następnie edytuj element Origin Latitude/Longitude/Height w panelu Szczegóły.
Praca z Cesium for Unity
Aby używać fotorealistycznych kafelków w Cesium for Unity, wykonaj te czynności.
Utwórz nowy projekt Unity.
Dodaj nowy rejestr o ograniczonym zakresie w sekcji Package Manager (Menedżer pakietów) (Edytor > Ustawienia projektu).
Nazwa: Cesium
URL: https://unity.pkg.cesium.com
Zakresy: com.cesium.unity
Zainstaluj pakiet Cesium for Unity.
Połącz się z interfejsem Google Photorealistic 3D Tiles API.
Otwórz okno Cesium, wybierając w menu Cesium > Cesium.
Kliknij Blank 3D Tiles Tileset (Pusty zestaw kafelków 3D).
W panelu po lewej stronie w sekcji Źródło wybierz opcję Źródło zestawu kafelków, a następnie Z adresu URL (zamiast Z Cesium Ion).
Ustaw adres URL na adres URL Google 3D Tiles.
https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY
- Aby prawidłowo wyświetlać atrybucje, włącz opcję Wyświetlaj informacje o autorach na ekranie.
Świat zostanie wczytany. Aby przejść do dowolnego obiektu LatLng, wybierz element CesiumGeoreference w hierarchii sceny, a następnie w inspektorze zmień szerokość/długość geograficzną/wysokość punktu początkowego.
Praca z deck.gl
deck.gl to oparta na WebGL platforma JavaScript typu open source do tworzenia wydajnych wizualizacji danych na dużą skalę.
Atrybucja
Zadbaj o prawidłowe wyświetlanie atrybucji danych, wyodrębniając copyright
pole z pliku gltf kafelkówasset
, a następnie wyświetlając je w renderowanym widoku. Więcej informacji znajdziesz w artykule Wyświetlanie atrybucji danych.
Przykłady renderowania deck.gl
Prosty przykład
Poniższy przykład inicjuje moduł renderujący deck.gl, a następnie wczytuje miejsce w 3D. W kodzie zastąp YOUR_API_KEY swoim kluczem interfejsu API.
<!DOCTYPE html>
<html>
<head>
<title>deck.gl Photorealistic 3D Tiles example</title>
<script src="https://unpkg.com/deck.gl@latest/dist.min.js"></script>
<style>
body { margin: 0; padding: 0;}
#map { position: absolute; top: 0;bottom: 0;width: 100%;}
#credits { position: absolute; bottom: 0; right: 0; padding: 2px; font-size: 15px; color: white;
text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black;}
</style>
</head>
<body>
<div id="map"></div>
<div id="credits"></div>
<script>
const GOOGLE_API_KEY = YOUR_API_KEY;
const TILESET_URL = `https://tile.googleapis.com/v1/3dtiles/root.json`;
const creditsElement = document.getElementById('credits');
new deck.DeckGL({
container: 'map',
initialViewState: {
latitude: 50.0890,
longitude: 14.4196,
zoom: 16,
bearing: 90,
pitch: 60,
height: 200
},
controller: {minZoom: 8},
layers: [
new deck.Tile3DLayer({
id: 'google-3d-tiles',
data: TILESET_URL,
loadOptions: {
fetch: {
headers: {
'X-GOOG-API-KEY': GOOGLE_API_KEY
}
}
},
onTilesetLoad: tileset3d => {
tileset3d.options.onTraversalComplete = selectedTiles => {
const credits = new Set();
selectedTiles.forEach(tile => {
const {copyright} = tile.content.gltf.asset;
copyright.split(';').forEach(credits.add, credits);
creditsElement.innerHTML = [...credits].join('; ');
});
return selectedTiles;
}
}
})
]
});
</script>
</body>
</html>
wizualizować warstwy 2D na fotorealistycznych kafelkach 3D Google;
Rozszerzenie deck.gl TerrainExtension renderuje dane 2D na powierzchni 3D. Możesz na przykład nałożyć plik GeoJSON z konturami budynku na geometrię fotorealistycznych kafelków 3D.
W poniższym przykładzie warstwa budynków jest wizualizowana za pomocą wielokątów dostosowanych do powierzchni fotorealistycznych kafelków 3D.
<!DOCTYPE html>
<html>
<head>
<title>Google 3D tiles example</title>
<script src="https://unpkg.com/deck.gl@latest/dist.min.js"></script>
<style>
body { margin: 0; padding: 0;}
#map { position: absolute; top: 0;bottom: 0;width: 100%;}
#credits { position: absolute; bottom: 0; right: 0; padding: 2px; font-size: 15px; color: white;
text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black;}
</style>
</head>
<body>
<div id="map"></div>
<div id="credits"></div>
<script>
const GOOGLE_API_KEY = YOUR_API_KEY;
const TILESET_URL = `https://tile.googleapis.com/v1/3dtiles/root.json`;
const BUILDINGS_URL = 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/google-3d-tiles/buildings.geojson'
const creditsElement = document.getElementById('credits');
const deckgl = new deck.DeckGL({
container: 'map',
initialViewState: {
latitude: 50.0890,
longitude: 14.4196,
zoom: 16,
bearing: 90,
pitch: 60,
height: 200
},
controller: true,
layers: [
new deck.Tile3DLayer({
id: 'google-3d-tiles',
data: TILESET_URL,
loadOptions: {
fetch: {
headers: {
'X-GOOG-API-KEY': GOOGLE_API_KEY
}
}
},
onTilesetLoad: tileset3d => {
tileset3d.options.onTraversalComplete = selectedTiles => {
const credits = new Set();
selectedTiles.forEach(tile => {
const {copyright} = tile.content.gltf.asset;
copyright.split(';').forEach(credits.add, credits);
creditsElement.innerHTML = [...credits].join('; ');
});
return selectedTiles;
}
},
operation: 'terrain+draw'
}),
new deck.GeoJsonLayer({
id: 'buildings',
// This dataset is created by CARTO, using other Open Datasets available. More info at: https://3dtiles.carto.com/#about.
data: 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/google-3d-tiles/buildings.geojson',
stroked: false,
filled: true,
getFillColor: ({properties}) => {
const {tpp} = properties;
// quantiles break
if (tpp < 0.6249)
return [254, 246, 181]
else if (tpp < 0.6780)
return [255, 194, 133]
else if (tpp < 0.8594)
return [250, 138, 118]
return [225, 83, 131]
},
opacity: 0.2,
extensions: [new deck._TerrainExtension()]
})
]
});
</script>
</body>
</html>